blob: 17cdf129eff4e69bdcfd394f58c2a686836696e8 [file] [log] [blame]
// 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_