Add a simple integration test for QuicTransport. gfe-relnote: n/a (not used in production) PiperOrigin-RevId: 276340873 Change-Id: Ie2caa7e6d5389f9170d79f0b2e471c36dbfbcbc2
diff --git a/quic/quic_transport/quic_transport_client_session.h b/quic/quic_transport/quic_transport_client_session.h index a39f112..2e575e1 100644 --- a/quic/quic_transport/quic_transport_client_session.h +++ b/quic/quic_transport/quic_transport_client_session.h
@@ -53,6 +53,10 @@ return crypto_stream_.get(); } + // Returns true once the encryption has been established and the client + // indication has been sent. No application data will be read or written + // before the connection is ready. Once the connection becomes ready, this + // method will never return false. bool IsSessionReady() const override { return ready_; } QuicStream* CreateIncomingStream(QuicStreamId id) override;
diff --git a/quic/quic_transport/quic_transport_integration_test.cc b/quic/quic_transport/quic_transport_integration_test.cc new file mode 100644 index 0000000..f4c6a2a --- /dev/null +++ b/quic/quic_transport/quic_transport_integration_test.cc
@@ -0,0 +1,227 @@ +// 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. + +// An integration test that covers interactions between QuicTransport client and +// server sessions. + +#include <memory> + +#include "url/gurl.h" +#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/crypto/quic_crypto_server_config.h" +#include "net/third_party/quiche/src/quic/core/quic_connection.h" +#include "net/third_party/quiche/src/quic/core/quic_error_codes.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_test.h" +#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.h" +#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.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" +#include "net/third_party/quiche/src/quic/test_tools/simulator/link.h" +#include "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint_base.h" +#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h" +#include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h" + +namespace quic { +namespace test { +namespace { + +using simulator::QuicEndpointBase; +using simulator::Simulator; +using testing::_; +using testing::Return; + +url::Origin GetTestOrigin() { + constexpr char kTestOrigin[] = "https://test-origin.test"; + GURL origin_url(kTestOrigin); + return url::Origin::Create(origin_url); +} + +ParsedQuicVersionVector GetVersions() { + return {ParsedQuicVersion{PROTOCOL_TLS1_3, QUIC_VERSION_99}}; +} + +class QuicTransportEndpointBase : public QuicEndpointBase { + public: + QuicTransportEndpointBase(Simulator* simulator, + const std::string& name, + const std::string& peer_name, + Perspective perspective) + : QuicEndpointBase(simulator, name, peer_name) { + connection_ = std::make_unique<QuicConnection>( + TestConnectionId(0x10), simulator::GetAddressFromName(peer_name), + simulator, simulator->GetAlarmFactory(), &writer_, + /*owns_writer=*/false, perspective, GetVersions()); + connection_->SetSelfAddress(simulator::GetAddressFromName(name)); + + SetQuicReloadableFlag(quic_supports_tls_handshake, true); + } +}; + +class QuicTransportClientEndpoint : public QuicTransportEndpointBase { + public: + QuicTransportClientEndpoint(Simulator* simulator, + const std::string& name, + const std::string& peer_name, + url::Origin origin) + : QuicTransportEndpointBase(simulator, + name, + peer_name, + Perspective::IS_CLIENT), + crypto_config_(crypto_test_utils::ProofVerifierForTesting()), + session_(connection_.get(), + nullptr, + DefaultQuicConfig(), + GetVersions(), + QuicServerId("test.example.com", 443), + &crypto_config_, + origin) { + session_.Initialize(); + } + + QuicTransportClientSession* session() { return &session_; } + + private: + QuicCryptoClientConfig crypto_config_; + QuicTransportClientSession session_; +}; + +class MockServerVisitor : public QuicTransportServerSession::ServerVisitor { + public: + MOCK_METHOD1(CheckOrigin, bool(url::Origin)); +}; + +class QuicTransportServerEndpoint : public QuicTransportEndpointBase { + public: + QuicTransportServerEndpoint(Simulator* simulator, + const std::string& name, + const std::string& peer_name) + : QuicTransportEndpointBase(simulator, + name, + peer_name, + Perspective::IS_SERVER), + crypto_config_(QuicCryptoServerConfig::TESTING, + QuicRandom::GetInstance(), + crypto_test_utils::ProofSourceForTesting(), + KeyExchangeSource::Default()), + compressed_certs_cache_( + QuicCompressedCertsCache::kQuicCompressedCertsCacheSize), + session_(connection_.get(), + nullptr, + DefaultQuicConfig(), + GetVersions(), + &crypto_config_, + &compressed_certs_cache_, + &visitor_) { + session_.Initialize(); + } + + QuicTransportServerSession* session() { return &session_; } + MockServerVisitor* visitor() { return &visitor_; } + + private: + QuicCryptoServerConfig crypto_config_; + QuicCompressedCertsCache compressed_certs_cache_; + QuicTransportServerSession session_; + MockServerVisitor visitor_; +}; + +constexpr QuicBandwidth kClientBandwidth = + QuicBandwidth::FromKBitsPerSecond(10000); +constexpr QuicTime::Delta kClientPropagationDelay = + QuicTime::Delta::FromMilliseconds(2); +constexpr QuicBandwidth kServerBandwidth = + QuicBandwidth::FromKBitsPerSecond(4000); +constexpr QuicTime::Delta kServerPropagationDelay = + QuicTime::Delta::FromMilliseconds(50); +const QuicTime::Delta kTransferTime = + kClientBandwidth.TransferTime(kMaxOutgoingPacketSize) + + kServerBandwidth.TransferTime(kMaxOutgoingPacketSize); +const QuicTime::Delta kRtt = + (kClientPropagationDelay + kServerPropagationDelay + kTransferTime) * 2; +const QuicByteCount kBdp = kRtt * kServerBandwidth; + +constexpr QuicTime::Delta kHandshakeTimeout = QuicTime::Delta::FromSeconds(3); + +class QuicTransportIntegrationTest : public QuicTest { + public: + QuicTransportIntegrationTest() + : switch_(&simulator_, "Switch", 8, 2 * kBdp) {} + + void CreateDefaultEndpoints() { + client_ = std::make_unique<QuicTransportClientEndpoint>( + &simulator_, "Client", "Server", GetTestOrigin()); + server_ = std::make_unique<QuicTransportServerEndpoint>(&simulator_, + "Server", "Client"); + ON_CALL(*server_->visitor(), CheckOrigin(_)).WillByDefault(Return(true)); + } + + void WireUpEndpoints() { + client_link_ = std::make_unique<simulator::SymmetricLink>( + client_.get(), switch_.port(1), kClientBandwidth, + kClientPropagationDelay); + server_link_ = std::make_unique<simulator::SymmetricLink>( + server_.get(), switch_.port(2), kServerBandwidth, + kServerPropagationDelay); + } + + void RunHandshake() { + client_->session()->CryptoConnect(); + bool result = simulator_.RunUntilOrTimeout( + [this]() { + return IsHandshakeDone(client_->session()) && + IsHandshakeDone(server_->session()); + }, + kHandshakeTimeout); + EXPECT_TRUE(result); + } + + protected: + template <class Session> + static bool IsHandshakeDone(const Session* session) { + return session->IsSessionReady() || session->error() != QUIC_NO_ERROR; + } + + Simulator simulator_; + simulator::Switch switch_; + std::unique_ptr<simulator::SymmetricLink> client_link_; + std::unique_ptr<simulator::SymmetricLink> server_link_; + + std::unique_ptr<QuicTransportClientEndpoint> client_; + std::unique_ptr<QuicTransportServerEndpoint> server_; +}; + +TEST_F(QuicTransportIntegrationTest, SuccessfulHandshake) { + CreateDefaultEndpoints(); + WireUpEndpoints(); + RunHandshake(); + EXPECT_TRUE(client_->session()->IsSessionReady()); + EXPECT_TRUE(server_->session()->IsSessionReady()); +} + +TEST_F(QuicTransportIntegrationTest, OriginMismatch) { + CreateDefaultEndpoints(); + WireUpEndpoints(); + EXPECT_CALL(*server_->visitor(), CheckOrigin(_)) + .WillRepeatedly(Return(false)); + RunHandshake(); + // Wait until the client receives CONNECTION_CLOSE. + simulator_.RunUntilOrTimeout( + [this]() { return !client_->session()->connection()->connected(); }, + kHandshakeTimeout); + EXPECT_TRUE(client_->session()->IsSessionReady()); + EXPECT_FALSE(server_->session()->IsSessionReady()); + EXPECT_FALSE(client_->session()->connection()->connected()); + EXPECT_FALSE(server_->session()->connection()->connected()); + EXPECT_EQ(client_->session()->error(), + QUIC_TRANSPORT_INVALID_CLIENT_INDICATION); + EXPECT_EQ(server_->session()->error(), + QUIC_TRANSPORT_INVALID_CLIENT_INDICATION); +} + +} // namespace +} // namespace test +} // namespace quic
diff --git a/quic/quic_transport/quic_transport_server_session.h b/quic/quic_transport/quic_transport_server_session.h index b1a9043..c488775 100644 --- a/quic/quic_transport/quic_transport_server_session.h +++ b/quic/quic_transport/quic_transport_server_session.h
@@ -49,6 +49,10 @@ return crypto_stream_.get(); } + // Returns true once the encryption has been established, the client + // indication has been received and the origin has been verified. No + // application data will be read or written before the connection is ready. + // Once the connection becomes ready, this method will never return false. bool IsSessionReady() const override { return ready_; } QuicStream* CreateIncomingStream(QuicStreamId id) override;