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

Side by Side Diff: webkit/blob/blob_url_request_job.cc

Issue 6612051: In BlobURLRequestJob, open files asynchronously to avoid blocking the IO thread (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Added DCHECK, merged with trunk Created 9 years, 9 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 | Annotate | Revision Log
« no previous file with comments | « webkit/blob/blob_url_request_job.h ('k') | webkit/blob/blob_url_request_job_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) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 #include "webkit/blob/blob_url_request_job.h" 5 #include "webkit/blob/blob_url_request_job.h"
6 6
7 #include "base/compiler_specific.h" 7 #include "base/compiler_specific.h"
8 #include "base/file_path.h" 8 #include "base/file_path.h"
9 #include "base/file_util.h" 9 #include "base/file_util.h"
10 #include "base/file_util_proxy.h" 10 #include "base/file_util_proxy.h"
11 #include "base/message_loop.h" 11 #include "base/message_loop.h"
12 #include "base/message_loop_proxy.h" 12 #include "base/message_loop_proxy.h"
13 #include "base/string_number_conversions.h" 13 #include "base/string_number_conversions.h"
14 #include "base/threading/thread_restrictions.h" 14 #include "base/threading/thread_restrictions.h"
15 #include "net/base/file_stream.h"
15 #include "net/base/io_buffer.h" 16 #include "net/base/io_buffer.h"
16 #include "net/base/net_errors.h" 17 #include "net/base/net_errors.h"
17 #include "net/http/http_request_headers.h" 18 #include "net/http/http_request_headers.h"
18 #include "net/http/http_response_headers.h" 19 #include "net/http/http_response_headers.h"
19 #include "net/http/http_response_info.h" 20 #include "net/http/http_response_info.h"
20 #include "net/http/http_util.h" 21 #include "net/http/http_util.h"
21 #include "net/url_request/url_request.h" 22 #include "net/url_request/url_request.h"
22 #include "net/url_request/url_request_error_job.h" 23 #include "net/url_request/url_request_error_job.h"
23 #include "net/url_request/url_request_status.h" 24 #include "net/url_request/url_request_status.h"
24 25
25 namespace webkit_blob { 26 namespace webkit_blob {
26 27
27 static const int kHTTPOk = 200; 28 static const int kHTTPOk = 200;
28 static const int kHTTPPartialContent = 206; 29 static const int kHTTPPartialContent = 206;
29 static const int kHTTPNotAllowed = 403; 30 static const int kHTTPNotAllowed = 403;
30 static const int kHTTPNotFound = 404; 31 static const int kHTTPNotFound = 404;
31 static const int kHTTPMethodNotAllow = 405; 32 static const int kHTTPMethodNotAllow = 405;
32 static const int kHTTPRequestedRangeNotSatisfiable = 416; 33 static const int kHTTPRequestedRangeNotSatisfiable = 416;
33 static const int kHTTPInternalError = 500; 34 static const int kHTTPInternalError = 500;
34 35
35 static const char kHTTPOKText[] = "OK"; 36 static const char kHTTPOKText[] = "OK";
36 static const char kHTTPPartialContentText[] = "Partial Content"; 37 static const char kHTTPPartialContentText[] = "Partial Content";
37 static const char kHTTPNotAllowedText[] = "Not Allowed"; 38 static const char kHTTPNotAllowedText[] = "Not Allowed";
38 static const char kHTTPNotFoundText[] = "Not Found"; 39 static const char kHTTPNotFoundText[] = "Not Found";
39 static const char kHTTPMethodNotAllowText[] = "Method Not Allowed"; 40 static const char kHTTPMethodNotAllowText[] = "Method Not Allowed";
40 static const char kHTTPRequestedRangeNotSatisfiableText[] = 41 static const char kHTTPRequestedRangeNotSatisfiableText[] =
41 "Requested Range Not Satisfiable"; 42 "Requested Range Not Satisfiable";
42 static const char kHTTPInternalErrorText[] = "Internal Server Error"; 43 static const char kHTTPInternalErrorText[] = "Internal Server Error";
43 44
45 static const int kFileOpenFlags = base::PLATFORM_FILE_OPEN |
46 base::PLATFORM_FILE_READ |
47 base::PLATFORM_FILE_ASYNC;
48
44 BlobURLRequestJob::BlobURLRequestJob( 49 BlobURLRequestJob::BlobURLRequestJob(
45 net::URLRequest* request, 50 net::URLRequest* request,
46 BlobData* blob_data, 51 BlobData* blob_data,
47 base::MessageLoopProxy* file_thread_proxy) 52 base::MessageLoopProxy* file_thread_proxy)
48 : net::URLRequestJob(request), 53 : net::URLRequestJob(request),
49 callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), 54 callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
50 blob_data_(blob_data), 55 blob_data_(blob_data),
51 file_thread_proxy_(file_thread_proxy), 56 file_thread_proxy_(file_thread_proxy),
52 ALLOW_THIS_IN_INITIALIZER_LIST( 57 ALLOW_THIS_IN_INITIALIZER_LIST(
53 io_callback_(this, &BlobURLRequestJob::DidRead)), 58 io_callback_(this, &BlobURLRequestJob::DidRead)),
54 item_index_(0), 59 item_index_(0),
55 total_size_(0), 60 total_size_(0),
56 current_item_offset_(0), 61 current_item_offset_(0),
57 remaining_bytes_(0), 62 remaining_bytes_(0),
58 read_buf_offset_(0), 63 read_buf_offset_(0),
59 read_buf_size_(0), 64 read_buf_size_(0),
60 read_buf_remaining_bytes_(0), 65 read_buf_remaining_bytes_(0),
66 bytes_to_read_(0),
61 error_(false), 67 error_(false),
62 headers_set_(false), 68 headers_set_(false),
63 byte_range_set_(false), 69 byte_range_set_(false),
64 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { 70 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
71 DCHECK(file_thread_proxy_);
65 } 72 }
66 73
67 BlobURLRequestJob::~BlobURLRequestJob() { 74 BlobURLRequestJob::~BlobURLRequestJob() {
75 // FileStream's destructor won't close it for us because we passed in our own
76 // file handle.
77 CloseStream();
68 } 78 }
69 79
70 void BlobURLRequestJob::Start() { 80 void BlobURLRequestJob::Start() {
71 // Continue asynchronously. 81 // Continue asynchronously.
72 MessageLoop::current()->PostTask( 82 MessageLoop::current()->PostTask(
73 FROM_HERE, 83 FROM_HERE,
74 method_factory_.NewRunnableMethod(&BlobURLRequestJob::DidStart)); 84 method_factory_.NewRunnableMethod(&BlobURLRequestJob::DidStart));
75 } 85 }
76 86
77 void BlobURLRequestJob::DidStart() { 87 void BlobURLRequestJob::DidStart() {
78 // We only support GET request per the spec. 88 // We only support GET request per the spec.
79 if (request()->method() != "GET") { 89 if (request()->method() != "GET") {
80 NotifyFailure(net::ERR_METHOD_NOT_SUPPORTED); 90 NotifyFailure(net::ERR_METHOD_NOT_SUPPORTED);
81 return; 91 return;
82 } 92 }
83 93
84 // If the blob data is not present, bail out. 94 // If the blob data is not present, bail out.
85 if (!blob_data_) { 95 if (!blob_data_) {
86 NotifyFailure(net::ERR_FILE_NOT_FOUND); 96 NotifyFailure(net::ERR_FILE_NOT_FOUND);
87 return; 97 return;
88 } 98 }
89 99
90 CountSize(); 100 CountSize();
91 } 101 }
92 102
103 void BlobURLRequestJob::CloseStream() {
104 if (stream_ != NULL) {
105 stream_->Close();
106 stream_.reset(NULL);
107 }
108 }
109
93 void BlobURLRequestJob::Kill() { 110 void BlobURLRequestJob::Kill() {
94 stream_.Close(); 111 CloseStream();
95 112
96 net::URLRequestJob::Kill(); 113 net::URLRequestJob::Kill();
97 callback_factory_.RevokeAll(); 114 callback_factory_.RevokeAll();
98 method_factory_.RevokeAll(); 115 method_factory_.RevokeAll();
99 } 116 }
100 117
101 void BlobURLRequestJob::ResolveFile(const FilePath& file_path) { 118 void BlobURLRequestJob::ResolveFile(const FilePath& file_path) {
102 // If the file thread proxy is provided, we can use it get the file info. 119 base::FileUtilProxy::GetFileInfo(
103 if (file_thread_proxy_) { 120 file_thread_proxy_,
104 base::FileUtilProxy::GetFileInfo( 121 file_path,
105 file_thread_proxy_, 122 callback_factory_.NewCallback(&BlobURLRequestJob::DidResolve));
106 file_path,
107 callback_factory_.NewCallback(&BlobURLRequestJob::DidResolve));
108 return;
109 }
110
111 // Otherwise, we use current thread, i.e. IO thread, as this is the case when
112 // we run the unittest or test shell.
113 // TODO(jianli): Consider using the proxy of current thread.
114 base::PlatformFileInfo file_info;
115 bool exists = file_util::GetFileInfo(file_path, &file_info);
116
117 // Continue asynchronously.
118 MessageLoop::current()->PostTask(
119 FROM_HERE,
120 method_factory_.NewRunnableMethod(
121 &BlobURLRequestJob::DidResolve,
122 exists ? base::PLATFORM_FILE_OK : base::PLATFORM_FILE_ERROR_NOT_FOUND,
123 file_info));
124 } 123 }
125 124
126 void BlobURLRequestJob::DidResolve(base::PlatformFileError rv, 125 void BlobURLRequestJob::DidResolve(base::PlatformFileError rv,
127 const base::PlatformFileInfo& file_info) { 126 const base::PlatformFileInfo& file_info) {
128 // If an error occured, bail out. 127 // If an error occured, bail out.
129 if (rv == base::PLATFORM_FILE_ERROR_NOT_FOUND) { 128 if (rv == base::PLATFORM_FILE_ERROR_NOT_FOUND) {
130 NotifyFailure(net::ERR_FILE_NOT_FOUND); 129 NotifyFailure(net::ERR_FILE_NOT_FOUND);
131 return; 130 return;
132 } else if (rv != base::PLATFORM_FILE_OK) { 131 } else if (rv != base::PLATFORM_FILE_OK) {
133 NotifyFailure(net::ERR_FAILED); 132 NotifyFailure(net::ERR_FAILED);
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
248 // Read until we encounter an error or could not get the data immediately. 247 // Read until we encounter an error or could not get the data immediately.
249 while (remaining_bytes_ > 0 && read_buf_remaining_bytes_ > 0) { 248 while (remaining_bytes_ > 0 && read_buf_remaining_bytes_ > 0) {
250 if (!ReadItem()) 249 if (!ReadItem())
251 return false; 250 return false;
252 } 251 }
253 252
254 *bytes_read = ReadCompleted(); 253 *bytes_read = ReadCompleted();
255 return true; 254 return true;
256 } 255 }
257 256
257 int BlobURLRequestJob::ComputeBytesToRead() const {
258 int64 current_item_remaining_bytes =
259 item_length_list_[item_index_] - current_item_offset_;
260 int bytes_to_read = (read_buf_remaining_bytes_ > current_item_remaining_bytes)
261 ? static_cast<int>(current_item_remaining_bytes)
262 : read_buf_remaining_bytes_;
263 if (bytes_to_read > remaining_bytes_)
264 bytes_to_read = static_cast<int>(remaining_bytes_);
265 return bytes_to_read;
266 }
267
258 bool BlobURLRequestJob::ReadItem() { 268 bool BlobURLRequestJob::ReadItem() {
259 // Are we done with reading all the blob data? 269 // Are we done with reading all the blob data?
260 if (remaining_bytes_ == 0) 270 if (remaining_bytes_ == 0)
261 return true; 271 return true;
262 272
263 // If we get to the last item but still expect something to read, bail out 273 // If we get to the last item but still expect something to read, bail out
264 // since something is wrong. 274 // since something is wrong.
265 if (item_index_ >= blob_data_->items().size()) { 275 if (item_index_ >= blob_data_->items().size()) {
266 NotifyFailure(net::ERR_FAILED); 276 NotifyFailure(net::ERR_FAILED);
267 return false; 277 return false;
268 } 278 }
269 279
270 const BlobData::Item& item = blob_data_->items().at(item_index_);
271
272 // Compute the bytes to read for current item. 280 // Compute the bytes to read for current item.
273 int64 current_item_remaining_bytes = 281 bytes_to_read_ = ComputeBytesToRead();
274 item_length_list_[item_index_] - current_item_offset_;
275 int bytes_to_read = (read_buf_remaining_bytes_ > current_item_remaining_bytes)
276 ? static_cast<int>(current_item_remaining_bytes)
277 : read_buf_remaining_bytes_;
278 if (bytes_to_read > remaining_bytes_)
279 bytes_to_read = static_cast<int>(remaining_bytes_);
280 282
281 // If nothing to read for current item, advance to next item. 283 // If nothing to read for current item, advance to next item.
282 if (bytes_to_read == 0) { 284 if (bytes_to_read_ == 0) {
283 AdvanceItem(); 285 AdvanceItem();
284 return ReadItem(); 286 return ReadItem();
285 } 287 }
286 288
287 // Do the reading. 289 // Do the reading.
290 const BlobData::Item& item = blob_data_->items().at(item_index_);
288 switch (item.type()) { 291 switch (item.type()) {
289 case BlobData::TYPE_DATA: 292 case BlobData::TYPE_DATA:
290 return ReadBytes(item, bytes_to_read); 293 return ReadBytes(item);
291 case BlobData::TYPE_FILE: 294 case BlobData::TYPE_FILE:
292 return ReadFile(item, bytes_to_read); 295 return DispatchReadFile(item);
293 default: 296 default:
294 DCHECK(false); 297 DCHECK(false);
295 return false; 298 return false;
296 } 299 }
297 } 300 }
298 301
299 bool BlobURLRequestJob::ReadBytes(const BlobData::Item& item, 302 bool BlobURLRequestJob::ReadBytes(const BlobData::Item& item) {
300 int bytes_to_read) { 303 DCHECK(read_buf_remaining_bytes_ >= bytes_to_read_);
301 DCHECK(read_buf_remaining_bytes_ >= bytes_to_read);
302 304
303 memcpy(read_buf_->data() + read_buf_offset_, 305 memcpy(read_buf_->data() + read_buf_offset_,
304 &item.data().at(0) + item.offset() + current_item_offset_, 306 &item.data().at(0) + item.offset() + current_item_offset_,
305 bytes_to_read); 307 bytes_to_read_);
306 308
307 AdvanceBytesRead(bytes_to_read); 309 AdvanceBytesRead(bytes_to_read_);
308 return true; 310 return true;
309 } 311 }
310 312
311 bool BlobURLRequestJob::ReadFile(const BlobData::Item& item, 313 bool BlobURLRequestJob::DispatchReadFile(const BlobData::Item& item) {
312 int bytes_to_read) { 314 // If the stream already exists, keep reading from it.
313 DCHECK(read_buf_remaining_bytes_ >= bytes_to_read); 315 if (stream_ != NULL)
316 return ReadFile(item);
314 317
315 // Open the file if not yet. 318 base::FileUtilProxy::CreateOrOpen(
316 if (!stream_.IsOpen()) { 319 file_thread_proxy_, item.file_path(), kFileOpenFlags,
317 // stream_.Open() and stream_.Seek() block the IO thread. 320 callback_factory_.NewCallback(&BlobURLRequestJob::DidOpen));
318 // See http://crbug.com/75548. 321 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
322 return false;
323 }
324
325 void BlobURLRequestJob::DidOpen(base::PlatformFileError rv,
326 base::PassPlatformFile file,
327 bool created) {
328 if (rv != base::PLATFORM_FILE_OK) {
329 NotifyFailure(net::ERR_FAILED);
330 return;
331 }
332
333 DCHECK(!stream_.get());
334 stream_.reset(new net::FileStream(file.ReleaseValue(), kFileOpenFlags));
335
336 const BlobData::Item& item = blob_data_->items().at(item_index_);
337 {
338 // stream_.Seek() blocks the IO thread, see http://crbug.com/75548.
319 base::ThreadRestrictions::ScopedAllowIO allow_io; 339 base::ThreadRestrictions::ScopedAllowIO allow_io;
320 int rv = stream_.Open(item.file_path(), base::PLATFORM_FILE_OPEN | 340 int64 offset = current_item_offset_ + static_cast<int64>(item.offset());
321 base::PLATFORM_FILE_READ | base::PLATFORM_FILE_ASYNC); 341 if (offset > 0 && offset != stream_->Seek(net::FROM_BEGIN, offset)) {
322 if (rv != net::OK) {
323 NotifyFailure(net::ERR_FAILED); 342 NotifyFailure(net::ERR_FAILED);
324 return false; 343 return;
325 }
326
327 // Seek the file if needed.
328 int64 offset = current_item_offset_ + static_cast<int64>(item.offset());
329 if (offset > 0) {
330 if (offset != stream_.Seek(net::FROM_BEGIN, offset)) {
331 NotifyFailure(net::ERR_FAILED);
332 return false;
333 }
334 } 344 }
335 } 345 }
336 346
347 ReadFile(item);
348 }
349
350 bool BlobURLRequestJob::ReadFile(const BlobData::Item& item) {
351 DCHECK(stream_.get());
352 DCHECK(stream_->IsOpen());
353 DCHECK(read_buf_remaining_bytes_ >= bytes_to_read_);
354
337 // Start the asynchronous reading. 355 // Start the asynchronous reading.
338 int rv = stream_.Read(read_buf_->data() + read_buf_offset_, 356 int rv = stream_->Read(read_buf_->data() + read_buf_offset_,
339 bytes_to_read, 357 bytes_to_read_,
340 &io_callback_); 358 &io_callback_);
341 359
342 // If I/O pending error is returned, we just need to wait. 360 // If I/O pending error is returned, we just need to wait.
343 if (rv == net::ERR_IO_PENDING) { 361 if (rv == net::ERR_IO_PENDING) {
344 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0)); 362 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
345 return false; 363 return false;
346 } 364 }
347 365
348 // For all other errors, bail out. 366 // For all other errors, bail out.
349 if (rv < 0) { 367 if (rv < 0) {
350 NotifyFailure(net::ERR_FAILED); 368 NotifyFailure(net::ERR_FAILED);
351 return false; 369 return false;
352 } 370 }
353 371
354 // Otherwise, data is immediately available. 372 // Otherwise, data is immediately available.
355 AdvanceBytesRead(rv); 373 if (GetStatus().is_io_pending())
374 DidRead(rv);
375 else
376 AdvanceBytesRead(rv);
377
356 return true; 378 return true;
357 } 379 }
358 380
359 void BlobURLRequestJob::DidRead(int result) { 381 void BlobURLRequestJob::DidRead(int result) {
360 if (result < 0) { 382 if (result < 0) {
361 NotifyFailure(net::ERR_FAILED); 383 NotifyFailure(net::ERR_FAILED);
362 return; 384 return;
363 } 385 }
364 SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status 386 SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status
365 387
366 AdvanceBytesRead(result); 388 AdvanceBytesRead(result);
367 389
368 // If the read buffer is completely filled, we're done. 390 // If the read buffer is completely filled, we're done.
369 if (!read_buf_remaining_bytes_) { 391 if (!read_buf_remaining_bytes_) {
370 int bytes_read = ReadCompleted(); 392 int bytes_read = ReadCompleted();
371 NotifyReadComplete(bytes_read); 393 NotifyReadComplete(bytes_read);
372 return; 394 return;
373 } 395 }
374 396
375 // Otherwise, continue the reading. 397 // Otherwise, continue the reading.
376 int bytes_read = 0; 398 int bytes_read = 0;
377 if (ReadLoop(&bytes_read)) 399 if (ReadLoop(&bytes_read))
378 NotifyReadComplete(bytes_read); 400 NotifyReadComplete(bytes_read);
379 } 401 }
380 402
381 void BlobURLRequestJob::AdvanceItem() { 403 void BlobURLRequestJob::AdvanceItem() {
382 // Close the stream if the current item is a file. 404 // Close the stream if the current item is a file.
383 if (stream_.IsOpen()) 405 CloseStream();
384 stream_.Close();
385 406
386 // Advance to the next item. 407 // Advance to the next item.
387 item_index_++; 408 item_index_++;
388 current_item_offset_ = 0; 409 current_item_offset_ = 0;
389 } 410 }
390 411
391 void BlobURLRequestJob::AdvanceBytesRead(int result) { 412 void BlobURLRequestJob::AdvanceBytesRead(int result) {
392 DCHECK_GT(result, 0); 413 DCHECK_GT(result, 0);
393 414
394 // Do we finish reading the current item? 415 // Do we finish reading the current item?
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after
540 // We don't support multiple range requests in one single URL request, 561 // We don't support multiple range requests in one single URL request,
541 // because we need to do multipart encoding here. 562 // because we need to do multipart encoding here.
542 // TODO(jianli): Support multipart byte range requests. 563 // TODO(jianli): Support multipart byte range requests.
543 NotifyFailure(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE); 564 NotifyFailure(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
544 } 565 }
545 } 566 }
546 } 567 }
547 } 568 }
548 569
549 } // namespace webkit_blob 570 } // namespace webkit_blob
OLDNEW
« no previous file with comments | « webkit/blob/blob_url_request_job.h ('k') | webkit/blob/blob_url_request_job_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698