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

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
46 : public base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver> {
47 public:
48 explicit AsyncResolver(URLRequestFileJob* owner)
49 : owner_(owner), owner_loop_(MessageLoop::current()) {
50 }
51
52 void Resolve(const FilePath& file_path) {
53 base::PlatformFileInfo file_info;
54 bool exists = file_util::GetFileInfo(file_path, &file_info);
55 base::AutoLock locked(lock_);
56 if (owner_loop_) {
57 owner_loop_->PostTask(
58 FROM_HERE,
59 base::Bind(&AsyncResolver::ReturnResults, this, exists, file_info));
60 }
61 }
62
63 void Cancel() {
64 owner_ = NULL;
65
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
86 URLRequestFileJob::URLRequestFileJob(URLRequest* request, 45 URLRequestFileJob::URLRequestFileJob(URLRequest* request,
87 const FilePath& file_path) 46 const FilePath& file_path)
88 : URLRequestJob(request, request->context()->network_delegate()), 47 : URLRequestJob(request, request->context()->network_delegate()),
89 file_path_(file_path), 48 file_path_(file_path),
90 stream_(NULL), 49 stream_(NULL),
91 is_directory_(false), 50 remaining_bytes_(0),
92 remaining_bytes_(0) { 51 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
52 meta_info_.file_exists_ = false;
53 meta_info_.file_size_ = -1;
54 meta_info_.is_directory_ = false;
55 meta_info_.mime_type_result_ = false;
wtc 2012/07/13 22:38:55 Please define a default constructor for the FileMe
93 } 56 }
94 57
95 // static 58 // static
96 URLRequestJob* URLRequestFileJob::Factory(URLRequest* request, 59 URLRequestJob* URLRequestFileJob::Factory(URLRequest* request,
97 const std::string& scheme) { 60 const std::string& scheme) {
98 FilePath file_path; 61 FilePath file_path;
99 const bool is_file = FileURLToFilePath(request->url(), &file_path); 62 const bool is_file = FileURLToFilePath(request->url(), &file_path);
100 63
101 // Check file access permissions. 64 // Check file access permissions.
102 if (!IsFileAccessAllowed(*request, file_path)) 65 if (!IsFileAccessAllowed(*request, file_path))
103 return new URLRequestErrorJob(request, ERR_ACCESS_DENIED); 66 return new URLRequestErrorJob(request, ERR_ACCESS_DENIED);
104 67
105 // We need to decide whether to create URLRequestFileJob for file access or 68 // We need to decide whether to create URLRequestFileJob for file access or
106 // URLRequestFileDirJob for directory access. To avoid accessing the 69 // URLRequestFileDirJob for directory access. To avoid accessing the
107 // filesystem, we only look at the path string here. 70 // filesystem, we only look at the path string here.
108 // The code in the URLRequestFileJob::Start() method discovers that a path, 71 // 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, 72 // which doesn't end with a slash, should really be treated as a directory,
110 // and it then redirects to the URLRequestFileDirJob. 73 // and it then redirects to the URLRequestFileDirJob.
111 if (is_file && 74 if (is_file &&
112 file_util::EndsWithSeparator(file_path) && 75 file_util::EndsWithSeparator(file_path) &&
113 file_path.IsAbsolute()) 76 file_path.IsAbsolute())
114 return new URLRequestFileDirJob(request, file_path); 77 return new URLRequestFileDirJob(request, file_path);
115 78
116 // Use a regular file request job for all non-directories (including invalid 79 // Use a regular file request job for all non-directories (including invalid
117 // file names). 80 // file names).
118 return new URLRequestFileJob(request, file_path); 81 return new URLRequestFileJob(request, file_path);
119 } 82 }
120 83
121 void URLRequestFileJob::Start() { 84 void URLRequestFileJob::Start() {
122 DCHECK(!async_resolver_); 85 FileMetaInfo* meta_info = new FileMetaInfo;
wtc 2012/07/13 22:38:55 Will meta_info be deleted by the WorkerPool becaus
pivanof 2012/07/13 23:51:21 Yes, that was my intention.
123 async_resolver_ = new AsyncResolver(this); 86 base::WorkerPool::PostTaskAndReply(
124 base::WorkerPool::PostTask(
125 FROM_HERE, 87 FROM_HERE,
126 base::Bind(&AsyncResolver::Resolve, async_resolver_.get(), file_path_), 88 base::Bind(&URLRequestFileJob::FetchMetaInfo, file_path_,
89 base::Unretained(meta_info)),
90 base::Bind(&URLRequestFileJob::DidFetchMetaInfo,
91 weak_ptr_factory_.GetWeakPtr(),
92 base::Owned(meta_info)),
127 true); 93 true);
128 } 94 }
129 95
130 void URLRequestFileJob::Kill() { 96 void URLRequestFileJob::Kill() {
131 // URL requests should not block on the disk! 97 stream_.CloseAndCancelAsync();
132 // http://code.google.com/p/chromium/issues/detail?id=59849 98 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 99
141 URLRequestJob::Kill(); 100 URLRequestJob::Kill();
142 } 101 }
143 102
144 bool URLRequestFileJob::ReadRawData(IOBuffer* dest, int dest_size, 103 bool URLRequestFileJob::ReadRawData(IOBuffer* dest, int dest_size,
145 int *bytes_read) { 104 int *bytes_read) {
146 DCHECK_NE(dest_size, 0); 105 DCHECK_NE(dest_size, 0);
147 DCHECK(bytes_read); 106 DCHECK(bytes_read);
148 DCHECK_GE(remaining_bytes_, 0); 107 DCHECK_GE(remaining_bytes_, 0);
149 108
150 if (remaining_bytes_ < dest_size) 109 if (remaining_bytes_ < dest_size)
151 dest_size = static_cast<int>(remaining_bytes_); 110 dest_size = static_cast<int>(remaining_bytes_);
152 111
153 // If we should copy zero bytes because |remaining_bytes_| is zero, short 112 // If we should copy zero bytes because |remaining_bytes_| is zero, short
154 // circuit here. 113 // circuit here.
155 if (!dest_size) { 114 if (!dest_size) {
156 *bytes_read = 0; 115 *bytes_read = 0;
157 return true; 116 return true;
158 } 117 }
159 118
160 int rv = stream_.Read(dest, dest_size, 119 int rv = stream_.Read(dest, dest_size,
161 base::Bind(&URLRequestFileJob::DidRead, 120 base::Bind(&URLRequestFileJob::DidRead,
162 base::Unretained(this))); 121 weak_ptr_factory_.GetWeakPtr()));
163 if (rv >= 0) { 122 if (rv >= 0) {
164 // Data is immediately available. 123 // Data is immediately available.
165 *bytes_read = rv; 124 *bytes_read = rv;
166 remaining_bytes_ -= rv; 125 remaining_bytes_ -= rv;
167 DCHECK_GE(remaining_bytes_, 0); 126 DCHECK_GE(remaining_bytes_, 0);
168 return true; 127 return true;
169 } 128 }
170 129
171 // Otherwise, a read error occured. We may just need to wait... 130 // Otherwise, a read error occured. We may just need to wait...
172 if (rv == ERR_IO_PENDING) { 131 if (rv == ERR_IO_PENDING) {
173 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); 132 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
174 } else { 133 } else {
175 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); 134 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv));
176 } 135 }
177 return false; 136 return false;
178 } 137 }
179 138
180 bool URLRequestFileJob::IsRedirectResponse(GURL* location, 139 bool URLRequestFileJob::IsRedirectResponse(GURL* location,
181 int* http_status_code) { 140 int* http_status_code) {
182 if (is_directory_) { 141 if (meta_info_.is_directory_) {
183 // This happens when we discovered the file is a directory, so needs a 142 // This happens when we discovered the file is a directory, so needs a
184 // slash at the end of the path. 143 // slash at the end of the path.
185 std::string new_path = request_->url().path(); 144 std::string new_path = request_->url().path();
186 new_path.push_back('/'); 145 new_path.push_back('/');
187 GURL::Replacements replacements; 146 GURL::Replacements replacements;
188 replacements.SetPathStr(new_path); 147 replacements.SetPathStr(new_path);
189 148
190 *location = request_->url().ReplaceComponents(replacements); 149 *location = request_->url().ReplaceComponents(replacements);
191 *http_status_code = 301; // simulate a permanent redirect 150 *http_status_code = 301; // simulate a permanent redirect
192 return true; 151 return true;
(...skipping 21 matching lines...) Expand all
214 #endif 173 #endif
215 } 174 }
216 175
217 Filter* URLRequestFileJob::SetupFilter() const { 176 Filter* URLRequestFileJob::SetupFilter() const {
218 // Bug 9936 - .svgz files needs to be decompressed. 177 // Bug 9936 - .svgz files needs to be decompressed.
219 return LowerCaseEqualsASCII(file_path_.Extension(), ".svgz") 178 return LowerCaseEqualsASCII(file_path_.Extension(), ".svgz")
220 ? Filter::GZipFactory() : NULL; 179 ? Filter::GZipFactory() : NULL;
221 } 180 }
222 181
223 bool URLRequestFileJob::GetMimeType(std::string* mime_type) const { 182 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_); 183 DCHECK(request_);
229 return GetMimeTypeFromFile(file_path_, mime_type); 184 *mime_type = meta_info_.mime_type_;
wtc 2012/07/13 22:38:55 Nit: it seems that we only need to set *mime_type
pivanof 2012/07/13 23:51:21 I've also thought like that. But just in case of m
185 return meta_info_.mime_type_result_;
230 } 186 }
231 187
232 void URLRequestFileJob::SetExtraRequestHeaders( 188 void URLRequestFileJob::SetExtraRequestHeaders(
233 const HttpRequestHeaders& headers) { 189 const HttpRequestHeaders& headers) {
234 std::string range_header; 190 std::string range_header;
235 if (headers.GetHeader(HttpRequestHeaders::kRange, &range_header)) { 191 if (headers.GetHeader(HttpRequestHeaders::kRange, &range_header)) {
236 // We only care about "Range" header here. 192 // We only care about "Range" header here.
237 std::vector<HttpByteRange> ranges; 193 std::vector<HttpByteRange> ranges;
238 if (HttpUtil::ParseRangeHeader(range_header, &ranges)) { 194 if (HttpUtil::ParseRangeHeader(range_header, &ranges)) {
239 if (ranges.size() == 1) { 195 if (ranges.size() == 1) {
(...skipping 16 matching lines...) Expand all
256 const URLRequestContext* context = request.context(); 212 const URLRequestContext* context = request.context();
257 if (!context) 213 if (!context)
258 return false; 214 return false;
259 const NetworkDelegate* delegate = context->network_delegate(); 215 const NetworkDelegate* delegate = context->network_delegate();
260 if (delegate) 216 if (delegate)
261 return delegate->CanAccessFile(request, path); 217 return delegate->CanAccessFile(request, path);
262 return false; 218 return false;
263 } 219 }
264 220
265 URLRequestFileJob::~URLRequestFileJob() { 221 URLRequestFileJob::~URLRequestFileJob() {
266 DCHECK(!async_resolver_);
267 } 222 }
268 223
269 void URLRequestFileJob::DidResolve( 224 void URLRequestFileJob::FetchMetaInfo(const FilePath& file_path,
270 bool exists, const base::PlatformFileInfo& file_info) { 225 FileMetaInfo* meta_info) {
271 async_resolver_ = NULL; 226 base::PlatformFileInfo platform_info;
227 meta_info->file_exists_ = file_util::GetFileInfo(file_path, &platform_info);
228 meta_info->file_size_ = platform_info.size;
229 meta_info->is_directory_ = platform_info.is_directory;
230 // On Windows GetMimeTypeFromFile() goes to the registry. Thus it should be
231 // done in WorkerPool.
232 meta_info->mime_type_result_ = GetMimeTypeFromFile(file_path,
233 &meta_info->mime_type_);
234 }
272 235
273 // We may have been orphaned... 236 void URLRequestFileJob::DidFetchMetaInfo(const FileMetaInfo* meta_info) {
274 if (!request_) 237 meta_info_ = *meta_info;
275 return;
276 238
277 is_directory_ = file_info.is_directory;
278
279 int rv = OK;
280 // We use URLRequestFileJob to handle files as well as directories without 239 // We use URLRequestFileJob to handle files as well as directories without
281 // trailing slash. 240 // trailing slash.
282 // If a directory does not exist, we return ERR_FILE_NOT_FOUND. Otherwise, 241 // If a directory does not exist, we return ERR_FILE_NOT_FOUND. Otherwise,
283 // we will append trailing slash and redirect to FileDirJob. 242 // we will append trailing slash and redirect to FileDirJob.
284 // A special case is "\" on Windows. We should resolve as invalid. 243 // A special case is "\" on Windows. We should resolve as invalid.
285 // However, Windows resolves "\" to "C:\", thus reports it as existent. 244 // 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 245 // So what happens is we append it with trailing slash and redirect it to
287 // FileDirJob where it is resolved as invalid. 246 // FileDirJob where it is resolved as invalid.
288 if (!exists) { 247 if (!meta_info_.file_exists_) {
289 rv = ERR_FILE_NOT_FOUND; 248 DidOpen(ERR_FILE_NOT_FOUND);
290 } else if (!is_directory_) { 249 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 } 250 }
300 251 if (meta_info_.is_directory_) {
301 if (rv != OK) { 252 DidOpen(OK);
302 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv));
303 return; 253 return;
304 } 254 }
305 255
306 if (!byte_range_.ComputeBounds(file_info.size)) { 256 int flags = base::PLATFORM_FILE_OPEN |
257 base::PLATFORM_FILE_READ |
258 base::PLATFORM_FILE_ASYNC;
259 int rv = stream_.Open(file_path_, flags,
260 base::Bind(&URLRequestFileJob::DidOpen,
261 weak_ptr_factory_.GetWeakPtr()));
262 if (rv != ERR_IO_PENDING)
263 DidOpen(rv);
264 }
265
266 void URLRequestFileJob::DidOpen(int result) {
267 if (result != OK) {
268 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
269 return;
270 }
271
272 if (!byte_range_.ComputeBounds(meta_info_.file_size_)) {
307 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, 273 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
308 ERR_REQUEST_RANGE_NOT_SATISFIABLE)); 274 ERR_REQUEST_RANGE_NOT_SATISFIABLE));
309 return; 275 return;
310 } 276 }
311 277
312 remaining_bytes_ = byte_range_.last_byte_position() - 278 remaining_bytes_ = byte_range_.last_byte_position() -
313 byte_range_.first_byte_position() + 1; 279 byte_range_.first_byte_position() + 1;
314 DCHECK_GE(remaining_bytes_, 0); 280 DCHECK_GE(remaining_bytes_, 0);
315 281
316 // URL requests should not block on the disk! 282 if (remaining_bytes_ > 0 && byte_range_.first_byte_position() != 0) {
317 // http://code.google.com/p/chromium/issues/detail?id=59849 283 int rv = stream_.Seek(FROM_BEGIN, byte_range_.first_byte_position(),
318 { 284 base::Bind(&URLRequestFileJob::DidSeek,
319 base::ThreadRestrictions::ScopedAllowIO allow_io; 285 weak_ptr_factory_.GetWeakPtr()));
320 // Do the seek at the beginning of the request. 286 if (rv != ERR_IO_PENDING)
321 if (remaining_bytes_ > 0 && 287 DidSeek(-1);
wtc 2012/07/13 22:38:55 BUG?: Why do we pass -1 to DidSeek here? It seems
pivanof 2012/07/13 23:51:21 -1 is to force DidSeek to go to the path of error
pivanof 2012/07/14 02:28:45 Just noticed that comment to FileStream::Seek() sa
322 byte_range_.first_byte_position() != 0 && 288 } else {
323 byte_range_.first_byte_position() != 289 DidSeek(byte_range_.first_byte_position());
wtc 2012/07/13 22:38:55 This DidSeek call is a little hard to understand b
pivanof 2012/07/13 23:51:21 Is adding a comment explaining that would be good
324 stream_.SeekSync(FROM_BEGIN, byte_range_.first_byte_position())) { 290 }
325 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, 291 }
326 ERR_REQUEST_RANGE_NOT_SATISFIABLE)); 292
327 return; 293 void URLRequestFileJob::DidSeek(int64 result) {
328 } 294 if (result != byte_range_.first_byte_position()) {
295 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
296 ERR_REQUEST_RANGE_NOT_SATISFIABLE));
297 return;
329 } 298 }
330 299
331 set_expected_content_size(remaining_bytes_); 300 set_expected_content_size(remaining_bytes_);
332 NotifyHeadersComplete(); 301 NotifyHeadersComplete();
333 } 302 }
334 303
335 void URLRequestFileJob::DidRead(int result) { 304 void URLRequestFileJob::DidRead(int result) {
336 if (result > 0) { 305 if (result > 0) {
337 SetStatus(URLRequestStatus()); // Clear the IO_PENDING status 306 SetStatus(URLRequestStatus()); // Clear the IO_PENDING status
338 } else if (result == 0) { 307 } else if (result == 0) {
339 NotifyDone(URLRequestStatus()); 308 NotifyDone(URLRequestStatus());
340 } else { 309 } else {
341 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result)); 310 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
342 } 311 }
343 312
344 remaining_bytes_ -= result; 313 remaining_bytes_ -= result;
345 DCHECK_GE(remaining_bytes_, 0); 314 DCHECK_GE(remaining_bytes_, 0);
346 315
347 NotifyReadComplete(result); 316 NotifyReadComplete(result);
348 } 317 }
349 318
350 } // namespace net 319 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698