blob: 47563fb33ed90f7dc7ca72b4820b24d409d80604 [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_PARTIAL_DECODER_H_
#define SERVICES_NETWORK_PARTIAL_DECODER_H_
#include <string>
#include <vector>
#include "base/component_export.h"
#include "base/containers/queue.h"
#include "base/containers/span.h"
#include "base/functional/callback.h"
#include "net/base/net_errors.h"
#include "net/filter/source_stream.h"
#include "net/filter/source_stream_type.h"
namespace net {
class DrainableIOBuffer;
class GrowableIOBuffer;
class IOBuffer;
class IOBufferWithSize;
} // namespace net
namespace network {
// Represents the result of a partial decoding operation. It contains the raw
// (encoded) data read so far and the completion status of the underlying
// read operation.
class COMPONENT_EXPORT(NETWORK_SERVICE) PartialDecoderResult {
public:
// `raw_buffers` is a queue of buffers containing the raw, encoded data
// received from the network.
// `completion_status` indicates the final status of the network read
// operation (e.g., success, error, or end-of-file).
PartialDecoderResult(
base::queue<scoped_refptr<net::IOBufferWithSize>> raw_buffers,
const std::optional<net::Error>& completion_status);
PartialDecoderResult(const PartialDecoderResult&) = delete;
PartialDecoderResult& operator=(const PartialDecoderResult&) = delete;
PartialDecoderResult(PartialDecoderResult&& other);
PartialDecoderResult& operator=(PartialDecoderResult&&);
~PartialDecoderResult();
// Returns true if there is raw (encoded) data available to consume.
bool HasRawData() const;
// Copies raw (encoded) data into `out`. Returns the number of bytes copied.
// This can be called multiple times to consume the raw data in chunks.
// The `out` span should be a buffer where the raw data will be copied.
size_t ConsumeRawData(base::span<uint8_t> out);
// The completion status of the request. This is set only after receiving a
// 0 or negative value (other than IO_PENDING) from the underlying data
// source.
const std::optional<net::Error>& completion_status() const {
return completion_status_;
}
private:
// A queue of buffers containing the raw, encoded data. These are wrapped in
// `DrainableIOBuffer` to allow for partial consumption during the
// `ConsumeRawData` calls.
base::queue<scoped_refptr<net::DrainableIOBuffer>> raw_buffers_;
// The final status of the underlying network read operation.
std::optional<net::Error> completion_status_;
};
// `PartialDecoder` decodes the first `decoded_buffer_size` bytes of a response
// body, making both the decoded data and the raw (encoded) data available. This
// is useful for situations where you need to inspect a small portion of the
// decoded data (e.g., for MIME sniffing) but still want access to the original
// encoded data for the initial part of the stream.
class COMPONENT_EXPORT(NETWORK_SERVICE) PartialDecoder {
public:
// Constructs a `PartialDecoder`.
// `read_raw_data_callback` is called to read raw data from the underlying
// data source (e.g., net::URLRequest). It takes a buffer and size, and
// returns the number of bytes read or a net error.
// `types` is the ordered list of `SourceStreamType` values, indicating which
// decoding filters (like gzip, brotli) to attempt to apply. An empty list
// means no decoding will be performed by the `decoding_stream_`.
// `decoding_buffer_size` is the maximum number of decoded bytes to buffer
// internally.
PartialDecoder(
base::RepeatingCallback<int(net::IOBuffer*, int)> read_raw_data_callback,
const std::vector<net::SourceStreamType>& types,
size_t decoding_buffer_size);
~PartialDecoder();
// Attempts to read more decoded data into the internal buffer.
// When the read operation finishes synchronously, it returns the number of
// newly read bytes or a net error. Otherwise returns net::ERR_IO_PENDING, and
// `callback` will be invoked with the result of the read operation (number of
// bytes read or a net error).
// This must not be called when `HasRemainingBuffer()` is false.
// This may call `read_raw_data_callback` multiple times until a decoded chunk
// becomes available for reading.
int ReadDecodedDataMore(base::OnceCallback<void(int)> callback);
// Indicates asynchronous completion of a read started by
// `read_raw_data_callback`. The creator of `read_raw_data_callback`
// should arrange for `OnReadRawDataCompleted()` to be called later in
// the case when `read_raw_data_callback` returns net::ERR_IO_PENDING.
// `result` is the number of bytes read or a net error code.
void OnReadRawDataCompleted(int result);
// Returns true if an asynchronous read operation for decoded data is
// currently in progress.
bool read_in_progress() const;
// Returns true if there is space remaining in the internal buffer to store
// more decoded data.
bool HasRemainingBuffer() const;
// Returns the decoded data that has been buffered so far.
base::span<const uint8_t> decoded_data() const;
// Takes ownership of the accumulated raw data and the completion status.
// This should be called once all necessary decoded data has been read.
PartialDecoderResult TakeResult() &&;
private:
// A custom SourceStream that records the raw data passed to its `Read()`
// method, while making it available for subsequent decoding by the
// `decoding_stream_`.
class RecordingStream : public net::SourceStream {
public:
// `read_callback` will be called to fetch more encoded data from
// the underlying data source.
explicit RecordingStream(
base::RepeatingCallback<int(net::IOBuffer*, int)> read_callback);
RecordingStream(const RecordingStream&) = delete;
RecordingStream& operator=(const RecordingStream&) = delete;
~RecordingStream() override;
// net::SourceStream implementation:
// Reads data from the underlying source stream. The read data is also
// recorded internally.
int Read(net::IOBuffer* dest_buffer,
int buffer_size,
net::CompletionOnceCallback callback) override;
// Returns a description of the source stream.
std::string Description() const override;
// Indicates whether the source stream might have more bytes.
bool MayHaveMoreBytes() const override;
// Called when the read operation initiated by `Read()` completes. `result`
// is the number of read bytes or a net error code.
void OnReadCompleted(int result);
// Returns the recorded raw buffers and clears the internal buffer.
base::queue<scoped_refptr<net::IOBufferWithSize>> TakeRawBuffers();
// Returns the completion status of the underlying read operations.
const std::optional<net::Error>& completion_status() const {
return completion_status_;
}
private:
// Handles the result of `read_callback_`.
void HandleReadCompleted(int result, net::IOBuffer* dest_buffer);
// The callback to read more data from the underlying source.
base::RepeatingCallback<int(net::IOBuffer*, int)> read_callback_;
// Buffers to store the raw, encoded data received from the underlying
// source.
base::queue<scoped_refptr<net::IOBufferWithSize>> raw_buffers_;
// If `Read()` returns `net::ERR_IO_PENDING`, these store a pointer of the
// destination buffer and the `callback` passed to the `Read()` method, so
// they can be used when the read operation completes.
raw_ptr<net::IOBuffer> pending_dest_buffer_ = nullptr;
net::CompletionOnceCallback pending_callback_;
// The completion status of the request. This is set only after receiving a
// 0 or negative value (other than IO_PENDING) from `read_callback_` or via
// `OnReadCompleted::OnReadCompleted()`.
std::optional<net::Error> completion_status_;
};
// Called when a read operation on `decoding_stream_` completes
// asynchronously. `result` is the number of bytes read or a net error.
void OnReadDecodedDataAsyncComplete(int result);
// The SourceStream that performs the actual content decoding (e.g., gzip).
// It takes the `RecordingStream` as its input, so the raw data is
// recorded before being decoded.
std::unique_ptr<net::SourceStream> decoding_stream_;
// The `RecordingStream` instance used to record raw data. This is a raw
// pointer because the `decoding_stream_` owns the recording stream.
raw_ptr<RecordingStream> recording_stream_;
// Buffer to hold the decoded data. The capacity is set during construction.
scoped_refptr<net::GrowableIOBuffer> decoded_buffer_;
// Callback to be invoked when an asynchronous read of decoded data
// completes.
base::OnceCallback<void(int)> pending_read_decoded_data_more_callback_;
};
} // namespace network
#endif // SERVICES_NETWORK_PARTIAL_DECODER_H_