| // Copyright 2016 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 "net/reporting/reporting_uploader.h" |
| |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/callback_helpers.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "net/base/elements_upload_data_stream.h" |
| #include "net/base/load_flags.h" |
| #include "net/base/upload_bytes_element_reader.h" |
| #include "net/http/http_response_headers.h" |
| #include "net/traffic_annotation/network_traffic_annotation.h" |
| #include "net/url_request/redirect_info.h" |
| #include "net/url_request/url_request_context.h" |
| #include "url/gurl.h" |
| |
| namespace net { |
| |
| namespace { |
| |
| class UploadUserData : public base::SupportsUserData::Data { |
| public: |
| static const void* const kUserDataKey; |
| }; |
| |
| // SetUserData needs a unique const void* to serve as the key, so create a const |
| // void* and use its own address as the unique pointer. |
| const void* const UploadUserData::kUserDataKey = &UploadUserData::kUserDataKey; |
| |
| ReportingUploader::Outcome ResponseCodeToOutcome(int response_code) { |
| if (response_code >= 200 && response_code <= 299) |
| return ReportingUploader::Outcome::SUCCESS; |
| if (response_code == 410) |
| return ReportingUploader::Outcome::REMOVE_ENDPOINT; |
| return ReportingUploader::Outcome::FAILURE; |
| } |
| |
| class ReportingUploaderImpl : public ReportingUploader, URLRequest::Delegate { |
| public: |
| ReportingUploaderImpl(const URLRequestContext* context) : context_(context) { |
| DCHECK(context_); |
| } |
| |
| ~ReportingUploaderImpl() override { |
| for (auto& it : uploads_) { |
| base::ResetAndReturn(&it.second->second).Run(Outcome::FAILURE); |
| it.second->first->Cancel(); |
| } |
| uploads_.clear(); |
| } |
| |
| void StartUpload(const GURL& url, |
| const std::string& json, |
| UploadCallback callback) override { |
| net::NetworkTrafficAnnotationTag traffic_annotation = |
| net::DefineNetworkTrafficAnnotation("reporting", R"( |
| semantics { |
| sender: "Reporting API" |
| description: |
| "The Reporting API reports various issues back to website owners " |
| "to help them detect and fix problems." |
| trigger: |
| "Encountering issues. Examples of these issues are Content " |
| "Security Policy violations and Interventions/Deprecations " |
| "encountered. See draft of reporting spec here: " |
| "https://wicg.github.io/reporting." |
| data: "Details of the issue, depending on issue type." |
| destination: OTHER |
| } |
| policy { |
| cookies_allowed: NO |
| setting: "This feature cannot be disabled by settings." |
| policy_exception_justification: "Not implemented." |
| })"); |
| std::unique_ptr<URLRequest> request = |
| context_->CreateRequest(url, IDLE, this, traffic_annotation); |
| |
| request->set_method("POST"); |
| |
| request->SetLoadFlags(LOAD_DISABLE_CACHE | LOAD_DO_NOT_SAVE_COOKIES | |
| LOAD_DO_NOT_SEND_COOKIES); |
| |
| request->SetExtraRequestHeaderByName(HttpRequestHeaders::kContentType, |
| kUploadContentType, true); |
| |
| std::vector<char> json_data(json.begin(), json.end()); |
| std::unique_ptr<UploadElementReader> reader( |
| new UploadOwnedBytesElementReader(&json_data)); |
| request->set_upload( |
| ElementsUploadDataStream::CreateWithReader(std::move(reader), 0)); |
| |
| request->SetUserData(UploadUserData::kUserDataKey, |
| std::make_unique<UploadUserData>()); |
| |
| // This inherently sets mode = "no-cors", but that doesn't matter, because |
| // the origins that are included in the upload don't actually get to see |
| // the response. |
| // |
| // This inherently skips Service Worker, too. |
| request->Start(); |
| |
| // Have to grab the unique_ptr* first to ensure request.get() happens |
| // before std::move(request). |
| std::unique_ptr<Upload>* upload = &uploads_[request.get()]; |
| *upload = std::make_unique<Upload>(std::move(request), std::move(callback)); |
| } |
| |
| // static |
| bool RequestIsUpload(const net::URLRequest& request) override { |
| return request.GetUserData(UploadUserData::kUserDataKey); |
| } |
| |
| // URLRequest::Delegate implementation: |
| |
| void OnReceivedRedirect(URLRequest* request, |
| const RedirectInfo& redirect_info, |
| bool* defer_redirect) override { |
| if (!redirect_info.new_url.SchemeIsCryptographic()) { |
| request->Cancel(); |
| return; |
| } |
| } |
| |
| void OnAuthRequired(URLRequest* request, |
| AuthChallengeInfo* auth_info) override { |
| request->Cancel(); |
| } |
| |
| void OnCertificateRequested(URLRequest* request, |
| SSLCertRequestInfo* cert_request_info) override { |
| request->Cancel(); |
| } |
| |
| void OnSSLCertificateError(URLRequest* request, |
| const SSLInfo& ssl_info, |
| bool fatal) override { |
| request->Cancel(); |
| } |
| |
| void OnResponseStarted(URLRequest* request, int net_error) override { |
| // Grab Upload from map, and hold on to it in a local unique_ptr so it's |
| // removed at the end of the method. |
| auto it = uploads_.find(request); |
| DCHECK(it != uploads_.end()); |
| std::unique_ptr<Upload> upload = std::move(it->second); |
| uploads_.erase(it); |
| |
| // request->GetResponseCode() should work, but doesn't in the cases above |
| // where the request was canceled, so get the response code by hand. |
| // TODO(juliatuttle): Check if mmenke fixed this yet. |
| HttpResponseHeaders* headers = request->response_headers(); |
| int response_code = headers ? headers->response_code() : 0; |
| Outcome outcome = ResponseCodeToOutcome(response_code); |
| |
| std::move(upload->second).Run(outcome); |
| |
| request->Cancel(); |
| } |
| |
| void OnReadCompleted(URLRequest* request, int bytes_read) override { |
| // Reporting doesn't need anything in the body of the response, so it |
| // doesn't read it, so it should never get OnReadCompleted calls. |
| NOTREACHED(); |
| } |
| |
| private: |
| using Upload = std::pair<std::unique_ptr<URLRequest>, UploadCallback>; |
| |
| const URLRequestContext* context_; |
| std::map<const URLRequest*, std::unique_ptr<Upload>> uploads_; |
| }; |
| |
| } // namespace |
| |
| // static |
| const char ReportingUploader::kUploadContentType[] = "application/report"; |
| |
| ReportingUploader::~ReportingUploader() = default; |
| |
| // static |
| std::unique_ptr<ReportingUploader> ReportingUploader::Create( |
| const URLRequestContext* context) { |
| return std::make_unique<ReportingUploaderImpl>(context); |
| } |
| |
| } // namespace net |