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

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

Powered by Google App Engine
This is Rietveld 408576698