Commit 622b84ed authored by Tsuyoshi Horo's avatar Tsuyoshi Horo Committed by Commit Bot

Change the content-type for SignedHTTPExchange

After this cl, "application/signed-exchange;v=b0" will be used for SignedHTTPExchange.

Bug: 803774
Change-Id: I07e6218f07fb6917b1617ea208dcc7da31f13edf
Reviewed-on: https://chromium-review.googlesource.com/964024Reviewed-by: default avatarKunihiko Sakamoto <ksakamoto@chromium.org>
Reviewed-by: default avatarKouhei Ueno <kouhei@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Commit-Queue: Tsuyoshi Horo <horo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#543611}
parent afd36256
...@@ -343,9 +343,9 @@ IN_PROC_BROWSER_TEST_P(PrefetchBrowserTest, WebPackageWithPreload) { ...@@ -343,9 +343,9 @@ IN_PROC_BROWSER_TEST_P(PrefetchBrowserTest, WebPackageWithPreload) {
RegisterResponse( RegisterResponse(
target_htxg, target_htxg,
// We mock the SignedExchangeHandler, so just return a HTML content // We mock the SignedExchangeHandler, so just return a HTML content
// as application/http-exchange+cbor. // as "application/signed-exchange;v=b0".
ResponseEntry("<head><title>Prefetch Target (HTXG)</title></head>", ResponseEntry("<head><title>Prefetch Target (HTXG)</title></head>",
"application/http-exchange+cbor")); "application/signed-exchange;v=b0"));
RegisterResponse(preload_url_in_htxg, RegisterResponse(preload_url_in_htxg,
ResponseEntry("function foo() {}", "text/javascript")); ResponseEntry("function foo() {}", "text/javascript"));
......
...@@ -64,6 +64,7 @@ void SignedExchangeHandler::SetVerificationTimeForTesting( ...@@ -64,6 +64,7 @@ void SignedExchangeHandler::SetVerificationTimeForTesting(
} }
SignedExchangeHandler::SignedExchangeHandler( SignedExchangeHandler::SignedExchangeHandler(
std::string content_type,
std::unique_ptr<net::SourceStream> body, std::unique_ptr<net::SourceStream> body,
ExchangeHeadersCallback headers_callback, ExchangeHeadersCallback headers_callback,
url::Origin request_initiator, url::Origin request_initiator,
...@@ -81,12 +82,32 @@ SignedExchangeHandler::SignedExchangeHandler( ...@@ -81,12 +82,32 @@ SignedExchangeHandler::SignedExchangeHandler(
net::NetLogSourceType::CERT_VERIFIER_JOB)), net::NetLogSourceType::CERT_VERIFIER_JOB)),
weak_factory_(this) { weak_factory_(this) {
DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange)); DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange));
TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"),
"SignedExchangeHandler::SignedExchangeHandler");
base::Optional<std::string> content_type_version_param;
if (!SignedExchangeHeaderParser::GetVersionParamFromContentType(
content_type, &content_type_version_param) ||
!content_type_version_param || *content_type_version_param != "b0") {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&SignedExchangeHandler::RunErrorCallback,
weak_factory_.GetWeakPtr(), net::ERR_FAILED));
TRACE_EVENT_END2(TRACE_DISABLED_BY_DEFAULT("loading"),
"SignedExchangeHandler::SignedExchangeHandler", "error",
"Unsupported version of the content type. Currentry "
"content type must be "
"\"application/signed-exchange;v=b0\".",
"content-type", content_type);
return;
}
// Triggering the read (asynchronously) for the encoded header length. // Triggering the read (asynchronously) for the encoded header length.
SetupBuffers(SignedExchangeHeader::kEncodedHeaderLengthInBytes); SetupBuffers(SignedExchangeHeader::kEncodedHeaderLengthInBytes);
base::SequencedTaskRunnerHandle::Get()->PostTask( base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&SignedExchangeHandler::DoHeaderLoop, FROM_HERE, base::BindOnce(&SignedExchangeHandler::DoHeaderLoop,
weak_factory_.GetWeakPtr())); weak_factory_.GetWeakPtr()));
TRACE_EVENT_END0(TRACE_DISABLED_BY_DEFAULT("loading"),
"SignedExchangeCertFetcher::SignedExchangeHandler");
} }
SignedExchangeHandler::~SignedExchangeHandler() = default; SignedExchangeHandler::~SignedExchangeHandler() = default;
......
...@@ -71,6 +71,7 @@ class CONTENT_EXPORT SignedExchangeHandler { ...@@ -71,6 +71,7 @@ class CONTENT_EXPORT SignedExchangeHandler {
// and |url_loader_throttles_getter| are used to set up a network URLLoader to // and |url_loader_throttles_getter| are used to set up a network URLLoader to
// actually fetch the certificate. // actually fetch the certificate.
SignedExchangeHandler( SignedExchangeHandler(
std::string content_type,
std::unique_ptr<net::SourceStream> body, std::unique_ptr<net::SourceStream> body,
ExchangeHeadersCallback headers_callback, ExchangeHeadersCallback headers_callback,
url::Origin request_initiator, url::Origin request_initiator,
......
...@@ -188,6 +188,7 @@ class StructuredHeaderParser { ...@@ -188,6 +188,7 @@ class StructuredHeaderParser {
} // namespace } // namespace
// static
base::Optional<std::vector<SignedExchangeHeaderParser::Signature>> base::Optional<std::vector<SignedExchangeHeaderParser::Signature>>
SignedExchangeHeaderParser::ParseSignature(base::StringPiece signature_str) { SignedExchangeHeaderParser::ParseSignature(base::StringPiece signature_str) {
TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"), TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"),
...@@ -279,6 +280,25 @@ SignedExchangeHeaderParser::ParseSignature(base::StringPiece signature_str) { ...@@ -279,6 +280,25 @@ SignedExchangeHeaderParser::ParseSignature(base::StringPiece signature_str) {
return signatures; return signatures;
} }
// static
bool SignedExchangeHeaderParser::GetVersionParamFromContentType(
base::StringPiece content_type,
base::Optional<std::string>* version_param) {
DCHECK(version_param);
StructuredHeaderParser parser(content_type);
ParameterisedLabel parameterised_label;
parser.ParseParameterisedLabel(&parameterised_label);
if (!parser.ParsedSuccessfully())
return false;
const auto it = parameterised_label.params.find("v");
if (it == parameterised_label.params.end()) {
*version_param = base::nullopt;
} else {
*version_param = it->second;
}
return true;
}
SignedExchangeHeaderParser::Signature::Signature() = default; SignedExchangeHeaderParser::Signature::Signature() = default;
SignedExchangeHeaderParser::Signature::Signature(const Signature& other) = SignedExchangeHeaderParser::Signature::Signature(const Signature& other) =
default; default;
......
...@@ -43,6 +43,13 @@ class CONTENT_EXPORT SignedExchangeHeaderParser { ...@@ -43,6 +43,13 @@ class CONTENT_EXPORT SignedExchangeHeaderParser {
// https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#rfc.section.3.2 // https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#rfc.section.3.2
static base::Optional<std::vector<Signature>> ParseSignature( static base::Optional<std::vector<Signature>> ParseSignature(
base::StringPiece signature_str); base::StringPiece signature_str);
// Parses |content_type| to get the value of "v=" parameter of the signed
// exchange. Example: "b0" for "application/signed-exchange;v=b0". Returns
// false if failed to parse.
static bool GetVersionParamFromContentType(
base::StringPiece content_type,
base::Optional<std::string>* version_param);
}; };
} // namespace content } // namespace content
......
...@@ -160,4 +160,53 @@ TEST_F(SignedExchangeHeaderParserTest, OpenQuoteAtEnd) { ...@@ -160,4 +160,53 @@ TEST_F(SignedExchangeHeaderParserTest, OpenQuoteAtEnd) {
EXPECT_FALSE(signatures.has_value()); EXPECT_FALSE(signatures.has_value());
} }
TEST_F(SignedExchangeHeaderParserTest, VersionParam_None) {
const char content_type[] = "application/signed-exchange";
base::Optional<std::string> version;
EXPECT_TRUE(SignedExchangeHeaderParser::GetVersionParamFromContentType(
content_type, &version));
EXPECT_FALSE(version);
}
TEST_F(SignedExchangeHeaderParserTest, VersionParam_NoneWithSemicolon) {
const char content_type[] = "application/signed-exchange;";
base::Optional<std::string> version;
EXPECT_FALSE(SignedExchangeHeaderParser::GetVersionParamFromContentType(
content_type, &version));
}
TEST_F(SignedExchangeHeaderParserTest, VersionParam_EmptyString) {
const char content_type[] = "application/signed-exchange;v=";
base::Optional<std::string> version;
EXPECT_FALSE(SignedExchangeHeaderParser::GetVersionParamFromContentType(
content_type, &version));
}
TEST_F(SignedExchangeHeaderParserTest, VersionParam_Simple) {
const char content_type[] = "application/signed-exchange;v=b0";
base::Optional<std::string> version;
EXPECT_TRUE(SignedExchangeHeaderParser::GetVersionParamFromContentType(
content_type, &version));
ASSERT_TRUE(version);
EXPECT_EQ(*version, "b0");
}
TEST_F(SignedExchangeHeaderParserTest, VersionParam_SimpleWithSpace) {
const char content_type[] = "application/signed-exchange; v=b0";
base::Optional<std::string> version;
EXPECT_TRUE(SignedExchangeHeaderParser::GetVersionParamFromContentType(
content_type, &version));
ASSERT_TRUE(version);
EXPECT_EQ(*version, "b0");
}
TEST_F(SignedExchangeHeaderParserTest, VersionParam_SimpleWithDoublequotes) {
const char content_type[] = "application/signed-exchange;v=\"b0\"";
base::Optional<std::string> version;
EXPECT_TRUE(SignedExchangeHeaderParser::GetVersionParamFromContentType(
content_type, &version));
ASSERT_TRUE(version);
EXPECT_EQ(*version, "b0");
}
} // namespace content } // namespace content
...@@ -93,6 +93,12 @@ WebPackageLoader::WebPackageLoader( ...@@ -93,6 +93,12 @@ WebPackageLoader::WebPackageLoader(
request_context_getter_(std::move(request_context_getter)), request_context_getter_(std::move(request_context_getter)),
weak_factory_(this) { weak_factory_(this) {
DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange)); DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange));
// Can't use HttpResponseHeaders::GetMimeType() because SignedExchangeHandler
// checks "v=" parameter.
original_response.headers->EnumerateHeader(nullptr, "content-type",
&content_type_);
url_loader_.Bind(std::move(endpoints->url_loader)); url_loader_.Bind(std::move(endpoints->url_loader));
if (url_loader_options_ & if (url_loader_options_ &
...@@ -170,7 +176,7 @@ void WebPackageLoader::OnStartLoadingResponseBody( ...@@ -170,7 +176,7 @@ void WebPackageLoader::OnStartLoadingResponseBody(
} }
signed_exchange_handler_ = std::make_unique<SignedExchangeHandler>( signed_exchange_handler_ = std::make_unique<SignedExchangeHandler>(
std::make_unique<DataPipeToSourceStream>(std::move(body)), content_type_, std::make_unique<DataPipeToSourceStream>(std::move(body)),
base::BindOnce(&WebPackageLoader::OnHTTPExchangeFound, base::BindOnce(&WebPackageLoader::OnHTTPExchangeFound,
weak_factory_.GetWeakPtr()), weak_factory_.GetWeakPtr()),
std::move(request_initiator_), std::move(url_loader_factory_), std::move(request_initiator_), std::move(url_loader_factory_),
......
...@@ -133,6 +133,8 @@ class WebPackageLoader final : public network::mojom::URLLoaderClient, ...@@ -133,6 +133,8 @@ class WebPackageLoader final : public network::mojom::URLLoaderClient,
base::Optional<net::SSLInfo> ssl_info_; base::Optional<net::SSLInfo> ssl_info_;
std::string content_type_;
base::WeakPtrFactory<WebPackageLoader> weak_factory_; base::WeakPtrFactory<WebPackageLoader> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(WebPackageLoader); DISALLOW_COPY_AND_ASSIGN(WebPackageLoader);
......
...@@ -24,7 +24,7 @@ namespace content { ...@@ -24,7 +24,7 @@ namespace content {
bool WebPackageRequestHandler::IsSupportedMimeType( bool WebPackageRequestHandler::IsSupportedMimeType(
const std::string& mime_type) { const std::string& mime_type) {
DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange)); DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange));
return mime_type == "application/http-exchange+cbor"; return mime_type == "application/signed-exchange";
} }
WebPackageRequestHandler::WebPackageRequestHandler( WebPackageRequestHandler::WebPackageRequestHandler(
......
...@@ -218,6 +218,35 @@ IN_PROC_BROWSER_TEST_P(WebPackageRequestHandlerBrowserTest, Simple) { ...@@ -218,6 +218,35 @@ IN_PROC_BROWSER_TEST_P(WebPackageRequestHandlerBrowserTest, Simple) {
EXPECT_EQ(original_fingerprint, fingerprint); EXPECT_EQ(original_fingerprint, fingerprint);
} }
IN_PROC_BROWSER_TEST_P(WebPackageRequestHandlerBrowserTest,
InvalidContentType) {
InstallUrlInterceptor(
GURL("https://cert.example.org/cert.msg"),
"content/test/data/htxg/wildcard_example.org.public.pem.msg");
// Make the MockCertVerifier treat the certificate "wildcard.pem" as valid for
// "*.example.org".
scoped_refptr<net::X509Certificate> original_cert =
LoadCertificate("wildcard.pem");
net::CertVerifyResult dummy_result;
dummy_result.verified_cert = original_cert;
dummy_result.cert_status = net::OK;
mock_cert_verifier_->AddResultForCertAndHost(original_cert, "*.example.org",
dummy_result, net::OK);
embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
ASSERT_TRUE(embedded_test_server()->Start());
GURL url = embedded_test_server()->GetURL(
"/htxg/test.example.org_test_invalid_content_type.htxg");
NavigationFailureObserver failure_observer(shell()->web_contents());
NavigateToURL(shell(), url);
EXPECT_TRUE(failure_observer.did_fail());
NavigationEntry* entry =
shell()->web_contents()->GetController().GetVisibleEntry();
EXPECT_EQ(content::PAGE_TYPE_ERROR, entry->GetPageType());
}
IN_PROC_BROWSER_TEST_P(WebPackageRequestHandlerBrowserTest, CertNotFound) { IN_PROC_BROWSER_TEST_P(WebPackageRequestHandlerBrowserTest, CertNotFound) {
InstallUrlInterceptor(GURL("https://cert.example.org/cert.msg"), InstallUrlInterceptor(GURL("https://cert.example.org/cert.msg"),
"content/test/data/htxg/404.msg"); "content/test/data/htxg/404.msg");
......
HTTP/1.1 200 OK HTTP/1.1 200 OK
Content-Type: application/http-exchange+cbor Content-Type: application/signed-exchange;v=b0
HTTP/1.1 200 OK HTTP/1.1 200 OK
Content-Type: application/http-exchange+cbor Content-Type: application/signed-exchange;v=b0
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