Implement a QuicSession subclass for QuicTransport client.
gfe-relnote: n/a (code not used in production)
PiperOrigin-RevId: 272427421
Change-Id: I1a8ebe085ce26eaa9ec19c0be9b6f4bb7c2ad73e
diff --git a/quic/core/http/quic_spdy_client_session_test.cc b/quic/core/http/quic_spdy_client_session_test.cc
index 235d810..2fab3e1 100644
--- a/quic/core/http/quic_spdy_client_session_test.cc
+++ b/quic/core/http/quic_spdy_client_session_test.cc
@@ -165,7 +165,8 @@
server_max_incoming_streams);
}
crypto_test_utils::HandshakeWithFakeServer(
- &config, &helper_, &alarm_factory_, connection_, stream);
+ &config, &helper_, &alarm_factory_, connection_, stream,
+ AlpnForVersion(connection_->version()));
}
QuicCryptoClientConfig crypto_config_;
diff --git a/quic/core/quic_crypto_client_stream_test.cc b/quic/core/quic_crypto_client_stream_test.cc
index f0a60e0..9969baf 100644
--- a/quic/core/quic_crypto_client_stream_test.cc
+++ b/quic/core/quic_crypto_client_stream_test.cc
@@ -62,7 +62,8 @@
stream()->CryptoConnect();
QuicConfig config;
crypto_test_utils::HandshakeWithFakeServer(
- &config, &server_helper_, &alarm_factory_, connection_, stream());
+ &config, &server_helper_, &alarm_factory_, connection_, stream(),
+ AlpnForVersion(connection_->version()));
}
QuicCryptoClientStream* stream() {
diff --git a/quic/core/quic_crypto_server_stream_test.cc b/quic/core/quic_crypto_server_stream_test.cc
index e2ccfde..b73ea43 100644
--- a/quic/core/quic_crypto_server_stream_test.cc
+++ b/quic/core/quic_crypto_server_stream_test.cc
@@ -21,6 +21,7 @@
#include "net/third_party/quiche/src/quic/core/quic_packets.h"
#include "net/third_party/quiche/src/quic/core/quic_session.h"
#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.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_ptr_util.h"
@@ -90,6 +91,12 @@
server_session_.reset(server_session);
EXPECT_CALL(*server_session_->helper(), CanAcceptClientHello(_, _, _, _, _))
.Times(testing::AnyNumber());
+ EXPECT_CALL(*server_session_, SelectAlpn(_))
+ .WillRepeatedly([this](const std::vector<QuicStringPiece>& alpns) {
+ return std::find(
+ alpns.cbegin(), alpns.cend(),
+ AlpnForVersion(server_session_->connection()->version()));
+ });
crypto_test_utils::SetupCryptoServerConfigForTest(
server_connection_->clock(), server_connection_->random_generator(),
&server_crypto_config_);
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index 758106f..1805387 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -596,7 +596,7 @@
return false;
}
- bool IsHandshakeConfirmed() { return is_handshake_confirmed_; }
+ bool IsHandshakeConfirmed() const { return is_handshake_confirmed_; }
private:
friend class test::QuicSessionPeer;
diff --git a/quic/quic_transport/README.md b/quic/quic_transport/README.md
new file mode 100644
index 0000000..e2f0849
--- /dev/null
+++ b/quic/quic_transport/README.md
@@ -0,0 +1,7 @@
+# QuicTransport
+
+The files in this directory implement QuicTransport protocol as described in
+<https://tools.ietf.org/html/draft-vvv-webtransport-quic>.
+
+Design doc:
+https://docs.google.com/document/d/1UgviRBnZkMUq4OKcsAJvIQFX6UCXeCbOtX_wMgwD_es/edit#
diff --git a/quic/quic_transport/quic_transport_client_session.cc b/quic/quic_transport/quic_transport_client_session.cc
new file mode 100644
index 0000000..35d7a82
--- /dev/null
+++ b/quic/quic_transport/quic_transport_client_session.cc
@@ -0,0 +1,111 @@
+// Copyright (c) 2019 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/quic_transport/quic_transport_client_session.h"
+
+#include <memory>
+
+#include "url/gurl.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.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"
+
+namespace quic {
+
+const char* kQuicTransportAlpn = "wq-draft01";
+
+namespace {
+// ProofHandler is primarily used by QUIC crypto to persist QUIC server configs
+// and perform some of related debug logging. QuicTransport does not support
+// QUIC crypto, so those methods are not called.
+class DummyProofHandler : public QuicCryptoClientStream::ProofHandler {
+ public:
+ void OnProofValid(
+ const QuicCryptoClientConfig::CachedState& /*cached*/) override {}
+ void OnProofVerifyDetailsAvailable(
+ const ProofVerifyDetails& /*verify_details*/) override {}
+};
+} // namespace
+
+QuicTransportClientSession::QuicTransportClientSession(
+ QuicConnection* connection,
+ Visitor* owner,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicServerId& server_id,
+ QuicCryptoClientConfig* crypto_config,
+ url::Origin origin)
+ : QuicSession(connection,
+ owner,
+ config,
+ supported_versions,
+ /*num_expected_unidirectional_static_streams*/ 0),
+ origin_(origin) {
+ for (const ParsedQuicVersion& version : supported_versions) {
+ QUIC_BUG_IF(version.handshake_protocol != PROTOCOL_TLS1_3)
+ << "QuicTransport requires TLS 1.3 handshake";
+ }
+ // ProofHandler API is not used by TLS 1.3.
+ static DummyProofHandler* proof_handler = new DummyProofHandler();
+ crypto_stream_ = std::make_unique<QuicCryptoClientStream>(
+ server_id, this, crypto_config->proof_verifier()->CreateDefaultContext(),
+ crypto_config, proof_handler);
+}
+
+void QuicTransportClientSession::OnCryptoHandshakeEvent(
+ CryptoHandshakeEvent event) {
+ QuicSession::OnCryptoHandshakeEvent(event);
+ if (event != HANDSHAKE_CONFIRMED) {
+ return;
+ }
+
+ auto it = config()->received_custom_transport_parameters().find(
+ WebAcceptedOriginsParameter());
+ if (it == config()->received_custom_transport_parameters().end()) {
+ connection()->CloseConnection(
+ QUIC_HANDSHAKE_FAILED,
+ "QuicTransport requires web_accepted_origins transport parameter",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+
+ QUIC_DLOG(INFO) << "QuicTransport using origin: " << origin_.Serialize();
+ QUIC_DLOG(INFO) << "QuicTransport origins offered: " << it->second;
+
+ if (CheckOrigin(it->second)) {
+ is_origin_valid_ = true;
+ } else {
+ QUIC_DLOG(ERROR) << "Origin check failed for " << origin_
+ << ", allowed origin list: " << it->second;
+ connection()->CloseConnection(
+ QUIC_HANDSHAKE_FAILED, "QuicTransport origin check failed",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ }
+}
+
+bool QuicTransportClientSession::CheckOrigin(
+ QuicStringPiece raw_accepted_origins) {
+ if (raw_accepted_origins == "*") {
+ return true;
+ }
+
+ std::vector<QuicStringPiece> accepted_origins =
+ QuicTextUtils::Split(raw_accepted_origins, ',');
+ for (QuicStringPiece raw_origin : accepted_origins) {
+ url::Origin accepted_origin =
+ url::Origin::Create(GURL(std::string(raw_origin)));
+ QUIC_DVLOG(1) << "QuicTransport offered origin normalized: "
+ << accepted_origin.Serialize();
+ if (accepted_origin.IsSameOriginWith(origin_)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace quic
diff --git a/quic/quic_transport/quic_transport_client_session.h b/quic/quic_transport/quic_transport_client_session.h
new file mode 100644
index 0000000..7e1cf73
--- /dev/null
+++ b/quic/quic_transport/quic_transport_client_session.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2019 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_QUIC_TRANSPORT_QUIC_TRANSPORT_SESSION_H_
+#define QUICHE_QUIC_QUIC_TRANSPORT_QUIC_TRANSPORT_SESSION_H_
+
+#include <cstdint>
+#include <memory>
+
+#include "url/origin.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// The web_accepted_origins transport parameter ID.
+constexpr TransportParameters::TransportParameterId
+WebAcceptedOriginsParameter() {
+ return static_cast<TransportParameters::TransportParameterId>(0xffc8);
+}
+
+// The ALPN used by QuicTransport.
+QUIC_EXPORT extern const char* kQuicTransportAlpn;
+
+// A client session for the QuicTransport protocol.
+class QUIC_EXPORT QuicTransportClientSession : public QuicSession {
+ public:
+ QuicTransportClientSession(QuicConnection* connection,
+ Visitor* owner,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicServerId& server_id,
+ QuicCryptoClientConfig* crypto_config,
+ url::Origin origin);
+
+ std::vector<std::string> GetAlpnsToOffer() const override {
+ return std::vector<std::string>({kQuicTransportAlpn});
+ }
+
+ void CryptoConnect() { crypto_stream_->CryptoConnect(); }
+
+ bool ShouldKeepConnectionAlive() const override { return true; }
+
+ QuicCryptoStream* GetMutableCryptoStream() override {
+ return crypto_stream_.get();
+ }
+ const QuicCryptoStream* GetCryptoStream() const override {
+ return crypto_stream_.get();
+ }
+
+ bool IsSessionReady() const {
+ return IsCryptoHandshakeConfirmed() && is_origin_valid_;
+ }
+
+ void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override;
+
+ protected:
+ // Accepts the list of accepted origins in a format specified in
+ // <https://tools.ietf.org/html/draft-vvv-webtransport-quic-00#section-3.2>,
+ // and verifies that at least one of them matches |origin_|.
+ bool CheckOrigin(QuicStringPiece raw_accepted_origins);
+
+ std::unique_ptr<QuicCryptoClientStream> crypto_stream_;
+ url::Origin origin_;
+ bool is_origin_valid_ = false;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUIC_TRANSPORT_QUIC_TRANSPORT_SESSION_H_
diff --git a/quic/quic_transport/quic_transport_client_session_test.cc b/quic/quic_transport/quic_transport_client_session_test.cc
new file mode 100644
index 0000000..c500e2d
--- /dev/null
+++ b/quic/quic_transport/quic_transport_client_session_test.cc
@@ -0,0 +1,157 @@
+// Copyright (c) 2019 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/quic_transport/quic_transport_client_session.h"
+
+#include <memory>
+
+#include "url/gurl.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.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/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+using testing::_;
+using testing::ElementsAre;
+
+const char* kTestOrigin = "https://test-origin.test";
+const char* kTestOriginInsecure = "http://test-origin.test";
+url::Origin GetTestOrigin() {
+ GURL origin_url(kTestOrigin);
+ return url::Origin::Create(origin_url);
+}
+
+ParsedQuicVersionVector GetVersions() {
+ return {ParsedQuicVersion{PROTOCOL_TLS1_3, QUIC_VERSION_99}};
+}
+
+class TestClientSession : public QuicTransportClientSession {
+ public:
+ using QuicTransportClientSession::QuicTransportClientSession;
+
+ class Stream : public QuicStream {
+ public:
+ using QuicStream::QuicStream;
+ void OnDataAvailable() override {}
+ };
+
+ QuicStream* CreateIncomingStream(QuicStreamId id) override {
+ auto stream = std::make_unique<Stream>(
+ id, this, /*is_static=*/false,
+ QuicUtils::GetStreamType(id, connection()->perspective(),
+ /*peer_initiated=*/true));
+ QuicStream* result = stream.get();
+ ActivateStream(std::move(stream));
+ return result;
+ }
+
+ QuicStream* CreateIncomingStream(PendingStream* /*pending*/) override {
+ QUIC_NOTREACHED();
+ return nullptr;
+ }
+};
+
+class QuicTransportClientSessionTest : public QuicTest {
+ protected:
+ QuicTransportClientSessionTest()
+ : connection_(&helper_,
+ &alarm_factory_,
+ Perspective::IS_CLIENT,
+ GetVersions()),
+ server_id_("test.example.com", 443),
+ crypto_config_(crypto_test_utils::ProofVerifierForTesting()) {
+ SetQuicReloadableFlag(quic_supports_tls_handshake, true);
+ session_ = std::make_unique<TestClientSession>(
+ &connection_, nullptr, DefaultQuicConfig(), GetVersions(), server_id_,
+ &crypto_config_, GetTestOrigin());
+ session_->Initialize();
+ crypto_stream_ = static_cast<QuicCryptoClientStream*>(
+ session_->GetMutableCryptoStream());
+ }
+
+ void ConnectWithOriginList(std::string accepted_origins) {
+ session_->CryptoConnect();
+ QuicConfig server_config = DefaultQuicConfig();
+ server_config
+ .custom_transport_parameters_to_send()[WebAcceptedOriginsParameter()] =
+ accepted_origins;
+ crypto_test_utils::HandshakeWithFakeServer(
+ &server_config, &helper_, &alarm_factory_, &connection_, crypto_stream_,
+ kQuicTransportAlpn);
+ }
+
+ MockAlarmFactory alarm_factory_;
+ MockQuicConnectionHelper helper_;
+
+ PacketSavingConnection connection_;
+ QuicServerId server_id_;
+ QuicCryptoClientConfig crypto_config_;
+ std::unique_ptr<TestClientSession> session_;
+ QuicCryptoClientStream* crypto_stream_;
+};
+
+TEST_F(QuicTransportClientSessionTest, HasValidAlpn) {
+ EXPECT_THAT(session_->GetAlpnsToOffer(), ElementsAre(kQuicTransportAlpn));
+}
+
+TEST_F(QuicTransportClientSessionTest, SuccessfulConnection) {
+ ConnectWithOriginList(GetTestOrigin().Serialize());
+ EXPECT_TRUE(session_->IsSessionReady());
+}
+
+TEST_F(QuicTransportClientSessionTest, SuccessfulConnectionManyOrigins) {
+ ConnectWithOriginList(
+ QuicStrCat("http://example.org,", kTestOrigin, ",https://example.com"));
+ EXPECT_TRUE(session_->IsSessionReady());
+}
+
+TEST_F(QuicTransportClientSessionTest, SuccessfulConnectionWildcardOrigin) {
+ ConnectWithOriginList("*");
+ EXPECT_TRUE(session_->IsSessionReady());
+}
+
+TEST_F(QuicTransportClientSessionTest, OriginMismatch) {
+ EXPECT_CALL(connection_,
+ CloseConnection(_, "QuicTransport origin check failed", _));
+ ConnectWithOriginList("https://obviously-wrong-website.test");
+ EXPECT_FALSE(session_->IsSessionReady());
+}
+
+TEST_F(QuicTransportClientSessionTest, OriginSchemaMismatch) {
+ EXPECT_CALL(connection_,
+ CloseConnection(_, "QuicTransport origin check failed", _));
+ ConnectWithOriginList(kTestOriginInsecure);
+ EXPECT_FALSE(session_->IsSessionReady());
+}
+
+TEST_F(QuicTransportClientSessionTest, OriginListMissing) {
+ EXPECT_CALL(
+ connection_,
+ CloseConnection(
+ _, "QuicTransport requires web_accepted_origins transport parameter",
+ _));
+ session_->CryptoConnect();
+ QuicConfig server_config = DefaultQuicConfig();
+ crypto_test_utils::HandshakeWithFakeServer(
+ &server_config, &helper_, &alarm_factory_, &connection_, crypto_stream_,
+ kQuicTransportAlpn);
+ EXPECT_FALSE(session_->IsSessionReady());
+}
+
+TEST_F(QuicTransportClientSessionTest, OriginListEmpty) {
+ EXPECT_CALL(connection_,
+ CloseConnection(_, "QuicTransport origin check failed", _));
+ ConnectWithOriginList("");
+ EXPECT_FALSE(session_->IsSessionReady());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/crypto_test_utils.cc b/quic/test_tools/crypto_test_utils.cc
index ce0cb6e..7e9b882 100644
--- a/quic/test_tools/crypto_test_utils.cc
+++ b/quic/test_tools/crypto_test_utils.cc
@@ -45,6 +45,8 @@
namespace {
+using testing::_;
+
// CryptoFramerVisitor is a framer visitor that records handshake messages.
class CryptoFramerVisitor : public CryptoFramerVisitorInterface {
public:
@@ -210,7 +212,8 @@
MockQuicConnectionHelper* helper,
MockAlarmFactory* alarm_factory,
PacketSavingConnection* client_conn,
- QuicCryptoClientStream* client) {
+ QuicCryptoClientStream* client,
+ std::string alpn) {
PacketSavingConnection* server_conn = new PacketSavingConnection(
helper, alarm_factory, Perspective::IS_SERVER,
ParsedVersionOfIndex(client_conn->supported_versions(), 0));
@@ -234,6 +237,10 @@
.Times(testing::AnyNumber());
EXPECT_CALL(*server_conn, OnCanWrite()).Times(testing::AnyNumber());
EXPECT_CALL(*client_conn, OnCanWrite()).Times(testing::AnyNumber());
+ EXPECT_CALL(server_session, SelectAlpn(_))
+ .WillRepeatedly([alpn](const std::vector<QuicStringPiece>& alpns) {
+ return std::find(alpns.cbegin(), alpns.cend(), alpn);
+ });
// The client's handshake must have been started already.
CHECK_NE(0u, client_conn->encrypted_packets_.size());
diff --git a/quic/test_tools/crypto_test_utils.h b/quic/test_tools/crypto_test_utils.h
index 421c0f3d..4cee641 100644
--- a/quic/test_tools/crypto_test_utils.h
+++ b/quic/test_tools/crypto_test_utils.h
@@ -69,7 +69,8 @@
MockQuicConnectionHelper* helper,
MockAlarmFactory* alarm_factory,
PacketSavingConnection* client_conn,
- QuicCryptoClientStream* client);
+ QuicCryptoClientStream* client,
+ std::string alpn);
// returns: the number of client hellos that the client sent.
int HandshakeWithFakeClient(MockQuicConnectionHelper* helper,
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h
index c12587d..4246cfa 100644
--- a/quic/test_tools/quic_test_utils.h
+++ b/quic/test_tools/quic_test_utils.h
@@ -794,6 +794,9 @@
MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(PendingStream* stream));
MOCK_METHOD0(CreateOutgoingBidirectionalStream, QuicSpdyStream*());
MOCK_METHOD0(CreateOutgoingUnidirectionalStream, QuicSpdyStream*());
+ MOCK_CONST_METHOD1(SelectAlpn,
+ std::vector<QuicStringPiece>::const_iterator(
+ const std::vector<QuicStringPiece>&));
QuicCryptoServerStreamBase* CreateQuicCryptoServerStream(
const QuicCryptoServerConfig* crypto_config,
QuicCompressedCertsCache* compressed_certs_cache) override;