blob: 2902b19f0e1ecfcb10656a5cd2a39d0bdaaea1d3 [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 "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