| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef CONTENT_RENDERER_SERVICE_WORKER_SERVICE_WORKER_SUBRESOURCE_LOADER_H_ |
| #define CONTENT_RENDERER_SERVICE_WORKER_SERVICE_WORKER_SUBRESOURCE_LOADER_H_ |
| |
| #include <optional> |
| |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/scoped_observation.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "content/common/content_export.h" |
| #include "content/common/service_worker/forwarded_race_network_request_url_loader_factory.h" |
| #include "content/common/service_worker/race_network_request_url_loader_client.h" |
| #include "content/common/service_worker/service_worker_resource_loader.h" |
| #include "content/common/service_worker/service_worker_router_evaluator.h" |
| #include "content/renderer/service_worker/controller_service_worker_connector.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/receiver_set.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "net/traffic_annotation/network_traffic_annotation.h" |
| #include "net/url_request/redirect_info.h" |
| #include "services/network/public/cpp/resource_request.h" |
| #include "services/network/public/mojom/url_loader.mojom.h" |
| #include "services/network/public/mojom/url_loader_factory.mojom.h" |
| #include "services/network/public/mojom/url_response_head.mojom-forward.h" |
| #include "third_party/blink/public/common/service_worker/service_worker_status_code.h" |
| #include "third_party/blink/public/mojom/blob/blob.mojom-forward.h" |
| #include "third_party/blink/public/mojom/blob/blob.mojom.h" |
| #include "third_party/blink/public/mojom/service_worker/service_worker_event_status.mojom-forward.h" |
| #include "third_party/blink/public/mojom/service_worker/service_worker_fetch_response_callback.mojom.h" |
| #include "third_party/blink/public/mojom/service_worker/service_worker_stream_handle.mojom-forward.h" |
| |
| namespace network { |
| class SharedURLLoaderFactory; |
| } // namespace network |
| |
| namespace content { |
| |
| class ControllerServiceWorkerConnector; |
| class ServiceWorkerSubresourceLoaderFactory; |
| |
| // A custom URLLoader implementation used by Service Worker controllees |
| // for loading subresources via the controller Service Worker. |
| // Currently an instance of this class is created and used only on |
| // the main thread (while the implementation itself is thread agnostic). |
| class CONTENT_EXPORT ServiceWorkerSubresourceLoader |
| : public network::mojom::URLLoader, |
| public blink::mojom::ServiceWorkerFetchResponseCallback, |
| public ControllerServiceWorkerConnector::Observer, |
| public ServiceWorkerResourceLoader { |
| public: |
| // See the comments for ServiceWorkerSubresourceLoaderFactory's ctor (below) |
| // to see how each parameter is used. |
| ServiceWorkerSubresourceLoader( |
| mojo::PendingReceiver<network::mojom::URLLoader>, |
| int32_t request_id, |
| uint32_t options, |
| const network::ResourceRequest& resource_request, |
| mojo::PendingRemote<network::mojom::URLLoaderClient> client, |
| const net::MutableNetworkTrafficAnnotationTag& traffic_annotation, |
| scoped_refptr<ControllerServiceWorkerConnector> controller_connector, |
| scoped_refptr<network::SharedURLLoaderFactory> fallback_factory, |
| scoped_refptr<base::SequencedTaskRunner> task_runner, |
| base::WeakPtr<ServiceWorkerSubresourceLoaderFactory> |
| service_worker_subresource_loader_factory); |
| |
| ServiceWorkerSubresourceLoader(const ServiceWorkerSubresourceLoader&) = |
| delete; |
| ServiceWorkerSubresourceLoader& operator=( |
| const ServiceWorkerSubresourceLoader&) = delete; |
| |
| ~ServiceWorkerSubresourceLoader() override; |
| |
| // ControllerServiceWorkerConnector::Observer overrides: |
| void OnConnectionClosed() override; |
| |
| private: |
| class StreamWaiter; |
| enum class Status { |
| kNotStarted, |
| // |binding_| is bound and the fetch event is being dispatched to the |
| // service worker. |
| kStarted, |
| // A redirect happened, waiting for FollowRedirect(). |
| kSentRedirect, |
| // The response head has been sent to |url_loader_client_|. |
| kSentHeader, |
| // The data pipe for the response body has been sent to |
| // |url_loader_client_|. The body is being written to the pipe. |
| kSentBody, |
| // OnComplete() was called on |url_loader_client_|, or fallback to network |
| // occurred so the request was not handled. |
| kCompleted, |
| }; |
| |
| void OnMojoDisconnect(); |
| |
| void StartRequest(const network::ResourceRequest& resource_request); |
| void DispatchFetchEvent(); |
| void DispatchFetchEventForSubresource(); |
| void OnFetchEventFinished(blink::mojom::ServiceWorkerEventStatus status); |
| // Called when this loader no longer needs to restart dispatching the fetch |
| // event on failure. Null |status| means the event dispatch was not attempted. |
| void SettleFetchEventDispatch( |
| std::optional<blink::ServiceWorkerStatusCode> status); |
| |
| // blink::mojom::ServiceWorkerFetchResponseCallback overrides: |
| void OnResponse( |
| blink::mojom::FetchAPIResponsePtr response, |
| blink::mojom::ServiceWorkerFetchEventTimingPtr timing) override; |
| void OnResponseStream( |
| blink::mojom::FetchAPIResponsePtr response, |
| blink::mojom::ServiceWorkerStreamHandlePtr body_as_stream, |
| blink::mojom::ServiceWorkerFetchEventTimingPtr timing) override; |
| void OnFallback( |
| std::optional<network::DataElementChunkedDataPipe> request_body, |
| blink::mojom::ServiceWorkerFetchEventTimingPtr timing) override; |
| |
| void UpdateResponseTiming( |
| blink::mojom::ServiceWorkerFetchEventTimingPtr timing); |
| |
| void StartResponse(blink::mojom::FetchAPIResponsePtr response, |
| blink::mojom::ServiceWorkerStreamHandlePtr body_as_stream); |
| |
| // network::mojom::URLLoader overrides: |
| void FollowRedirect( |
| const std::vector<std::string>& removed_headers, |
| const net::HttpRequestHeaders& modified_headers, |
| const net::HttpRequestHeaders& modified_cors_exempt_headers, |
| const std::optional<GURL>& new_url) override; |
| void SetPriority(net::RequestPriority priority, |
| int intra_priority_value) override; |
| void PauseReadingBodyFromNet() override; |
| void ResumeReadingBodyFromNet() override; |
| |
| int StartBlobReading(mojo::ScopedDataPipeConsumerHandle* body_pipe); |
| void OnSideDataReadingComplete(mojo::ScopedDataPipeConsumerHandle data_pipe, |
| std::optional<mojo_base::BigBuffer> metadata); |
| void OnBodyReadingComplete(int net_error); |
| |
| // ServiceWorkerResourceLoader overrides: |
| void CommitResponseHeaders( |
| const network::mojom::URLResponseHeadPtr&) override; |
| |
| // Calls url_loader_client_->OnReceiveResponse() with given |response_head|, |
| // |response_body|, and |cached_metadata|. |
| void CommitResponseBody( |
| const network::mojom::URLResponseHeadPtr& response_head, |
| mojo::ScopedDataPipeConsumerHandle response_body, |
| std::optional<mojo_base::BigBuffer> cached_metadata) override; |
| |
| // Creates and sends an empty response's body with the net::OK status. |
| // Sends net::ERR_INSUFFICIENT_RESOURCES when it can't be created. |
| void CommitEmptyResponseAndComplete() override; |
| |
| // Calls url_loader_client_->OnComplete(). Expected to be called after |
| // CommitResponseHeaders (i.e. status_ == kSentHeader). |
| void CommitCompleted(int error_code, const char* reason) override; |
| |
| // Calls url_loader_client_->OnReceiveRedirect(). Sends too many redirects |
| // error if it hits the redirect limit. |
| void HandleRedirect( |
| const net::RedirectInfo& redirect_info, |
| const network::mojom::URLResponseHeadPtr& response_head) override; |
| |
| bool IsMainResourceLoader() override; |
| |
| // Record loading milestones. Called after a response is completed or |
| // a request is fall back to network. Never called when an error is |
| // occurred. |
| bool InitRecordTimingMetricsIfEligible( |
| const net::LoadTimingInfo& load_timing); |
| // Called when the fetch handler handles the request. |
| void RecordTimingMetricsForFetchHandlerHandledCase(); |
| // Called when the fetch handler doesn't handle the requset (i.e. network |
| // fallback case). |
| void RecordTimingMetricsForNetworkFallbackCase(); |
| // Called when the response from RaceNetworkRequest is faster than the |
| // response from the fetch handler. |
| void RecordTimingMetricsForRaceNetworkReqestCase(); |
| // Time between the request is made and the request is routed to this loader. |
| void RecordStartToForwardServiceWorkerTiming( |
| const net::LoadTimingInfo& load_timing); |
| // Mojo message delay. If the controller service worker lives in the same |
| // process this captures service worker thread -> background thread delay. |
| // Otherwise, this captures IPC delay (this renderer process -> other |
| // renderer process). |
| void RecordFetchHandlerEndToResponseReceivedTiming( |
| const net::LoadTimingInfo& load_timing); |
| // Time spent reading response body. |
| void RecordResponseReceivedToCompletedTiming( |
| const net::LoadTimingInfo& load_timing); |
| |
| // Time spent for service worker startup including mojo message delay. |
| void RecordForwardServiceWorkerToWorkerReadyTiming( |
| const net::LoadTimingInfo& load_timing); |
| // Time spent by fetch handlers. |
| void RecordWorkerReadyToFetchHandlerEndTiming( |
| const net::LoadTimingInfo& load_timing); |
| // Renderer -> Browser IPC delay (network fallback case). |
| void RecordFetchHandlerEndToFallbackNetworkTiming( |
| const net::LoadTimingInfo& load_timing); |
| // Time between the request is made and complete reading response body. |
| void RecordStartToCompletedTiming(const net::LoadTimingInfo& load_timing); |
| |
| base::TimeTicks completion_time_; |
| |
| void TransitionToStatus(Status new_status); |
| |
| // If eligible, dispatch the network request which races the ServiceWorker |
| // fetch handler. |
| bool MaybeStartRaceNetworkRequest(); |
| |
| // Returns false if fails to start race network request. |
| // A caller should handle the case. |
| bool StartRaceNetworkRequest(); |
| |
| std::optional<ServiceWorkerRouterEvaluator::Result> |
| MaybeEvaluateRouterConditions() const; |
| |
| bool MaybeStartAutoPreload(); |
| |
| void DidCacheStorageMatch(base::TimeTicks event_dispatch_time, |
| blink::mojom::MatchResultPtr result); |
| |
| network::mojom::URLResponseHeadPtr response_head_; |
| std::optional<net::RedirectInfo> redirect_info_; |
| int redirect_limit_; |
| |
| mojo::Remote<network::mojom::URLLoaderClient> url_loader_client_; |
| mojo::Receiver<network::mojom::URLLoader> url_loader_receiver_; |
| |
| // For handling FetchEvent response. |
| mojo::Receiver<blink::mojom::ServiceWorkerFetchResponseCallback> |
| response_callback_receiver_{this}; |
| // The blob needs to be held while it's read to keep it alive. |
| mojo::Remote<blink::mojom::Blob> body_as_blob_; |
| uint64_t body_as_blob_size_; |
| // The blob needs to be held while it's read to keep it alive. |
| mojo::Remote<blink::mojom::Blob> side_data_as_blob_; |
| |
| scoped_refptr<ControllerServiceWorkerConnector> controller_connector_; |
| |
| // Observes |controller_connector_| while this loader dispatches a fetch event |
| // to the controller. If a broken connection is observed, this loader attempts |
| // to restart the controller and dispatch the event again. |
| base::ScopedObservation<ControllerServiceWorkerConnector, |
| ControllerServiceWorkerConnector::Observer> |
| controller_connector_observation_{this}; |
| bool fetch_request_restarted_; |
| bool body_reading_complete_; |
| bool side_data_reading_complete_; |
| |
| // These are given by the constructor (as the params for |
| // URLLoaderFactory::CreateLoaderAndStart). |
| const int request_id_; |
| const uint32_t options_; |
| net::MutableNetworkTrafficAnnotationTag traffic_annotation_; |
| |
| // |resource_request_| is initialized in the constructor, and may change |
| // over the lifetime of this loader due to redirects. |
| network::ResourceRequest resource_request_; |
| |
| std::unique_ptr<StreamWaiter> stream_waiter_; |
| |
| // For network fallback. |
| scoped_refptr<network::SharedURLLoaderFactory> fallback_factory_; |
| |
| Status status_ = Status::kNotStarted; |
| |
| // The task runner where this loader is running. |
| scoped_refptr<base::SequencedTaskRunner> task_runner_; |
| |
| base::WeakPtr<ServiceWorkerSubresourceLoaderFactory> |
| service_worker_subresource_loader_factory_; |
| |
| blink::mojom::ServiceWorkerFetchEventTimingPtr fetch_event_timing_; |
| network::mojom::FetchResponseSource response_source_; |
| |
| scoped_refptr<network::SharedURLLoaderFactory> |
| race_network_request_url_loader_factory_; |
| std::optional<ServiceWorkerRaceNetworkRequestURLLoaderClient> |
| race_network_request_loader_client_; |
| std::optional<ServiceWorkerForwardedRaceNetworkRequestURLLoaderFactory> |
| forwarded_race_network_request_url_loader_factory_; |
| mojo::PendingRemote<network::mojom::URLLoaderFactory> |
| remote_forwarded_race_network_request_url_loader_factory_; |
| |
| base::WeakPtrFactory<ServiceWorkerSubresourceLoader> weak_factory_{this}; |
| }; |
| |
| // A custom URLLoaderFactory implementation used by Service Worker controllees |
| // for loading subresources via the controller Service Worker. |
| // Self destroys when no more bindings exist. |
| class CONTENT_EXPORT ServiceWorkerSubresourceLoaderFactory |
| : public network::mojom::URLLoaderFactory { |
| public: |
| // |controller_connector| is used to get a connection to the controller |
| // ServiceWorker. |
| // |fallback_factory| is used to get the associated loading context's |
| // default URLLoaderFactory for network fallback. This should be the |
| // URLLoaderFactory that directly goes to network without going through |
| // any custom URLLoader factories. |
| // |task_runner| is the runner where this loader runs. In production it runs, |
| // on a background thread. |
| static void Create( |
| scoped_refptr<ControllerServiceWorkerConnector> controller_connector, |
| scoped_refptr<network::SharedURLLoaderFactory> fallback_factory, |
| mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver, |
| scoped_refptr<base::SequencedTaskRunner> task_runner); |
| |
| ServiceWorkerSubresourceLoaderFactory( |
| const ServiceWorkerSubresourceLoaderFactory&) = delete; |
| ServiceWorkerSubresourceLoaderFactory& operator=( |
| const ServiceWorkerSubresourceLoaderFactory&) = delete; |
| |
| ~ServiceWorkerSubresourceLoaderFactory() override; |
| |
| // network::mojom::URLLoaderFactory overrides: |
| void CreateLoaderAndStart( |
| mojo::PendingReceiver<network::mojom::URLLoader> receiver, |
| int32_t request_id, |
| uint32_t options, |
| const network::ResourceRequest& resource_request, |
| mojo::PendingRemote<network::mojom::URLLoaderClient> client, |
| const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) |
| override; |
| void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) |
| override; |
| |
| private: |
| ServiceWorkerSubresourceLoaderFactory( |
| scoped_refptr<ControllerServiceWorkerConnector> controller_connector, |
| scoped_refptr<network::SharedURLLoaderFactory> fallback_factory, |
| mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver, |
| scoped_refptr<base::SequencedTaskRunner> task_runner); |
| |
| void OnMojoDisconnect(); |
| |
| scoped_refptr<ControllerServiceWorkerConnector> controller_connector_; |
| |
| // Used when a request falls back to network. |
| scoped_refptr<network::SharedURLLoaderFactory> fallback_factory_; |
| |
| mojo::ReceiverSet<network::mojom::URLLoaderFactory> receivers_; |
| |
| // The task runner where this factory is running. |
| scoped_refptr<base::SequencedTaskRunner> task_runner_; |
| |
| base::WeakPtrFactory<ServiceWorkerSubresourceLoaderFactory> weak_factory_{ |
| this}; |
| }; |
| |
| } // namespace content |
| |
| #endif // CONTENT_RENDERER_SERVICE_WORKER_SERVICE_WORKER_SUBRESOURCE_LOADER_H_ |