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