// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/chromeos/drive/drive_url_request_job.h" #include #include "base/bind.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "chrome/browser/chromeos/drive/drive.pb.h" #include "chrome/browser/chromeos/drive/drive_file_stream_reader.h" #include "chrome/browser/chromeos/drive/file_system_interface.h" #include "chrome/browser/chromeos/drive/file_system_util.h" #include "content/public/browser/browser_thread.h" #include "net/base/net_errors.h" #include "net/http/http_byte_range.h" #include "net/http/http_request_headers.h" #include "net/http/http_response_info.h" #include "net/http/http_util.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_status.h" using content::BrowserThread; namespace drive { namespace { struct MimeTypeReplacement { const char* original_type; const char* new_type; }; const MimeTypeReplacement kMimeTypeReplacements[] = { {"message/rfc822", "multipart/related"} // Fixes MHTML }; std::string FixupMimeType(const std::string& type) { for (size_t i = 0; i < arraysize(kMimeTypeReplacements); i++) { if (type == kMimeTypeReplacements[i].original_type) return kMimeTypeReplacements[i].new_type; } return type; } } // namespace DriveURLRequestJob::DriveURLRequestJob( const FileSystemGetter& file_system_getter, base::SequencedTaskRunner* file_task_runner, net::URLRequest* request, net::NetworkDelegate* network_delegate) : net::URLRequestJob(request, network_delegate), file_system_getter_(file_system_getter), file_task_runner_(file_task_runner), weak_ptr_factory_(this) { } void DriveURLRequestJob::SetExtraRequestHeaders( const net::HttpRequestHeaders& headers) { std::string range_header; if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) { // Note: We only support single range requests. std::vector ranges; if (net::HttpUtil::ParseRangeHeader(range_header, &ranges) && ranges.size() == 1) { byte_range_ = ranges[0]; } else { // Failed to parse Range: header, so notify the error. NotifyDone( net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)); } } } void DriveURLRequestJob::Start() { DVLOG(1) << "Starting request"; DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(!stream_reader_); // We only support GET request. if (request()->method() != "GET") { LOG(WARNING) << "Failed to start request: " << request()->method() << " method is not supported"; NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_METHOD_NOT_SUPPORTED)); return; } base::FilePath drive_file_path(util::DriveURLToFilePath(request_->url())); if (drive_file_path.empty()) { // Not a valid url. NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_INVALID_URL)); return; } // Initialize the stream reader. stream_reader_.reset( new DriveFileStreamReader(file_system_getter_, file_task_runner_.get())); stream_reader_->Initialize( drive_file_path, byte_range_, base::Bind(&DriveURLRequestJob::OnDriveFileStreamReaderInitialized, weak_ptr_factory_.GetWeakPtr())); } void DriveURLRequestJob::Kill() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); stream_reader_.reset(); net::URLRequestJob::Kill(); weak_ptr_factory_.InvalidateWeakPtrs(); } bool DriveURLRequestJob::GetMimeType(std::string* mime_type) const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (!entry_) { return false; } mime_type->assign( FixupMimeType(entry_->file_specific_info().content_mime_type())); return !mime_type->empty(); } bool DriveURLRequestJob::IsRedirectResponse( GURL* location, int* http_status_code) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (!entry_ || !entry_->file_specific_info().is_hosted_document()) { return false; } // Redirect a hosted document. *location = GURL(entry_->file_specific_info().alternate_url()); const int kHttpFound = 302; *http_status_code = kHttpFound; return true; } bool DriveURLRequestJob::ReadRawData( net::IOBuffer* buf, int buf_size, int* bytes_read) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(stream_reader_ && stream_reader_->IsInitialized()); int result = stream_reader_->Read( buf, buf_size, base::Bind(&DriveURLRequestJob::OnReadCompleted, weak_ptr_factory_.GetWeakPtr())); if (result == net::ERR_IO_PENDING) { // The data is not yet available. SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0)); return false; } if (result < 0) { // An error occurs. NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, result)); return false; } // Reading has been finished immediately. *bytes_read = result; return true; } DriveURLRequestJob::~DriveURLRequestJob() { } void DriveURLRequestJob::OnDriveFileStreamReaderInitialized( int error, scoped_ptr entry) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(stream_reader_); if (error != FILE_ERROR_OK) { NotifyStartError( net::URLRequestStatus(net::URLRequestStatus::FAILED, error)); return; } DCHECK(entry && entry->has_file_specific_info()); entry_ = entry.Pass(); if (!entry_->file_specific_info().is_hosted_document()) { // We don't need to set content size for hosted documents, // because it will be redirected. set_expected_content_size(entry_->file_info().size()); } NotifyHeadersComplete(); } void DriveURLRequestJob::OnReadCompleted(int read_result) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (read_result < 0) { DCHECK_NE(read_result, net::ERR_IO_PENDING); NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, read_result)); } SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status. NotifyReadComplete(read_result); } } // namespace drive