Parse the path component of the indication on the server. gfe-relnote: n/a (not used in production) PiperOrigin-RevId: 284179158 Change-Id: I5246996a99e93e5863502b0dbc950945cf1e6c25
diff --git a/quic/quic_transport/quic_transport_server_session.cc b/quic/quic_transport/quic_transport_server_session.cc index 7f00acd..a6d7c21 100644 --- a/quic/quic_transport/quic_transport_server_session.cc +++ b/quic/quic_transport/quic_transport_server_session.cc
@@ -4,9 +4,12 @@ #include "net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.h" +#include <algorithm> #include <memory> +#include <string> #include "url/gurl.h" +#include "url/url_constants.h" #include "net/third_party/quiche/src/quic/core/quic_error_codes.h" #include "net/third_party/quiche/src/quic/core/quic_stream.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" @@ -28,6 +31,7 @@ return true; } }; + } // namespace QuicTransportServerSession::QuicTransportServerSession( @@ -96,6 +100,7 @@ bool QuicTransportServerSession::ClientIndicationParser::Parse() { bool origin_received = false; + bool path_received = false; while (!reader_.IsDoneReading()) { uint16_t key; if (!reader_.ReadUInt16(&key)) { @@ -127,6 +132,14 @@ break; } + case QuicTransportClientIndicationKeys::kPath: { + if (!ProcessPath(value)) { + return false; + } + path_received = true; + break; + } + default: QUIC_DLOG(INFO) << "Unknown client indication key: " << key; break; @@ -137,10 +150,40 @@ Error("No origin received"); return false; } + if (!path_received) { + Error("No path received"); + return false; + } return true; } +bool QuicTransportServerSession::ClientIndicationParser::ProcessPath( + QuicStringPiece path) { + if (path.empty() || path[0] != '/') { + // https://tools.ietf.org/html/draft-vvv-webtransport-quic-01#section-3.2.2 + Error("Path must begin with a '/'"); + return false; + } + + // TODO(b/145674008): use the SNI value from the handshake instead of the IP + // address. + std::string url_text = + QuicStrCat(url::kQuicTransportScheme, url::kStandardSchemeSeparator, + session_->self_address().ToString(), path); + GURL url{url_text}; + if (!url.is_valid()) { + Error("Invalid path specified"); + return false; + } + + if (!session_->visitor_->ProcessPath(url)) { + Error("Specified path rejected"); + return false; + } + return true; +} + void QuicTransportServerSession::ClientIndicationParser::Error( const std::string& error_message) { session_->connection()->CloseConnection(
diff --git a/quic/quic_transport/quic_transport_server_session.h b/quic/quic_transport/quic_transport_server_session.h index b3fcfa0..8b0c063 100644 --- a/quic/quic_transport/quic_transport_server_session.h +++ b/quic/quic_transport/quic_transport_server_session.h
@@ -5,6 +5,7 @@ #ifndef QUICHE_QUIC_QUIC_TRANSPORT_QUIC_TRANSPORT_SERVER_SESSION_H_ #define QUICHE_QUIC_QUIC_TRANSPORT_QUIC_TRANSPORT_SERVER_SESSION_H_ +#include "url/gurl.h" #include "url/origin.h" #include "net/third_party/quiche/src/quic/core/quic_connection.h" #include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h" @@ -25,7 +26,14 @@ public: virtual ~ServerVisitor() {} + // Allows the server to decide whether the specified origin is allowed to + // connect to it. virtual bool CheckOrigin(url::Origin origin) = 0; + + // Indicates that the server received a path parameter from the client. The + // path parameter is parsed, and can be retrived from url.path() and + // url.query(). If this method returns false, the connection is closed. + virtual bool ProcessPath(const GURL& url) = 0; }; QuicTransportServerSession(QuicConnection* connection, @@ -90,6 +98,9 @@ void Error(const std::string& error_message); void ParseError(QuicStringPiece error_message); + // Processes the path portion of the client indication. + bool ProcessPath(QuicStringPiece path); + QuicTransportServerSession* session_; QuicDataReader reader_; };
diff --git a/quic/quic_transport/quic_transport_server_session_test.cc b/quic/quic_transport/quic_transport_server_session_test.cc index 5e67748..efa668c 100644 --- a/quic/quic_transport/quic_transport_server_session_test.cc +++ b/quic/quic_transport/quic_transport_server_session_test.cc
@@ -36,7 +36,12 @@ constexpr char kTestOrigin[] = "https://test-origin.test"; constexpr char kTestOriginClientIndication[] = - "\0\0\0\x18https://test-origin.test"; + "\0\0" // key (0x0000, origin) + "\0\x18" // length + "https://test-origin.test" // value + "\0\x01" // key (0x0001, path) + "\0\x05" // length + "/test"; // value const url::Origin GetTestOrigin() { return url::Origin::Create(GURL(kTestOrigin)); } @@ -75,6 +80,8 @@ if (!GetQuicReloadableFlag(quic_version_negotiated_by_default_at_server)) { crypto_stream_->OnSuccessfulVersionNegotiation(GetVersions()[0]); } + ON_CALL(visitor_, ProcessPath(_)) + .WillByDefault(DoAll(SaveArg<0>(&path_), Return(true))); } void Connect() { @@ -101,6 +108,20 @@ QuicStringPiece())); } + void ReceiveIndicationWithPath(QuicStringPiece path) { + constexpr char kTestOriginClientIndicationPrefix[] = + "\0\0" // key (0x0000, origin) + "\0\x18" // length + "https://test-origin.test" // value + "\0\x01"; // key (0x0001, path) + std::string indication{kTestOriginClientIndicationPrefix, + sizeof(kTestOriginClientIndicationPrefix) - 1}; + indication.push_back(static_cast<char>(path.size() >> 8)); + indication.push_back(static_cast<char>(path.size() & 0xff)); + indication += std::string{path}; + ReceiveIndication(indication); + } + protected: MockAlarmFactory alarm_factory_; MockQuicConnectionHelper helper_; @@ -109,8 +130,9 @@ QuicCryptoServerConfig crypto_config_; std::unique_ptr<QuicTransportServerSession> session_; QuicCompressedCertsCache compressed_certs_cache_; - testing::StrictMock<MockServerVisitor> visitor_; + testing::NiceMock<MockServerVisitor> visitor_; QuicCryptoServerStream* crypto_stream_; + GURL path_; }; TEST_F(QuicTransportServerSessionTest, SuccessfulHandshake) { @@ -122,6 +144,7 @@ ReceiveIndication(GetTestOriginClientIndication()); EXPECT_TRUE(session_->IsSessionReady()); EXPECT_EQ(origin, GetTestOrigin()); + EXPECT_EQ(path_.path(), "/test"); } TEST_F(QuicTransportServerSessionTest, PiecewiseClientIndication) { @@ -231,6 +254,41 @@ EXPECT_FALSE(session_->IsSessionReady()); } +TEST_F(QuicTransportServerSessionTest, PathWithQuery) { + Connect(); + EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(true)); + ReceiveIndicationWithPath("/test?foo=bar"); + EXPECT_TRUE(session_->IsSessionReady()); + EXPECT_EQ(path_.path(), "/test"); + EXPECT_EQ(path_.query(), "foo=bar"); +} + +TEST_F(QuicTransportServerSessionTest, PathNormalization) { + Connect(); + EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(true)); + ReceiveIndicationWithPath("/foo/../bar"); + EXPECT_TRUE(session_->IsSessionReady()); + EXPECT_EQ(path_.path(), "/bar"); +} + +TEST_F(QuicTransportServerSessionTest, EmptyPath) { + Connect(); + EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(true)); + EXPECT_CALL(connection_, + CloseConnection(_, HasSubstr("Path must begin with a '/'"), _)); + ReceiveIndicationWithPath(""); + EXPECT_FALSE(session_->IsSessionReady()); +} + +TEST_F(QuicTransportServerSessionTest, UnprefixedPath) { + Connect(); + EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(true)); + EXPECT_CALL(connection_, + CloseConnection(_, HasSubstr("Path must begin with a '/'"), _)); + ReceiveIndicationWithPath("test"); + EXPECT_FALSE(session_->IsSessionReady()); +} + } // namespace } // namespace test } // namespace quic
diff --git a/quic/test_tools/quic_transport_test_tools.h b/quic/test_tools/quic_transport_test_tools.h index c6a8b46..9632d4c 100644 --- a/quic/test_tools/quic_transport_test_tools.h +++ b/quic/test_tools/quic_transport_test_tools.h
@@ -21,6 +21,7 @@ class MockServerVisitor : public QuicTransportServerSession::ServerVisitor { public: MOCK_METHOD1(CheckOrigin, bool(url::Origin)); + MOCK_METHOD1(ProcessPath, bool(const GURL&)); }; class MockStreamVisitor : public QuicTransportStream::Visitor {
diff --git a/quic/tools/quic_transport_simple_server_session.cc b/quic/tools/quic_transport_simple_server_session.cc index 6e86cca..40e5af3 100644 --- a/quic/tools/quic_transport_simple_server_session.cc +++ b/quic/tools/quic_transport_simple_server_session.cc
@@ -202,6 +202,11 @@ return false; } +bool QuicTransportSimpleServerSession::ProcessPath(const GURL& url) { + QUIC_DLOG(INFO) << "Path requested: " << url; + return true; +} + void QuicTransportSimpleServerSession::MaybeEchoStreamsBack() { while (!streams_to_echo_back_.empty() && CanOpenNextOutgoingUnidirectionalStream()) {
diff --git a/quic/tools/quic_transport_simple_server_session.h b/quic/tools/quic_transport_simple_server_session.h index 11f82f2..5857930 100644 --- a/quic/tools/quic_transport_simple_server_session.h +++ b/quic/tools/quic_transport_simple_server_session.h
@@ -13,6 +13,7 @@ #include "net/third_party/quiche/src/quic/core/quic_versions.h" #include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" #include "net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.h" #include "net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h" @@ -51,6 +52,7 @@ void OnIncomingDataStream(QuicTransportStream* stream) override; void OnCanCreateNewOutgoingStream(bool unidirectional) override; bool CheckOrigin(url::Origin origin) override; + bool ProcessPath(const GURL& url) override; void EchoStreamBack(const std::string& data) { streams_to_echo_back_.push_back(data);