| // 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 "components/domain_reliability/uploader.h" | 
 |  | 
 | #include <utility> | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/callback.h" | 
 | #include "base/logging.h" | 
 | #include "base/memory/ptr_util.h" | 
 | #include "base/metrics/histogram_functions.h" | 
 | #include "base/supports_user_data.h" | 
 | #include "components/domain_reliability/util.h" | 
 | #include "net/base/load_flags.h" | 
 | #include "net/base/net_errors.h" | 
 | #include "net/http/http_response_headers.h" | 
 | #include "net/http/http_util.h" | 
 | #include "net/traffic_annotation/network_traffic_annotation.h" | 
 | #include "net/url_request/url_fetcher.h" | 
 | #include "net/url_request/url_fetcher_delegate.h" | 
 | #include "net/url_request/url_request_context_getter.h" | 
 |  | 
 | namespace domain_reliability { | 
 |  | 
 | namespace { | 
 |  | 
 | const char kJsonMimeType[] = "application/json; charset=utf-8"; | 
 |  | 
 | class UploadUserData : public base::SupportsUserData::Data { | 
 |  public: | 
 |   static net::URLFetcher::CreateDataCallback CreateCreateDataCallback( | 
 |       int depth) { | 
 |     return base::Bind(&UploadUserData::CreateUploadUserData, depth); | 
 |   } | 
 |  | 
 |   static const void* const kUserDataKey; | 
 |  | 
 |   int depth() const { return depth_; } | 
 |  | 
 |  private: | 
 |   UploadUserData(int depth) : depth_(depth) {} | 
 |  | 
 |   static std::unique_ptr<base::SupportsUserData::Data> CreateUploadUserData( | 
 |       int depth) { | 
 |     return base::WrapUnique(new UploadUserData(depth)); | 
 |   } | 
 |  | 
 |   int depth_; | 
 | }; | 
 |  | 
 | const void* const UploadUserData::kUserDataKey = | 
 |     &UploadUserData::kUserDataKey; | 
 |  | 
 | class DomainReliabilityUploaderImpl | 
 |     : public DomainReliabilityUploader, net::URLFetcherDelegate { | 
 |  public: | 
 |   DomainReliabilityUploaderImpl( | 
 |       MockableTime* time, | 
 |       const scoped_refptr<net::URLRequestContextGetter>& | 
 |           url_request_context_getter) | 
 |       : time_(time), | 
 |         url_request_context_getter_(url_request_context_getter), | 
 |         discard_uploads_(true), | 
 |         shutdown_(false), | 
 |         discarded_upload_count_(0u) {} | 
 |  | 
 |   ~DomainReliabilityUploaderImpl() override { | 
 |     DCHECK(shutdown_); | 
 |   } | 
 |  | 
 |   // DomainReliabilityUploader implementation: | 
 |   void UploadReport( | 
 |       const std::string& report_json, | 
 |       int max_upload_depth, | 
 |       const GURL& upload_url, | 
 |       const DomainReliabilityUploader::UploadCallback& callback) override { | 
 |     VLOG(1) << "Uploading report to " << upload_url; | 
 |     VLOG(2) << "Report JSON: " << report_json; | 
 |  | 
 |     if (discard_uploads_) | 
 |       discarded_upload_count_++; | 
 |  | 
 |     if (discard_uploads_ || shutdown_) { | 
 |       VLOG(1) << "Discarding report instead of uploading."; | 
 |       UploadResult result; | 
 |       result.status = UploadResult::SUCCESS; | 
 |       callback.Run(result); | 
 |       return; | 
 |     } | 
 |  | 
 |     net::NetworkTrafficAnnotationTag traffic_annotation = | 
 |         net::DefineNetworkTrafficAnnotation("domain_reliability_report_upload", | 
 |                                             R"( | 
 |           semantics { | 
 |             sender: "Domain Reliability" | 
 |             description: | 
 |               "If Chromium has trouble reaching certain Google sites or " | 
 |               "services, Domain Reliability may report the problems back to " | 
 |               "Google." | 
 |             trigger: "Failure to load certain Google sites or services." | 
 |             data: | 
 |               "Details of the failed request, including the URL, any IP " | 
 |               "addresses the browser tried to connect to, error(s) " | 
 |               "encountered loading the resource, and other connection details." | 
 |             destination: GOOGLE_OWNED_SERVICE | 
 |           } | 
 |           policy { | 
 |             cookies_allowed: NO | 
 |             setting: | 
 |               "Users can enable or disable Domain Reliability on desktop, via " | 
 |               "toggling 'Automatically send usage statistics and crash reports " | 
 |               "to Google' in Chromium's settings under Privacy. On ChromeOS, " | 
 |               "the setting is named 'Automatically send diagnostic and usage " | 
 |               "data to Google'." | 
 |             policy_exception_justification: "Not implemented." | 
 |           })"); | 
 |     std::unique_ptr<net::URLFetcher> owned_fetcher = net::URLFetcher::Create( | 
 |         0, upload_url, net::URLFetcher::POST, this, traffic_annotation); | 
 |     net::URLFetcher* fetcher = owned_fetcher.get(); | 
 |     fetcher->SetRequestContext(url_request_context_getter_.get()); | 
 |     fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | | 
 |                           net::LOAD_DO_NOT_SAVE_COOKIES); | 
 |     fetcher->SetUploadData(kJsonMimeType, report_json); | 
 |     fetcher->SetAutomaticallyRetryOn5xx(false); | 
 |     fetcher->SetURLRequestUserData( | 
 |         UploadUserData::kUserDataKey, | 
 |         UploadUserData::CreateCreateDataCallback(max_upload_depth + 1)); | 
 |     fetcher->Start(); | 
 |  | 
 |     uploads_[fetcher] = {std::move(owned_fetcher), callback}; | 
 |   } | 
 |  | 
 |   void SetDiscardUploads(bool discard_uploads) override { | 
 |     discard_uploads_ = discard_uploads; | 
 |     VLOG(1) << "Setting discard_uploads to " << discard_uploads; | 
 |   } | 
 |  | 
 |   void Shutdown() override { | 
 |     DCHECK(!shutdown_); | 
 |     shutdown_ = true; | 
 |     uploads_.clear(); | 
 |   } | 
 |  | 
 |   int GetDiscardedUploadCount() const override { | 
 |     return discarded_upload_count_; | 
 |   } | 
 |  | 
 |   // net::URLFetcherDelegate implementation: | 
 |   void OnURLFetchComplete(const net::URLFetcher* fetcher) override { | 
 |     DCHECK(fetcher); | 
 |  | 
 |     auto callback_it = uploads_.find(fetcher); | 
 |     DCHECK(callback_it != uploads_.end()); | 
 |  | 
 |     int net_error = GetNetErrorFromURLRequestStatus(fetcher->GetStatus()); | 
 |     int http_response_code = fetcher->GetResponseCode(); | 
 |     base::TimeDelta retry_after; | 
 |     { | 
 |       std::string retry_after_string; | 
 |       if (fetcher->GetResponseHeaders() && | 
 |           fetcher->GetResponseHeaders()->EnumerateHeader(nullptr, | 
 |                                                          "Retry-After", | 
 |                                                          &retry_after_string)) { | 
 |         net::HttpUtil::ParseRetryAfterHeader(retry_after_string, | 
 |                                              time_->Now(), | 
 |                                              &retry_after); | 
 |       } | 
 |     } | 
 |  | 
 |     VLOG(1) << "Upload finished with net error " << net_error | 
 |             << ", response code " << http_response_code | 
 |             << ", retry after " << retry_after; | 
 |  | 
 |     base::UmaHistogramSparse("DomainReliability.UploadResponseCode", | 
 |                              http_response_code); | 
 |     base::UmaHistogramSparse("DomainReliability.UploadNetError", -net_error); | 
 |  | 
 |     UploadResult result; | 
 |     GetUploadResultFromResponseDetails(net_error, | 
 |                                        http_response_code, | 
 |                                        retry_after, | 
 |                                        &result); | 
 |     callback_it->second.second.Run(result); | 
 |  | 
 |     uploads_.erase(callback_it); | 
 |   } | 
 |  | 
 |  private: | 
 |   using DomainReliabilityUploader::UploadCallback; | 
 |  | 
 |   MockableTime* time_; | 
 |   scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_; | 
 |   std::map<const net::URLFetcher*, | 
 |            std::pair<std::unique_ptr<net::URLFetcher>, UploadCallback>> | 
 |       uploads_; | 
 |   bool discard_uploads_; | 
 |   bool shutdown_; | 
 |   int discarded_upload_count_; | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | DomainReliabilityUploader::DomainReliabilityUploader() {} | 
 | DomainReliabilityUploader::~DomainReliabilityUploader() {} | 
 |  | 
 | // static | 
 | std::unique_ptr<DomainReliabilityUploader> DomainReliabilityUploader::Create( | 
 |     MockableTime* time, | 
 |     const scoped_refptr<net::URLRequestContextGetter>& | 
 |         url_request_context_getter) { | 
 |   return std::unique_ptr<DomainReliabilityUploader>( | 
 |       new DomainReliabilityUploaderImpl(time, url_request_context_getter)); | 
 | } | 
 |  | 
 | // static | 
 | bool DomainReliabilityUploader::OriginatedFromDomainReliability( | 
 |     const net::URLRequest& request) { | 
 |   return request.GetUserData(UploadUserData::kUserDataKey) != nullptr; | 
 | } | 
 |  | 
 | // static | 
 | int DomainReliabilityUploader::GetURLRequestUploadDepth( | 
 |     const net::URLRequest& request) { | 
 |   UploadUserData* data = static_cast<UploadUserData*>( | 
 |       request.GetUserData(UploadUserData::kUserDataKey)); | 
 |   if (!data) | 
 |     return 0; | 
 |   return data->depth(); | 
 | } | 
 |  | 
 | void DomainReliabilityUploader::Shutdown() {} | 
 |  | 
 | }  // namespace domain_reliability |