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

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, 1 month 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
« no previous file with comments | « net/url_request/url_request_file_job.h ('k') | net/url_request/url_request_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 27 matching lines...) Expand all
38 #include "net/http/http_util.h" 38 #include "net/http/http_util.h"
39 #include "net/url_request/url_request_error_job.h" 39 #include "net/url_request/url_request_error_job.h"
40 #include "net/url_request/url_request_file_dir_job.h" 40 #include "net/url_request/url_request_file_dir_job.h"
41 41
42 #if defined(OS_WIN) 42 #if defined(OS_WIN)
43 #include "base/win/shortcut.h" 43 #include "base/win/shortcut.h"
44 #endif 44 #endif
45 45
46 namespace net { 46 namespace net {
47 47
48 class URLRequestFileJob::AsyncResolver 48 URLRequestFileJob::FileMetaInfo::FileMetaInfo()
49 : public base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver> { 49 : file_size(0),
50 public: 50 mime_type_result(false),
51 explicit AsyncResolver(URLRequestFileJob* owner) 51 file_exists(false),
52 : owner_(owner), owner_loop_(MessageLoop::current()) { 52 is_directory(false) {
53 } 53 }
54
55 void Resolve(const FilePath& file_path) {
56 base::PlatformFileInfo file_info;
57 bool exists = file_util::GetFileInfo(file_path, &file_info);
58 base::AutoLock locked(lock_);
59 if (owner_loop_) {
60 owner_loop_->PostTask(
61 FROM_HERE,
62 base::Bind(&AsyncResolver::ReturnResults, this, exists, file_info));
63 }
64 }
65
66 void Cancel() {
67 owner_ = NULL;
68
69 base::AutoLock locked(lock_);
70 owner_loop_ = NULL;
71 }
72
73 private:
74 friend class base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver>;
75
76 ~AsyncResolver() {}
77
78 void ReturnResults(bool exists, const base::PlatformFileInfo& file_info) {
79 if (owner_)
80 owner_->DidResolve(exists, file_info);
81 }
82
83 URLRequestFileJob* owner_;
84
85 base::Lock lock_;
86 MessageLoop* owner_loop_;
87 };
88 54
89 URLRequestFileJob::URLRequestFileJob(URLRequest* request, 55 URLRequestFileJob::URLRequestFileJob(URLRequest* request,
90 NetworkDelegate* network_delegate, 56 NetworkDelegate* network_delegate,
91 const FilePath& file_path) 57 const FilePath& file_path)
92 : URLRequestJob(request, network_delegate), 58 : URLRequestJob(request, network_delegate),
93 file_path_(file_path), 59 file_path_(file_path),
94 is_directory_(false), 60 stream_(new FileStream(NULL)),
95 remaining_bytes_(0) { 61 remaining_bytes_(0),
62 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
96 } 63 }
97 64
98 // static 65 // static
99 URLRequestJob* URLRequestFileJob::Factory(URLRequest* request, 66 URLRequestJob* URLRequestFileJob::Factory(URLRequest* request,
100 NetworkDelegate* network_delegate, 67 NetworkDelegate* network_delegate,
101 const std::string& scheme) { 68 const std::string& scheme) {
102 FilePath file_path; 69 FilePath file_path;
103 const bool is_file = FileURLToFilePath(request->url(), &file_path); 70 const bool is_file = FileURLToFilePath(request->url(), &file_path);
104 71
105 // Check file access permissions. 72 // Check file access permissions.
(...skipping 11 matching lines...) Expand all
117 file_util::EndsWithSeparator(file_path) && 84 file_util::EndsWithSeparator(file_path) &&
118 file_path.IsAbsolute()) 85 file_path.IsAbsolute())
119 return new URLRequestFileDirJob(request, network_delegate, file_path); 86 return new URLRequestFileDirJob(request, network_delegate, file_path);
120 87
121 // Use a regular file request job for all non-directories (including invalid 88 // Use a regular file request job for all non-directories (including invalid
122 // file names). 89 // file names).
123 return new URLRequestFileJob(request, network_delegate, file_path); 90 return new URLRequestFileJob(request, network_delegate, file_path);
124 } 91 }
125 92
126 void URLRequestFileJob::Start() { 93 void URLRequestFileJob::Start() {
127 DCHECK(!async_resolver_); 94 FileMetaInfo* meta_info = new FileMetaInfo();
wtc 2012/11/19 19:48:01 The original form (without "()") should also be OK
128 async_resolver_ = new AsyncResolver(this); 95 base::WorkerPool::PostTaskAndReply(
129 base::WorkerPool::PostTask(
130 FROM_HERE, 96 FROM_HERE,
131 base::Bind(&AsyncResolver::Resolve, async_resolver_.get(), file_path_), 97 base::Bind(&URLRequestFileJob::FetchMetaInfo, file_path_,
98 base::Unretained(meta_info)),
99 base::Bind(&URLRequestFileJob::DidFetchMetaInfo,
100 weak_ptr_factory_.GetWeakPtr(),
101 base::Owned(meta_info)),
132 true); 102 true);
133 } 103 }
134 104
135 void URLRequestFileJob::Kill() { 105 void URLRequestFileJob::Kill() {
136 stream_.reset(); 106 stream_.reset();
137 107 weak_ptr_factory_.InvalidateWeakPtrs();
138 if (async_resolver_) {
139 async_resolver_->Cancel();
140 async_resolver_ = NULL;
141 }
142 108
143 URLRequestJob::Kill(); 109 URLRequestJob::Kill();
144 } 110 }
145 111
146 bool URLRequestFileJob::ReadRawData(IOBuffer* dest, int dest_size, 112 bool URLRequestFileJob::ReadRawData(IOBuffer* dest, int dest_size,
147 int *bytes_read) { 113 int *bytes_read) {
148 DCHECK_NE(dest_size, 0); 114 DCHECK_NE(dest_size, 0);
149 DCHECK(bytes_read); 115 DCHECK(bytes_read);
150 DCHECK_GE(remaining_bytes_, 0); 116 DCHECK_GE(remaining_bytes_, 0);
151 117
152 if (remaining_bytes_ < dest_size) 118 if (remaining_bytes_ < dest_size)
153 dest_size = static_cast<int>(remaining_bytes_); 119 dest_size = static_cast<int>(remaining_bytes_);
154 120
155 // If we should copy zero bytes because |remaining_bytes_| is zero, short 121 // If we should copy zero bytes because |remaining_bytes_| is zero, short
156 // circuit here. 122 // circuit here.
157 if (!dest_size) { 123 if (!dest_size) {
158 *bytes_read = 0; 124 *bytes_read = 0;
159 return true; 125 return true;
160 } 126 }
161 127
162 int rv = stream_->Read(dest, dest_size, 128 int rv = stream_->Read(dest, dest_size,
163 base::Bind(&URLRequestFileJob::DidRead, 129 base::Bind(&URLRequestFileJob::DidRead,
164 base::Unretained(this))); 130 weak_ptr_factory_.GetWeakPtr()));
165 if (rv >= 0) { 131 if (rv >= 0) {
166 // Data is immediately available. 132 // Data is immediately available.
167 *bytes_read = rv; 133 *bytes_read = rv;
168 remaining_bytes_ -= rv; 134 remaining_bytes_ -= rv;
169 DCHECK_GE(remaining_bytes_, 0); 135 DCHECK_GE(remaining_bytes_, 0);
170 return true; 136 return true;
171 } 137 }
172 138
173 // Otherwise, a read error occured. We may just need to wait... 139 // Otherwise, a read error occured. We may just need to wait...
174 if (rv == ERR_IO_PENDING) { 140 if (rv == ERR_IO_PENDING) {
175 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); 141 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
176 } else { 142 } else {
177 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); 143 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv));
178 } 144 }
179 return false; 145 return false;
180 } 146 }
181 147
182 bool URLRequestFileJob::IsRedirectResponse(GURL* location, 148 bool URLRequestFileJob::IsRedirectResponse(GURL* location,
183 int* http_status_code) { 149 int* http_status_code) {
184 if (is_directory_) { 150 if (meta_info_.is_directory) {
185 // This happens when we discovered the file is a directory, so needs a 151 // This happens when we discovered the file is a directory, so needs a
186 // slash at the end of the path. 152 // slash at the end of the path.
187 std::string new_path = request_->url().path(); 153 std::string new_path = request_->url().path();
188 new_path.push_back('/'); 154 new_path.push_back('/');
189 GURL::Replacements replacements; 155 GURL::Replacements replacements;
190 replacements.SetPathStr(new_path); 156 replacements.SetPathStr(new_path);
191 157
192 *location = request_->url().ReplaceComponents(replacements); 158 *location = request_->url().ReplaceComponents(replacements);
193 *http_status_code = 301; // simulate a permanent redirect 159 *http_status_code = 301; // simulate a permanent redirect
194 return true; 160 return true;
(...skipping 21 matching lines...) Expand all
216 #endif 182 #endif
217 } 183 }
218 184
219 Filter* URLRequestFileJob::SetupFilter() const { 185 Filter* URLRequestFileJob::SetupFilter() const {
220 // Bug 9936 - .svgz files needs to be decompressed. 186 // Bug 9936 - .svgz files needs to be decompressed.
221 return LowerCaseEqualsASCII(file_path_.Extension(), ".svgz") 187 return LowerCaseEqualsASCII(file_path_.Extension(), ".svgz")
222 ? Filter::GZipFactory() : NULL; 188 ? Filter::GZipFactory() : NULL;
223 } 189 }
224 190
225 bool URLRequestFileJob::GetMimeType(std::string* mime_type) const { 191 bool URLRequestFileJob::GetMimeType(std::string* mime_type) const {
226 // URL requests should not block on the disk! On Windows this goes to the
227 // registry.
228 // http://code.google.com/p/chromium/issues/detail?id=59849
229 base::ThreadRestrictions::ScopedAllowIO allow_io;
230 DCHECK(request_); 192 DCHECK(request_);
231 return GetMimeTypeFromFile(file_path_, mime_type); 193 if (meta_info_.mime_type_result) {
194 *mime_type = meta_info_.mime_type;
195 return true;
196 }
197 return false;
232 } 198 }
233 199
234 void URLRequestFileJob::SetExtraRequestHeaders( 200 void URLRequestFileJob::SetExtraRequestHeaders(
235 const HttpRequestHeaders& headers) { 201 const HttpRequestHeaders& headers) {
236 std::string range_header; 202 std::string range_header;
237 if (headers.GetHeader(HttpRequestHeaders::kRange, &range_header)) { 203 if (headers.GetHeader(HttpRequestHeaders::kRange, &range_header)) {
238 // We only care about "Range" header here. 204 // We only care about "Range" header here.
239 std::vector<HttpByteRange> ranges; 205 std::vector<HttpByteRange> ranges;
240 if (HttpUtil::ParseRangeHeader(range_header, &ranges)) { 206 if (HttpUtil::ParseRangeHeader(range_header, &ranges)) {
241 if (ranges.size() == 1) { 207 if (ranges.size() == 1) {
242 byte_range_ = ranges[0]; 208 byte_range_ = ranges[0];
243 } else { 209 } else {
244 // We don't support multiple range requests in one single URL request, 210 // We don't support multiple range requests in one single URL request,
245 // because we need to do multipart encoding here. 211 // because we need to do multipart encoding here.
246 // TODO(hclam): decide whether we want to support multiple range 212 // TODO(hclam): decide whether we want to support multiple range
247 // requests. 213 // requests.
248 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, 214 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
249 ERR_REQUEST_RANGE_NOT_SATISFIABLE)); 215 ERR_REQUEST_RANGE_NOT_SATISFIABLE));
250 } 216 }
251 } 217 }
252 } 218 }
253 } 219 }
254 220
255 URLRequestFileJob::~URLRequestFileJob() { 221 URLRequestFileJob::~URLRequestFileJob() {
256 DCHECK(!async_resolver_);
257 } 222 }
258 223
259 void URLRequestFileJob::DidResolve( 224 void URLRequestFileJob::FetchMetaInfo(const FilePath& file_path,
260 bool exists, const base::PlatformFileInfo& file_info) { 225 FileMetaInfo* meta_info) {
261 async_resolver_ = NULL; 226 base::PlatformFileInfo platform_info;
227 meta_info->file_exists = file_util::GetFileInfo(file_path, &platform_info);
228 if (meta_info->file_exists) {
229 meta_info->file_size = platform_info.size;
230 meta_info->is_directory = platform_info.is_directory;
231 }
232 // On Windows GetMimeTypeFromFile() goes to the registry. Thus it should be
233 // done in WorkerPool.
234 meta_info->mime_type_result = GetMimeTypeFromFile(file_path,
235 &meta_info->mime_type);
236 }
262 237
263 // We may have been orphaned... 238 void URLRequestFileJob::DidFetchMetaInfo(const FileMetaInfo* meta_info) {
264 if (!request_) 239 meta_info_ = *meta_info;
265 return;
wtc 2012/11/20 20:56:55 Perhaps the fix is to carry this check to the URLR
266 240
267 is_directory_ = file_info.is_directory;
268
269 int rv = OK;
270 // We use URLRequestFileJob to handle files as well as directories without 241 // We use URLRequestFileJob to handle files as well as directories without
271 // trailing slash. 242 // trailing slash.
272 // If a directory does not exist, we return ERR_FILE_NOT_FOUND. Otherwise, 243 // If a directory does not exist, we return ERR_FILE_NOT_FOUND. Otherwise,
273 // we will append trailing slash and redirect to FileDirJob. 244 // we will append trailing slash and redirect to FileDirJob.
274 // A special case is "\" on Windows. We should resolve as invalid. 245 // A special case is "\" on Windows. We should resolve as invalid.
275 // However, Windows resolves "\" to "C:\", thus reports it as existent. 246 // However, Windows resolves "\" to "C:\", thus reports it as existent.
276 // So what happens is we append it with trailing slash and redirect it to 247 // So what happens is we append it with trailing slash and redirect it to
277 // FileDirJob where it is resolved as invalid. 248 // FileDirJob where it is resolved as invalid.
278 if (!exists) { 249 if (!meta_info_.file_exists) {
279 rv = ERR_FILE_NOT_FOUND; 250 DidOpen(ERR_FILE_NOT_FOUND);
280 } else if (!is_directory_) { 251 return;
281 stream_.reset(new FileStream(NULL));
282
283 // URL requests should not block on the disk!
284 // http://code.google.com/p/chromium/issues/detail?id=59849
285 base::ThreadRestrictions::ScopedAllowIO allow_io;
286
287 int flags = base::PLATFORM_FILE_OPEN |
288 base::PLATFORM_FILE_READ |
289 base::PLATFORM_FILE_ASYNC;
290 rv = stream_->OpenSync(file_path_, flags);
291 } 252 }
292 253 if (meta_info_.is_directory) {
293 if (rv != OK) { 254 DidOpen(OK);
294 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv));
295 return; 255 return;
296 } 256 }
297 257
298 if (!byte_range_.ComputeBounds(file_info.size)) { 258 int flags = base::PLATFORM_FILE_OPEN |
259 base::PLATFORM_FILE_READ |
260 base::PLATFORM_FILE_ASYNC;
261 int rv = stream_->Open(file_path_, flags,
262 base::Bind(&URLRequestFileJob::DidOpen,
263 weak_ptr_factory_.GetWeakPtr()));
264 if (rv != ERR_IO_PENDING)
265 DidOpen(rv);
266 }
267
268 void URLRequestFileJob::DidOpen(int result) {
269 if (result != OK) {
270 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
271 return;
272 }
273
274 if (!byte_range_.ComputeBounds(meta_info_.file_size)) {
299 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, 275 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
300 ERR_REQUEST_RANGE_NOT_SATISFIABLE)); 276 ERR_REQUEST_RANGE_NOT_SATISFIABLE));
301 return; 277 return;
302 } 278 }
303 279
304 remaining_bytes_ = byte_range_.last_byte_position() - 280 remaining_bytes_ = byte_range_.last_byte_position() -
305 byte_range_.first_byte_position() + 1; 281 byte_range_.first_byte_position() + 1;
306 DCHECK_GE(remaining_bytes_, 0); 282 DCHECK_GE(remaining_bytes_, 0);
307 283
308 // URL requests should not block on the disk! 284 if (remaining_bytes_ > 0 && byte_range_.first_byte_position() != 0) {
309 // http://code.google.com/p/chromium/issues/detail?id=59849 285 int rv = stream_->Seek(FROM_BEGIN, byte_range_.first_byte_position(),
310 { 286 base::Bind(&URLRequestFileJob::DidSeek,
311 base::ThreadRestrictions::ScopedAllowIO allow_io; 287 weak_ptr_factory_.GetWeakPtr()));
312 // Do the seek at the beginning of the request. 288 if (rv != ERR_IO_PENDING) {
313 if (remaining_bytes_ > 0 && 289 // stream_->Seek() failed, so pass an intentionally erroneous value
314 byte_range_.first_byte_position() != 0 && 290 // into DidSeek().
315 byte_range_.first_byte_position() != 291 DidSeek(-1);
316 stream_->SeekSync(FROM_BEGIN, byte_range_.first_byte_position())) {
317 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
318 ERR_REQUEST_RANGE_NOT_SATISFIABLE));
319 return;
320 } 292 }
293 } else {
294 // We didn't need to call stream_->Seek() at all, so we pass to DidSeek()
295 // the value that would mean seek success. This way we skip the code
296 // handling seek failure.
297 DidSeek(byte_range_.first_byte_position());
298 }
299 }
300
301 void URLRequestFileJob::DidSeek(int64 result) {
302 if (result != byte_range_.first_byte_position()) {
303 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
304 ERR_REQUEST_RANGE_NOT_SATISFIABLE));
305 return;
321 } 306 }
322 307
323 set_expected_content_size(remaining_bytes_); 308 set_expected_content_size(remaining_bytes_);
324 NotifyHeadersComplete(); 309 NotifyHeadersComplete();
325 } 310 }
326 311
327 void URLRequestFileJob::DidRead(int result) { 312 void URLRequestFileJob::DidRead(int result) {
328 if (result > 0) { 313 if (result > 0) {
329 SetStatus(URLRequestStatus()); // Clear the IO_PENDING status 314 SetStatus(URLRequestStatus()); // Clear the IO_PENDING status
330 } else if (result == 0) { 315 } else if (result == 0) {
331 NotifyDone(URLRequestStatus()); 316 NotifyDone(URLRequestStatus());
332 } else { 317 } else {
333 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result)); 318 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
334 } 319 }
335 320
336 remaining_bytes_ -= result; 321 remaining_bytes_ -= result;
337 DCHECK_GE(remaining_bytes_, 0); 322 DCHECK_GE(remaining_bytes_, 0);
338 323
339 NotifyReadComplete(result); 324 NotifyReadComplete(result);
340 } 325 }
341 326
342 } // namespace net 327 } // namespace net
OLDNEW
« no previous file with comments | « net/url_request/url_request_file_job.h ('k') | net/url_request/url_request_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698