| // Copyright 2025 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SERVICES_NETWORK_PUBLIC_CPP_CONTENT_DECODING_INTERCEPTOR_H_ |
| #define SERVICES_NETWORK_PUBLIC_CPP_CONTENT_DECODING_INTERCEPTOR_H_ |
| |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/component_export.h" |
| #include "base/functional/callback.h" |
| #include "mojo/public/cpp/system/data_pipe.h" |
| #include "net/base/net_errors.h" |
| #include "net/filter/source_stream_type.h" |
| #include "services/network/public/mojom/url_loader.mojom.h" |
| |
| namespace base { |
| class SequencedTaskRunner; |
| } |
| |
| namespace network { |
| namespace mojom { |
| class NetworkService; |
| } // namespace mojom |
| |
| class NetworkService; |
| |
| // Intercepts network requests to apply content decoding (e.g., gzip, brotli, |
| // zstd) to the response body. |
| class COMPONENT_EXPORT(NETWORK_CPP) ContentDecodingInterceptor { |
| public: |
| // Convenience type alias for the data pipe handle pair. |
| using DataPipePair = std::pair<mojo::ScopedDataPipeProducerHandle, |
| mojo::ScopedDataPipeConsumerHandle>; |
| |
| // Identifies the component or context initiating the content decoding that |
| // requires a data pipe. Used for categorizing UMA metrics. |
| // LINT.IfChange(ContentDecodingInterceptorClientType) |
| enum class ClientType { |
| kTest, |
| kURLLoaderThrottle, |
| kCommitNavigation, |
| kDownload, |
| kNavigationPreload, |
| kSignedExchange, |
| kDevTools, |
| kMaxValue = kDevTools, |
| }; |
| // LINT.ThenChange(//tools/metrics/histograms/metadata/network/histograms.xml:ContentDecodingInterceptorClientType) |
| |
| // Creates the Mojo data pipe pair (producer/consumer) used by the |
| // interceptor. Also handles test-only failure simulation and UMA logging for |
| // results. |
| // |
| // On success, this function returns an `std::optional<DataPipePair>` |
| // containing the pipe handles. On failure, it returns `std::nullopt`. |
| // Failure occurs if `mojo::CreateDataPipe` itself fails (e.g., due to |
| // resource exhaustion) or if failure is forced via the |
| // `kRendererSideContentDecodingForceMojoFailureForTesting` feature parameter |
| // for testing. |
| // |
| // Callers MUST check the return value for `std::nullopt` and handle the |
| // failure case gracefully (e.g., report net::ERR_INSUFFICIENT_RESOURCES). |
| static std::optional<DataPipePair> CreateDataPipePair(ClientType client_type); |
| |
| // Intercepts a URLLoader and its associated client, applying content decoding |
| // to the response body. The decoding is performed on the passed |
| // `worker_task_runner`. The provided `endpoints` and `body` are modified to |
| // connect the client to the decoding interceptor. |
| // Requires a valid `data_pipe_pair` (obtained from `CreateDataPipePair`) |
| // which connects the interceptor's output to the original client's input. |
| // The decoding is performed in the reverse order of the `types` vector. The |
| // `types` vector must not be empty, and must not contain |
| // SourceStreamType::kNone or SourceStreamType::kUnknown. |
| // |
| // The created interceptor is owned by the returned `endpoints`'s `url_loader` |
| // remote interface. So the caller must keep the returned `endpoints`'s |
| // `url_loader` alive until the caller receives the OnComplete callback via |
| // the returned `endpoints`'s `url_loader_client`. |
| // |
| // IMPORTANT NOTE: This method performs decoding, so it MUST NOT be used in |
| // the browser process, other than the network service on Android. |
| static void Intercept( |
| const std::vector<net::SourceStreamType>& types, |
| network::mojom::URLLoaderClientEndpointsPtr& endpoints, |
| mojo::ScopedDataPipeConsumerHandle& body, |
| DataPipePair data_pipe_pair, |
| scoped_refptr<base::SequencedTaskRunner> worker_task_runner); |
| |
| // Intercepts a URLLoader and its associated client, applying content decoding |
| // to the response body. The decoding is performed on the passed |
| // `worker_task_runner`. This version uses a callback to swap the |
| // URLLoaderClientEndpoints and data pipe, rather than modifying them |
| // directly. This is useful when integrating with |
| // blink::URLLoaderThrottle::Delegate's InterceptResponse() method. |
| // Requires a valid `data_pipe_pair` (obtained from `CreateDataPipePair`) |
| // which connects the interceptor's output to the original client's input. |
| // The decoding is performed in the reverse order of the `types` vector. |
| // The `types` vector must not be empty, and must not contain |
| // SourceStreamType::kNone or SourceStreamType::kUnknown. |
| // |
| // The created interceptor is owned by the returned `endpoints`'s `url_loader` |
| // remote interface. So the caller must keep the returned `endpoints`'s |
| // `url_loader` alive until the caller receives the OnComplete callback via |
| // the returned `endpoints`'s `url_loader_client`. |
| // |
| // IMPORTANT NOTE: This method performs decoding, so it MUST NOT be used in |
| // the browser process, other than the network service on Android. |
| static void Intercept( |
| const std::vector<net::SourceStreamType>& types, |
| DataPipePair data_pipe_pair, |
| base::OnceCallback< |
| void(network::mojom::URLLoaderClientEndpointsPtr& endpoints, |
| mojo::ScopedDataPipeConsumerHandle& body)> swap_callback, |
| scoped_refptr<base::SequencedTaskRunner> worker_task_runner); |
| |
| // Intercepts a URLLoader and its associated client, applying content decoding |
| // to the response body. The decoding is performed on the passed |
| // `worker_task_runner`. This version is useful when a |
| // ScopedDataPipeProducerHandle is provided by the caller side. |
| // |
| // IMPORTANT NOTE: This method performs decoding, so it MUST NOT be used in |
| // the browser process, other than the network service on Android. |
| static void Intercept( |
| const std::vector<net::SourceStreamType>& types, |
| mojo::ScopedDataPipeConsumerHandle source_body, |
| mojo::ScopedDataPipeProducerHandle dest_body, |
| mojo::PendingRemote<network::mojom::URLLoader> source_url_loader, |
| mojo::PendingReceiver<network::mojom::URLLoaderClient> |
| source_url_loader_client, |
| mojo::PendingReceiver<network::mojom::URLLoader> dest_url_loader, |
| mojo::PendingRemote<network::mojom::URLLoaderClient> |
| dest_url_loader_client, |
| scoped_refptr<base::SequencedTaskRunner> worker_task_runner); |
| |
| // Requests the network service process to intercept a URLLoader connection |
| // and perform content decoding based on the specified `types`. |
| // |
| // This method is intended for use by the browser process when it needs |
| // decoding for a response (e.g., for downloads or Signed Exchanges) even |
| // though client-side decoding might have been initially requested for the |
| // original load. It achieves this by calling the |
| // `NetworkService::InterceptUrlLoaderForBodyDecoding` Mojo method. |
| // |
| // Requires a valid `data_pipe_pair` (obtained from `CreateDataPipePair`) |
| // which connects the interceptor's output to the original client's input. |
| // |
| // The actual decoding work happens safely within the network service process. |
| static void InterceptOnNetworkService( |
| mojom::NetworkService& network_service, |
| const std::vector<net::SourceStreamType>& types, |
| network::mojom::URLLoaderClientEndpointsPtr& endpoints, |
| mojo::ScopedDataPipeConsumerHandle& body, |
| DataPipePair data_pipe_pair); |
| |
| // Requests the network service to decode a data stream with the specified |
| // content encodings. This method is intended for use by the browser process. |
| // |
| // This method creates a new data pipe (`new_producer`, `new_consumer`), swaps |
| // the caller's `body` with `new_consumer`, and sends the original `body` and |
| // `new_producer` along with the `types` of encoding to apply to the network |
| // service. The network service will read the data from the `body`, and write |
| // the decoded data into `new_producer`. |
| // |
| // The `callback` is invoked with net::OK on successful decoding or an net |
| // error code if an error occurs. |
| static void DecodeOnNetworkService( |
| mojom::NetworkService& network_service, |
| const std::vector<net::SourceStreamType>& types, |
| mojo::ScopedDataPipeConsumerHandle& body, |
| ClientType client_type, |
| base::OnceCallback<void(net::Error)> callback); |
| |
| // A capability class used as a key to restrict calls to |
| // SetIsNetworkServiceRunningInTheCurrentProcess. |
| class SetIsNetworkServiceRunningInTheCurrentProcessKey { |
| private: |
| SetIsNetworkServiceRunningInTheCurrentProcessKey() = default; |
| friend class ::network::NetworkService; |
| }; |
| // Sets a static flag indicating whether the Network Service is running within |
| // the current process. |
| static void SetIsNetworkServiceRunningInTheCurrentProcess( |
| bool value, |
| SetIsNetworkServiceRunningInTheCurrentProcessKey key); |
| |
| private: |
| // Returns true if content decoding is permitted in the current process. |
| // Decoding is allowed in non-browser processes. In the browser process, |
| // it's only allowed if the Network Service is also running in-process. |
| static bool IsInContentDecodingAllowedProcess(); |
| |
| // Flag indicating if the network service is running in the current process. |
| static bool is_network_serice_runnning_in_the_current_process_; |
| }; |
| |
| } // namespace network |
| |
| #endif // SERVICES_NETWORK_PUBLIC_CPP_CONTENT_DECODING_INTERCEPTOR_H_ |