OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // For loading files, we make use of overlapped i/o to ensure that reading from | 5 // For loading files, we make use of overlapped i/o to ensure that reading from |
6 // the filesystem (e.g., a network filesystem) does not block the calling | 6 // the filesystem (e.g., a network filesystem) does not block the calling |
7 // thread. An alternative approach would be to use a background thread or pool | 7 // thread. An alternative approach would be to use a background thread or pool |
8 // of threads, but it seems better to leverage the operating system's ability | 8 // of threads, but it seems better to leverage the operating system's ability |
9 // to do background file reads for us. | 9 // to do background file reads for us. |
10 // | 10 // |
11 // Since overlapped reads require a 'static' buffer for the duration of the | 11 // Since overlapped reads require a 'static' buffer for the duration of the |
(...skipping 10 matching lines...) Expand all Loading... |
22 #include "base/compiler_specific.h" | 22 #include "base/compiler_specific.h" |
23 #include "base/message_loop.h" | 23 #include "base/message_loop.h" |
24 #include "base/platform_file.h" | 24 #include "base/platform_file.h" |
25 #include "base/string_util.h" | 25 #include "base/string_util.h" |
26 #include "base/worker_pool.h" | 26 #include "base/worker_pool.h" |
27 #include "googleurl/src/gurl.h" | 27 #include "googleurl/src/gurl.h" |
28 #include "net/base/load_flags.h" | 28 #include "net/base/load_flags.h" |
29 #include "net/base/mime_util.h" | 29 #include "net/base/mime_util.h" |
30 #include "net/base/net_errors.h" | 30 #include "net/base/net_errors.h" |
31 #include "net/base/net_util.h" | 31 #include "net/base/net_util.h" |
| 32 #include "net/http/http_util.h" |
32 #include "net/url_request/url_request.h" | 33 #include "net/url_request/url_request.h" |
33 #include "net/url_request/url_request_file_dir_job.h" | 34 #include "net/url_request/url_request_file_dir_job.h" |
34 | 35 |
35 #if defined(OS_WIN) | 36 #if defined(OS_WIN) |
36 class URLRequestFileJob::AsyncResolver : | 37 class URLRequestFileJob::AsyncResolver : |
37 public base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver> { | 38 public base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver> { |
38 public: | 39 public: |
39 explicit AsyncResolver(URLRequestFileJob* owner) | 40 explicit AsyncResolver(URLRequestFileJob* owner) |
40 : owner_(owner), owner_loop_(MessageLoop::current()) { | 41 : owner_(owner), owner_loop_(MessageLoop::current()) { |
41 } | 42 } |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
83 // file names). | 84 // file names). |
84 return new URLRequestFileJob(request, file_path); | 85 return new URLRequestFileJob(request, file_path); |
85 } | 86 } |
86 | 87 |
87 URLRequestFileJob::URLRequestFileJob(URLRequest* request, | 88 URLRequestFileJob::URLRequestFileJob(URLRequest* request, |
88 const FilePath& file_path) | 89 const FilePath& file_path) |
89 : URLRequestJob(request), | 90 : URLRequestJob(request), |
90 file_path_(file_path), | 91 file_path_(file_path), |
91 ALLOW_THIS_IN_INITIALIZER_LIST( | 92 ALLOW_THIS_IN_INITIALIZER_LIST( |
92 io_callback_(this, &URLRequestFileJob::DidRead)), | 93 io_callback_(this, &URLRequestFileJob::DidRead)), |
93 is_directory_(false) { | 94 is_directory_(false), |
| 95 remaining_bytes_(0) { |
94 } | 96 } |
95 | 97 |
96 URLRequestFileJob::~URLRequestFileJob() { | 98 URLRequestFileJob::~URLRequestFileJob() { |
97 #if defined(OS_WIN) | 99 #if defined(OS_WIN) |
98 DCHECK(!async_resolver_); | 100 DCHECK(!async_resolver_); |
99 #endif | 101 #endif |
100 } | 102 } |
101 | 103 |
102 void URLRequestFileJob::Start() { | 104 void URLRequestFileJob::Start() { |
103 #if defined(OS_WIN) | 105 #if defined(OS_WIN) |
(...skipping 24 matching lines...) Expand all Loading... |
128 } | 130 } |
129 #endif | 131 #endif |
130 | 132 |
131 URLRequestJob::Kill(); | 133 URLRequestJob::Kill(); |
132 } | 134 } |
133 | 135 |
134 bool URLRequestFileJob::ReadRawData(net::IOBuffer* dest, int dest_size, | 136 bool URLRequestFileJob::ReadRawData(net::IOBuffer* dest, int dest_size, |
135 int *bytes_read) { | 137 int *bytes_read) { |
136 DCHECK_NE(dest_size, 0); | 138 DCHECK_NE(dest_size, 0); |
137 DCHECK(bytes_read); | 139 DCHECK(bytes_read); |
| 140 DCHECK_GE(remaining_bytes_, 0); |
| 141 |
| 142 if (remaining_bytes_ < dest_size) |
| 143 dest_size = static_cast<int>(remaining_bytes_); |
| 144 |
| 145 // If we should copy zero bytes because |remaining_bytes_| is zero, short |
| 146 // circuit here. |
| 147 if (!dest_size) { |
| 148 *bytes_read = 0; |
| 149 return true; |
| 150 } |
138 | 151 |
139 int rv = stream_.Read(dest->data(), dest_size, &io_callback_); | 152 int rv = stream_.Read(dest->data(), dest_size, &io_callback_); |
140 if (rv >= 0) { | 153 if (rv >= 0) { |
141 // Data is immediately available. | 154 // Data is immediately available. |
142 *bytes_read = rv; | 155 *bytes_read = rv; |
| 156 remaining_bytes_ -= rv; |
| 157 DCHECK_GE(remaining_bytes_, 0); |
143 return true; | 158 return true; |
144 } | 159 } |
145 | 160 |
146 // Otherwise, a read error occured. We may just need to wait... | 161 // Otherwise, a read error occured. We may just need to wait... |
147 if (rv == net::ERR_IO_PENDING) { | 162 if (rv == net::ERR_IO_PENDING) { |
148 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); | 163 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); |
149 } else { | 164 } else { |
150 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); | 165 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); |
151 } | 166 } |
152 return false; | 167 return false; |
(...skipping 15 matching lines...) Expand all Loading... |
168 stream_.IsOpen()) { | 183 stream_.IsOpen()) { |
169 info->response_data_file = | 184 info->response_data_file = |
170 base::CreatePlatformFile(file_path_.ToWStringHack(), | 185 base::CreatePlatformFile(file_path_.ToWStringHack(), |
171 base::PLATFORM_FILE_OPEN | | 186 base::PLATFORM_FILE_OPEN | |
172 base::PLATFORM_FILE_READ | | 187 base::PLATFORM_FILE_READ | |
173 base::PLATFORM_FILE_ASYNC, | 188 base::PLATFORM_FILE_ASYNC, |
174 &created); | 189 &created); |
175 } | 190 } |
176 } | 191 } |
177 | 192 |
| 193 void URLRequestFileJob::SetExtraRequestHeaders(const std::string& headers) { |
| 194 // We only care about "Range" header here. |
| 195 std::vector<net::HttpByteRange> ranges; |
| 196 if (net::HttpUtil::ParseRanges(headers, &ranges)) { |
| 197 if (ranges.size() == 1) { |
| 198 byte_range_ = ranges[0]; |
| 199 } else { |
| 200 // We don't support multiple range requests in one single URL request, |
| 201 // because we need to do multipart encoding here. |
| 202 // TODO(hclam): decide whether we want to support multiple range requests. |
| 203 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, |
| 204 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)); |
| 205 } |
| 206 } |
| 207 } |
| 208 |
178 void URLRequestFileJob::DidResolve( | 209 void URLRequestFileJob::DidResolve( |
179 bool exists, const file_util::FileInfo& file_info) { | 210 bool exists, const file_util::FileInfo& file_info) { |
180 #if defined(OS_WIN) | 211 #if defined(OS_WIN) |
181 async_resolver_ = NULL; | 212 async_resolver_ = NULL; |
182 #endif | 213 #endif |
183 | 214 |
184 // We may have been orphaned... | 215 // We may have been orphaned... |
185 if (!request_) | 216 if (!request_) |
186 return; | 217 return; |
187 | 218 |
188 int rv = net::OK; | 219 int rv = net::OK; |
189 // We use URLRequestFileJob to handle valid and invalid files as well as | 220 // We use URLRequestFileJob to handle valid and invalid files as well as |
190 // invalid directories. For a directory to be invalid, it must either not | 221 // invalid directories. For a directory to be invalid, it must either not |
191 // exist, or be "\" on Windows. (Windows resolves "\" to "C:\", thus | 222 // exist, or be "\" on Windows. (Windows resolves "\" to "C:\", thus |
192 // reporting it as existent.) On POSIX, we don't count any existent | 223 // reporting it as existent.) On POSIX, we don't count any existent |
193 // directory as invalid. | 224 // directory as invalid. |
194 if (!exists || file_info.is_directory) { | 225 if (!exists || file_info.is_directory) { |
195 rv = net::ERR_FILE_NOT_FOUND; | 226 rv = net::ERR_FILE_NOT_FOUND; |
196 } else { | 227 } else { |
197 int flags = base::PLATFORM_FILE_OPEN | | 228 int flags = base::PLATFORM_FILE_OPEN | |
198 base::PLATFORM_FILE_READ | | 229 base::PLATFORM_FILE_READ | |
199 base::PLATFORM_FILE_ASYNC; | 230 base::PLATFORM_FILE_ASYNC; |
200 rv = stream_.Open(file_path_, flags); | 231 rv = stream_.Open(file_path_, flags); |
201 } | 232 } |
202 | 233 |
203 if (rv == net::OK) { | 234 if (rv != net::OK) { |
204 set_expected_content_size(file_info.size); | |
205 NotifyHeadersComplete(); | |
206 } else { | |
207 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); | 235 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); |
| 236 return; |
208 } | 237 } |
| 238 |
| 239 if (!byte_range_.ComputeBounds(file_info.size)) { |
| 240 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, |
| 241 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)); |
| 242 return; |
| 243 } |
| 244 |
| 245 remaining_bytes_ = byte_range_.last_byte_position() - |
| 246 byte_range_.first_byte_position() + 1; |
| 247 DCHECK_GE(remaining_bytes_, 0); |
| 248 |
| 249 // Do the seek at the beginning of the request. |
| 250 if (remaining_bytes_ > 0 && |
| 251 byte_range_.first_byte_position() != 0 && |
| 252 byte_range_.first_byte_position() != |
| 253 stream_.Seek(net::FROM_BEGIN, byte_range_.first_byte_position())) { |
| 254 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, |
| 255 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)); |
| 256 return; |
| 257 } |
| 258 |
| 259 set_expected_content_size(remaining_bytes_); |
| 260 NotifyHeadersComplete(); |
209 } | 261 } |
210 | 262 |
211 void URLRequestFileJob::DidRead(int result) { | 263 void URLRequestFileJob::DidRead(int result) { |
212 if (result > 0) { | 264 if (result > 0) { |
213 SetStatus(URLRequestStatus()); // Clear the IO_PENDING status | 265 SetStatus(URLRequestStatus()); // Clear the IO_PENDING status |
214 } else if (result == 0) { | 266 } else if (result == 0) { |
215 NotifyDone(URLRequestStatus()); | 267 NotifyDone(URLRequestStatus()); |
216 } else { | 268 } else { |
217 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result)); | 269 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result)); |
218 } | 270 } |
| 271 |
| 272 remaining_bytes_ -= result; |
| 273 DCHECK_GE(remaining_bytes_, 0); |
| 274 |
219 NotifyReadComplete(result); | 275 NotifyReadComplete(result); |
220 } | 276 } |
221 | 277 |
222 bool URLRequestFileJob::IsRedirectResponse( | 278 bool URLRequestFileJob::IsRedirectResponse( |
223 GURL* location, int* http_status_code) { | 279 GURL* location, int* http_status_code) { |
224 #if defined(OS_WIN) | 280 #if defined(OS_WIN) |
225 std::wstring extension = | 281 std::wstring extension = |
226 file_util::GetFileExtensionFromPath(file_path_.value()); | 282 file_util::GetFileExtensionFromPath(file_path_.value()); |
227 | 283 |
228 // Follow a Windows shortcut. | 284 // Follow a Windows shortcut. |
229 // We just resolve .lnk file, ignore others. | 285 // We just resolve .lnk file, ignore others. |
230 if (!LowerCaseEqualsASCII(extension, "lnk")) | 286 if (!LowerCaseEqualsASCII(extension, "lnk")) |
231 return false; | 287 return false; |
232 | 288 |
233 std::wstring new_path = file_path_.value(); | 289 std::wstring new_path = file_path_.value(); |
234 bool resolved; | 290 bool resolved; |
235 resolved = file_util::ResolveShortcut(&new_path); | 291 resolved = file_util::ResolveShortcut(&new_path); |
236 | 292 |
237 // If shortcut is not resolved succesfully, do not redirect. | 293 // If shortcut is not resolved succesfully, do not redirect. |
238 if (!resolved) | 294 if (!resolved) |
239 return false; | 295 return false; |
240 | 296 |
241 *location = net::FilePathToFileURL(FilePath(new_path)); | 297 *location = net::FilePathToFileURL(FilePath(new_path)); |
242 *http_status_code = 301; | 298 *http_status_code = 301; |
243 return true; | 299 return true; |
244 #else | 300 #else |
245 return false; | 301 return false; |
246 #endif | 302 #endif |
247 } | 303 } |
OLD | NEW |