blob: 002911507d8b9495e8afb93608b6f5a077324aec [file] [log] [blame]
// 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 "quiche/quic/qbone/qbone_client.h"
#include <memory>
#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/quic_alarm_factory.h"
#include "quiche/quic/core/quic_default_clock.h"
#include "quiche/quic/core/quic_default_connection_helper.h"
#include "quiche/quic/core/quic_dispatcher.h"
#include "quiche/quic/platform/api/quic_mutex.h"
#include "quiche/quic/platform/api/quic_socket_address.h"
#include "quiche/quic/platform/api/quic_test.h"
#include "quiche/quic/platform/api/quic_test_loopback.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/quic_connection_peer.h"
#include "quiche/quic/test_tools/quic_dispatcher_peer.h"
#include "quiche/quic/test_tools/quic_server_peer.h"
#include "quiche/quic/test_tools/server_thread.h"
#include "quiche/quic/tools/quic_memory_cache_backend.h"
#include "quiche/quic/tools/quic_server.h"
namespace quic {
namespace test {
namespace {
using ::testing::ElementsAre;
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;
}
std::string TestPacketIn(const std::string& body) {
return PrependIPv6HeaderForTest(body, 5);
}
std::string TestPacketOut(const std::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(std::string(packet, size));
}
std::vector<std::string> data() {
QuicWriterMutexLock lock(&mu_);
return data_;
}
private:
QuicMutex mu_;
std::vector<std::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<QuicCryptoServerStreamBase::Helper> session_helper,
std::unique_ptr<QuicAlarmFactory> alarm_factory,
QbonePacketWriter* writer, ConnectionIdGeneratorInterface& generator)
: QuicDispatcher(config, crypto_config, version_manager,
std::move(helper), std::move(session_helper),
std::move(alarm_factory), kQuicDefaultConnectionIdLength,
generator),
writer_(writer) {}
std::unique_ptr<QuicSession> CreateQuicSession(
QuicConnectionId id, const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address, absl::string_view alpn,
const ParsedQuicVersion& version,
const ParsedClientHello& /*parsed_chlo*/) override {
QUICHE_CHECK_EQ(alpn, "qbone");
QuicConnection* connection = new QuicConnection(
id, self_address, peer_address, helper(), alarm_factory(), writer(),
/* owns_writer= */ false, Perspective::IS_SERVER,
ParsedQuicVersionVector{version}, connection_id_generator());
// The connection owning wrapper owns the connection created.
auto session = std::make_unique<ConnectionOwningQboneServerSession>(
GetSupportedVersions(), connection, this, config(), crypto_config(),
compressed_certs_cache(), writer_);
session->Initialize();
return session;
}
private:
QbonePacketWriter* writer_;
};
class QboneTestServer : public QuicServer {
public:
explicit QboneTestServer(std::unique_ptr<ProofSource> proof_source,
quic::QuicMemoryCacheBackend* response_cache)
: QuicServer(std::move(proof_source), response_cache) {}
QuicDispatcher* CreateQuicDispatcher() override {
return new QuicQboneDispatcher(
&config(), &crypto_config(), version_manager(),
std::make_unique<QuicDefaultConnectionHelper>(),
std::make_unique<QboneCryptoServerStreamHelper>(),
event_loop()->CreateAlarmFactory(), &writer_,
connection_id_generator());
}
std::vector<std::string> data() { return writer_.data(); }
private:
DataSavingQbonePacketWriter writer_;
};
class QboneTestClient : public QboneClient {
public:
QboneTestClient(QuicSocketAddress server_address,
const QuicServerId& server_id,
const ParsedQuicVersionVector& supported_versions,
QuicEventLoop* event_loop,
std::unique_ptr<ProofVerifier> proof_verifier)
: QboneClient(server_address, server_id, supported_versions,
/*session_owner=*/nullptr, QuicConfig(), event_loop,
std::move(proof_verifier), &qbone_writer_, nullptr) {}
~QboneTestClient() override {}
void SendData(const std::string& data) {
qbone_session()->ProcessPacketFromNetwork(data);
}
void WaitForWriteToFlush() {
while (connected() && session()->HasDataToWrite()) {
WaitForEvents();
}
}
// Returns true when the data size is reached or false on timeouts.
bool WaitForDataSize(int n, QuicTime::Delta timeout) {
const QuicClock* clock =
quic::test::QuicConnectionPeer::GetHelper(session()->connection())
->GetClock();
const QuicTime deadline = clock->Now() + timeout;
while (data().size() < n) {
if (clock->Now() > deadline) {
return false;
}
WaitForEvents();
}
return true;
}
std::vector<std::string> data() { return qbone_writer_.data(); }
private:
DataSavingQbonePacketWriter qbone_writer_;
};
class QboneClientTest : public QuicTestWithParam<ParsedQuicVersion> {};
INSTANTIATE_TEST_SUITE_P(Tests, QboneClientTest,
::testing::ValuesIn(GetTestParams()),
::testing::PrintToStringParamName());
TEST_P(QboneClientTest, SendDataFromClient) {
quic::QuicMemoryCacheBackend server_backend;
auto server = std::make_unique<QboneTestServer>(
crypto_test_utils::ProofSourceForTesting(), &server_backend);
QboneTestServer* server_ptr = server.get();
QuicSocketAddress server_address(TestLoopback(), 0);
ServerThread server_thread(std::move(server), server_address);
server_thread.Initialize();
server_address =
QuicSocketAddress(server_address.host(), server_thread.GetPort());
server_thread.Start();
std::unique_ptr<QuicEventLoop> event_loop =
GetDefaultEventLoop()->Create(quic::QuicDefaultClock::Get());
QboneTestClient client(
server_address,
QuicServerId("test.example.com", server_address.port(), false),
ParsedQuicVersionVector{GetParam()}, event_loop.get(),
crypto_test_utils::ProofVerifierForTesting());
ASSERT_TRUE(client.Initialize());
ASSERT_TRUE(client.Connect());
ASSERT_TRUE(client.WaitForOneRttKeysAvailable());
client.SendData(TestPacketIn("hello"));
client.SendData(TestPacketIn("world"));
client.WaitForWriteToFlush();
// Wait until the server has received at least two packets, timeout after 5s.
ASSERT_TRUE(
server_thread.WaitUntil([&] { return server_ptr->data().size() >= 2; },
QuicTime::Delta::FromSeconds(5)));
// Pretend the server gets data.
std::string long_data(1000, 'A');
server_thread.Schedule([server_ptr, &long_data]() {
EXPECT_THAT(server_ptr->data(),
ElementsAre(TestPacketOut("hello"), TestPacketOut("world")));
auto server_session = static_cast<QboneServerSession*>(
QuicDispatcherPeer::GetFirstSessionIfAny(
QuicServerPeer::GetDispatcher(server_ptr)));
server_session->ProcessPacketFromNetwork(
TestPacketIn("Somethingsomething"));
server_session->ProcessPacketFromNetwork(TestPacketIn(long_data));
server_session->ProcessPacketFromNetwork(TestPacketIn(long_data));
});
EXPECT_TRUE(client.WaitForDataSize(3, QuicTime::Delta::FromSeconds(5)));
EXPECT_THAT(client.data(),
ElementsAre(TestPacketOut("Somethingsomething"),
TestPacketOut(long_data), TestPacketOut(long_data)));
client.Disconnect();
server_thread.Quit();
server_thread.Join();
}
} // namespace
} // namespace test
} // namespace quic