// 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 CONTENT_COMMON_THROTTLING_URL_LOADER_H_ #define CONTENT_COMMON_THROTTLING_URL_LOADER_H_ #include #include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/strings/string_piece.h" #include "base/threading/thread_task_runner_handle.h" #include "content/common/content_export.h" #include "content/common/possibly_associated_interface_ptr.h" #include "content/public/common/url_loader_throttle.h" #include "mojo/public/cpp/bindings/binding.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "services/network/public/mojom/url_loader.mojom.h" #include "services/network/public/mojom/url_loader_factory.mojom.h" namespace base { class SingleThreadTaskRunner; } namespace content { // ThrottlingURLLoader is a wrapper around the // network::mojom::URLLoader[Factory] interfaces. It applies a list of // URLLoaderThrottle instances which could defer, resume restart or cancel the // URL loading. If the Mojo connection fails during the request it is canceled // with net::ERR_ABORTED. class CONTENT_EXPORT ThrottlingURLLoader : public network::mojom::URLLoaderClient { public: // |client| must stay alive during the lifetime of the returned object. Please // note that the request may not start immediately since it could be deferred // by throttles. static std::unique_ptr CreateLoaderAndStart( scoped_refptr factory, std::vector> throttles, int32_t routing_id, int32_t request_id, uint32_t options, network::ResourceRequest* url_request, network::mojom::URLLoaderClient* client, const net::NetworkTrafficAnnotationTag& traffic_annotation, scoped_refptr task_runner); ~ThrottlingURLLoader() override; void FollowRedirect(const std::vector& removed_headers, const net::HttpRequestHeaders& modified_headers); // Follows a redirect, calling CreateLoaderAndStart() on the factory. This // is useful if the factory uses different loaders for different URLs. void FollowRedirectForcingRestart(); void SetPriority(net::RequestPriority priority, int32_t intra_priority_value); // Restarts the load immediately with |factory| and |url_loader_options|. // It must only be called when the following conditions are met: // 1. The request already started and the original factory decided to not // handle the request. This condition is required because throttles are not // consulted prior to restarting. // 2. The original factory did not call URLLoaderClient callbacks (e.g., // OnReceiveResponse). // This function is useful in the case of service worker network fallback. void RestartWithFactory( scoped_refptr factory, uint32_t url_loader_options); // Disconnect the forwarding URLLoaderClient and the URLLoader. Returns the // datapipe endpoints. network::mojom::URLLoaderClientEndpointsPtr Unbind(); // Sets the forwarding client to receive all subsequent notifications. void set_forwarding_client(network::mojom::URLLoaderClient* client) { forwarding_client_ = client; } bool response_intercepted() const { return response_intercepted_; } private: class ForwardingThrottleDelegate; ThrottlingURLLoader( std::vector> throttles, network::mojom::URLLoaderClient* client, const net::NetworkTrafficAnnotationTag& traffic_annotation); void Start(scoped_refptr factory, int32_t routing_id, int32_t request_id, uint32_t options, network::ResourceRequest* url_request, scoped_refptr task_runner); void StartNow(); void RestartWithFlagsNow(); // Processes the result of a URLLoaderThrottle call, adding the throttle to // the blocking set if it deferred and updating |*should_defer| accordingly. // Returns |true| if the request should continue to be processed (regardless // of whether it's been deferred) or |false| if it's been cancelled. bool HandleThrottleResult(URLLoaderThrottle* throttle, bool throttle_deferred, bool* should_defer); // Stops a given throttle from deferring the request. If this was not the last // deferring throttle, the request remains deferred. Otherwise it resumes // progress. void StopDeferringForThrottle(URLLoaderThrottle* throttle); void RestartWithFlags(int additional_load_flags); // network::mojom::URLLoaderClient implementation: void OnReceiveResponse( const network::ResourceResponseHead& response_head) override; void OnReceiveRedirect( const net::RedirectInfo& redirect_info, const network::ResourceResponseHead& response_head) override; void OnUploadProgress(int64_t current_position, int64_t total_size, OnUploadProgressCallback ack_callback) override; void OnReceiveCachedMetadata(const std::vector& data) override; void OnTransferSizeUpdated(int32_t transfer_size_diff) override; void OnStartLoadingResponseBody( mojo::ScopedDataPipeConsumerHandle body) override; void OnComplete(const network::URLLoaderCompletionStatus& status) override; void OnClientConnectionError(); void CancelWithError(int error_code, base::StringPiece custom_reason); void Resume(); void SetPriority(net::RequestPriority priority); void UpdateDeferredResponseHead( const network::ResourceResponseHead& new_response_head); void PauseReadingBodyFromNet(URLLoaderThrottle* throttle); void ResumeReadingBodyFromNet(URLLoaderThrottle* throttle); void InterceptResponse( network::mojom::URLLoaderPtr new_loader, network::mojom::URLLoaderClientRequest new_client_request, network::mojom::URLLoaderPtr* original_loader, network::mojom::URLLoaderClientRequest* original_client_request); // Disconnects the client connection and releases the URLLoader. void DisconnectClient(base::StringPiece custom_description); enum DeferredStage { DEFERRED_NONE, DEFERRED_START, DEFERRED_REDIRECT, DEFERRED_BEFORE_RESPONSE, DEFERRED_RESPONSE, DEFERRED_COMPLETE }; DeferredStage deferred_stage_ = DEFERRED_NONE; bool loader_completed_ = false; struct ThrottleEntry { ThrottleEntry(ThrottlingURLLoader* loader, std::unique_ptr the_throttle); ThrottleEntry(ThrottleEntry&& other); ~ThrottleEntry(); ThrottleEntry& operator=(ThrottleEntry&& other); std::unique_ptr delegate; std::unique_ptr throttle; private: DISALLOW_COPY_AND_ASSIGN(ThrottleEntry); }; std::vector throttles_; std::set deferring_throttles_; std::set pausing_reading_body_from_net_throttles_; // NOTE: This may point to a native implementation (instead of a Mojo proxy // object). And it is possible that the implementation of |forwarding_client_| // destroys this object synchronously when this object is calling into it. network::mojom::URLLoaderClient* forwarding_client_; mojo::Binding client_binding_; network::mojom::URLLoaderPtr url_loader_; struct StartInfo { StartInfo( scoped_refptr in_url_loader_factory, int32_t in_routing_id, int32_t in_request_id, uint32_t in_options, network::ResourceRequest* in_url_request, scoped_refptr in_task_runner); ~StartInfo(); scoped_refptr url_loader_factory; int32_t routing_id; int32_t request_id; uint32_t options; network::ResourceRequest url_request; // |task_runner_| is used to set up |client_binding_|. scoped_refptr task_runner; }; // Holds any info needed to start or restart the request. Used when start is // deferred or when FollowRedirectForcingRestart() is called. std::unique_ptr start_info_; struct ResponseInfo { explicit ResponseInfo( const network::ResourceResponseHead& in_response_head); ~ResponseInfo(); network::ResourceResponseHead response_head; }; // Set if response is deferred. std::unique_ptr response_info_; struct RedirectInfo { RedirectInfo(const net::RedirectInfo& in_redirect_info, const network::ResourceResponseHead& in_response_head); ~RedirectInfo(); net::RedirectInfo redirect_info; network::ResourceResponseHead response_head; }; // Set if redirect is deferred. std::unique_ptr redirect_info_; struct PriorityInfo { PriorityInfo(net::RequestPriority in_priority, int32_t in_intra_priority_value); ~PriorityInfo(); net::RequestPriority priority; int32_t intra_priority_value; }; // Set if request is deferred and SetPriority() is called. std::unique_ptr priority_info_; // Set if a throttle changed the URL in WillStartRequest. GURL throttle_will_start_redirect_url_; // Set if a throttle changed the URL in WillRedirectRequest. // Only supported with the network service. GURL throttle_will_redirect_redirect_url_; const net::NetworkTrafficAnnotationTag traffic_annotation_; uint32_t inside_delegate_calls_ = 0; // The latest request URL from where we expect a response GURL response_url_; bool response_intercepted_ = false; std::vector removed_headers_; net::HttpRequestHeaders modified_headers_; int pending_restart_flags_ = 0; bool has_pending_restart_ = false; base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(ThrottlingURLLoader); }; } // namespace content #endif // CONTENT_COMMON_THROTTLING_URL_LOADER_H_