| // 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_ |