// 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 #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 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& 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 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 url_request_context_getter_; std::map, UploadCallback>> uploads_; bool discard_uploads_; bool shutdown_; int discarded_upload_count_; }; } // namespace DomainReliabilityUploader::DomainReliabilityUploader() {} DomainReliabilityUploader::~DomainReliabilityUploader() {} // static std::unique_ptr DomainReliabilityUploader::Create( MockableTime* time, const scoped_refptr& url_request_context_getter) { return std::unique_ptr( 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( request.GetUserData(UploadUserData::kUserDataKey)); if (!data) return 0; return data->depth(); } void DomainReliabilityUploader::Shutdown() {} } // namespace domain_reliability