#include "content/browser/web_package/mock_signed_exchange_handler.h"
#include <memory>
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/strings/string_piece.h"
#include "base/task/sequenced_task_runner.h"
#include "content/browser/web_package/prefetched_signed_exchange_cache_entry.h"
#include "content/browser/web_package/signed_exchange_cert_fetcher_factory.h"
#include "net/filter/source_stream.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_util.h"
namespace content {
namespace {
class PrefixStrippingSourceStream : public net::SourceStream {
public:
PrefixStrippingSourceStream(base::StringPiece prefix_to_strip,
std::unique_ptr<net::SourceStream> stream_to_wrap)
: net::SourceStream(stream_to_wrap->type()),
remaining_prefix_to_strip_(prefix_to_strip),
wrapped_stream_(std::move(stream_to_wrap)),
weak_factory_(this) {
DCHECK(wrapped_stream_);
}
~PrefixStrippingSourceStream() override = default;
int Read(net::IOBuffer* dest_buffer,
int buffer_size,
net::CompletionOnceCallback callback) override {
DCHECK(dest_buffer);
DCHECK_GT(buffer_size, 0);
DCHECK(callback);
auto pending_read = base::MakeRefCounted<PendingRead>();
pending_read->dest_buffer = dest_buffer;
pending_read->buffer_size = buffer_size;
pending_read->callback = std::move(callback);
return Read(pending_read);
}
std::string Description() const override {
return wrapped_stream_->Description();
}
bool MayHaveMoreBytes() const override {
return wrapped_stream_->MayHaveMoreBytes();
}
private:
struct PendingRead : public base::RefCountedThreadSafe<PendingRead> {
scoped_refptr<net::IOBuffer> dest_buffer;
int buffer_size;
net::CompletionOnceCallback callback;
private:
friend class base::RefCountedThreadSafe<PendingRead>;
virtual ~PendingRead() = default;
};
int Read(const scoped_refptr<PendingRead>& pending_read) {
DCHECK(pending_read);
if (remaining_prefix_to_strip_.empty()) {
return wrapped_stream_->Read(pending_read->dest_buffer.get(),
pending_read->buffer_size,
std::move(pending_read->callback));
}
int number_of_bytes_read = wrapped_stream_->Read(
pending_read->dest_buffer.get(), pending_read->buffer_size,
base::BindOnce(&PrefixStrippingSourceStream::OnWrappedReadCompleted,
weak_factory_.GetWeakPtr(), pending_read));
int number_of_post_prefix_bytes_read =
ConsumeWrappedRead(pending_read, number_of_bytes_read);
return number_of_post_prefix_bytes_read;
}
void OnWrappedReadCompleted(const scoped_refptr<PendingRead>& pending_read,
int number_of_bytes_read) {
DCHECK(pending_read);
DCHECK_NE(number_of_bytes_read, net::ERR_IO_PENDING);
int number_of_post_prefix_bytes_read =
ConsumeWrappedRead(pending_read, number_of_bytes_read);
if (number_of_post_prefix_bytes_read != net::ERR_IO_PENDING) {
std::move(pending_read->callback).Run(number_of_post_prefix_bytes_read);
}
}
int ConsumeWrappedRead(const scoped_refptr<PendingRead>& pending_read,
int number_of_bytes_read) {
DCHECK(pending_read);
if (number_of_bytes_read < 0) {
return number_of_bytes_read;
}
base::StringPiece bytes_read(pending_read->dest_buffer->data(),
number_of_bytes_read);
int maybe_consumed_bytes =
std::min(bytes_read.size(), remaining_prefix_to_strip_.size());
CHECK_EQ(remaining_prefix_to_strip_.substr(0, maybe_consumed_bytes),
bytes_read.substr(0, maybe_consumed_bytes))
<< "Unexpectedly mismatched prefix - can't continue (because some "
<< "of the wrapped bytes may have been already discarded earlier)";
remaining_prefix_to_strip_.remove_prefix(maybe_consumed_bytes);
bytes_read.remove_prefix(maybe_consumed_bytes);
bool maybe_incorrect_eof = bytes_read.empty() && MayHaveMoreBytes();
if (remaining_prefix_to_strip_.empty() && !maybe_incorrect_eof) {
memmove(pending_read->dest_buffer->data(), bytes_read.data(),
bytes_read.size());
return bytes_read.size();
}
return Read(pending_read);
}
base::StringPiece remaining_prefix_to_strip_;
const std::unique_ptr<net::SourceStream> wrapped_stream_;
base::WeakPtrFactory<PrefixStrippingSourceStream> weak_factory_;
};
}
MockSignedExchangeHandlerParams::MockSignedExchangeHandlerParams(
const GURL& outer_url,
SignedExchangeLoadResult result,
net::Error error,
const GURL& inner_url,
const std::string& mime_type,
std::vector<std::pair<std::string, std::string>> response_headers,
const net::SHA256HashValue& header_integrity,
const base::Time& signature_expire_time)
: outer_url(outer_url),
result(result),
error(error),
inner_url(inner_url),
mime_type(mime_type),
response_headers(std::move(response_headers)),
header_integrity(header_integrity),
signature_expire_time(signature_expire_time.is_null()
? base::Time::Now() + base::Days(1)
: signature_expire_time) {}
MockSignedExchangeHandlerParams::MockSignedExchangeHandlerParams(
const MockSignedExchangeHandlerParams& other) = default;
MockSignedExchangeHandlerParams::~MockSignedExchangeHandlerParams() = default;
MockSignedExchangeHandler::MockSignedExchangeHandler(
const MockSignedExchangeHandlerParams& params,
std::unique_ptr<net::SourceStream> body,
ExchangeHeadersCallback headers_callback)
: header_integrity_(params.header_integrity),
signature_expire_time_(params.signature_expire_time),
cert_url_(params.outer_url.Resolve("mock_cert")),
cert_server_ip_address_(net::IPAddress::IPv4Localhost()) {
auto head = network::mojom::URLResponseHead::New();
if (params.error == net::OK) {
head->headers =
base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
head->mime_type = params.mime_type;
head->headers->SetHeader("Content-type", params.mime_type);
for (const auto& header : params.response_headers)
head->headers->AddHeader(header.first, header.second);
head->is_signed_exchange_inner_response = true;
head->content_length = head->headers->GetContentLength();
}
body = std::make_unique<PrefixStrippingSourceStream>(kMockSxgPrefix,
std::move(body));
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(headers_callback), params.result, params.error,
params.inner_url, std::move(head), std::move(body)));
}
bool MockSignedExchangeHandler::GetSignedExchangeInfoForPrefetchCache(
PrefetchedSignedExchangeCacheEntry& entry) const {
entry.SetHeaderIntegrity(
std::make_unique<net::SHA256HashValue>(header_integrity_));
entry.SetSignatureExpireTime(signature_expire_time_);
entry.SetCertUrl(cert_url_);
entry.SetCertServerIPAddress(cert_server_ip_address_);
return true;
}
MockSignedExchangeHandler::~MockSignedExchangeHandler() {}
MockSignedExchangeHandlerFactory::MockSignedExchangeHandlerFactory(
std::vector<MockSignedExchangeHandlerParams> params_list)
: params_list_(std::move(params_list)) {}
MockSignedExchangeHandlerFactory::~MockSignedExchangeHandlerFactory() = default;
std::unique_ptr<SignedExchangeHandler> MockSignedExchangeHandlerFactory::Create(
const GURL& outer_url,
std::unique_ptr<net::SourceStream> body,
ExchangeHeadersCallback headers_callback,
std::unique_ptr<SignedExchangeCertFetcherFactory> cert_fetcher_factory) {
for (const auto& params : params_list_) {
if (params.outer_url == outer_url) {
return std::make_unique<MockSignedExchangeHandler>(
params, std::move(body), std::move(headers_callback));
}
}
NOTREACHED();
return nullptr;
}
}