| Index: chrome/browser/chromeos/geolocation/simple_geolocation_request.cc
|
| diff --git a/chrome/browser/chromeos/geolocation/simple_geolocation_request.cc b/chrome/browser/chromeos/geolocation/simple_geolocation_request.cc
|
| deleted file mode 100644
|
| index 96d05004e54dfd9a4685410b10da4266a7a89e96..0000000000000000000000000000000000000000
|
| --- a/chrome/browser/chromeos/geolocation/simple_geolocation_request.cc
|
| +++ /dev/null
|
| @@ -1,392 +0,0 @@
|
| -// Copyright 2014 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/geolocation/simple_geolocation_request.h"
|
| -
|
| -#include <algorithm>
|
| -#include <string>
|
| -
|
| -#include "base/json/json_reader.h"
|
| -#include "base/metrics/histogram.h"
|
| -#include "base/metrics/sparse_histogram.h"
|
| -#include "base/strings/string_number_conversions.h"
|
| -#include "base/strings/stringprintf.h"
|
| -#include "base/time/time.h"
|
| -#include "base/values.h"
|
| -#include "chrome/browser/chromeos/geolocation/geoposition.h"
|
| -#include "chrome/browser/chromeos/geolocation/simple_geolocation_provider.h"
|
| -#include "google_apis/google_api_keys.h"
|
| -#include "net/base/escape.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_context_getter.h"
|
| -#include "net/url_request/url_request_status.h"
|
| -
|
| -// Location resolve timeout is usually 1 minute, so 2 minutes with 50 buckets
|
| -// should be enough.
|
| -#define UMA_HISTOGRAM_LOCATION_RESPONSE_TIMES(name, sample) \
|
| - UMA_HISTOGRAM_CUSTOM_TIMES(name, \
|
| - sample, \
|
| - base::TimeDelta::FromMilliseconds(10), \
|
| - base::TimeDelta::FromMinutes(2), \
|
| - 50)
|
| -
|
| -namespace chromeos {
|
| -
|
| -namespace {
|
| -
|
| -// The full request text. (no parameters are supported by now)
|
| -const char kSimpleGeolocationRequestBody[] = "{\"considerIP\": \"true\"}";
|
| -
|
| -// Response data.
|
| -const char kLocationString[] = "location";
|
| -const char kLatString[] = "lat";
|
| -const char kLngString[] = "lng";
|
| -const char kAccuracyString[] = "accuracy";
|
| -// Error object and its contents.
|
| -const char kErrorString[] = "error";
|
| -// "errors" array in "erorr" object is ignored.
|
| -const char kCodeString[] = "code";
|
| -const char kMessageString[] = "message";
|
| -
|
| -// We are using "sparse" histograms for the number of retry attempts,
|
| -// so we need to explicitly limit maximum value (in case something goes wrong).
|
| -const size_t kMaxRetriesValueInHistograms = 20;
|
| -
|
| -// Sleep between geolocation request retry on HTTP error.
|
| -const unsigned int kResolveGeolocationRetrySleepOnServerErrorSeconds = 5;
|
| -
|
| -// Sleep between geolocation request retry on bad server response.
|
| -const unsigned int kResolveGeolocationRetrySleepBadResponseSeconds = 10;
|
| -
|
| -enum SimpleGeolocationRequestEvent {
|
| - // NOTE: Do not renumber these as that would confuse interpretation of
|
| - // previously logged data. When making changes, also update the enum list
|
| - // in tools/metrics/histograms/histograms.xml to keep it in sync.
|
| - SIMPLE_GEOLOCATION_REQUEST_EVENT_REQUEST_START = 0,
|
| - SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_SUCCESS = 1,
|
| - SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_NOT_OK = 2,
|
| - SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_EMPTY = 3,
|
| - SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_MALFORMED = 4,
|
| -
|
| - // NOTE: Add entries only immediately above this line.
|
| - SIMPLE_GEOLOCATION_REQUEST_EVENT_COUNT = 5
|
| -};
|
| -
|
| -enum SimpleGeolocationRequestResult {
|
| - // NOTE: Do not renumber these as that would confuse interpretation of
|
| - // previously logged data. When making changes, also update the enum list
|
| - // in tools/metrics/histograms/histograms.xml to keep it in sync.
|
| - SIMPLE_GEOLOCATION_REQUEST_RESULT_SUCCESS = 0,
|
| - SIMPLE_GEOLOCATION_REQUEST_RESULT_FAILURE = 1,
|
| - SIMPLE_GEOLOCATION_REQUEST_RESULT_SERVER_ERROR = 2,
|
| - SIMPLE_GEOLOCATION_REQUEST_RESULT_CANCELLED = 3,
|
| -
|
| - // NOTE: Add entries only immediately above this line.
|
| - SIMPLE_GEOLOCATION_REQUEST_RESULT_COUNT = 4
|
| -};
|
| -
|
| -// Too many requests (more than 1) mean there is a problem in implementation.
|
| -void RecordUmaEvent(SimpleGeolocationRequestEvent event) {
|
| - UMA_HISTOGRAM_ENUMERATION("SimpleGeolocation.Request.Event",
|
| - event,
|
| - SIMPLE_GEOLOCATION_REQUEST_EVENT_COUNT);
|
| -}
|
| -
|
| -void RecordUmaResponseCode(int code) {
|
| - UMA_HISTOGRAM_SPARSE_SLOWLY("SimpleGeolocation.Request.ResponseCode", code);
|
| -}
|
| -
|
| -// Slow geolocation resolve leads to bad user experience.
|
| -void RecordUmaResponseTime(base::TimeDelta elapsed, bool success) {
|
| - if (success) {
|
| - UMA_HISTOGRAM_LOCATION_RESPONSE_TIMES(
|
| - "SimpleGeolocation.Request.ResponseSuccessTime", elapsed);
|
| - } else {
|
| - UMA_HISTOGRAM_LOCATION_RESPONSE_TIMES(
|
| - "SimpleGeolocation.Request.ResponseFailureTime", elapsed);
|
| - }
|
| -}
|
| -
|
| -void RecordUmaResult(SimpleGeolocationRequestResult result, size_t retries) {
|
| - UMA_HISTOGRAM_ENUMERATION("SimpleGeolocation.Request.Result",
|
| - result,
|
| - SIMPLE_GEOLOCATION_REQUEST_RESULT_COUNT);
|
| - UMA_HISTOGRAM_SPARSE_SLOWLY("SimpleGeolocation.Request.Retries",
|
| - std::min(retries, kMaxRetriesValueInHistograms));
|
| -}
|
| -
|
| -// Creates the request url to send to the server.
|
| -GURL GeolocationRequestURL(const GURL& url) {
|
| - if (url != SimpleGeolocationProvider::DefaultGeolocationProviderURL())
|
| - return url;
|
| -
|
| - std::string api_key = google_apis::GetAPIKey();
|
| - if (api_key.empty())
|
| - return url;
|
| -
|
| - std::string query(url.query());
|
| - if (!query.empty())
|
| - query += "&";
|
| - query += "key=" + net::EscapeQueryParamValue(api_key, true);
|
| - GURL::Replacements replacements;
|
| - replacements.SetQueryStr(query);
|
| - return url.ReplaceComponents(replacements);
|
| -}
|
| -
|
| -void PrintGeolocationError(const GURL& server_url,
|
| - const std::string& message,
|
| - Geoposition* position) {
|
| - position->status = Geoposition::STATUS_SERVER_ERROR;
|
| - position->error_message =
|
| - base::StringPrintf("SimpleGeolocation provider at '%s' : %s.",
|
| - server_url.GetOrigin().spec().c_str(),
|
| - message.c_str());
|
| - VLOG(1) << "SimpleGeolocationRequest::GetGeolocationFromResponse() : "
|
| - << position->error_message;
|
| -}
|
| -
|
| -// Parses the server response body. Returns true if parsing was successful.
|
| -// Sets |*position| to the parsed Geolocation if a valid position was received,
|
| -// otherwise leaves it unchanged.
|
| -bool ParseServerResponse(const GURL& server_url,
|
| - const std::string& response_body,
|
| - Geoposition* position) {
|
| - DCHECK(position);
|
| -
|
| - if (response_body.empty()) {
|
| - PrintGeolocationError(
|
| - server_url, "Server returned empty response", position);
|
| - RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_EMPTY);
|
| - return false;
|
| - }
|
| - VLOG(1) << "SimpleGeolocationRequest::ParseServerResponse() : "
|
| - "Parsing response '" << response_body << "'";
|
| -
|
| - // Parse the response, ignoring comments.
|
| - std::string error_msg;
|
| - scoped_ptr<base::Value> response_value(base::JSONReader::ReadAndReturnError(
|
| - response_body, base::JSON_PARSE_RFC, NULL, &error_msg));
|
| - if (response_value == NULL) {
|
| - PrintGeolocationError(
|
| - server_url, "JSONReader failed: " + error_msg, position);
|
| - RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_MALFORMED);
|
| - return false;
|
| - }
|
| -
|
| - base::DictionaryValue* response_object = NULL;
|
| - if (!response_value->GetAsDictionary(&response_object)) {
|
| - PrintGeolocationError(
|
| - server_url,
|
| - "Unexpected response type : " +
|
| - base::StringPrintf("%u", response_value->GetType()),
|
| - position);
|
| - RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_MALFORMED);
|
| - return false;
|
| - }
|
| -
|
| - base::DictionaryValue* error_object = NULL;
|
| - base::DictionaryValue* location_object = NULL;
|
| - response_object->GetDictionaryWithoutPathExpansion(kLocationString,
|
| - &location_object);
|
| - response_object->GetDictionaryWithoutPathExpansion(kErrorString,
|
| - &error_object);
|
| -
|
| - position->timestamp = base::Time::Now();
|
| -
|
| - if (error_object) {
|
| - if (!error_object->GetStringWithoutPathExpansion(
|
| - kMessageString, &(position->error_message))) {
|
| - position->error_message = "Server returned error without message.";
|
| - }
|
| -
|
| - // Ignore result (code defaults to zero).
|
| - error_object->GetIntegerWithoutPathExpansion(kCodeString,
|
| - &(position->error_code));
|
| - } else {
|
| - position->error_message.erase();
|
| - }
|
| -
|
| - if (location_object) {
|
| - if (!location_object->GetDoubleWithoutPathExpansion(
|
| - kLatString, &(position->latitude))) {
|
| - PrintGeolocationError(server_url, "Missing 'lat' attribute.", position);
|
| - RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_MALFORMED);
|
| - return false;
|
| - }
|
| - if (!location_object->GetDoubleWithoutPathExpansion(
|
| - kLngString, &(position->longitude))) {
|
| - PrintGeolocationError(server_url, "Missing 'lon' attribute.", position);
|
| - RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_MALFORMED);
|
| - return false;
|
| - }
|
| - if (!response_object->GetDoubleWithoutPathExpansion(
|
| - kAccuracyString, &(position->accuracy))) {
|
| - PrintGeolocationError(
|
| - server_url, "Missing 'accuracy' attribute.", position);
|
| - RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_MALFORMED);
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - if (error_object) {
|
| - position->status = Geoposition::STATUS_SERVER_ERROR;
|
| - return false;
|
| - }
|
| - // Empty response is STATUS_OK but not Valid().
|
| - position->status = Geoposition::STATUS_OK;
|
| - return true;
|
| -}
|
| -
|
| -// Attempts to extract a position from the response. Detects and indicates
|
| -// various failure cases.
|
| -bool GetGeolocationFromResponse(bool http_success,
|
| - int status_code,
|
| - const std::string& response_body,
|
| - const GURL& server_url,
|
| - Geoposition* position) {
|
| -
|
| - // HttpPost can fail for a number of reasons. Most likely this is because
|
| - // we're offline, or there was no response.
|
| - if (!http_success) {
|
| - PrintGeolocationError(server_url, "No response received", position);
|
| - RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_EMPTY);
|
| - return false;
|
| - }
|
| - if (status_code != net::HTTP_OK) {
|
| - std::string message = "Returned error code ";
|
| - message += base::IntToString(status_code);
|
| - PrintGeolocationError(server_url, message, position);
|
| - RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_NOT_OK);
|
| - return false;
|
| - }
|
| -
|
| - return ParseServerResponse(server_url, response_body, position);
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -SimpleGeolocationRequest::SimpleGeolocationRequest(
|
| - net::URLRequestContextGetter* url_context_getter,
|
| - const GURL& service_url,
|
| - base::TimeDelta timeout)
|
| - : url_context_getter_(url_context_getter),
|
| - service_url_(service_url),
|
| - retry_sleep_on_server_error_(base::TimeDelta::FromSeconds(
|
| - kResolveGeolocationRetrySleepOnServerErrorSeconds)),
|
| - retry_sleep_on_bad_response_(base::TimeDelta::FromSeconds(
|
| - kResolveGeolocationRetrySleepBadResponseSeconds)),
|
| - timeout_(timeout),
|
| - retries_(0) {
|
| -}
|
| -
|
| -SimpleGeolocationRequest::~SimpleGeolocationRequest() {
|
| - DCHECK(thread_checker_.CalledOnValidThread());
|
| -
|
| - // If callback is not empty, request is cancelled.
|
| - if (!callback_.is_null()) {
|
| - RecordUmaResponseTime(base::Time::Now() - request_started_at_, false);
|
| - RecordUmaResult(SIMPLE_GEOLOCATION_REQUEST_RESULT_CANCELLED, retries_);
|
| - }
|
| -}
|
| -
|
| -void SimpleGeolocationRequest::StartRequest() {
|
| - DCHECK(thread_checker_.CalledOnValidThread());
|
| - RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_REQUEST_START);
|
| - ++retries_;
|
| -
|
| - url_fetcher_.reset(
|
| - net::URLFetcher::Create(request_url_, net::URLFetcher::POST, this));
|
| - url_fetcher_->SetRequestContext(url_context_getter_.get());
|
| - url_fetcher_->SetUploadData("application/json",
|
| - std::string(kSimpleGeolocationRequestBody));
|
| - url_fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE |
|
| - net::LOAD_DISABLE_CACHE |
|
| - net::LOAD_DO_NOT_SAVE_COOKIES |
|
| - net::LOAD_DO_NOT_SEND_COOKIES |
|
| - net::LOAD_DO_NOT_SEND_AUTH_DATA);
|
| - url_fetcher_->Start();
|
| -}
|
| -
|
| -void SimpleGeolocationRequest::MakeRequest(const ResponseCallback& callback) {
|
| - callback_ = callback;
|
| - request_url_ = GeolocationRequestURL(service_url_);
|
| - timeout_timer_.Start(
|
| - FROM_HERE, timeout_, this, &SimpleGeolocationRequest::OnTimeout);
|
| - request_started_at_ = base::Time::Now();
|
| - StartRequest();
|
| -}
|
| -
|
| -void SimpleGeolocationRequest::Retry(bool server_error) {
|
| - base::TimeDelta delay(server_error ? retry_sleep_on_server_error_
|
| - : retry_sleep_on_bad_response_);
|
| - request_scheduled_.Start(
|
| - FROM_HERE, delay, this, &SimpleGeolocationRequest::StartRequest);
|
| -}
|
| -
|
| -void SimpleGeolocationRequest::OnURLFetchComplete(
|
| - const net::URLFetcher* source) {
|
| - DCHECK_EQ(url_fetcher_.get(), source);
|
| -
|
| - net::URLRequestStatus status = source->GetStatus();
|
| - int response_code = source->GetResponseCode();
|
| - RecordUmaResponseCode(response_code);
|
| -
|
| - std::string data;
|
| - source->GetResponseAsString(&data);
|
| - const bool parse_success = GetGeolocationFromResponse(
|
| - status.is_success(), response_code, data, source->GetURL(), &position_);
|
| - const bool server_error =
|
| - !status.is_success() || (response_code >= 500 && response_code < 600);
|
| - const bool success = parse_success && position_.Valid();
|
| - url_fetcher_.reset();
|
| -
|
| - DVLOG(1) << "SimpleGeolocationRequest::OnURLFetchComplete(): position={"
|
| - << position_.ToString() << "}";
|
| -
|
| - if (!success) {
|
| - Retry(server_error);
|
| - return;
|
| - }
|
| - const base::TimeDelta elapsed = base::Time::Now() - request_started_at_;
|
| - RecordUmaResponseTime(elapsed, success);
|
| -
|
| - RecordUmaResult(SIMPLE_GEOLOCATION_REQUEST_RESULT_SUCCESS, retries_);
|
| -
|
| - ReplyAndDestroySelf(elapsed, server_error);
|
| - // "this" is already destroyed here.
|
| -}
|
| -
|
| -void SimpleGeolocationRequest::ReplyAndDestroySelf(
|
| - const base::TimeDelta elapsed,
|
| - bool server_error) {
|
| - url_fetcher_.reset();
|
| - timeout_timer_.Stop();
|
| - request_scheduled_.Stop();
|
| -
|
| - ResponseCallback callback = callback_;
|
| -
|
| - // Empty callback is used to identify "completed or not yet started request".
|
| - callback_.Reset();
|
| -
|
| - // callback.Run() usually destroys SimpleGeolocationRequest, because this is
|
| - // the way callback is implemented in GeolocationProvider.
|
| - callback.Run(position_, server_error, elapsed);
|
| - // "this" is already destroyed here.
|
| -}
|
| -
|
| -void SimpleGeolocationRequest::OnTimeout() {
|
| - const SimpleGeolocationRequestResult result =
|
| - (position_.status == Geoposition::STATUS_SERVER_ERROR
|
| - ? SIMPLE_GEOLOCATION_REQUEST_RESULT_SERVER_ERROR
|
| - : SIMPLE_GEOLOCATION_REQUEST_RESULT_FAILURE);
|
| - RecordUmaResult(result, retries_);
|
| - position_.status = Geoposition::STATUS_TIMEOUT;
|
| - const base::TimeDelta elapsed = base::Time::Now() - request_started_at_;
|
| - ReplyAndDestroySelf(elapsed, true /* server_error */);
|
| - // "this" is already destroyed here.
|
| -}
|
| -
|
| -} // namespace chromeos
|
|
|