|  | // 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 <utility> | 
|  |  | 
|  | #include "absl/strings/string_view.h" | 
|  | #include "net/third_party/quiche/src/quic/core/proto/crypto_server_config_proto.h" | 
|  | #include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h" | 
|  | #include "net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h" | 
|  | #include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" | 
|  | #include "net/third_party/quiche/src/quic/platform/api/quic_port_utils.h" | 
|  | #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" | 
|  | #include "net/third_party/quiche/src/quic/platform/api/quic_test_loopback.h" | 
|  | #include "net/third_party/quiche/src/quic/qbone/platform/icmp_packet.h" | 
|  | #include "net/third_party/quiche/src/quic/qbone/qbone_client_session.h" | 
|  | #include "net/third_party/quiche/src/quic/qbone/qbone_constants.h" | 
|  | #include "net/third_party/quiche/src/quic/qbone/qbone_control_placeholder.pb.h" | 
|  | #include "net/third_party/quiche/src/quic/qbone/qbone_packet_processor_test_tools.h" | 
|  | #include "net/third_party/quiche/src/quic/qbone/qbone_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/mock_clock.h" | 
|  | #include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" | 
|  | #include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h" | 
|  | #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" | 
|  | #include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h" | 
|  |  | 
|  | namespace quic { | 
|  | namespace test { | 
|  | namespace { | 
|  |  | 
|  | using ::testing::_; | 
|  | using ::testing::Contains; | 
|  | using ::testing::ElementsAre; | 
|  | using ::testing::Eq; | 
|  | using ::testing::Invoke; | 
|  | using ::testing::NiceMock; | 
|  | using ::testing::Not; | 
|  |  | 
|  | std::string TestPacketIn(const std::string& body) { | 
|  | return PrependIPv6HeaderForTest(body, 5); | 
|  | } | 
|  |  | 
|  | std::string TestPacketOut(const std::string& body) { | 
|  | return PrependIPv6HeaderForTest(body, 4); | 
|  | } | 
|  |  | 
|  | ParsedQuicVersionVector GetTestParams() { | 
|  | ParsedQuicVersionVector test_versions; | 
|  |  | 
|  | // TODO(b/113130636): Make QBONE work with TLS. | 
|  | for (const auto& version : CurrentSupportedVersionsWithQuicCrypto()) { | 
|  | // QBONE requires MESSAGE frames | 
|  | if (!version.SupportsMessageFrames()) { | 
|  | continue; | 
|  | } | 
|  | test_versions.push_back(version); | 
|  | } | 
|  |  | 
|  | return test_versions; | 
|  | } | 
|  |  | 
|  | // Used by QuicCryptoServerConfig to provide server credentials, passes | 
|  | // everything through to ProofSourceForTesting if success is true, | 
|  | // and fails otherwise. | 
|  | class IndirectionProofSource : public ProofSource { | 
|  | public: | 
|  | explicit IndirectionProofSource(bool success) { | 
|  | if (success) { | 
|  | proof_source_ = crypto_test_utils::ProofSourceForTesting(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // ProofSource override. | 
|  | void GetProof(const QuicSocketAddress& server_address, | 
|  | const QuicSocketAddress& client_address, | 
|  | const std::string& hostname, | 
|  | const std::string& server_config, | 
|  | QuicTransportVersion transport_version, | 
|  | absl::string_view chlo_hash, | 
|  | std::unique_ptr<Callback> callback) override { | 
|  | if (!proof_source_) { | 
|  | QuicReferenceCountedPointer<ProofSource::Chain> chain = | 
|  | GetCertChain(server_address, client_address, hostname); | 
|  | QuicCryptoProof proof; | 
|  | callback->Run(/*ok=*/false, chain, proof, /*details=*/nullptr); | 
|  | return; | 
|  | } | 
|  | proof_source_->GetProof(server_address, client_address, hostname, | 
|  | server_config, transport_version, chlo_hash, | 
|  | std::move(callback)); | 
|  | } | 
|  |  | 
|  | QuicReferenceCountedPointer<Chain> GetCertChain( | 
|  | const QuicSocketAddress& server_address, | 
|  | const QuicSocketAddress& client_address, | 
|  | const std::string& hostname) override { | 
|  | if (!proof_source_) { | 
|  | return QuicReferenceCountedPointer<Chain>(); | 
|  | } | 
|  | return proof_source_->GetCertChain(server_address, client_address, | 
|  | hostname); | 
|  | } | 
|  |  | 
|  | void ComputeTlsSignature( | 
|  | const QuicSocketAddress& server_address, | 
|  | const QuicSocketAddress& client_address, | 
|  | const std::string& hostname, | 
|  | uint16_t signature_algorithm, | 
|  | absl::string_view in, | 
|  | std::unique_ptr<SignatureCallback> callback) override { | 
|  | if (!proof_source_) { | 
|  | callback->Run(/*ok=*/true, "Signature", /*details=*/nullptr); | 
|  | return; | 
|  | } | 
|  | proof_source_->ComputeTlsSignature(server_address, client_address, hostname, | 
|  | signature_algorithm, in, | 
|  | std::move(callback)); | 
|  | } | 
|  |  | 
|  | TicketCrypter* GetTicketCrypter() override { return nullptr; } | 
|  |  | 
|  | private: | 
|  | std::unique_ptr<ProofSource> proof_source_; | 
|  | }; | 
|  |  | 
|  | // Used by QuicCryptoClientConfig to verify server credentials, passes | 
|  | // everything through to ProofVerifierForTesting is success is true, | 
|  | // otherwise returns a canned response of QUIC_FAILURE. | 
|  | class IndirectionProofVerifier : public ProofVerifier { | 
|  | public: | 
|  | explicit IndirectionProofVerifier(bool success) { | 
|  | if (success) { | 
|  | proof_verifier_ = crypto_test_utils::ProofVerifierForTesting(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // ProofVerifier override | 
|  | QuicAsyncStatus VerifyProof( | 
|  | const std::string& hostname, | 
|  | const uint16_t port, | 
|  | const std::string& server_config, | 
|  | QuicTransportVersion transport_version, | 
|  | absl::string_view chlo_hash, | 
|  | const std::vector<std::string>& certs, | 
|  | const std::string& cert_sct, | 
|  | const std::string& signature, | 
|  | const ProofVerifyContext* context, | 
|  | std::string* error_details, | 
|  | std::unique_ptr<ProofVerifyDetails>* verify_details, | 
|  | std::unique_ptr<ProofVerifierCallback> callback) override { | 
|  | if (!proof_verifier_) { | 
|  | return QUIC_FAILURE; | 
|  | } | 
|  | return proof_verifier_->VerifyProof( | 
|  | hostname, port, server_config, transport_version, chlo_hash, certs, | 
|  | cert_sct, signature, context, error_details, verify_details, | 
|  | std::move(callback)); | 
|  | } | 
|  |  | 
|  | QuicAsyncStatus VerifyCertChain( | 
|  | const std::string& hostname, | 
|  | const uint16_t port, | 
|  | const std::vector<std::string>& certs, | 
|  | const std::string& ocsp_response, | 
|  | const std::string& cert_sct, | 
|  | const ProofVerifyContext* context, | 
|  | std::string* error_details, | 
|  | std::unique_ptr<ProofVerifyDetails>* details, | 
|  | uint8_t* out_alert, | 
|  | std::unique_ptr<ProofVerifierCallback> callback) override { | 
|  | if (!proof_verifier_) { | 
|  | return QUIC_FAILURE; | 
|  | } | 
|  | return proof_verifier_->VerifyCertChain( | 
|  | hostname, port, certs, ocsp_response, cert_sct, context, error_details, | 
|  | details, out_alert, std::move(callback)); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<ProofVerifyContext> CreateDefaultContext() override { | 
|  | if (!proof_verifier_) { | 
|  | return nullptr; | 
|  | } | 
|  | return proof_verifier_->CreateDefaultContext(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | std::unique_ptr<ProofVerifier> proof_verifier_; | 
|  | }; | 
|  |  | 
|  | class DataSavingQbonePacketWriter : public QbonePacketWriter { | 
|  | public: | 
|  | void WritePacketToNetwork(const char* packet, size_t size) override { | 
|  | data_.push_back(std::string(packet, size)); | 
|  | } | 
|  |  | 
|  | const std::vector<std::string>& data() { return data_; } | 
|  |  | 
|  | private: | 
|  | std::vector<std::string> data_; | 
|  | }; | 
|  |  | 
|  | template <class T> | 
|  | class DataSavingQboneControlHandler : public QboneControlHandler<T> { | 
|  | public: | 
|  | void OnControlRequest(const T& request) override { data_.push_back(request); } | 
|  |  | 
|  | void OnControlError() override { error_ = true; } | 
|  |  | 
|  | const std::vector<T>& data() { return data_; } | 
|  | bool error() { return error_; } | 
|  |  | 
|  | private: | 
|  | std::vector<T> data_; | 
|  | bool error_ = false; | 
|  | }; | 
|  |  | 
|  | // Single-threaded scheduled task runner based on a MockClock. | 
|  | // | 
|  | // Simulates asynchronous execution on a single thread by holding scheduled | 
|  | // tasks until Run() is called. Performs no synchronization, assumes that | 
|  | // Schedule() and Run() are called on the same thread. | 
|  | class FakeTaskRunner { | 
|  | public: | 
|  | explicit FakeTaskRunner(MockQuicConnectionHelper* helper) | 
|  | : tasks_([](const TaskType& l, const TaskType& r) { | 
|  | // Items at a later time should run after items at an earlier time. | 
|  | // Priority queue comparisons should return true if l appears after r. | 
|  | return l->time() > r->time(); | 
|  | }), | 
|  | helper_(helper) {} | 
|  |  | 
|  | // Runs all tasks in time order.  Executes tasks scheduled at | 
|  | // the same in an arbitrary order. | 
|  | void Run() { | 
|  | while (!tasks_.empty()) { | 
|  | tasks_.top()->Run(); | 
|  | tasks_.pop(); | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  | class InnerTask { | 
|  | public: | 
|  | InnerTask(std::function<void()> task, QuicTime time) | 
|  | : task_(std::move(task)), time_(time) {} | 
|  |  | 
|  | void Cancel() { cancelled_ = true; } | 
|  |  | 
|  | void Run() { | 
|  | if (!cancelled_) { | 
|  | task_(); | 
|  | } | 
|  | } | 
|  |  | 
|  | QuicTime time() const { return time_; } | 
|  |  | 
|  | private: | 
|  | bool cancelled_ = false; | 
|  | std::function<void()> task_; | 
|  | QuicTime time_; | 
|  | }; | 
|  |  | 
|  | public: | 
|  | // Schedules a function to run immediately and advances the time. | 
|  | void Schedule(std::function<void()> task) { | 
|  | tasks_.push(std::shared_ptr<InnerTask>( | 
|  | new InnerTask(std::move(task), helper_->GetClock()->Now()))); | 
|  | helper_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); | 
|  | } | 
|  |  | 
|  | private: | 
|  | using TaskType = std::shared_ptr<InnerTask>; | 
|  | std::priority_queue<TaskType, | 
|  | std::vector<TaskType>, | 
|  | std::function<bool(const TaskType&, const TaskType&)>> | 
|  | tasks_; | 
|  | MockQuicConnectionHelper* helper_; | 
|  | }; | 
|  |  | 
|  | class QboneSessionTest : public QuicTestWithParam<ParsedQuicVersion> { | 
|  | public: | 
|  | QboneSessionTest() | 
|  | : supported_versions_({GetParam()}), | 
|  | runner_(&helper_), | 
|  | compressed_certs_cache_(100) {} | 
|  |  | 
|  | ~QboneSessionTest() override { | 
|  | delete client_connection_; | 
|  | delete server_connection_; | 
|  | } | 
|  |  | 
|  | const MockClock* GetClock() const { | 
|  | return static_cast<const MockClock*>(helper_.GetClock()); | 
|  | } | 
|  |  | 
|  | // The parameters are used to control whether the handshake will success or | 
|  | // not. | 
|  | void CreateClientAndServerSessions(bool client_handshake_success = true, | 
|  | bool server_handshake_success = true, | 
|  | bool send_qbone_alpn = true) { | 
|  | // Quic crashes if packets are sent at time 0, and the clock defaults to 0. | 
|  | helper_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1000)); | 
|  | alarm_factory_ = std::make_unique<QuicEpollAlarmFactory>(&epoll_server_); | 
|  | client_writer_ = std::make_unique<DataSavingQbonePacketWriter>(); | 
|  | server_writer_ = std::make_unique<DataSavingQbonePacketWriter>(); | 
|  | client_handler_ = | 
|  | std::make_unique<DataSavingQboneControlHandler<QboneClientRequest>>(); | 
|  | server_handler_ = | 
|  | std::make_unique<DataSavingQboneControlHandler<QboneServerRequest>>(); | 
|  | QuicSocketAddress server_address(TestLoopback(), | 
|  | QuicPickServerPortForTestsOrDie()); | 
|  | QuicSocketAddress client_address; | 
|  | if (server_address.host().address_family() == IpAddressFamily::IP_V4) { | 
|  | client_address = QuicSocketAddress(QuicIpAddress::Any4(), 0); | 
|  | } else { | 
|  | client_address = QuicSocketAddress(QuicIpAddress::Any6(), 0); | 
|  | } | 
|  |  | 
|  | { | 
|  | client_connection_ = new QuicConnection( | 
|  | TestConnectionId(), client_address, server_address, &helper_, | 
|  | alarm_factory_.get(), new NiceMock<MockPacketWriter>(), true, | 
|  | Perspective::IS_CLIENT, supported_versions_); | 
|  | client_connection_->SetSelfAddress(client_address); | 
|  | QuicConfig config; | 
|  | client_crypto_config_ = std::make_unique<QuicCryptoClientConfig>( | 
|  | std::make_unique<IndirectionProofVerifier>(client_handshake_success)); | 
|  | if (send_qbone_alpn) { | 
|  | client_crypto_config_->set_alpn("qbone"); | 
|  | } | 
|  | client_peer_ = std::make_unique<QboneClientSession>( | 
|  | client_connection_, client_crypto_config_.get(), | 
|  | /*owner=*/nullptr, config, supported_versions_, | 
|  | QuicServerId("test.example.com", 1234, false), client_writer_.get(), | 
|  | client_handler_.get()); | 
|  | } | 
|  |  | 
|  | { | 
|  | server_connection_ = new QuicConnection( | 
|  | TestConnectionId(), server_address, client_address, &helper_, | 
|  | alarm_factory_.get(), new NiceMock<MockPacketWriter>(), true, | 
|  | Perspective::IS_SERVER, supported_versions_); | 
|  | server_connection_->SetSelfAddress(server_address); | 
|  | QuicConfig config; | 
|  | server_crypto_config_ = std::make_unique<QuicCryptoServerConfig>( | 
|  | QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance(), | 
|  | std::make_unique<IndirectionProofSource>(server_handshake_success), | 
|  | KeyExchangeSource::Default()); | 
|  | QuicCryptoServerConfig::ConfigOptions options; | 
|  | QuicServerConfigProtobuf primary_config = | 
|  | server_crypto_config_->GenerateConfig(QuicRandom::GetInstance(), | 
|  | GetClock(), options); | 
|  | std::unique_ptr<CryptoHandshakeMessage> message( | 
|  | server_crypto_config_->AddConfig(primary_config, | 
|  | GetClock()->WallNow())); | 
|  |  | 
|  | server_peer_ = std::make_unique<QboneServerSession>( | 
|  | supported_versions_, server_connection_, nullptr, config, | 
|  | server_crypto_config_.get(), &compressed_certs_cache_, | 
|  | server_writer_.get(), TestLoopback6(), TestLoopback6(), 64, | 
|  | server_handler_.get()); | 
|  | } | 
|  |  | 
|  | // Hook everything up! | 
|  | MockPacketWriter* client_writer = static_cast<MockPacketWriter*>( | 
|  | QuicConnectionPeer::GetWriter(client_peer_->connection())); | 
|  | ON_CALL(*client_writer, WritePacket(_, _, _, _, _)) | 
|  | .WillByDefault(Invoke([this](const char* buffer, size_t buf_len, | 
|  | const QuicIpAddress& self_address, | 
|  | const QuicSocketAddress& peer_address, | 
|  | PerPacketOptions* options) { | 
|  | char* copy = new char[1024 * 1024]; | 
|  | memcpy(copy, buffer, buf_len); | 
|  | runner_.Schedule([this, copy, buf_len] { | 
|  | QuicReceivedPacket packet(copy, buf_len, GetClock()->Now()); | 
|  | server_peer_->ProcessUdpPacket(server_connection_->self_address(), | 
|  | client_connection_->self_address(), | 
|  | packet); | 
|  | delete[] copy; | 
|  | }); | 
|  | return WriteResult(WRITE_STATUS_OK, buf_len); | 
|  | })); | 
|  | MockPacketWriter* server_writer = static_cast<MockPacketWriter*>( | 
|  | QuicConnectionPeer::GetWriter(server_peer_->connection())); | 
|  | ON_CALL(*server_writer, WritePacket(_, _, _, _, _)) | 
|  | .WillByDefault(Invoke([this](const char* buffer, size_t buf_len, | 
|  | const QuicIpAddress& self_address, | 
|  | const QuicSocketAddress& peer_address, | 
|  | PerPacketOptions* options) { | 
|  | char* copy = new char[1024 * 1024]; | 
|  | memcpy(copy, buffer, buf_len); | 
|  | runner_.Schedule([this, copy, buf_len] { | 
|  | QuicReceivedPacket packet(copy, buf_len, GetClock()->Now()); | 
|  | client_peer_->ProcessUdpPacket(client_connection_->self_address(), | 
|  | server_connection_->self_address(), | 
|  | packet); | 
|  | delete[] copy; | 
|  | }); | 
|  | return WriteResult(WRITE_STATUS_OK, buf_len); | 
|  | })); | 
|  | } | 
|  |  | 
|  | void StartHandshake() { | 
|  | server_peer_->Initialize(); | 
|  | client_peer_->Initialize(); | 
|  | runner_.Run(); | 
|  | } | 
|  |  | 
|  | void ExpectICMPTooBigResponse(const std::vector<std::string>& written_packets, | 
|  | const int mtu, | 
|  | const std::string& packet) { | 
|  | auto* header = reinterpret_cast<const ip6_hdr*>(packet.data()); | 
|  | icmp6_hdr icmp_header{}; | 
|  | icmp_header.icmp6_type = ICMP6_PACKET_TOO_BIG; | 
|  | icmp_header.icmp6_mtu = mtu; | 
|  |  | 
|  | std::string expected; | 
|  | CreateIcmpPacket(header->ip6_dst, header->ip6_src, icmp_header, packet, | 
|  | [&expected](absl::string_view icmp_packet) { | 
|  | expected = std::string(icmp_packet); | 
|  | }); | 
|  |  | 
|  | EXPECT_THAT(written_packets, Contains(expected)); | 
|  | } | 
|  |  | 
|  | // Test handshake establishment and sending/receiving of data for two | 
|  | // directions. | 
|  | void TestStreamConnection(bool use_messages) { | 
|  | ASSERT_TRUE(server_peer_->OneRttKeysAvailable()); | 
|  | ASSERT_TRUE(client_peer_->OneRttKeysAvailable()); | 
|  | ASSERT_TRUE(server_peer_->IsEncryptionEstablished()); | 
|  | ASSERT_TRUE(client_peer_->IsEncryptionEstablished()); | 
|  |  | 
|  | // Create an outgoing stream from the client and say hello. | 
|  | QUIC_LOG(INFO) << "Sending client -> server"; | 
|  | client_peer_->ProcessPacketFromNetwork(TestPacketIn("hello")); | 
|  | client_peer_->ProcessPacketFromNetwork(TestPacketIn("world")); | 
|  | runner_.Run(); | 
|  | // The server should see the data, the client hasn't received | 
|  | // anything yet. | 
|  | EXPECT_THAT(server_writer_->data(), | 
|  | ElementsAre(TestPacketOut("hello"), TestPacketOut("world"))); | 
|  | EXPECT_TRUE(client_writer_->data().empty()); | 
|  | EXPECT_EQ(0u, server_peer_->GetNumActiveStreams()); | 
|  | EXPECT_EQ(0u, client_peer_->GetNumActiveStreams()); | 
|  |  | 
|  | // Let's pretend some service responds. | 
|  | QUIC_LOG(INFO) << "Sending server -> client"; | 
|  | server_peer_->ProcessPacketFromNetwork(TestPacketIn("Hello Again")); | 
|  | server_peer_->ProcessPacketFromNetwork(TestPacketIn("Again")); | 
|  | runner_.Run(); | 
|  | EXPECT_THAT(server_writer_->data(), | 
|  | ElementsAre(TestPacketOut("hello"), TestPacketOut("world"))); | 
|  | EXPECT_THAT( | 
|  | client_writer_->data(), | 
|  | ElementsAre(TestPacketOut("Hello Again"), TestPacketOut("Again"))); | 
|  | EXPECT_EQ(0u, server_peer_->GetNumActiveStreams()); | 
|  | EXPECT_EQ(0u, client_peer_->GetNumActiveStreams()); | 
|  |  | 
|  | // Try to send long payloads that are larger than the QUIC MTU but | 
|  | // smaller than the QBONE max size. | 
|  | // This should trigger the non-ephemeral stream code path. | 
|  | std::string long_data( | 
|  | QboneConstants::kMaxQbonePacketBytes - sizeof(ip6_hdr) - 1, 'A'); | 
|  | QUIC_LOG(INFO) << "Sending server -> client long data"; | 
|  | server_peer_->ProcessPacketFromNetwork(TestPacketIn(long_data)); | 
|  | runner_.Run(); | 
|  | if (use_messages) { | 
|  | ExpectICMPTooBigResponse( | 
|  | server_writer_->data(), | 
|  | server_peer_->connection()->GetGuaranteedLargestMessagePayload(), | 
|  | TestPacketOut(long_data)); | 
|  | } else { | 
|  | EXPECT_THAT(client_writer_->data(), Contains(TestPacketOut(long_data))); | 
|  | } | 
|  | EXPECT_THAT(server_writer_->data(), | 
|  | Not(Contains(TestPacketOut(long_data)))); | 
|  | EXPECT_EQ(0u, server_peer_->GetNumActiveStreams()); | 
|  | EXPECT_EQ(0u, client_peer_->GetNumActiveStreams()); | 
|  |  | 
|  | QUIC_LOG(INFO) << "Sending client -> server long data"; | 
|  | client_peer_->ProcessPacketFromNetwork(TestPacketIn(long_data)); | 
|  | runner_.Run(); | 
|  | if (use_messages) { | 
|  | ExpectICMPTooBigResponse( | 
|  | client_writer_->data(), | 
|  | client_peer_->connection()->GetGuaranteedLargestMessagePayload(), | 
|  | TestPacketIn(long_data)); | 
|  | } else { | 
|  | EXPECT_THAT(server_writer_->data(), Contains(TestPacketOut(long_data))); | 
|  | } | 
|  | EXPECT_FALSE(client_peer_->EarlyDataAccepted()); | 
|  | EXPECT_FALSE(client_peer_->ReceivedInchoateReject()); | 
|  | EXPECT_THAT(client_peer_->GetNumReceivedServerConfigUpdates(), Eq(0)); | 
|  |  | 
|  | if (!use_messages) { | 
|  | EXPECT_THAT(client_peer_->GetNumStreamedPackets(), Eq(1)); | 
|  | EXPECT_THAT(server_peer_->GetNumStreamedPackets(), Eq(1)); | 
|  | } | 
|  |  | 
|  | if (use_messages) { | 
|  | EXPECT_THAT(client_peer_->GetNumEphemeralPackets(), Eq(0)); | 
|  | EXPECT_THAT(server_peer_->GetNumEphemeralPackets(), Eq(0)); | 
|  | EXPECT_THAT(client_peer_->GetNumMessagePackets(), Eq(2)); | 
|  | EXPECT_THAT(server_peer_->GetNumMessagePackets(), Eq(2)); | 
|  | } else { | 
|  | EXPECT_THAT(client_peer_->GetNumEphemeralPackets(), Eq(2)); | 
|  | EXPECT_THAT(server_peer_->GetNumEphemeralPackets(), Eq(2)); | 
|  | EXPECT_THAT(client_peer_->GetNumMessagePackets(), Eq(0)); | 
|  | EXPECT_THAT(server_peer_->GetNumMessagePackets(), Eq(0)); | 
|  | } | 
|  |  | 
|  | // All streams are ephemeral and should be gone. | 
|  | EXPECT_EQ(0u, server_peer_->GetNumActiveStreams()); | 
|  | EXPECT_EQ(0u, client_peer_->GetNumActiveStreams()); | 
|  | } | 
|  |  | 
|  | // Test that client and server are not connected after handshake failure. | 
|  | void TestDisconnectAfterFailedHandshake() { | 
|  | EXPECT_FALSE(client_peer_->IsEncryptionEstablished()); | 
|  | EXPECT_FALSE(client_peer_->OneRttKeysAvailable()); | 
|  |  | 
|  | EXPECT_FALSE(server_peer_->IsEncryptionEstablished()); | 
|  | EXPECT_FALSE(server_peer_->OneRttKeysAvailable()); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | const ParsedQuicVersionVector supported_versions_; | 
|  | QuicEpollServer epoll_server_; | 
|  | std::unique_ptr<QuicAlarmFactory> alarm_factory_; | 
|  | FakeTaskRunner runner_; | 
|  | MockQuicConnectionHelper helper_; | 
|  | QuicConnection* client_connection_; | 
|  | QuicConnection* server_connection_; | 
|  | QuicCompressedCertsCache compressed_certs_cache_; | 
|  |  | 
|  | std::unique_ptr<QuicCryptoClientConfig> client_crypto_config_; | 
|  | std::unique_ptr<QuicCryptoServerConfig> server_crypto_config_; | 
|  | std::unique_ptr<DataSavingQbonePacketWriter> client_writer_; | 
|  | std::unique_ptr<DataSavingQbonePacketWriter> server_writer_; | 
|  | std::unique_ptr<DataSavingQboneControlHandler<QboneClientRequest>> | 
|  | client_handler_; | 
|  | std::unique_ptr<DataSavingQboneControlHandler<QboneServerRequest>> | 
|  | server_handler_; | 
|  |  | 
|  | std::unique_ptr<QboneServerSession> server_peer_; | 
|  | std::unique_ptr<QboneClientSession> client_peer_; | 
|  | }; | 
|  |  | 
|  | INSTANTIATE_TEST_SUITE_P(Tests, | 
|  | QboneSessionTest, | 
|  | ::testing::ValuesIn(GetTestParams()), | 
|  | ::testing::PrintToStringParamName()); | 
|  |  | 
|  | TEST_P(QboneSessionTest, StreamConnection) { | 
|  | CreateClientAndServerSessions(); | 
|  | client_peer_->set_send_packets_as_messages(false); | 
|  | server_peer_->set_send_packets_as_messages(false); | 
|  | StartHandshake(); | 
|  | TestStreamConnection(false); | 
|  | } | 
|  |  | 
|  | TEST_P(QboneSessionTest, Messages) { | 
|  | CreateClientAndServerSessions(); | 
|  | client_peer_->set_send_packets_as_messages(true); | 
|  | server_peer_->set_send_packets_as_messages(true); | 
|  | StartHandshake(); | 
|  | TestStreamConnection(true); | 
|  | } | 
|  |  | 
|  | TEST_P(QboneSessionTest, ClientRejection) { | 
|  | CreateClientAndServerSessions(false /*client_handshake_success*/, | 
|  | true /*server_handshake_success*/, | 
|  | true /*send_qbone_alpn*/); | 
|  | StartHandshake(); | 
|  | TestDisconnectAfterFailedHandshake(); | 
|  | } | 
|  |  | 
|  | TEST_P(QboneSessionTest, BadAlpn) { | 
|  | CreateClientAndServerSessions(true /*client_handshake_success*/, | 
|  | true /*server_handshake_success*/, | 
|  | false /*send_qbone_alpn*/); | 
|  | StartHandshake(); | 
|  | TestDisconnectAfterFailedHandshake(); | 
|  | } | 
|  |  | 
|  | TEST_P(QboneSessionTest, ServerRejection) { | 
|  | CreateClientAndServerSessions(true /*client_handshake_success*/, | 
|  | false /*server_handshake_success*/, | 
|  | true /*send_qbone_alpn*/); | 
|  | StartHandshake(); | 
|  | TestDisconnectAfterFailedHandshake(); | 
|  | } | 
|  |  | 
|  | // Test that data streams are not created before handshake. | 
|  | TEST_P(QboneSessionTest, CannotCreateDataStreamBeforeHandshake) { | 
|  | CreateClientAndServerSessions(); | 
|  | EXPECT_QUIC_BUG(client_peer_->ProcessPacketFromNetwork(TestPacketIn("hello")), | 
|  | "Attempting to send packet before encryption established"); | 
|  | EXPECT_QUIC_BUG(server_peer_->ProcessPacketFromNetwork(TestPacketIn("hello")), | 
|  | "Attempting to send packet before encryption established"); | 
|  | EXPECT_EQ(0u, server_peer_->GetNumActiveStreams()); | 
|  | EXPECT_EQ(0u, client_peer_->GetNumActiveStreams()); | 
|  | } | 
|  |  | 
|  | TEST_P(QboneSessionTest, ControlRequests) { | 
|  | CreateClientAndServerSessions(); | 
|  | StartHandshake(); | 
|  | EXPECT_TRUE(client_handler_->data().empty()); | 
|  | EXPECT_FALSE(client_handler_->error()); | 
|  | EXPECT_TRUE(server_handler_->data().empty()); | 
|  | EXPECT_FALSE(server_handler_->error()); | 
|  |  | 
|  | QboneClientRequest client_request; | 
|  | client_request.SetExtension(client_placeholder, "hello from the server"); | 
|  | EXPECT_TRUE(server_peer_->SendClientRequest(client_request)); | 
|  | runner_.Run(); | 
|  | ASSERT_FALSE(client_handler_->data().empty()); | 
|  | EXPECT_THAT(client_handler_->data()[0].GetExtension(client_placeholder), | 
|  | Eq("hello from the server")); | 
|  | EXPECT_FALSE(client_handler_->error()); | 
|  |  | 
|  | QboneServerRequest server_request; | 
|  | server_request.SetExtension(server_placeholder, "hello from the client"); | 
|  | EXPECT_TRUE(client_peer_->SendServerRequest(server_request)); | 
|  | runner_.Run(); | 
|  | ASSERT_FALSE(server_handler_->data().empty()); | 
|  | EXPECT_THAT(server_handler_->data()[0].GetExtension(server_placeholder), | 
|  | Eq("hello from the client")); | 
|  | EXPECT_FALSE(server_handler_->error()); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace test | 
|  | }  // namespace quic |