Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(253)

Side by Side Diff: net/url_request/url_request_file_job.cc

Issue 10695110: Avoid disk accesses on the wrong thread in URLRequestFileJob (Closed) Base URL: https://src.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698