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