| // 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 <memory> | 
 | #include <utility> | 
 |  | 
 | #include "absl/strings/string_view.h" | 
 | #include "quiche/quic/core/io/quic_default_event_loop.h" | 
 | #include "quiche/quic/core/io/quic_event_loop.h" | 
 | #include "quiche/quic/core/proto/crypto_server_config_proto.h" | 
 | #include "quiche/quic/core/quic_alarm_factory.h" | 
 | #include "quiche/quic/core/quic_default_clock.h" | 
 | #include "quiche/quic/platform/api/quic_expect_bug.h" | 
 | #include "quiche/quic/platform/api/quic_test.h" | 
 | #include "quiche/quic/platform/api/quic_test_loopback.h" | 
 | #include "quiche/quic/qbone/platform/icmp_packet.h" | 
 | #include "quiche/quic/qbone/qbone_client_session.h" | 
 | #include "quiche/quic/qbone/qbone_constants.h" | 
 | #include "quiche/quic/qbone/qbone_control_placeholder.pb.h" | 
 | #include "quiche/quic/qbone/qbone_packet_processor_test_tools.h" | 
 | #include "quiche/quic/qbone/qbone_server_session.h" | 
 | #include "quiche/quic/test_tools/crypto_test_utils.h" | 
 | #include "quiche/quic/test_tools/mock_clock.h" | 
 | #include "quiche/quic/test_tools/mock_connection_id_generator.h" | 
 | #include "quiche/quic/test_tools/quic_connection_peer.h" | 
 | #include "quiche/quic/test_tools/quic_session_peer.h" | 
 | #include "quiche/quic/test_tools/quic_test_utils.h" | 
 | #include "quiche/common/quiche_callbacks.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() { | 
 |   SetQuicReloadableFlag(quic_disable_version_q046, false); | 
 |   return CurrentSupportedVersionsWithQuicCrypto(); | 
 | } | 
 |  | 
 | // 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_) { | 
 |       QuicCryptoProof proof; | 
 |       quiche::QuicheReferenceCountedPointer<ProofSource::Chain> chain = | 
 |           GetCertChain(server_address, client_address, hostname, | 
 |                        &proof.cert_matched_sni); | 
 |       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)); | 
 |   } | 
 |  | 
 |   quiche::QuicheReferenceCountedPointer<Chain> GetCertChain( | 
 |       const QuicSocketAddress& server_address, | 
 |       const QuicSocketAddress& client_address, const std::string& hostname, | 
 |       bool* cert_matched_sni) override { | 
 |     if (!proof_source_) { | 
 |       return quiche::QuicheReferenceCountedPointer<Chain>(); | 
 |     } | 
 |     return proof_source_->GetCertChain(server_address, client_address, hostname, | 
 |                                        cert_matched_sni); | 
 |   } | 
 |  | 
 |   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)); | 
 |   } | 
 |  | 
 |   absl::InlinedVector<uint16_t, 8> SupportedTlsSignatureAlgorithms() | 
 |       const override { | 
 |     if (!proof_source_) { | 
 |       return {}; | 
 |     } | 
 |     return proof_source_->SupportedTlsSignatureAlgorithms(); | 
 |   } | 
 |  | 
 |   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_) { | 
 |         std::move(task_)(); | 
 |       } | 
 |     } | 
 |  | 
 |     QuicTime time() const { return time_; } | 
 |  | 
 |    private: | 
 |     bool cancelled_ = false; | 
 |     quiche::SingleUseCallback<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>, | 
 |       quiche::UnretainedCallback<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)); | 
 |     event_loop_ = GetDefaultEventLoop()->Create(QuicDefaultClock::Get()); | 
 |     alarm_factory_ = event_loop_->CreateAlarmFactory(); | 
 |     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(), 0); | 
 |     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_, | 
 |           connection_id_generator_); | 
 |       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_, | 
 |           connection_id_generator_); | 
 |       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* option, | 
 |                                      const QuicPacketWriterParams& params) { | 
 |           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, | 
 |                                      const QuicPacketWriterParams& params) { | 
 |           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_; | 
 |   std::unique_ptr<QuicEventLoop> event_loop_; | 
 |   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_; | 
 |   MockConnectionIdGenerator connection_id_generator_; | 
 | }; | 
 |  | 
 | 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 |