// Copyright 2017 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. #ifndef SERVICES_NETWORK_URL_LOADER_H_ #define SERVICES_NETWORK_URL_LOADER_H_ #include #include #include #include #include "base/callback.h" #include "base/component_export.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/optional.h" #include "base/unguessable_token.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/system/data_pipe.h" #include "mojo/public/cpp/system/simple_watcher.h" #include "net/base/load_states.h" #include "net/http/http_raw_request_headers.h" #include "net/traffic_annotation/network_traffic_annotation.h" #include "net/url_request/url_request.h" #include "services/network/cross_origin_read_blocking.h" #include "services/network/keepalive_statistics_recorder.h" #include "services/network/network_service.h" #include "services/network/public/cpp/initiator_lock_compatibility.h" #include "services/network/public/mojom/cookie_access_observer.mojom.h" #include "services/network/public/mojom/cross_origin_embedder_policy.mojom-forward.h" #include "services/network/public/mojom/fetch_api.mojom.h" #include "services/network/public/mojom/network_service.mojom.h" #include "services/network/public/mojom/trust_tokens.mojom-shared.h" #include "services/network/public/mojom/url_loader.mojom.h" #include "services/network/resource_scheduler/resource_scheduler.h" #include "services/network/resource_scheduler/resource_scheduler_client.h" #include "services/network/trust_tokens/pending_trust_token_store.h" #include "services/network/trust_tokens/trust_token_request_helper.h" #include "services/network/trust_tokens/trust_token_request_helper_factory.h" #include "services/network/upload_progress_tracker.h" namespace net { class HttpResponseHeaders; class URLRequestContext; } // namespace net namespace network { namespace mojom { class OriginPolicyManager; } constexpr size_t kMaxFileUploadRequestsPerBatch = 64; class NetToMojoPendingBuffer; class NetworkUsageAccumulator; class KeepaliveStatisticsRecorder; class ScopedThrottlingToken; struct OriginPolicy; class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader : public mojom::URLLoader, public net::URLRequest::Delegate, public mojom::AuthChallengeResponder, public mojom::ClientCertificateResponder { public: // Enumeration for UMA histograms logged by LogConcerningRequestHeaders(). // Entries should not be renumbered and numeric values should never be reused. // Please keep in sync with "NetworkServiceConcerningRequestHeaders" in // src/tools/metrics/histograms/enums.xml. enum class ConcerningHeaderId { kConnection = 0, kCookie = 1, kCookie2 = 2, kContentTransferEncoding = 3, kDate = 4, kExpect = 5, kKeepAlive = 6, kReferer = 7, kTe = 8, kTransferEncoding = 9, kVia = 10, kMaxValue = kVia, }; using DeleteCallback = base::OnceCallback; // |delete_callback| tells the URLLoader's owner to destroy the URLLoader. // The URLLoader must be destroyed before the |url_request_context|. // The |origin_policy_manager| must always be provided for requests that // have the |obey_origin_policy| flag set. // |trust_token_helper_factory| must be non-null exactly when the request has // Trust Tokens parameters. URLLoader( net::URLRequestContext* url_request_context, mojom::NetworkServiceClient* network_service_client, mojom::NetworkContextClient* network_context_client, DeleteCallback delete_callback, mojo::PendingReceiver url_loader_receiver, int32_t options, const ResourceRequest& request, mojo::PendingRemote url_loader_client, base::Optional response_body_use_tracker, const net::NetworkTrafficAnnotationTag& traffic_annotation, const mojom::URLLoaderFactoryParams* factory_params, mojom::CrossOriginEmbedderPolicyReporter* reporter, uint32_t request_id, int keepalive_request_size, scoped_refptr resource_scheduler_client, base::WeakPtr keepalive_statistics_recorder, base::WeakPtr network_usage_accumulator, mojom::TrustedURLLoaderHeaderClient* url_loader_header_client, mojom::OriginPolicyManager* origin_policy_manager, std::unique_ptr trust_token_helper_factory, mojo::PendingRemote cookie_observer); ~URLLoader() override; // mojom::URLLoader implementation: void FollowRedirect( const std::vector& removed_headers, const net::HttpRequestHeaders& modified_headers, const net::HttpRequestHeaders& modified_cors_exempt_headers, const base::Optional& new_url) override; void SetPriority(net::RequestPriority priority, int32_t intra_priority_value) override; void PauseReadingBodyFromNet() override; void ResumeReadingBodyFromNet() override; // net::URLRequest::Delegate implementation: void OnReceivedRedirect(net::URLRequest* url_request, const net::RedirectInfo& redirect_info, bool* defer_redirect) override; void OnAuthRequired(net::URLRequest* request, const net::AuthChallengeInfo& info) override; void OnCertificateRequested(net::URLRequest* request, net::SSLCertRequestInfo* info) override; void OnSSLCertificateError(net::URLRequest* request, int net_error, const net::SSLInfo& info, bool fatal) override; void OnResponseStarted(net::URLRequest* url_request, int net_error) override; void OnReadCompleted(net::URLRequest* url_request, int bytes_read) override; // These methods are called by the network delegate to forward these events to // the |header_client_|. int OnBeforeStartTransaction(net::CompletionOnceCallback callback, net::HttpRequestHeaders* headers); int OnHeadersReceived( net::CompletionOnceCallback callback, const net::HttpResponseHeaders* original_response_headers, scoped_refptr* override_response_headers, const net::IPEndPoint& endpoint, base::Optional* preserve_fragment_on_redirect_url); // mojom::AuthChallengeResponder: void OnAuthCredentials( const base::Optional& credentials) override; // mojom::ClientCertificateResponder: void ContinueWithCertificate( const scoped_refptr& x509_certificate, const std::string& provider_name, const std::vector& algorithm_preferences, mojo::PendingRemote ssl_private_key) override; void ContinueWithoutCertificate() override; void CancelRequest() override; net::LoadState GetLoadStateForTesting() const; int32_t GetRenderFrameId() const; int32_t GetProcessId() const; uint32_t GetResourceType() const; // Whether this URLLoader should allow sending/setting cookies for requests // with |url| and |site_for_cookies|. This decision is based on the options // passed to URLLoaderFactory::CreateLoaderAndStart(). bool AllowCookies(const GURL& url, const net::SiteForCookies& site_for_cookies) const; const net::HttpRequestHeaders& custom_proxy_pre_cache_headers() const { return custom_proxy_pre_cache_headers_; } const net::HttpRequestHeaders& custom_proxy_post_cache_headers() const { return custom_proxy_post_cache_headers_; } const base::Optional& new_redirect_url() const { return new_redirect_url_; } const base::Optional& devtools_request_id() const { return devtools_request_id_; } void SetAllowReportingRawHeaders(bool allow); // Gets the URLLoader associated with this request. static URLLoader* ForRequest(const net::URLRequest& request); static const void* const kUserDataKey; static void LogConcerningRequestHeaders( const net::HttpRequestHeaders& request_headers, bool added_during_redirect); static bool HasFetchStreamingUploadBody(const ResourceRequest*); private: // This class is used to set the URLLoader as user data on a URLRequest. This // is used instead of URLLoader directly because SetUserData requires a // std::unique_ptr. This is safe because URLLoader owns the URLRequest, so is // guaranteed to outlive it. class UnownedPointer : public base::SupportsUserData::Data { public: explicit UnownedPointer(URLLoader* pointer) : pointer_(pointer) {} URLLoader* get() const { return pointer_; } private: URLLoader* const pointer_; DISALLOW_COPY_AND_ASSIGN(UnownedPointer); }; class FileOpenerForUpload; friend class FileOpenerForUpload; // An enum class representing the result of keepalive requests. This is used // for UMA so do NOT re-assign values. enum class KeepaliveRequestResult { kOk = 0, kMojoConnectionErrorBeforeResponseArrival = 1, kMojoConnectionErrorAfterResponseArrival = 2, kErrorBeforeResponseArrival = 3, kErrorAfterResponseArrival = 4, kMaxValue = kErrorAfterResponseArrival, }; void OpenFilesForUpload(const ResourceRequest& request); void SetUpUpload(const ResourceRequest& request, int error_code, const std::vector opened_files); // A request with Trust Tokens parameters will (assuming preconditions pass // and operations are successful) have one TrustTokenRequestHelper::Begin // executed against the request and one TrustTokenRequestHelper::Finalize // executed against its response. // // Outbound control flow: // // Start in BeginTrustTokenOperationIfNecessaryAndThenScheduleStart // - If there are no Trust Tokens parameters, immediately ScheduleStart. // - Otherwise: // - asynchronously construct a TrustTokenRequestHelper; // - receive the helper (or an error) in OnDoneConstructingTrustTokenHelper // and, if an error, fail the request; // - execute TrustTokenRequestHelper::Begin against the helper; // - receive the result in OnDoneBeginningTrustTokenOperation; // - if successful, ScheduleStart; if there was an error, fail. // // (Inbound, response handling just takes a synchronous Finalize call.) void BeginTrustTokenOperationIfNecessaryAndThenScheduleStart( const ResourceRequest& request); void OnDoneConstructingTrustTokenHelper( mojom::TrustTokenOperationType type, TrustTokenStatusOrRequestHelper status_or_helper); void OnDoneBeginningTrustTokenOperation( mojom::TrustTokenOperationStatus status); // Continuation of |OnResponseStarted| after possibly asynchronously // concluding the request's Trust Tokens operation. void ContinueOnResponseStarted(net::URLRequest* url_request, int net_error); void ScheduleStart(); void ReadMore(); void DidRead(int num_bytes, bool completed_synchronously); void NotifyCompleted(int error_code); void RecordKeepaliveResult(KeepaliveRequestResult result); void OnMojoDisconnect(); void OnResponseBodyStreamConsumerClosed(MojoResult result); void OnResponseBodyStreamReady(MojoResult result); void DeleteSelf(); void SendResponseToClient(); void CompletePendingWrite(bool success); void SetRawResponseHeaders(scoped_refptr); void SetRawRequestHeadersAndNotify(net::HttpRawRequestHeaders); void SendUploadProgress(const net::UploadProgress& progress); void OnUploadProgressACK(); void OnSSLCertificateErrorResponse(const net::SSLInfo& ssl_info, int net_error); bool HasDataPipe() const; void RecordBodyReadFromNetBeforePausedIfNeeded(); void ResumeStart(); void OnBeforeSendHeadersComplete( net::CompletionOnceCallback callback, net::HttpRequestHeaders* out_headers, int result, const base::Optional& headers); void OnHeadersReceivedComplete( net::CompletionOnceCallback callback, scoped_refptr* out_headers, base::Optional* out_preserve_fragment_on_redirect_url, int result, const base::Optional& headers, const base::Optional& preserve_fragment_on_redirect_url); void CompleteBlockedResponse( int error_code, bool should_report_corb_blocking, base::Optional reason = base::nullopt); enum BlockResponseForCorbResult { // Returned when caller of BlockResponseForCorb doesn't need to continue, // because the request will be cancelled soon. kWillCancelRequest, // Returned when the caller of BlockResponseForCorb should continue // processing the request (e.g. by calling ReadMore as necessary). kContinueRequest, }; BlockResponseForCorbResult BlockResponseForCorb(); void ReportFlaggedResponseCookies(); void StartReading(); void OnOriginPolicyManagerRetrieveDone(const OriginPolicy& origin_policy); net::URLRequestContext* url_request_context_; mojom::NetworkServiceClient* network_service_client_; mojom::NetworkContextClient* network_context_client_; DeleteCallback delete_callback_; int32_t options_; bool corb_detachable_; int resource_type_; bool is_load_timing_enabled_; bool has_received_response_ = false; bool has_recorded_keepalive_result_ = false; // URLLoaderFactory is guaranteed to outlive URLLoader, so it is safe to // store a raw pointer to mojom::URLLoaderFactoryParams. const mojom::URLLoaderFactoryParams* const factory_params_; // This also belongs to URLLoaderFactory and outlives this loader. mojom::CrossOriginEmbedderPolicyReporter* const coep_reporter_; int render_frame_id_; uint32_t request_id_; const int keepalive_request_size_; const bool keepalive_; const bool do_not_prompt_for_login_; std::unique_ptr url_request_; mojo::Receiver receiver_; mojo::Receiver auth_challenge_responder_receiver_{this}; mojo::Receiver client_cert_responder_receiver_{this}; mojo::Remote url_loader_client_; int64_t total_written_bytes_ = 0; mojo::ScopedDataPipeProducerHandle response_body_stream_; base::Optional response_body_use_tracker_; scoped_refptr pending_write_; uint32_t pending_write_buffer_size_ = 0; uint32_t pending_write_buffer_offset_ = 0; mojo::SimpleWatcher writable_handle_watcher_; mojo::SimpleWatcher peer_closed_handle_watcher_; // True if there's a URLRequest::Read() call in progress. bool read_in_progress_ = false; // Used when deferring sending the data to the client until mime sniffing is // finished. mojom::URLResponseHeadPtr response_; mojo::ScopedDataPipeConsumerHandle consumer_handle_; // Sniffing state. std::unique_ptr corb_analyzer_; bool is_more_corb_sniffing_needed_ = false; bool is_more_mime_sniffing_needed_ = false; std::unique_ptr resource_scheduler_request_handle_; // Whether client requested raw headers. const bool want_raw_headers_; // Whether we actually should report them. bool report_raw_headers_; net::HttpRawRequestHeaders raw_request_headers_; scoped_refptr raw_response_headers_; std::unique_ptr upload_progress_tracker_; // Holds the URL of a redirect if it's currently deferred. std::unique_ptr deferred_redirect_url_; // If |new_url| is given to FollowRedirect() it's saved here, so that it can // be later referred to from NetworkContext::OnBeforeURLRequestInternal, which // is called from NetworkDelegate::NotifyBeforeURLRequest. base::Optional new_redirect_url_; // The ID that DevTools uses to track network requests. It is generated in the // renderer process and is only present when DevTools is enabled in the // renderer. const base::Optional devtools_request_id_; bool should_pause_reading_body_ = false; // The response body stream is open, but transferring data is paused. bool paused_reading_body_ = false; // Whether to update |body_read_before_paused_| after the pending read is // completed (or when the response body stream is closed). bool update_body_read_before_paused_ = false; // The number of bytes obtained by the reads initiated before the last // PauseReadingBodyFromNet() call. -1 means the request hasn't been paused. // The body may be read from cache or network. So even if this value is not // -1, we still need to check whether it is from network before reporting it // as BodyReadFromNetBeforePaused. int64_t body_read_before_paused_ = -1; // This is used to compute the delta since last time received // encoded body size was reported to the client. int64_t reported_total_encoded_bytes_ = 0; // Indicates whether this request was made by a CORB-excluded request type and // was not using CORS. Such requests are exempt from blocking, while other // CORB-excluded requests must be blocked if the CORS check fails. bool is_nocors_corb_excluded_request_ = false; mojom::RequestMode request_mode_; bool has_user_activation_; mojom::RequestDestination request_destination_ = mojom::RequestDestination::kEmpty; scoped_refptr resource_scheduler_client_; base::WeakPtr keepalive_statistics_recorder_; base::WeakPtr network_usage_accumulator_; bool first_auth_attempt_; std::unique_ptr throttling_token_; net::HttpRequestHeaders custom_proxy_pre_cache_headers_; net::HttpRequestHeaders custom_proxy_post_cache_headers_; // Indicates the originating frame of the request, see // network::ResourceRequest::fetch_window_id for details. base::Optional fetch_window_id_; mojo::Remote header_client_; std::unique_ptr file_opener_for_upload_; // Will only be set for requests that have |obey_origin_policy| set. mojom::OriginPolicyManager* origin_policy_manager_; // If the request is configured for Trust Tokens // (https://github.com/WICG/trust-token-api) protocol operations, annotates // the request with the pertinent request headers and, on receiving the // corresponding response, processes and strips Trust Tokens response headers. // // For requests configured for Trust Tokens operations, |trust_token_helper_| // is constructed (using |trust_token_helper_factory_|) just before the // outbound (Begin) operation; for requests without associated Trust Tokens // operations, the field remains null, as does |trust_token_helper_factory_|. std::unique_ptr trust_token_helper_; std::unique_ptr trust_token_helper_factory_; // The cached result of the request's Trust Tokens protocol operation, if any. // This can describe the result of either an outbound (request-annotating) // protocol step or an inbound (response header reading) step; some error // codes, like kFailedPrecondition (outbound) and kBadResponse (inbound) are // specific to one direction. base::Optional trust_token_status_; // Stores ResourceRequest::isolated_world_origin. // // Note that |isolated_world_origin_| is unreliable (i.e. always // base::nullopt) when URLLoaderFactoryParams::ignore_isolated_world_origin // may be |true| (e.g. when the CorbAllowlistAlsoAppliesToOorCors feature is // enabled). // // TODO(lukasza): https://crbug.com/920638: Remove // |isolated_world_origin_| once we gather enough UMA and UKM data. const base::Optional isolated_world_origin_; // Observer listening to all cookie reads and writes made by this request. mojo::Remote cookie_observer_; // Indicates |url_request_| is fetch upload request and that has streaming // body. const bool has_fetch_streaming_upload_body_; // Indicates whether fetch upload streaming is allowed/rejected over H/1. // Even if this is false but there is a QUIC/H2 stream, the upload is allowed. const bool allow_http1_for_streaming_upload_; base::WeakPtrFactory weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(URLLoader); }; } // namespace network #endif // SERVICES_NETWORK_URL_LOADER_H_