gfe-relnote: n/a(refactor only) Split out utility functions needed on quic client side from SpdyUtils. This is for avoiding dependency on GURL from libraries used by both server and client sides. PiperOrigin-RevId: 256363427 Change-Id: I8aece427461710d3438e099c92d46394834a233c
diff --git a/quic/core/http/quic_client_promised_info.cc b/quic/core/http/quic_client_promised_info.cc index 8b54969..6af35d5 100644 --- a/quic/core/http/quic_client_promised_info.cc +++ b/quic/core/http/quic_client_promised_info.cc
@@ -7,7 +7,7 @@ #include <string> #include <utility> -#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" +#include "net/third_party/quiche/src/quic/core/http/spdy_server_push_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" #include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" @@ -56,14 +56,14 @@ Reset(QUIC_INVALID_PROMISE_METHOD); return false; } - if (!SpdyUtils::PromisedUrlIsValid(headers)) { + if (!SpdyServerPushUtils::PromisedUrlIsValid(headers)) { QUIC_DVLOG(1) << "Promise for stream " << id_ << " has invalid URL " << url_; Reset(QUIC_INVALID_PROMISE_URL); return false; } if (!session_->IsAuthorized( - SpdyUtils::GetPromisedHostNameFromHeaders(headers))) { + SpdyServerPushUtils::GetPromisedHostNameFromHeaders(headers))) { Reset(QUIC_UNAUTHORIZED_PROMISE_URL); return false; }
diff --git a/quic/core/http/quic_client_promised_info_test.cc b/quic/core/http/quic_client_promised_info_test.cc index fcc85ba..cd54dde 100644 --- a/quic/core/http/quic_client_promised_info_test.cc +++ b/quic/core/http/quic_client_promised_info_test.cc
@@ -8,7 +8,7 @@ #include <string> #include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h" -#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" +#include "net/third_party/quiche/src/quic/core/http/spdy_server_push_utils.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" @@ -93,7 +93,8 @@ push_promise_[":method"] = "GET"; push_promise_[":scheme"] = "https"; - promise_url_ = SpdyUtils::GetPromisedUrlFromHeaders(push_promise_); + promise_url_ = + SpdyServerPushUtils::GetPromisedUrlFromHeaders(push_promise_); client_request_ = push_promise_.Clone(); promise_id_ = GetNthServerInitiatedUnidirectionalStreamId(
diff --git a/quic/core/http/quic_client_push_promise_index.cc b/quic/core/http/quic_client_push_promise_index.cc index 0f11af4..877525a 100644 --- a/quic/core/http/quic_client_push_promise_index.cc +++ b/quic/core/http/quic_client_push_promise_index.cc
@@ -7,7 +7,7 @@ #include <string> #include "net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h" -#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" +#include "net/third_party/quiche/src/quic/core/http/spdy_server_push_utils.h" using spdy::SpdyHeaderBlock; @@ -32,7 +32,7 @@ const spdy::SpdyHeaderBlock& request, QuicClientPushPromiseIndex::Delegate* delegate, TryHandle** handle) { - std::string url(SpdyUtils::GetPromisedUrlFromHeaders(request)); + std::string url(SpdyServerPushUtils::GetPromisedUrlFromHeaders(request)); auto it = promised_by_url_.find(url); if (it != promised_by_url_.end()) { QuicClientPromisedInfo* promised = it->second;
diff --git a/quic/core/http/quic_client_push_promise_index_test.cc b/quic/core/http/quic_client_push_promise_index_test.cc index 401ea86..933ef6b 100644 --- a/quic/core/http/quic_client_push_promise_index_test.cc +++ b/quic/core/http/quic_client_push_promise_index_test.cc
@@ -7,7 +7,7 @@ #include <string> #include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h" -#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" +#include "net/third_party/quiche/src/quic/core/http/spdy_server_push_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" #include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" #include "net/third_party/quiche/src/quic/test_tools/mock_quic_client_promised_info.h" @@ -63,7 +63,7 @@ request_[":version"] = "HTTP/1.1"; request_[":method"] = "GET"; request_[":scheme"] = "https"; - url_ = SpdyUtils::GetPromisedUrlFromHeaders(request_); + url_ = SpdyServerPushUtils::GetPromisedUrlFromHeaders(request_); } MockQuicConnectionHelper helper_;
diff --git a/quic/core/http/quic_spdy_client_session_base.cc b/quic/core/http/quic_spdy_client_session_base.cc index fe7e21c..9bb2ee2 100644 --- a/quic/core/http/quic_spdy_client_session_base.cc +++ b/quic/core/http/quic_spdy_client_session_base.cc
@@ -7,7 +7,7 @@ #include <string> #include "net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h" -#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" +#include "net/third_party/quiche/src/quic/core/http/spdy_server_push_utils.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" @@ -119,7 +119,8 @@ return false; } - const std::string url = SpdyUtils::GetPromisedUrlFromHeaders(headers); + const std::string url = + SpdyServerPushUtils::GetPromisedUrlFromHeaders(headers); QuicClientPromisedInfo* old_promised = GetPromisedByUrl(url); if (old_promised) { QUIC_DVLOG(1) << "Promise for stream " << promised_id
diff --git a/quic/core/http/quic_spdy_client_session_test.cc b/quic/core/http/quic_spdy_client_session_test.cc index e6a6a91..716ae42 100644 --- a/quic/core/http/quic_spdy_client_session_test.cc +++ b/quic/core/http/quic_spdy_client_session_test.cc
@@ -11,7 +11,7 @@ #include "net/third_party/quiche/src/quic/core/crypto/null_decrypter.h" #include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h" #include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h" -#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" +#include "net/third_party/quiche/src/quic/core/http/spdy_server_push_utils.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" @@ -107,7 +107,8 @@ push_promise_[":version"] = "HTTP/1.1"; push_promise_[":method"] = "GET"; push_promise_[":scheme"] = "https"; - promise_url_ = SpdyUtils::GetPromisedUrlFromHeaders(push_promise_); + promise_url_ = + SpdyServerPushUtils::GetPromisedUrlFromHeaders(push_promise_); promised_stream_id_ = GetNthServerInitiatedUnidirectionalStreamId( connection_->transport_version(), 0); associated_stream_id_ = GetNthClientInitiatedBidirectionalStreamId( @@ -736,7 +737,7 @@ // Verify that the promise is in the unclaimed streams map. std::string promise_url( - SpdyUtils::GetPromisedUrlFromHeaders(push_promise_)); + SpdyServerPushUtils::GetPromisedUrlFromHeaders(push_promise_)); EXPECT_NE(session_->GetPromisedByUrl(promise_url), nullptr); EXPECT_NE(session_->GetPromisedById(id), nullptr); } @@ -754,7 +755,8 @@ session_->HandlePromised(associated_stream_id_, id, push_promise_)); // Verify that the promise was not created. - std::string promise_url(SpdyUtils::GetPromisedUrlFromHeaders(push_promise_)); + std::string promise_url( + SpdyServerPushUtils::GetPromisedUrlFromHeaders(push_promise_)); EXPECT_EQ(session_->GetPromisedById(id), nullptr); EXPECT_EQ(session_->GetPromisedByUrl(promise_url), nullptr); }
diff --git a/quic/core/http/spdy_server_push_utils.cc b/quic/core/http/spdy_server_push_utils.cc new file mode 100644 index 0000000..02c963c --- /dev/null +++ b/quic/core/http/spdy_server_push_utils.cc
@@ -0,0 +1,214 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/core/http/spdy_server_push_utils.h" + +#include "url/gurl.h" + +using spdy::SpdyHeaderBlock; + +namespace quic { + +// static +std::string SpdyServerPushUtils::GetPromisedUrlFromHeaders( + const SpdyHeaderBlock& headers) { + // RFC 7540, Section 8.1.2.3: All HTTP/2 requests MUST include exactly + // one valid value for the ":method", ":scheme", and ":path" pseudo-header + // fields, unless it is a CONNECT request. + + // RFC 7540, Section 8.2.1: The header fields in PUSH_PROMISE and any + // subsequent CONTINUATION frames MUST be a valid and complete set of request + // header fields (Section 8.1.2.3). The server MUST include a method in the + // ":method" pseudo-header field that is safe and cacheable. + // + // RFC 7231, Section 4.2.1: Of the request methods defined by this + // specification, the GET, HEAD, OPTIONS, and TRACE methods are defined to be + // safe. + // + // RFC 7231, Section 4.2.1: ... this specification defines GET, HEAD, and + // POST as cacheable, ... + // + // So the only methods allowed in a PUSH_PROMISE are GET and HEAD. + SpdyHeaderBlock::const_iterator it = headers.find(":method"); + if (it == headers.end() || (it->second != "GET" && it->second != "HEAD")) { + return std::string(); + } + + it = headers.find(":scheme"); + if (it == headers.end() || it->second.empty()) { + return std::string(); + } + QuicStringPiece scheme = it->second; + + // RFC 7540, Section 8.2: The server MUST include a value in the + // ":authority" pseudo-header field for which the server is authoritative + // (see Section 10.1). + it = headers.find(":authority"); + if (it == headers.end() || it->second.empty()) { + return std::string(); + } + QuicStringPiece authority = it->second; + + // RFC 7540, Section 8.1.2.3 requires that the ":path" pseudo-header MUST + // NOT be empty for "http" or "https" URIs; + // + // However, to ensure the scheme is consistently canonicalized, that check + // is deferred to implementations in QuicUrlUtils::GetPushPromiseUrl(). + it = headers.find(":path"); + if (it == headers.end()) { + return std::string(); + } + QuicStringPiece path = it->second; + + return GetPushPromiseUrl(scheme, authority, path); +} + +// static +std::string SpdyServerPushUtils::GetPromisedHostNameFromHeaders( + const SpdyHeaderBlock& headers) { + // TODO(fayang): Consider just checking out the value of the ":authority" key + // in headers. + return GURL(GetPromisedUrlFromHeaders(headers)).host(); +} + +// static +bool SpdyServerPushUtils::PromisedUrlIsValid(const SpdyHeaderBlock& headers) { + std::string url(GetPromisedUrlFromHeaders(headers)); + return !url.empty() && GURL(url).is_valid(); +} + +// static +std::string SpdyServerPushUtils::GetPushPromiseUrl(QuicStringPiece scheme, + QuicStringPiece authority, + QuicStringPiece path) { + // RFC 7540, Section 8.1.2.3: The ":path" pseudo-header field includes the + // path and query parts of the target URI (the "path-absolute" production + // and optionally a '?' character followed by the "query" production (see + // Sections 3.3 and 3.4 of RFC3986). A request in asterisk form includes the + // value '*' for the ":path" pseudo-header field. + // + // This pseudo-header field MUST NOT be empty for "http" or "https" URIs; + // "http" or "https" URIs that do not contain a path MUST include a value of + // '/'. The exception to this rule is an OPTIONS request for an "http" or + // "https" URI that does not include a path component; these MUST include a + // ":path" pseudo-header with a value of '*' (see RFC7230, Section 5.3.4). + // + // In addition to the above restriction from RFC 7540, note that RFC3986 + // defines the "path-absolute" construction as starting with "/" but not "//". + // + // RFC 7540, Section 8.2.1: The header fields in PUSH_PROMISE and any + // subsequent CONTINUATION frames MUST be a valid and complete set of request + // header fields (Section 8.1.2.3). The server MUST include a method in the + // ":method" pseudo-header field that is safe and cacheable. + // + // RFC 7231, Section 4.2.1: + // ... this specification defines GET, HEAD, and POST as cacheable, ... + // + // Since the OPTIONS method is not cacheable, it cannot be the method of a + // PUSH_PROMISE. Therefore, the exception mentioned in RFC 7540, Section + // 8.1.2.3 about OPTIONS requests does not apply here (i.e. ":path" cannot be + // "*"). + if (path.empty() || path[0] != '/' || (path.size() >= 2 && path[1] == '/')) { + return std::string(); + } + + // Validate the scheme; this is to ensure a scheme of "foo://bar" is not + // parsed as a URL of "foo://bar://baz" when combined with a host of "baz". + std::string canonical_scheme; + url::StdStringCanonOutput canon_scheme_output(&canonical_scheme); + url::Component canon_component; + url::Component scheme_component(0, scheme.size()); + + if (!url::CanonicalizeScheme(scheme.data(), scheme_component, + &canon_scheme_output, &canon_component) || + !canon_component.is_nonempty() || canon_component.begin != 0) { + return std::string(); + } + canonical_scheme.resize(canon_component.len + 1); + + // Validate the authority; this is to ensure an authority such as + // "host/path" is not accepted, as when combined with a scheme like + // "http://", could result in a URL of "http://host/path". + url::Component auth_component(0, authority.size()); + url::Component username_component; + url::Component password_component; + url::Component host_component; + url::Component port_component; + + url::ParseAuthority(authority.data(), auth_component, &username_component, + &password_component, &host_component, &port_component); + + // RFC 7540, Section 8.1.2.3: The authority MUST NOT include the deprecated + // "userinfo" subcomponent for "http" or "https" schemed URIs. + // + // Note: Although |canonical_scheme| has not yet been checked for that, as + // it is performed later in processing, only "http" and "https" schemed + // URIs are supported for PUSH. + if (username_component.is_valid() || password_component.is_valid()) { + return std::string(); + } + + // Failed parsing or no host present. ParseAuthority() will ensure that + // host_component + port_component cover the entire string, if + // username_component and password_component are not present. + if (!host_component.is_nonempty()) { + return std::string(); + } + + // Validate the port (if present; it's optional). + int parsed_port_number = url::PORT_INVALID; + if (port_component.is_nonempty()) { + parsed_port_number = url::ParsePort(authority.data(), port_component); + if (parsed_port_number < 0 && parsed_port_number != url::PORT_UNSPECIFIED) { + return std::string(); + } + } + + // Validate the host by attempting to canonicalize it. Invalid characters + // will result in a canonicalization failure (e.g. '/') + std::string canon_host; + url::StdStringCanonOutput canon_host_output(&canon_host); + canon_component.reset(); + if (!url::CanonicalizeHost(authority.data(), host_component, + &canon_host_output, &canon_component) || + !canon_component.is_nonempty() || canon_component.begin != 0) { + return std::string(); + } + + // At this point, "authority" has been validated to either be of the form + // 'host:port' or 'host', with 'host' being a valid domain or IP address, + // and 'port' (if present), being a valid port. Attempt to construct a + // URL of just the (scheme, host, port), which should be safe and will not + // result in ambiguous parsing. + // + // This also enforces that all PUSHed URLs are either HTTP or HTTPS-schemed + // URIs, consistent with the other restrictions enforced above. + // + // Note: url::CanonicalizeScheme() will have added the ':' to + // |canonical_scheme|. + GURL origin_url(canonical_scheme + "//" + std::string(authority)); + if (!origin_url.is_valid() || !origin_url.SchemeIsHTTPOrHTTPS() || + // The following checks are merely defense in depth. + origin_url.has_username() || origin_url.has_password() || + (origin_url.has_path() && origin_url.path_piece() != "/") || + origin_url.has_query() || origin_url.has_ref()) { + return std::string(); + } + + // Attempt to parse the path. + std::string spec = origin_url.GetWithEmptyPath().spec(); + spec.pop_back(); // Remove the '/', as ":path" must contain it. + spec.append(std::string(path)); + + // Attempt to parse the full URL, with the path as well. Ensure there is no + // fragment to the query. + GURL full_url(spec); + if (!full_url.is_valid() || full_url.has_ref()) { + return std::string(); + } + + return full_url.spec(); +} + +} // namespace quic
diff --git a/quic/core/http/spdy_server_push_utils.h b/quic/core/http/spdy_server_push_utils.h new file mode 100644 index 0000000..3d11b65 --- /dev/null +++ b/quic/core/http/spdy_server_push_utils.h
@@ -0,0 +1,43 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_CORE_HTTP_SPDY_SERVER_PUSH_UTILS_H_ +#define QUICHE_QUIC_CORE_HTTP_SPDY_SERVER_PUSH_UTILS_H_ + +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h" + +namespace quic { + +class QUIC_EXPORT_PRIVATE SpdyServerPushUtils { + public: + SpdyServerPushUtils() = delete; + + // Returns a canonicalized URL composed from the :scheme, :authority, and + // :path headers of a PUSH_PROMISE. Returns empty string if the headers do not + // conform to HTTP/2 spec or if the ":method" header contains a forbidden + // method for PUSH_PROMISE. + static std::string GetPromisedUrlFromHeaders( + const spdy::SpdyHeaderBlock& headers); + + // Returns hostname, or empty string if missing. + static std::string GetPromisedHostNameFromHeaders( + const spdy::SpdyHeaderBlock& headers); + + // Returns true if result of |GetPromisedUrlFromHeaders()| is non-empty + // and is a well-formed URL. + static bool PromisedUrlIsValid(const spdy::SpdyHeaderBlock& headers); + + // Returns a canonical, valid URL for a PUSH_PROMISE with the specified + // ":scheme", ":authority", and ":path" header fields, or an empty + // string if the resulting URL is not valid or supported. + static std::string GetPushPromiseUrl(QuicStringPiece scheme, + QuicStringPiece authority, + QuicStringPiece path); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_HTTP_SPDY_SERVER_PUSH_UTILS_H_
diff --git a/quic/core/http/spdy_server_push_utils_test.cc b/quic/core/http/spdy_server_push_utils_test.cc new file mode 100644 index 0000000..72d4a25 --- /dev/null +++ b/quic/core/http/spdy_server_push_utils_test.cc
@@ -0,0 +1,221 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/core/http/spdy_server_push_utils.h" + +#include <memory> +#include <string> + +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" + +using spdy::SpdyHeaderBlock; +using testing::Pair; +using testing::UnorderedElementsAre; + +namespace quic { +namespace test { + +using GetPromisedUrlFromHeaders = QuicTest; + +TEST_F(GetPromisedUrlFromHeaders, Basic) { + SpdyHeaderBlock headers; + headers[":method"] = "GET"; + EXPECT_EQ(SpdyServerPushUtils::GetPromisedUrlFromHeaders(headers), ""); + headers[":scheme"] = "https"; + EXPECT_EQ(SpdyServerPushUtils::GetPromisedUrlFromHeaders(headers), ""); + headers[":authority"] = "www.google.com"; + EXPECT_EQ(SpdyServerPushUtils::GetPromisedUrlFromHeaders(headers), ""); + headers[":path"] = "/index.html"; + EXPECT_EQ(SpdyServerPushUtils::GetPromisedUrlFromHeaders(headers), + "https://www.google.com/index.html"); + headers["key1"] = "value1"; + headers["key2"] = "value2"; + EXPECT_EQ(SpdyServerPushUtils::GetPromisedUrlFromHeaders(headers), + "https://www.google.com/index.html"); +} + +TEST_F(GetPromisedUrlFromHeaders, Connect) { + SpdyHeaderBlock headers; + headers[":method"] = "CONNECT"; + EXPECT_EQ(SpdyServerPushUtils::GetPromisedUrlFromHeaders(headers), ""); + headers[":authority"] = "www.google.com"; + EXPECT_EQ(SpdyServerPushUtils::GetPromisedUrlFromHeaders(headers), ""); + headers[":scheme"] = "https"; + EXPECT_EQ(SpdyServerPushUtils::GetPromisedUrlFromHeaders(headers), ""); + headers[":path"] = "https"; + EXPECT_EQ(SpdyServerPushUtils::GetPromisedUrlFromHeaders(headers), ""); +} + +TEST_F(GetPromisedUrlFromHeaders, InvalidUserinfo) { + SpdyHeaderBlock headers; + headers[":method"] = "GET"; + headers[":authority"] = "user@www.google.com"; + headers[":scheme"] = "https"; + headers[":path"] = "/"; + EXPECT_EQ(SpdyServerPushUtils::GetPromisedUrlFromHeaders(headers), ""); +} + +TEST_F(GetPromisedUrlFromHeaders, InvalidPath) { + SpdyHeaderBlock headers; + headers[":method"] = "GET"; + headers[":authority"] = "www.google.com"; + headers[":scheme"] = "https"; + headers[":path"] = ""; + EXPECT_EQ(SpdyServerPushUtils::GetPromisedUrlFromHeaders(headers), ""); +} + +using GetPromisedHostNameFromHeaders = QuicTest; + +TEST_F(GetPromisedHostNameFromHeaders, NormalUsage) { + SpdyHeaderBlock headers; + headers[":method"] = "GET"; + EXPECT_EQ(SpdyServerPushUtils::GetPromisedHostNameFromHeaders(headers), ""); + headers[":scheme"] = "https"; + EXPECT_EQ(SpdyServerPushUtils::GetPromisedHostNameFromHeaders(headers), ""); + headers[":authority"] = "www.google.com"; + EXPECT_EQ(SpdyServerPushUtils::GetPromisedHostNameFromHeaders(headers), ""); + headers[":path"] = "/index.html"; + EXPECT_EQ(SpdyServerPushUtils::GetPromisedHostNameFromHeaders(headers), + "www.google.com"); + headers["key1"] = "value1"; + headers["key2"] = "value2"; + EXPECT_EQ(SpdyServerPushUtils::GetPromisedHostNameFromHeaders(headers), + "www.google.com"); + headers[":authority"] = "www.google.com:6666"; + EXPECT_EQ(SpdyServerPushUtils::GetPromisedHostNameFromHeaders(headers), + "www.google.com"); + headers[":authority"] = "192.168.1.1"; + EXPECT_EQ(SpdyServerPushUtils::GetPromisedHostNameFromHeaders(headers), + "192.168.1.1"); + headers[":authority"] = "192.168.1.1:6666"; + EXPECT_EQ(SpdyServerPushUtils::GetPromisedHostNameFromHeaders(headers), + "192.168.1.1"); +} + +using PushPromiseUrlTest = QuicTest; + +TEST_F(PushPromiseUrlTest, GetPushPromiseUrl) { + // Test rejection of various inputs. + EXPECT_EQ("", SpdyServerPushUtils::GetPushPromiseUrl("file", "localhost", + "/etc/password")); + EXPECT_EQ("", SpdyServerPushUtils::GetPushPromiseUrl( + "file", "", "/C:/Windows/System32/Config/")); + EXPECT_EQ("", SpdyServerPushUtils::GetPushPromiseUrl( + "", "https://www.google.com", "/")); + + EXPECT_EQ("", SpdyServerPushUtils::GetPushPromiseUrl("https://www.google.com", + "www.google.com", "/")); + EXPECT_EQ("", SpdyServerPushUtils::GetPushPromiseUrl("https://", + "www.google.com", "/")); + EXPECT_EQ("", SpdyServerPushUtils::GetPushPromiseUrl("https", "", "/")); + EXPECT_EQ("", SpdyServerPushUtils::GetPushPromiseUrl("https", "", + "www.google.com/")); + EXPECT_EQ("", SpdyServerPushUtils::GetPushPromiseUrl("https", + "www.google.com/", "/")); + EXPECT_EQ("", SpdyServerPushUtils::GetPushPromiseUrl("https", + "www.google.com", "")); + EXPECT_EQ("", SpdyServerPushUtils::GetPushPromiseUrl("https", "www.google", + ".com/")); + + // Test acception/rejection of various input combinations. + // |input_headers| is an array of pairs. The first value of each pair is a + // string that will be used as one of the inputs of GetPushPromiseUrl(). The + // second value of each pair is a bitfield where the lowest 3 bits indicate + // for which headers that string is valid (in a PUSH_PROMISE). For example, + // the string "http" would be valid for both the ":scheme" and ":authority" + // headers, so the bitfield paired with it is set to SCHEME | AUTH. + const unsigned char SCHEME = (1u << 0); + const unsigned char AUTH = (1u << 1); + const unsigned char PATH = (1u << 2); + const std::pair<const char*, unsigned char> input_headers[] = { + {"http", SCHEME | AUTH}, + {"https", SCHEME | AUTH}, + {"hTtP", SCHEME | AUTH}, + {"HTTPS", SCHEME | AUTH}, + {"www.google.com", AUTH}, + {"90af90e0", AUTH}, + {"12foo%20-bar:00001233", AUTH}, + {"GOO\u200b\u2060\ufeffgoo", AUTH}, + {"192.168.0.5", AUTH}, + {"[::ffff:192.168.0.1.]", AUTH}, + {"http:", AUTH}, + {"bife l", AUTH}, + {"/", PATH}, + {"/foo/bar/baz", PATH}, + {"/%20-2DVdkj.cie/foe_.iif/", PATH}, + {"http://", 0}, + {":443", 0}, + {":80/eddd", 0}, + {"google.com:-0", 0}, + {"google.com:65536", 0}, + {"http://google.com", 0}, + {"http://google.com:39", 0}, + {"//google.com/foo", 0}, + {".com/", 0}, + {"http://www.google.com/", 0}, + {"http://foo:439", 0}, + {"[::ffff:192.168", 0}, + {"]/", 0}, + {"//", 0}}; + for (size_t i = 0; i < QUIC_ARRAYSIZE(input_headers); ++i) { + bool should_accept = (input_headers[i].second & SCHEME); + for (size_t j = 0; j < QUIC_ARRAYSIZE(input_headers); ++j) { + bool should_accept_2 = should_accept && (input_headers[j].second & AUTH); + for (size_t k = 0; k < QUIC_ARRAYSIZE(input_headers); ++k) { + // |should_accept_3| indicates whether or not GetPushPromiseUrl() is + // expected to accept this input combination. + bool should_accept_3 = + should_accept_2 && (input_headers[k].second & PATH); + + std::string url = SpdyServerPushUtils::GetPushPromiseUrl( + input_headers[i].first, input_headers[j].first, + input_headers[k].first); + + ::testing::AssertionResult result = ::testing::AssertionSuccess(); + if (url.empty() == should_accept_3) { + result = ::testing::AssertionFailure() + << "GetPushPromiseUrl() accepted/rejected the inputs when " + "it shouldn't have." + << std::endl + << " scheme: " << input_headers[i].first << std::endl + << " authority: " << input_headers[j].first << std::endl + << " path: " << input_headers[k].first << std::endl + << "Output: " << url << std::endl; + } + ASSERT_TRUE(result); + } + } + } + + // Test canonicalization of various valid inputs. + EXPECT_EQ("http://www.google.com/", SpdyServerPushUtils::GetPushPromiseUrl( + "http", "www.google.com", "/")); + EXPECT_EQ("https://www.goo-gle.com/fOOo/baRR", + SpdyServerPushUtils::GetPushPromiseUrl("hTtPs", "wWw.gOo-gLE.cOm", + "/fOOo/baRR")); + EXPECT_EQ("https://www.goo-gle.com:3278/pAth/To/reSOurce", + SpdyServerPushUtils::GetPushPromiseUrl( + "hTtPs", "Www.gOo-Gle.Com:000003278", "/pAth/To/reSOurce")); + EXPECT_EQ("https://foo%20bar/foo/bar/baz", + SpdyServerPushUtils::GetPushPromiseUrl("https", "foo bar", + "/foo/bar/baz")); + EXPECT_EQ("http://foo.com:70/e/", SpdyServerPushUtils::GetPushPromiseUrl( + "http", "foo.com:0000070", "/e/")); + EXPECT_EQ("http://192.168.0.1:70/e/", + SpdyServerPushUtils::GetPushPromiseUrl( + "http", "0300.0250.00.01:0070", "/e/")); + EXPECT_EQ("http://192.168.0.1/e/", SpdyServerPushUtils::GetPushPromiseUrl( + "http", "0xC0a80001", "/e/")); + EXPECT_EQ("http://[::c0a8:1]/", SpdyServerPushUtils::GetPushPromiseUrl( + "http", "[::192.168.0.1]", "/")); + EXPECT_EQ("https://[::ffff:c0a8:1]/", + SpdyServerPushUtils::GetPushPromiseUrl( + "https", "[::ffff:0xC0.0Xa8.0x0.0x1]", "/")); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/http/spdy_utils.cc b/quic/core/http/spdy_utils.cc index 721ae33..d72a18d 100644 --- a/quic/core/http/spdy_utils.cc +++ b/quic/core/http/spdy_utils.cc
@@ -8,15 +8,12 @@ #include <string> #include <vector> -#include "url/gurl.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" #include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" #include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" -#include "net/third_party/quiche/src/spdy/core/spdy_frame_builder.h" -#include "net/third_party/quiche/src/spdy/core/spdy_framer.h" #include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" using spdy::SpdyHeaderBlock; @@ -131,74 +128,7 @@ } // static -std::string SpdyUtils::GetPromisedUrlFromHeaders( - const SpdyHeaderBlock& headers) { - // RFC 7540, Section 8.1.2.3: All HTTP/2 requests MUST include exactly - // one valid value for the ":method", ":scheme", and ":path" pseudo-header - // fields, unless it is a CONNECT request. - - // RFC 7540, Section 8.2.1: The header fields in PUSH_PROMISE and any - // subsequent CONTINUATION frames MUST be a valid and complete set of request - // header fields (Section 8.1.2.3). The server MUST include a method in the - // ":method" pseudo-header field that is safe and cacheable. - // - // RFC 7231, Section 4.2.1: Of the request methods defined by this - // specification, the GET, HEAD, OPTIONS, and TRACE methods are defined to be - // safe. - // - // RFC 7231, Section 4.2.1: ... this specification defines GET, HEAD, and - // POST as cacheable, ... - // - // So the only methods allowed in a PUSH_PROMISE are GET and HEAD. - SpdyHeaderBlock::const_iterator it = headers.find(":method"); - if (it == headers.end() || (it->second != "GET" && it->second != "HEAD")) { - return std::string(); - } - - it = headers.find(":scheme"); - if (it == headers.end() || it->second.empty()) { - return std::string(); - } - QuicStringPiece scheme = it->second; - - // RFC 7540, Section 8.2: The server MUST include a value in the - // ":authority" pseudo-header field for which the server is authoritative - // (see Section 10.1). - it = headers.find(":authority"); - if (it == headers.end() || it->second.empty()) { - return std::string(); - } - QuicStringPiece authority = it->second; - - // RFC 7540, Section 8.1.2.3 requires that the ":path" pseudo-header MUST - // NOT be empty for "http" or "https" URIs; - // - // However, to ensure the scheme is consistently canonicalized, that check - // is deferred to implementations in QuicUrlUtils::GetPushPromiseUrl(). - it = headers.find(":path"); - if (it == headers.end()) { - return std::string(); - } - QuicStringPiece path = it->second; - - return GetPushPromiseUrl(scheme, authority, path); -} - -// static -std::string SpdyUtils::GetPromisedHostNameFromHeaders( - const SpdyHeaderBlock& headers) { - // TODO(fayang): Consider just checking out the value of the ":authority" key - // in headers. - return GURL(GetPromisedUrlFromHeaders(headers)).host(); -} - -// static -bool SpdyUtils::PromisedUrlIsValid(const SpdyHeaderBlock& headers) { - std::string url(GetPromisedUrlFromHeaders(headers)); - return !url.empty() && GURL(url).is_valid(); -} - -// static +// TODO(danzh): Move it to quic/tools/ and switch to use GURL. bool SpdyUtils::PopulateHeaderBlockFromUrl(const std::string url, SpdyHeaderBlock* headers) { (*headers)[":method"] = "GET"; @@ -219,137 +149,4 @@ return true; } -// static -std::string SpdyUtils::GetPushPromiseUrl(QuicStringPiece scheme, - QuicStringPiece authority, - QuicStringPiece path) { - // RFC 7540, Section 8.1.2.3: The ":path" pseudo-header field includes the - // path and query parts of the target URI (the "path-absolute" production - // and optionally a '?' character followed by the "query" production (see - // Sections 3.3 and 3.4 of RFC3986). A request in asterisk form includes the - // value '*' for the ":path" pseudo-header field. - // - // This pseudo-header field MUST NOT be empty for "http" or "https" URIs; - // "http" or "https" URIs that do not contain a path MUST include a value of - // '/'. The exception to this rule is an OPTIONS request for an "http" or - // "https" URI that does not include a path component; these MUST include a - // ":path" pseudo-header with a value of '*' (see RFC7230, Section 5.3.4). - // - // In addition to the above restriction from RFC 7540, note that RFC3986 - // defines the "path-absolute" construction as starting with "/" but not "//". - // - // RFC 7540, Section 8.2.1: The header fields in PUSH_PROMISE and any - // subsequent CONTINUATION frames MUST be a valid and complete set of request - // header fields (Section 8.1.2.3). The server MUST include a method in the - // ":method" pseudo-header field that is safe and cacheable. - // - // RFC 7231, Section 4.2.1: - // ... this specification defines GET, HEAD, and POST as cacheable, ... - // - // Since the OPTIONS method is not cacheable, it cannot be the method of a - // PUSH_PROMISE. Therefore, the exception mentioned in RFC 7540, Section - // 8.1.2.3 about OPTIONS requests does not apply here (i.e. ":path" cannot be - // "*"). - if (path.empty() || path[0] != '/' || (path.size() >= 2 && path[1] == '/')) { - return std::string(); - } - - // Validate the scheme; this is to ensure a scheme of "foo://bar" is not - // parsed as a URL of "foo://bar://baz" when combined with a host of "baz". - std::string canonical_scheme; - url::StdStringCanonOutput canon_scheme_output(&canonical_scheme); - url::Component canon_component; - url::Component scheme_component(0, scheme.size()); - - if (!url::CanonicalizeScheme(scheme.data(), scheme_component, - &canon_scheme_output, &canon_component) || - !canon_component.is_nonempty() || canon_component.begin != 0) { - return std::string(); - } - canonical_scheme.resize(canon_component.len + 1); - - // Validate the authority; this is to ensure an authority such as - // "host/path" is not accepted, as when combined with a scheme like - // "http://", could result in a URL of "http://host/path". - url::Component auth_component(0, authority.size()); - url::Component username_component; - url::Component password_component; - url::Component host_component; - url::Component port_component; - - url::ParseAuthority(authority.data(), auth_component, &username_component, - &password_component, &host_component, &port_component); - - // RFC 7540, Section 8.1.2.3: The authority MUST NOT include the deprecated - // "userinfo" subcomponent for "http" or "https" schemed URIs. - // - // Note: Although |canonical_scheme| has not yet been checked for that, as - // it is performed later in processing, only "http" and "https" schemed - // URIs are supported for PUSH. - if (username_component.is_valid() || password_component.is_valid()) { - return std::string(); - } - - // Failed parsing or no host present. ParseAuthority() will ensure that - // host_component + port_component cover the entire string, if - // username_component and password_component are not present. - if (!host_component.is_nonempty()) { - return std::string(); - } - - // Validate the port (if present; it's optional). - int parsed_port_number = url::PORT_INVALID; - if (port_component.is_nonempty()) { - parsed_port_number = url::ParsePort(authority.data(), port_component); - if (parsed_port_number < 0 && parsed_port_number != url::PORT_UNSPECIFIED) { - return std::string(); - } - } - - // Validate the host by attempting to canonicalize it. Invalid characters - // will result in a canonicalization failure (e.g. '/') - std::string canon_host; - url::StdStringCanonOutput canon_host_output(&canon_host); - canon_component.reset(); - if (!url::CanonicalizeHost(authority.data(), host_component, - &canon_host_output, &canon_component) || - !canon_component.is_nonempty() || canon_component.begin != 0) { - return std::string(); - } - - // At this point, "authority" has been validated to either be of the form - // 'host:port' or 'host', with 'host' being a valid domain or IP address, - // and 'port' (if present), being a valid port. Attempt to construct a - // URL of just the (scheme, host, port), which should be safe and will not - // result in ambiguous parsing. - // - // This also enforces that all PUSHed URLs are either HTTP or HTTPS-schemed - // URIs, consistent with the other restrictions enforced above. - // - // Note: url::CanonicalizeScheme() will have added the ':' to - // |canonical_scheme|. - GURL origin_url(canonical_scheme + "//" + std::string(authority)); - if (!origin_url.is_valid() || !origin_url.SchemeIsHTTPOrHTTPS() || - // The following checks are merely defense in depth. - origin_url.has_username() || origin_url.has_password() || - (origin_url.has_path() && origin_url.path_piece() != "/") || - origin_url.has_query() || origin_url.has_ref()) { - return std::string(); - } - - // Attempt to parse the path. - std::string spec = origin_url.GetWithEmptyPath().spec(); - spec.pop_back(); // Remove the '/', as ":path" must contain it. - spec.append(std::string(path)); - - // Attempt to parse the full URL, with the path as well. Ensure there is no - // fragment to the query. - GURL full_url(spec); - if (!full_url.is_valid() || full_url.has_ref()) { - return std::string(); - } - - return full_url.spec(); -} - } // namespace quic
diff --git a/quic/core/http/spdy_utils.h b/quic/core/http/spdy_utils.h index dc3fabc..4b962c2 100644 --- a/quic/core/http/spdy_utils.h +++ b/quic/core/http/spdy_utils.h
@@ -12,7 +12,7 @@ #include "net/third_party/quiche/src/quic/core/http/quic_header_list.h" #include "net/third_party/quiche/src/quic/core/quic_packets.h" #include "net/third_party/quiche/src/quic/platform/api/quic_export.h" -#include "net/third_party/quiche/src/spdy/core/spdy_framer.h" +#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h" namespace quic { @@ -46,32 +46,10 @@ size_t* final_byte_offset, spdy::SpdyHeaderBlock* trailers); - // Returns a canonicalized URL composed from the :scheme, :authority, and - // :path headers of a PUSH_PROMISE. Returns empty string if the headers do not - // conform to HTTP/2 spec or if the ":method" header contains a forbidden - // method for PUSH_PROMISE. - static std::string GetPromisedUrlFromHeaders( - const spdy::SpdyHeaderBlock& headers); - - // Returns hostname, or empty string if missing. - static std::string GetPromisedHostNameFromHeaders( - const spdy::SpdyHeaderBlock& headers); - - // Returns true if result of |GetPromisedUrlFromHeaders()| is non-empty - // and is a well-formed URL. - static bool PromisedUrlIsValid(const spdy::SpdyHeaderBlock& headers); - // Populates the fields of |headers| to make a GET request of |url|, // which must be fully-qualified. static bool PopulateHeaderBlockFromUrl(const std::string url, spdy::SpdyHeaderBlock* headers); - - // Returns a canonical, valid URL for a PUSH_PROMISE with the specified - // ":scheme", ":authority", and ":path" header fields, or an empty - // string if the resulting URL is not valid or supported. - static std::string GetPushPromiseUrl(QuicStringPiece scheme, - QuicStringPiece authority, - QuicStringPiece path); }; } // namespace quic
diff --git a/quic/core/http/spdy_utils_test.cc b/quic/core/http/spdy_utils_test.cc index d5c96fb..2ebf0cf 100644 --- a/quic/core/http/spdy_utils_test.cc +++ b/quic/core/http/spdy_utils_test.cc
@@ -334,81 +334,6 @@ Pair("key", "value"))); } -using GetPromisedUrlFromHeaders = QuicTest; - -TEST_F(GetPromisedUrlFromHeaders, Basic) { - SpdyHeaderBlock headers; - headers[":method"] = "GET"; - EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), ""); - headers[":scheme"] = "https"; - EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), ""); - headers[":authority"] = "www.google.com"; - EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), ""); - headers[":path"] = "/index.html"; - EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), - "https://www.google.com/index.html"); - headers["key1"] = "value1"; - headers["key2"] = "value2"; - EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), - "https://www.google.com/index.html"); -} - -TEST_F(GetPromisedUrlFromHeaders, Connect) { - SpdyHeaderBlock headers; - headers[":method"] = "CONNECT"; - EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), ""); - headers[":authority"] = "www.google.com"; - EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), ""); - headers[":scheme"] = "https"; - EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), ""); - headers[":path"] = "https"; - EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), ""); -} - -TEST_F(GetPromisedUrlFromHeaders, InvalidUserinfo) { - SpdyHeaderBlock headers; - headers[":method"] = "GET"; - headers[":authority"] = "user@www.google.com"; - headers[":scheme"] = "https"; - headers[":path"] = "/"; - EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), ""); -} - -TEST_F(GetPromisedUrlFromHeaders, InvalidPath) { - SpdyHeaderBlock headers; - headers[":method"] = "GET"; - headers[":authority"] = "www.google.com"; - headers[":scheme"] = "https"; - headers[":path"] = ""; - EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), ""); -} - -using GetPromisedHostNameFromHeaders = QuicTest; - -TEST_F(GetPromisedHostNameFromHeaders, NormalUsage) { - SpdyHeaderBlock headers; - headers[":method"] = "GET"; - EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers), ""); - headers[":scheme"] = "https"; - EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers), ""); - headers[":authority"] = "www.google.com"; - EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers), ""); - headers[":path"] = "/index.html"; - EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers), - "www.google.com"); - headers["key1"] = "value1"; - headers["key2"] = "value2"; - EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers), - "www.google.com"); - headers[":authority"] = "www.google.com:6666"; - EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers), - "www.google.com"); - headers[":authority"] = "192.168.1.1"; - EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers), "192.168.1.1"); - headers[":authority"] = "192.168.1.1:6666"; - EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers), "192.168.1.1"); -} - using PopulateHeaderBlockFromUrl = QuicTest; TEST_F(PopulateHeaderBlockFromUrl, NormalUsage) { @@ -437,121 +362,5 @@ SpdyUtils::PopulateHeaderBlockFromUrl("www.google.com/", &headers)); } -using PushPromiseUrlTest = QuicTest; - -TEST_F(PushPromiseUrlTest, GetPushPromiseUrl) { - // Test rejection of various inputs. - EXPECT_EQ("", - SpdyUtils::GetPushPromiseUrl("file", "localhost", "/etc/password")); - EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("file", "", - "/C:/Windows/System32/Config/")); - EXPECT_EQ("", - SpdyUtils::GetPushPromiseUrl("", "https://www.google.com", "/")); - - EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https://www.google.com", - "www.google.com", "/")); - EXPECT_EQ("", - SpdyUtils::GetPushPromiseUrl("https://", "www.google.com", "/")); - EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https", "", "/")); - EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https", "", "www.google.com/")); - EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https", "www.google.com/", "/")); - EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https", "www.google.com", "")); - EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https", "www.google", ".com/")); - - // Test acception/rejection of various input combinations. - // |input_headers| is an array of pairs. The first value of each pair is a - // string that will be used as one of the inputs of GetPushPromiseUrl(). The - // second value of each pair is a bitfield where the lowest 3 bits indicate - // for which headers that string is valid (in a PUSH_PROMISE). For example, - // the string "http" would be valid for both the ":scheme" and ":authority" - // headers, so the bitfield paired with it is set to SCHEME | AUTH. - const unsigned char SCHEME = (1u << 0); - const unsigned char AUTH = (1u << 1); - const unsigned char PATH = (1u << 2); - const std::pair<const char*, unsigned char> input_headers[] = { - {"http", SCHEME | AUTH}, - {"https", SCHEME | AUTH}, - {"hTtP", SCHEME | AUTH}, - {"HTTPS", SCHEME | AUTH}, - {"www.google.com", AUTH}, - {"90af90e0", AUTH}, - {"12foo%20-bar:00001233", AUTH}, - {"GOO\u200b\u2060\ufeffgoo", AUTH}, - {"192.168.0.5", AUTH}, - {"[::ffff:192.168.0.1.]", AUTH}, - {"http:", AUTH}, - {"bife l", AUTH}, - {"/", PATH}, - {"/foo/bar/baz", PATH}, - {"/%20-2DVdkj.cie/foe_.iif/", PATH}, - {"http://", 0}, - {":443", 0}, - {":80/eddd", 0}, - {"google.com:-0", 0}, - {"google.com:65536", 0}, - {"http://google.com", 0}, - {"http://google.com:39", 0}, - {"//google.com/foo", 0}, - {".com/", 0}, - {"http://www.google.com/", 0}, - {"http://foo:439", 0}, - {"[::ffff:192.168", 0}, - {"]/", 0}, - {"//", 0}}; - for (size_t i = 0; i < QUIC_ARRAYSIZE(input_headers); ++i) { - bool should_accept = (input_headers[i].second & SCHEME); - for (size_t j = 0; j < QUIC_ARRAYSIZE(input_headers); ++j) { - bool should_accept_2 = should_accept && (input_headers[j].second & AUTH); - for (size_t k = 0; k < QUIC_ARRAYSIZE(input_headers); ++k) { - // |should_accept_3| indicates whether or not GetPushPromiseUrl() is - // expected to accept this input combination. - bool should_accept_3 = - should_accept_2 && (input_headers[k].second & PATH); - - std::string url = SpdyUtils::GetPushPromiseUrl(input_headers[i].first, - input_headers[j].first, - input_headers[k].first); - - ::testing::AssertionResult result = ::testing::AssertionSuccess(); - if (url.empty() == should_accept_3) { - result = ::testing::AssertionFailure() - << "GetPushPromiseUrl() accepted/rejected the inputs when " - "it shouldn't have." - << std::endl - << " scheme: " << input_headers[i].first << std::endl - << " authority: " << input_headers[j].first << std::endl - << " path: " << input_headers[k].first << std::endl - << "Output: " << url << std::endl; - } - ASSERT_TRUE(result); - } - } - } - - // Test canonicalization of various valid inputs. - EXPECT_EQ("http://www.google.com/", - SpdyUtils::GetPushPromiseUrl("http", "www.google.com", "/")); - EXPECT_EQ( - "https://www.goo-gle.com/fOOo/baRR", - SpdyUtils::GetPushPromiseUrl("hTtPs", "wWw.gOo-gLE.cOm", "/fOOo/baRR")); - EXPECT_EQ("https://www.goo-gle.com:3278/pAth/To/reSOurce", - SpdyUtils::GetPushPromiseUrl("hTtPs", "Www.gOo-Gle.Com:000003278", - "/pAth/To/reSOurce")); - EXPECT_EQ("https://foo%20bar/foo/bar/baz", - SpdyUtils::GetPushPromiseUrl("https", "foo bar", "/foo/bar/baz")); - EXPECT_EQ("http://foo.com:70/e/", - SpdyUtils::GetPushPromiseUrl("http", "foo.com:0000070", "/e/")); - EXPECT_EQ( - "http://192.168.0.1:70/e/", - SpdyUtils::GetPushPromiseUrl("http", "0300.0250.00.01:0070", "/e/")); - EXPECT_EQ("http://192.168.0.1/e/", - SpdyUtils::GetPushPromiseUrl("http", "0xC0a80001", "/e/")); - EXPECT_EQ("http://[::c0a8:1]/", - SpdyUtils::GetPushPromiseUrl("http", "[::192.168.0.1]", "/")); - EXPECT_EQ( - "https://[::ffff:c0a8:1]/", - SpdyUtils::GetPushPromiseUrl("https", "[::ffff:0xC0.0Xa8.0x0.0x1]", "/")); -} - } // namespace test } // namespace quic
diff --git a/quic/tools/quic_backend_response.h b/quic/tools/quic_backend_response.h index 4ef0fdc..b578915 100644 --- a/quic/tools/quic_backend_response.h +++ b/quic/tools/quic_backend_response.h
@@ -5,8 +5,8 @@ #ifndef QUICHE_QUIC_TOOLS_QUIC_BACKEND_RESPONSE_H_ #define QUICHE_QUIC_TOOLS_QUIC_BACKEND_RESPONSE_H_ -#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" #include "net/third_party/quiche/src/quic/tools/quic_url.h" +#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" namespace quic {
diff --git a/quic/tools/quic_simple_server_backend.h b/quic/tools/quic_simple_server_backend.h index 74afb60..968ec66 100644 --- a/quic/tools/quic_simple_server_backend.h +++ b/quic/tools/quic_simple_server_backend.h
@@ -5,6 +5,7 @@ #ifndef QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_BACKEND_H_ #define QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_BACKEND_H_ +#include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/tools/quic_backend_response.h" namespace spdy {