root/chrome/browser/chromeos/drive/drive_url_request_job.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. FixupMimeType
  2. weak_ptr_factory_
  3. SetExtraRequestHeaders
  4. Start
  5. Kill
  6. GetMimeType
  7. IsRedirectResponse
  8. ReadRawData
  9. OnDriveFileStreamReaderInitialized
  10. OnReadCompleted

// 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 <string>

#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<net::HttpByteRange> 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<ResourceEntry> 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

/* [<][>][^][v][top][bottom][index][help] */