| // 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) { |
| 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 |