root/chrome/service/cloud_print/cloud_print_url_fetcher.cc

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

DEFINITIONS

This source file includes following definitions.
  1. ReportRequestTime
  2. ReportRetriesCount
  3. ReportDownloadSize
  4. ReportUploadSize
  5. Create
  6. factory
  7. set_factory
  8. HandleRawResponse
  9. HandleRawData
  10. HandleJSONData
  11. type_
  12. IsSameRequest
  13. StartGetRequest
  14. StartPostRequest
  15. OnURLFetchComplete
  16. StartRequestHelper
  17. SetupRequestHeaders
  18. GetRequestContextGetter

// Copyright (c) 2012 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/service/cloud_print/cloud_print_url_fetcher.h"

#include "base/metrics/histogram.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "chrome/common/cloud_print/cloud_print_constants.h"
#include "chrome/common/cloud_print/cloud_print_helpers.h"
#include "chrome/service/cloud_print/cloud_print_service_helpers.h"
#include "chrome/service/cloud_print/cloud_print_token_store.h"
#include "chrome/service/net/service_url_request_context.h"
#include "chrome/service/service_process.h"
#include "net/base/load_flags.h"
#include "net/http/http_status_code.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_request_status.h"
#include "url/gurl.h"

namespace cloud_print {

namespace {

void ReportRequestTime(CloudPrintURLFetcher::RequestType type,
                       base::TimeDelta time) {
  if (type == CloudPrintURLFetcher::REQUEST_REGISTER) {
    UMA_HISTOGRAM_TIMES("CloudPrint.UrlFetcherRequestTime.Register", time);
  } else if (type == CloudPrintURLFetcher::REQUEST_UPDATE_PRINTER) {
    UMA_HISTOGRAM_TIMES("CloudPrint.UrlFetcherRequestTime.UpdatePrinter", time);
  } else if (type == CloudPrintURLFetcher::REQUEST_DATA) {
    UMA_HISTOGRAM_TIMES("CloudPrint.UrlFetcherRequestTime.DownloadData", time);
  } else {
    UMA_HISTOGRAM_TIMES("CloudPrint.UrlFetcherRequestTime.Other", time);
  }
}

void ReportRetriesCount(CloudPrintURLFetcher::RequestType type,
                        int retries) {
  if (type == CloudPrintURLFetcher::REQUEST_REGISTER) {
    UMA_HISTOGRAM_COUNTS_100("CloudPrint.UrlFetcherRetries.Register", retries);
  } else if (type == CloudPrintURLFetcher::REQUEST_UPDATE_PRINTER) {
    UMA_HISTOGRAM_COUNTS_100("CloudPrint.UrlFetcherRetries.UpdatePrinter",
                             retries);
  } else if (type == CloudPrintURLFetcher::REQUEST_DATA) {
    UMA_HISTOGRAM_COUNTS_100("CloudPrint.UrlFetcherRetries.DownloadData",
                             retries);
  } else {
    UMA_HISTOGRAM_COUNTS_100("CloudPrint.UrlFetcherRetries.Other", retries);
  }
}

void ReportDownloadSize(CloudPrintURLFetcher::RequestType type, size_t size) {
  if (type == CloudPrintURLFetcher::REQUEST_REGISTER) {
    UMA_HISTOGRAM_MEMORY_KB("CloudPrint.UrlFetcherDownloadSize.Register", size);
  } else if (type == CloudPrintURLFetcher::REQUEST_UPDATE_PRINTER) {
    UMA_HISTOGRAM_MEMORY_KB("CloudPrint.UrlFetcherDownloadSize.UpdatePrinter",
                            size);
  } else if (type == CloudPrintURLFetcher::REQUEST_DATA) {
    UMA_HISTOGRAM_MEMORY_KB("CloudPrint.UrlFetcherDownloadSize.DownloadData",
                            size);
  } else {
    UMA_HISTOGRAM_MEMORY_KB("CloudPrint.UrlFetcherDownloadSize.Other", size);
  }
}

void ReportUploadSize(CloudPrintURLFetcher::RequestType type, size_t size) {
  if (type == CloudPrintURLFetcher::REQUEST_REGISTER) {
    UMA_HISTOGRAM_MEMORY_KB("CloudPrint.UrlFetcherUploadSize.Register", size);
  } else if (type == CloudPrintURLFetcher::REQUEST_UPDATE_PRINTER) {
    UMA_HISTOGRAM_MEMORY_KB("CloudPrint.UrlFetcherUploadSize.UpdatePrinter",
                            size);
  } else if (type == CloudPrintURLFetcher::REQUEST_DATA) {
    UMA_HISTOGRAM_MEMORY_KB("CloudPrint.UrlFetcherUploadSize.DownloadData",
                            size);
  } else {
    UMA_HISTOGRAM_MEMORY_KB("CloudPrint.UrlFetcherUploadSize.Other", size);
  }
}

CloudPrintURLFetcherFactory* g_factory = NULL;

}  // namespace

// virtual
CloudPrintURLFetcherFactory::~CloudPrintURLFetcherFactory() {}

// static
CloudPrintURLFetcher* CloudPrintURLFetcher::Create() {
  CloudPrintURLFetcherFactory* factory = CloudPrintURLFetcher::factory();
  return factory ? factory->CreateCloudPrintURLFetcher() :
      new CloudPrintURLFetcher;
}

// static
CloudPrintURLFetcherFactory* CloudPrintURLFetcher::factory() {
  return g_factory;
}

// static
void CloudPrintURLFetcher::set_factory(CloudPrintURLFetcherFactory* factory) {
  g_factory = factory;
}

CloudPrintURLFetcher::ResponseAction
CloudPrintURLFetcher::Delegate::HandleRawResponse(
    const net::URLFetcher* source,
    const GURL& url,
    const net::URLRequestStatus& status,
    int response_code,
    const net::ResponseCookies& cookies,
    const std::string& data) {
  return CONTINUE_PROCESSING;
}

CloudPrintURLFetcher::ResponseAction
CloudPrintURLFetcher::Delegate::HandleRawData(
    const net::URLFetcher* source,
    const GURL& url,
    const std::string& data) {
  return CONTINUE_PROCESSING;
}

CloudPrintURLFetcher::ResponseAction
CloudPrintURLFetcher::Delegate::HandleJSONData(
    const net::URLFetcher* source,
    const GURL& url,
    base::DictionaryValue* json_data,
    bool succeeded) {
  return CONTINUE_PROCESSING;
}

CloudPrintURLFetcher::CloudPrintURLFetcher()
    : delegate_(NULL),
      num_retries_(0),
      type_(REQUEST_MAX) {
}

bool CloudPrintURLFetcher::IsSameRequest(const net::URLFetcher* source) {
  return (request_.get() == source);
}

void CloudPrintURLFetcher::StartGetRequest(
    RequestType type,
    const GURL& url,
    Delegate* delegate,
    int max_retries,
    const std::string& additional_headers) {
  StartRequestHelper(type, url, net::URLFetcher::GET, delegate, max_retries,
                     std::string(), std::string(), additional_headers);
}

void CloudPrintURLFetcher::StartPostRequest(
    RequestType type,
    const GURL& url,
    Delegate* delegate,
    int max_retries,
    const std::string& post_data_mime_type,
    const std::string& post_data,
    const std::string& additional_headers) {
  StartRequestHelper(type, url, net::URLFetcher::POST, delegate, max_retries,
                     post_data_mime_type, post_data, additional_headers);
}

void CloudPrintURLFetcher::OnURLFetchComplete(
    const net::URLFetcher* source) {
  VLOG(1) << "CP_PROXY: OnURLFetchComplete, url: " << source->GetURL()
          << ", response code: " << source->GetResponseCode();
  // Make sure we stay alive through the body of this function.
  scoped_refptr<CloudPrintURLFetcher> keep_alive(this);
  std::string data;
  source->GetResponseAsString(&data);
  ReportRequestTime(type_, base::Time::Now() - start_time_);
  ReportDownloadSize(type_, data.size());
  ResponseAction action = delegate_->HandleRawResponse(
      source,
      source->GetURL(),
      source->GetStatus(),
      source->GetResponseCode(),
      source->GetCookies(),
      data);

  // If we get auth error, notify delegate and check if it wants to proceed.
  if (action == CONTINUE_PROCESSING &&
      source->GetResponseCode() == net::HTTP_FORBIDDEN) {
    action = delegate_->OnRequestAuthError();
  }

  if (action == CONTINUE_PROCESSING) {
    // We need to retry on all network errors.
    if (!source->GetStatus().is_success() || (source->GetResponseCode() != 200))
      action = RETRY_REQUEST;
    else
      action = delegate_->HandleRawData(source, source->GetURL(), data);

    if (action == CONTINUE_PROCESSING) {
      // If the delegate is not interested in handling the raw response data,
      // we assume that a JSON response is expected. If we do not get a JSON
      // response, we will retry (to handle the case where we got redirected
      // to a non-cloudprint-server URL eg. for authentication).
      bool succeeded = false;
      scoped_ptr<base::DictionaryValue> response_dict =
          ParseResponseJSON(data, &succeeded);

      if (response_dict) {
        action = delegate_->HandleJSONData(source,
                                           source->GetURL(),
                                           response_dict.get(),
                                           succeeded);
      } else {
        action = RETRY_REQUEST;
      }
    }
  }
  // Retry the request if needed.
  if (action == RETRY_REQUEST) {
    // Explicitly call ReceivedContentWasMalformed() to ensure the current
    // request gets counted as a failure for calculation of the back-off
    // period.  If it was already a failure by status code, this call will
    // be ignored.
    request_->ReceivedContentWasMalformed();

    // If we receive error code from the server "Media Type Not Supported",
    // there is no reason to retry, request will never succeed.
    // In that case we should call OnRequestGiveUp() right away.
    if (source->GetResponseCode() == net::HTTP_UNSUPPORTED_MEDIA_TYPE)
      num_retries_ = source->GetMaxRetriesOn5xx();

    ++num_retries_;
    if ((-1 != source->GetMaxRetriesOn5xx()) &&
        (num_retries_ > source->GetMaxRetriesOn5xx())) {
      // Retry limit reached. Give up.
      delegate_->OnRequestGiveUp();
      action = STOP_PROCESSING;
    } else {
      // Either no retry limit specified or retry limit has not yet been
      // reached. Try again. Set up the request headers again because the token
      // may have changed.
      SetupRequestHeaders();
      request_->SetRequestContext(GetRequestContextGetter());
      start_time_ = base::Time::Now();
      request_->Start();
    }
  }
  if (action != RETRY_REQUEST) {
    ReportRetriesCount(type_, num_retries_);
  }
}

void CloudPrintURLFetcher::StartRequestHelper(
    RequestType type,
    const GURL& url,
    net::URLFetcher::RequestType request_type,
    Delegate* delegate,
    int max_retries,
    const std::string& post_data_mime_type,
    const std::string& post_data,
    const std::string& additional_headers) {
  DCHECK(delegate);
  type_ = type;
  UMA_HISTOGRAM_ENUMERATION("CloudPrint.UrlFetcherRequestType", type,
                            REQUEST_MAX);
  // Persist the additional headers in case we need to retry the request.
  additional_headers_ = additional_headers;
  request_.reset(net::URLFetcher::Create(0, url, request_type, this));
  request_->SetRequestContext(GetRequestContextGetter());
  // Since we implement our own retry logic, disable the retry in URLFetcher.
  request_->SetAutomaticallyRetryOn5xx(false);
  request_->SetMaxRetriesOn5xx(max_retries);
  delegate_ = delegate;
  SetupRequestHeaders();
  request_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
                         net::LOAD_DO_NOT_SAVE_COOKIES);
  if (request_type == net::URLFetcher::POST) {
    request_->SetUploadData(post_data_mime_type, post_data);
    ReportUploadSize(type_, post_data.size());
  }
  start_time_ = base::Time::Now();
  request_->Start();
}

void CloudPrintURLFetcher::SetupRequestHeaders() {
  std::string headers = delegate_->GetAuthHeader();
  if (!headers.empty())
    headers += "\r\n";
  headers += kChromeCloudPrintProxyHeader;
  if (!additional_headers_.empty()) {
    headers += "\r\n";
    headers += additional_headers_;
  }
  request_->SetExtraRequestHeaders(headers);
}

CloudPrintURLFetcher::~CloudPrintURLFetcher() {}

net::URLRequestContextGetter* CloudPrintURLFetcher::GetRequestContextGetter() {
  ServiceURLRequestContextGetter* getter =
      g_service_process->GetServiceURLRequestContextGetter();
  // Now set up the user agent for cloudprint.
  std::string user_agent = getter->user_agent();
  base::StringAppendF(&user_agent, " %s", kCloudPrintUserAgent);
  getter->set_user_agent(user_agent);
  return getter;
}

}  // namespace cloud_print

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