Commit c97ed49e authored by Kunihiko Sakamoto's avatar Kunihiko Sakamoto Committed by Commit Bot

SignedExchange: Populate BodySize Resource Timing fields

Before this patch, encodedBodySize [1] and decodedBodySize [2] fields of
navigation / resource timing entry for Signed Exchanges were not
populated.

After this patch, these fields will have the sizes of Signed Exchange's
inner response body, before and after decoding mi-sha256 encoding.

[1] https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-encodedbodysize
[2] https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-decodedbodysize

Bug: 928589
Change-Id: I6b7800497e411822172dea4769ab8fc9d4955b53
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1496261
Commit-Queue: Kunihiko Sakamoto <ksakamoto@chromium.org>
Reviewed-by: default avatarTsuyoshi Horo <horo@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Cr-Commit-Position: refs/heads/master@{#638001}
parent 78dfb8b9
......@@ -73,6 +73,7 @@ void SourceStreamToDataPipe::DidRead(int result) {
}
dest_ = pending_write_->Complete(result);
pending_write_ = nullptr;
transferred_bytes_ += result;
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&SourceStreamToDataPipe::ReadMore,
......
......@@ -30,6 +30,7 @@ class CONTENT_EXPORT SourceStreamToDataPipe {
// Start reading the source.
void Start();
int64_t TransferredBytes() const { return transferred_bytes_; }
private:
void ReadMore();
......@@ -42,6 +43,7 @@ class CONTENT_EXPORT SourceStreamToDataPipe {
std::unique_ptr<net::SourceStream> source_;
mojo::ScopedDataPipeProducerHandle dest_;
base::OnceCallback<void(int)> completion_callback_;
int64_t transferred_bytes_ = 0;
scoped_refptr<network::NetToMojoPendingBuffer> pending_write_;
mojo::SimpleWatcher writable_handle_watcher_;
......
......@@ -309,6 +309,7 @@ void SignedExchangeHandler::DidReadHeader(bool completed_syncly,
}
header_read_buf_->DidConsume(read_result);
exchange_header_length_ += read_result;
if (header_read_buf_->BytesRemaining() == 0) {
SignedExchangeLoadResult result = SignedExchangeLoadResult::kSuccess;
switch (state_) {
......
......@@ -99,6 +99,8 @@ class CONTENT_EXPORT SignedExchangeHandler {
base::RepeatingCallback<int(void)> frame_tree_node_id_getter);
~SignedExchangeHandler();
int64_t GetExchangeHeaderLength() const { return exchange_header_length_; }
protected:
SignedExchangeHandler();
......@@ -143,6 +145,7 @@ class CONTENT_EXPORT SignedExchangeHandler {
scoped_refptr<net::IOBuffer> header_buf_;
// Wrapper around |header_buf_| to progressively read fixed-size data.
scoped_refptr<net::DrainableIOBuffer> header_read_buf_;
int64_t exchange_header_length_ = 0;
signed_exchange_prologue::BeforeFallbackUrl prologue_before_fallback_url_;
signed_exchange_prologue::FallbackUrlAndAfter
......
......@@ -237,8 +237,10 @@ void SignedExchangeLoader::OnStartLoadingResponseBody(
void SignedExchangeLoader::OnComplete(
const network::URLLoaderCompletionStatus& status) {
DCHECK(!encoded_data_length_);
encoded_data_length_ = status.encoded_data_length;
DCHECK(!outer_response_length_info_);
outer_response_length_info_ = OuterResponseLengthInfo();
outer_response_length_info_->encoded_data_length = status.encoded_data_length;
outer_response_length_info_->decoded_body_length = status.decoded_body_length;
NotifyClientOnCompleteIfReady();
}
......@@ -364,21 +366,23 @@ void SignedExchangeLoader::FinishReadingBody(int result) {
}
void SignedExchangeLoader::NotifyClientOnCompleteIfReady() {
// If |encoded_data_length_| or |decoded_body_read_result_| is unavailable, do
// nothing and rely on the subsequent call to notify client.
if (!encoded_data_length_ || !decoded_body_read_result_)
// If |outer_response_length_info_| or |decoded_body_read_result_| is
// unavailable, do nothing and rely on the subsequent call to notify client.
if (!outer_response_length_info_ || !decoded_body_read_result_)
return;
ReportLoadResult(*decoded_body_read_result_ == net::OK
? SignedExchangeLoadResult::kSuccess
: SignedExchangeLoadResult::kMerkleIntegrityError);
// TODO(https://crbug.com/803774): Fill the data length information (
// encoded_body_length, decoded_body_length) too.
network::URLLoaderCompletionStatus status;
status.error_code = *decoded_body_read_result_;
status.completion_time = base::TimeTicks::Now();
status.encoded_data_length = *encoded_data_length_;
status.encoded_data_length = outer_response_length_info_->encoded_data_length;
status.encoded_body_length =
outer_response_length_info_->decoded_body_length -
signed_exchange_handler_->GetExchangeHeaderLength();
status.decoded_body_length = body_data_pipe_adapter_->TransferredBytes();
if (ssl_info_) {
DCHECK((url_loader_options_ &
......
......@@ -169,8 +169,13 @@ class CONTENT_EXPORT SignedExchangeLoader final
base::Optional<GURL> fallback_url_;
base::Optional<GURL> inner_request_url_;
struct OuterResponseLengthInfo {
int64_t encoded_data_length;
int64_t decoded_body_length;
};
// Set when URLLoaderClient::OnComplete() is called.
base::Optional<int64_t> encoded_data_length_;
base::Optional<OuterResponseLengthInfo> outer_response_length_info_;
// Set when |body_data_pipe_adapter_| finishes loading the decoded body.
base::Optional<int> decoded_body_read_result_;
const std::string accept_langs_;
......
......@@ -355,8 +355,6 @@ gen-signedexchange \
-miRecordSize 100 \
-ignoreErrors true
# Signed Exchange with payload integrity error.
echo 'garbage' | cat sxg/sxg-location.sxg - >sxg/sxg-merkle-integrity-error.sxg
......@@ -441,4 +439,19 @@ gen-signedexchange \
-o sxg/sxg-variants-mismatch.sxg \
-miRecordSize 100
# A valid Signed Exchange that reports navigation timing.
gen-signedexchange \
-version $sxg_version \
-uri $inner_url_origin/signed-exchange/resources/inner-url.html \
-status 200 \
-content sxg-navigation-timing.html \
-certificate $certfile \
-certUrl $cert_url_origin/signed-exchange/resources/$certfile.cbor \
-validityUrl $inner_url_origin/signed-exchange/resources/resource.validity.msg \
-privateKey $keyfile \
-date 2018-04-01T00:00:00Z \
-expire 168h \
-o sxg/sxg-navigation-timing.sxg \
-miRecordSize 100
rm -fr $tmpdir
<!DOCTYPE html>
<title>Navigation timing of SignedHTTPExchange</title>
<script>
window.addEventListener('message', (event) => {
event.data.port.postMessage({
location: document.location.href,
timing: JSON.stringify(performance.getEntriesByType('navigation')[0]),
is_fallback: false});
}, false);
</script>
<!DOCTYPE html>
<title>Navigation timing of SignedHTTPExchange</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="./resources/sxg-util.js"></script>
<body>
<script>
promise_test(async (t) => {
const sxgUrl = get_host_info().HTTPS_ORIGIN + '/signed-exchange/resources/sxg/sxg-navigation-timing.sxg';
const message = await openSXGInIframeAndWaitForMessage(t, sxgUrl);
assert_false(message.is_fallback);
let timing = JSON.parse(message.timing);
let originalContent = await fetch('resources/sxg-navigation-timing.html').then(resp => resp.arrayBuffer());
assert_equals(timing.decodedBodySize, originalContent.byteLength);
assert_equals(timing.encodedBodySize, computeMiceLength(originalContent.byteLength, 100));
// TODO(https://crbug.com/928589): Test other fields too.
}, 'Navigation timing of SignedHTTPExchange');
// Returns content length after MI encode.
function computeMiceLength(len, recordSize) {
const recordSizeLen = 8;
const sha256DigestLength = 32;
return recordSizeLen + len + Math.floor(len / recordSize) * sha256DigestLength;
}
</script>
</body>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment