OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 // |
(...skipping 24 matching lines...) Expand all Loading... | |
35 #include "net/base/net_errors.h" | 35 #include "net/base/net_errors.h" |
36 #include "net/base/net_util.h" | 36 #include "net/base/net_util.h" |
37 #include "net/http/http_util.h" | 37 #include "net/http/http_util.h" |
38 #include "net/url_request/url_request.h" | 38 #include "net/url_request/url_request.h" |
39 #include "net/url_request/url_request_context.h" | 39 #include "net/url_request/url_request_context.h" |
40 #include "net/url_request/url_request_error_job.h" | 40 #include "net/url_request/url_request_error_job.h" |
41 #include "net/url_request/url_request_file_dir_job.h" | 41 #include "net/url_request/url_request_file_dir_job.h" |
42 | 42 |
43 namespace net { | 43 namespace net { |
44 | 44 |
45 class URLRequestFileJob::AsyncResolver | 45 namespace { |
46 : public base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver> { | |
47 public: | |
48 explicit AsyncResolver(URLRequestFileJob* owner) | |
49 : owner_(owner), owner_loop_(MessageLoop::current()) { | |
50 } | |
51 | 46 |
52 void Resolve(const FilePath& file_path) { | 47 void ResolveAndGetMimeType(const FilePath& file_path, |
53 base::PlatformFileInfo file_info; | 48 bool* exists, |
54 bool exists = file_util::GetFileInfo(file_path, &file_info); | 49 base::PlatformFileInfo* file_info, |
55 base::AutoLock locked(lock_); | 50 bool* mime_type_result, |
56 if (owner_loop_) { | 51 std::string* mime_type) { |
57 owner_loop_->PostTask( | 52 *exists = file_util::GetFileInfo(file_path, file_info); |
58 FROM_HERE, | 53 // On Windows GetMimeTypeFromFile() goes to the registry. Thus it should be |
59 base::Bind(&AsyncResolver::ReturnResults, this, exists, file_info)); | 54 // done in WorkerPool. |
60 } | 55 *mime_type_result = GetMimeTypeFromFile(file_path, mime_type); |
61 } | 56 } |
62 | 57 |
63 void Cancel() { | 58 } |
64 owner_ = NULL; | |
65 | 59 |
66 base::AutoLock locked(lock_); | |
67 owner_loop_ = NULL; | |
68 } | |
69 | |
70 private: | |
71 friend class base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver>; | |
72 | |
73 ~AsyncResolver() {} | |
74 | |
75 void ReturnResults(bool exists, const base::PlatformFileInfo& file_info) { | |
76 if (owner_) | |
77 owner_->DidResolve(exists, file_info); | |
78 } | |
79 | |
80 URLRequestFileJob* owner_; | |
81 | |
82 base::Lock lock_; | |
83 MessageLoop* owner_loop_; | |
84 }; | |
85 | 60 |
86 URLRequestFileJob::URLRequestFileJob(URLRequest* request, | 61 URLRequestFileJob::URLRequestFileJob(URLRequest* request, |
87 const FilePath& file_path) | 62 const FilePath& file_path) |
88 : URLRequestJob(request, request->context()->network_delegate()), | 63 : URLRequestJob(request, request->context()->network_delegate()), |
89 file_path_(file_path), | 64 file_path_(file_path), |
90 stream_(NULL), | 65 stream_(NULL), |
91 is_directory_(false), | 66 is_directory_(false), |
92 remaining_bytes_(0) { | 67 mime_type_result_(false), |
68 file_size_(-1), | |
69 remaining_bytes_(0), | |
70 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { | |
93 } | 71 } |
94 | 72 |
95 // static | 73 // static |
96 URLRequestJob* URLRequestFileJob::Factory(URLRequest* request, | 74 URLRequestJob* URLRequestFileJob::Factory(URLRequest* request, |
97 const std::string& scheme) { | 75 const std::string& scheme) { |
98 FilePath file_path; | 76 FilePath file_path; |
99 const bool is_file = FileURLToFilePath(request->url(), &file_path); | 77 const bool is_file = FileURLToFilePath(request->url(), &file_path); |
100 | 78 |
101 // Check file access permissions. | 79 // Check file access permissions. |
102 if (!IsFileAccessAllowed(*request, file_path)) | 80 if (!IsFileAccessAllowed(*request, file_path)) |
103 return new URLRequestErrorJob(request, ERR_ACCESS_DENIED); | 81 return new URLRequestErrorJob(request, ERR_ACCESS_DENIED); |
104 | 82 |
105 // We need to decide whether to create URLRequestFileJob for file access or | 83 // We need to decide whether to create URLRequestFileJob for file access or |
106 // URLRequestFileDirJob for directory access. To avoid accessing the | 84 // URLRequestFileDirJob for directory access. To avoid accessing the |
107 // filesystem, we only look at the path string here. | 85 // filesystem, we only look at the path string here. |
108 // The code in the URLRequestFileJob::Start() method discovers that a path, | 86 // The code in the URLRequestFileJob::Start() method discovers that a path, |
109 // which doesn't end with a slash, should really be treated as a directory, | 87 // which doesn't end with a slash, should really be treated as a directory, |
110 // and it then redirects to the URLRequestFileDirJob. | 88 // and it then redirects to the URLRequestFileDirJob. |
111 if (is_file && | 89 if (is_file && |
112 file_util::EndsWithSeparator(file_path) && | 90 file_util::EndsWithSeparator(file_path) && |
113 file_path.IsAbsolute()) | 91 file_path.IsAbsolute()) |
114 return new URLRequestFileDirJob(request, file_path); | 92 return new URLRequestFileDirJob(request, file_path); |
115 | 93 |
116 // Use a regular file request job for all non-directories (including invalid | 94 // Use a regular file request job for all non-directories (including invalid |
117 // file names). | 95 // file names). |
118 return new URLRequestFileJob(request, file_path); | 96 return new URLRequestFileJob(request, file_path); |
119 } | 97 } |
120 | 98 |
121 void URLRequestFileJob::Start() { | 99 void URLRequestFileJob::Start() { |
122 DCHECK(!async_resolver_); | 100 bool* exists = new bool; |
123 async_resolver_ = new AsyncResolver(this); | 101 base::PlatformFileInfo* file_info = new base::PlatformFileInfo; |
eroman
2012/07/12 19:13:16
This feels a bit crowded now with the extra parame
| |
124 base::WorkerPool::PostTask( | 102 bool* mime_type_result = new bool; |
103 std::string* read_mime_type = new std::string; | |
104 base::WorkerPool::PostTaskAndReply( | |
125 FROM_HERE, | 105 FROM_HERE, |
126 base::Bind(&AsyncResolver::Resolve, async_resolver_.get(), file_path_), | 106 base::Bind(&ResolveAndGetMimeType, file_path_, |
107 base::Unretained(exists), | |
108 base::Unretained(file_info), | |
109 base::Unretained(mime_type_result), | |
110 base::Unretained(read_mime_type)), | |
111 base::Bind(&URLRequestFileJob::DidResolveAndMimeType, | |
112 weak_ptr_factory_.GetWeakPtr(), | |
113 base::Owned(exists), | |
114 base::Owned(file_info), | |
115 base::Owned(mime_type_result), | |
116 base::Owned(read_mime_type)), | |
127 true); | 117 true); |
128 } | 118 } |
129 | 119 |
130 void URLRequestFileJob::Kill() { | 120 void URLRequestFileJob::Kill() { |
131 // URL requests should not block on the disk! | 121 stream_.CloseAndCancelAsync(); |
132 // http://code.google.com/p/chromium/issues/detail?id=59849 | 122 weak_ptr_factory_.InvalidateWeakPtrs(); |
133 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
134 stream_.CloseSync(); | |
135 | |
136 if (async_resolver_) { | |
137 async_resolver_->Cancel(); | |
138 async_resolver_ = NULL; | |
139 } | |
140 | 123 |
141 URLRequestJob::Kill(); | 124 URLRequestJob::Kill(); |
142 } | 125 } |
143 | 126 |
144 bool URLRequestFileJob::ReadRawData(IOBuffer* dest, int dest_size, | 127 bool URLRequestFileJob::ReadRawData(IOBuffer* dest, int dest_size, |
145 int *bytes_read) { | 128 int *bytes_read) { |
146 DCHECK_NE(dest_size, 0); | 129 DCHECK_NE(dest_size, 0); |
147 DCHECK(bytes_read); | 130 DCHECK(bytes_read); |
148 DCHECK_GE(remaining_bytes_, 0); | 131 DCHECK_GE(remaining_bytes_, 0); |
149 | 132 |
150 if (remaining_bytes_ < dest_size) | 133 if (remaining_bytes_ < dest_size) |
151 dest_size = static_cast<int>(remaining_bytes_); | 134 dest_size = static_cast<int>(remaining_bytes_); |
152 | 135 |
153 // If we should copy zero bytes because |remaining_bytes_| is zero, short | 136 // If we should copy zero bytes because |remaining_bytes_| is zero, short |
154 // circuit here. | 137 // circuit here. |
155 if (!dest_size) { | 138 if (!dest_size) { |
156 *bytes_read = 0; | 139 *bytes_read = 0; |
157 return true; | 140 return true; |
158 } | 141 } |
159 | 142 |
160 int rv = stream_.Read(dest, dest_size, | 143 int rv = stream_.Read(dest, dest_size, |
161 base::Bind(&URLRequestFileJob::DidRead, | 144 base::Bind(&URLRequestFileJob::DidRead, |
162 base::Unretained(this))); | 145 weak_ptr_factory_.GetWeakPtr())); |
163 if (rv >= 0) { | 146 if (rv >= 0) { |
164 // Data is immediately available. | 147 // Data is immediately available. |
165 *bytes_read = rv; | 148 *bytes_read = rv; |
166 remaining_bytes_ -= rv; | 149 remaining_bytes_ -= rv; |
167 DCHECK_GE(remaining_bytes_, 0); | 150 DCHECK_GE(remaining_bytes_, 0); |
168 return true; | 151 return true; |
169 } | 152 } |
170 | 153 |
171 // Otherwise, a read error occured. We may just need to wait... | 154 // Otherwise, a read error occured. We may just need to wait... |
172 if (rv == ERR_IO_PENDING) { | 155 if (rv == ERR_IO_PENDING) { |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
214 #endif | 197 #endif |
215 } | 198 } |
216 | 199 |
217 Filter* URLRequestFileJob::SetupFilter() const { | 200 Filter* URLRequestFileJob::SetupFilter() const { |
218 // Bug 9936 - .svgz files needs to be decompressed. | 201 // Bug 9936 - .svgz files needs to be decompressed. |
219 return LowerCaseEqualsASCII(file_path_.Extension(), ".svgz") | 202 return LowerCaseEqualsASCII(file_path_.Extension(), ".svgz") |
220 ? Filter::GZipFactory() : NULL; | 203 ? Filter::GZipFactory() : NULL; |
221 } | 204 } |
222 | 205 |
223 bool URLRequestFileJob::GetMimeType(std::string* mime_type) const { | 206 bool URLRequestFileJob::GetMimeType(std::string* mime_type) const { |
224 // URL requests should not block on the disk! On Windows this goes to the | |
225 // registry. | |
226 // http://code.google.com/p/chromium/issues/detail?id=59849 | |
227 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
228 DCHECK(request_); | 207 DCHECK(request_); |
229 return GetMimeTypeFromFile(file_path_, mime_type); | 208 *mime_type = mime_type_; |
209 return mime_type_result_; | |
230 } | 210 } |
231 | 211 |
232 void URLRequestFileJob::SetExtraRequestHeaders( | 212 void URLRequestFileJob::SetExtraRequestHeaders( |
233 const HttpRequestHeaders& headers) { | 213 const HttpRequestHeaders& headers) { |
234 std::string range_header; | 214 std::string range_header; |
235 if (headers.GetHeader(HttpRequestHeaders::kRange, &range_header)) { | 215 if (headers.GetHeader(HttpRequestHeaders::kRange, &range_header)) { |
236 // We only care about "Range" header here. | 216 // We only care about "Range" header here. |
237 std::vector<HttpByteRange> ranges; | 217 std::vector<HttpByteRange> ranges; |
238 if (HttpUtil::ParseRangeHeader(range_header, &ranges)) { | 218 if (HttpUtil::ParseRangeHeader(range_header, &ranges)) { |
239 if (ranges.size() == 1) { | 219 if (ranges.size() == 1) { |
(...skipping 16 matching lines...) Expand all Loading... | |
256 const URLRequestContext* context = request.context(); | 236 const URLRequestContext* context = request.context(); |
257 if (!context) | 237 if (!context) |
258 return false; | 238 return false; |
259 const NetworkDelegate* delegate = context->network_delegate(); | 239 const NetworkDelegate* delegate = context->network_delegate(); |
260 if (delegate) | 240 if (delegate) |
261 return delegate->CanAccessFile(request, path); | 241 return delegate->CanAccessFile(request, path); |
262 return false; | 242 return false; |
263 } | 243 } |
264 | 244 |
265 URLRequestFileJob::~URLRequestFileJob() { | 245 URLRequestFileJob::~URLRequestFileJob() { |
266 DCHECK(!async_resolver_); | |
267 } | 246 } |
268 | 247 |
269 void URLRequestFileJob::DidResolve( | 248 void URLRequestFileJob::DidResolveAndMimeType( |
270 bool exists, const base::PlatformFileInfo& file_info) { | 249 const bool* exists, |
271 async_resolver_ = NULL; | 250 const base::PlatformFileInfo* file_info, |
251 const bool* mime_type_result, | |
252 const std::string* read_mime_type) { | |
253 is_directory_ = file_info->is_directory; | |
254 file_size_ = file_info->size; | |
255 mime_type_result_ = *mime_type_result; | |
256 mime_type_ = *read_mime_type; | |
272 | 257 |
273 // We may have been orphaned... | |
274 if (!request_) | |
275 return; | |
276 | |
277 is_directory_ = file_info.is_directory; | |
278 | |
279 int rv = OK; | |
280 // We use URLRequestFileJob to handle files as well as directories without | 258 // We use URLRequestFileJob to handle files as well as directories without |
281 // trailing slash. | 259 // trailing slash. |
282 // If a directory does not exist, we return ERR_FILE_NOT_FOUND. Otherwise, | 260 // If a directory does not exist, we return ERR_FILE_NOT_FOUND. Otherwise, |
283 // we will append trailing slash and redirect to FileDirJob. | 261 // we will append trailing slash and redirect to FileDirJob. |
284 // A special case is "\" on Windows. We should resolve as invalid. | 262 // A special case is "\" on Windows. We should resolve as invalid. |
285 // However, Windows resolves "\" to "C:\", thus reports it as existent. | 263 // However, Windows resolves "\" to "C:\", thus reports it as existent. |
286 // So what happens is we append it with trailing slash and redirect it to | 264 // So what happens is we append it with trailing slash and redirect it to |
287 // FileDirJob where it is resolved as invalid. | 265 // FileDirJob where it is resolved as invalid. |
288 if (!exists) { | 266 if (!(*exists)) { |
289 rv = ERR_FILE_NOT_FOUND; | 267 DidOpen(ERR_FILE_NOT_FOUND); |
290 } else if (!is_directory_) { | 268 return; |
291 // URL requests should not block on the disk! | |
292 // http://code.google.com/p/chromium/issues/detail?id=59849 | |
293 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
294 | |
295 int flags = base::PLATFORM_FILE_OPEN | | |
296 base::PLATFORM_FILE_READ | | |
297 base::PLATFORM_FILE_ASYNC; | |
298 rv = stream_.OpenSync(file_path_, flags); | |
299 } | 269 } |
300 | 270 if (is_directory_) { |
301 if (rv != OK) { | 271 DidOpen(OK); |
302 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); | |
303 return; | 272 return; |
304 } | 273 } |
305 | 274 |
306 if (!byte_range_.ComputeBounds(file_info.size)) { | 275 int flags = base::PLATFORM_FILE_OPEN | |
276 base::PLATFORM_FILE_READ | | |
277 base::PLATFORM_FILE_ASYNC; | |
278 int rv = stream_.Open(file_path_, flags, | |
279 base::Bind(&URLRequestFileJob::DidOpen, | |
280 weak_ptr_factory_.GetWeakPtr())); | |
281 if (rv != ERR_IO_PENDING) | |
282 DidOpen(rv); | |
283 } | |
284 | |
285 void URLRequestFileJob::DidOpen(int result) { | |
286 if (result != OK) { | |
287 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result)); | |
288 return; | |
289 } | |
290 | |
291 if (!byte_range_.ComputeBounds(file_size_)) { | |
307 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, | 292 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, |
308 ERR_REQUEST_RANGE_NOT_SATISFIABLE)); | 293 ERR_REQUEST_RANGE_NOT_SATISFIABLE)); |
309 return; | 294 return; |
310 } | 295 } |
311 | 296 |
312 remaining_bytes_ = byte_range_.last_byte_position() - | 297 remaining_bytes_ = byte_range_.last_byte_position() - |
313 byte_range_.first_byte_position() + 1; | 298 byte_range_.first_byte_position() + 1; |
314 DCHECK_GE(remaining_bytes_, 0); | 299 DCHECK_GE(remaining_bytes_, 0); |
315 | 300 |
316 // URL requests should not block on the disk! | 301 if (remaining_bytes_ > 0 && byte_range_.first_byte_position() != 0) { |
317 // http://code.google.com/p/chromium/issues/detail?id=59849 | 302 int rv = stream_.Seek(FROM_BEGIN, byte_range_.first_byte_position(), |
318 { | 303 base::Bind(&URLRequestFileJob::DidSeek, |
319 base::ThreadRestrictions::ScopedAllowIO allow_io; | 304 weak_ptr_factory_.GetWeakPtr())); |
320 // Do the seek at the beginning of the request. | 305 if (rv != ERR_IO_PENDING) |
321 if (remaining_bytes_ > 0 && | 306 DidSeek(-1); |
322 byte_range_.first_byte_position() != 0 && | 307 } else { |
323 byte_range_.first_byte_position() != | 308 DidSeek(byte_range_.first_byte_position()); |
324 stream_.SeekSync(FROM_BEGIN, byte_range_.first_byte_position())) { | 309 } |
325 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, | 310 } |
326 ERR_REQUEST_RANGE_NOT_SATISFIABLE)); | 311 |
327 return; | 312 void URLRequestFileJob::DidSeek(int64 result) { |
328 } | 313 if (result != byte_range_.first_byte_position()) { |
314 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, | |
315 ERR_REQUEST_RANGE_NOT_SATISFIABLE)); | |
316 return; | |
329 } | 317 } |
330 | 318 |
331 set_expected_content_size(remaining_bytes_); | 319 set_expected_content_size(remaining_bytes_); |
332 NotifyHeadersComplete(); | 320 NotifyHeadersComplete(); |
333 } | 321 } |
334 | 322 |
335 void URLRequestFileJob::DidRead(int result) { | 323 void URLRequestFileJob::DidRead(int result) { |
336 if (result > 0) { | 324 if (result > 0) { |
337 SetStatus(URLRequestStatus()); // Clear the IO_PENDING status | 325 SetStatus(URLRequestStatus()); // Clear the IO_PENDING status |
338 } else if (result == 0) { | 326 } else if (result == 0) { |
339 NotifyDone(URLRequestStatus()); | 327 NotifyDone(URLRequestStatus()); |
340 } else { | 328 } else { |
341 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result)); | 329 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result)); |
342 } | 330 } |
343 | 331 |
344 remaining_bytes_ -= result; | 332 remaining_bytes_ -= result; |
345 DCHECK_GE(remaining_bytes_, 0); | 333 DCHECK_GE(remaining_bytes_, 0); |
346 | 334 |
347 NotifyReadComplete(result); | 335 NotifyReadComplete(result); |
348 } | 336 } |
349 | 337 |
350 } // namespace net | 338 } // namespace net |
OLD | NEW |