Home | History | Annotate | Download | only in drive
      1 // Copyright 2013 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 "chrome/browser/chromeos/drive/drive_url_request_job.h"
      6 
      7 #include <string>
      8 
      9 #include "base/bind.h"
     10 #include "base/logging.h"
     11 #include "base/memory/scoped_ptr.h"
     12 #include "chrome/browser/chromeos/drive/drive.pb.h"
     13 #include "chrome/browser/chromeos/drive/drive_file_stream_reader.h"
     14 #include "chrome/browser/chromeos/drive/file_system_interface.h"
     15 #include "chrome/browser/chromeos/drive/file_system_util.h"
     16 #include "content/public/browser/browser_thread.h"
     17 #include "net/base/net_errors.h"
     18 #include "net/http/http_byte_range.h"
     19 #include "net/http/http_request_headers.h"
     20 #include "net/http/http_response_info.h"
     21 #include "net/http/http_util.h"
     22 #include "net/url_request/url_request.h"
     23 #include "net/url_request/url_request_status.h"
     24 
     25 using content::BrowserThread;
     26 
     27 namespace drive {
     28 namespace {
     29 
     30 struct MimeTypeReplacement {
     31   const char* original_type;
     32   const char* new_type;
     33 };
     34 
     35 const MimeTypeReplacement kMimeTypeReplacements[] = {
     36   {"message/rfc822", "multipart/related"}  // Fixes MHTML
     37 };
     38 
     39 std::string FixupMimeType(const std::string& type) {
     40   for (size_t i = 0; i < arraysize(kMimeTypeReplacements); i++) {
     41     if (type == kMimeTypeReplacements[i].original_type)
     42       return kMimeTypeReplacements[i].new_type;
     43   }
     44   return type;
     45 }
     46 
     47 }  // namespace
     48 
     49 DriveURLRequestJob::DriveURLRequestJob(
     50     const FileSystemGetter& file_system_getter,
     51     base::SequencedTaskRunner* file_task_runner,
     52     net::URLRequest* request,
     53     net::NetworkDelegate* network_delegate)
     54     : net::URLRequestJob(request, network_delegate),
     55       file_system_getter_(file_system_getter),
     56       file_task_runner_(file_task_runner),
     57       weak_ptr_factory_(this) {
     58 }
     59 
     60 void DriveURLRequestJob::SetExtraRequestHeaders(
     61     const net::HttpRequestHeaders& headers) {
     62   std::string range_header;
     63   if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) {
     64     // Note: We only support single range requests.
     65     std::vector<net::HttpByteRange> ranges;
     66     if (net::HttpUtil::ParseRangeHeader(range_header, &ranges) &&
     67         ranges.size() == 1) {
     68       byte_range_ = ranges[0];
     69     } else {
     70       // Failed to parse Range: header, so notify the error.
     71       NotifyDone(
     72           net::URLRequestStatus(net::URLRequestStatus::FAILED,
     73                                 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
     74     }
     75   }
     76 }
     77 
     78 void DriveURLRequestJob::Start() {
     79   DVLOG(1) << "Starting request";
     80   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     81   DCHECK(!stream_reader_);
     82 
     83   // We only support GET request.
     84   if (request()->method() != "GET") {
     85     LOG(WARNING) << "Failed to start request: "
     86                  << request()->method() << " method is not supported";
     87     NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
     88                                            net::ERR_METHOD_NOT_SUPPORTED));
     89     return;
     90   }
     91 
     92   base::FilePath drive_file_path(util::DriveURLToFilePath(request_->url()));
     93   if (drive_file_path.empty()) {
     94     // Not a valid url.
     95     NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
     96                                            net::ERR_INVALID_URL));
     97     return;
     98   }
     99 
    100   // Initialize the stream reader.
    101   stream_reader_.reset(
    102       new DriveFileStreamReader(file_system_getter_, file_task_runner_.get()));
    103   stream_reader_->Initialize(
    104       drive_file_path,
    105       byte_range_,
    106       base::Bind(&DriveURLRequestJob::OnDriveFileStreamReaderInitialized,
    107                  weak_ptr_factory_.GetWeakPtr()));
    108 }
    109 
    110 void DriveURLRequestJob::Kill() {
    111   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    112 
    113   stream_reader_.reset();
    114   net::URLRequestJob::Kill();
    115   weak_ptr_factory_.InvalidateWeakPtrs();
    116 }
    117 
    118 bool DriveURLRequestJob::GetMimeType(std::string* mime_type) const {
    119   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    120 
    121   if (!entry_) {
    122     return false;
    123   }
    124 
    125   mime_type->assign(
    126       FixupMimeType(entry_->file_specific_info().content_mime_type()));
    127   return !mime_type->empty();
    128 }
    129 
    130 bool DriveURLRequestJob::IsRedirectResponse(
    131     GURL* location, int* http_status_code) {
    132   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    133 
    134   if (!entry_ || !entry_->file_specific_info().is_hosted_document()) {
    135     return false;
    136   }
    137 
    138   // Redirect a hosted document.
    139   *location = GURL(entry_->file_specific_info().alternate_url());
    140   const int kHttpFound = 302;
    141   *http_status_code = kHttpFound;
    142   return true;
    143 }
    144 
    145 bool DriveURLRequestJob::ReadRawData(
    146     net::IOBuffer* buf, int buf_size, int* bytes_read) {
    147   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    148   DCHECK(stream_reader_ && stream_reader_->IsInitialized());
    149 
    150   int result = stream_reader_->Read(
    151       buf, buf_size,
    152       base::Bind(&DriveURLRequestJob::OnReadCompleted,
    153                  weak_ptr_factory_.GetWeakPtr()));
    154 
    155   if (result == net::ERR_IO_PENDING) {
    156     // The data is not yet available.
    157     SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
    158     return false;
    159   }
    160   if (result < 0) {
    161     // An error occurs.
    162     NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, result));
    163     return false;
    164   }
    165 
    166   // Reading has been finished immediately.
    167   *bytes_read = result;
    168   return true;
    169 }
    170 
    171 DriveURLRequestJob::~DriveURLRequestJob() {
    172 }
    173 
    174 void DriveURLRequestJob::OnDriveFileStreamReaderInitialized(
    175     int error, scoped_ptr<ResourceEntry> entry) {
    176   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    177   DCHECK(stream_reader_);
    178 
    179   if (error != FILE_ERROR_OK) {
    180     NotifyStartError(
    181         net::URLRequestStatus(net::URLRequestStatus::FAILED, error));
    182     return;
    183   }
    184 
    185   DCHECK(entry && entry->has_file_specific_info());
    186   entry_ = entry.Pass();
    187 
    188   if (!entry_->file_specific_info().is_hosted_document()) {
    189     // We don't need to set content size for hosted documents,
    190     // because it will be redirected.
    191     set_expected_content_size(entry_->file_info().size());
    192   }
    193 
    194   NotifyHeadersComplete();
    195 }
    196 
    197 void DriveURLRequestJob::OnReadCompleted(int read_result) {
    198   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    199 
    200   if (read_result < 0) {
    201     DCHECK_NE(read_result, net::ERR_IO_PENDING);
    202     NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
    203                                      read_result));
    204   }
    205 
    206   SetStatus(net::URLRequestStatus());  // Clear the IO_PENDING status.
    207   NotifyReadComplete(read_result);
    208 }
    209 
    210 }  // namespace drive
    211