Upstream version 10.38.222.0
[platform/framework/web/crosswalk.git] / src / webkit / browser / blob / blob_url_request_job.cc
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
3 // found in the LICENSE file.
4
5 #include "webkit/browser/blob/blob_url_request_job.h"
6
7 #include <limits>
8
9 #include "base/basictypes.h"
10 #include "base/bind.h"
11 #include "base/compiler_specific.h"
12 #include "base/files/file_util_proxy.h"
13 #include "base/format_macros.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/message_loop/message_loop_proxy.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/stringprintf.h"
19 #include "net/base/io_buffer.h"
20 #include "net/base/net_errors.h"
21 #include "net/http/http_request_headers.h"
22 #include "net/http/http_response_headers.h"
23 #include "net/http/http_response_info.h"
24 #include "net/http/http_util.h"
25 #include "net/url_request/url_request.h"
26 #include "net/url_request/url_request_context.h"
27 #include "net/url_request/url_request_error_job.h"
28 #include "net/url_request/url_request_status.h"
29 #include "webkit/browser/blob/file_stream_reader.h"
30 #include "webkit/browser/fileapi/file_system_context.h"
31 #include "webkit/browser/fileapi/file_system_url.h"
32
33 namespace webkit_blob {
34
35 namespace {
36
37 bool IsFileType(BlobData::Item::Type type) {
38   switch (type) {
39     case BlobData::Item::TYPE_FILE:
40     case BlobData::Item::TYPE_FILE_FILESYSTEM:
41       return true;
42     default:
43       return false;
44   }
45 }
46
47 }  // namespace
48
49 BlobURLRequestJob::BlobURLRequestJob(
50     net::URLRequest* request,
51     net::NetworkDelegate* network_delegate,
52     BlobData* blob_data,
53     fileapi::FileSystemContext* file_system_context,
54     base::MessageLoopProxy* file_thread_proxy)
55     : net::URLRequestJob(request, network_delegate),
56       blob_data_(blob_data),
57       file_system_context_(file_system_context),
58       file_thread_proxy_(file_thread_proxy),
59       total_size_(0),
60       remaining_bytes_(0),
61       pending_get_file_info_count_(0),
62       current_item_index_(0),
63       current_item_offset_(0),
64       error_(false),
65       byte_range_set_(false),
66       weak_factory_(this) {
67   DCHECK(file_thread_proxy_.get());
68 }
69
70 void BlobURLRequestJob::Start() {
71   // Continue asynchronously.
72   base::MessageLoop::current()->PostTask(
73       FROM_HERE,
74       base::Bind(&BlobURLRequestJob::DidStart, weak_factory_.GetWeakPtr()));
75 }
76
77 void BlobURLRequestJob::Kill() {
78   DeleteCurrentFileReader();
79
80   net::URLRequestJob::Kill();
81   weak_factory_.InvalidateWeakPtrs();
82 }
83
84 bool BlobURLRequestJob::ReadRawData(net::IOBuffer* dest,
85                                     int dest_size,
86                                     int* bytes_read) {
87   DCHECK_NE(dest_size, 0);
88   DCHECK(bytes_read);
89   DCHECK_GE(remaining_bytes_, 0);
90
91   // Bail out immediately if we encounter an error.
92   if (error_) {
93     *bytes_read = 0;
94     return true;
95   }
96
97   if (remaining_bytes_ < dest_size)
98     dest_size = static_cast<int>(remaining_bytes_);
99
100   // If we should copy zero bytes because |remaining_bytes_| is zero, short
101   // circuit here.
102   if (!dest_size) {
103     *bytes_read = 0;
104     return true;
105   }
106
107   // Keep track of the buffer.
108   DCHECK(!read_buf_.get());
109   read_buf_ = new net::DrainableIOBuffer(dest, dest_size);
110
111   return ReadLoop(bytes_read);
112 }
113
114 bool BlobURLRequestJob::GetMimeType(std::string* mime_type) const {
115   if (!response_info_)
116     return false;
117
118   return response_info_->headers->GetMimeType(mime_type);
119 }
120
121 void BlobURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
122   if (response_info_)
123     *info = *response_info_;
124 }
125
126 int BlobURLRequestJob::GetResponseCode() const {
127   if (!response_info_)
128     return -1;
129
130   return response_info_->headers->response_code();
131 }
132
133 void BlobURLRequestJob::SetExtraRequestHeaders(
134     const net::HttpRequestHeaders& headers) {
135   std::string range_header;
136   if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) {
137     // We only care about "Range" header here.
138     std::vector<net::HttpByteRange> ranges;
139     if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) {
140       if (ranges.size() == 1) {
141         byte_range_set_ = true;
142         byte_range_ = ranges[0];
143       } else {
144         // We don't support multiple range requests in one single URL request,
145         // because we need to do multipart encoding here.
146         // TODO(jianli): Support multipart byte range requests.
147         NotifyFailure(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
148       }
149     }
150   }
151 }
152
153 BlobURLRequestJob::~BlobURLRequestJob() {
154   STLDeleteValues(&index_to_reader_);
155 }
156
157 void BlobURLRequestJob::DidStart() {
158   error_ = false;
159
160   // We only support GET request per the spec.
161   if (request()->method() != "GET") {
162     NotifyFailure(net::ERR_METHOD_NOT_SUPPORTED);
163     return;
164   }
165
166   // If the blob data is not present, bail out.
167   if (!blob_data_.get()) {
168     NotifyFailure(net::ERR_FILE_NOT_FOUND);
169     return;
170   }
171
172   CountSize();
173 }
174
175 bool BlobURLRequestJob::AddItemLength(size_t index, int64 item_length) {
176   if (item_length > kint64max - total_size_) {
177     NotifyFailure(net::ERR_FAILED);
178     return false;
179   }
180
181   // Cache the size and add it to the total size.
182   DCHECK_LT(index, item_length_list_.size());
183   item_length_list_[index] = item_length;
184   total_size_ += item_length;
185   return true;
186 }
187
188 void BlobURLRequestJob::CountSize() {
189   pending_get_file_info_count_ = 0;
190   total_size_ = 0;
191   item_length_list_.resize(blob_data_->items().size());
192
193   for (size_t i = 0; i < blob_data_->items().size(); ++i) {
194     const BlobData::Item& item = blob_data_->items().at(i);
195     if (IsFileType(item.type())) {
196       ++pending_get_file_info_count_;
197       GetFileStreamReader(i)->GetLength(
198           base::Bind(&BlobURLRequestJob::DidGetFileItemLength,
199                      weak_factory_.GetWeakPtr(), i));
200       continue;
201     }
202
203     if (!AddItemLength(i, item.length()))
204       return;
205   }
206
207   if (pending_get_file_info_count_ == 0)
208     DidCountSize(net::OK);
209 }
210
211 void BlobURLRequestJob::DidCountSize(int error) {
212   DCHECK(!error_);
213
214   // If an error occured, bail out.
215   if (error != net::OK) {
216     NotifyFailure(error);
217     return;
218   }
219
220   // Apply the range requirement.
221   if (!byte_range_.ComputeBounds(total_size_)) {
222     NotifyFailure(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
223     return;
224   }
225
226   remaining_bytes_ = byte_range_.last_byte_position() -
227                      byte_range_.first_byte_position() + 1;
228   DCHECK_GE(remaining_bytes_, 0);
229
230   // Do the seek at the beginning of the request.
231   if (byte_range_.first_byte_position())
232     Seek(byte_range_.first_byte_position());
233
234   NotifySuccess();
235 }
236
237 void BlobURLRequestJob::DidGetFileItemLength(size_t index, int64 result) {
238   // Do nothing if we have encountered an error.
239   if (error_)
240     return;
241
242   if (result == net::ERR_UPLOAD_FILE_CHANGED) {
243     NotifyFailure(net::ERR_FILE_NOT_FOUND);
244     return;
245   } else if (result < 0) {
246     NotifyFailure(result);
247     return;
248   }
249
250   DCHECK_LT(index, blob_data_->items().size());
251   const BlobData::Item& item = blob_data_->items().at(index);
252   DCHECK(IsFileType(item.type()));
253
254   uint64 file_length = result;
255   uint64 item_offset = item.offset();
256   uint64 item_length = item.length();
257
258   if (item_offset > file_length) {
259     NotifyFailure(net::ERR_FILE_NOT_FOUND);
260     return;
261   }
262
263   uint64 max_length = file_length - item_offset;
264
265   // If item length is -1, we need to use the file size being resolved
266   // in the real time.
267   if (item_length == static_cast<uint64>(-1)) {
268     item_length = max_length;
269   } else if (item_length > max_length) {
270     NotifyFailure(net::ERR_FILE_NOT_FOUND);
271     return;
272   }
273
274   if (!AddItemLength(index, item_length))
275     return;
276
277   if (--pending_get_file_info_count_ == 0)
278     DidCountSize(net::OK);
279 }
280
281 void BlobURLRequestJob::Seek(int64 offset) {
282   // Skip the initial items that are not in the range.
283   for (current_item_index_ = 0;
284        current_item_index_ < blob_data_->items().size() &&
285            offset >= item_length_list_[current_item_index_];
286        ++current_item_index_) {
287     offset -= item_length_list_[current_item_index_];
288   }
289
290   // Set the offset that need to jump to for the first item in the range.
291   current_item_offset_ = offset;
292
293   if (offset == 0)
294     return;
295
296   // Adjust the offset of the first stream if it is of file type.
297   const BlobData::Item& item = blob_data_->items().at(current_item_index_);
298   if (IsFileType(item.type())) {
299     DeleteCurrentFileReader();
300     CreateFileStreamReader(current_item_index_, offset);
301   }
302 }
303
304 bool BlobURLRequestJob::ReadItem() {
305   // Are we done with reading all the blob data?
306   if (remaining_bytes_ == 0)
307     return true;
308
309   // If we get to the last item but still expect something to read, bail out
310   // since something is wrong.
311   if (current_item_index_ >= blob_data_->items().size()) {
312     NotifyFailure(net::ERR_FAILED);
313     return false;
314   }
315
316   // Compute the bytes to read for current item.
317   int bytes_to_read = ComputeBytesToRead();
318
319   // If nothing to read for current item, advance to next item.
320   if (bytes_to_read == 0) {
321     AdvanceItem();
322     return ReadItem();
323   }
324
325   // Do the reading.
326   const BlobData::Item& item = blob_data_->items().at(current_item_index_);
327   if (item.type() == BlobData::Item::TYPE_BYTES)
328     return ReadBytesItem(item, bytes_to_read);
329   if (IsFileType(item.type())) {
330     return ReadFileItem(GetFileStreamReader(current_item_index_),
331                         bytes_to_read);
332   }
333   NOTREACHED();
334   return false;
335 }
336
337 void BlobURLRequestJob::AdvanceItem() {
338   // Close the file if the current item is a file.
339   DeleteCurrentFileReader();
340
341   // Advance to the next item.
342   current_item_index_++;
343   current_item_offset_ = 0;
344 }
345
346 void BlobURLRequestJob::AdvanceBytesRead(int result) {
347   DCHECK_GT(result, 0);
348
349   // Do we finish reading the current item?
350   current_item_offset_ += result;
351   if (current_item_offset_ == item_length_list_[current_item_index_])
352     AdvanceItem();
353
354   // Subtract the remaining bytes.
355   remaining_bytes_ -= result;
356   DCHECK_GE(remaining_bytes_, 0);
357
358   // Adjust the read buffer.
359   read_buf_->DidConsume(result);
360   DCHECK_GE(read_buf_->BytesRemaining(), 0);
361 }
362
363 bool BlobURLRequestJob::ReadBytesItem(const BlobData::Item& item,
364                                       int bytes_to_read) {
365   DCHECK_GE(read_buf_->BytesRemaining(), bytes_to_read);
366
367   memcpy(read_buf_->data(),
368          item.bytes() + item.offset() + current_item_offset_,
369          bytes_to_read);
370
371   AdvanceBytesRead(bytes_to_read);
372   return true;
373 }
374
375 bool BlobURLRequestJob::ReadFileItem(FileStreamReader* reader,
376                                      int bytes_to_read) {
377   DCHECK_GE(read_buf_->BytesRemaining(), bytes_to_read);
378   DCHECK(reader);
379   const int result = reader->Read(
380       read_buf_.get(),
381       bytes_to_read,
382       base::Bind(&BlobURLRequestJob::DidReadFile, base::Unretained(this)));
383   if (result >= 0) {
384     // Data is immediately available.
385     if (GetStatus().is_io_pending())
386       DidReadFile(result);
387     else
388       AdvanceBytesRead(result);
389     return true;
390   }
391   if (result == net::ERR_IO_PENDING)
392     SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
393   else
394     NotifyFailure(result);
395   return false;
396 }
397
398 void BlobURLRequestJob::DidReadFile(int result) {
399   if (result <= 0) {
400     NotifyFailure(net::ERR_FAILED);
401     return;
402   }
403   SetStatus(net::URLRequestStatus());  // Clear the IO_PENDING status
404
405   AdvanceBytesRead(result);
406
407   // If the read buffer is completely filled, we're done.
408   if (!read_buf_->BytesRemaining()) {
409     int bytes_read = BytesReadCompleted();
410     NotifyReadComplete(bytes_read);
411     return;
412   }
413
414   // Otherwise, continue the reading.
415   int bytes_read = 0;
416   if (ReadLoop(&bytes_read))
417     NotifyReadComplete(bytes_read);
418 }
419
420 void BlobURLRequestJob::DeleteCurrentFileReader() {
421   IndexToReaderMap::iterator found = index_to_reader_.find(current_item_index_);
422   if (found != index_to_reader_.end() && found->second) {
423     delete found->second;
424     index_to_reader_.erase(found);
425   }
426 }
427
428 int BlobURLRequestJob::BytesReadCompleted() {
429   int bytes_read = read_buf_->BytesConsumed();
430   read_buf_ = NULL;
431   return bytes_read;
432 }
433
434 int BlobURLRequestJob::ComputeBytesToRead() const {
435   int64 current_item_length = item_length_list_[current_item_index_];
436
437   int64 item_remaining = current_item_length - current_item_offset_;
438   int64 buf_remaining = read_buf_->BytesRemaining();
439   int64 max_remaining = std::numeric_limits<int>::max();
440
441   int64 min = std::min(std::min(std::min(item_remaining,
442                                          buf_remaining),
443                                          remaining_bytes_),
444                                          max_remaining);
445
446   return static_cast<int>(min);
447 }
448
449 bool BlobURLRequestJob::ReadLoop(int* bytes_read) {
450   // Read until we encounter an error or could not get the data immediately.
451   while (remaining_bytes_ > 0 && read_buf_->BytesRemaining() > 0) {
452     if (!ReadItem())
453       return false;
454   }
455
456   *bytes_read = BytesReadCompleted();
457   return true;
458 }
459
460 void BlobURLRequestJob::NotifySuccess() {
461   net::HttpStatusCode status_code = net::HTTP_OK;
462   if (byte_range_set_ && byte_range_.IsValid())
463     status_code = net::HTTP_PARTIAL_CONTENT;
464   HeadersCompleted(status_code);
465 }
466
467 void BlobURLRequestJob::NotifyFailure(int error_code) {
468   error_ = true;
469
470   // If we already return the headers on success, we can't change the headers
471   // now. Instead, we just error out.
472   if (response_info_) {
473     NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
474                                      error_code));
475     return;
476   }
477
478   net::HttpStatusCode status_code = net::HTTP_INTERNAL_SERVER_ERROR;
479   switch (error_code) {
480     case net::ERR_ACCESS_DENIED:
481       status_code = net::HTTP_FORBIDDEN;
482       break;
483     case net::ERR_FILE_NOT_FOUND:
484       status_code = net::HTTP_NOT_FOUND;
485       break;
486     case net::ERR_METHOD_NOT_SUPPORTED:
487       status_code = net::HTTP_METHOD_NOT_ALLOWED;
488       break;
489     case net::ERR_REQUEST_RANGE_NOT_SATISFIABLE:
490       status_code = net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE;
491       break;
492     case net::ERR_FAILED:
493       break;
494     default:
495       DCHECK(false);
496       break;
497   }
498   HeadersCompleted(status_code);
499 }
500
501 void BlobURLRequestJob::HeadersCompleted(net::HttpStatusCode status_code) {
502   std::string status("HTTP/1.1 ");
503   status.append(base::IntToString(status_code));
504   status.append(" ");
505   status.append(net::GetHttpReasonPhrase(status_code));
506   status.append("\0\0", 2);
507   net::HttpResponseHeaders* headers = new net::HttpResponseHeaders(status);
508
509   if (status_code == net::HTTP_OK || status_code == net::HTTP_PARTIAL_CONTENT) {
510     std::string content_length_header(net::HttpRequestHeaders::kContentLength);
511     content_length_header.append(": ");
512     content_length_header.append(base::Int64ToString(remaining_bytes_));
513     headers->AddHeader(content_length_header);
514     if (status_code == net::HTTP_PARTIAL_CONTENT) {
515       DCHECK(byte_range_set_);
516       DCHECK(byte_range_.IsValid());
517       std::string content_range_header(net::HttpResponseHeaders::kContentRange);
518       content_range_header.append(": bytes ");
519       content_range_header.append(base::StringPrintf(
520           "%" PRId64 "-%" PRId64,
521           byte_range_.first_byte_position(), byte_range_.last_byte_position()));
522       content_range_header.append("/");
523       content_range_header.append(base::StringPrintf("%" PRId64, total_size_));
524       headers->AddHeader(content_range_header);
525     }
526     if (!blob_data_->content_type().empty()) {
527       std::string content_type_header(net::HttpRequestHeaders::kContentType);
528       content_type_header.append(": ");
529       content_type_header.append(blob_data_->content_type());
530       headers->AddHeader(content_type_header);
531     }
532     if (!blob_data_->content_disposition().empty()) {
533       std::string content_disposition_header("Content-Disposition: ");
534       content_disposition_header.append(blob_data_->content_disposition());
535       headers->AddHeader(content_disposition_header);
536     }
537   }
538
539   response_info_.reset(new net::HttpResponseInfo());
540   response_info_->headers = headers;
541
542   set_expected_content_size(remaining_bytes_);
543
544   NotifyHeadersComplete();
545 }
546
547 FileStreamReader* BlobURLRequestJob::GetFileStreamReader(size_t index) {
548   DCHECK_LT(index, blob_data_->items().size());
549   const BlobData::Item& item = blob_data_->items().at(index);
550   if (!IsFileType(item.type()))
551     return NULL;
552   if (index_to_reader_.find(index) == index_to_reader_.end())
553     CreateFileStreamReader(index, 0);
554   DCHECK(index_to_reader_[index]);
555   return index_to_reader_[index];
556 }
557
558 void BlobURLRequestJob::CreateFileStreamReader(size_t index,
559                                                int64 additional_offset) {
560   DCHECK_LT(index, blob_data_->items().size());
561   const BlobData::Item& item = blob_data_->items().at(index);
562   DCHECK(IsFileType(item.type()));
563   DCHECK_EQ(0U, index_to_reader_.count(index));
564
565   FileStreamReader* reader = NULL;
566   switch (item.type()) {
567     case BlobData::Item::TYPE_FILE:
568       reader = FileStreamReader::CreateForLocalFile(
569           file_thread_proxy_.get(),
570           item.path(),
571           item.offset() + additional_offset,
572           item.expected_modification_time());
573       break;
574     case BlobData::Item::TYPE_FILE_FILESYSTEM:
575       reader = file_system_context_->CreateFileStreamReader(
576           fileapi::FileSystemURL(
577               file_system_context_->CrackURL(item.filesystem_url())),
578           item.offset() + additional_offset,
579           item.expected_modification_time()).release();
580       break;
581     default:
582       NOTREACHED();
583   }
584   DCHECK(reader);
585   index_to_reader_[index] = reader;
586 }
587
588 }  // namespace webkit_blob