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);