| // 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. | |
| // Sets up a dispatcher and sends requests via the QboneClient. | |
| #include "net/third_party/quiche/src/quic/qbone/qbone_client.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_default_packet_writer.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_dispatcher.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_packet_reader.h" | |
| #include "net/third_party/quiche/src/quic/platform/api/quic_mutex.h" | |
| #include "net/third_party/quiche/src/quic/platform/api/quic_port_utils.h" | |
| #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.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/quic/platform/impl/quic_socket_utils.h" | |
| #include "net/third_party/quiche/src/quic/qbone/qbone_constants.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/quic_server_peer.h" | |
| #include "net/third_party/quiche/src/quic/test_tools/server_thread.h" | |
| #include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h" | |
| #include "net/third_party/quiche/src/quic/tools/quic_server.h" | |
| namespace quic { | |
| namespace test { | |
| namespace { | |
| string TestPacketIn(const string& body) { | |
| return PrependIPv6HeaderForTest(body, 5); | |
| } | |
| string TestPacketOut(const string& body) { | |
| return PrependIPv6HeaderForTest(body, 4); | |
| } | |
| class DataSavingQbonePacketWriter : public QbonePacketWriter { | |
| public: | |
| void WritePacketToNetwork(const char* packet, size_t size) override { | |
| QuicWriterMutexLock lock(&mu_); | |
| data_.push_back(string(packet, size)); | |
| } | |
| std::vector<string> data() { | |
| QuicWriterMutexLock lock(&mu_); | |
| return data_; | |
| } | |
| private: | |
| QuicMutex mu_; | |
| std::vector<string> data_; | |
| }; | |
| // A subclass of a qbone session that will own the connection passed in. | |
| class ConnectionOwningQboneServerSession : public QboneServerSession { | |
| public: | |
| ConnectionOwningQboneServerSession( | |
| const ParsedQuicVersionVector& supported_versions, | |
| QuicConnection* connection, | |
| Visitor* owner, | |
| const QuicConfig& config, | |
| const QuicCryptoServerConfig* quic_crypto_server_config, | |
| QuicCompressedCertsCache* compressed_certs_cache, | |
| QbonePacketWriter* writer) | |
| : QboneServerSession(supported_versions, | |
| connection, | |
| owner, | |
| config, | |
| quic_crypto_server_config, | |
| compressed_certs_cache, | |
| writer, | |
| TestLoopback6(), | |
| TestLoopback6(), | |
| 64, | |
| nullptr), | |
| connection_(connection) {} | |
| private: | |
| // Note that we don't expect the QboneServerSession or any of its parent | |
| // classes to do anything with the connection_ in their destructors. | |
| std::unique_ptr<QuicConnection> connection_; | |
| }; | |
| class QuicQboneDispatcher : public QuicDispatcher { | |
| public: | |
| QuicQboneDispatcher( | |
| const QuicConfig* config, | |
| const QuicCryptoServerConfig* crypto_config, | |
| QuicVersionManager* version_manager, | |
| std::unique_ptr<QuicConnectionHelperInterface> helper, | |
| std::unique_ptr<QuicCryptoServerStream::Helper> session_helper, | |
| std::unique_ptr<QuicAlarmFactory> alarm_factory, | |
| QbonePacketWriter* writer) | |
| : QuicDispatcher(config, | |
| crypto_config, | |
| version_manager, | |
| std::move(helper), | |
| std::move(session_helper), | |
| std::move(alarm_factory), | |
| kQuicDefaultConnectionIdLength), | |
| writer_(writer) {} | |
| QuicSession* CreateQuicSession( | |
| QuicConnectionId id, | |
| const QuicSocketAddress& client, | |
| QuicStringPiece alpn, | |
| const quic::ParsedQuicVersion& version) override { | |
| CHECK_EQ(alpn, "qbone"); | |
| QuicConnection* connection = | |
| new QuicConnection(id, client, helper(), alarm_factory(), writer(), | |
| /* owns_writer= */ false, Perspective::IS_SERVER, | |
| ParsedQuicVersionVector{version}); | |
| // The connection owning wrapper owns the connection created. | |
| QboneServerSession* session = new ConnectionOwningQboneServerSession( | |
| GetSupportedVersions(), connection, this, config(), crypto_config(), | |
| compressed_certs_cache(), writer_); | |
| session->Initialize(); | |
| return session; | |
| } | |
| QuicConnectionId GenerateNewServerConnectionId( | |
| ParsedQuicVersion version, | |
| QuicConnectionId connection_id) const override { | |
| char connection_id_bytes[kQuicDefaultConnectionIdLength] = {}; | |
| return QuicConnectionId(connection_id_bytes, sizeof(connection_id_bytes)); | |
| } | |
| private: | |
| QbonePacketWriter* writer_; | |
| }; | |
| class QboneTestServer : public QuicServer { | |
| public: | |
| explicit QboneTestServer(std::unique_ptr<ProofSource> proof_source) | |
| : QuicServer(std::move(proof_source), &response_cache_) {} | |
| QuicDispatcher* CreateQuicDispatcher() override { | |
| QuicEpollAlarmFactory alarm_factory(epoll_server()); | |
| return new QuicQboneDispatcher( | |
| &config(), &crypto_config(), version_manager(), | |
| std::unique_ptr<QuicEpollConnectionHelper>( | |
| new QuicEpollConnectionHelper(epoll_server(), | |
| QuicAllocator::BUFFER_POOL)), | |
| std::unique_ptr<QuicCryptoServerStream::Helper>( | |
| new QboneCryptoServerStreamHelper()), | |
| std::unique_ptr<QuicEpollAlarmFactory>( | |
| new QuicEpollAlarmFactory(epoll_server())), | |
| &writer_); | |
| } | |
| std::vector<string> data() { return writer_.data(); } | |
| void WaitForDataSize(int n) { | |
| while (data().size() != n) { | |
| } | |
| } | |
| private: | |
| quic::QuicMemoryCacheBackend response_cache_; | |
| DataSavingQbonePacketWriter writer_; | |
| }; | |
| class QboneTestClient : public QboneClient { | |
| public: | |
| QboneTestClient(QuicSocketAddress server_address, | |
| const QuicServerId& server_id, | |
| const ParsedQuicVersionVector& supported_versions, | |
| QuicEpollServer* epoll_server, | |
| std::unique_ptr<ProofVerifier> proof_verifier) | |
| : QboneClient(server_address, | |
| server_id, | |
| supported_versions, | |
| /*session_owner=*/nullptr, | |
| QuicConfig(), | |
| epoll_server, | |
| std::move(proof_verifier), | |
| &qbone_writer_, | |
| nullptr) {} | |
| ~QboneTestClient() override {} | |
| void SendData(const string& data) { | |
| qbone_session()->ProcessPacketFromNetwork(data); | |
| } | |
| void WaitForWriteToFlush() { | |
| while (connected() && session()->HasDataToWrite()) { | |
| WaitForEvents(); | |
| } | |
| } | |
| void WaitForDataSize(int n) { | |
| while (data().size() != n) { | |
| WaitForEvents(); | |
| } | |
| } | |
| std::vector<string> data() { return qbone_writer_.data(); } | |
| private: | |
| DataSavingQbonePacketWriter qbone_writer_; | |
| }; | |
| TEST(QboneClientTest, SendDataFromClient) { | |
| SetQuicReloadableFlag(quic_use_parse_public_header, true); | |
| auto server = new QboneTestServer(crypto_test_utils::ProofSourceForTesting()); | |
| QuicSocketAddress server_address(TestLoopback(), | |
| QuicPickServerPortForTestsOrDie()); | |
| ServerThread server_thread(server, server_address); | |
| server_thread.Initialize(); | |
| server_thread.Start(); | |
| QuicEpollServer epoll_server; | |
| QboneTestClient client( | |
| server_address, | |
| QuicServerId("test.example.com", server_address.port(), false), | |
| AllSupportedVersions(), &epoll_server, | |
| crypto_test_utils::ProofVerifierForTesting()); | |
| ASSERT_TRUE(client.Initialize()); | |
| ASSERT_TRUE(client.Connect()); | |
| ASSERT_TRUE(client.WaitForCryptoHandshakeConfirmed()); | |
| client.SendData(TestPacketIn("hello")); | |
| client.SendData(TestPacketIn("world")); | |
| client.WaitForWriteToFlush(); | |
| server->WaitForDataSize(2); | |
| EXPECT_THAT(server->data()[0], testing::Eq(TestPacketOut("hello"))); | |
| EXPECT_THAT(server->data()[1], testing::Eq(TestPacketOut("world"))); | |
| auto server_session = | |
| static_cast<QboneServerSession*>(QuicServerPeer::GetDispatcher(server) | |
| ->session_map() | |
| .begin() | |
| ->second.get()); | |
| string long_data(QboneConstants::kMaxQbonePacketBytes - sizeof(ip6_hdr) - 1, | |
| 'A'); | |
| // Pretend the server gets data. | |
| server_thread.Schedule([&server_session, &long_data]() { | |
| server_session->ProcessPacketFromNetwork( | |
| TestPacketIn("Somethingsomething")); | |
| server_session->ProcessPacketFromNetwork(TestPacketIn(long_data)); | |
| server_session->ProcessPacketFromNetwork(TestPacketIn(long_data)); | |
| }); | |
| client.WaitForDataSize(3); | |
| EXPECT_THAT(client.data()[0], | |
| testing::Eq(TestPacketOut("Somethingsomething"))); | |
| EXPECT_THAT(client.data()[1], testing::Eq(TestPacketOut(long_data))); | |
| EXPECT_THAT(client.data()[2], testing::Eq(TestPacketOut(long_data))); | |
| client.Disconnect(); | |
| server_thread.Quit(); | |
| server_thread.Join(); | |
| } | |
| } // namespace | |
| } // namespace test | |
| } // namespace quic |