OLD | NEW |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |