Relocate QUICHE files into quiche/ directory within the quiche repo, and change the relative include paths accordingly.
PiperOrigin-RevId: 440164720
Change-Id: I64d8a975d08888a3a86f6c51908e63d5cd45fa35
diff --git a/quiche/quic/qbone/qbone_session_test.cc b/quiche/quic/qbone/qbone_session_test.cc
new file mode 100644
index 0000000..e774202
--- /dev/null
+++ b/quiche/quic/qbone/qbone_session_test.cc
@@ -0,0 +1,642 @@
+// 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 "quiche/quic/core/proto/crypto_server_config_proto.h"
+#include "quiche/quic/core/quic_alarm_factory.h"
+#include "quiche/quic/core/quic_epoll_alarm_factory.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/quic_connection_peer.h"
+#include "quiche/quic/test_tools/quic_session_peer.h"
+#include "quiche/quic/test_tools/quic_test_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_) {
+ 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_) {
+ 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(), 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_);
+ 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