Project import generated by Copybara.
PiperOrigin-RevId: 237361882
Change-Id: I109a68f44db867b20f8c6a7732b0ce657133e52a
diff --git a/quic/test_tools/bad_packet_writer.cc b/quic/test_tools/bad_packet_writer.cc
new file mode 100644
index 0000000..3d00f8e
--- /dev/null
+++ b/quic/test_tools/bad_packet_writer.cc
@@ -0,0 +1,36 @@
+// Copyright 2017 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 "net/third_party/quiche/src/quic/test_tools/bad_packet_writer.h"
+
+namespace quic {
+namespace test {
+
+BadPacketWriter::BadPacketWriter(size_t packet_causing_write_error,
+ int error_code)
+ : packet_causing_write_error_(packet_causing_write_error),
+ error_code_(error_code) {}
+
+BadPacketWriter::~BadPacketWriter() {}
+
+WriteResult BadPacketWriter::WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) {
+ if (error_code_ == 0 || packet_causing_write_error_ > 0) {
+ if (packet_causing_write_error_ > 0) {
+ --packet_causing_write_error_;
+ }
+ return QuicPacketWriterWrapper::WritePacket(buffer, buf_len, self_address,
+ peer_address, options);
+ }
+ // It's time to cause write error.
+ int error_code = error_code_;
+ error_code_ = 0;
+ return WriteResult(WRITE_STATUS_ERROR, error_code);
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/bad_packet_writer.h b/quic/test_tools/bad_packet_writer.h
new file mode 100644
index 0000000..35bf601
--- /dev/null
+++ b/quic/test_tools/bad_packet_writer.h
@@ -0,0 +1,36 @@
+// Copyright 2017 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_BAD_PACKET_WRITER_H_
+#define QUICHE_QUIC_TEST_TOOLS_BAD_PACKET_WRITER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h"
+
+namespace quic {
+
+namespace test {
+// This packet writer allows causing packet write error with specified error
+// code when writing a particular packet.
+class BadPacketWriter : public QuicPacketWriterWrapper {
+ public:
+ BadPacketWriter(size_t packet_causing_write_error, int error_code);
+
+ ~BadPacketWriter() override;
+
+ WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) override;
+
+ private:
+ size_t packet_causing_write_error_;
+ int error_code_;
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_BAD_PACKET_WRITER_H_
diff --git a/quic/test_tools/crypto_test_utils.cc b/quic/test_tools/crypto_test_utils.cc
new file mode 100644
index 0000000..7179288
--- /dev/null
+++ b/quic/test_tools/crypto_test_utils.cc
@@ -0,0 +1,1046 @@
+// Copyright (c) 2012 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 "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+
+#include <memory>
+#include <string>
+
+#include "third_party/boringssl/src/include/openssl/bn.h"
+#include "third_party/boringssl/src/include/openssl/ec.h"
+#include "third_party/boringssl/src/include/openssl/ecdsa.h"
+#include "third_party/boringssl/src/include/openssl/nid.h"
+#include "third_party/boringssl/src/include/openssl/sha.h"
+#include "net/third_party/quiche/src/quic/core/crypto/channel_id.h"
+#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/proto/crypto_server_config.pb.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.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_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/simple_quic_framer.h"
+
+namespace quic {
+namespace test {
+
+TestChannelIDKey::TestChannelIDKey(EVP_PKEY* ecdsa_key)
+ : ecdsa_key_(ecdsa_key) {}
+TestChannelIDKey::~TestChannelIDKey() {}
+
+bool TestChannelIDKey::Sign(QuicStringPiece signed_data,
+ QuicString* out_signature) const {
+ bssl::ScopedEVP_MD_CTX md_ctx;
+ if (EVP_DigestSignInit(md_ctx.get(), nullptr, EVP_sha256(), nullptr,
+ ecdsa_key_.get()) != 1) {
+ return false;
+ }
+
+ EVP_DigestUpdate(md_ctx.get(), ChannelIDVerifier::kContextStr,
+ strlen(ChannelIDVerifier::kContextStr) + 1);
+ EVP_DigestUpdate(md_ctx.get(), ChannelIDVerifier::kClientToServerStr,
+ strlen(ChannelIDVerifier::kClientToServerStr) + 1);
+ EVP_DigestUpdate(md_ctx.get(), signed_data.data(), signed_data.size());
+
+ size_t sig_len;
+ if (!EVP_DigestSignFinal(md_ctx.get(), nullptr, &sig_len)) {
+ return false;
+ }
+
+ std::unique_ptr<uint8_t[]> der_sig(new uint8_t[sig_len]);
+ if (!EVP_DigestSignFinal(md_ctx.get(), der_sig.get(), &sig_len)) {
+ return false;
+ }
+
+ uint8_t* derp = der_sig.get();
+ bssl::UniquePtr<ECDSA_SIG> sig(
+ d2i_ECDSA_SIG(nullptr, const_cast<const uint8_t**>(&derp), sig_len));
+ if (sig.get() == nullptr) {
+ return false;
+ }
+
+ // The signature consists of a pair of 32-byte numbers.
+ static const size_t kSignatureLength = 32 * 2;
+ std::unique_ptr<uint8_t[]> signature(new uint8_t[kSignatureLength]);
+ if (!BN_bn2bin_padded(&signature[0], 32, sig->r) ||
+ !BN_bn2bin_padded(&signature[32], 32, sig->s)) {
+ return false;
+ }
+
+ *out_signature =
+ QuicString(reinterpret_cast<char*>(signature.get()), kSignatureLength);
+
+ return true;
+}
+
+QuicString TestChannelIDKey::SerializeKey() const {
+ // i2d_PublicKey will produce an ANSI X9.62 public key which, for a P-256
+ // key, is 0x04 (meaning uncompressed) followed by the x and y field
+ // elements as 32-byte, big-endian numbers.
+ static const int kExpectedKeyLength = 65;
+
+ int len = i2d_PublicKey(ecdsa_key_.get(), nullptr);
+ if (len != kExpectedKeyLength) {
+ return "";
+ }
+
+ uint8_t buf[kExpectedKeyLength];
+ uint8_t* derp = buf;
+ i2d_PublicKey(ecdsa_key_.get(), &derp);
+
+ return QuicString(reinterpret_cast<char*>(buf + 1), kExpectedKeyLength - 1);
+}
+
+TestChannelIDSource::~TestChannelIDSource() {}
+
+QuicAsyncStatus TestChannelIDSource::GetChannelIDKey(
+ const QuicString& hostname,
+ std::unique_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* /*callback*/) {
+ *channel_id_key = QuicMakeUnique<TestChannelIDKey>(HostnameToKey(hostname));
+ return QUIC_SUCCESS;
+}
+
+// static
+EVP_PKEY* TestChannelIDSource::HostnameToKey(const QuicString& hostname) {
+ // In order to generate a deterministic key for a given hostname the
+ // hostname is hashed with SHA-256 and the resulting digest is treated as a
+ // big-endian number. The most-significant bit is cleared to ensure that
+ // the resulting value is less than the order of the group and then it's
+ // taken as a private key. Given the private key, the public key is
+ // calculated with a group multiplication.
+ SHA256_CTX sha256;
+ SHA256_Init(&sha256);
+ SHA256_Update(&sha256, hostname.data(), hostname.size());
+
+ unsigned char digest[SHA256_DIGEST_LENGTH];
+ SHA256_Final(digest, &sha256);
+
+ // Ensure that the digest is less than the order of the P-256 group by
+ // clearing the most-significant bit.
+ digest[0] &= 0x7f;
+
+ bssl::UniquePtr<BIGNUM> k(BN_new());
+ CHECK(BN_bin2bn(digest, sizeof(digest), k.get()) != nullptr);
+
+ bssl::UniquePtr<EC_GROUP> p256(
+ EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+ CHECK(p256);
+
+ bssl::UniquePtr<EC_KEY> ecdsa_key(EC_KEY_new());
+ CHECK(ecdsa_key && EC_KEY_set_group(ecdsa_key.get(), p256.get()));
+
+ bssl::UniquePtr<EC_POINT> point(EC_POINT_new(p256.get()));
+ CHECK(EC_POINT_mul(p256.get(), point.get(), k.get(), nullptr, nullptr,
+ nullptr));
+
+ EC_KEY_set_private_key(ecdsa_key.get(), k.get());
+ EC_KEY_set_public_key(ecdsa_key.get(), point.get());
+
+ bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
+ // EVP_PKEY_set1_EC_KEY takes a reference so no |release| here.
+ EVP_PKEY_set1_EC_KEY(pkey.get(), ecdsa_key.get());
+
+ return pkey.release();
+}
+
+namespace crypto_test_utils {
+
+namespace {
+
+// CryptoFramerVisitor is a framer visitor that records handshake messages.
+class CryptoFramerVisitor : public CryptoFramerVisitorInterface {
+ public:
+ CryptoFramerVisitor() : error_(false) {}
+
+ void OnError(CryptoFramer* framer) override { error_ = true; }
+
+ void OnHandshakeMessage(const CryptoHandshakeMessage& message) override {
+ messages_.push_back(message);
+ }
+
+ bool error() const { return error_; }
+
+ const std::vector<CryptoHandshakeMessage>& messages() const {
+ return messages_;
+ }
+
+ private:
+ bool error_;
+ std::vector<CryptoHandshakeMessage> messages_;
+};
+
+// HexChar parses |c| as a hex character. If valid, it sets |*value| to the
+// value of the hex character and returns true. Otherwise it returns false.
+bool HexChar(char c, uint8_t* value) {
+ if (c >= '0' && c <= '9') {
+ *value = c - '0';
+ return true;
+ }
+ if (c >= 'a' && c <= 'f') {
+ *value = c - 'a' + 10;
+ return true;
+ }
+ if (c >= 'A' && c <= 'F') {
+ *value = c - 'A' + 10;
+ return true;
+ }
+ return false;
+}
+
+// A ChannelIDSource that works in asynchronous mode unless the |callback|
+// argument to GetChannelIDKey is nullptr.
+class AsyncTestChannelIDSource : public ChannelIDSource, public CallbackSource {
+ public:
+ // Takes ownership of |sync_source|, a synchronous ChannelIDSource.
+ explicit AsyncTestChannelIDSource(ChannelIDSource* sync_source)
+ : sync_source_(sync_source) {}
+ ~AsyncTestChannelIDSource() override {}
+
+ // ChannelIDSource implementation.
+ QuicAsyncStatus GetChannelIDKey(const QuicString& hostname,
+ std::unique_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* callback) override {
+ // Synchronous mode.
+ if (!callback) {
+ return sync_source_->GetChannelIDKey(hostname, channel_id_key, nullptr);
+ }
+
+ // Asynchronous mode.
+ QuicAsyncStatus status =
+ sync_source_->GetChannelIDKey(hostname, &channel_id_key_, nullptr);
+ if (status != QUIC_SUCCESS) {
+ return QUIC_FAILURE;
+ }
+ callback_.reset(callback);
+ return QUIC_PENDING;
+ }
+
+ // CallbackSource implementation.
+ void RunPendingCallbacks() override {
+ if (callback_) {
+ callback_->Run(&channel_id_key_);
+ callback_.reset();
+ }
+ }
+
+ private:
+ std::unique_ptr<ChannelIDSource> sync_source_;
+ std::unique_ptr<ChannelIDSourceCallback> callback_;
+ std::unique_ptr<ChannelIDKey> channel_id_key_;
+};
+
+} // anonymous namespace
+
+FakeServerOptions::FakeServerOptions() {}
+
+FakeServerOptions::~FakeServerOptions() {}
+
+FakeClientOptions::FakeClientOptions()
+ : channel_id_enabled(false), channel_id_source_async(false) {}
+
+FakeClientOptions::~FakeClientOptions() {}
+
+namespace {
+// This class is used by GenerateFullCHLO() to extract SCID and STK from
+// REJ/SREJ and to construct a full CHLO with these fields and given inchoate
+// CHLO.
+class FullChloGenerator {
+ public:
+ FullChloGenerator(
+ QuicCryptoServerConfig* crypto_config,
+ QuicSocketAddress server_addr,
+ QuicSocketAddress client_addr,
+ const QuicClock* clock,
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ CryptoHandshakeMessage* out)
+ : crypto_config_(crypto_config),
+ server_addr_(server_addr),
+ client_addr_(client_addr),
+ clock_(clock),
+ signed_config_(signed_config),
+ compressed_certs_cache_(compressed_certs_cache),
+ out_(out),
+ params_(new QuicCryptoNegotiatedParameters) {}
+
+ class ValidateClientHelloCallback : public ValidateClientHelloResultCallback {
+ public:
+ explicit ValidateClientHelloCallback(FullChloGenerator* generator)
+ : generator_(generator) {}
+ void Run(QuicReferenceCountedPointer<
+ ValidateClientHelloResultCallback::Result> result,
+ std::unique_ptr<ProofSource::Details> /* details */) override {
+ generator_->ValidateClientHelloDone(std::move(result));
+ }
+
+ private:
+ FullChloGenerator* generator_;
+ };
+
+ std::unique_ptr<ValidateClientHelloCallback>
+ GetValidateClientHelloCallback() {
+ return QuicMakeUnique<ValidateClientHelloCallback>(this);
+ }
+
+ private:
+ void ValidateClientHelloDone(
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ result) {
+ result_ = result;
+ crypto_config_->ProcessClientHello(
+ result_, /*reject_only=*/false, TestConnectionId(1), server_addr_,
+ client_addr_, AllSupportedVersions().front(), AllSupportedVersions(),
+ /*use_stateless_rejects=*/true,
+ /*server_designated_connection_id=*/TestConnectionId(2), clock_,
+ QuicRandom::GetInstance(), compressed_certs_cache_, params_,
+ signed_config_, /*total_framing_overhead=*/50, kDefaultMaxPacketSize,
+ GetProcessClientHelloCallback());
+ }
+
+ class ProcessClientHelloCallback : public ProcessClientHelloResultCallback {
+ public:
+ explicit ProcessClientHelloCallback(FullChloGenerator* generator)
+ : generator_(generator) {}
+ void Run(
+ QuicErrorCode error,
+ const QuicString& error_details,
+ std::unique_ptr<CryptoHandshakeMessage> message,
+ std::unique_ptr<DiversificationNonce> diversification_nonce,
+ std::unique_ptr<ProofSource::Details> proof_source_details) override {
+ generator_->ProcessClientHelloDone(std::move(message));
+ }
+
+ private:
+ FullChloGenerator* generator_;
+ };
+
+ std::unique_ptr<ProcessClientHelloCallback> GetProcessClientHelloCallback() {
+ return QuicMakeUnique<ProcessClientHelloCallback>(this);
+ }
+
+ void ProcessClientHelloDone(std::unique_ptr<CryptoHandshakeMessage> rej) {
+ // Verify output is a REJ or SREJ.
+ EXPECT_THAT(rej->tag(),
+ testing::AnyOf(testing::Eq(kSREJ), testing::Eq(kREJ)));
+
+ VLOG(1) << "Extract valid STK and SCID from\n" << rej->DebugString();
+ QuicStringPiece srct;
+ ASSERT_TRUE(rej->GetStringPiece(kSourceAddressTokenTag, &srct));
+
+ QuicStringPiece scfg;
+ ASSERT_TRUE(rej->GetStringPiece(kSCFG, &scfg));
+ std::unique_ptr<CryptoHandshakeMessage> server_config(
+ CryptoFramer::ParseMessage(scfg));
+
+ QuicStringPiece scid;
+ ASSERT_TRUE(server_config->GetStringPiece(kSCID, &scid));
+
+ *out_ = result_->client_hello;
+ out_->SetStringPiece(kSCID, scid);
+ out_->SetStringPiece(kSourceAddressTokenTag, srct);
+ uint64_t xlct = LeafCertHashForTesting();
+ out_->SetValue(kXLCT, xlct);
+ }
+
+ protected:
+ QuicCryptoServerConfig* crypto_config_;
+ QuicSocketAddress server_addr_;
+ QuicSocketAddress client_addr_;
+ const QuicClock* clock_;
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config_;
+ QuicCompressedCertsCache* compressed_certs_cache_;
+ CryptoHandshakeMessage* out_;
+
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_;
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ result_;
+};
+
+} // namespace
+
+int HandshakeWithFakeServer(QuicConfig* server_quic_config,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ PacketSavingConnection* client_conn,
+ QuicCryptoClientStream* client,
+ const FakeServerOptions& options) {
+ PacketSavingConnection* server_conn = new PacketSavingConnection(
+ helper, alarm_factory, Perspective::IS_SERVER,
+ ParsedVersionOfIndex(client_conn->supported_versions(), 0));
+
+ QuicCryptoServerConfig crypto_config(
+ QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance(),
+ ProofSourceForTesting(), KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx());
+ QuicCompressedCertsCache compressed_certs_cache(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize);
+ SetupCryptoServerConfigForTest(server_conn->clock(),
+ server_conn->random_generator(),
+ &crypto_config, options);
+
+ TestQuicSpdyServerSession server_session(
+ server_conn, *server_quic_config, client_conn->supported_versions(),
+ &crypto_config, &compressed_certs_cache);
+ server_session.OnSuccessfulVersionNegotiation(
+ client_conn->supported_versions().front());
+ EXPECT_CALL(*server_session.helper(),
+ CanAcceptClientHello(testing::_, testing::_, testing::_,
+ testing::_, testing::_))
+ .Times(testing::AnyNumber());
+ EXPECT_CALL(*server_session.helper(),
+ GenerateConnectionIdForReject(testing::_, testing::_))
+ .Times(testing::AnyNumber());
+ EXPECT_CALL(*server_conn, OnCanWrite()).Times(testing::AnyNumber());
+ EXPECT_CALL(*client_conn, OnCanWrite()).Times(testing::AnyNumber());
+
+ // The client's handshake must have been started already.
+ CHECK_NE(0u, client_conn->encrypted_packets_.size());
+
+ CommunicateHandshakeMessages(client_conn, client, server_conn,
+ server_session.GetMutableCryptoStream());
+ CompareClientAndServerKeys(client, server_session.GetMutableCryptoStream());
+
+ return client->num_sent_client_hellos();
+}
+
+int HandshakeWithFakeClient(MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ PacketSavingConnection* server_conn,
+ QuicCryptoServerStream* server,
+ const QuicServerId& server_id,
+ const FakeClientOptions& options) {
+ ParsedQuicVersionVector supported_versions = AllSupportedVersions();
+ if (options.only_tls_versions) {
+ supported_versions.clear();
+ for (QuicTransportVersion transport_version :
+ AllSupportedTransportVersions()) {
+ supported_versions.push_back(
+ ParsedQuicVersion(PROTOCOL_TLS1_3, transport_version));
+ }
+ }
+ PacketSavingConnection* client_conn = new PacketSavingConnection(
+ helper, alarm_factory, Perspective::IS_CLIENT, supported_versions);
+ // Advance the time, because timers do not like uninitialized times.
+ client_conn->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+
+ QuicCryptoClientConfig crypto_config(ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx());
+ AsyncTestChannelIDSource* async_channel_id_source = nullptr;
+ if (options.channel_id_enabled) {
+ ChannelIDSource* source = ChannelIDSourceForTesting();
+ if (options.channel_id_source_async) {
+ async_channel_id_source = new AsyncTestChannelIDSource(source);
+ source = async_channel_id_source;
+ }
+ crypto_config.SetChannelIDSource(source);
+ }
+ if (!options.token_binding_params.empty()) {
+ crypto_config.tb_key_params = options.token_binding_params;
+ }
+ TestQuicSpdyClientSession client_session(client_conn, DefaultQuicConfig(),
+ supported_versions, server_id,
+ &crypto_config);
+
+ EXPECT_CALL(client_session, OnProofValid(testing::_))
+ .Times(testing::AnyNumber());
+ EXPECT_CALL(client_session, OnProofVerifyDetailsAvailable(testing::_))
+ .Times(testing::AnyNumber());
+ EXPECT_CALL(*client_conn, OnCanWrite()).Times(testing::AnyNumber());
+ client_session.GetMutableCryptoStream()->CryptoConnect();
+ CHECK_EQ(1u, client_conn->encrypted_packets_.size());
+
+ CommunicateHandshakeMessagesAndRunCallbacks(
+ client_conn, client_session.GetMutableCryptoStream(), server_conn, server,
+ async_channel_id_source);
+
+ if (server->handshake_confirmed() && server->encryption_established()) {
+ CompareClientAndServerKeys(client_session.GetMutableCryptoStream(), server);
+
+ if (options.channel_id_enabled) {
+ std::unique_ptr<ChannelIDKey> channel_id_key;
+ QuicAsyncStatus status =
+ crypto_config.channel_id_source()->GetChannelIDKey(
+ server_id.host(), &channel_id_key, nullptr);
+ EXPECT_EQ(QUIC_SUCCESS, status);
+ EXPECT_EQ(channel_id_key->SerializeKey(),
+ server->crypto_negotiated_params().channel_id);
+ EXPECT_EQ(
+ options.channel_id_source_async,
+ client_session.GetCryptoStream()->WasChannelIDSourceCallbackRun());
+ }
+ }
+
+ return client_session.GetCryptoStream()->num_sent_client_hellos();
+}
+
+void SetupCryptoServerConfigForTest(const QuicClock* clock,
+ QuicRandom* rand,
+ QuicCryptoServerConfig* crypto_config,
+ const FakeServerOptions& fake_options) {
+ QuicCryptoServerConfig::ConfigOptions options;
+ options.channel_id_enabled = true;
+ options.token_binding_params = fake_options.token_binding_params;
+ std::unique_ptr<CryptoHandshakeMessage> scfg(
+ crypto_config->AddDefaultConfig(rand, clock, options));
+}
+
+void SendHandshakeMessageToStream(QuicCryptoStream* stream,
+ const CryptoHandshakeMessage& message,
+ Perspective perspective) {
+ const QuicData& data = message.GetSerialized();
+ QuicSession* session = QuicStreamPeer::session(stream);
+ if (session->connection()->transport_version() < QUIC_VERSION_47) {
+ QuicStreamFrame frame(QuicUtils::GetCryptoStreamId(
+ session->connection()->transport_version()),
+ false, stream->crypto_bytes_read(),
+ data.AsStringPiece());
+ stream->OnStreamFrame(frame);
+ } else {
+ EncryptionLevel level = session->connection()->last_decrypted_level();
+ QuicCryptoFrame frame(level, stream->BytesReadOnLevel(level),
+ data.AsStringPiece());
+ stream->OnCryptoFrame(frame);
+ }
+}
+
+void CommunicateHandshakeMessages(PacketSavingConnection* client_conn,
+ QuicCryptoStream* client,
+ PacketSavingConnection* server_conn,
+ QuicCryptoStream* server) {
+ CommunicateHandshakeMessagesAndRunCallbacks(client_conn, client, server_conn,
+ server, nullptr);
+}
+
+void CommunicateHandshakeMessagesAndRunCallbacks(
+ PacketSavingConnection* client_conn,
+ QuicCryptoStream* client,
+ PacketSavingConnection* server_conn,
+ QuicCryptoStream* server,
+ CallbackSource* callback_source) {
+ size_t client_i = 0, server_i = 0;
+ while (!client->handshake_confirmed() || !server->handshake_confirmed()) {
+ ASSERT_GT(client_conn->encrypted_packets_.size(), client_i);
+ QUIC_LOG(INFO) << "Processing "
+ << client_conn->encrypted_packets_.size() - client_i
+ << " packets client->server";
+ MovePackets(client_conn, &client_i, server, server_conn,
+ Perspective::IS_SERVER);
+ if (callback_source) {
+ callback_source->RunPendingCallbacks();
+ }
+
+ if (client->handshake_confirmed() && server->handshake_confirmed()) {
+ break;
+ }
+ ASSERT_GT(server_conn->encrypted_packets_.size(), server_i);
+ QUIC_LOG(INFO) << "Processing "
+ << server_conn->encrypted_packets_.size() - server_i
+ << " packets server->client";
+ MovePackets(server_conn, &server_i, client, client_conn,
+ Perspective::IS_CLIENT);
+ if (callback_source) {
+ callback_source->RunPendingCallbacks();
+ }
+ }
+}
+
+std::pair<size_t, size_t> AdvanceHandshake(PacketSavingConnection* client_conn,
+ QuicCryptoStream* client,
+ size_t client_i,
+ PacketSavingConnection* server_conn,
+ QuicCryptoStream* server,
+ size_t server_i) {
+ QUIC_LOG(INFO) << "Processing "
+ << client_conn->encrypted_packets_.size() - client_i
+ << " packets client->server";
+ MovePackets(client_conn, &client_i, server, server_conn,
+ Perspective::IS_SERVER);
+
+ QUIC_LOG(INFO) << "Processing "
+ << server_conn->encrypted_packets_.size() - server_i
+ << " packets server->client";
+ if (server_conn->encrypted_packets_.size() - server_i == 2) {
+ QUIC_LOG(INFO) << "here";
+ }
+ MovePackets(server_conn, &server_i, client, client_conn,
+ Perspective::IS_CLIENT);
+
+ return std::make_pair(client_i, server_i);
+}
+
+QuicString GetValueForTag(const CryptoHandshakeMessage& message, QuicTag tag) {
+ auto it = message.tag_value_map().find(tag);
+ if (it == message.tag_value_map().end()) {
+ return QuicString();
+ }
+ return it->second;
+}
+
+uint64_t LeafCertHashForTesting() {
+ QuicReferenceCountedPointer<ProofSource::Chain> chain;
+ QuicSocketAddress server_address(QuicIpAddress::Any4(), 42);
+ QuicCryptoProof proof;
+ std::unique_ptr<ProofSource> proof_source(ProofSourceForTesting());
+
+ class Callback : public ProofSource::Callback {
+ public:
+ Callback(bool* ok, QuicReferenceCountedPointer<ProofSource::Chain>* chain)
+ : ok_(ok), chain_(chain) {}
+
+ void Run(bool ok,
+ const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const QuicCryptoProof& /* proof */,
+ std::unique_ptr<ProofSource::Details> /* details */) override {
+ *ok_ = ok;
+ *chain_ = chain;
+ }
+
+ private:
+ bool* ok_;
+ QuicReferenceCountedPointer<ProofSource::Chain>* chain_;
+ };
+
+ // Note: relies on the callback being invoked synchronously
+ bool ok = false;
+ proof_source->GetProof(
+ server_address, "", "", AllSupportedTransportVersions().front(), "",
+ std::unique_ptr<ProofSource::Callback>(new Callback(&ok, &chain)));
+ if (!ok || chain->certs.empty()) {
+ DCHECK(false) << "Proof generation failed";
+ return 0;
+ }
+
+ return QuicUtils::FNV1a_64_Hash(chain->certs.at(0));
+}
+
+class MockCommonCertSets : public CommonCertSets {
+ public:
+ MockCommonCertSets(QuicStringPiece cert, uint64_t hash, uint32_t index)
+ : cert_(cert), hash_(hash), index_(index) {}
+
+ QuicStringPiece GetCommonHashes() const override {
+ QUIC_BUG << "not implemented";
+ return QuicStringPiece();
+ }
+
+ QuicStringPiece GetCert(uint64_t hash, uint32_t index) const override {
+ if (hash == hash_ && index == index_) {
+ return cert_;
+ }
+ return QuicStringPiece();
+ }
+
+ bool MatchCert(QuicStringPiece cert,
+ QuicStringPiece common_set_hashes,
+ uint64_t* out_hash,
+ uint32_t* out_index) const override {
+ if (cert != cert_) {
+ return false;
+ }
+
+ if (common_set_hashes.size() % sizeof(uint64_t) != 0) {
+ return false;
+ }
+ bool client_has_set = false;
+ for (size_t i = 0; i < common_set_hashes.size(); i += sizeof(uint64_t)) {
+ uint64_t hash;
+ memcpy(&hash, common_set_hashes.data() + i, sizeof(hash));
+ if (hash == hash_) {
+ client_has_set = true;
+ break;
+ }
+ }
+
+ if (!client_has_set) {
+ return false;
+ }
+
+ *out_hash = hash_;
+ *out_index = index_;
+ return true;
+ }
+
+ private:
+ const QuicString cert_;
+ const uint64_t hash_;
+ const uint32_t index_;
+};
+
+CommonCertSets* MockCommonCertSets(QuicStringPiece cert,
+ uint64_t hash,
+ uint32_t index) {
+ return new class MockCommonCertSets(cert, hash, index);
+}
+
+void FillInDummyReject(CryptoHandshakeMessage* rej, bool reject_is_stateless) {
+ if (reject_is_stateless) {
+ rej->set_tag(kSREJ);
+ } else {
+ rej->set_tag(kREJ);
+ }
+
+ // Minimum SCFG that passes config validation checks.
+ // clang-format off
+ unsigned char scfg[] = {
+ // SCFG
+ 0x53, 0x43, 0x46, 0x47,
+ // num entries
+ 0x01, 0x00,
+ // padding
+ 0x00, 0x00,
+ // EXPY
+ 0x45, 0x58, 0x50, 0x59,
+ // EXPY end offset
+ 0x08, 0x00, 0x00, 0x00,
+ // Value
+ '1', '2', '3', '4',
+ '5', '6', '7', '8'
+ };
+ // clang-format on
+ rej->SetValue(kSCFG, scfg);
+ rej->SetStringPiece(kServerNonceTag, "SERVER_NONCE");
+ int64_t ttl = 2 * 24 * 60 * 60;
+ rej->SetValue(kSTTL, ttl);
+ std::vector<QuicTag> reject_reasons;
+ reject_reasons.push_back(CLIENT_NONCE_INVALID_FAILURE);
+ rej->SetVector(kRREJ, reject_reasons);
+}
+
+void CompareClientAndServerKeys(QuicCryptoClientStream* client,
+ QuicCryptoServerStream* server) {
+ QuicFramer* client_framer = QuicConnectionPeer::GetFramer(
+ QuicStreamPeer::session(client)->connection());
+ QuicFramer* server_framer = QuicConnectionPeer::GetFramer(
+ QuicStreamPeer::session(server)->connection());
+ const QuicEncrypter* client_encrypter(
+ QuicFramerPeer::GetEncrypter(client_framer, ENCRYPTION_ZERO_RTT));
+ const QuicDecrypter* client_decrypter(
+ QuicStreamPeer::session(client)->connection()->decrypter());
+ const QuicEncrypter* client_forward_secure_encrypter(
+ QuicFramerPeer::GetEncrypter(client_framer, ENCRYPTION_FORWARD_SECURE));
+ const QuicDecrypter* client_forward_secure_decrypter(
+ QuicStreamPeer::session(client)->connection()->alternative_decrypter());
+ const QuicEncrypter* server_encrypter(
+ QuicFramerPeer::GetEncrypter(server_framer, ENCRYPTION_ZERO_RTT));
+ const QuicDecrypter* server_decrypter(
+ QuicStreamPeer::session(server)->connection()->decrypter());
+ const QuicEncrypter* server_forward_secure_encrypter(
+ QuicFramerPeer::GetEncrypter(server_framer, ENCRYPTION_FORWARD_SECURE));
+ const QuicDecrypter* server_forward_secure_decrypter(
+ QuicStreamPeer::session(server)->connection()->alternative_decrypter());
+
+ QuicStringPiece client_encrypter_key = client_encrypter->GetKey();
+ QuicStringPiece client_encrypter_iv = client_encrypter->GetNoncePrefix();
+ QuicStringPiece client_decrypter_key = client_decrypter->GetKey();
+ QuicStringPiece client_decrypter_iv = client_decrypter->GetNoncePrefix();
+ QuicStringPiece client_forward_secure_encrypter_key =
+ client_forward_secure_encrypter->GetKey();
+ QuicStringPiece client_forward_secure_encrypter_iv =
+ client_forward_secure_encrypter->GetNoncePrefix();
+ QuicStringPiece client_forward_secure_decrypter_key =
+ client_forward_secure_decrypter->GetKey();
+ QuicStringPiece client_forward_secure_decrypter_iv =
+ client_forward_secure_decrypter->GetNoncePrefix();
+ QuicStringPiece server_encrypter_key = server_encrypter->GetKey();
+ QuicStringPiece server_encrypter_iv = server_encrypter->GetNoncePrefix();
+ QuicStringPiece server_decrypter_key = server_decrypter->GetKey();
+ QuicStringPiece server_decrypter_iv = server_decrypter->GetNoncePrefix();
+ QuicStringPiece server_forward_secure_encrypter_key =
+ server_forward_secure_encrypter->GetKey();
+ QuicStringPiece server_forward_secure_encrypter_iv =
+ server_forward_secure_encrypter->GetNoncePrefix();
+ QuicStringPiece server_forward_secure_decrypter_key =
+ server_forward_secure_decrypter->GetKey();
+ QuicStringPiece server_forward_secure_decrypter_iv =
+ server_forward_secure_decrypter->GetNoncePrefix();
+
+ QuicStringPiece client_subkey_secret =
+ client->crypto_negotiated_params().subkey_secret;
+ QuicStringPiece server_subkey_secret =
+ server->crypto_negotiated_params().subkey_secret;
+
+ const char kSampleLabel[] = "label";
+ const char kSampleContext[] = "context";
+ const size_t kSampleOutputLength = 32;
+ QuicString client_key_extraction;
+ QuicString server_key_extraction;
+ QuicString client_tb_ekm;
+ QuicString server_tb_ekm;
+ EXPECT_TRUE(client->ExportKeyingMaterial(kSampleLabel, kSampleContext,
+ kSampleOutputLength,
+ &client_key_extraction));
+ EXPECT_TRUE(server->ExportKeyingMaterial(kSampleLabel, kSampleContext,
+ kSampleOutputLength,
+ &server_key_extraction));
+
+ CompareCharArraysWithHexError("client write key", client_encrypter_key.data(),
+ client_encrypter_key.length(),
+ server_decrypter_key.data(),
+ server_decrypter_key.length());
+ CompareCharArraysWithHexError("client write IV", client_encrypter_iv.data(),
+ client_encrypter_iv.length(),
+ server_decrypter_iv.data(),
+ server_decrypter_iv.length());
+ CompareCharArraysWithHexError("server write key", server_encrypter_key.data(),
+ server_encrypter_key.length(),
+ client_decrypter_key.data(),
+ client_decrypter_key.length());
+ CompareCharArraysWithHexError("server write IV", server_encrypter_iv.data(),
+ server_encrypter_iv.length(),
+ client_decrypter_iv.data(),
+ client_decrypter_iv.length());
+ CompareCharArraysWithHexError("client forward secure write key",
+ client_forward_secure_encrypter_key.data(),
+ client_forward_secure_encrypter_key.length(),
+ server_forward_secure_decrypter_key.data(),
+ server_forward_secure_decrypter_key.length());
+ CompareCharArraysWithHexError("client forward secure write IV",
+ client_forward_secure_encrypter_iv.data(),
+ client_forward_secure_encrypter_iv.length(),
+ server_forward_secure_decrypter_iv.data(),
+ server_forward_secure_decrypter_iv.length());
+ CompareCharArraysWithHexError("server forward secure write key",
+ server_forward_secure_encrypter_key.data(),
+ server_forward_secure_encrypter_key.length(),
+ client_forward_secure_decrypter_key.data(),
+ client_forward_secure_decrypter_key.length());
+ CompareCharArraysWithHexError("server forward secure write IV",
+ server_forward_secure_encrypter_iv.data(),
+ server_forward_secure_encrypter_iv.length(),
+ client_forward_secure_decrypter_iv.data(),
+ client_forward_secure_decrypter_iv.length());
+ CompareCharArraysWithHexError("subkey secret", client_subkey_secret.data(),
+ client_subkey_secret.length(),
+ server_subkey_secret.data(),
+ server_subkey_secret.length());
+ CompareCharArraysWithHexError(
+ "sample key extraction", client_key_extraction.data(),
+ client_key_extraction.length(), server_key_extraction.data(),
+ server_key_extraction.length());
+
+ CompareCharArraysWithHexError("token binding key extraction",
+ client_tb_ekm.data(), client_tb_ekm.length(),
+ server_tb_ekm.data(), server_tb_ekm.length());
+}
+
+QuicTag ParseTag(const char* tagstr) {
+ const size_t len = strlen(tagstr);
+ CHECK_NE(0u, len);
+
+ QuicTag tag = 0;
+
+ if (tagstr[0] == '#') {
+ CHECK_EQ(static_cast<size_t>(1 + 2 * 4), len);
+ tagstr++;
+
+ for (size_t i = 0; i < 8; i++) {
+ tag <<= 4;
+
+ uint8_t v = 0;
+ CHECK(HexChar(tagstr[i], &v));
+ tag |= v;
+ }
+
+ return tag;
+ }
+
+ CHECK_LE(len, 4u);
+ for (size_t i = 0; i < 4; i++) {
+ tag >>= 8;
+ if (i < len) {
+ tag |= static_cast<uint32_t>(tagstr[i]) << 24;
+ }
+ }
+
+ return tag;
+}
+
+CryptoHandshakeMessage CreateCHLO(
+ std::vector<std::pair<QuicString, QuicString>> tags_and_values) {
+ return CreateCHLO(tags_and_values, -1);
+}
+
+CryptoHandshakeMessage CreateCHLO(
+ std::vector<std::pair<QuicString, QuicString>> tags_and_values,
+ int minimum_size_bytes) {
+ CryptoHandshakeMessage msg;
+ msg.set_tag(MakeQuicTag('C', 'H', 'L', 'O'));
+
+ if (minimum_size_bytes > 0) {
+ msg.set_minimum_size(minimum_size_bytes);
+ }
+
+ for (const auto& tag_and_value : tags_and_values) {
+ const QuicString& tag = tag_and_value.first;
+ const QuicString& value = tag_and_value.second;
+
+ const QuicTag quic_tag = ParseTag(tag.c_str());
+
+ size_t value_len = value.length();
+ if (value_len > 0 && value[0] == '#') {
+ // This is ascii encoded hex.
+ QuicString hex_value =
+ QuicTextUtils::HexDecode(QuicStringPiece(&value[1]));
+ msg.SetStringPiece(quic_tag, hex_value);
+ continue;
+ }
+ msg.SetStringPiece(quic_tag, value);
+ }
+
+ // The CryptoHandshakeMessage needs to be serialized and parsed to ensure
+ // that any padding is included.
+ std::unique_ptr<QuicData> bytes(CryptoFramer::ConstructHandshakeMessage(msg));
+ std::unique_ptr<CryptoHandshakeMessage> parsed(
+ CryptoFramer::ParseMessage(bytes->AsStringPiece()));
+ CHECK(parsed);
+
+ return *parsed;
+}
+
+ChannelIDSource* ChannelIDSourceForTesting() {
+ return new TestChannelIDSource();
+}
+
+void MovePackets(PacketSavingConnection* source_conn,
+ size_t* inout_packet_index,
+ QuicCryptoStream* dest_stream,
+ PacketSavingConnection* dest_conn,
+ Perspective dest_perspective) {
+ SimpleQuicFramer framer(source_conn->supported_versions(), dest_perspective);
+
+ SimpleQuicFramer null_encryption_framer(source_conn->supported_versions(),
+ dest_perspective);
+
+ size_t index = *inout_packet_index;
+ for (; index < source_conn->encrypted_packets_.size(); index++) {
+ // In order to properly test the code we need to perform encryption and
+ // decryption so that the crypters latch when expected. The crypters are in
+ // |dest_conn|, but we don't want to try and use them there. Instead we swap
+ // them into |framer|, perform the decryption with them, and then swap ther
+ // back.
+ QuicConnectionPeer::SwapCrypters(dest_conn, framer.framer());
+ if (!framer.ProcessPacket(*source_conn->encrypted_packets_[index])) {
+ // The framer will be unable to decrypt forward-secure packets sent after
+ // the handshake is complete. Don't treat them as handshake packets.
+ break;
+ }
+ QuicConnectionPeer::SwapCrypters(dest_conn, framer.framer());
+ dest_conn->OnDecryptedPacket(framer.last_decrypted_level());
+
+ if (dest_stream->handshake_protocol() == PROTOCOL_TLS1_3) {
+ // Try to process the packet with a framer that only has the NullDecrypter
+ // for decryption. If ProcessPacket succeeds, that means the packet was
+ // encrypted with the NullEncrypter. With the TLS handshaker in use, no
+ // packets should ever be encrypted with the NullEncrypter, instead
+ // they're encrypted with an obfuscation cipher based on QUIC version and
+ // connection ID.
+ ASSERT_FALSE(null_encryption_framer.ProcessPacket(
+ *source_conn->encrypted_packets_[index]))
+ << "No TLS packets should be encrypted with the NullEncrypter";
+ }
+
+ // Since we're using QuicFramers separate from the connections to move
+ // packets, the QuicConnection never gets notified about what level the last
+ // packet was decrypted at. This is needed by TLS to know what encryption
+ // level was used for the data it's receiving, so we plumb this information
+ // from the SimpleQuicFramer back into the connection.
+ dest_conn->OnDecryptedPacket(framer.last_decrypted_level());
+
+ QuicConnectionPeer::SetCurrentPacket(
+ dest_conn, source_conn->encrypted_packets_[index]->AsStringPiece());
+ for (const auto& stream_frame : framer.stream_frames()) {
+ dest_stream->OnStreamFrame(*stream_frame);
+ }
+ for (const auto& crypto_frame : framer.crypto_frames()) {
+ dest_stream->OnCryptoFrame(*crypto_frame);
+ }
+ }
+ *inout_packet_index = index;
+
+ QuicConnectionPeer::SetCurrentPacket(dest_conn, QuicStringPiece(nullptr, 0));
+}
+
+CryptoHandshakeMessage GenerateDefaultInchoateCHLO(
+ const QuicClock* clock,
+ QuicTransportVersion version,
+ QuicCryptoServerConfig* crypto_config) {
+ // clang-format off
+ return CreateCHLO(
+ {{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"PUBS", GenerateClientPublicValuesHex().c_str()},
+ {"NONC", GenerateClientNonceHex(clock, crypto_config).c_str()},
+ {"VER\0", QuicVersionLabelToString(
+ QuicVersionToQuicVersionLabel(version)).c_str()}},
+ kClientHelloMinimumSize);
+ // clang-format on
+}
+
+QuicString GenerateClientNonceHex(const QuicClock* clock,
+ QuicCryptoServerConfig* crypto_config) {
+ QuicCryptoServerConfig::ConfigOptions old_config_options;
+ QuicCryptoServerConfig::ConfigOptions new_config_options;
+ old_config_options.id = "old-config-id";
+ delete crypto_config->AddDefaultConfig(QuicRandom::GetInstance(), clock,
+ old_config_options);
+ std::unique_ptr<QuicServerConfigProtobuf> primary_config(
+ crypto_config->GenerateConfig(QuicRandom::GetInstance(), clock,
+ new_config_options));
+ primary_config->set_primary_time(clock->WallNow().ToUNIXSeconds());
+ std::unique_ptr<CryptoHandshakeMessage> msg(
+ crypto_config->AddConfig(std::move(primary_config), clock->WallNow()));
+ QuicStringPiece orbit;
+ CHECK(msg->GetStringPiece(kORBT, &orbit));
+ QuicString nonce;
+ CryptoUtils::GenerateNonce(clock->WallNow(), QuicRandom::GetInstance(), orbit,
+ &nonce);
+ return ("#" + QuicTextUtils::HexEncode(nonce));
+}
+
+QuicString GenerateClientPublicValuesHex() {
+ char public_value[32];
+ memset(public_value, 42, sizeof(public_value));
+ return ("#" + QuicTextUtils::HexEncode(public_value, sizeof(public_value)));
+}
+
+void GenerateFullCHLO(const CryptoHandshakeMessage& inchoate_chlo,
+ QuicCryptoServerConfig* crypto_config,
+ QuicSocketAddress server_addr,
+ QuicSocketAddress client_addr,
+ QuicTransportVersion version,
+ const QuicClock* clock,
+ QuicReferenceCountedPointer<QuicSignedServerConfig> proof,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ CryptoHandshakeMessage* out) {
+ // Pass a inchoate CHLO.
+ FullChloGenerator generator(crypto_config, server_addr, client_addr, clock,
+ proof, compressed_certs_cache, out);
+ crypto_config->ValidateClientHello(
+ inchoate_chlo, client_addr.host(), server_addr, version, clock, proof,
+ generator.GetValidateClientHelloCallback());
+}
+
+} // namespace crypto_test_utils
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/crypto_test_utils.h b/quic/test_tools/crypto_test_utils.h
new file mode 100644
index 0000000..8421895
--- /dev/null
+++ b/quic/test_tools/crypto_test_utils.h
@@ -0,0 +1,277 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_CRYPTO_TEST_UTILS_H_
+#define QUICHE_QUIC_TEST_TOOLS_CRYPTO_TEST_UTILS_H_
+
+#include <cstdarg>
+#include <cstddef>
+#include <cstdint>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "third_party/boringssl/src/include/openssl/evp.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+
+class ChannelIDSource;
+class CommonCertSets;
+class ProofSource;
+class ProofVerifier;
+class ProofVerifyContext;
+class QuicClock;
+class QuicConfig;
+class QuicCryptoClientStream;
+class QuicCryptoServerConfig;
+class QuicCryptoServerStream;
+class QuicCryptoStream;
+class QuicRandom;
+class QuicServerId;
+
+namespace test {
+
+class PacketSavingConnection;
+
+class TestChannelIDKey : public ChannelIDKey {
+ public:
+ explicit TestChannelIDKey(EVP_PKEY* ecdsa_key);
+ ~TestChannelIDKey() override;
+
+ // ChannelIDKey implementation.
+
+ bool Sign(QuicStringPiece signed_data,
+ QuicString* out_signature) const override;
+
+ QuicString SerializeKey() const override;
+
+ private:
+ bssl::UniquePtr<EVP_PKEY> ecdsa_key_;
+};
+
+class TestChannelIDSource : public ChannelIDSource {
+ public:
+ ~TestChannelIDSource() override;
+
+ // ChannelIDSource implementation.
+
+ QuicAsyncStatus GetChannelIDKey(
+ const QuicString& hostname,
+ std::unique_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* /*callback*/) override;
+
+ private:
+ static EVP_PKEY* HostnameToKey(const QuicString& hostname);
+};
+
+namespace crypto_test_utils {
+
+// An interface for a source of callbacks. This is used for invoking
+// callbacks asynchronously.
+//
+// Call the RunPendingCallbacks method regularly to run the callbacks from
+// this source.
+class CallbackSource {
+ public:
+ virtual ~CallbackSource() {}
+
+ // Runs pending callbacks from this source. If there is no pending
+ // callback, does nothing.
+ virtual void RunPendingCallbacks() = 0;
+};
+
+// FakeServerOptions bundles together a number of options for configuring the
+// server in HandshakeWithFakeServer.
+struct FakeServerOptions {
+ FakeServerOptions();
+ ~FakeServerOptions();
+
+ // The Token Binding params that the server supports and will negotiate.
+ QuicTagVector token_binding_params;
+};
+
+// FakeClientOptions bundles together a number of options for configuring
+// HandshakeWithFakeClient.
+struct FakeClientOptions {
+ FakeClientOptions();
+ ~FakeClientOptions();
+
+ // If channel_id_enabled is true then the client will attempt to send a
+ // ChannelID.
+ bool channel_id_enabled;
+
+ // If channel_id_source_async is true then the client will use an async
+ // ChannelIDSource for testing. Ignored if channel_id_enabled is false.
+ bool channel_id_source_async;
+
+ // The Token Binding params that the client supports and will negotiate.
+ QuicTagVector token_binding_params;
+
+ // If only_tls_versions is set, then the client will only use TLS for the
+ // crypto handshake.
+ bool only_tls_versions = false;
+};
+
+// returns: the number of client hellos that the client sent.
+int HandshakeWithFakeServer(QuicConfig* server_quic_config,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ PacketSavingConnection* client_conn,
+ QuicCryptoClientStream* client,
+ const FakeServerOptions& options);
+
+// returns: the number of client hellos that the client sent.
+int HandshakeWithFakeClient(MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ PacketSavingConnection* server_conn,
+ QuicCryptoServerStream* server,
+ const QuicServerId& server_id,
+ const FakeClientOptions& options);
+
+// SetupCryptoServerConfigForTest configures |crypto_config|
+// with sensible defaults for testing.
+void SetupCryptoServerConfigForTest(const QuicClock* clock,
+ QuicRandom* rand,
+ QuicCryptoServerConfig* crypto_config,
+ const FakeServerOptions& options);
+
+// Sends the handshake message |message| to stream |stream| with the perspective
+// that the message is coming from |perspective|.
+void SendHandshakeMessageToStream(QuicCryptoStream* stream,
+ const CryptoHandshakeMessage& message,
+ Perspective perspective);
+
+// CommunicateHandshakeMessages moves messages from |client| to |server| and
+// back until |clients|'s handshake has completed.
+void CommunicateHandshakeMessages(PacketSavingConnection* client_conn,
+ QuicCryptoStream* client,
+ PacketSavingConnection* server_conn,
+ QuicCryptoStream* server);
+
+// CommunicateHandshakeMessagesAndRunCallbacks moves messages from |client|
+// to |server| and back until |client|'s handshake has completed. If
+// |callback_source| is not nullptr,
+// CommunicateHandshakeMessagesAndRunCallbacks also runs callbacks from
+// |callback_source| between processing messages.
+void CommunicateHandshakeMessagesAndRunCallbacks(
+ PacketSavingConnection* client_conn,
+ QuicCryptoStream* client,
+ PacketSavingConnection* server_conn,
+ QuicCryptoStream* server,
+ CallbackSource* callback_source);
+
+// AdvanceHandshake attempts to moves messages from |client| to |server| and
+// |server| to |client|. Returns the number of messages moved.
+std::pair<size_t, size_t> AdvanceHandshake(PacketSavingConnection* client_conn,
+ QuicCryptoStream* client,
+ size_t client_i,
+ PacketSavingConnection* server_conn,
+ QuicCryptoStream* server,
+ size_t server_i);
+
+// Returns the value for the tag |tag| in the tag value map of |message|.
+QuicString GetValueForTag(const CryptoHandshakeMessage& message, QuicTag tag);
+
+// Returns a new |ProofSource| that serves up test certificates.
+std::unique_ptr<ProofSource> ProofSourceForTesting();
+
+// Returns a new |ProofVerifier| that uses the QUIC testing root CA.
+std::unique_ptr<ProofVerifier> ProofVerifierForTesting();
+
+// Returns a hash of the leaf test certificate.
+uint64_t LeafCertHashForTesting();
+
+// Returns a |ProofVerifyContext| that must be used with the verifier
+// returned by |ProofVerifierForTesting|.
+std::unique_ptr<ProofVerifyContext> ProofVerifyContextForTesting();
+
+// MockCommonCertSets returns a CommonCertSets that contains a single set with
+// hash |hash|, consisting of the certificate |cert| at index |index|.
+CommonCertSets* MockCommonCertSets(QuicStringPiece cert,
+ uint64_t hash,
+ uint32_t index);
+
+// Creates a minimal dummy reject message that will pass the client-config
+// validation tests. This will include a server config, but no certs, proof
+// source address token, or server nonce.
+void FillInDummyReject(CryptoHandshakeMessage* rej, bool reject_is_stateless);
+
+// ParseTag returns a QuicTag from parsing |tagstr|. |tagstr| may either be
+// in the format "EXMP" (i.e. ASCII format), or "#11223344" (an explicit hex
+// format). It CHECK fails if there's a parse error.
+QuicTag ParseTag(const char* tagstr);
+
+// Message constructs a CHLO message from a provided vector of tag/value pairs.
+// The first of each pair is the tag of a tag/value and is given as an argument
+// to |ParseTag|. The second is the value of the tag/value pair and is either a
+// hex dump, preceeded by a '#', or a raw value. If minimum_size_bytes is
+// provided then the message will be padded to this minimum size.
+//
+// CreateCHLO(
+// {{"NOCE", "#11223344"},
+// {"SNI", "www.example.com"}},
+// optional_minimum_size_bytes);
+CryptoHandshakeMessage CreateCHLO(
+ std::vector<std::pair<QuicString, QuicString>> tags_and_values);
+CryptoHandshakeMessage CreateCHLO(
+ std::vector<std::pair<QuicString, QuicString>> tags_and_values,
+ int minimum_size_bytes);
+
+// ChannelIDSourceForTesting returns a ChannelIDSource that generates keys
+// deterministically based on the hostname given in the GetChannelIDKey call.
+// This ChannelIDSource works in synchronous mode, i.e., its GetChannelIDKey
+// method never returns QUIC_PENDING.
+ChannelIDSource* ChannelIDSourceForTesting();
+
+// MovePackets parses crypto handshake messages from packet number
+// |*inout_packet_index| through to the last packet (or until a packet fails
+// to decrypt) and has |dest_stream| process them. |*inout_packet_index| is
+// updated with an index one greater than the last packet processed.
+void MovePackets(PacketSavingConnection* source_conn,
+ size_t* inout_packet_index,
+ QuicCryptoStream* dest_stream,
+ PacketSavingConnection* dest_conn,
+ Perspective dest_perspective);
+
+// Return an inchoate CHLO with some basic tag value pairs.
+CryptoHandshakeMessage GenerateDefaultInchoateCHLO(
+ const QuicClock* clock,
+ QuicTransportVersion version,
+ QuicCryptoServerConfig* crypto_config);
+
+// Takes a inchoate CHLO, returns a full CHLO in |out| which can pass
+// |crypto_config|'s validation.
+void GenerateFullCHLO(
+ const CryptoHandshakeMessage& inchoate_chlo,
+ QuicCryptoServerConfig* crypto_config,
+ QuicSocketAddress server_addr,
+ QuicSocketAddress client_addr,
+ QuicTransportVersion version,
+ const QuicClock* clock,
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ CryptoHandshakeMessage* out);
+
+void CompareClientAndServerKeys(QuicCryptoClientStream* client,
+ QuicCryptoServerStream* server);
+
+// Return a CHLO nonce in hexadecimal.
+QuicString GenerateClientNonceHex(const QuicClock* clock,
+ QuicCryptoServerConfig* crypto_config);
+
+// Return a CHLO PUBS in hexadecimal.
+QuicString GenerateClientPublicValuesHex();
+
+} // namespace crypto_test_utils
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_CRYPTO_TEST_UTILS_H_
diff --git a/quic/test_tools/crypto_test_utils_test.cc b/quic/test_tools/crypto_test_utils_test.cc
new file mode 100644
index 0000000..cecdaa2
--- /dev/null
+++ b/quic/test_tools/crypto_test_utils_test.cc
@@ -0,0 +1,172 @@
+// Copyright (c) 2012 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 "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+
+#include "net/third_party/quiche/src/quic/core/proto/crypto_server_config.pb.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+
+namespace quic {
+namespace test {
+
+class ShloVerifier {
+ public:
+ ShloVerifier(
+ QuicCryptoServerConfig* crypto_config,
+ QuicSocketAddress server_addr,
+ QuicSocketAddress client_addr,
+ const QuicClock* clock,
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config,
+ QuicCompressedCertsCache* compressed_certs_cache)
+ : crypto_config_(crypto_config),
+ server_addr_(server_addr),
+ client_addr_(client_addr),
+ clock_(clock),
+ signed_config_(signed_config),
+ compressed_certs_cache_(compressed_certs_cache),
+ params_(new QuicCryptoNegotiatedParameters) {}
+
+ class ValidateClientHelloCallback : public ValidateClientHelloResultCallback {
+ public:
+ explicit ValidateClientHelloCallback(ShloVerifier* shlo_verifier)
+ : shlo_verifier_(shlo_verifier) {}
+ void Run(QuicReferenceCountedPointer<
+ ValidateClientHelloResultCallback::Result> result,
+ std::unique_ptr<ProofSource::Details> /* details */) override {
+ shlo_verifier_->ValidateClientHelloDone(result);
+ }
+
+ private:
+ ShloVerifier* shlo_verifier_;
+ };
+
+ std::unique_ptr<ValidateClientHelloCallback>
+ GetValidateClientHelloCallback() {
+ return QuicMakeUnique<ValidateClientHelloCallback>(this);
+ }
+
+ private:
+ void ValidateClientHelloDone(
+ const QuicReferenceCountedPointer<
+ ValidateClientHelloResultCallback::Result>& result) {
+ result_ = result;
+ crypto_config_->ProcessClientHello(
+ result_, /*reject_only=*/false,
+ /*connection_id=*/TestConnectionId(1), server_addr_, client_addr_,
+ AllSupportedVersions().front(), AllSupportedVersions(),
+ /*use_stateless_rejects=*/true,
+ /*server_designated_connection_id=*/TestConnectionId(2), clock_,
+ QuicRandom::GetInstance(), compressed_certs_cache_, params_,
+ signed_config_, /*total_framing_overhead=*/50, kDefaultMaxPacketSize,
+ GetProcessClientHelloCallback());
+ }
+
+ class ProcessClientHelloCallback : public ProcessClientHelloResultCallback {
+ public:
+ explicit ProcessClientHelloCallback(ShloVerifier* shlo_verifier)
+ : shlo_verifier_(shlo_verifier) {}
+ void Run(
+ QuicErrorCode error,
+ const QuicString& error_details,
+ std::unique_ptr<CryptoHandshakeMessage> message,
+ std::unique_ptr<DiversificationNonce> diversification_nonce,
+ std::unique_ptr<ProofSource::Details> proof_source_details) override {
+ shlo_verifier_->ProcessClientHelloDone(std::move(message));
+ }
+
+ private:
+ ShloVerifier* shlo_verifier_;
+ };
+
+ std::unique_ptr<ProcessClientHelloCallback> GetProcessClientHelloCallback() {
+ return QuicMakeUnique<ProcessClientHelloCallback>(this);
+ }
+
+ void ProcessClientHelloDone(std::unique_ptr<CryptoHandshakeMessage> message) {
+ // Verify output is a SHLO.
+ EXPECT_EQ(message->tag(), kSHLO)
+ << "Fail to pass validation. Get " << message->DebugString();
+ }
+
+ QuicCryptoServerConfig* crypto_config_;
+ QuicSocketAddress server_addr_;
+ QuicSocketAddress client_addr_;
+ const QuicClock* clock_;
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config_;
+ QuicCompressedCertsCache* compressed_certs_cache_;
+
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_;
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ result_;
+};
+
+class CryptoTestUtilsTest : public QuicTest {};
+
+TEST_F(CryptoTestUtilsTest, TestGenerateFullCHLO) {
+ MockClock clock;
+ QuicCryptoServerConfig crypto_config(
+ QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance(),
+ crypto_test_utils::ProofSourceForTesting(), KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx());
+ QuicSocketAddress server_addr(QuicIpAddress::Any4(), 5);
+ QuicSocketAddress client_addr(QuicIpAddress::Loopback4(), 1);
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config(
+ new QuicSignedServerConfig);
+ QuicCompressedCertsCache compressed_certs_cache(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize);
+ CryptoHandshakeMessage full_chlo;
+
+ QuicCryptoServerConfig::ConfigOptions old_config_options;
+ old_config_options.id = "old-config-id";
+ delete crypto_config.AddDefaultConfig(QuicRandom::GetInstance(), &clock,
+ old_config_options);
+ QuicCryptoServerConfig::ConfigOptions new_config_options;
+ std::unique_ptr<QuicServerConfigProtobuf> primary_config(
+ crypto_config.GenerateConfig(QuicRandom::GetInstance(), &clock,
+ new_config_options));
+ primary_config->set_primary_time(clock.WallNow().ToUNIXSeconds());
+ std::unique_ptr<CryptoHandshakeMessage> msg(
+ crypto_config.AddConfig(std::move(primary_config), clock.WallNow()));
+ QuicStringPiece orbit;
+ ASSERT_TRUE(msg->GetStringPiece(kORBT, &orbit));
+ QuicString nonce;
+ CryptoUtils::GenerateNonce(clock.WallNow(), QuicRandom::GetInstance(), orbit,
+ &nonce);
+ QuicString nonce_hex = "#" + QuicTextUtils::HexEncode(nonce);
+
+ char public_value[32];
+ memset(public_value, 42, sizeof(public_value));
+ QuicString pub_hex =
+ "#" + QuicTextUtils::HexEncode(public_value, sizeof(public_value));
+
+ QuicTransportVersion version(AllSupportedTransportVersions().front());
+ CryptoHandshakeMessage inchoate_chlo = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"COPT", "SREJ"},
+ {"PUBS", pub_hex},
+ {"NONC", nonce_hex},
+ {"VER\0",
+ QuicVersionLabelToString(QuicVersionToQuicVersionLabel(version))}},
+ kClientHelloMinimumSize);
+
+ crypto_test_utils::GenerateFullCHLO(
+ inchoate_chlo, &crypto_config, server_addr, client_addr, version, &clock,
+ signed_config, &compressed_certs_cache, &full_chlo);
+ // Verify that full_chlo can pass crypto_config's verification.
+ ShloVerifier shlo_verifier(&crypto_config, server_addr, client_addr, &clock,
+ signed_config, &compressed_certs_cache);
+ crypto_config.ValidateClientHello(
+ full_chlo, client_addr.host(), server_addr, version, &clock,
+ signed_config, shlo_verifier.GetValidateClientHelloCallback());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/failing_proof_source.cc b/quic/test_tools/failing_proof_source.cc
new file mode 100644
index 0000000..2db5717
--- /dev/null
+++ b/quic/test_tools/failing_proof_source.cc
@@ -0,0 +1,35 @@
+// Copyright (c) 2017 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 "net/third_party/quiche/src/quic/test_tools/failing_proof_source.h"
+
+namespace quic {
+namespace test {
+
+void FailingProofSource::GetProof(const QuicSocketAddress& server_address,
+ const QuicString& hostname,
+ const QuicString& server_config,
+ QuicTransportVersion transport_version,
+ QuicStringPiece chlo_hash,
+ std::unique_ptr<Callback> callback) {
+ callback->Run(false, nullptr, QuicCryptoProof(), nullptr);
+}
+
+QuicReferenceCountedPointer<ProofSource::Chain>
+FailingProofSource::GetCertChain(const QuicSocketAddress& server_address,
+ const QuicString& hostname) {
+ return QuicReferenceCountedPointer<Chain>();
+}
+
+void FailingProofSource::ComputeTlsSignature(
+ const QuicSocketAddress& server_address,
+ const QuicString& hostname,
+ uint16_t signature_algorithm,
+ QuicStringPiece in,
+ std::unique_ptr<SignatureCallback> callback) {
+ callback->Run(false, "");
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/failing_proof_source.h b/quic/test_tools/failing_proof_source.h
new file mode 100644
index 0000000..4427c4c
--- /dev/null
+++ b/quic/test_tools/failing_proof_source.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2017 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_FAILING_PROOF_SOURCE_H_
+#define QUICHE_QUIC_TEST_TOOLS_FAILING_PROOF_SOURCE_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/proof_source.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+namespace test {
+
+class FailingProofSource : public ProofSource {
+ public:
+ void GetProof(const QuicSocketAddress& server_address,
+ const QuicString& hostname,
+ const QuicString& server_config,
+ QuicTransportVersion transport_version,
+ QuicStringPiece chlo_hash,
+ std::unique_ptr<Callback> callback) override;
+
+ QuicReferenceCountedPointer<Chain> GetCertChain(
+ const QuicSocketAddress& server_address,
+ const QuicString& hostname) override;
+
+ void ComputeTlsSignature(
+ const QuicSocketAddress& server_address,
+ const QuicString& hostname,
+ uint16_t signature_algorithm,
+ QuicStringPiece in,
+ std::unique_ptr<SignatureCallback> callback) override;
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_FAILING_PROOF_SOURCE_H_
diff --git a/quic/test_tools/fake_proof_source.cc b/quic/test_tools/fake_proof_source.cc
new file mode 100644
index 0000000..3a02cd5
--- /dev/null
+++ b/quic/test_tools/fake_proof_source.cc
@@ -0,0 +1,128 @@
+// Copyright (c) 2016 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 "net/third_party/quiche/src/quic/test_tools/fake_proof_source.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+
+namespace quic {
+namespace test {
+
+FakeProofSource::FakeProofSource()
+ : delegate_(crypto_test_utils::ProofSourceForTesting()) {}
+
+FakeProofSource::~FakeProofSource() {}
+
+FakeProofSource::PendingOp::~PendingOp() = default;
+
+FakeProofSource::GetProofOp::GetProofOp(
+ const QuicSocketAddress& server_addr,
+ QuicString hostname,
+ QuicString server_config,
+ QuicTransportVersion transport_version,
+ QuicString chlo_hash,
+ std::unique_ptr<ProofSource::Callback> callback,
+ ProofSource* delegate)
+ : server_address_(server_addr),
+ hostname_(std::move(hostname)),
+ server_config_(std::move(server_config)),
+ transport_version_(transport_version),
+ chlo_hash_(std::move(chlo_hash)),
+ callback_(std::move(callback)),
+ delegate_(delegate) {}
+
+FakeProofSource::GetProofOp::~GetProofOp() = default;
+
+void FakeProofSource::GetProofOp::Run() {
+ // Note: relies on the callback being invoked synchronously
+ delegate_->GetProof(server_address_, hostname_, server_config_,
+ transport_version_, chlo_hash_, std::move(callback_));
+}
+
+FakeProofSource::ComputeSignatureOp::ComputeSignatureOp(
+ const QuicSocketAddress& server_address,
+ QuicString hostname,
+ uint16_t sig_alg,
+ QuicStringPiece in,
+ std::unique_ptr<ProofSource::SignatureCallback> callback,
+ ProofSource* delegate)
+ : server_address_(server_address),
+ hostname_(std::move(hostname)),
+ sig_alg_(sig_alg),
+ in_(in),
+ callback_(std::move(callback)),
+ delegate_(delegate) {}
+
+FakeProofSource::ComputeSignatureOp::~ComputeSignatureOp() = default;
+
+void FakeProofSource::ComputeSignatureOp::Run() {
+ delegate_->ComputeTlsSignature(server_address_, hostname_, sig_alg_, in_,
+ std::move(callback_));
+}
+
+void FakeProofSource::Activate() {
+ active_ = true;
+}
+
+void FakeProofSource::GetProof(
+ const QuicSocketAddress& server_address,
+ const QuicString& hostname,
+ const QuicString& server_config,
+ QuicTransportVersion transport_version,
+ QuicStringPiece chlo_hash,
+ std::unique_ptr<ProofSource::Callback> callback) {
+ if (!active_) {
+ delegate_->GetProof(server_address, hostname, server_config,
+ transport_version, chlo_hash, std::move(callback));
+ return;
+ }
+
+ pending_ops_.push_back(QuicMakeUnique<GetProofOp>(
+ server_address, hostname, server_config, transport_version,
+ QuicString(chlo_hash), std::move(callback), delegate_.get()));
+}
+
+QuicReferenceCountedPointer<ProofSource::Chain> FakeProofSource::GetCertChain(
+ const QuicSocketAddress& server_address,
+ const QuicString& hostname) {
+ return delegate_->GetCertChain(server_address, hostname);
+}
+
+void FakeProofSource::ComputeTlsSignature(
+ const QuicSocketAddress& server_address,
+ const QuicString& hostname,
+ uint16_t signature_algorithm,
+ QuicStringPiece in,
+ std::unique_ptr<ProofSource::SignatureCallback> callback) {
+ QUIC_LOG(INFO) << "FakeProofSource::ComputeTlsSignature";
+ if (!active_) {
+ QUIC_LOG(INFO) << "Not active - directly calling delegate";
+ delegate_->ComputeTlsSignature(
+ server_address, hostname, signature_algorithm, in, std::move(callback));
+ return;
+ }
+
+ QUIC_LOG(INFO) << "Adding pending op";
+ pending_ops_.push_back(QuicMakeUnique<ComputeSignatureOp>(
+ server_address, hostname, signature_algorithm, in, std::move(callback),
+ delegate_.get()));
+}
+
+int FakeProofSource::NumPendingCallbacks() const {
+ return pending_ops_.size();
+}
+
+void FakeProofSource::InvokePendingCallback(int n) {
+ CHECK(NumPendingCallbacks() > n);
+
+ pending_ops_[n]->Run();
+
+ auto it = pending_ops_.begin() + n;
+ pending_ops_.erase(it);
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/fake_proof_source.h b/quic/test_tools/fake_proof_source.h
new file mode 100644
index 0000000..27f63a3
--- /dev/null
+++ b/quic/test_tools/fake_proof_source.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2016 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_FAKE_PROOF_SOURCE_H_
+#define QUICHE_QUIC_TEST_TOOLS_FAKE_PROOF_SOURCE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/crypto/proof_source.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+namespace test {
+
+// Implementation of ProofSource which delegates to a ProofSourceForTesting,
+// except that when the async GetProof is called, it captures the call and
+// allows tests to see that a call is pending, which they can then cause to
+// complete at a time of their choosing.
+class FakeProofSource : public ProofSource {
+ public:
+ FakeProofSource();
+ ~FakeProofSource() override;
+
+ // Before this object is "active", all calls to GetProof will be delegated
+ // immediately. Once "active", the async ones will be intercepted. This
+ // distinction is necessary to ensure that GetProof can be called without
+ // interference during test case setup.
+ void Activate();
+
+ // ProofSource interface
+ void GetProof(const QuicSocketAddress& server_address,
+ const QuicString& hostname,
+ const QuicString& server_config,
+ QuicTransportVersion transport_version,
+ QuicStringPiece chlo_hash,
+ std::unique_ptr<ProofSource::Callback> callback) override;
+ QuicReferenceCountedPointer<Chain> GetCertChain(
+ const QuicSocketAddress& server_address,
+ const QuicString& hostname) override;
+ void ComputeTlsSignature(
+ const QuicSocketAddress& server_address,
+ const QuicString& hostname,
+ uint16_t signature_algorithm,
+ QuicStringPiece in,
+ std::unique_ptr<ProofSource::SignatureCallback> callback) override;
+
+ // Get the number of callbacks which are pending
+ int NumPendingCallbacks() const;
+
+ // Invoke a pending callback. The index refers to the position in
+ // pending_ops_ of the callback to be completed.
+ void InvokePendingCallback(int n);
+
+ private:
+ std::unique_ptr<ProofSource> delegate_;
+ bool active_ = false;
+
+ class PendingOp {
+ public:
+ virtual ~PendingOp();
+ virtual void Run() = 0;
+ };
+
+ class GetProofOp : public PendingOp {
+ public:
+ GetProofOp(const QuicSocketAddress& server_addr,
+ QuicString hostname,
+ QuicString server_config,
+ QuicTransportVersion transport_version,
+ QuicString chlo_hash,
+ std::unique_ptr<ProofSource::Callback> callback,
+ ProofSource* delegate);
+ ~GetProofOp() override;
+
+ void Run() override;
+
+ private:
+ QuicSocketAddress server_address_;
+ QuicString hostname_;
+ QuicString server_config_;
+ QuicTransportVersion transport_version_;
+ QuicString chlo_hash_;
+ std::unique_ptr<ProofSource::Callback> callback_;
+ ProofSource* delegate_;
+ };
+
+ class ComputeSignatureOp : public PendingOp {
+ public:
+ ComputeSignatureOp(const QuicSocketAddress& server_address,
+ QuicString hostname,
+ uint16_t sig_alg,
+ QuicStringPiece in,
+ std::unique_ptr<ProofSource::SignatureCallback> callback,
+ ProofSource* delegate);
+ ~ComputeSignatureOp() override;
+
+ void Run() override;
+
+ private:
+ QuicSocketAddress server_address_;
+ QuicString hostname_;
+ uint16_t sig_alg_;
+ QuicString in_;
+ std::unique_ptr<ProofSource::SignatureCallback> callback_;
+ ProofSource* delegate_;
+ };
+
+ std::vector<std::unique_ptr<PendingOp>> pending_ops_;
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_FAKE_PROOF_SOURCE_H_
diff --git a/quic/test_tools/fuzzing/quic_framer_fuzzer.cc b/quic/test_tools/fuzzing/quic_framer_fuzzer.cc
new file mode 100644
index 0000000..df997e6
--- /dev/null
+++ b/quic/test_tools/fuzzing/quic_framer_fuzzer.cc
@@ -0,0 +1,33 @@
+// 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 "base/commandlineflags.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ SetQuicFlag(&FLAGS_minloglevel, 3);
+
+ quic::QuicFramer framer(quic::AllSupportedVersions(), quic::QuicTime::Zero(),
+ quic::Perspective::IS_SERVER,
+ quic::kQuicDefaultConnectionIdLength);
+ const char* const packet_bytes = reinterpret_cast<const char*>(data);
+
+ // Test the CryptoFramer.
+ quic::QuicStringPiece crypto_input(packet_bytes, size);
+ std::unique_ptr<quic::CryptoHandshakeMessage> handshake_message(
+ quic::CryptoFramer::ParseMessage(crypto_input));
+
+ // Test the regular QuicFramer with the same input.
+ quic::test::NoOpFramerVisitor visitor;
+ framer.set_visitor(&visitor);
+ quic::QuicEncryptedPacket packet(packet_bytes, size);
+ framer.ProcessPacket(packet);
+
+ return 0;
+}
diff --git a/quic/test_tools/limited_mtu_test_writer.cc b/quic/test_tools/limited_mtu_test_writer.cc
new file mode 100644
index 0000000..bc71c7c
--- /dev/null
+++ b/quic/test_tools/limited_mtu_test_writer.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2015 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 "net/third_party/quiche/src/quic/test_tools/limited_mtu_test_writer.h"
+
+namespace quic {
+namespace test {
+
+LimitedMtuTestWriter::LimitedMtuTestWriter(QuicByteCount mtu) : mtu_(mtu) {}
+
+LimitedMtuTestWriter::~LimitedMtuTestWriter() = default;
+
+WriteResult LimitedMtuTestWriter::WritePacket(
+ const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) {
+ if (buf_len > mtu_) {
+ // Drop the packet.
+ return WriteResult(WRITE_STATUS_OK, buf_len);
+ }
+
+ return QuicPacketWriterWrapper::WritePacket(buffer, buf_len, self_address,
+ peer_address, options);
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/limited_mtu_test_writer.h b/quic/test_tools/limited_mtu_test_writer.h
new file mode 100644
index 0000000..7c95417
--- /dev/null
+++ b/quic/test_tools/limited_mtu_test_writer.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2015 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_LIMITED_MTU_TEST_WRITER_H_
+#define QUICHE_QUIC_TEST_TOOLS_LIMITED_MTU_TEST_WRITER_H_
+
+#include "base/macros.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+namespace test {
+
+// Simulates a connection over a link with fixed MTU. Drops packets which
+// exceed the MTU and passes the rest of them as-is.
+class LimitedMtuTestWriter : public QuicPacketWriterWrapper {
+ public:
+ explicit LimitedMtuTestWriter(QuicByteCount mtu);
+ LimitedMtuTestWriter(const LimitedMtuTestWriter&) = delete;
+ LimitedMtuTestWriter& operator=(const LimitedMtuTestWriter&) = delete;
+ ~LimitedMtuTestWriter() override;
+
+ // Inherited from QuicPacketWriterWrapper.
+ WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) override;
+
+ private:
+ QuicByteCount mtu_;
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_LIMITED_MTU_TEST_WRITER_H_
diff --git a/quic/test_tools/mock_clock.cc b/quic/test_tools/mock_clock.cc
new file mode 100644
index 0000000..1761dd9
--- /dev/null
+++ b/quic/test_tools/mock_clock.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 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 "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+
+namespace quic {
+
+MockClock::MockClock() : now_(QuicTime::Zero()) {}
+
+MockClock::~MockClock() {}
+
+void MockClock::AdvanceTime(QuicTime::Delta delta) {
+ now_ = now_ + delta;
+}
+
+QuicTime MockClock::Now() const {
+ return now_;
+}
+
+QuicTime MockClock::ApproximateNow() const {
+ return now_;
+}
+
+QuicWallTime MockClock::WallNow() const {
+ return QuicWallTime::FromUNIXSeconds((now_ - QuicTime::Zero()).ToSeconds());
+}
+
+} // namespace quic
diff --git a/quic/test_tools/mock_clock.h b/quic/test_tools/mock_clock.h
new file mode 100644
index 0000000..dbd57d3
--- /dev/null
+++ b/quic/test_tools/mock_clock.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_MOCK_CLOCK_H_
+#define QUICHE_QUIC_TEST_TOOLS_MOCK_CLOCK_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+
+namespace quic {
+
+class MockClock : public QuicClock {
+ public:
+ MockClock();
+ MockClock(const MockClock&) = delete;
+ MockClock& operator=(const MockClock&) = delete;
+ ~MockClock() override;
+
+ // QuicClock implementation:
+ QuicTime Now() const override;
+ QuicTime ApproximateNow() const override;
+ QuicWallTime WallNow() const override;
+
+ // Advances the current time by |delta|, which may be negative.
+ void AdvanceTime(QuicTime::Delta delta);
+
+ private:
+ QuicTime now_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_MOCK_CLOCK_H_
diff --git a/quic/test_tools/mock_quic_client_promised_info.cc b/quic/test_tools/mock_quic_client_promised_info.cc
new file mode 100644
index 0000000..4400b03
--- /dev/null
+++ b/quic/test_tools/mock_quic_client_promised_info.cc
@@ -0,0 +1,19 @@
+// Copyright 2016 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 "net/third_party/quiche/src/quic/test_tools/mock_quic_client_promised_info.h"
+
+namespace quic {
+namespace test {
+
+MockQuicClientPromisedInfo::MockQuicClientPromisedInfo(
+ QuicSpdyClientSessionBase* session,
+ QuicStreamId id,
+ QuicString url)
+ : QuicClientPromisedInfo(session, id, url) {}
+
+MockQuicClientPromisedInfo::~MockQuicClientPromisedInfo() {}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/mock_quic_client_promised_info.h b/quic/test_tools/mock_quic_client_promised_info.h
new file mode 100644
index 0000000..a157ac5
--- /dev/null
+++ b/quic/test_tools/mock_quic_client_promised_info.h
@@ -0,0 +1,33 @@
+// Copyright 2016 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_CLIENT_PROMISED_INFO_H_
+#define QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_CLIENT_PROMISED_INFO_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+namespace test {
+
+class MockQuicClientPromisedInfo : public QuicClientPromisedInfo {
+ public:
+ MockQuicClientPromisedInfo(QuicSpdyClientSessionBase* session,
+ QuicStreamId id,
+ QuicString url);
+ ~MockQuicClientPromisedInfo() override;
+
+ MOCK_METHOD2(HandleClientRequest,
+ QuicAsyncStatus(const spdy::SpdyHeaderBlock& headers,
+ QuicClientPushPromiseIndex::Delegate* delegate));
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_CLIENT_PROMISED_INFO_H_
diff --git a/quic/test_tools/mock_quic_dispatcher.cc b/quic/test_tools/mock_quic_dispatcher.cc
new file mode 100644
index 0000000..3b3ca46
--- /dev/null
+++ b/quic/test_tools/mock_quic_dispatcher.cc
@@ -0,0 +1,32 @@
+// Copyright 2014 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 "net/third_party/quiche/src/quic/test_tools/mock_quic_dispatcher.h"
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+
+MockQuicDispatcher::MockQuicDispatcher(
+ 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,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicSimpleDispatcher(config,
+ crypto_config,
+ version_manager,
+ std::move(helper),
+ std::move(session_helper),
+ std::move(alarm_factory),
+ quic_simple_server_backend,
+ kQuicDefaultConnectionIdLength) {}
+
+MockQuicDispatcher::~MockQuicDispatcher() {}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/mock_quic_dispatcher.h b/quic/test_tools/mock_quic_dispatcher.h
new file mode 100644
index 0000000..93acda9
--- /dev/null
+++ b/quic/test_tools/mock_quic_dispatcher.h
@@ -0,0 +1,43 @@
+// Copyright 2014 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_
+#define QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_
+
+#include "base/macros.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h"
+
+namespace quic {
+namespace test {
+
+class MockQuicDispatcher : public QuicSimpleDispatcher {
+ public:
+ MockQuicDispatcher(
+ 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,
+ QuicSimpleServerBackend* quic_simple_server_backend);
+ MockQuicDispatcher(const MockQuicDispatcher&) = delete;
+ MockQuicDispatcher& operator=(const MockQuicDispatcher&) = delete;
+
+ ~MockQuicDispatcher() override;
+
+ MOCK_METHOD3(ProcessPacket,
+ void(const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address,
+ const QuicReceivedPacket& packet));
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_
diff --git a/quic/test_tools/mock_quic_session_visitor.cc b/quic/test_tools/mock_quic_session_visitor.cc
new file mode 100644
index 0000000..fea8414
--- /dev/null
+++ b/quic/test_tools/mock_quic_session_visitor.cc
@@ -0,0 +1,19 @@
+// Copyright (c) 2016 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 "net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.h"
+
+namespace quic {
+namespace test {
+
+MockQuicSessionVisitor::MockQuicSessionVisitor() = default;
+
+MockQuicSessionVisitor::~MockQuicSessionVisitor() = default;
+
+MockQuicCryptoServerStreamHelper::MockQuicCryptoServerStreamHelper() = default;
+
+MockQuicCryptoServerStreamHelper::~MockQuicCryptoServerStreamHelper() = default;
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/mock_quic_session_visitor.h b/quic/test_tools/mock_quic_session_visitor.h
new file mode 100644
index 0000000..fb01da6
--- /dev/null
+++ b/quic/test_tools/mock_quic_session_visitor.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2016 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_SESSION_VISITOR_H_
+#define QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_SESSION_VISITOR_H_
+
+#include "base/macros.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h"
+
+namespace quic {
+namespace test {
+
+class MockQuicSessionVisitor : public QuicTimeWaitListManager::Visitor {
+ public:
+ MockQuicSessionVisitor();
+ MockQuicSessionVisitor(const MockQuicSessionVisitor&) = delete;
+ MockQuicSessionVisitor& operator=(const MockQuicSessionVisitor&) = delete;
+ ~MockQuicSessionVisitor() override;
+ MOCK_METHOD4(OnConnectionClosed,
+ void(QuicConnectionId connection_id,
+ QuicErrorCode error,
+ const QuicString& error_details,
+ ConnectionCloseSource source));
+ MOCK_METHOD1(OnWriteBlocked,
+ void(QuicBlockedWriterInterface* blocked_writer));
+ MOCK_METHOD1(OnRstStreamReceived, void(const QuicRstStreamFrame& frame));
+ MOCK_METHOD1(OnStopSendingReceived, void(const QuicStopSendingFrame& frame));
+ MOCK_METHOD1(OnConnectionAddedToTimeWaitList,
+ void(QuicConnectionId connection_id));
+};
+
+class MockQuicCryptoServerStreamHelper : public QuicCryptoServerStream::Helper {
+ public:
+ MockQuicCryptoServerStreamHelper();
+ MockQuicCryptoServerStreamHelper(const MockQuicCryptoServerStreamHelper&) =
+ delete;
+ MockQuicCryptoServerStreamHelper& operator=(
+ const MockQuicCryptoServerStreamHelper&) = delete;
+ ~MockQuicCryptoServerStreamHelper() override;
+ MOCK_CONST_METHOD2(GenerateConnectionIdForReject,
+ QuicConnectionId(QuicTransportVersion version,
+ QuicConnectionId connection_id));
+ MOCK_CONST_METHOD5(CanAcceptClientHello,
+ bool(const CryptoHandshakeMessage& message,
+ const QuicSocketAddress& client_address,
+ const QuicSocketAddress& peer_address,
+ const QuicSocketAddress& self_address,
+ QuicString* error_details));
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_SESSION_VISITOR_H_
diff --git a/quic/test_tools/mock_quic_spdy_client_stream.cc b/quic/test_tools/mock_quic_spdy_client_stream.cc
new file mode 100644
index 0000000..1d6e2f9
--- /dev/null
+++ b/quic/test_tools/mock_quic_spdy_client_stream.cc
@@ -0,0 +1,19 @@
+// Copyright (c) 2016 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 "net/third_party/quiche/src/quic/test_tools/mock_quic_spdy_client_stream.h"
+
+namespace quic {
+namespace test {
+
+MockQuicSpdyClientStream::MockQuicSpdyClientStream(
+ QuicStreamId id,
+ QuicSpdyClientSession* session,
+ StreamType type)
+ : QuicSpdyClientStream(id, session, type) {}
+
+MockQuicSpdyClientStream::~MockQuicSpdyClientStream() {}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/mock_quic_spdy_client_stream.h b/quic/test_tools/mock_quic_spdy_client_stream.h
new file mode 100644
index 0000000..2a5d6ab
--- /dev/null
+++ b/quic/test_tools/mock_quic_spdy_client_stream.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2016 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_SPDY_CLIENT_STREAM_H_
+#define QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_SPDY_CLIENT_STREAM_H_
+
+#include "base/macros.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_header_list.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+namespace test {
+
+class MockQuicSpdyClientStream : public QuicSpdyClientStream {
+ public:
+ MockQuicSpdyClientStream(QuicStreamId id,
+ QuicSpdyClientSession* session,
+ StreamType type);
+ ~MockQuicSpdyClientStream() override;
+
+ MOCK_METHOD1(OnStreamFrame, void(const QuicStreamFrame& frame));
+ MOCK_METHOD3(OnPromiseHeaderList,
+ void(QuicStreamId promised_stream_id,
+ size_t frame_len,
+ const QuicHeaderList& list));
+ MOCK_METHOD0(OnDataAvailable, void());
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_SPDY_CLIENT_STREAM_H_
diff --git a/quic/test_tools/mock_quic_time_wait_list_manager.cc b/quic/test_tools/mock_quic_time_wait_list_manager.cc
new file mode 100644
index 0000000..b1890f0
--- /dev/null
+++ b/quic/test_tools/mock_quic_time_wait_list_manager.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 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 "net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.h"
+
+using testing::_;
+using testing::Invoke;
+
+namespace quic {
+namespace test {
+
+MockTimeWaitListManager::MockTimeWaitListManager(
+ QuicPacketWriter* writer,
+ Visitor* visitor,
+ const QuicClock* clock,
+ QuicAlarmFactory* alarm_factory)
+ : QuicTimeWaitListManager(writer, visitor, clock, alarm_factory) {
+ // Though AddConnectionIdToTimeWait is mocked, we want to retain its
+ // functionality.
+ EXPECT_CALL(*this, AddConnectionIdToTimeWait(_, _, _, _, _))
+ .Times(testing::AnyNumber());
+ ON_CALL(*this, AddConnectionIdToTimeWait(_, _, _, _, _))
+ .WillByDefault(
+ Invoke(this, &MockTimeWaitListManager::
+ QuicTimeWaitListManager_AddConnectionIdToTimeWait));
+}
+
+MockTimeWaitListManager::~MockTimeWaitListManager() = default;
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/mock_quic_time_wait_list_manager.h b/quic/test_tools/mock_quic_time_wait_list_manager.h
new file mode 100644
index 0000000..742fef2
--- /dev/null
+++ b/quic/test_tools/mock_quic_time_wait_list_manager.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_TIME_WAIT_LIST_MANAGER_H_
+#define QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_TIME_WAIT_LIST_MANAGER_H_
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h"
+
+namespace quic {
+namespace test {
+
+class MockTimeWaitListManager : public QuicTimeWaitListManager {
+ public:
+ MockTimeWaitListManager(QuicPacketWriter* writer,
+ Visitor* visitor,
+ const QuicClock* clock,
+ QuicAlarmFactory* alarm_factory);
+ ~MockTimeWaitListManager() override;
+
+ MOCK_METHOD5(AddConnectionIdToTimeWait,
+ void(QuicConnectionId connection_id,
+ bool ietf_quic,
+ QuicTimeWaitListManager::TimeWaitAction action,
+ EncryptionLevel encryption_level,
+ std::vector<std::unique_ptr<QuicEncryptedPacket>>*
+ termination_packets));
+
+ void QuicTimeWaitListManager_AddConnectionIdToTimeWait(
+ QuicConnectionId connection_id,
+ bool ietf_quic,
+ QuicTimeWaitListManager::TimeWaitAction action,
+ EncryptionLevel encryption_level,
+ std::vector<std::unique_ptr<QuicEncryptedPacket>>* termination_packets) {
+ QuicTimeWaitListManager::AddConnectionIdToTimeWait(connection_id, ietf_quic,
+ action, encryption_level,
+ termination_packets);
+ }
+
+ MOCK_METHOD5(ProcessPacket,
+ void(const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address,
+ QuicConnectionId connection_id,
+ PacketHeaderFormat header_format,
+ std::unique_ptr<QuicPerPacketContext> packet_context));
+
+ MOCK_METHOD6(SendVersionNegotiationPacket,
+ void(QuicConnectionId connection_id,
+ bool ietf_quic,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address,
+ std::unique_ptr<QuicPerPacketContext> packet_context));
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_TIME_WAIT_LIST_MANAGER_H_
diff --git a/quic/test_tools/mock_random.cc b/quic/test_tools/mock_random.cc
new file mode 100644
index 0000000..a01a5a7
--- /dev/null
+++ b/quic/test_tools/mock_random.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 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 "net/third_party/quiche/src/quic/test_tools/mock_random.h"
+
+#include <string.h>
+
+namespace quic {
+namespace test {
+
+MockRandom::MockRandom() : base_(0xDEADBEEF), increment_(0) {}
+
+MockRandom::MockRandom(uint32_t base) : base_(base), increment_(0) {}
+
+void MockRandom::RandBytes(void* data, size_t len) {
+ memset(data, increment_ + static_cast<uint8_t>('r'), len);
+}
+
+uint64_t MockRandom::RandUint64() {
+ return base_ + increment_;
+}
+
+void MockRandom::ChangeValue() {
+ increment_++;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/mock_random.h b/quic/test_tools/mock_random.h
new file mode 100644
index 0000000..e8229c0
--- /dev/null
+++ b/quic/test_tools/mock_random.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_MOCK_RANDOM_H_
+#define QUICHE_QUIC_TEST_TOOLS_MOCK_RANDOM_H_
+
+#include "base/macros.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+
+namespace quic {
+namespace test {
+
+class MockRandom : public QuicRandom {
+ public:
+ // Initializes base_ to 0xDEADBEEF.
+ MockRandom();
+ explicit MockRandom(uint32_t base);
+ MockRandom(const MockRandom&) = delete;
+ MockRandom& operator=(const MockRandom&) = delete;
+
+ // QuicRandom:
+ // Fills the |data| buffer with a repeating byte, initially 'r'.
+ void RandBytes(void* data, size_t len) override;
+ // Returns base + the current increment.
+ uint64_t RandUint64() override;
+
+ // ChangeValue increments |increment_|. This causes the value returned by
+ // |RandUint64| and the byte that |RandBytes| fills with, to change.
+ void ChangeValue();
+
+ private:
+ uint32_t base_;
+ uint8_t increment_;
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_MOCK_RANDOM_H_
diff --git a/quic/test_tools/packet_dropping_test_writer.cc b/quic/test_tools/packet_dropping_test_writer.cc
new file mode 100644
index 0000000..87935d5
--- /dev/null
+++ b/quic/test_tools/packet_dropping_test_writer.cc
@@ -0,0 +1,252 @@
+// Copyright 2013 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 "net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/quic/platform/impl/quic_socket_utils.h"
+
+namespace quic {
+namespace test {
+
+const int32_t kMaxConsecutivePacketLoss = 3;
+
+// An alarm that is scheduled if a blocked socket is simulated to indicate
+// it's writable again.
+class WriteUnblockedAlarm : public QuicAlarm::Delegate {
+ public:
+ explicit WriteUnblockedAlarm(PacketDroppingTestWriter* writer)
+ : writer_(writer) {}
+
+ void OnAlarm() override {
+ QUIC_DLOG(INFO) << "Unblocking socket.";
+ writer_->OnCanWrite();
+ }
+
+ private:
+ PacketDroppingTestWriter* writer_;
+};
+
+// An alarm that is scheduled every time a new packet is to be written at a
+// later point.
+class DelayAlarm : public QuicAlarm::Delegate {
+ public:
+ explicit DelayAlarm(PacketDroppingTestWriter* writer) : writer_(writer) {}
+
+ void OnAlarm() override {
+ QuicTime new_deadline = writer_->ReleaseOldPackets();
+ if (new_deadline.IsInitialized()) {
+ writer_->SetDelayAlarm(new_deadline);
+ }
+ }
+
+ private:
+ PacketDroppingTestWriter* writer_;
+};
+
+PacketDroppingTestWriter::PacketDroppingTestWriter()
+ : clock_(nullptr),
+ cur_buffer_size_(0),
+ num_calls_to_write_(0),
+ fake_packet_loss_percentage_(0),
+ fake_drop_first_n_packets_(0),
+ fake_blocked_socket_percentage_(0),
+ fake_packet_reorder_percentage_(0),
+ fake_packet_delay_(QuicTime::Delta::Zero()),
+ fake_bandwidth_(QuicBandwidth::Zero()),
+ buffer_size_(0),
+ num_consecutive_packet_lost_(0) {
+ uint64_t seed = QuicRandom::GetInstance()->RandUint64();
+ QUIC_LOG(INFO) << "Seeding packet loss with " << seed;
+ simple_random_.set_seed(seed);
+}
+
+PacketDroppingTestWriter::~PacketDroppingTestWriter() = default;
+
+void PacketDroppingTestWriter::Initialize(
+ QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory,
+ std::unique_ptr<Delegate> on_can_write) {
+ clock_ = helper->GetClock();
+ write_unblocked_alarm_.reset(
+ alarm_factory->CreateAlarm(new WriteUnblockedAlarm(this)));
+ delay_alarm_.reset(alarm_factory->CreateAlarm(new DelayAlarm(this)));
+ on_can_write_ = std::move(on_can_write);
+}
+
+WriteResult PacketDroppingTestWriter::WritePacket(
+ const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) {
+ ++num_calls_to_write_;
+ ReleaseOldPackets();
+
+ QuicWriterMutexLock lock(&config_mutex_);
+ if (fake_drop_first_n_packets_ > 0 &&
+ num_calls_to_write_ <=
+ static_cast<uint64_t>(fake_drop_first_n_packets_)) {
+ QUIC_DVLOG(1) << "Dropping first " << fake_drop_first_n_packets_
+ << " packets (packet number " << num_calls_to_write_ << ")";
+ return WriteResult(WRITE_STATUS_OK, buf_len);
+ }
+ const int32_t kMaxPacketLossPercentage =
+ kMaxConsecutivePacketLoss * 100.0 / (kMaxConsecutivePacketLoss + 1);
+ if (fake_packet_loss_percentage_ > 0 &&
+ // Do not allow too many consecutive packet drops to avoid test flakiness.
+ (num_consecutive_packet_lost_ <= kMaxConsecutivePacketLoss ||
+ // Allow as many consecutive packet drops as possbile if
+ // |fake_packet_lost_percentage_| is large enough. Without this exception
+ // it is hard to simulate high loss rate, like 100%.
+ fake_packet_loss_percentage_ > kMaxPacketLossPercentage) &&
+ (simple_random_.RandUint64() % 100 <
+ static_cast<uint64_t>(fake_packet_loss_percentage_))) {
+ QUIC_DVLOG(1) << "Dropping packet.";
+ ++num_consecutive_packet_lost_;
+ return WriteResult(WRITE_STATUS_OK, buf_len);
+ } else {
+ num_consecutive_packet_lost_ = 0;
+ }
+ if (fake_blocked_socket_percentage_ > 0 &&
+ simple_random_.RandUint64() % 100 <
+ static_cast<uint64_t>(fake_blocked_socket_percentage_)) {
+ CHECK(on_can_write_ != nullptr);
+ QUIC_DVLOG(1) << "Blocking socket.";
+ if (!write_unblocked_alarm_->IsSet()) {
+ // Set the alarm to fire immediately.
+ write_unblocked_alarm_->Set(clock_->ApproximateNow());
+ }
+ return WriteResult(WRITE_STATUS_BLOCKED, EAGAIN);
+ }
+
+ if (!fake_packet_delay_.IsZero() || !fake_bandwidth_.IsZero()) {
+ if (buffer_size_ > 0 && buf_len + cur_buffer_size_ > buffer_size_) {
+ // Drop packets which do not fit into the buffer.
+ QUIC_DVLOG(1) << "Dropping packet because the buffer is full.";
+ return WriteResult(WRITE_STATUS_OK, buf_len);
+ }
+
+ // Queue it to be sent.
+ QuicTime send_time = clock_->ApproximateNow() + fake_packet_delay_;
+ if (!fake_bandwidth_.IsZero()) {
+ // Calculate a time the bandwidth limit would impose.
+ QuicTime::Delta bandwidth_delay = QuicTime::Delta::FromMicroseconds(
+ (buf_len * kNumMicrosPerSecond) / fake_bandwidth_.ToBytesPerSecond());
+ send_time = delayed_packets_.empty()
+ ? send_time + bandwidth_delay
+ : delayed_packets_.back().send_time + bandwidth_delay;
+ }
+ std::unique_ptr<PerPacketOptions> delayed_options;
+ if (options != nullptr) {
+ delayed_options = options->Clone();
+ }
+ delayed_packets_.push_back(
+ DelayedWrite(buffer, buf_len, self_address, peer_address,
+ std::move(delayed_options), send_time));
+ cur_buffer_size_ += buf_len;
+
+ // Set the alarm if it's not yet set.
+ if (!delay_alarm_->IsSet()) {
+ delay_alarm_->Set(send_time);
+ }
+
+ return WriteResult(WRITE_STATUS_OK, buf_len);
+ }
+
+ return QuicPacketWriterWrapper::WritePacket(buffer, buf_len, self_address,
+ peer_address, options);
+}
+
+bool PacketDroppingTestWriter::IsWriteBlocked() const {
+ if (write_unblocked_alarm_ != nullptr && write_unblocked_alarm_->IsSet()) {
+ return true;
+ }
+ return QuicPacketWriterWrapper::IsWriteBlocked();
+}
+
+void PacketDroppingTestWriter::SetWritable() {
+ if (write_unblocked_alarm_ != nullptr && write_unblocked_alarm_->IsSet()) {
+ write_unblocked_alarm_->Cancel();
+ }
+ QuicPacketWriterWrapper::SetWritable();
+}
+
+QuicTime PacketDroppingTestWriter::ReleaseNextPacket() {
+ if (delayed_packets_.empty()) {
+ return QuicTime::Zero();
+ }
+ QuicReaderMutexLock lock(&config_mutex_);
+ auto iter = delayed_packets_.begin();
+ // Determine if we should re-order.
+ if (delayed_packets_.size() > 1 && fake_packet_reorder_percentage_ > 0 &&
+ simple_random_.RandUint64() % 100 <
+ static_cast<uint64_t>(fake_packet_reorder_percentage_)) {
+ QUIC_DLOG(INFO) << "Reordering packets.";
+ ++iter;
+ // Swap the send times when re-ordering packets.
+ delayed_packets_.begin()->send_time = iter->send_time;
+ }
+
+ QUIC_DVLOG(1) << "Releasing packet. " << (delayed_packets_.size() - 1)
+ << " remaining.";
+ // Grab the next one off the queue and send it.
+ QuicPacketWriterWrapper::WritePacket(
+ iter->buffer.data(), iter->buffer.length(), iter->self_address,
+ iter->peer_address, iter->options.get());
+ DCHECK_GE(cur_buffer_size_, iter->buffer.length());
+ cur_buffer_size_ -= iter->buffer.length();
+ delayed_packets_.erase(iter);
+
+ // If there are others, find the time for the next to be sent.
+ if (delayed_packets_.empty()) {
+ return QuicTime::Zero();
+ }
+ return delayed_packets_.begin()->send_time;
+}
+
+QuicTime PacketDroppingTestWriter::ReleaseOldPackets() {
+ while (!delayed_packets_.empty()) {
+ QuicTime next_send_time = delayed_packets_.front().send_time;
+ if (next_send_time > clock_->Now()) {
+ return next_send_time;
+ }
+ ReleaseNextPacket();
+ }
+ return QuicTime::Zero();
+}
+
+void PacketDroppingTestWriter::SetDelayAlarm(QuicTime new_deadline) {
+ delay_alarm_->Set(new_deadline);
+}
+
+void PacketDroppingTestWriter::OnCanWrite() {
+ on_can_write_->OnCanWrite();
+}
+
+void PacketDroppingTestWriter::set_fake_packet_loss_percentage(
+ int32_t fake_packet_loss_percentage) {
+ QuicWriterMutexLock lock(&config_mutex_);
+ fake_packet_loss_percentage_ = fake_packet_loss_percentage;
+ num_consecutive_packet_lost_ = 0;
+}
+
+PacketDroppingTestWriter::DelayedWrite::DelayedWrite(
+ const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ std::unique_ptr<PerPacketOptions> options,
+ QuicTime send_time)
+ : buffer(buffer, buf_len),
+ self_address(self_address),
+ peer_address(peer_address),
+ options(std::move(options)),
+ send_time(send_time) {}
+
+PacketDroppingTestWriter::DelayedWrite::~DelayedWrite() = default;
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/packet_dropping_test_writer.h b/quic/test_tools/packet_dropping_test_writer.h
new file mode 100644
index 0000000..9537944
--- /dev/null
+++ b/quic/test_tools/packet_dropping_test_writer.h
@@ -0,0 +1,177 @@
+// Copyright 2013 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_PACKET_DROPPING_TEST_WRITER_H_
+#define QUICHE_QUIC_TEST_TOOLS_PACKET_DROPPING_TEST_WRITER_H_
+
+#include <cstdint>
+#include <list>
+#include <memory>
+
+#include "base/macros.h"
+#include "net/third_party/quiche/src/quic/core/quic_alarm.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_macros.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_client.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+
+// Simulates a connection that drops packets a configured percentage of the time
+// and has a blocked socket a configured percentage of the time. Also provides
+// the options to delay packets and reorder packets if delay is enabled.
+class PacketDroppingTestWriter : public QuicPacketWriterWrapper {
+ public:
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+ virtual void OnCanWrite() = 0;
+ };
+
+ PacketDroppingTestWriter();
+ PacketDroppingTestWriter(const PacketDroppingTestWriter&) = delete;
+ PacketDroppingTestWriter& operator=(const PacketDroppingTestWriter&) = delete;
+
+ ~PacketDroppingTestWriter() override;
+
+ // Must be called before blocking, reordering or delaying (loss is OK). May be
+ // called after connecting if the helper is not available before.
+ // |on_can_write| will be triggered when fake-unblocking.
+ void Initialize(QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory,
+ std::unique_ptr<Delegate> on_can_write);
+
+ // QuicPacketWriter methods:
+ WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) override;
+
+ bool IsWriteBlocked() const override;
+
+ void SetWritable() override;
+
+ char* GetNextWriteLocation(const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address) override {
+ // If the wrapped writer supports zero-copy, disable it, because it is not
+ // compatible with delayed writes in this class.
+ return nullptr;
+ }
+
+ // Writes out any packet which should have been sent by now
+ // to the contained writer and returns the time
+ // for the next delayed packet to be written.
+ QuicTime ReleaseOldPackets();
+
+ // Sets |delay_alarm_| to fire at |new_deadline|.
+ void SetDelayAlarm(QuicTime new_deadline);
+
+ void OnCanWrite();
+
+ // The percent of time a packet is simulated as being lost.
+ void set_fake_packet_loss_percentage(int32_t fake_packet_loss_percentage);
+
+ // Simulate dropping the first n packets unconditionally.
+ // Subsequent packets will be lost at fake_packet_loss_percentage_ if set.
+ void set_fake_drop_first_n_packets(int32_t fake_drop_first_n_packets) {
+ QuicWriterMutexLock lock(&config_mutex_);
+ fake_drop_first_n_packets_ = fake_drop_first_n_packets;
+ }
+
+ // The percent of time WritePacket will block and set WriteResult's status
+ // to WRITE_STATUS_BLOCKED.
+ void set_fake_blocked_socket_percentage(
+ int32_t fake_blocked_socket_percentage) {
+ DCHECK(clock_);
+ QuicWriterMutexLock lock(&config_mutex_);
+ fake_blocked_socket_percentage_ = fake_blocked_socket_percentage;
+ }
+
+ // The percent of time a packet is simulated as being reordered.
+ void set_fake_reorder_percentage(int32_t fake_packet_reorder_percentage) {
+ DCHECK(clock_);
+ QuicWriterMutexLock lock(&config_mutex_);
+ DCHECK(!fake_packet_delay_.IsZero());
+ fake_packet_reorder_percentage_ = fake_packet_reorder_percentage;
+ }
+
+ // The delay before writing this packet.
+ void set_fake_packet_delay(QuicTime::Delta fake_packet_delay) {
+ DCHECK(clock_);
+ QuicWriterMutexLock lock(&config_mutex_);
+ fake_packet_delay_ = fake_packet_delay;
+ }
+
+ // The maximum bandwidth and buffer size of the connection. When these are
+ // set, packets will be delayed until a connection with that bandwidth would
+ // transmit it. Once the |buffer_size| is reached, all new packets are
+ // dropped.
+ void set_max_bandwidth_and_buffer_size(QuicBandwidth fake_bandwidth,
+ QuicByteCount buffer_size) {
+ DCHECK(clock_);
+ QuicWriterMutexLock lock(&config_mutex_);
+ fake_bandwidth_ = fake_bandwidth;
+ buffer_size_ = buffer_size;
+ }
+
+ // Useful for reproducing very flaky issues.
+ QUIC_UNUSED void set_seed(uint64_t seed) { simple_random_.set_seed(seed); }
+
+ private:
+ // Writes out the next packet to the contained writer and returns the time
+ // for the next delayed packet to be written.
+ QuicTime ReleaseNextPacket();
+
+ // A single packet which will be sent at the supplied send_time.
+ struct DelayedWrite {
+ public:
+ DelayedWrite(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ std::unique_ptr<PerPacketOptions> options,
+ QuicTime send_time);
+ DelayedWrite(const DelayedWrite&) = delete;
+ DelayedWrite(DelayedWrite&&) = default;
+ DelayedWrite& operator=(const DelayedWrite&) = delete;
+ DelayedWrite& operator=(DelayedWrite&&) = default;
+ ~DelayedWrite();
+
+ QuicString buffer;
+ QuicIpAddress self_address;
+ QuicSocketAddress peer_address;
+ std::unique_ptr<PerPacketOptions> options;
+ QuicTime send_time;
+ };
+
+ typedef std::list<DelayedWrite> DelayedPacketList;
+
+ const QuicClock* clock_;
+ std::unique_ptr<QuicAlarm> write_unblocked_alarm_;
+ std::unique_ptr<QuicAlarm> delay_alarm_;
+ std::unique_ptr<Delegate> on_can_write_;
+ SimpleRandom simple_random_;
+ // Stored packets delayed by fake packet delay or bandwidth restrictions.
+ DelayedPacketList delayed_packets_;
+ QuicByteCount cur_buffer_size_;
+ uint64_t num_calls_to_write_;
+
+ QuicMutex config_mutex_;
+ int32_t fake_packet_loss_percentage_ GUARDED_BY(config_mutex_);
+ int32_t fake_drop_first_n_packets_ GUARDED_BY(config_mutex_);
+ int32_t fake_blocked_socket_percentage_ GUARDED_BY(config_mutex_);
+ int32_t fake_packet_reorder_percentage_ GUARDED_BY(config_mutex_);
+ QuicTime::Delta fake_packet_delay_ GUARDED_BY(config_mutex_);
+ QuicBandwidth fake_bandwidth_ GUARDED_BY(config_mutex_);
+ QuicByteCount buffer_size_ GUARDED_BY(config_mutex_);
+ int32_t num_consecutive_packet_lost_ GUARDED_BY(config_mutex_);
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_PACKET_DROPPING_TEST_WRITER_H_
diff --git a/quic/test_tools/packet_reordering_writer.cc b/quic/test_tools/packet_reordering_writer.cc
new file mode 100644
index 0000000..a8385d8
--- /dev/null
+++ b/quic/test_tools/packet_reordering_writer.cc
@@ -0,0 +1,53 @@
+// Copyright 2013 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 "net/third_party/quiche/src/quic/test_tools/packet_reordering_writer.h"
+
+namespace quic {
+namespace test {
+
+PacketReorderingWriter::PacketReorderingWriter() = default;
+
+PacketReorderingWriter::~PacketReorderingWriter() = default;
+
+WriteResult PacketReorderingWriter::WritePacket(
+ const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) {
+ if (!delay_next_) {
+ VLOG(2) << "Writing a non-delayed packet";
+ WriteResult wr = QuicPacketWriterWrapper::WritePacket(
+ buffer, buf_len, self_address, peer_address, options);
+ --num_packets_to_wait_;
+ if (num_packets_to_wait_ == 0) {
+ VLOG(2) << "Writing a delayed packet";
+ // It's time to write the delayed packet.
+ QuicPacketWriterWrapper::WritePacket(
+ delayed_data_.data(), delayed_data_.length(), delayed_self_address_,
+ delayed_peer_address_, delayed_options_.get());
+ }
+ return wr;
+ }
+ // Still have packet to wait.
+ DCHECK_LT(0u, num_packets_to_wait_) << "Only allow one packet to be delayed";
+ delayed_data_ = QuicString(buffer, buf_len);
+ delayed_self_address_ = self_address;
+ delayed_peer_address_ = peer_address;
+ if (options != nullptr) {
+ delayed_options_ = options->Clone();
+ }
+ delay_next_ = false;
+ return WriteResult(WRITE_STATUS_OK, buf_len);
+}
+
+void PacketReorderingWriter::SetDelay(size_t num_packets_to_wait) {
+ DCHECK_GT(num_packets_to_wait, 0u);
+ num_packets_to_wait_ = num_packets_to_wait;
+ delay_next_ = true;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/packet_reordering_writer.h b/quic/test_tools/packet_reordering_writer.h
new file mode 100644
index 0000000..ec49269
--- /dev/null
+++ b/quic/test_tools/packet_reordering_writer.h
@@ -0,0 +1,45 @@
+// Copyright 2013 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_PACKET_REORDERING_WRITER_H_
+#define QUICHE_QUIC_TEST_TOOLS_PACKET_REORDERING_WRITER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h"
+
+namespace quic {
+
+namespace test {
+
+// This packet writer allows delaying writing the next packet after
+// SetDelay(num_packets_to_wait)
+// is called and buffer this packet and write it after it writes next
+// |num_packets_to_wait| packets. It doesn't support delaying a packet while
+// there is already a packet delayed.
+class PacketReorderingWriter : public QuicPacketWriterWrapper {
+ public:
+ PacketReorderingWriter();
+
+ ~PacketReorderingWriter() override;
+
+ WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) override;
+
+ void SetDelay(size_t num_packets_to_wait);
+
+ private:
+ bool delay_next_ = false;
+ size_t num_packets_to_wait_ = 0;
+ QuicString delayed_data_;
+ QuicIpAddress delayed_self_address_;
+ QuicSocketAddress delayed_peer_address_;
+ std::unique_ptr<PerPacketOptions> delayed_options_;
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_PACKET_REORDERING_WRITER_H_
diff --git a/quic/test_tools/quic_buffered_packet_store_peer.cc b/quic/test_tools/quic_buffered_packet_store_peer.cc
new file mode 100644
index 0000000..73b9e98
--- /dev/null
+++ b/quic/test_tools/quic_buffered_packet_store_peer.cc
@@ -0,0 +1,25 @@
+// Copyright 2016 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 "net/third_party/quiche/src/quic/test_tools/quic_buffered_packet_store_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_buffered_packet_store.h"
+
+namespace quic {
+namespace test {
+
+// static
+QuicAlarm* QuicBufferedPacketStorePeer::expiration_alarm(
+ QuicBufferedPacketStore* store) {
+ return store->expiration_alarm_.get();
+}
+
+// static
+void QuicBufferedPacketStorePeer::set_clock(QuicBufferedPacketStore* store,
+ const QuicClock* clock) {
+ store->clock_ = clock;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_buffered_packet_store_peer.h b/quic/test_tools/quic_buffered_packet_store_peer.h
new file mode 100644
index 0000000..e857dfb
--- /dev/null
+++ b/quic/test_tools/quic_buffered_packet_store_peer.h
@@ -0,0 +1,32 @@
+// Copyright 2016 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_BUFFERED_PACKET_STORE_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_BUFFERED_PACKET_STORE_PEER_H_
+
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/quic_alarm.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+
+namespace quic {
+
+class QuicBufferedPacketStore;
+
+namespace test {
+
+class QuicBufferedPacketStorePeer {
+ public:
+ QuicBufferedPacketStorePeer() = delete;
+
+ static QuicAlarm* expiration_alarm(QuicBufferedPacketStore* store);
+
+ static void set_clock(QuicBufferedPacketStore* store, const QuicClock* clock);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_BUFFERED_PACKET_STORE_PEER_H_
diff --git a/quic/test_tools/quic_client_peer.cc b/quic/test_tools/quic_client_peer.cc
new file mode 100644
index 0000000..c8bfa6a
--- /dev/null
+++ b/quic/test_tools/quic_client_peer.cc
@@ -0,0 +1,35 @@
+// Copyright (c) 2013 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 "net/third_party/quiche/src/quic/test_tools/quic_client_peer.h"
+
+#include "net/third_party/quiche/src/quic/tools/quic_client.h"
+
+namespace quic {
+namespace test {
+
+// static
+bool QuicClientPeer::CreateUDPSocketAndBind(QuicClient* client) {
+ return client->network_helper()->CreateUDPSocketAndBind(
+ client->server_address(), client->bind_to_address(),
+ client->local_port());
+}
+
+// static
+void QuicClientPeer::CleanUpUDPSocket(QuicClient* client, int fd) {
+ client->epoll_network_helper()->CleanUpUDPSocket(fd);
+}
+
+// static
+void QuicClientPeer::SetClientPort(QuicClient* client, int port) {
+ client->epoll_network_helper()->SetClientPort(port);
+}
+
+// static
+void QuicClientPeer::SetWriter(QuicClient* client, QuicPacketWriter* writer) {
+ client->set_writer(writer);
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_client_peer.h b/quic/test_tools/quic_client_peer.h
new file mode 100644
index 0000000..66987e4
--- /dev/null
+++ b/quic/test_tools/quic_client_peer.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2013 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_CLIENT_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_CLIENT_PEER_H_
+
+#include "base/macros.h"
+
+namespace quic {
+
+class QuicClient;
+class QuicPacketWriter;
+
+namespace test {
+
+class QuicClientPeer {
+ public:
+ QuicClientPeer() = delete;
+
+ static bool CreateUDPSocketAndBind(QuicClient* client);
+ static void CleanUpUDPSocket(QuicClient* client, int fd);
+ static void SetClientPort(QuicClient* client, int port);
+ static void SetWriter(QuicClient* client, QuicPacketWriter* writer);
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_CLIENT_PEER_H_
diff --git a/quic/test_tools/quic_client_promised_info_peer.cc b/quic/test_tools/quic_client_promised_info_peer.cc
new file mode 100644
index 0000000..91fec1d
--- /dev/null
+++ b/quic/test_tools/quic_client_promised_info_peer.cc
@@ -0,0 +1,17 @@
+// Copyright 2016 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 "net/third_party/quiche/src/quic/test_tools/quic_client_promised_info_peer.h"
+
+namespace quic {
+namespace test {
+
+// static
+QuicAlarm* QuicClientPromisedInfoPeer::GetAlarm(
+ QuicClientPromisedInfo* promised_stream) {
+ return promised_stream->cleanup_alarm_.get();
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_client_promised_info_peer.h b/quic/test_tools/quic_client_promised_info_peer.h
new file mode 100644
index 0000000..9b743f3
--- /dev/null
+++ b/quic/test_tools/quic_client_promised_info_peer.h
@@ -0,0 +1,23 @@
+// Copyright 2016 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_CLIENT_PROMISED_INFO_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_CLIENT_PROMISED_INFO_PEER_H_
+
+#include "base/macros.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h"
+
+namespace quic {
+namespace test {
+
+class QuicClientPromisedInfoPeer {
+ public:
+ QuicClientPromisedInfoPeer() = delete;
+
+ static QuicAlarm* GetAlarm(QuicClientPromisedInfo* promised_stream);
+};
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_CLIENT_PROMISED_INFO_PEER_H_
diff --git a/quic/test_tools/quic_config_peer.cc b/quic/test_tools/quic_config_peer.cc
new file mode 100644
index 0000000..f5a5b31
--- /dev/null
+++ b/quic/test_tools/quic_config_peer.cc
@@ -0,0 +1,67 @@
+// Copyright 2014 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 "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+
+namespace quic {
+namespace test {
+
+// static
+void QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(
+ QuicConfig* config,
+ uint32_t window_bytes) {
+ config->initial_stream_flow_control_window_bytes_.SetReceivedValue(
+ window_bytes);
+}
+
+// static
+void QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(
+ QuicConfig* config,
+ uint32_t window_bytes) {
+ config->initial_session_flow_control_window_bytes_.SetReceivedValue(
+ window_bytes);
+}
+
+// static
+void QuicConfigPeer::SetReceivedConnectionOptions(
+ QuicConfig* config,
+ const QuicTagVector& options) {
+ config->connection_options_.SetReceivedValues(options);
+}
+
+// static
+void QuicConfigPeer::SetReceivedBytesForConnectionId(QuicConfig* config,
+ uint32_t bytes) {
+ DCHECK(bytes == 0 || bytes == 8);
+ config->bytes_for_connection_id_.SetReceivedValue(bytes);
+}
+
+// static
+void QuicConfigPeer::SetReceivedDisableConnectionMigration(QuicConfig* config) {
+ config->connection_migration_disabled_.SetReceivedValue(1);
+}
+
+// static
+void QuicConfigPeer::SetReceivedMaxIncomingDynamicStreams(
+ QuicConfig* config,
+ uint32_t max_streams) {
+ config->max_incoming_dynamic_streams_.SetReceivedValue(max_streams);
+}
+
+// static
+void QuicConfigPeer::SetConnectionOptionsToSend(QuicConfig* config,
+ const QuicTagVector& options) {
+ config->SetConnectionOptionsToSend(options);
+}
+
+// static
+void QuicConfigPeer::SetReceivedStatelessResetToken(QuicConfig* config,
+ QuicUint128 token) {
+ config->stateless_reset_token_.SetReceivedValue(token);
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_config_peer.h b/quic/test_tools/quic_config_peer.h
new file mode 100644
index 0000000..6e2653b
--- /dev/null
+++ b/quic/test_tools/quic_config_peer.h
@@ -0,0 +1,51 @@
+// Copyright 2014 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_CONFIG_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_CONFIG_PEER_H_
+
+#include "base/macros.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h"
+
+namespace quic {
+
+class QuicConfig;
+
+namespace test {
+
+class QuicConfigPeer {
+ public:
+ QuicConfigPeer() = delete;
+
+ static void SetReceivedInitialStreamFlowControlWindow(QuicConfig* config,
+ uint32_t window_bytes);
+
+ static void SetReceivedInitialSessionFlowControlWindow(QuicConfig* config,
+ uint32_t window_bytes);
+
+ static void SetReceivedConnectionOptions(QuicConfig* config,
+ const QuicTagVector& options);
+
+ static void SetReceivedBytesForConnectionId(QuicConfig* config,
+ uint32_t bytes);
+
+ static void SetReceivedDisableConnectionMigration(QuicConfig* config);
+
+ static void SetReceivedMaxIncomingDynamicStreams(QuicConfig* config,
+ uint32_t max_streams);
+
+ static void SetConnectionOptionsToSend(QuicConfig* config,
+ const QuicTagVector& options);
+
+ static void SetReceivedStatelessResetToken(QuicConfig* config,
+ QuicUint128 token);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_CONFIG_PEER_H_
diff --git a/quic/test_tools/quic_connection_peer.cc b/quic/test_tools/quic_connection_peer.cc
new file mode 100644
index 0000000..100fe15
--- /dev/null
+++ b/quic/test_tools/quic_connection_peer.cc
@@ -0,0 +1,355 @@
+// Copyright (c) 2012 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 "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_received_packet_manager.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_packet_generator_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h"
+
+namespace quic {
+namespace test {
+
+// static
+void QuicConnectionPeer::SendAck(QuicConnection* connection) {
+ connection->SendAck();
+}
+
+// static
+void QuicConnectionPeer::SetSendAlgorithm(
+ QuicConnection* connection,
+ SendAlgorithmInterface* send_algorithm) {
+ GetSentPacketManager(connection)->SetSendAlgorithm(send_algorithm);
+}
+
+// static
+void QuicConnectionPeer::SetLossAlgorithm(
+ QuicConnection* connection,
+ LossDetectionInterface* loss_algorithm) {
+ GetSentPacketManager(connection)->loss_algorithm_ = loss_algorithm;
+}
+
+// static
+const QuicFrame QuicConnectionPeer::GetUpdatedAckFrame(
+ QuicConnection* connection) {
+ const bool ack_frame_updated = connection->ack_frame_updated();
+ const QuicFrame ack_frame = connection->GetUpdatedAckFrame();
+ connection->received_packet_manager_.ack_frame_updated_ = ack_frame_updated;
+ return ack_frame;
+}
+
+// static
+void QuicConnectionPeer::PopulateStopWaitingFrame(
+ QuicConnection* connection,
+ QuicStopWaitingFrame* stop_waiting) {
+ connection->PopulateStopWaitingFrame(stop_waiting);
+}
+
+// static
+QuicConnectionVisitorInterface* QuicConnectionPeer::GetVisitor(
+ QuicConnection* connection) {
+ return connection->visitor_;
+}
+
+// static
+QuicPacketCreator* QuicConnectionPeer::GetPacketCreator(
+ QuicConnection* connection) {
+ return QuicPacketGeneratorPeer::GetPacketCreator(
+ &connection->packet_generator_);
+}
+
+// static
+QuicPacketGenerator* QuicConnectionPeer::GetPacketGenerator(
+ QuicConnection* connection) {
+ return &connection->packet_generator_;
+}
+
+// static
+QuicSentPacketManager* QuicConnectionPeer::GetSentPacketManager(
+ QuicConnection* connection) {
+ return &connection->sent_packet_manager_;
+}
+
+// static
+QuicTime::Delta QuicConnectionPeer::GetNetworkTimeout(
+ QuicConnection* connection) {
+ return connection->idle_network_timeout_;
+}
+
+// static
+void QuicConnectionPeer::SetPerspective(QuicConnection* connection,
+ Perspective perspective) {
+ connection->perspective_ = perspective;
+ QuicFramerPeer::SetPerspective(&connection->framer_, perspective);
+}
+
+// static
+void QuicConnectionPeer::SetSelfAddress(QuicConnection* connection,
+ const QuicSocketAddress& self_address) {
+ connection->self_address_ = self_address;
+}
+
+// static
+void QuicConnectionPeer::SetPeerAddress(QuicConnection* connection,
+ const QuicSocketAddress& peer_address) {
+ connection->peer_address_ = peer_address;
+}
+
+// static
+void QuicConnectionPeer::SetDirectPeerAddress(
+ QuicConnection* connection,
+ const QuicSocketAddress& direct_peer_address) {
+ connection->direct_peer_address_ = direct_peer_address;
+}
+
+// static
+void QuicConnectionPeer::SetEffectivePeerAddress(
+ QuicConnection* connection,
+ const QuicSocketAddress& effective_peer_address) {
+ connection->effective_peer_address_ = effective_peer_address;
+}
+
+// static
+bool QuicConnectionPeer::IsSilentCloseEnabled(QuicConnection* connection) {
+ return connection->idle_timeout_connection_close_behavior_ ==
+ ConnectionCloseBehavior::SILENT_CLOSE;
+}
+
+// static
+void QuicConnectionPeer::SwapCrypters(QuicConnection* connection,
+ QuicFramer* framer) {
+ QuicFramerPeer::SwapCrypters(framer, &connection->framer_);
+}
+
+// static
+void QuicConnectionPeer::SetCurrentPacket(QuicConnection* connection,
+ QuicStringPiece current_packet) {
+ connection->current_packet_data_ = current_packet.data();
+ connection->last_size_ = current_packet.size();
+}
+
+// static
+QuicConnectionHelperInterface* QuicConnectionPeer::GetHelper(
+ QuicConnection* connection) {
+ return connection->helper_;
+}
+
+// static
+QuicAlarmFactory* QuicConnectionPeer::GetAlarmFactory(
+ QuicConnection* connection) {
+ return connection->alarm_factory_;
+}
+
+// static
+QuicFramer* QuicConnectionPeer::GetFramer(QuicConnection* connection) {
+ return &connection->framer_;
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetAckAlarm(QuicConnection* connection) {
+ return connection->ack_alarm_.get();
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetPingAlarm(QuicConnection* connection) {
+ return connection->ping_alarm_.get();
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetRetransmissionAlarm(
+ QuicConnection* connection) {
+ return connection->retransmission_alarm_.get();
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetSendAlarm(QuicConnection* connection) {
+ return connection->send_alarm_.get();
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetTimeoutAlarm(QuicConnection* connection) {
+ return connection->timeout_alarm_.get();
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetMtuDiscoveryAlarm(
+ QuicConnection* connection) {
+ return connection->mtu_discovery_alarm_.get();
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetPathDegradingAlarm(
+ QuicConnection* connection) {
+ return connection->path_degrading_alarm_.get();
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetProcessUndecryptablePacketsAlarm(
+ QuicConnection* connection) {
+ return connection->process_undecryptable_packets_alarm_.get();
+}
+
+// static
+QuicPacketWriter* QuicConnectionPeer::GetWriter(QuicConnection* connection) {
+ return connection->writer_;
+}
+
+// static
+void QuicConnectionPeer::SetWriter(QuicConnection* connection,
+ QuicPacketWriter* writer,
+ bool owns_writer) {
+ if (connection->owns_writer_) {
+ delete connection->writer_;
+ }
+ connection->writer_ = writer;
+ connection->owns_writer_ = owns_writer;
+}
+
+// static
+void QuicConnectionPeer::TearDownLocalConnectionState(
+ QuicConnection* connection) {
+ connection->connected_ = false;
+}
+
+// static
+QuicEncryptedPacket* QuicConnectionPeer::GetConnectionClosePacket(
+ QuicConnection* connection) {
+ if (connection->termination_packets_ == nullptr ||
+ connection->termination_packets_->empty()) {
+ return nullptr;
+ }
+ return (*connection->termination_packets_)[0].get();
+}
+
+// static
+QuicPacketHeader* QuicConnectionPeer::GetLastHeader(
+ QuicConnection* connection) {
+ return &connection->last_header_;
+}
+
+// static
+QuicConnectionStats* QuicConnectionPeer::GetStats(QuicConnection* connection) {
+ return &connection->stats_;
+}
+
+// static
+QuicPacketCount QuicConnectionPeer::GetPacketsBetweenMtuProbes(
+ QuicConnection* connection) {
+ return connection->packets_between_mtu_probes_;
+}
+
+// static
+void QuicConnectionPeer::SetPacketsBetweenMtuProbes(QuicConnection* connection,
+ QuicPacketCount packets) {
+ connection->packets_between_mtu_probes_ = packets;
+}
+
+// static
+void QuicConnectionPeer::SetNextMtuProbeAt(QuicConnection* connection,
+ QuicPacketNumber number) {
+ connection->next_mtu_probe_at_ = number;
+}
+
+// static
+void QuicConnectionPeer::SetAckMode(QuicConnection* connection,
+ AckMode ack_mode) {
+ if (connection->received_packet_manager_.decide_when_to_send_acks()) {
+ connection->received_packet_manager_.ack_mode_ = ack_mode;
+ } else {
+ connection->ack_mode_ = ack_mode;
+ }
+}
+
+// static
+void QuicConnectionPeer::SetFastAckAfterQuiescence(
+ QuicConnection* connection,
+ bool fast_ack_after_quiescence) {
+ if (connection->received_packet_manager_.decide_when_to_send_acks()) {
+ connection->received_packet_manager_.fast_ack_after_quiescence_ =
+ fast_ack_after_quiescence;
+ } else {
+ connection->fast_ack_after_quiescence_ = fast_ack_after_quiescence;
+ }
+}
+
+// static
+void QuicConnectionPeer::SetAckDecimationDelay(QuicConnection* connection,
+ float ack_decimation_delay) {
+ if (connection->received_packet_manager_.decide_when_to_send_acks()) {
+ connection->received_packet_manager_.ack_decimation_delay_ =
+ ack_decimation_delay;
+ } else {
+ connection->ack_decimation_delay_ = ack_decimation_delay;
+ }
+}
+
+// static
+bool QuicConnectionPeer::HasRetransmittableFrames(QuicConnection* connection,
+ uint64_t packet_number) {
+ return QuicSentPacketManagerPeer::HasRetransmittableFrames(
+ GetSentPacketManager(connection), packet_number);
+}
+
+// static
+bool QuicConnectionPeer::GetNoStopWaitingFrames(QuicConnection* connection) {
+ return connection->no_stop_waiting_frames_;
+}
+
+// static
+void QuicConnectionPeer::SetNoStopWaitingFrames(QuicConnection* connection,
+ bool no_stop_waiting_frames) {
+ connection->no_stop_waiting_frames_ = no_stop_waiting_frames;
+}
+
+// static
+void QuicConnectionPeer::SetMaxTrackedPackets(
+ QuicConnection* connection,
+ QuicPacketCount max_tracked_packets) {
+ connection->max_tracked_packets_ = max_tracked_packets;
+}
+
+// static
+void QuicConnectionPeer::SetSessionDecidesWhatToWrite(
+ QuicConnection* connection) {
+ connection->sent_packet_manager_.SetSessionDecideWhatToWrite(true);
+ connection->packet_generator_.SetCanSetTransmissionType(true);
+}
+
+// static
+void QuicConnectionPeer::SetNegotiatedVersion(QuicConnection* connection) {
+ connection->version_negotiation_state_ = QuicConnection::NEGOTIATED_VERSION;
+}
+
+// static
+void QuicConnectionPeer::SetMaxConsecutiveNumPacketsWithNoRetransmittableFrames(
+ QuicConnection* connection,
+ size_t new_value) {
+ connection->max_consecutive_num_packets_with_no_retransmittable_frames_ =
+ new_value;
+}
+
+// static
+void QuicConnectionPeer::SetNoVersionNegotiation(QuicConnection* connection,
+ bool no_version_negotiation) {
+ *const_cast<bool*>(&connection->no_version_negotiation_) =
+ no_version_negotiation;
+}
+
+// static
+bool QuicConnectionPeer::SupportsReleaseTime(QuicConnection* connection) {
+ return connection->supports_release_time_;
+}
+
+// static
+QuicConnection::PacketContent QuicConnectionPeer::GetCurrentPacketContent(
+ QuicConnection* connection) {
+ return connection->current_packet_content_;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_connection_peer.h b/quic/test_tools/quic_connection_peer.h
new file mode 100644
index 0000000..b82c1b3
--- /dev/null
+++ b/quic/test_tools/quic_connection_peer.h
@@ -0,0 +1,147 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_CONNECTION_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_CONNECTION_PEER_H_
+
+#include "base/macros.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_stats.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+struct QuicPacketHeader;
+class QuicAlarm;
+class QuicConnectionHelperInterface;
+class QuicConnectionVisitorInterface;
+class QuicEncryptedPacket;
+class QuicFramer;
+class QuicPacketCreator;
+class QuicPacketGenerator;
+class QuicPacketWriter;
+class QuicSentPacketManager;
+class SendAlgorithmInterface;
+
+namespace test {
+
+// Peer to make public a number of otherwise private QuicConnection methods.
+class QuicConnectionPeer {
+ public:
+ QuicConnectionPeer() = delete;
+
+ static void SendAck(QuicConnection* connection);
+
+ static void SetSendAlgorithm(QuicConnection* connection,
+ SendAlgorithmInterface* send_algorithm);
+
+ static void SetLossAlgorithm(QuicConnection* connection,
+ LossDetectionInterface* loss_algorithm);
+
+ static const QuicFrame GetUpdatedAckFrame(QuicConnection* connection);
+
+ static void PopulateStopWaitingFrame(QuicConnection* connection,
+ QuicStopWaitingFrame* stop_waiting);
+
+ static QuicConnectionVisitorInterface* GetVisitor(QuicConnection* connection);
+
+ static QuicPacketCreator* GetPacketCreator(QuicConnection* connection);
+
+ static QuicPacketGenerator* GetPacketGenerator(QuicConnection* connection);
+
+ static QuicSentPacketManager* GetSentPacketManager(
+ QuicConnection* connection);
+
+ static QuicTime::Delta GetNetworkTimeout(QuicConnection* connection);
+
+ static void SetPerspective(QuicConnection* connection,
+ Perspective perspective);
+
+ static void SetSelfAddress(QuicConnection* connection,
+ const QuicSocketAddress& self_address);
+
+ static void SetPeerAddress(QuicConnection* connection,
+ const QuicSocketAddress& peer_address);
+
+ static void SetDirectPeerAddress(
+ QuicConnection* connection,
+ const QuicSocketAddress& direct_peer_address);
+
+ static void SetEffectivePeerAddress(
+ QuicConnection* connection,
+ const QuicSocketAddress& effective_peer_address);
+
+ static bool IsSilentCloseEnabled(QuicConnection* connection);
+
+ static void SwapCrypters(QuicConnection* connection, QuicFramer* framer);
+
+ static void SetCurrentPacket(QuicConnection* connection,
+ QuicStringPiece current_packet);
+
+ static QuicConnectionHelperInterface* GetHelper(QuicConnection* connection);
+
+ static QuicAlarmFactory* GetAlarmFactory(QuicConnection* connection);
+
+ static QuicFramer* GetFramer(QuicConnection* connection);
+
+ static QuicAlarm* GetAckAlarm(QuicConnection* connection);
+ static QuicAlarm* GetPingAlarm(QuicConnection* connection);
+ static QuicAlarm* GetRetransmissionAlarm(QuicConnection* connection);
+ static QuicAlarm* GetSendAlarm(QuicConnection* connection);
+ static QuicAlarm* GetTimeoutAlarm(QuicConnection* connection);
+ static QuicAlarm* GetMtuDiscoveryAlarm(QuicConnection* connection);
+ static QuicAlarm* GetPathDegradingAlarm(QuicConnection* connection);
+ static QuicAlarm* GetProcessUndecryptablePacketsAlarm(
+ QuicConnection* connection);
+
+ static QuicPacketWriter* GetWriter(QuicConnection* connection);
+ // If |owns_writer| is true, takes ownership of |writer|.
+ static void SetWriter(QuicConnection* connection,
+ QuicPacketWriter* writer,
+ bool owns_writer);
+ static void TearDownLocalConnectionState(QuicConnection* connection);
+ static QuicEncryptedPacket* GetConnectionClosePacket(
+ QuicConnection* connection);
+
+ static QuicPacketHeader* GetLastHeader(QuicConnection* connection);
+
+ static QuicConnectionStats* GetStats(QuicConnection* connection);
+
+ static QuicPacketCount GetPacketsBetweenMtuProbes(QuicConnection* connection);
+
+ static void SetPacketsBetweenMtuProbes(QuicConnection* connection,
+ QuicPacketCount packets);
+ static void SetNextMtuProbeAt(QuicConnection* connection,
+ QuicPacketNumber number);
+ static void SetAckMode(QuicConnection* connection, AckMode ack_mode);
+ static void SetFastAckAfterQuiescence(QuicConnection* connection,
+ bool fast_ack_after_quiescence);
+ static void SetAckDecimationDelay(QuicConnection* connection,
+ float ack_decimation_delay);
+ static bool HasRetransmittableFrames(QuicConnection* connection,
+ uint64_t packet_number);
+ static bool GetNoStopWaitingFrames(QuicConnection* connection);
+ static void SetNoStopWaitingFrames(QuicConnection* connection,
+ bool no_stop_waiting_frames);
+ static void SetMaxTrackedPackets(QuicConnection* connection,
+ QuicPacketCount max_tracked_packets);
+ static void SetSessionDecidesWhatToWrite(QuicConnection* connection);
+ static void SetNegotiatedVersion(QuicConnection* connection);
+ static void SetMaxConsecutiveNumPacketsWithNoRetransmittableFrames(
+ QuicConnection* connection,
+ size_t new_value);
+ static void SetNoVersionNegotiation(QuicConnection* connection,
+ bool no_version_negotiation);
+ static bool SupportsReleaseTime(QuicConnection* connection);
+ static QuicConnection::PacketContent GetCurrentPacketContent(
+ QuicConnection* connection);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_CONNECTION_PEER_H_
diff --git a/quic/test_tools/quic_crypto_server_config_peer.cc b/quic/test_tools/quic_crypto_server_config_peer.cc
new file mode 100644
index 0000000..048e6bb
--- /dev/null
+++ b/quic/test_tools/quic_crypto_server_config_peer.cc
@@ -0,0 +1,162 @@
+// Copyright 2016 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 "net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h"
+
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_random.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+
+QuicReferenceCountedPointer<QuicCryptoServerConfig::Config>
+QuicCryptoServerConfigPeer::GetPrimaryConfig() {
+ QuicReaderMutexLock locked(&server_config_->configs_lock_);
+ return QuicReferenceCountedPointer<QuicCryptoServerConfig::Config>(
+ server_config_->primary_config_);
+}
+
+QuicReferenceCountedPointer<QuicCryptoServerConfig::Config>
+QuicCryptoServerConfigPeer::GetConfig(QuicString config_id) {
+ QuicReaderMutexLock locked(&server_config_->configs_lock_);
+ if (config_id == "<primary>") {
+ return QuicReferenceCountedPointer<QuicCryptoServerConfig::Config>(
+ server_config_->primary_config_);
+ } else {
+ return server_config_->GetConfigWithScid(config_id);
+ }
+}
+
+ProofSource* QuicCryptoServerConfigPeer::GetProofSource() const {
+ return server_config_->proof_source_.get();
+}
+
+void QuicCryptoServerConfigPeer::ResetProofSource(
+ std::unique_ptr<ProofSource> proof_source) {
+ server_config_->proof_source_ = std::move(proof_source);
+}
+
+QuicString QuicCryptoServerConfigPeer::NewSourceAddressToken(
+ QuicString config_id,
+ SourceAddressTokens previous_tokens,
+ const QuicIpAddress& ip,
+ QuicRandom* rand,
+ QuicWallTime now,
+ CachedNetworkParameters* cached_network_params) {
+ return server_config_->NewSourceAddressToken(*GetConfig(config_id),
+ previous_tokens, ip, rand, now,
+ cached_network_params);
+}
+
+HandshakeFailureReason QuicCryptoServerConfigPeer::ValidateSourceAddressTokens(
+ QuicString config_id,
+ QuicStringPiece srct,
+ const QuicIpAddress& ip,
+ QuicWallTime now,
+ CachedNetworkParameters* cached_network_params) {
+ SourceAddressTokens tokens;
+ HandshakeFailureReason reason = server_config_->ParseSourceAddressToken(
+ *GetConfig(config_id), srct, &tokens);
+ if (reason != HANDSHAKE_OK) {
+ return reason;
+ }
+
+ return server_config_->ValidateSourceAddressTokens(tokens, ip, now,
+ cached_network_params);
+}
+
+HandshakeFailureReason
+QuicCryptoServerConfigPeer::ValidateSingleSourceAddressToken(
+ QuicStringPiece token,
+ const QuicIpAddress& ip,
+ QuicWallTime now) {
+ SourceAddressTokens tokens;
+ HandshakeFailureReason parse_status = server_config_->ParseSourceAddressToken(
+ *GetPrimaryConfig(), token, &tokens);
+ if (HANDSHAKE_OK != parse_status) {
+ return parse_status;
+ }
+ EXPECT_EQ(1, tokens.tokens_size());
+ return server_config_->ValidateSingleSourceAddressToken(tokens.tokens(0), ip,
+ now);
+}
+
+void QuicCryptoServerConfigPeer::CheckConfigs(
+ std::vector<std::pair<QuicString, bool>> expected_ids_and_status) {
+ QuicReaderMutexLock locked(&server_config_->configs_lock_);
+
+ ASSERT_EQ(expected_ids_and_status.size(), server_config_->configs_.size())
+ << ConfigsDebug();
+
+ for (const std::pair<
+ const ServerConfigID,
+ QuicReferenceCountedPointer<QuicCryptoServerConfig::Config>>& i :
+ server_config_->configs_) {
+ bool found = false;
+ for (std::pair<ServerConfigID, bool>& j : expected_ids_and_status) {
+ if (i.first == j.first && i.second->is_primary == j.second) {
+ found = true;
+ j.first.clear();
+ break;
+ }
+ }
+
+ ASSERT_TRUE(found) << "Failed to find match for " << i.first
+ << " in configs:\n"
+ << ConfigsDebug();
+ }
+}
+
+// ConfigsDebug returns a QuicString that contains debugging information about
+// the set of Configs loaded in |server_config_| and their status.
+QuicString QuicCryptoServerConfigPeer::ConfigsDebug() {
+ if (server_config_->configs_.empty()) {
+ return "No Configs in QuicCryptoServerConfig";
+ }
+
+ QuicString s;
+
+ for (const auto& i : server_config_->configs_) {
+ const QuicReferenceCountedPointer<QuicCryptoServerConfig::Config> config =
+ i.second;
+ if (config->is_primary) {
+ s += "(primary) ";
+ } else {
+ s += " ";
+ }
+ s += config->id;
+ s += "\n";
+ }
+
+ return s;
+}
+
+void QuicCryptoServerConfigPeer::SelectNewPrimaryConfig(int seconds) {
+ QuicWriterMutexLock locked(&server_config_->configs_lock_);
+ server_config_->SelectNewPrimaryConfig(
+ QuicWallTime::FromUNIXSeconds(seconds));
+}
+
+QuicString QuicCryptoServerConfigPeer::CompressChain(
+ QuicCompressedCertsCache* compressed_certs_cache,
+ const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const QuicString& client_common_set_hashes,
+ const QuicString& client_cached_cert_hashes,
+ const CommonCertSets* common_sets) {
+ return QuicCryptoServerConfig::CompressChain(
+ compressed_certs_cache, chain, client_common_set_hashes,
+ client_cached_cert_hashes, common_sets);
+}
+
+uint32_t QuicCryptoServerConfigPeer::source_address_token_future_secs() {
+ return server_config_->source_address_token_future_secs_;
+}
+
+uint32_t QuicCryptoServerConfigPeer::source_address_token_lifetime_secs() {
+ return server_config_->source_address_token_lifetime_secs_;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_crypto_server_config_peer.h b/quic/test_tools/quic_crypto_server_config_peer.h
new file mode 100644
index 0000000..0034c4b
--- /dev/null
+++ b/quic/test_tools/quic_crypto_server_config_peer.h
@@ -0,0 +1,98 @@
+// Copyright 2016 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_CRYPTO_SERVER_CONFIG_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_CRYPTO_SERVER_CONFIG_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+namespace test {
+
+// Peer for accessing otherwise private members of a QuicCryptoServerConfig.
+class QuicCryptoServerConfigPeer {
+ public:
+ explicit QuicCryptoServerConfigPeer(QuicCryptoServerConfig* server_config)
+ : server_config_(server_config) {}
+
+ // Returns the primary config.
+ QuicReferenceCountedPointer<QuicCryptoServerConfig::Config>
+ GetPrimaryConfig();
+
+ // Returns the config associated with |config_id|.
+ QuicReferenceCountedPointer<QuicCryptoServerConfig::Config> GetConfig(
+ QuicString config_id);
+
+ // Returns a pointer to the ProofSource object.
+ ProofSource* GetProofSource() const;
+
+ // Reset the proof_source_ member.
+ void ResetProofSource(std::unique_ptr<ProofSource> proof_source);
+
+ // Generates a new valid source address token.
+ QuicString NewSourceAddressToken(
+ QuicString config_id,
+ SourceAddressTokens previous_tokens,
+ const QuicIpAddress& ip,
+ QuicRandom* rand,
+ QuicWallTime now,
+ CachedNetworkParameters* cached_network_params);
+
+ // Attempts to validate the tokens in |tokens|.
+ HandshakeFailureReason ValidateSourceAddressTokens(
+ QuicString config_id,
+ QuicStringPiece tokens,
+ const QuicIpAddress& ip,
+ QuicWallTime now,
+ CachedNetworkParameters* cached_network_params);
+
+ // Attempts to validate the single source address token in |token|.
+ HandshakeFailureReason ValidateSingleSourceAddressToken(
+ QuicStringPiece token,
+ const QuicIpAddress& ip,
+ QuicWallTime now);
+
+ // CheckConfigs compares the state of the Configs in |server_config_| to the
+ // description given as arguments.
+ // The first of each pair is the server config ID of a Config. The second is a
+ // boolean describing whether the config is the primary. For example:
+ // CheckConfigs(std::vector<std::pair<ServerConfigID, bool>>()); // checks
+ // that no Configs are loaded.
+ //
+ // // Checks that exactly three Configs are loaded with the given IDs and
+ // // status.
+ // CheckConfigs(
+ // {{"id1", false},
+ // {"id2", true},
+ // {"id3", false}});
+ void CheckConfigs(
+ std::vector<std::pair<ServerConfigID, bool>> expected_ids_and_status);
+
+ // ConfigsDebug returns a QuicString that contains debugging information about
+ // the set of Configs loaded in |server_config_| and their status.
+ QuicString ConfigsDebug()
+ SHARED_LOCKS_REQUIRED(server_config_->configs_lock_);
+
+ void SelectNewPrimaryConfig(int seconds);
+
+ static QuicString CompressChain(
+ QuicCompressedCertsCache* compressed_certs_cache,
+ const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const QuicString& client_common_set_hashes,
+ const QuicString& client_cached_cert_hashes,
+ const CommonCertSets* common_sets);
+
+ uint32_t source_address_token_future_secs();
+
+ uint32_t source_address_token_lifetime_secs();
+
+ private:
+ QuicCryptoServerConfig* server_config_;
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_CRYPTO_SERVER_CONFIG_PEER_H_
diff --git a/quic/test_tools/quic_dispatcher_peer.cc b/quic/test_tools/quic_dispatcher_peer.cc
new file mode 100644
index 0000000..2590834
--- /dev/null
+++ b/quic/test_tools/quic_dispatcher_peer.cc
@@ -0,0 +1,111 @@
+// Copyright 2013 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 "net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_dispatcher.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h"
+
+namespace quic {
+namespace test {
+
+// static
+void QuicDispatcherPeer::SetTimeWaitListManager(
+ QuicDispatcher* dispatcher,
+ QuicTimeWaitListManager* time_wait_list_manager) {
+ dispatcher->time_wait_list_manager_.reset(time_wait_list_manager);
+}
+
+// static
+void QuicDispatcherPeer::UseWriter(QuicDispatcher* dispatcher,
+ QuicPacketWriterWrapper* writer) {
+ writer->set_writer(dispatcher->writer_.release());
+ dispatcher->writer_.reset(writer);
+}
+
+// static
+QuicPacketWriter* QuicDispatcherPeer::GetWriter(QuicDispatcher* dispatcher) {
+ return dispatcher->writer_.get();
+}
+
+// static
+QuicCompressedCertsCache* QuicDispatcherPeer::GetCache(
+ QuicDispatcher* dispatcher) {
+ return dispatcher->compressed_certs_cache();
+}
+
+// static
+QuicConnectionHelperInterface* QuicDispatcherPeer::GetHelper(
+ QuicDispatcher* dispatcher) {
+ return dispatcher->helper_.get();
+}
+
+// static
+QuicAlarmFactory* QuicDispatcherPeer::GetAlarmFactory(
+ QuicDispatcher* dispatcher) {
+ return dispatcher->alarm_factory_.get();
+}
+
+// static
+QuicDispatcher::WriteBlockedList* QuicDispatcherPeer::GetWriteBlockedList(
+ QuicDispatcher* dispatcher) {
+ return &dispatcher->write_blocked_list_;
+}
+
+// static
+QuicErrorCode QuicDispatcherPeer::GetAndClearLastError(
+ QuicDispatcher* dispatcher) {
+ QuicErrorCode ret = dispatcher->last_error_;
+ dispatcher->last_error_ = QUIC_NO_ERROR;
+ return ret;
+}
+
+// static
+QuicBufferedPacketStore* QuicDispatcherPeer::GetBufferedPackets(
+ QuicDispatcher* dispatcher) {
+ return &(dispatcher->buffered_packets_);
+}
+
+// static
+const QuicDispatcher::SessionMap& QuicDispatcherPeer::session_map(
+ QuicDispatcher* dispatcher) {
+ return dispatcher->session_map();
+}
+
+// static
+void QuicDispatcherPeer::set_new_sessions_allowed_per_event_loop(
+ QuicDispatcher* dispatcher,
+ size_t num_session_allowed) {
+ return dispatcher->set_new_sessions_allowed_per_event_loop(
+ num_session_allowed);
+}
+
+// static
+void QuicDispatcherPeer::SendPublicReset(
+ QuicDispatcher* dispatcher,
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ QuicConnectionId connection_id,
+ bool ietf_quic,
+ std::unique_ptr<QuicPerPacketContext> packet_context) {
+ dispatcher->time_wait_list_manager()->SendPublicReset(
+ self_address, peer_address, connection_id, ietf_quic,
+ std::move(packet_context));
+}
+
+// static
+std::unique_ptr<QuicPerPacketContext> QuicDispatcherPeer::GetPerPacketContext(
+ QuicDispatcher* dispatcher) {
+ return dispatcher->GetPerPacketContext();
+}
+
+// static
+void QuicDispatcherPeer::RestorePerPacketContext(
+ QuicDispatcher* dispatcher,
+ std::unique_ptr<QuicPerPacketContext> context) {
+ dispatcher->RestorePerPacketContext(std::move(context));
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_dispatcher_peer.h b/quic/test_tools/quic_dispatcher_peer.h
new file mode 100644
index 0000000..e071938
--- /dev/null
+++ b/quic/test_tools/quic_dispatcher_peer.h
@@ -0,0 +1,72 @@
+// Copyright 2013 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_DISPATCHER_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_DISPATCHER_PEER_H_
+
+#include "base/macros.h"
+#include "net/third_party/quiche/src/quic/core/quic_dispatcher.h"
+
+namespace quic {
+
+class QuicPacketWriterWrapper;
+
+namespace test {
+
+class QuicDispatcherPeer {
+ public:
+ QuicDispatcherPeer() = delete;
+
+ static void SetTimeWaitListManager(
+ QuicDispatcher* dispatcher,
+ QuicTimeWaitListManager* time_wait_list_manager);
+
+ // Injects |writer| into |dispatcher| as the shared writer.
+ static void UseWriter(QuicDispatcher* dispatcher,
+ QuicPacketWriterWrapper* writer);
+
+ static QuicPacketWriter* GetWriter(QuicDispatcher* dispatcher);
+
+ static QuicCompressedCertsCache* GetCache(QuicDispatcher* dispatcher);
+
+ static QuicConnectionHelperInterface* GetHelper(QuicDispatcher* dispatcher);
+
+ static QuicAlarmFactory* GetAlarmFactory(QuicDispatcher* dispatcher);
+
+ static QuicDispatcher::WriteBlockedList* GetWriteBlockedList(
+ QuicDispatcher* dispatcher);
+
+ // Get the dispatcher's record of the last error reported to its framer
+ // visitor's OnError() method. Then set that record to QUIC_NO_ERROR.
+ static QuicErrorCode GetAndClearLastError(QuicDispatcher* dispatcher);
+
+ static QuicBufferedPacketStore* GetBufferedPackets(
+ QuicDispatcher* dispatcher);
+
+ static const QuicDispatcher::SessionMap& session_map(
+ QuicDispatcher* dispatcher);
+
+ static void set_new_sessions_allowed_per_event_loop(
+ QuicDispatcher* dispatcher,
+ size_t num_session_allowed);
+
+ static void SendPublicReset(
+ QuicDispatcher* dispatcher,
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ QuicConnectionId connection_id,
+ bool ietf_quic,
+ std::unique_ptr<QuicPerPacketContext> packet_context);
+
+ static std::unique_ptr<QuicPerPacketContext> GetPerPacketContext(
+ QuicDispatcher* dispatcher);
+
+ static void RestorePerPacketContext(QuicDispatcher* dispatcher,
+ std::unique_ptr<QuicPerPacketContext>);
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_DISPATCHER_PEER_H_
diff --git a/quic/test_tools/quic_flow_controller_peer.cc b/quic/test_tools/quic_flow_controller_peer.cc
new file mode 100644
index 0000000..32efe45
--- /dev/null
+++ b/quic/test_tools/quic_flow_controller_peer.cc
@@ -0,0 +1,68 @@
+// Copyright 2014 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 "net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h"
+
+#include <list>
+
+#include "net/third_party/quiche/src/quic/core/quic_flow_controller.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+namespace test {
+
+// static
+void QuicFlowControllerPeer::SetSendWindowOffset(
+ QuicFlowController* flow_controller,
+ QuicStreamOffset offset) {
+ flow_controller->send_window_offset_ = offset;
+}
+
+// static
+void QuicFlowControllerPeer::SetReceiveWindowOffset(
+ QuicFlowController* flow_controller,
+ QuicStreamOffset offset) {
+ flow_controller->receive_window_offset_ = offset;
+}
+
+// static
+void QuicFlowControllerPeer::SetMaxReceiveWindow(
+ QuicFlowController* flow_controller,
+ QuicByteCount window_size) {
+ flow_controller->receive_window_size_ = window_size;
+}
+
+// static
+QuicStreamOffset QuicFlowControllerPeer::SendWindowOffset(
+ QuicFlowController* flow_controller) {
+ return flow_controller->send_window_offset_;
+}
+
+// static
+QuicByteCount QuicFlowControllerPeer::SendWindowSize(
+ QuicFlowController* flow_controller) {
+ return flow_controller->SendWindowSize();
+}
+
+// static
+QuicStreamOffset QuicFlowControllerPeer::ReceiveWindowOffset(
+ QuicFlowController* flow_controller) {
+ return flow_controller->receive_window_offset_;
+}
+
+// static
+QuicByteCount QuicFlowControllerPeer::ReceiveWindowSize(
+ QuicFlowController* flow_controller) {
+ return flow_controller->receive_window_offset_ -
+ flow_controller->highest_received_byte_offset_;
+}
+
+// static
+QuicByteCount QuicFlowControllerPeer::WindowUpdateThreshold(
+ QuicFlowController* flow_controller) {
+ return flow_controller->WindowUpdateThreshold();
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_flow_controller_peer.h b/quic/test_tools/quic_flow_controller_peer.h
new file mode 100644
index 0000000..1dc5a1a
--- /dev/null
+++ b/quic/test_tools/quic_flow_controller_peer.h
@@ -0,0 +1,46 @@
+// Copyright 2014 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_FLOW_CONTROLLER_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_FLOW_CONTROLLER_PEER_H_
+
+#include "base/macros.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+
+class QuicFlowController;
+
+namespace test {
+
+class QuicFlowControllerPeer {
+ public:
+ QuicFlowControllerPeer() = delete;
+
+ static void SetSendWindowOffset(QuicFlowController* flow_controller,
+ QuicStreamOffset offset);
+
+ static void SetReceiveWindowOffset(QuicFlowController* flow_controller,
+ QuicStreamOffset offset);
+
+ static void SetMaxReceiveWindow(QuicFlowController* flow_controller,
+ QuicByteCount window_size);
+
+ static QuicStreamOffset SendWindowOffset(QuicFlowController* flow_controller);
+
+ static QuicByteCount SendWindowSize(QuicFlowController* flow_controller);
+
+ static QuicStreamOffset ReceiveWindowOffset(
+ QuicFlowController* flow_controller);
+
+ static QuicByteCount ReceiveWindowSize(QuicFlowController* flow_controller);
+
+ static QuicByteCount WindowUpdateThreshold(
+ QuicFlowController* flow_controller);
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_FLOW_CONTROLLER_PEER_H_
diff --git a/quic/test_tools/quic_framer_peer.cc b/quic/test_tools/quic_framer_peer.cc
new file mode 100644
index 0000000..defdab4
--- /dev/null
+++ b/quic/test_tools/quic_framer_peer.cc
@@ -0,0 +1,358 @@
+// Copyright (c) 2013 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 "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+
+namespace quic {
+namespace test {
+
+// static
+uint64_t QuicFramerPeer::CalculatePacketNumberFromWire(
+ QuicFramer* framer,
+ QuicPacketNumberLength packet_number_length,
+ QuicPacketNumber last_packet_number,
+ uint64_t packet_number) {
+ return framer->CalculatePacketNumberFromWire(
+ packet_number_length, last_packet_number, packet_number);
+}
+
+// static
+void QuicFramerPeer::SetLastSerializedConnectionId(
+ QuicFramer* framer,
+ QuicConnectionId connection_id) {
+ framer->last_serialized_connection_id_ = connection_id;
+}
+
+// static
+void QuicFramerPeer::SetLargestPacketNumber(QuicFramer* framer,
+ QuicPacketNumber packet_number) {
+ framer->largest_packet_number_ = packet_number;
+}
+
+// static
+void QuicFramerPeer::SetPerspective(QuicFramer* framer,
+ Perspective perspective) {
+ framer->perspective_ = perspective;
+ framer->infer_packet_header_type_from_version_ =
+ perspective == Perspective::IS_CLIENT;
+}
+
+// static
+bool QuicFramerPeer::ProcessIetfStreamFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ uint8_t frame_type,
+ QuicStreamFrame* frame) {
+ return framer->ProcessIetfStreamFrame(reader, frame_type, frame);
+}
+
+// static
+bool QuicFramerPeer::AppendIetfStreamFrame(QuicFramer* framer,
+ const QuicStreamFrame& frame,
+ bool last_frame_in_packet,
+ QuicDataWriter* writer) {
+ return framer->AppendIetfStreamFrame(frame, last_frame_in_packet, writer);
+}
+
+// static
+bool QuicFramerPeer::ProcessCryptoFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicCryptoFrame* frame) {
+ return framer->ProcessCryptoFrame(reader, frame);
+}
+
+// static
+bool QuicFramerPeer::AppendCryptoFrame(QuicFramer* framer,
+ const QuicCryptoFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendCryptoFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::ProcessIetfAckFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ uint64_t frame_type,
+ QuicAckFrame* ack_frame) {
+ return framer->ProcessIetfAckFrame(reader, frame_type, ack_frame);
+}
+
+// static
+bool QuicFramerPeer::AppendIetfAckFrameAndTypeByte(QuicFramer* framer,
+ const QuicAckFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendIetfAckFrameAndTypeByte(frame, writer);
+}
+// static
+size_t QuicFramerPeer::GetIetfAckFrameSize(QuicFramer* framer,
+ const QuicAckFrame& frame) {
+ return framer->GetIetfAckFrameSize(frame);
+}
+
+// static
+bool QuicFramerPeer::AppendIetfConnectionCloseFrame(
+ QuicFramer* framer,
+ const QuicConnectionCloseFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendIetfConnectionCloseFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::AppendApplicationCloseFrame(
+ QuicFramer* framer,
+ const QuicApplicationCloseFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendApplicationCloseFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::ProcessIetfConnectionCloseFrame(
+ QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicConnectionCloseFrame* frame) {
+ return framer->ProcessIetfConnectionCloseFrame(reader, frame);
+}
+
+// static
+bool QuicFramerPeer::ProcessApplicationCloseFrame(
+ QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicApplicationCloseFrame* frame) {
+ return framer->ProcessApplicationCloseFrame(reader, frame);
+}
+
+// static
+bool QuicFramerPeer::ProcessPathChallengeFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicPathChallengeFrame* frame) {
+ return framer->ProcessPathChallengeFrame(reader, frame);
+}
+
+// static
+bool QuicFramerPeer::ProcessPathResponseFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicPathResponseFrame* frame) {
+ return framer->ProcessPathResponseFrame(reader, frame);
+}
+
+// static
+bool QuicFramerPeer::AppendPathChallengeFrame(
+ QuicFramer* framer,
+ const QuicPathChallengeFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendPathChallengeFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::AppendPathResponseFrame(QuicFramer* framer,
+ const QuicPathResponseFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendPathResponseFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::AppendIetfResetStreamFrame(QuicFramer* framer,
+ const QuicRstStreamFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendIetfResetStreamFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::ProcessIetfResetStreamFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicRstStreamFrame* frame) {
+ return framer->ProcessIetfResetStreamFrame(reader, frame);
+}
+
+// static
+bool QuicFramerPeer::ProcessStopSendingFrame(
+ QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicStopSendingFrame* stop_sending_frame) {
+ return framer->ProcessStopSendingFrame(reader, stop_sending_frame);
+}
+
+// static
+bool QuicFramerPeer::AppendStopSendingFrame(
+ QuicFramer* framer,
+ const QuicStopSendingFrame& stop_sending_frame,
+ QuicDataWriter* writer) {
+ return framer->AppendStopSendingFrame(stop_sending_frame, writer);
+}
+
+// static
+bool QuicFramerPeer::AppendMaxDataFrame(QuicFramer* framer,
+ const QuicWindowUpdateFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendMaxDataFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::AppendMaxStreamDataFrame(
+ QuicFramer* framer,
+ const QuicWindowUpdateFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendMaxStreamDataFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::ProcessMaxDataFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicWindowUpdateFrame* frame) {
+ return framer->ProcessMaxDataFrame(reader, frame);
+}
+
+// static
+bool QuicFramerPeer::ProcessMaxStreamDataFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicWindowUpdateFrame* frame) {
+ return framer->ProcessMaxStreamDataFrame(reader, frame);
+}
+
+// static
+bool QuicFramerPeer::AppendMaxStreamsFrame(QuicFramer* framer,
+ const QuicMaxStreamIdFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendMaxStreamsFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::ProcessMaxStreamsFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicMaxStreamIdFrame* frame,
+ uint64_t frame_type) {
+ return framer->ProcessMaxStreamsFrame(reader, frame, frame_type);
+}
+
+// static
+bool QuicFramerPeer::AppendIetfBlockedFrame(QuicFramer* framer,
+ const QuicBlockedFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendIetfBlockedFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::ProcessIetfBlockedFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicBlockedFrame* frame) {
+ return framer->ProcessIetfBlockedFrame(reader, frame);
+}
+
+// static
+bool QuicFramerPeer::AppendStreamBlockedFrame(QuicFramer* framer,
+ const QuicBlockedFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendStreamBlockedFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::ProcessStreamBlockedFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicBlockedFrame* frame) {
+ return framer->ProcessStreamBlockedFrame(reader, frame);
+}
+
+// static
+bool QuicFramerPeer::AppendStreamsBlockedFrame(
+ QuicFramer* framer,
+ const QuicStreamIdBlockedFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendStreamsBlockedFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::ProcessStreamsBlockedFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicStreamIdBlockedFrame* frame,
+ uint64_t frame_type) {
+ return framer->ProcessStreamsBlockedFrame(reader, frame, frame_type);
+}
+
+// static
+bool QuicFramerPeer::AppendNewConnectionIdFrame(
+ QuicFramer* framer,
+ const QuicNewConnectionIdFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendNewConnectionIdFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::ProcessNewConnectionIdFrame(
+ QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicNewConnectionIdFrame* frame) {
+ return framer->ProcessNewConnectionIdFrame(reader, frame);
+}
+
+// static
+bool QuicFramerPeer::AppendRetireConnectionIdFrame(
+ QuicFramer* framer,
+ const QuicRetireConnectionIdFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendRetireConnectionIdFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::ProcessRetireConnectionIdFrame(
+ QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicRetireConnectionIdFrame* frame) {
+ return framer->ProcessRetireConnectionIdFrame(reader, frame);
+}
+
+// static
+void QuicFramerPeer::SwapCrypters(QuicFramer* framer1, QuicFramer* framer2) {
+ for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; i++) {
+ framer1->encrypter_[i].swap(framer2->encrypter_[i]);
+ }
+ framer1->decrypter_.swap(framer2->decrypter_);
+ framer1->alternative_decrypter_.swap(framer2->alternative_decrypter_);
+
+ EncryptionLevel framer2_level = framer2->decrypter_level_;
+ framer2->decrypter_level_ = framer1->decrypter_level_;
+ framer1->decrypter_level_ = framer2_level;
+ framer2_level = framer2->alternative_decrypter_level_;
+ framer2->alternative_decrypter_level_ = framer1->alternative_decrypter_level_;
+ framer1->alternative_decrypter_level_ = framer2_level;
+
+ const bool framer2_latch = framer2->alternative_decrypter_latch_;
+ framer2->alternative_decrypter_latch_ = framer1->alternative_decrypter_latch_;
+ framer1->alternative_decrypter_latch_ = framer2_latch;
+}
+
+// static
+QuicEncrypter* QuicFramerPeer::GetEncrypter(QuicFramer* framer,
+ EncryptionLevel level) {
+ return framer->encrypter_[level].get();
+}
+
+// static
+size_t QuicFramerPeer::ComputeFrameLength(
+ QuicFramer* framer,
+ const QuicFrame& frame,
+ bool last_frame_in_packet,
+ QuicPacketNumberLength packet_number_length) {
+ return framer->ComputeFrameLength(frame, last_frame_in_packet,
+ packet_number_length);
+}
+
+// static
+void QuicFramerPeer::SetFirstSendingPacketNumber(QuicFramer* framer,
+ uint64_t packet_number) {
+ *const_cast<QuicPacketNumber*>(&framer->first_sending_packet_number_) =
+ QuicPacketNumber(packet_number);
+}
+
+// static
+void QuicFramerPeer::SetExpectedConnectionIDLength(
+ QuicFramer* framer,
+ uint8_t expected_connection_id_length) {
+ *const_cast<uint8_t*>(&framer->expected_connection_id_length_) =
+ expected_connection_id_length;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_framer_peer.h b/quic/test_tools/quic_framer_peer.h
new file mode 100644
index 0000000..94509b5
--- /dev/null
+++ b/quic/test_tools/quic_framer_peer.h
@@ -0,0 +1,177 @@
+// Copyright (c) 2013 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_FRAMER_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_FRAMER_PEER_H_
+
+#include "base/macros.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+
+namespace test {
+
+class QuicFramerPeer {
+ public:
+ QuicFramerPeer() = delete;
+
+ static uint64_t CalculatePacketNumberFromWire(
+ QuicFramer* framer,
+ QuicPacketNumberLength packet_number_length,
+ QuicPacketNumber last_packet_number,
+ uint64_t packet_number);
+ static void SetLastSerializedConnectionId(QuicFramer* framer,
+ QuicConnectionId connection_id);
+ static void SetLargestPacketNumber(QuicFramer* framer,
+ QuicPacketNumber packet_number);
+ static void SetPerspective(QuicFramer* framer, Perspective perspective);
+
+ // SwapCrypters exchanges the state of the crypters of |framer1| with
+ // |framer2|.
+ static void SwapCrypters(QuicFramer* framer1, QuicFramer* framer2);
+
+ static QuicEncrypter* GetEncrypter(QuicFramer* framer, EncryptionLevel level);
+
+ // IETF defined frame append/process methods.
+ static bool ProcessIetfStreamFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ uint8_t frame_type,
+ QuicStreamFrame* frame);
+ static bool AppendIetfStreamFrame(QuicFramer* framer,
+ const QuicStreamFrame& frame,
+ bool last_frame_in_packet,
+ QuicDataWriter* writer);
+ static bool ProcessCryptoFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicCryptoFrame* frame);
+ static bool AppendCryptoFrame(QuicFramer* framer,
+ const QuicCryptoFrame& frame,
+ QuicDataWriter* writer);
+
+ static bool AppendIetfConnectionCloseFrame(
+ QuicFramer* framer,
+ const QuicConnectionCloseFrame& frame,
+ QuicDataWriter* writer);
+ static bool AppendApplicationCloseFrame(
+ QuicFramer* framer,
+ const QuicApplicationCloseFrame& frame,
+ QuicDataWriter* writer);
+ static bool ProcessIetfConnectionCloseFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicConnectionCloseFrame* frame);
+ static bool ProcessApplicationCloseFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicApplicationCloseFrame* frame);
+ static bool ProcessIetfAckFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ uint64_t frame_type,
+ QuicAckFrame* ack_frame);
+ static bool AppendIetfAckFrameAndTypeByte(QuicFramer* framer,
+ const QuicAckFrame& frame,
+ QuicDataWriter* writer);
+ static size_t GetIetfAckFrameSize(QuicFramer* framer,
+ const QuicAckFrame& frame);
+ static bool AppendIetfResetStreamFrame(QuicFramer* framer,
+ const QuicRstStreamFrame& frame,
+ QuicDataWriter* writer);
+ static bool ProcessIetfResetStreamFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicRstStreamFrame* frame);
+
+ static bool ProcessPathChallengeFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicPathChallengeFrame* frame);
+ static bool ProcessPathResponseFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicPathResponseFrame* frame);
+
+ static bool AppendPathChallengeFrame(QuicFramer* framer,
+ const QuicPathChallengeFrame& frame,
+ QuicDataWriter* writer);
+ static bool AppendPathResponseFrame(QuicFramer* framer,
+ const QuicPathResponseFrame& frame,
+ QuicDataWriter* writer);
+
+ static bool ProcessStopSendingFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicStopSendingFrame* stop_sending_frame);
+ static bool AppendStopSendingFrame(
+ QuicFramer* framer,
+ const QuicStopSendingFrame& stop_sending_frame,
+ QuicDataWriter* writer);
+
+ // Append/consume IETF-Format MAX_DATA and MAX_STREAM_DATA frames
+ static bool AppendMaxDataFrame(QuicFramer* framer,
+ const QuicWindowUpdateFrame& frame,
+ QuicDataWriter* writer);
+ static bool AppendMaxStreamDataFrame(QuicFramer* framer,
+ const QuicWindowUpdateFrame& frame,
+ QuicDataWriter* writer);
+ static bool ProcessMaxDataFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicWindowUpdateFrame* frame);
+ static bool ProcessMaxStreamDataFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicWindowUpdateFrame* frame);
+ static bool AppendMaxStreamsFrame(QuicFramer* framer,
+ const QuicMaxStreamIdFrame& frame,
+ QuicDataWriter* writer);
+ static bool ProcessMaxStreamsFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicMaxStreamIdFrame* frame,
+ uint64_t frame_type);
+ static bool AppendIetfBlockedFrame(QuicFramer* framer,
+ const QuicBlockedFrame& frame,
+ QuicDataWriter* writer);
+ static bool ProcessIetfBlockedFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicBlockedFrame* frame);
+
+ static bool AppendStreamBlockedFrame(QuicFramer* framer,
+ const QuicBlockedFrame& frame,
+ QuicDataWriter* writer);
+ static bool ProcessStreamBlockedFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicBlockedFrame* frame);
+
+ static bool AppendStreamsBlockedFrame(QuicFramer* framer,
+ const QuicStreamIdBlockedFrame& frame,
+ QuicDataWriter* writer);
+ static bool ProcessStreamsBlockedFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicStreamIdBlockedFrame* frame,
+ uint64_t frame_type);
+
+ static bool AppendNewConnectionIdFrame(QuicFramer* framer,
+ const QuicNewConnectionIdFrame& frame,
+ QuicDataWriter* writer);
+ static bool ProcessNewConnectionIdFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicNewConnectionIdFrame* frame);
+ static bool AppendRetireConnectionIdFrame(
+ QuicFramer* framer,
+ const QuicRetireConnectionIdFrame& frame,
+ QuicDataWriter* writer);
+ static bool ProcessRetireConnectionIdFrame(
+ QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicRetireConnectionIdFrame* frame);
+ static size_t ComputeFrameLength(QuicFramer* framer,
+ const QuicFrame& frame,
+ bool last_frame_in_packet,
+ QuicPacketNumberLength packet_number_length);
+ static void SetFirstSendingPacketNumber(QuicFramer* framer,
+ uint64_t packet_number);
+ static void SetExpectedConnectionIDLength(
+ QuicFramer* framer,
+ uint8_t expected_connection_id_length);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_FRAMER_PEER_H_
diff --git a/quic/test_tools/quic_packet_creator_peer.cc b/quic/test_tools/quic_packet_creator_peer.cc
new file mode 100644
index 0000000..51d9588
--- /dev/null
+++ b/quic/test_tools/quic_packet_creator_peer.cc
@@ -0,0 +1,133 @@
+// Copyright (c) 2013 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 "net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_packet_creator.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+
+namespace quic {
+namespace test {
+
+// static
+bool QuicPacketCreatorPeer::SendVersionInPacket(QuicPacketCreator* creator) {
+ return creator->IncludeVersionInHeader();
+}
+
+// static
+void QuicPacketCreatorPeer::SetSendVersionInPacket(
+ QuicPacketCreator* creator,
+ bool send_version_in_packet) {
+ if (creator->framer_->transport_version() != QUIC_VERSION_99) {
+ creator->send_version_in_packet_ = send_version_in_packet;
+ return;
+ }
+ if (!send_version_in_packet) {
+ creator->packet_.encryption_level = ENCRYPTION_FORWARD_SECURE;
+ return;
+ }
+ DCHECK(creator->packet_.encryption_level < ENCRYPTION_FORWARD_SECURE);
+}
+
+// static
+void QuicPacketCreatorPeer::SetPacketNumberLength(
+ QuicPacketCreator* creator,
+ QuicPacketNumberLength packet_number_length) {
+ creator->packet_.packet_number_length = packet_number_length;
+}
+
+// static
+QuicPacketNumberLength QuicPacketCreatorPeer::GetPacketNumberLength(
+ QuicPacketCreator* creator) {
+ return creator->GetPacketNumberLength();
+}
+
+// static
+QuicVariableLengthIntegerLength
+QuicPacketCreatorPeer::GetRetryTokenLengthLength(QuicPacketCreator* creator) {
+ return creator->GetRetryTokenLengthLength();
+}
+
+// static
+QuicVariableLengthIntegerLength QuicPacketCreatorPeer::GetLengthLength(
+ QuicPacketCreator* creator) {
+ return creator->GetLengthLength();
+}
+
+void QuicPacketCreatorPeer::SetPacketNumber(QuicPacketCreator* creator,
+ uint64_t s) {
+ DCHECK_NE(0u, s);
+ creator->packet_.packet_number = QuicPacketNumber(s);
+}
+
+// static
+void QuicPacketCreatorPeer::ClearPacketNumber(QuicPacketCreator* creator) {
+ creator->packet_.packet_number.Clear();
+}
+
+// static
+void QuicPacketCreatorPeer::FillPacketHeader(QuicPacketCreator* creator,
+ QuicPacketHeader* header) {
+ creator->FillPacketHeader(header);
+}
+
+// static
+void QuicPacketCreatorPeer::CreateStreamFrame(QuicPacketCreator* creator,
+ QuicStreamId id,
+ size_t write_length,
+ size_t iov_offset,
+ QuicStreamOffset offset,
+ bool fin,
+ QuicFrame* frame) {
+ creator->CreateStreamFrame(id, write_length, iov_offset, offset, fin, frame);
+}
+
+// static
+SerializedPacket QuicPacketCreatorPeer::SerializeAllFrames(
+ QuicPacketCreator* creator,
+ const QuicFrames& frames,
+ char* buffer,
+ size_t buffer_len) {
+ DCHECK(creator->queued_frames_.empty());
+ DCHECK(!frames.empty());
+ for (const QuicFrame& frame : frames) {
+ bool success = creator->AddFrame(frame, false, NOT_RETRANSMISSION);
+ DCHECK(success);
+ }
+ creator->SerializePacket(buffer, buffer_len);
+ SerializedPacket packet = creator->packet_;
+ // The caller takes ownership of the QuicEncryptedPacket.
+ creator->packet_.encrypted_buffer = nullptr;
+ DCHECK(packet.retransmittable_frames.empty());
+ return packet;
+}
+
+// static
+OwningSerializedPacketPointer
+QuicPacketCreatorPeer::SerializeConnectivityProbingPacket(
+ QuicPacketCreator* creator) {
+ return creator->SerializeConnectivityProbingPacket();
+}
+
+// static
+OwningSerializedPacketPointer
+QuicPacketCreatorPeer::SerializePathChallengeConnectivityProbingPacket(
+ QuicPacketCreator* creator,
+ QuicPathFrameBuffer* payload) {
+ return creator->SerializePathChallengeConnectivityProbingPacket(payload);
+}
+
+// static
+EncryptionLevel QuicPacketCreatorPeer::GetEncryptionLevel(
+ QuicPacketCreator* creator) {
+ return creator->packet_.encryption_level;
+}
+
+// static
+QuicFramer* QuicPacketCreatorPeer::framer(QuicPacketCreator* creator) {
+ return creator->framer_;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_packet_creator_peer.h b/quic/test_tools/quic_packet_creator_peer.h
new file mode 100644
index 0000000..c637ac2
--- /dev/null
+++ b/quic/test_tools/quic_packet_creator_peer.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2013 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_PACKET_CREATOR_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_PACKET_CREATOR_PEER_H_
+
+#include "base/macros.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+class QuicFramer;
+class QuicPacketCreator;
+
+namespace test {
+
+class QuicPacketCreatorPeer {
+ public:
+ QuicPacketCreatorPeer() = delete;
+
+ static bool SendVersionInPacket(QuicPacketCreator* creator);
+
+ static void SetSendVersionInPacket(QuicPacketCreator* creator,
+ bool send_version_in_packet);
+ static void SetPacketNumberLength(
+ QuicPacketCreator* creator,
+ QuicPacketNumberLength packet_number_length);
+ static QuicPacketNumberLength GetPacketNumberLength(
+ QuicPacketCreator* creator);
+ static QuicVariableLengthIntegerLength GetRetryTokenLengthLength(
+ QuicPacketCreator* creator);
+ static QuicVariableLengthIntegerLength GetLengthLength(
+ QuicPacketCreator* creator);
+ static void SetPacketNumber(QuicPacketCreator* creator, uint64_t s);
+ static void ClearPacketNumber(QuicPacketCreator* creator);
+ static void FillPacketHeader(QuicPacketCreator* creator,
+ QuicPacketHeader* header);
+ static void CreateStreamFrame(QuicPacketCreator* creator,
+ QuicStreamId id,
+ size_t write_length,
+ size_t iov_offset,
+ QuicStreamOffset offset,
+ bool fin,
+ QuicFrame* frame);
+ static SerializedPacket SerializeAllFrames(QuicPacketCreator* creator,
+ const QuicFrames& frames,
+ char* buffer,
+ size_t buffer_len);
+ static OwningSerializedPacketPointer SerializeConnectivityProbingPacket(
+ QuicPacketCreator* creator);
+ static OwningSerializedPacketPointer
+ SerializePathChallengeConnectivityProbingPacket(QuicPacketCreator* creator,
+ QuicPathFrameBuffer* payload);
+
+ static EncryptionLevel GetEncryptionLevel(QuicPacketCreator* creator);
+ static QuicFramer* framer(QuicPacketCreator* creator);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_PACKET_CREATOR_PEER_H_
diff --git a/quic/test_tools/quic_packet_generator_peer.cc b/quic/test_tools/quic_packet_generator_peer.cc
new file mode 100644
index 0000000..91a8752
--- /dev/null
+++ b/quic/test_tools/quic_packet_generator_peer.cc
@@ -0,0 +1,20 @@
+// Copyright 2014 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 "net/third_party/quiche/src/quic/test_tools/quic_packet_generator_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_packet_creator.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_generator.h"
+
+namespace quic {
+namespace test {
+
+// static
+QuicPacketCreator* QuicPacketGeneratorPeer::GetPacketCreator(
+ QuicPacketGenerator* generator) {
+ return &generator->packet_creator_;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_packet_generator_peer.h b/quic/test_tools/quic_packet_generator_peer.h
new file mode 100644
index 0000000..5d8ff4b
--- /dev/null
+++ b/quic/test_tools/quic_packet_generator_peer.h
@@ -0,0 +1,29 @@
+// Copyright 2014 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_PACKET_GENERATOR_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_PACKET_GENERATOR_PEER_H_
+
+#include "base/macros.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+
+class QuicPacketCreator;
+class QuicPacketGenerator;
+
+namespace test {
+
+class QuicPacketGeneratorPeer {
+ public:
+ QuicPacketGeneratorPeer() = delete;
+
+ static QuicPacketCreator* GetPacketCreator(QuicPacketGenerator* generator);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_PACKET_GENERATOR_PEER_H_
diff --git a/quic/test_tools/quic_sent_packet_manager_peer.cc b/quic/test_tools/quic_sent_packet_manager_peer.cc
new file mode 100644
index 0000000..3057e5e
--- /dev/null
+++ b/quic/test_tools/quic_sent_packet_manager_peer.cc
@@ -0,0 +1,239 @@
+// Copyright 2013 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 "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/loss_detection_interface.h"
+#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_unacked_packet_map_peer.h"
+
+namespace quic {
+namespace test {
+
+// static
+size_t QuicSentPacketManagerPeer::GetMaxTailLossProbes(
+ QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->max_tail_loss_probes_;
+}
+
+// static
+void QuicSentPacketManagerPeer::SetMaxTailLossProbes(
+ QuicSentPacketManager* sent_packet_manager,
+ size_t max_tail_loss_probes) {
+ sent_packet_manager->max_tail_loss_probes_ = max_tail_loss_probes;
+}
+
+// static
+bool QuicSentPacketManagerPeer::GetEnableHalfRttTailLossProbe(
+ QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->enable_half_rtt_tail_loss_probe_;
+}
+
+// static
+bool QuicSentPacketManagerPeer::GetUseNewRto(
+ QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->use_new_rto_;
+}
+
+// static
+void QuicSentPacketManagerPeer::SetPerspective(
+ QuicSentPacketManager* sent_packet_manager,
+ Perspective perspective) {
+ QuicUnackedPacketMapPeer::SetPerspective(
+ &sent_packet_manager->unacked_packets_, perspective);
+}
+
+// static
+SendAlgorithmInterface* QuicSentPacketManagerPeer::GetSendAlgorithm(
+ const QuicSentPacketManager& sent_packet_manager) {
+ return sent_packet_manager.send_algorithm_.get();
+}
+
+// static
+void QuicSentPacketManagerPeer::SetSendAlgorithm(
+ QuicSentPacketManager* sent_packet_manager,
+ SendAlgorithmInterface* send_algorithm) {
+ sent_packet_manager->SetSendAlgorithm(send_algorithm);
+}
+
+// static
+const LossDetectionInterface* QuicSentPacketManagerPeer::GetLossAlgorithm(
+ QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->loss_algorithm_;
+}
+
+// static
+void QuicSentPacketManagerPeer::SetLossAlgorithm(
+ QuicSentPacketManager* sent_packet_manager,
+ LossDetectionInterface* loss_detector) {
+ sent_packet_manager->loss_algorithm_ = loss_detector;
+}
+
+// static
+RttStats* QuicSentPacketManagerPeer::GetRttStats(
+ QuicSentPacketManager* sent_packet_manager) {
+ return &sent_packet_manager->rtt_stats_;
+}
+
+// static
+bool QuicSentPacketManagerPeer::HasPendingPackets(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->unacked_packets_.HasInFlightPackets();
+}
+
+// static
+bool QuicSentPacketManagerPeer::IsRetransmission(
+ QuicSentPacketManager* sent_packet_manager,
+ uint64_t packet_number) {
+ DCHECK(HasRetransmittableFrames(sent_packet_manager, packet_number));
+ if (!HasRetransmittableFrames(sent_packet_manager, packet_number)) {
+ return false;
+ }
+ if (sent_packet_manager->session_decides_what_to_write()) {
+ return sent_packet_manager->unacked_packets_
+ .GetTransmissionInfo(QuicPacketNumber(packet_number))
+ .transmission_type != NOT_RETRANSMISSION;
+ }
+ for (auto transmission_info : sent_packet_manager->unacked_packets_) {
+ if (transmission_info.retransmission.IsInitialized() &&
+ transmission_info.retransmission == QuicPacketNumber(packet_number)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// static
+void QuicSentPacketManagerPeer::MarkForRetransmission(
+ QuicSentPacketManager* sent_packet_manager,
+ uint64_t packet_number,
+ TransmissionType transmission_type) {
+ sent_packet_manager->MarkForRetransmission(QuicPacketNumber(packet_number),
+ transmission_type);
+}
+
+// static
+QuicTime::Delta QuicSentPacketManagerPeer::GetRetransmissionDelay(
+ const QuicSentPacketManager* sent_packet_manager,
+ size_t consecutive_rto_count) {
+ return sent_packet_manager->GetRetransmissionDelay(consecutive_rto_count);
+}
+
+// static
+QuicTime::Delta QuicSentPacketManagerPeer::GetRetransmissionDelay(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->GetRetransmissionDelay();
+}
+
+// static
+QuicTime::Delta QuicSentPacketManagerPeer::GetTailLossProbeDelay(
+ const QuicSentPacketManager* sent_packet_manager,
+ size_t consecutive_tlp_count) {
+ return sent_packet_manager->GetTailLossProbeDelay(consecutive_tlp_count);
+}
+
+// static
+QuicTime::Delta QuicSentPacketManagerPeer::GetTailLossProbeDelay(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->GetTailLossProbeDelay();
+}
+
+// static
+bool QuicSentPacketManagerPeer::HasUnackedCryptoPackets(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->unacked_packets_.HasPendingCryptoPackets();
+}
+
+// static
+size_t QuicSentPacketManagerPeer::GetNumRetransmittablePackets(
+ const QuicSentPacketManager* sent_packet_manager) {
+ size_t num_unacked_packets = 0;
+ for (auto it = sent_packet_manager->unacked_packets_.begin();
+ it != sent_packet_manager->unacked_packets_.end(); ++it) {
+ if (sent_packet_manager->unacked_packets_.HasRetransmittableFrames(*it)) {
+ ++num_unacked_packets;
+ }
+ }
+ return num_unacked_packets;
+}
+
+// static
+QuicByteCount QuicSentPacketManagerPeer::GetBytesInFlight(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->unacked_packets_.bytes_in_flight();
+}
+
+// static
+void QuicSentPacketManagerPeer::SetConsecutiveRtoCount(
+ QuicSentPacketManager* sent_packet_manager,
+ size_t count) {
+ sent_packet_manager->consecutive_rto_count_ = count;
+}
+
+// static
+void QuicSentPacketManagerPeer::SetConsecutiveTlpCount(
+ QuicSentPacketManager* sent_packet_manager,
+ size_t count) {
+ sent_packet_manager->consecutive_tlp_count_ = count;
+}
+
+// static
+QuicSustainedBandwidthRecorder& QuicSentPacketManagerPeer::GetBandwidthRecorder(
+ QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->sustained_bandwidth_recorder_;
+}
+
+// static
+bool QuicSentPacketManagerPeer::UsingPacing(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->using_pacing_;
+}
+
+// static
+void QuicSentPacketManagerPeer::SetUsingPacing(
+ QuicSentPacketManager* sent_packet_manager,
+ bool using_pacing) {
+ sent_packet_manager->using_pacing_ = using_pacing;
+}
+
+// static
+bool QuicSentPacketManagerPeer::IsUnacked(
+ QuicSentPacketManager* sent_packet_manager,
+ uint64_t packet_number) {
+ return sent_packet_manager->unacked_packets_.IsUnacked(
+ QuicPacketNumber(packet_number));
+}
+
+// static
+bool QuicSentPacketManagerPeer::HasRetransmittableFrames(
+ QuicSentPacketManager* sent_packet_manager,
+ uint64_t packet_number) {
+ return sent_packet_manager->unacked_packets_.HasRetransmittableFrames(
+ QuicPacketNumber(packet_number));
+}
+
+// static
+QuicUnackedPacketMap* QuicSentPacketManagerPeer::GetUnackedPacketMap(
+ QuicSentPacketManager* sent_packet_manager) {
+ return &sent_packet_manager->unacked_packets_;
+}
+
+// static
+void QuicSentPacketManagerPeer::DisablePacerBursts(
+ QuicSentPacketManager* sent_packet_manager) {
+ sent_packet_manager->pacing_sender_.burst_tokens_ = 0;
+ sent_packet_manager->pacing_sender_.initial_burst_size_ = 0;
+}
+
+// static
+void QuicSentPacketManagerPeer::SetNextPacedPacketTime(
+ QuicSentPacketManager* sent_packet_manager,
+ QuicTime time) {
+ sent_packet_manager->pacing_sender_.ideal_next_packet_send_time_ = time;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_sent_packet_manager_peer.h b/quic/test_tools/quic_sent_packet_manager_peer.h
new file mode 100644
index 0000000..b8ec1d3
--- /dev/null
+++ b/quic/test_tools/quic_sent_packet_manager_peer.h
@@ -0,0 +1,115 @@
+// Copyright 2013 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_SENT_PACKET_MANAGER_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_SENT_PACKET_MANAGER_PEER_H_
+
+#include "base/macros.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h"
+
+namespace quic {
+
+class SendAlgorithmInterface;
+
+namespace test {
+
+class QuicSentPacketManagerPeer {
+ public:
+ QuicSentPacketManagerPeer() = delete;
+
+ static size_t GetMaxTailLossProbes(
+ QuicSentPacketManager* sent_packet_manager);
+
+ static void SetMaxTailLossProbes(QuicSentPacketManager* sent_packet_manager,
+ size_t max_tail_loss_probes);
+
+ static bool GetEnableHalfRttTailLossProbe(
+ QuicSentPacketManager* sent_packet_manager);
+
+ static bool GetUseNewRto(QuicSentPacketManager* sent_packet_manager);
+
+ static void SetPerspective(QuicSentPacketManager* sent_packet_manager,
+ Perspective perspective);
+
+ static SendAlgorithmInterface* GetSendAlgorithm(
+ const QuicSentPacketManager& sent_packet_manager);
+
+ static void SetSendAlgorithm(QuicSentPacketManager* sent_packet_manager,
+ SendAlgorithmInterface* send_algorithm);
+
+ static const LossDetectionInterface* GetLossAlgorithm(
+ QuicSentPacketManager* sent_packet_manager);
+
+ static void SetLossAlgorithm(QuicSentPacketManager* sent_packet_manager,
+ LossDetectionInterface* loss_detector);
+
+ static RttStats* GetRttStats(QuicSentPacketManager* sent_packet_manager);
+
+ static bool HasPendingPackets(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ // Returns true if |packet_number| is a retransmission of a packet.
+ static bool IsRetransmission(QuicSentPacketManager* sent_packet_manager,
+ uint64_t packet_number);
+
+ static void MarkForRetransmission(QuicSentPacketManager* sent_packet_manager,
+ uint64_t packet_number,
+ TransmissionType transmission_type);
+
+ static QuicTime::Delta GetRetransmissionDelay(
+ const QuicSentPacketManager* sent_packet_manager,
+ size_t consecutive_rto_count);
+ static QuicTime::Delta GetRetransmissionDelay(
+ const QuicSentPacketManager* sent_packet_manager);
+ static QuicTime::Delta GetTailLossProbeDelay(
+ const QuicSentPacketManager* sent_packet_manager,
+ size_t consecutive_tlp_count);
+ static QuicTime::Delta GetTailLossProbeDelay(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static bool HasUnackedCryptoPackets(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static size_t GetNumRetransmittablePackets(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static QuicByteCount GetBytesInFlight(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static void SetConsecutiveRtoCount(QuicSentPacketManager* sent_packet_manager,
+ size_t count);
+
+ static void SetConsecutiveTlpCount(QuicSentPacketManager* sent_packet_manager,
+ size_t count);
+
+ static QuicSustainedBandwidthRecorder& GetBandwidthRecorder(
+ QuicSentPacketManager* sent_packet_manager);
+
+ static void SetUsingPacing(QuicSentPacketManager* sent_packet_manager,
+ bool using_pacing);
+
+ static bool UsingPacing(const QuicSentPacketManager* sent_packet_manager);
+
+ static bool IsUnacked(QuicSentPacketManager* sent_packet_manager,
+ uint64_t packet_number);
+
+ static bool HasRetransmittableFrames(
+ QuicSentPacketManager* sent_packet_manager,
+ uint64_t packet_number);
+
+ static QuicUnackedPacketMap* GetUnackedPacketMap(
+ QuicSentPacketManager* sent_packet_manager);
+
+ static void DisablePacerBursts(QuicSentPacketManager* sent_packet_manager);
+
+ static void SetNextPacedPacketTime(QuicSentPacketManager* sent_packet_manager,
+ QuicTime time);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_SENT_PACKET_MANAGER_PEER_H_
diff --git a/quic/test_tools/quic_server_peer.cc b/quic/test_tools/quic_server_peer.cc
new file mode 100644
index 0000000..9e8d962
--- /dev/null
+++ b/quic/test_tools/quic_server_peer.cc
@@ -0,0 +1,32 @@
+// Copyright 2013 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 "net/third_party/quiche/src/quic/test_tools/quic_server_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_dispatcher.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_reader.h"
+#include "net/third_party/quiche/src/quic/tools/quic_server.h"
+
+namespace quic {
+namespace test {
+
+// static
+bool QuicServerPeer::SetSmallSocket(QuicServer* server) {
+ int size = 1024 * 10;
+ return setsockopt(server->fd_, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) !=
+ -1;
+}
+
+// static
+QuicDispatcher* QuicServerPeer::GetDispatcher(QuicServer* server) {
+ return server->dispatcher_.get();
+}
+
+// static
+void QuicServerPeer::SetReader(QuicServer* server, QuicPacketReader* reader) {
+ server->packet_reader_.reset(reader);
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_server_peer.h b/quic/test_tools/quic_server_peer.h
new file mode 100644
index 0000000..62243d6
--- /dev/null
+++ b/quic/test_tools/quic_server_peer.h
@@ -0,0 +1,30 @@
+// Copyright 2013 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_SERVER_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_SERVER_PEER_H_
+
+#include "base/macros.h"
+
+namespace quic {
+
+class QuicDispatcher;
+class QuicServer;
+class QuicPacketReader;
+
+namespace test {
+
+class QuicServerPeer {
+ public:
+ QuicServerPeer() = delete;
+
+ static bool SetSmallSocket(QuicServer* server);
+ static QuicDispatcher* GetDispatcher(QuicServer* server);
+ static void SetReader(QuicServer* server, QuicPacketReader* reader);
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_SERVER_PEER_H_
diff --git a/quic/test_tools/quic_server_session_base_peer.h b/quic/test_tools/quic_server_session_base_peer.h
new file mode 100644
index 0000000..30c9b22
--- /dev/null
+++ b/quic/test_tools/quic_server_session_base_peer.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2018 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_SERVER_SESSION_BASE_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_SERVER_SESSION_BASE_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/http/quic_server_session_base.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+
+namespace quic {
+namespace test {
+
+class QuicServerSessionBasePeer {
+ public:
+ static QuicStream* GetOrCreateDynamicStream(QuicServerSessionBase* s,
+ QuicStreamId id) {
+ return s->GetOrCreateDynamicStream(id);
+ }
+ static void SetCryptoStream(QuicServerSessionBase* s,
+ QuicCryptoServerStream* crypto_stream) {
+ s->crypto_stream_.reset(crypto_stream);
+ s->RegisterStaticStream(
+ QuicUtils::GetCryptoStreamId(s->connection()->transport_version()),
+ crypto_stream);
+ }
+ static bool IsBandwidthResumptionEnabled(QuicServerSessionBase* s) {
+ return s->bandwidth_resumption_enabled_;
+ }
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_SERVER_SESSION_BASE_PEER_H_
diff --git a/quic/test_tools/quic_session_peer.cc b/quic/test_tools/quic_session_peer.cc
new file mode 100644
index 0000000..37e5013
--- /dev/null
+++ b/quic/test_tools/quic_session_peer.cc
@@ -0,0 +1,197 @@
+// Copyright (c) 2012 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 "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+
+namespace quic {
+namespace test {
+
+// static
+QuicStreamId QuicSessionPeer::GetNextOutgoingBidirectionalStreamId(
+ QuicSession* session) {
+ return session->GetNextOutgoingBidirectionalStreamId();
+}
+
+// static
+QuicStreamId QuicSessionPeer::GetNextOutgoingUnidirectionalStreamId(
+ QuicSession* session) {
+ return session->GetNextOutgoingUnidirectionalStreamId();
+}
+
+// static
+void QuicSessionPeer::SetNextOutgoingBidirectionalStreamId(QuicSession* session,
+ QuicStreamId id) {
+ if (session->connection()->transport_version() == QUIC_VERSION_99) {
+ session->v99_streamid_manager_.bidirectional_stream_id_manager_
+ .next_outgoing_stream_id_ = id;
+ return;
+ }
+ session->stream_id_manager_.next_outgoing_stream_id_ = id;
+}
+
+// static
+void QuicSessionPeer::SetMaxOpenIncomingStreams(QuicSession* session,
+ uint32_t max_streams) {
+ if (session->connection()->transport_version() == QUIC_VERSION_99) {
+ session->v99_streamid_manager_.SetMaxOpenIncomingStreams(max_streams);
+ return;
+ }
+ session->stream_id_manager_.set_max_open_incoming_streams(max_streams);
+}
+
+// static
+void QuicSessionPeer::SetMaxOpenOutgoingStreams(QuicSession* session,
+ uint32_t max_streams) {
+ if (session->connection()->transport_version() == QUIC_VERSION_99) {
+ session->v99_streamid_manager_.SetMaxOpenOutgoingStreams(max_streams);
+ return;
+ }
+ session->stream_id_manager_.set_max_open_outgoing_streams(max_streams);
+}
+
+// static
+QuicCryptoStream* QuicSessionPeer::GetMutableCryptoStream(
+ QuicSession* session) {
+ return session->GetMutableCryptoStream();
+}
+
+// static
+QuicWriteBlockedList* QuicSessionPeer::GetWriteBlockedStreams(
+ QuicSession* session) {
+ return &session->write_blocked_streams_;
+}
+
+// static
+QuicStream* QuicSessionPeer::GetOrCreateDynamicStream(QuicSession* session,
+ QuicStreamId stream_id) {
+ return session->GetOrCreateDynamicStream(stream_id);
+}
+
+// static
+std::map<QuicStreamId, QuicStreamOffset>&
+QuicSessionPeer::GetLocallyClosedStreamsHighestOffset(QuicSession* session) {
+ return session->locally_closed_streams_highest_offset_;
+}
+
+// static
+QuicSession::StaticStreamMap& QuicSessionPeer::static_streams(
+ QuicSession* session) {
+ return session->static_stream_map_;
+}
+
+// static
+QuicSession::DynamicStreamMap& QuicSessionPeer::dynamic_streams(
+ QuicSession* session) {
+ return session->dynamic_streams();
+}
+
+// static
+const QuicSession::ClosedStreams& QuicSessionPeer::closed_streams(
+ QuicSession* session) {
+ return *session->closed_streams();
+}
+
+// static
+QuicSession::ZombieStreamMap& QuicSessionPeer::zombie_streams(
+ QuicSession* session) {
+ return session->zombie_streams_;
+}
+
+// static
+QuicUnorderedSet<QuicStreamId>* QuicSessionPeer::GetDrainingStreams(
+ QuicSession* session) {
+ return &session->draining_streams_;
+}
+
+// static
+void QuicSessionPeer::ActivateStream(QuicSession* session,
+ std::unique_ptr<QuicStream> stream) {
+ return session->ActivateStream(std::move(stream));
+}
+
+// static
+bool QuicSessionPeer::IsStreamClosed(QuicSession* session, QuicStreamId id) {
+ DCHECK_NE(0u, id);
+ return session->IsClosedStream(id);
+}
+
+// static
+bool QuicSessionPeer::IsStreamCreated(QuicSession* session, QuicStreamId id) {
+ DCHECK_NE(0u, id);
+ return QuicContainsKey(session->dynamic_streams(), id);
+}
+
+// static
+bool QuicSessionPeer::IsStreamAvailable(QuicSession* session, QuicStreamId id) {
+ DCHECK_NE(0u, id);
+ if (session->connection()->transport_version() == QUIC_VERSION_99) {
+ if (id % kV99StreamIdIncrement < 2) {
+ return QuicContainsKey(
+ session->v99_streamid_manager_.bidirectional_stream_id_manager_
+ .available_streams_,
+ id);
+ }
+ return QuicContainsKey(
+ session->v99_streamid_manager_.unidirectional_stream_id_manager_
+ .available_streams_,
+ id);
+ }
+ return QuicContainsKey(session->stream_id_manager_.available_streams_, id);
+}
+
+// static
+QuicStream* QuicSessionPeer::GetStream(QuicSession* session, QuicStreamId id) {
+ return session->GetStream(id);
+}
+
+// static
+bool QuicSessionPeer::IsStreamWriteBlocked(QuicSession* session,
+ QuicStreamId id) {
+ return session->write_blocked_streams_.IsStreamBlocked(id);
+}
+
+// static
+QuicAlarm* QuicSessionPeer::GetCleanUpClosedStreamsAlarm(QuicSession* session) {
+ return session->closed_streams_clean_up_alarm_.get();
+}
+
+// static
+LegacyQuicStreamIdManager* QuicSessionPeer::GetStreamIdManager(
+ QuicSession* session) {
+ return &session->stream_id_manager_;
+}
+
+// static
+UberQuicStreamIdManager* QuicSessionPeer::v99_streamid_manager(
+ QuicSession* session) {
+ return &session->v99_streamid_manager_;
+}
+
+// static
+QuicStreamIdManager* QuicSessionPeer::v99_bidirectional_stream_id_manager(
+ QuicSession* session) {
+ return &session->v99_streamid_manager_.bidirectional_stream_id_manager_;
+}
+
+// static
+QuicStreamIdManager* QuicSessionPeer::v99_unidirectional_stream_id_manager(
+ QuicSession* session) {
+ return &session->v99_streamid_manager_.unidirectional_stream_id_manager_;
+}
+
+// static
+void QuicSessionPeer::SendRstStreamInner(QuicSession* session,
+ QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written,
+ bool close_write_side_only) {
+ session->SendRstStreamInner(id, error, bytes_written, close_write_side_only);
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_session_peer.h b/quic/test_tools/quic_session_peer.h
new file mode 100644
index 0000000..8c2e72c
--- /dev/null
+++ b/quic/test_tools/quic_session_peer.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_SESSION_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_SESSION_PEER_H_
+
+#include <cstdint>
+#include <map>
+#include <memory>
+
+#include "base/macros.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+
+namespace quic {
+
+class QuicCryptoStream;
+class QuicSession;
+class QuicStream;
+
+namespace test {
+
+class QuicSessionPeer {
+ public:
+ QuicSessionPeer() = delete;
+
+ static QuicStreamId GetNextOutgoingBidirectionalStreamId(
+ QuicSession* session);
+ static QuicStreamId GetNextOutgoingUnidirectionalStreamId(
+ QuicSession* session);
+ static void SetNextOutgoingBidirectionalStreamId(QuicSession* session,
+ QuicStreamId id);
+ static void SetMaxOpenIncomingStreams(QuicSession* session,
+ uint32_t max_streams);
+ static void SetMaxOpenOutgoingStreams(QuicSession* session,
+ uint32_t max_streams);
+ static QuicCryptoStream* GetMutableCryptoStream(QuicSession* session);
+ static QuicWriteBlockedList* GetWriteBlockedStreams(QuicSession* session);
+ static QuicStream* GetOrCreateDynamicStream(QuicSession* session,
+ QuicStreamId stream_id);
+ static std::map<QuicStreamId, QuicStreamOffset>&
+ GetLocallyClosedStreamsHighestOffset(QuicSession* session);
+ static QuicSession::StaticStreamMap& static_streams(QuicSession* session);
+ static QuicSession::DynamicStreamMap& dynamic_streams(QuicSession* session);
+ static const QuicSession::ClosedStreams& closed_streams(QuicSession* session);
+ static QuicSession::ZombieStreamMap& zombie_streams(QuicSession* session);
+ static QuicUnorderedSet<QuicStreamId>* GetDrainingStreams(
+ QuicSession* session);
+ static void ActivateStream(QuicSession* session,
+ std::unique_ptr<QuicStream> stream);
+
+ // Discern the state of a stream. Exactly one of these should be true at a
+ // time for any stream id > 0 (other than the special streams 1 and 3).
+ static bool IsStreamClosed(QuicSession* session, QuicStreamId id);
+ static bool IsStreamCreated(QuicSession* session, QuicStreamId id);
+ static bool IsStreamAvailable(QuicSession* session, QuicStreamId id);
+
+ static QuicStream* GetStream(QuicSession* session, QuicStreamId id);
+ static bool IsStreamWriteBlocked(QuicSession* session, QuicStreamId id);
+ static QuicAlarm* GetCleanUpClosedStreamsAlarm(QuicSession* session);
+ static LegacyQuicStreamIdManager* GetStreamIdManager(QuicSession* session);
+ static UberQuicStreamIdManager* v99_streamid_manager(QuicSession* session);
+ static QuicStreamIdManager* v99_bidirectional_stream_id_manager(
+ QuicSession* session);
+ static QuicStreamIdManager* v99_unidirectional_stream_id_manager(
+ QuicSession* session);
+ static void SendRstStreamInner(QuicSession* session,
+ QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written,
+ bool close_write_side_only);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_SESSION_PEER_H_
diff --git a/quic/test_tools/quic_spdy_session_peer.cc b/quic/test_tools/quic_spdy_session_peer.cc
new file mode 100644
index 0000000..b739fa1
--- /dev/null
+++ b/quic/test_tools/quic_spdy_session_peer.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2015 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 "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+
+namespace quic {
+namespace test {
+
+// static
+QuicHeadersStream* QuicSpdySessionPeer::GetHeadersStream(
+ QuicSpdySession* session) {
+ return session->headers_stream_.get();
+}
+
+// static
+void QuicSpdySessionPeer::SetHeadersStream(QuicSpdySession* session,
+ QuicHeadersStream* headers_stream) {
+ session->headers_stream_.reset(headers_stream);
+ if (headers_stream != nullptr) {
+ session->RegisterStaticStream(headers_stream->id(), headers_stream);
+ }
+}
+
+// static
+const spdy::SpdyFramer& QuicSpdySessionPeer::GetSpdyFramer(
+ QuicSpdySession* session) {
+ return session->spdy_framer_;
+}
+
+void QuicSpdySessionPeer::SetHpackEncoderDebugVisitor(
+ QuicSpdySession* session,
+ std::unique_ptr<QuicHpackDebugVisitor> visitor) {
+ session->SetHpackEncoderDebugVisitor(std::move(visitor));
+}
+
+void QuicSpdySessionPeer::SetHpackDecoderDebugVisitor(
+ QuicSpdySession* session,
+ std::unique_ptr<QuicHpackDebugVisitor> visitor) {
+ session->SetHpackDecoderDebugVisitor(std::move(visitor));
+}
+
+void QuicSpdySessionPeer::SetMaxUncompressedHeaderBytes(
+ QuicSpdySession* session,
+ size_t set_max_uncompressed_header_bytes) {
+ session->set_max_uncompressed_header_bytes(set_max_uncompressed_header_bytes);
+}
+
+// static
+size_t QuicSpdySessionPeer::WriteHeadersOnHeadersStream(
+ QuicSpdySession* session,
+ QuicStreamId id,
+ spdy::SpdyHeaderBlock headers,
+ bool fin,
+ spdy::SpdyPriority priority,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+ return session->WriteHeadersOnHeadersStream(
+ id, std::move(headers), fin, priority, std::move(ack_listener));
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_spdy_session_peer.h b/quic/test_tools/quic_spdy_session_peer.h
new file mode 100644
index 0000000..1993ec5
--- /dev/null
+++ b/quic/test_tools/quic_spdy_session_peer.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2015 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_SPDY_SESSION_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_SPDY_SESSION_PEER_H_
+
+#include "base/macros.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_framer.h"
+
+namespace quic {
+
+class QuicHeadersStream;
+class QuicSpdySession;
+class QuicHpackDebugVisitor;
+
+namespace test {
+
+class QuicSpdySessionPeer {
+ public:
+ QuicSpdySessionPeer() = delete;
+
+ static QuicHeadersStream* GetHeadersStream(QuicSpdySession* session);
+ static void SetHeadersStream(QuicSpdySession* session,
+ QuicHeadersStream* headers_stream);
+ static const spdy::SpdyFramer& GetSpdyFramer(QuicSpdySession* session);
+ static void SetHpackEncoderDebugVisitor(
+ QuicSpdySession* session,
+ std::unique_ptr<QuicHpackDebugVisitor> visitor);
+ static void SetHpackDecoderDebugVisitor(
+ QuicSpdySession* session,
+ std::unique_ptr<QuicHpackDebugVisitor> visitor);
+ static void SetMaxUncompressedHeaderBytes(
+ QuicSpdySession* session,
+ size_t set_max_uncompressed_header_bytes);
+ static size_t WriteHeadersOnHeadersStream(
+ QuicSpdySession* session,
+ QuicStreamId id,
+ spdy::SpdyHeaderBlock headers,
+ bool fin,
+ spdy::SpdyPriority priority,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_SPDY_SESSION_PEER_H_
diff --git a/quic/test_tools/quic_spdy_stream_peer.cc b/quic/test_tools/quic_spdy_stream_peer.cc
new file mode 100644
index 0000000..6632ec1
--- /dev/null
+++ b/quic/test_tools/quic_spdy_stream_peer.cc
@@ -0,0 +1,26 @@
+// 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 "net/third_party/quiche/src/quic/test_tools/quic_spdy_stream_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h"
+
+namespace quic {
+namespace test {
+
+// static
+void QuicSpdyStreamPeer::set_ack_listener(
+ QuicSpdyStream* stream,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+ stream->set_ack_listener(std::move(ack_listener));
+}
+
+// static
+const QuicIntervalSet<QuicStreamOffset>&
+QuicSpdyStreamPeer::unacked_frame_headers_offsets(QuicSpdyStream* stream) {
+ return stream->unacked_frame_headers_offsets();
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_spdy_stream_peer.h b/quic/test_tools/quic_spdy_stream_peer.h
new file mode 100644
index 0000000..7b3fe7c
--- /dev/null
+++ b/quic/test_tools/quic_spdy_stream_peer.h
@@ -0,0 +1,31 @@
+// 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_SPDY_STREAM_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_SPDY_STREAM_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_ack_listener_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval_set.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+
+namespace quic {
+
+class QuicSpdyStream;
+
+namespace test {
+
+class QuicSpdyStreamPeer {
+ public:
+ static void set_ack_listener(
+ QuicSpdyStream* stream,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+ static const QuicIntervalSet<QuicStreamOffset>& unacked_frame_headers_offsets(
+ QuicSpdyStream* stream);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_SPDY_STREAM_PEER_H_
diff --git a/quic/test_tools/quic_stream_id_manager_peer.cc b/quic/test_tools/quic_stream_id_manager_peer.cc
new file mode 100644
index 0000000..705ee27
--- /dev/null
+++ b/quic/test_tools/quic_stream_id_manager_peer.cc
@@ -0,0 +1,36 @@
+// Copyright (c) 2018 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 "net/third_party/quiche/src/quic/test_tools/quic_stream_id_manager_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_stream_id_manager.h"
+
+namespace quic {
+namespace test {
+
+// static
+void QuicStreamIdManagerPeer::IncrementMaximumAllowedOutgoingStreamId(
+ QuicStreamIdManager* stream_id_manager,
+ int increment) {
+ stream_id_manager->max_allowed_outgoing_stream_id_ +=
+ (increment * kV99StreamIdIncrement);
+}
+
+// static
+void QuicStreamIdManagerPeer::IncrementMaximumAllowedIncomingStreamId(
+ QuicStreamIdManager* stream_id_manager,
+ int increment) {
+ stream_id_manager->actual_max_allowed_incoming_stream_id_ +=
+ (increment * kV99StreamIdIncrement);
+ stream_id_manager->advertised_max_allowed_incoming_stream_id_ +=
+ (increment * kV99StreamIdIncrement);
+}
+
+// static
+void QuicStreamIdManagerPeer::SetMaxOpenIncomingStreams(
+ QuicStreamIdManager* stream_id_manager,
+ size_t max_streams) {
+ stream_id_manager->SetMaxOpenIncomingStreams(max_streams);
+}
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_stream_id_manager_peer.h b/quic/test_tools/quic_stream_id_manager_peer.h
new file mode 100644
index 0000000..2ec07b1
--- /dev/null
+++ b/quic/test_tools/quic_stream_id_manager_peer.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2018 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.
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_ID_MANAGER_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_ID_MANAGER_PEER_H_
+
+#include <stddef.h>
+
+namespace quic {
+
+class QuicStreamIdManager;
+
+namespace test {
+
+class QuicStreamIdManagerPeer {
+ public:
+ QuicStreamIdManagerPeer() = delete;
+ static void IncrementMaximumAllowedOutgoingStreamId(
+ QuicStreamIdManager* stream_id_manager,
+ int increment);
+ static void IncrementMaximumAllowedIncomingStreamId(
+ QuicStreamIdManager* stream_id_manager,
+ int increment);
+ static void SetMaxOpenIncomingStreams(QuicStreamIdManager* stream_id_manager,
+ size_t max_streams);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_SESSION_PEER_H_
diff --git a/quic/test_tools/quic_stream_peer.cc b/quic/test_tools/quic_stream_peer.cc
new file mode 100644
index 0000000..7607523
--- /dev/null
+++ b/quic/test_tools/quic_stream_peer.cc
@@ -0,0 +1,91 @@
+// Copyright (c) 2012 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 "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
+
+#include <list>
+
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_send_buffer_peer.h"
+
+namespace quic {
+namespace test {
+
+// static
+void QuicStreamPeer::SetWriteSideClosed(bool value, QuicStream* stream) {
+ stream->write_side_closed_ = value;
+}
+
+// static
+void QuicStreamPeer::SetStreamBytesWritten(
+ QuicStreamOffset stream_bytes_written,
+ QuicStream* stream) {
+ stream->send_buffer_.stream_bytes_written_ = stream_bytes_written;
+ stream->send_buffer_.stream_bytes_outstanding_ = stream_bytes_written;
+ QuicStreamSendBufferPeer::SetStreamOffset(&stream->send_buffer_,
+ stream_bytes_written);
+}
+
+// static
+bool QuicStreamPeer::read_side_closed(QuicStream* stream) {
+ return stream->read_side_closed();
+}
+
+// static
+bool QuicStreamPeer::write_side_closed(QuicStream* stream) {
+ return stream->write_side_closed();
+}
+
+// static
+void QuicStreamPeer::CloseReadSide(QuicStream* stream) {
+ stream->CloseReadSide();
+}
+
+// static
+bool QuicStreamPeer::FinSent(QuicStream* stream) {
+ return stream->fin_sent_;
+}
+
+// static
+bool QuicStreamPeer::RstSent(QuicStream* stream) {
+ return stream->rst_sent_;
+}
+
+// static
+uint32_t QuicStreamPeer::SizeOfQueuedData(QuicStream* stream) {
+ return stream->BufferedDataBytes();
+}
+
+// static
+bool QuicStreamPeer::StreamContributesToConnectionFlowControl(
+ QuicStream* stream) {
+ return stream->stream_contributes_to_connection_flow_control_;
+}
+
+// static
+void QuicStreamPeer::WriteOrBufferData(
+ QuicStream* stream,
+ QuicStringPiece data,
+ bool fin,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+ stream->WriteOrBufferData(data, fin, std::move(ack_listener));
+}
+
+// static
+QuicStreamSequencer* QuicStreamPeer::sequencer(QuicStream* stream) {
+ return &(stream->sequencer_);
+}
+
+// static
+QuicSession* QuicStreamPeer::session(QuicStream* stream) {
+ return stream->session();
+}
+
+// static
+QuicStreamSendBuffer& QuicStreamPeer::SendBuffer(QuicStream* stream) {
+ return stream->send_buffer_;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_stream_peer.h b/quic/test_tools/quic_stream_peer.h
new file mode 100644
index 0000000..e0058ba
--- /dev/null
+++ b/quic/test_tools/quic_stream_peer.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_PEER_H_
+
+#include <cstdint>
+
+#include "base/macros.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QuicStream;
+class QuicSession;
+
+namespace test {
+
+class QuicStreamPeer {
+ public:
+ QuicStreamPeer() = delete;
+
+ static void SetWriteSideClosed(bool value, QuicStream* stream);
+ static void SetStreamBytesWritten(QuicStreamOffset stream_bytes_written,
+ QuicStream* stream);
+ static bool write_side_closed(QuicStream* stream);
+ static bool read_side_closed(QuicStream* stream);
+ static void CloseReadSide(QuicStream* stream);
+
+ static bool FinSent(QuicStream* stream);
+ static bool RstSent(QuicStream* stream);
+
+ static uint32_t SizeOfQueuedData(QuicStream* stream);
+
+ static bool StreamContributesToConnectionFlowControl(QuicStream* stream);
+
+ static void WriteOrBufferData(
+ QuicStream* stream,
+ QuicStringPiece data,
+ bool fin,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+
+ static QuicStreamSequencer* sequencer(QuicStream* stream);
+ static QuicSession* session(QuicStream* stream);
+
+ static QuicStreamSendBuffer& SendBuffer(QuicStream* stream);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_PEER_H_
diff --git a/quic/test_tools/quic_stream_send_buffer_peer.cc b/quic/test_tools/quic_stream_send_buffer_peer.cc
new file mode 100644
index 0000000..22e5e56
--- /dev/null
+++ b/quic/test_tools/quic_stream_send_buffer_peer.cc
@@ -0,0 +1,39 @@
+// Copyright 2017 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 "net/third_party/quiche/src/quic/test_tools/quic_stream_send_buffer_peer.h"
+
+namespace quic {
+
+namespace test {
+
+// static
+void QuicStreamSendBufferPeer::SetStreamOffset(
+ QuicStreamSendBuffer* send_buffer,
+ QuicStreamOffset stream_offset) {
+ send_buffer->stream_offset_ = stream_offset;
+}
+
+// static
+const BufferedSlice* QuicStreamSendBufferPeer::CurrentWriteSlice(
+ QuicStreamSendBuffer* send_buffer) {
+ if (send_buffer->write_index_ == -1) {
+ return nullptr;
+ }
+ return &send_buffer->buffered_slices_[send_buffer->write_index_];
+}
+
+// static
+QuicByteCount QuicStreamSendBufferPeer::TotalLength(
+ QuicStreamSendBuffer* send_buffer) {
+ QuicByteCount length = 0;
+ for (const auto& slice : send_buffer->buffered_slices_) {
+ length += slice.slice.length();
+ }
+ return length;
+}
+
+} // namespace test
+
+} // namespace quic
diff --git a/quic/test_tools/quic_stream_send_buffer_peer.h b/quic/test_tools/quic_stream_send_buffer_peer.h
new file mode 100644
index 0000000..f61cb00
--- /dev/null
+++ b/quic/test_tools/quic_stream_send_buffer_peer.h
@@ -0,0 +1,29 @@
+// Copyright 2017 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_SEND_BUFFER_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_SEND_BUFFER_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h"
+
+namespace quic {
+
+namespace test {
+
+class QuicStreamSendBufferPeer {
+ public:
+ static void SetStreamOffset(QuicStreamSendBuffer* send_buffer,
+ QuicStreamOffset stream_offset);
+
+ static const BufferedSlice* CurrentWriteSlice(
+ QuicStreamSendBuffer* send_buffer);
+
+ static QuicByteCount TotalLength(QuicStreamSendBuffer* send_buffer);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_SEND_BUFFER_PEER_H_
diff --git a/quic/test_tools/quic_stream_sequencer_buffer_peer.cc b/quic/test_tools/quic_stream_sequencer_buffer_peer.cc
new file mode 100644
index 0000000..9c96c82
--- /dev/null
+++ b/quic/test_tools/quic_stream_sequencer_buffer_peer.cc
@@ -0,0 +1,155 @@
+// Copyright 2016 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 "net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_buffer_peer.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+typedef quic::QuicStreamSequencerBuffer::BufferBlock BufferBlock;
+
+static const size_t kBlockSizeBytes =
+ quic::QuicStreamSequencerBuffer::kBlockSizeBytes;
+
+namespace quic {
+namespace test {
+
+QuicStreamSequencerBufferPeer::QuicStreamSequencerBufferPeer(
+ QuicStreamSequencerBuffer* buffer)
+ : buffer_(buffer) {}
+
+// Read from this buffer_ into the given destination buffer_ up to the
+// size of the destination. Returns the number of bytes read. Reading from
+// an empty buffer_->returns 0.
+size_t QuicStreamSequencerBufferPeer::Read(char* dest_buffer, size_t size) {
+ iovec dest;
+ dest.iov_base = dest_buffer, dest.iov_len = size;
+ size_t bytes_read;
+ QuicString error_details;
+ EXPECT_EQ(QUIC_NO_ERROR,
+ buffer_->Readv(&dest, 1, &bytes_read, &error_details));
+ return bytes_read;
+}
+
+// If buffer is empty, the blocks_ array must be empty, which means all
+// blocks are deallocated.
+bool QuicStreamSequencerBufferPeer::CheckEmptyInvariants() {
+ return !buffer_->Empty() || IsBlockArrayEmpty();
+}
+
+bool QuicStreamSequencerBufferPeer::IsBlockArrayEmpty() {
+ if (buffer_->blocks_ == nullptr) {
+ return true;
+ }
+
+ size_t count = buffer_->blocks_count_;
+ for (size_t i = 0; i < count; i++) {
+ if (buffer_->blocks_[i] != nullptr) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool QuicStreamSequencerBufferPeer::CheckInitialState() {
+ EXPECT_TRUE(buffer_->Empty() && buffer_->total_bytes_read_ == 0 &&
+ buffer_->num_bytes_buffered_ == 0);
+ return CheckBufferInvariants();
+}
+
+bool QuicStreamSequencerBufferPeer::CheckBufferInvariants() {
+ QuicStreamOffset data_span =
+ buffer_->NextExpectedByte() - buffer_->total_bytes_read_;
+ bool capacity_sane = data_span <= buffer_->max_buffer_capacity_bytes_ &&
+ data_span >= buffer_->num_bytes_buffered_;
+ if (!capacity_sane) {
+ QUIC_LOG(ERROR) << "data span is larger than capacity.";
+ QUIC_LOG(ERROR) << "total read: " << buffer_->total_bytes_read_
+ << " last byte: " << buffer_->NextExpectedByte();
+ }
+ bool total_read_sane =
+ buffer_->FirstMissingByte() >= buffer_->total_bytes_read_;
+ if (!total_read_sane) {
+ QUIC_LOG(ERROR) << "read across 1st gap.";
+ }
+ bool read_offset_sane = buffer_->ReadOffset() < kBlockSizeBytes;
+ if (!capacity_sane) {
+ QUIC_LOG(ERROR) << "read offset go beyond 1st block";
+ }
+ bool block_match_capacity = (buffer_->max_buffer_capacity_bytes_ <=
+ buffer_->blocks_count_ * kBlockSizeBytes) &&
+ (buffer_->max_buffer_capacity_bytes_ >
+ (buffer_->blocks_count_ - 1) * kBlockSizeBytes);
+ if (!capacity_sane) {
+ QUIC_LOG(ERROR) << "block number not match capcaity.";
+ }
+ bool block_retired_when_empty = CheckEmptyInvariants();
+ if (!block_retired_when_empty) {
+ QUIC_LOG(ERROR) << "block is not retired after use.";
+ }
+ return capacity_sane && total_read_sane && read_offset_sane &&
+ block_match_capacity && block_retired_when_empty;
+}
+
+size_t QuicStreamSequencerBufferPeer::GetInBlockOffset(
+ QuicStreamOffset offset) {
+ return buffer_->GetInBlockOffset(offset);
+}
+
+BufferBlock* QuicStreamSequencerBufferPeer::GetBlock(size_t index) {
+ return buffer_->blocks_[index];
+}
+
+int QuicStreamSequencerBufferPeer::IntervalSize() {
+ if (buffer_->bytes_received_.Empty()) {
+ return 1;
+ }
+ int gap_size = buffer_->bytes_received_.Size() + 1;
+ if (buffer_->bytes_received_.Empty()) {
+ return gap_size;
+ }
+ if (buffer_->bytes_received_.begin()->min() == 0) {
+ --gap_size;
+ }
+ if (buffer_->bytes_received_.rbegin()->max() ==
+ std::numeric_limits<uint64_t>::max()) {
+ --gap_size;
+ }
+ return gap_size;
+}
+
+size_t QuicStreamSequencerBufferPeer::max_buffer_capacity() {
+ return buffer_->max_buffer_capacity_bytes_;
+}
+
+size_t QuicStreamSequencerBufferPeer::ReadableBytes() {
+ return buffer_->ReadableBytes();
+}
+
+void QuicStreamSequencerBufferPeer::set_total_bytes_read(
+ QuicStreamOffset total_bytes_read) {
+ buffer_->total_bytes_read_ = total_bytes_read;
+}
+
+void QuicStreamSequencerBufferPeer::AddBytesReceived(QuicStreamOffset offset,
+ QuicByteCount length) {
+ buffer_->bytes_received_.Add(offset, offset + length);
+}
+
+bool QuicStreamSequencerBufferPeer::IsBufferAllocated() {
+ return buffer_->blocks_ != nullptr;
+}
+
+size_t QuicStreamSequencerBufferPeer::block_count() {
+ return buffer_->blocks_count_;
+}
+
+const QuicIntervalSet<QuicStreamOffset>&
+QuicStreamSequencerBufferPeer::bytes_received() {
+ return buffer_->bytes_received_;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_stream_sequencer_buffer_peer.h b/quic/test_tools/quic_stream_sequencer_buffer_peer.h
new file mode 100644
index 0000000..da2d054
--- /dev/null
+++ b/quic/test_tools/quic_stream_sequencer_buffer_peer.h
@@ -0,0 +1,63 @@
+// Copyright 2016 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_BUFFER_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_BUFFER_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h"
+
+namespace quic {
+
+namespace test {
+
+class QuicStreamSequencerBufferPeer {
+ public:
+ explicit QuicStreamSequencerBufferPeer(QuicStreamSequencerBuffer* buffer);
+ QuicStreamSequencerBufferPeer(const QuicStreamSequencerBufferPeer&) = delete;
+ QuicStreamSequencerBufferPeer& operator=(
+ const QuicStreamSequencerBufferPeer&) = delete;
+
+ // Read from this buffer_ into the given destination buffer_ up to the
+ // size of the destination. Returns the number of bytes read. Reading from
+ // an empty buffer_->returns 0.
+ size_t Read(char* dest_buffer, size_t size);
+
+ // If buffer is empty, the blocks_ array must be empty, which means all
+ // blocks are deallocated.
+ bool CheckEmptyInvariants();
+
+ bool IsBlockArrayEmpty();
+
+ bool CheckInitialState();
+
+ bool CheckBufferInvariants();
+
+ size_t GetInBlockOffset(QuicStreamOffset offset);
+
+ QuicStreamSequencerBuffer::BufferBlock* GetBlock(size_t index);
+
+ int IntervalSize();
+
+ size_t max_buffer_capacity();
+
+ size_t ReadableBytes();
+
+ void set_total_bytes_read(QuicStreamOffset total_bytes_read);
+
+ void AddBytesReceived(QuicStreamOffset offset, QuicByteCount length);
+
+ bool IsBufferAllocated();
+
+ size_t block_count();
+
+ const QuicIntervalSet<QuicStreamOffset>& bytes_received();
+
+ private:
+ QuicStreamSequencerBuffer* buffer_;
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_BUFFER_PEER_H_
diff --git a/quic/test_tools/quic_stream_sequencer_peer.cc b/quic/test_tools/quic_stream_sequencer_peer.cc
new file mode 100644
index 0000000..05cf35c
--- /dev/null
+++ b/quic/test_tools/quic_stream_sequencer_peer.cc
@@ -0,0 +1,40 @@
+// Copyright 2014 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 "net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_buffer_peer.h"
+
+namespace quic {
+namespace test {
+
+// static
+size_t QuicStreamSequencerPeer::GetNumBufferedBytes(
+ QuicStreamSequencer* sequencer) {
+ return sequencer->buffered_frames_.BytesBuffered();
+}
+
+// static
+QuicStreamOffset QuicStreamSequencerPeer::GetCloseOffset(
+ QuicStreamSequencer* sequencer) {
+ return sequencer->close_offset_;
+}
+
+// static
+bool QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(
+ QuicStreamSequencer* sequencer) {
+ QuicStreamSequencerBufferPeer buffer_peer(&(sequencer->buffered_frames_));
+ return buffer_peer.IsBufferAllocated();
+}
+
+// static
+void QuicStreamSequencerPeer::SetFrameBufferTotalBytesRead(
+ QuicStreamSequencer* sequencer,
+ QuicStreamOffset total_bytes_read) {
+ QuicStreamSequencerBufferPeer buffer_peer(&(sequencer->buffered_frames_));
+ buffer_peer.set_total_bytes_read(total_bytes_read);
+}
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_stream_sequencer_peer.h b/quic/test_tools/quic_stream_sequencer_peer.h
new file mode 100644
index 0000000..c317209
--- /dev/null
+++ b/quic/test_tools/quic_stream_sequencer_peer.h
@@ -0,0 +1,34 @@
+// Copyright 2014 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_PEER_H_
+
+#include "base/macros.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+
+class QuicStreamSequencer;
+
+namespace test {
+
+class QuicStreamSequencerPeer {
+ public:
+ QuicStreamSequencerPeer() = delete;
+
+ static size_t GetNumBufferedBytes(QuicStreamSequencer* sequencer);
+
+ static QuicStreamOffset GetCloseOffset(QuicStreamSequencer* sequencer);
+
+ static bool IsUnderlyingBufferAllocated(QuicStreamSequencer* sequencer);
+
+ static void SetFrameBufferTotalBytesRead(QuicStreamSequencer* sequencer,
+ QuicStreamOffset total_bytes_read);
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_PEER_H_
diff --git a/quic/test_tools/quic_sustained_bandwidth_recorder_peer.cc b/quic/test_tools/quic_sustained_bandwidth_recorder_peer.cc
new file mode 100644
index 0000000..9f08faf
--- /dev/null
+++ b/quic/test_tools/quic_sustained_bandwidth_recorder_peer.cc
@@ -0,0 +1,34 @@
+// Copyright 2014 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 "net/third_party/quiche/src/quic/test_tools/quic_sustained_bandwidth_recorder_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_sustained_bandwidth_recorder.h"
+
+namespace quic {
+namespace test {
+
+// static
+void QuicSustainedBandwidthRecorderPeer::SetBandwidthEstimate(
+ QuicSustainedBandwidthRecorder* bandwidth_recorder,
+ int32_t bandwidth_estimate_kbytes_per_second) {
+ bandwidth_recorder->has_estimate_ = true;
+ bandwidth_recorder->bandwidth_estimate_ =
+ QuicBandwidth::FromKBytesPerSecond(bandwidth_estimate_kbytes_per_second);
+}
+
+// static
+void QuicSustainedBandwidthRecorderPeer::SetMaxBandwidthEstimate(
+ QuicSustainedBandwidthRecorder* bandwidth_recorder,
+ int32_t max_bandwidth_estimate_kbytes_per_second,
+ int32_t max_bandwidth_timestamp) {
+ bandwidth_recorder->max_bandwidth_estimate_ =
+ QuicBandwidth::FromKBytesPerSecond(
+ max_bandwidth_estimate_kbytes_per_second);
+ bandwidth_recorder->max_bandwidth_timestamp_ = max_bandwidth_timestamp;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_sustained_bandwidth_recorder_peer.h b/quic/test_tools/quic_sustained_bandwidth_recorder_peer.h
new file mode 100644
index 0000000..f73e00c
--- /dev/null
+++ b/quic/test_tools/quic_sustained_bandwidth_recorder_peer.h
@@ -0,0 +1,36 @@
+// Copyright 2014 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_SUSTAINED_BANDWIDTH_RECORDER_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_SUSTAINED_BANDWIDTH_RECORDER_PEER_H_
+
+#include <cstdint>
+
+#include "base/macros.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+
+class QuicSustainedBandwidthRecorder;
+
+namespace test {
+
+class QuicSustainedBandwidthRecorderPeer {
+ public:
+ QuicSustainedBandwidthRecorderPeer() = delete;
+
+ static void SetBandwidthEstimate(
+ QuicSustainedBandwidthRecorder* bandwidth_recorder,
+ int32_t bandwidth_estimate_kbytes_per_second);
+
+ static void SetMaxBandwidthEstimate(
+ QuicSustainedBandwidthRecorder* bandwidth_recorder,
+ int32_t max_bandwidth_estimate_kbytes_per_second,
+ int32_t max_bandwidth_timestamp);
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_SUSTAINED_BANDWIDTH_RECORDER_PEER_H_
diff --git a/quic/test_tools/quic_test_client.cc b/quic/test_tools/quic_test_client.cc
new file mode 100644
index 0000000..a0068d5
--- /dev/null
+++ b/quic/test_tools/quic_test_client.cc
@@ -0,0 +1,909 @@
+// Copyright (c) 2012 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 "net/third_party/quiche/src/quic/test_tools/quic_test_client.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "third_party/boringssl/src/include/openssl/x509.h"
+#include "net/third_party/quiche/src/quic/core/crypto/proof_verifier.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_stack_trace.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_client_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_stream_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/tools/quic_url.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+// RecordingProofVerifier accepts any certificate chain and records the common
+// name of the leaf and then delegates the actual verification to an actual
+// verifier. If no optional verifier is provided, then VerifyProof will return
+// success.
+class RecordingProofVerifier : public ProofVerifier {
+ public:
+ explicit RecordingProofVerifier(std::unique_ptr<ProofVerifier> verifier)
+ : verifier_(std::move(verifier)) {}
+
+ // ProofVerifier interface.
+ QuicAsyncStatus VerifyProof(
+ const QuicString& hostname,
+ const uint16_t port,
+ const QuicString& server_config,
+ QuicTransportVersion transport_version,
+ QuicStringPiece chlo_hash,
+ const std::vector<QuicString>& certs,
+ const QuicString& cert_sct,
+ const QuicString& signature,
+ const ProofVerifyContext* context,
+ QuicString* error_details,
+ std::unique_ptr<ProofVerifyDetails>* details,
+ std::unique_ptr<ProofVerifierCallback> callback) override {
+ common_name_.clear();
+ if (certs.empty()) {
+ return QUIC_FAILURE;
+ }
+
+ const uint8_t* data;
+ data = reinterpret_cast<const uint8_t*>(certs[0].data());
+ bssl::UniquePtr<X509> cert(d2i_X509(nullptr, &data, certs[0].size()));
+ if (!cert.get()) {
+ return QUIC_FAILURE;
+ }
+
+ static const unsigned kMaxCommonNameLength = 256;
+ char buf[kMaxCommonNameLength];
+ X509_NAME* subject_name = X509_get_subject_name(cert.get());
+ if (X509_NAME_get_text_by_NID(subject_name, NID_commonName, buf,
+ sizeof(buf)) <= 0) {
+ return QUIC_FAILURE;
+ }
+
+ common_name_ = buf;
+ cert_sct_ = cert_sct;
+
+ if (!verifier_) {
+ return QUIC_SUCCESS;
+ }
+
+ return verifier_->VerifyProof(hostname, port, server_config,
+ transport_version, chlo_hash, certs, cert_sct,
+ signature, context, error_details, details,
+ std::move(callback));
+ }
+
+ QuicAsyncStatus VerifyCertChain(
+ const QuicString& hostname,
+ const std::vector<QuicString>& certs,
+ const ProofVerifyContext* context,
+ QuicString* error_details,
+ std::unique_ptr<ProofVerifyDetails>* details,
+ std::unique_ptr<ProofVerifierCallback> callback) override {
+ return QUIC_SUCCESS;
+ }
+
+ std::unique_ptr<ProofVerifyContext> CreateDefaultContext() override {
+ return verifier_ != nullptr ? verifier_->CreateDefaultContext() : nullptr;
+ }
+
+ const QuicString& common_name() const { return common_name_; }
+
+ const QuicString& cert_sct() const { return cert_sct_; }
+
+ private:
+ std::unique_ptr<ProofVerifier> verifier_;
+ QuicString common_name_;
+ QuicString cert_sct_;
+};
+} // namespace
+
+class MockableQuicClientEpollNetworkHelper
+ : public QuicClientEpollNetworkHelper {
+ public:
+ using QuicClientEpollNetworkHelper::QuicClientEpollNetworkHelper;
+ ~MockableQuicClientEpollNetworkHelper() override = default;
+
+ void ProcessPacket(const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ const QuicReceivedPacket& packet) override {
+ QuicClientEpollNetworkHelper::ProcessPacket(self_address, peer_address,
+ packet);
+ if (track_last_incoming_packet_) {
+ last_incoming_packet_ = packet.Clone();
+ }
+ }
+
+ QuicPacketWriter* CreateQuicPacketWriter() override {
+ QuicPacketWriter* writer =
+ QuicClientEpollNetworkHelper::CreateQuicPacketWriter();
+ if (!test_writer_) {
+ return writer;
+ }
+ test_writer_->set_writer(writer);
+ return test_writer_;
+ }
+
+ const QuicReceivedPacket* last_incoming_packet() {
+ return last_incoming_packet_.get();
+ }
+
+ void set_track_last_incoming_packet(bool track) {
+ track_last_incoming_packet_ = track;
+ }
+
+ void UseWriter(QuicPacketWriterWrapper* writer) {
+ CHECK(test_writer_ == nullptr);
+ test_writer_ = writer;
+ }
+
+ void set_peer_address(const QuicSocketAddress& address) {
+ CHECK(test_writer_ != nullptr);
+ test_writer_->set_peer_address(address);
+ }
+
+ private:
+ QuicPacketWriterWrapper* test_writer_ = nullptr;
+ // The last incoming packet, iff |track_last_incoming_packet_| is true.
+ std::unique_ptr<QuicReceivedPacket> last_incoming_packet_;
+ // If true, copy each packet from ProcessPacket into |last_incoming_packet_|
+ bool track_last_incoming_packet_ = false;
+};
+
+MockableQuicClient::MockableQuicClient(
+ QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicEpollServer* epoll_server)
+ : MockableQuicClient(server_address,
+ server_id,
+ QuicConfig(),
+ supported_versions,
+ epoll_server) {}
+
+MockableQuicClient::MockableQuicClient(
+ QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicEpollServer* epoll_server)
+ : MockableQuicClient(server_address,
+ server_id,
+ config,
+ supported_versions,
+ epoll_server,
+ nullptr) {}
+
+MockableQuicClient::MockableQuicClient(
+ QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicEpollServer* epoll_server,
+ std::unique_ptr<ProofVerifier> proof_verifier)
+ : QuicClient(
+ server_address,
+ server_id,
+ supported_versions,
+ config,
+ epoll_server,
+ QuicMakeUnique<MockableQuicClientEpollNetworkHelper>(epoll_server,
+ this),
+ QuicWrapUnique(
+ new RecordingProofVerifier(std::move(proof_verifier)))),
+ override_connection_id_(EmptyQuicConnectionId()),
+ connection_id_overridden_(false) {}
+
+MockableQuicClient::~MockableQuicClient() {
+ if (connected()) {
+ Disconnect();
+ }
+}
+
+MockableQuicClientEpollNetworkHelper*
+MockableQuicClient::mockable_network_helper() {
+ return static_cast<MockableQuicClientEpollNetworkHelper*>(
+ epoll_network_helper());
+}
+
+const MockableQuicClientEpollNetworkHelper*
+MockableQuicClient::mockable_network_helper() const {
+ return static_cast<const MockableQuicClientEpollNetworkHelper*>(
+ epoll_network_helper());
+}
+
+QuicConnectionId MockableQuicClient::GenerateNewConnectionId() {
+ return connection_id_overridden_ ? override_connection_id_
+ : QuicClient::GenerateNewConnectionId();
+}
+
+void MockableQuicClient::UseConnectionId(QuicConnectionId connection_id) {
+ connection_id_overridden_ = true;
+ override_connection_id_ = connection_id;
+}
+
+void MockableQuicClient::UseWriter(QuicPacketWriterWrapper* writer) {
+ mockable_network_helper()->UseWriter(writer);
+}
+
+void MockableQuicClient::set_peer_address(const QuicSocketAddress& address) {
+ mockable_network_helper()->set_peer_address(address);
+}
+
+const QuicReceivedPacket* MockableQuicClient::last_incoming_packet() {
+ return mockable_network_helper()->last_incoming_packet();
+}
+
+void MockableQuicClient::set_track_last_incoming_packet(bool track) {
+ mockable_network_helper()->set_track_last_incoming_packet(track);
+}
+
+QuicTestClient::QuicTestClient(
+ QuicSocketAddress server_address,
+ const QuicString& server_hostname,
+ const ParsedQuicVersionVector& supported_versions)
+ : QuicTestClient(server_address,
+ server_hostname,
+ QuicConfig(),
+ supported_versions) {}
+
+QuicTestClient::QuicTestClient(
+ QuicSocketAddress server_address,
+ const QuicString& server_hostname,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions)
+ : client_(new MockableQuicClient(
+ server_address,
+ QuicServerId(server_hostname, server_address.port(), false),
+ config,
+ supported_versions,
+ &epoll_server_)) {
+ Initialize();
+}
+
+QuicTestClient::QuicTestClient(
+ QuicSocketAddress server_address,
+ const QuicString& server_hostname,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ std::unique_ptr<ProofVerifier> proof_verifier)
+ : client_(new MockableQuicClient(
+ server_address,
+ QuicServerId(server_hostname, server_address.port(), false),
+ config,
+ supported_versions,
+ &epoll_server_,
+ std::move(proof_verifier))) {
+ Initialize();
+}
+
+QuicTestClient::QuicTestClient() = default;
+
+QuicTestClient::~QuicTestClient() {
+ for (std::pair<QuicStreamId, QuicSpdyClientStream*> stream : open_streams_) {
+ stream.second->set_visitor(nullptr);
+ }
+}
+
+void QuicTestClient::Initialize() {
+ priority_ = 3;
+ connect_attempted_ = false;
+ auto_reconnect_ = false;
+ buffer_body_ = true;
+ num_requests_ = 0;
+ num_responses_ = 0;
+ ClearPerConnectionState();
+ // As chrome will generally do this, we want it to be the default when it's
+ // not overridden.
+ if (!client_->config()->HasSetBytesForConnectionIdToSend()) {
+ client_->config()->SetBytesForConnectionIdToSend(0);
+ }
+}
+
+void QuicTestClient::SetUserAgentID(const QuicString& user_agent_id) {
+ client_->SetUserAgentID(user_agent_id);
+}
+
+ssize_t QuicTestClient::SendRequest(const QuicString& uri) {
+ spdy::SpdyHeaderBlock headers;
+ if (!PopulateHeaderBlockFromUrl(uri, &headers)) {
+ return 0;
+ }
+ return SendMessage(headers, "");
+}
+
+ssize_t QuicTestClient::SendRequestAndRstTogether(const QuicString& uri) {
+ spdy::SpdyHeaderBlock headers;
+ if (!PopulateHeaderBlockFromUrl(uri, &headers)) {
+ return 0;
+ }
+
+ QuicSpdyClientSession* session = client()->client_session();
+ QuicConnection::ScopedPacketFlusher flusher(
+ session->connection(), QuicConnection::SEND_ACK_IF_PENDING);
+ ssize_t ret = SendMessage(headers, "", /*fin=*/true, /*flush=*/false);
+
+ QuicStreamId stream_id = GetNthClientInitiatedBidirectionalStreamId(
+ session->connection()->transport_version(), 0);
+ session->SendRstStream(stream_id, QUIC_STREAM_CANCELLED, 0);
+ return ret;
+}
+
+void QuicTestClient::SendRequestsAndWaitForResponses(
+ const std::vector<QuicString>& url_list) {
+ for (const QuicString& url : url_list) {
+ SendRequest(url);
+ }
+ while (client()->WaitForEvents()) {
+ }
+}
+
+ssize_t QuicTestClient::GetOrCreateStreamAndSendRequest(
+ const spdy::SpdyHeaderBlock* headers,
+ QuicStringPiece body,
+ bool fin,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+ if (headers) {
+ QuicClientPushPromiseIndex::TryHandle* handle;
+ QuicAsyncStatus rv =
+ client()->push_promise_index()->Try(*headers, this, &handle);
+ if (rv == QUIC_SUCCESS)
+ return 1;
+ if (rv == QUIC_PENDING) {
+ // May need to retry request if asynchronous rendezvous fails.
+ std::unique_ptr<spdy::SpdyHeaderBlock> new_headers(
+ new spdy::SpdyHeaderBlock(headers->Clone()));
+ push_promise_data_to_resend_ = QuicMakeUnique<TestClientDataToResend>(
+ std::move(new_headers), body, fin, this, std::move(ack_listener));
+ return 1;
+ }
+ }
+
+ // Maybe it's better just to overload this. it's just that we need
+ // for the GetOrCreateStream function to call something else...which
+ // is icky and complicated, but maybe not worse than this.
+ QuicSpdyClientStream* stream = GetOrCreateStream();
+ if (stream == nullptr) {
+ return 0;
+ }
+ QuicSpdyStreamPeer::set_ack_listener(stream, ack_listener);
+
+ ssize_t ret = 0;
+ if (headers != nullptr) {
+ spdy::SpdyHeaderBlock spdy_headers(headers->Clone());
+ if (spdy_headers[":authority"].as_string().empty()) {
+ spdy_headers[":authority"] = client_->server_id().host();
+ }
+ ret = stream->SendRequest(std::move(spdy_headers), body, fin);
+ ++num_requests_;
+ } else {
+ stream->WriteOrBufferBody(QuicString(body), fin);
+ ret = body.length();
+ }
+ if (GetQuicReloadableFlag(enable_quic_stateless_reject_support)) {
+ std::unique_ptr<spdy::SpdyHeaderBlock> new_headers;
+ if (headers) {
+ new_headers = QuicMakeUnique<spdy::SpdyHeaderBlock>(headers->Clone());
+ }
+ std::unique_ptr<QuicSpdyClientBase::QuicDataToResend> data_to_resend(
+ new TestClientDataToResend(std::move(new_headers), body, fin, this,
+ ack_listener));
+ client()->MaybeAddQuicDataToResend(std::move(data_to_resend));
+ }
+ return ret;
+}
+
+ssize_t QuicTestClient::SendMessage(const spdy::SpdyHeaderBlock& headers,
+ QuicStringPiece body) {
+ return SendMessage(headers, body, /*fin=*/true);
+}
+
+ssize_t QuicTestClient::SendMessage(const spdy::SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin) {
+ return SendMessage(headers, body, fin, /*flush=*/true);
+}
+
+ssize_t QuicTestClient::SendMessage(const spdy::SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin,
+ bool flush) {
+ // Always force creation of a stream for SendMessage.
+ latest_created_stream_ = nullptr;
+
+ ssize_t ret = GetOrCreateStreamAndSendRequest(&headers, body, fin, nullptr);
+
+ if (flush) {
+ WaitForWriteToFlush();
+ }
+ return ret;
+}
+
+ssize_t QuicTestClient::SendData(const QuicString& data, bool last_data) {
+ return SendData(data, last_data, nullptr);
+}
+
+ssize_t QuicTestClient::SendData(
+ const QuicString& data,
+ bool last_data,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+ return GetOrCreateStreamAndSendRequest(nullptr, QuicStringPiece(data),
+ last_data, std::move(ack_listener));
+}
+
+bool QuicTestClient::response_complete() const {
+ return response_complete_;
+}
+
+int64_t QuicTestClient::response_body_size() const {
+ return response_body_size_;
+}
+
+bool QuicTestClient::buffer_body() const {
+ return buffer_body_;
+}
+
+void QuicTestClient::set_buffer_body(bool buffer_body) {
+ buffer_body_ = buffer_body;
+}
+
+const QuicString& QuicTestClient::response_body() const {
+ return response_;
+}
+
+QuicString QuicTestClient::SendCustomSynchronousRequest(
+ const spdy::SpdyHeaderBlock& headers,
+ const QuicString& body) {
+ // Clear connection state here and only track this synchronous request.
+ ClearPerConnectionState();
+ if (SendMessage(headers, body) == 0) {
+ QUIC_DLOG(ERROR) << "Failed the request for: " << headers.DebugString();
+ // Set the response_ explicitly. Otherwise response_ will contain the
+ // response from the previously successful request.
+ response_ = "";
+ } else {
+ WaitForResponse();
+ }
+ return response_;
+}
+
+QuicString QuicTestClient::SendSynchronousRequest(const QuicString& uri) {
+ spdy::SpdyHeaderBlock headers;
+ if (!PopulateHeaderBlockFromUrl(uri, &headers)) {
+ return "";
+ }
+ return SendCustomSynchronousRequest(headers, "");
+}
+
+void QuicTestClient::SendConnectivityProbing() {
+ QuicConnection* connection = client()->client_session()->connection();
+ connection->SendConnectivityProbingPacket(connection->writer(),
+ connection->peer_address());
+}
+
+void QuicTestClient::SetLatestCreatedStream(QuicSpdyClientStream* stream) {
+ latest_created_stream_ = stream;
+ if (latest_created_stream_ != nullptr) {
+ open_streams_[stream->id()] = stream;
+ stream->set_visitor(this);
+ }
+}
+
+QuicSpdyClientStream* QuicTestClient::GetOrCreateStream() {
+ if (!connect_attempted_ || auto_reconnect_) {
+ if (!connected()) {
+ Connect();
+ }
+ if (!connected()) {
+ return nullptr;
+ }
+ }
+ if (open_streams_.empty()) {
+ ClearPerConnectionState();
+ }
+ if (!latest_created_stream_) {
+ SetLatestCreatedStream(client_->CreateClientStream());
+ if (latest_created_stream_) {
+ latest_created_stream_->SetPriority(priority_);
+ }
+ }
+
+ return latest_created_stream_;
+}
+
+QuicErrorCode QuicTestClient::connection_error() {
+ return client()->connection_error();
+}
+
+MockableQuicClient* QuicTestClient::client() {
+ return client_.get();
+}
+
+const QuicString& QuicTestClient::cert_common_name() const {
+ return reinterpret_cast<RecordingProofVerifier*>(client_->proof_verifier())
+ ->common_name();
+}
+
+const QuicString& QuicTestClient::cert_sct() const {
+ return reinterpret_cast<RecordingProofVerifier*>(client_->proof_verifier())
+ ->cert_sct();
+}
+
+QuicTagValueMap QuicTestClient::GetServerConfig() const {
+ QuicCryptoClientConfig* config = client_->crypto_config();
+ QuicCryptoClientConfig::CachedState* state =
+ config->LookupOrCreate(client_->server_id());
+ const CryptoHandshakeMessage* handshake_msg = state->GetServerConfig();
+ if (handshake_msg != nullptr) {
+ return handshake_msg->tag_value_map();
+ } else {
+ return QuicTagValueMap();
+ }
+}
+
+bool QuicTestClient::connected() const {
+ return client_->connected();
+}
+
+void QuicTestClient::Connect() {
+ DCHECK(!connected());
+ if (!connect_attempted_) {
+ client_->Initialize();
+ }
+
+ // If we've been asked to override SNI, set it now
+ if (override_sni_set_) {
+ client_->set_server_id(
+ QuicServerId(override_sni_, address().port(), false));
+ }
+
+ client_->Connect();
+ connect_attempted_ = true;
+}
+
+void QuicTestClient::ResetConnection() {
+ Disconnect();
+ Connect();
+}
+
+void QuicTestClient::Disconnect() {
+ ClearPerConnectionState();
+ client_->Disconnect();
+ connect_attempted_ = false;
+}
+
+QuicSocketAddress QuicTestClient::local_address() const {
+ return client_->network_helper()->GetLatestClientAddress();
+}
+
+void QuicTestClient::ClearPerRequestState() {
+ stream_error_ = QUIC_STREAM_NO_ERROR;
+ response_ = "";
+ response_complete_ = false;
+ response_headers_complete_ = false;
+ preliminary_headers_.clear();
+ response_headers_.clear();
+ response_trailers_.clear();
+ bytes_read_ = 0;
+ bytes_written_ = 0;
+ response_body_size_ = 0;
+}
+
+bool QuicTestClient::HaveActiveStream() {
+ return push_promise_data_to_resend_.get() || !open_streams_.empty();
+}
+
+bool QuicTestClient::WaitUntil(int timeout_ms, std::function<bool()> trigger) {
+ int64_t timeout_us = timeout_ms * kNumMicrosPerMilli;
+ int64_t old_timeout_us = epoll_server()->timeout_in_us_for_test();
+ if (timeout_us > 0) {
+ epoll_server()->set_timeout_in_us(timeout_us);
+ }
+ const QuicClock* clock =
+ QuicConnectionPeer::GetHelper(client()->session()->connection())
+ ->GetClock();
+ QuicTime end_waiting_time =
+ clock->Now() + QuicTime::Delta::FromMicroseconds(timeout_us);
+ while (HaveActiveStream() && !(trigger && trigger()) &&
+ (timeout_us < 0 || clock->Now() < end_waiting_time)) {
+ client_->WaitForEvents();
+ }
+ ReadNextResponse();
+ if (timeout_us > 0) {
+ epoll_server()->set_timeout_in_us(old_timeout_us);
+ }
+ if (trigger && !trigger()) {
+ VLOG(1) << "Client WaitUntil returning with trigger returning false."
+ << QuicStackTrace();
+ return false;
+ }
+ return true;
+}
+
+ssize_t QuicTestClient::Send(const void* buffer, size_t size) {
+ return SendData(QuicString(static_cast<const char*>(buffer), size), false);
+}
+
+bool QuicTestClient::response_headers_complete() const {
+ for (std::pair<QuicStreamId, QuicSpdyClientStream*> stream : open_streams_) {
+ if (stream.second->headers_decompressed()) {
+ return true;
+ }
+ }
+ return response_headers_complete_;
+}
+
+const spdy::SpdyHeaderBlock* QuicTestClient::response_headers() const {
+ for (std::pair<QuicStreamId, QuicSpdyClientStream*> stream : open_streams_) {
+ size_t bytes_read =
+ stream.second->stream_bytes_read() + stream.second->header_bytes_read();
+ if (bytes_read > 0) {
+ response_headers_ = stream.second->response_headers().Clone();
+ break;
+ }
+ }
+ return &response_headers_;
+}
+
+const spdy::SpdyHeaderBlock* QuicTestClient::preliminary_headers() const {
+ for (std::pair<QuicStreamId, QuicSpdyClientStream*> stream : open_streams_) {
+ size_t bytes_read =
+ stream.second->stream_bytes_read() + stream.second->header_bytes_read();
+ if (bytes_read > 0) {
+ preliminary_headers_ = stream.second->preliminary_headers().Clone();
+ break;
+ }
+ }
+ return &preliminary_headers_;
+}
+
+const spdy::SpdyHeaderBlock& QuicTestClient::response_trailers() const {
+ return response_trailers_;
+}
+
+int64_t QuicTestClient::response_size() const {
+ return bytes_read();
+}
+
+size_t QuicTestClient::bytes_read() const {
+ for (std::pair<QuicStreamId, QuicSpdyClientStream*> stream : open_streams_) {
+ size_t bytes_read = stream.second->total_body_bytes_read() +
+ stream.second->header_bytes_read();
+ if (bytes_read > 0) {
+ return bytes_read;
+ }
+ }
+ return bytes_read_;
+}
+
+size_t QuicTestClient::bytes_written() const {
+ for (std::pair<QuicStreamId, QuicSpdyClientStream*> stream : open_streams_) {
+ size_t bytes_written = stream.second->stream_bytes_written() +
+ stream.second->header_bytes_written();
+ if (bytes_written > 0) {
+ return bytes_written;
+ }
+ }
+ return bytes_written_;
+}
+
+void QuicTestClient::OnClose(QuicSpdyStream* stream) {
+ if (stream == nullptr) {
+ return;
+ }
+ // Always close the stream, regardless of whether it was the last stream
+ // written.
+ client()->OnClose(stream);
+ ++num_responses_;
+ if (!QuicContainsKey(open_streams_, stream->id())) {
+ return;
+ }
+ if (latest_created_stream_ == stream) {
+ latest_created_stream_ = nullptr;
+ }
+ QuicSpdyClientStream* client_stream =
+ static_cast<QuicSpdyClientStream*>(stream);
+ QuicStreamId id = client_stream->id();
+ closed_stream_states_.insert(std::make_pair(
+ id,
+ PerStreamState(
+ client_stream->stream_error(), true,
+ client_stream->headers_decompressed(),
+ client_stream->response_headers(),
+ client_stream->preliminary_headers(),
+ (buffer_body() ? client_stream->data() : ""),
+ client_stream->received_trailers(),
+ // Use NumBytesConsumed to avoid counting retransmitted stream frames.
+ client_stream->total_body_bytes_read() +
+ client_stream->header_bytes_read(),
+ client_stream->stream_bytes_written() +
+ client_stream->header_bytes_written(),
+ client_stream->data().size())));
+ open_streams_.erase(id);
+}
+
+bool QuicTestClient::CheckVary(const spdy::SpdyHeaderBlock& client_request,
+ const spdy::SpdyHeaderBlock& promise_request,
+ const spdy::SpdyHeaderBlock& promise_response) {
+ return true;
+}
+
+void QuicTestClient::OnRendezvousResult(QuicSpdyStream* stream) {
+ std::unique_ptr<TestClientDataToResend> data_to_resend =
+ std::move(push_promise_data_to_resend_);
+ SetLatestCreatedStream(static_cast<QuicSpdyClientStream*>(stream));
+ if (stream) {
+ stream->OnBodyAvailable();
+ } else if (data_to_resend) {
+ data_to_resend->Resend();
+ }
+}
+
+void QuicTestClient::UseWriter(QuicPacketWriterWrapper* writer) {
+ client_->UseWriter(writer);
+}
+
+void QuicTestClient::UseConnectionId(QuicConnectionId connection_id) {
+ DCHECK(!connected());
+ client_->UseConnectionId(connection_id);
+}
+
+bool QuicTestClient::MigrateSocket(const QuicIpAddress& new_host) {
+ return client_->MigrateSocket(new_host);
+}
+
+bool QuicTestClient::MigrateSocketWithSpecifiedPort(
+ const QuicIpAddress& new_host,
+ int port) {
+ client_->set_local_port(port);
+ return client_->MigrateSocket(new_host);
+}
+
+QuicIpAddress QuicTestClient::bind_to_address() const {
+ return client_->bind_to_address();
+}
+
+void QuicTestClient::set_bind_to_address(QuicIpAddress address) {
+ client_->set_bind_to_address(address);
+}
+
+const QuicSocketAddress& QuicTestClient::address() const {
+ return client_->server_address();
+}
+
+void QuicTestClient::WaitForWriteToFlush() {
+ while (connected() && client()->session()->HasDataToWrite()) {
+ client_->WaitForEvents();
+ }
+}
+
+QuicTestClient::TestClientDataToResend::TestClientDataToResend(
+ std::unique_ptr<spdy::SpdyHeaderBlock> headers,
+ QuicStringPiece body,
+ bool fin,
+ QuicTestClient* test_client,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener)
+ : QuicClient::QuicDataToResend(std::move(headers), body, fin),
+ test_client_(test_client),
+ ack_listener_(std::move(ack_listener)) {}
+
+QuicTestClient::TestClientDataToResend::~TestClientDataToResend() = default;
+
+void QuicTestClient::TestClientDataToResend::Resend() {
+ test_client_->GetOrCreateStreamAndSendRequest(headers_.get(), body_, fin_,
+ ack_listener_);
+ headers_.reset();
+}
+
+QuicTestClient::PerStreamState::PerStreamState(const PerStreamState& other)
+ : stream_error(other.stream_error),
+ response_complete(other.response_complete),
+ response_headers_complete(other.response_headers_complete),
+ response_headers(other.response_headers.Clone()),
+ preliminary_headers(other.preliminary_headers.Clone()),
+ response(other.response),
+ response_trailers(other.response_trailers.Clone()),
+ bytes_read(other.bytes_read),
+ bytes_written(other.bytes_written),
+ response_body_size(other.response_body_size) {}
+
+QuicTestClient::PerStreamState::PerStreamState(
+ QuicRstStreamErrorCode stream_error,
+ bool response_complete,
+ bool response_headers_complete,
+ const spdy::SpdyHeaderBlock& response_headers,
+ const spdy::SpdyHeaderBlock& preliminary_headers,
+ const QuicString& response,
+ const spdy::SpdyHeaderBlock& response_trailers,
+ uint64_t bytes_read,
+ uint64_t bytes_written,
+ int64_t response_body_size)
+ : stream_error(stream_error),
+ response_complete(response_complete),
+ response_headers_complete(response_headers_complete),
+ response_headers(response_headers.Clone()),
+ preliminary_headers(preliminary_headers.Clone()),
+ response(response),
+ response_trailers(response_trailers.Clone()),
+ bytes_read(bytes_read),
+ bytes_written(bytes_written),
+ response_body_size(response_body_size) {}
+
+QuicTestClient::PerStreamState::~PerStreamState() = default;
+
+bool QuicTestClient::PopulateHeaderBlockFromUrl(
+ const QuicString& uri,
+ spdy::SpdyHeaderBlock* headers) {
+ QuicString url;
+ if (QuicTextUtils::StartsWith(uri, "https://") ||
+ QuicTextUtils::StartsWith(uri, "http://")) {
+ url = uri;
+ } else if (uri[0] == '/') {
+ url = "https://" + client_->server_id().host() + uri;
+ } else {
+ url = "https://" + uri;
+ }
+ return SpdyUtils::PopulateHeaderBlockFromUrl(url, headers);
+}
+
+void QuicTestClient::ReadNextResponse() {
+ if (closed_stream_states_.empty()) {
+ return;
+ }
+
+ PerStreamState state(closed_stream_states_.front().second);
+
+ stream_error_ = state.stream_error;
+ response_ = state.response;
+ response_complete_ = state.response_complete;
+ response_headers_complete_ = state.response_headers_complete;
+ preliminary_headers_ = state.preliminary_headers.Clone();
+ response_headers_ = state.response_headers.Clone();
+ response_trailers_ = state.response_trailers.Clone();
+ bytes_read_ = state.bytes_read;
+ bytes_written_ = state.bytes_written;
+ response_body_size_ = state.response_body_size;
+
+ closed_stream_states_.pop_front();
+}
+
+void QuicTestClient::ClearPerConnectionState() {
+ ClearPerRequestState();
+ open_streams_.clear();
+ closed_stream_states_.clear();
+ latest_created_stream_ = nullptr;
+}
+
+void QuicTestClient::WaitForDelayedAcks() {
+ // kWaitDuration is a period of time that is long enough for all delayed
+ // acks to be sent and received on the other end.
+ const QuicTime::Delta kWaitDuration =
+ 4 * QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs);
+
+ const QuicClock* clock = client()->client_session()->connection()->clock();
+
+ QuicTime wait_until = clock->ApproximateNow() + kWaitDuration;
+ while (clock->ApproximateNow() < wait_until) {
+ // This waits for up to 50 ms.
+ client()->WaitForEvents();
+ }
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_test_client.h b/quic/test_tools/quic_test_client.h
new file mode 100644
index 0000000..f9cc532
--- /dev/null
+++ b/quic/test_tools/quic_test_client.h
@@ -0,0 +1,413 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_CLIENT_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_CLIENT_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_creator.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/tools/quic_client.h"
+
+namespace quic {
+
+class ProofVerifier;
+class QuicPacketWriterWrapper;
+
+namespace test {
+
+class MockableQuicClientEpollNetworkHelper;
+
+// A quic client which allows mocking out reads and writes.
+class MockableQuicClient : public QuicClient {
+ public:
+ MockableQuicClient(QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicEpollServer* epoll_server);
+
+ MockableQuicClient(QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicEpollServer* epoll_server);
+
+ MockableQuicClient(QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicEpollServer* epoll_server,
+ std::unique_ptr<ProofVerifier> proof_verifier);
+ MockableQuicClient(const MockableQuicClient&) = delete;
+ MockableQuicClient& operator=(const MockableQuicClient&) = delete;
+
+ ~MockableQuicClient() override;
+
+ QuicConnectionId GenerateNewConnectionId() override;
+ void UseConnectionId(QuicConnectionId connection_id);
+
+ void UseWriter(QuicPacketWriterWrapper* writer);
+ void set_peer_address(const QuicSocketAddress& address);
+ // The last incoming packet, iff |track_last_incoming_packet| is true.
+ const QuicReceivedPacket* last_incoming_packet();
+ // If true, copy each packet from ProcessPacket into |last_incoming_packet|
+ void set_track_last_incoming_packet(bool track);
+
+ // Casts the network helper to a MockableQuicClientEpollNetworkHelper.
+ MockableQuicClientEpollNetworkHelper* mockable_network_helper();
+ const MockableQuicClientEpollNetworkHelper* mockable_network_helper() const;
+
+ private:
+ // ConnectionId to use, if connection_id_overridden_
+ QuicConnectionId override_connection_id_;
+ bool connection_id_overridden_;
+ CachedNetworkParameters cached_network_paramaters_;
+};
+
+// A toy QUIC client used for testing.
+class QuicTestClient : public QuicSpdyStream::Visitor,
+ public QuicClientPushPromiseIndex::Delegate {
+ public:
+ QuicTestClient(QuicSocketAddress server_address,
+ const QuicString& server_hostname,
+ const ParsedQuicVersionVector& supported_versions);
+ QuicTestClient(QuicSocketAddress server_address,
+ const QuicString& server_hostname,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions);
+ QuicTestClient(QuicSocketAddress server_address,
+ const QuicString& server_hostname,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ std::unique_ptr<ProofVerifier> proof_verifier);
+
+ ~QuicTestClient() override;
+
+ // Sets the |user_agent_id| of the |client_|.
+ void SetUserAgentID(const QuicString& user_agent_id);
+
+ // Wraps data in a quic packet and sends it.
+ ssize_t SendData(const QuicString& data, bool last_data);
+ // As above, but |delegate| will be notified when |data| is ACKed.
+ ssize_t SendData(
+ const QuicString& data,
+ bool last_data,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+
+ // Clears any outstanding state and sends a simple GET of 'uri' to the
+ // server. Returns 0 if the request failed and no bytes were written.
+ ssize_t SendRequest(const QuicString& uri);
+ // Send a request R and a RST_FRAME which resets R, in the same packet.
+ ssize_t SendRequestAndRstTogether(const QuicString& uri);
+ // Sends requests for all the urls and waits for the responses. To process
+ // the individual responses as they are returned, the caller should use the
+ // set the response_listener on the client().
+ void SendRequestsAndWaitForResponses(const std::vector<QuicString>& url_list);
+ // Sends a request containing |headers| and |body| and returns the number of
+ // bytes sent (the size of the serialized request headers and body).
+ ssize_t SendMessage(const spdy::SpdyHeaderBlock& headers,
+ QuicStringPiece body);
+ // Sends a request containing |headers| and |body| with the fin bit set to
+ // |fin| and returns the number of bytes sent (the size of the serialized
+ // request headers and body).
+ ssize_t SendMessage(const spdy::SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin);
+ // Sends a request containing |headers| and |body| with the fin bit set to
+ // |fin| and returns the number of bytes sent (the size of the serialized
+ // request headers and body). If |flush| is true, will wait for the message to
+ // be flushed before returning.
+ ssize_t SendMessage(const spdy::SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin,
+ bool flush);
+ // Sends a request containing |headers| and |body|, waits for the response,
+ // and returns the response body.
+ QuicString SendCustomSynchronousRequest(const spdy::SpdyHeaderBlock& headers,
+ const QuicString& body);
+ // Sends a GET request for |uri|, waits for the response, and returns the
+ // response body.
+ QuicString SendSynchronousRequest(const QuicString& uri);
+ void SendConnectivityProbing();
+ void Connect();
+ void ResetConnection();
+ void Disconnect();
+ QuicSocketAddress local_address() const;
+ void ClearPerRequestState();
+ bool WaitUntil(int timeout_ms, std::function<bool()> trigger);
+ ssize_t Send(const void* buffer, size_t size);
+ bool connected() const;
+ bool buffer_body() const;
+ void set_buffer_body(bool buffer_body);
+
+ // Getters for stream state. Please note, these getters are divided into two
+ // groups. 1) returns state which only get updated once a complete response
+ // is received. 2) returns state of the oldest active stream which have
+ // received partial response (if any).
+ // Group 1.
+ const spdy::SpdyHeaderBlock& response_trailers() const;
+ bool response_complete() const;
+ int64_t response_body_size() const;
+ const QuicString& response_body() const;
+ // Group 2.
+ bool response_headers_complete() const;
+ const spdy::SpdyHeaderBlock* response_headers() const;
+ const spdy::SpdyHeaderBlock* preliminary_headers() const;
+ int64_t response_size() const;
+ size_t bytes_read() const;
+ size_t bytes_written() const;
+
+ // Returns once at least one complete response or a connection close has been
+ // received from the server. If responses are received for multiple (say 2)
+ // streams, next WaitForResponse will return immediately.
+ void WaitForResponse() { WaitForResponseForMs(-1); }
+
+ // Returns once some data is received on any open streams or at least one
+ // complete response is received from the server.
+ void WaitForInitialResponse() { WaitForInitialResponseForMs(-1); }
+
+ // Returns once at least one complete response or a connection close has been
+ // received from the server, or once the timeout expires. -1 means no timeout.
+ // If responses are received for multiple (say 2) streams, next
+ // WaitForResponseForMs will return immediately.
+ void WaitForResponseForMs(int timeout_ms) {
+ WaitUntil(timeout_ms, [this]() { return !closed_stream_states_.empty(); });
+ if (response_complete()) {
+ VLOG(1) << "Client received response:"
+ << response_headers()->DebugString() << response_body();
+ }
+ }
+
+ // Returns once some data is received on any open streams or at least one
+ // complete response is received from the server, or once the timeout
+ // expires. -1 means no timeout.
+ void WaitForInitialResponseForMs(int timeout_ms) {
+ WaitUntil(timeout_ms, [this]() { return response_size() != 0; });
+ }
+
+ // Migrate local address to <|new_host|, a random port>.
+ // Return whether the migration succeeded.
+ bool MigrateSocket(const QuicIpAddress& new_host);
+ // Migrate local address to <|new_host|, |port|>.
+ // Return whether the migration succeeded.
+ bool MigrateSocketWithSpecifiedPort(const QuicIpAddress& new_host, int port);
+ QuicIpAddress bind_to_address() const;
+ void set_bind_to_address(QuicIpAddress address);
+ const QuicSocketAddress& address() const;
+
+ // From QuicSpdyStream::Visitor
+ void OnClose(QuicSpdyStream* stream) override;
+
+ // From QuicClientPushPromiseIndex::Delegate
+ bool CheckVary(const spdy::SpdyHeaderBlock& client_request,
+ const spdy::SpdyHeaderBlock& promise_request,
+ const spdy::SpdyHeaderBlock& promise_response) override;
+ void OnRendezvousResult(QuicSpdyStream*) override;
+
+ // Configures client_ to take ownership of and use the writer.
+ // Must be called before initial connect.
+ void UseWriter(QuicPacketWriterWrapper* writer);
+ // If the given ConnectionId is nonzero, configures client_ to use a specific
+ // ConnectionId instead of a random one.
+ void UseConnectionId(QuicConnectionId connection_id);
+
+ // Returns nullptr if the maximum number of streams have already been created.
+ QuicSpdyClientStream* GetOrCreateStream();
+
+ // Calls GetOrCreateStream(), sends the request on the stream, and
+ // stores the request in case it needs to be resent. If |headers| is
+ // null, only the body will be sent on the stream.
+ ssize_t GetOrCreateStreamAndSendRequest(
+ const spdy::SpdyHeaderBlock* headers,
+ QuicStringPiece body,
+ bool fin,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+
+ QuicRstStreamErrorCode stream_error() { return stream_error_; }
+ QuicErrorCode connection_error();
+
+ MockableQuicClient* client();
+
+ // cert_common_name returns the common name value of the server's certificate,
+ // or the empty QuicString if no certificate was presented.
+ const QuicString& cert_common_name() const;
+
+ // cert_sct returns the signed timestamp of the server's certificate,
+ // or the empty QuicString if no signed timestamp was presented.
+ const QuicString& cert_sct() const;
+
+ // Get the server config map.
+ QuicTagValueMap GetServerConfig() const;
+
+ void set_auto_reconnect(bool reconnect) { auto_reconnect_ = reconnect; }
+
+ void set_priority(spdy::SpdyPriority priority) { priority_ = priority; }
+
+ void WaitForWriteToFlush();
+
+ QuicEpollServer* epoll_server() { return &epoll_server_; }
+
+ size_t num_requests() const { return num_requests_; }
+
+ size_t num_responses() const { return num_responses_; }
+
+ void set_server_address(const QuicSocketAddress& server_address) {
+ client_->set_server_address(server_address);
+ }
+
+ void set_peer_address(const QuicSocketAddress& address) {
+ client_->set_peer_address(address);
+ }
+
+ // Explicitly set the SNI value for this client, overriding the default
+ // behavior which extracts the SNI value from the request URL.
+ void OverrideSni(const QuicString& sni) {
+ override_sni_set_ = true;
+ override_sni_ = sni;
+ }
+
+ void Initialize();
+
+ void set_client(MockableQuicClient* client) { client_.reset(client); }
+
+ // Given |uri|, populates the fields in |headers| for a simple GET
+ // request. If |uri| is a relative URL, the QuicServerId will be
+ // use to specify the authority.
+ bool PopulateHeaderBlockFromUrl(const QuicString& uri,
+ spdy::SpdyHeaderBlock* headers);
+
+ // Waits for a period of time that is long enough to receive all delayed acks
+ // sent by peer.
+ void WaitForDelayedAcks();
+
+ QuicSpdyClientStream* latest_created_stream() {
+ return latest_created_stream_;
+ }
+
+ protected:
+ QuicTestClient();
+ QuicTestClient(const QuicTestClient&) = delete;
+ QuicTestClient& operator=(const QuicTestClient&) = delete;
+
+ private:
+ class TestClientDataToResend : public QuicClient::QuicDataToResend {
+ public:
+ TestClientDataToResend(
+ std::unique_ptr<spdy::SpdyHeaderBlock> headers,
+ QuicStringPiece body,
+ bool fin,
+ QuicTestClient* test_client,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+
+ ~TestClientDataToResend() override;
+
+ void Resend() override;
+
+ protected:
+ QuicTestClient* test_client_;
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener_;
+ };
+
+ // PerStreamState of a stream is updated when it is closed.
+ struct PerStreamState {
+ PerStreamState(const PerStreamState& other);
+ PerStreamState(QuicRstStreamErrorCode stream_error,
+ bool response_complete,
+ bool response_headers_complete,
+ const spdy::SpdyHeaderBlock& response_headers,
+ const spdy::SpdyHeaderBlock& preliminary_headers,
+ const QuicString& response,
+ const spdy::SpdyHeaderBlock& response_trailers,
+ uint64_t bytes_read,
+ uint64_t bytes_written,
+ int64_t response_body_size);
+ ~PerStreamState();
+
+ QuicRstStreamErrorCode stream_error;
+ bool response_complete;
+ bool response_headers_complete;
+ spdy::SpdyHeaderBlock response_headers;
+ spdy::SpdyHeaderBlock preliminary_headers;
+ QuicString response;
+ spdy::SpdyHeaderBlock response_trailers;
+ uint64_t bytes_read;
+ uint64_t bytes_written;
+ int64_t response_body_size;
+ };
+
+ bool HaveActiveStream();
+
+ // Read oldest received response and remove it from closed_stream_states_.
+ void ReadNextResponse();
+
+ // Clear open_streams_, closed_stream_states_ and reset
+ // latest_created_stream_.
+ void ClearPerConnectionState();
+
+ // Update latest_created_stream_, add |stream| to open_streams_ and starts
+ // tracking its state.
+ void SetLatestCreatedStream(QuicSpdyClientStream* stream);
+
+ QuicEpollServer epoll_server_;
+ std::unique_ptr<MockableQuicClient> client_; // The actual client
+ QuicSpdyClientStream* latest_created_stream_;
+ std::map<QuicStreamId, QuicSpdyClientStream*> open_streams_;
+ // Received responses of closed streams.
+ QuicLinkedHashMap<QuicStreamId, PerStreamState> closed_stream_states_;
+
+ QuicRstStreamErrorCode stream_error_;
+
+ bool response_complete_;
+ bool response_headers_complete_;
+ mutable spdy::SpdyHeaderBlock preliminary_headers_;
+ mutable spdy::SpdyHeaderBlock response_headers_;
+
+ // Parsed response trailers (if present), copied from the stream in OnClose.
+ spdy::SpdyHeaderBlock response_trailers_;
+
+ spdy::SpdyPriority priority_;
+ QuicString response_;
+ // bytes_read_ and bytes_written_ are updated only when stream_ is released;
+ // prefer bytes_read() and bytes_written() member functions.
+ uint64_t bytes_read_;
+ uint64_t bytes_written_;
+ // The number of HTTP body bytes received.
+ int64_t response_body_size_;
+ // True if we tried to connect already since the last call to Disconnect().
+ bool connect_attempted_;
+ // The client will auto-connect exactly once before sending data. If
+ // something causes a connection reset, it will not automatically reconnect
+ // unless auto_reconnect_ is true.
+ bool auto_reconnect_;
+ // Should we buffer the response body? Defaults to true.
+ bool buffer_body_;
+ // For async push promise rendezvous, validation may fail in which
+ // case the request should be retried.
+ std::unique_ptr<TestClientDataToResend> push_promise_data_to_resend_;
+ // Number of requests/responses this client has sent/received.
+ size_t num_requests_;
+ size_t num_responses_;
+
+ // If set, this value is used for the connection SNI, overriding the usual
+ // logic which extracts the SNI from the request URL.
+ bool override_sni_set_ = false;
+ QuicString override_sni_;
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_CLIENT_H_
diff --git a/quic/test_tools/quic_test_server.cc b/quic/test_tools/quic_test_server.cc
new file mode 100644
index 0000000..bda2148
--- /dev/null
+++ b/quic/test_tools/quic_test_server.cc
@@ -0,0 +1,232 @@
+// Copyright (c) 2015 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 "net/third_party/quiche/src/quic/test_tools/quic_test_server.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/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_session.h"
+
+namespace quic {
+
+namespace test {
+
+class CustomStreamSession : public QuicSimpleServerSession {
+ public:
+ CustomStreamSession(
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection,
+ QuicSession::Visitor* visitor,
+ QuicCryptoServerStream::Helper* helper,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicTestServer::StreamFactory* stream_factory,
+ QuicTestServer::CryptoStreamFactory* crypto_stream_factory,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicSimpleServerSession(config,
+ supported_versions,
+ connection,
+ visitor,
+ helper,
+ crypto_config,
+ compressed_certs_cache,
+ quic_simple_server_backend),
+ stream_factory_(stream_factory),
+ crypto_stream_factory_(crypto_stream_factory) {}
+
+ QuicSpdyStream* CreateIncomingStream(QuicStreamId id) override {
+ if (!ShouldCreateIncomingStream(id)) {
+ return nullptr;
+ }
+ if (stream_factory_) {
+ QuicSpdyStream* stream =
+ stream_factory_->CreateStream(id, this, server_backend());
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+ }
+ return QuicSimpleServerSession::CreateIncomingStream(id);
+ }
+
+ QuicCryptoServerStreamBase* CreateQuicCryptoServerStream(
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache) override {
+ if (crypto_stream_factory_) {
+ return crypto_stream_factory_->CreateCryptoStream(crypto_config, this);
+ }
+ return QuicSimpleServerSession::CreateQuicCryptoServerStream(
+ crypto_config, compressed_certs_cache);
+ }
+
+ private:
+ QuicTestServer::StreamFactory* stream_factory_; // Not owned.
+ QuicTestServer::CryptoStreamFactory* crypto_stream_factory_; // Not owned.
+};
+
+class QuicTestDispatcher : public QuicSimpleDispatcher {
+ public:
+ QuicTestDispatcher(
+ 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,
+ QuicSimpleServerBackend* quic_simple_server_backend,
+ uint8_t expected_connection_id_length)
+ : QuicSimpleDispatcher(config,
+ crypto_config,
+ version_manager,
+ std::move(helper),
+ std::move(session_helper),
+ std::move(alarm_factory),
+ quic_simple_server_backend,
+ expected_connection_id_length),
+ session_factory_(nullptr),
+ stream_factory_(nullptr),
+ crypto_stream_factory_(nullptr) {}
+
+ QuicServerSessionBase* CreateQuicSession(
+ QuicConnectionId id,
+ const QuicSocketAddress& client,
+ QuicStringPiece alpn,
+ const ParsedQuicVersion& version) override {
+ QuicReaderMutexLock lock(&factory_lock_);
+ if (session_factory_ == nullptr && stream_factory_ == nullptr &&
+ crypto_stream_factory_ == nullptr) {
+ return QuicSimpleDispatcher::CreateQuicSession(id, client, alpn, version);
+ }
+ QuicConnection* connection =
+ new QuicConnection(id, client, helper(), alarm_factory(), writer(),
+ /* owns_writer= */ false, Perspective::IS_SERVER,
+ ParsedQuicVersionVector{version});
+
+ QuicServerSessionBase* session = nullptr;
+ if (stream_factory_ != nullptr || crypto_stream_factory_ != nullptr) {
+ session = new CustomStreamSession(
+ config(), GetSupportedVersions(), connection, this, session_helper(),
+ crypto_config(), compressed_certs_cache(), stream_factory_,
+ crypto_stream_factory_, server_backend());
+ } else {
+ session = session_factory_->CreateSession(
+ config(), connection, this, session_helper(), crypto_config(),
+ compressed_certs_cache(), server_backend());
+ }
+ session->Initialize();
+ return session;
+ }
+
+ void SetSessionFactory(QuicTestServer::SessionFactory* factory) {
+ QuicWriterMutexLock lock(&factory_lock_);
+ DCHECK(session_factory_ == nullptr);
+ DCHECK(stream_factory_ == nullptr);
+ DCHECK(crypto_stream_factory_ == nullptr);
+ session_factory_ = factory;
+ }
+
+ void SetStreamFactory(QuicTestServer::StreamFactory* factory) {
+ QuicWriterMutexLock lock(&factory_lock_);
+ DCHECK(session_factory_ == nullptr);
+ DCHECK(stream_factory_ == nullptr);
+ stream_factory_ = factory;
+ }
+
+ void SetCryptoStreamFactory(QuicTestServer::CryptoStreamFactory* factory) {
+ QuicWriterMutexLock lock(&factory_lock_);
+ DCHECK(session_factory_ == nullptr);
+ DCHECK(crypto_stream_factory_ == nullptr);
+ crypto_stream_factory_ = factory;
+ }
+
+ private:
+ QuicMutex factory_lock_;
+ QuicTestServer::SessionFactory* session_factory_; // Not owned.
+ QuicTestServer::StreamFactory* stream_factory_; // Not owned.
+ QuicTestServer::CryptoStreamFactory* crypto_stream_factory_; // Not owned.
+};
+
+QuicTestServer::QuicTestServer(
+ std::unique_ptr<ProofSource> proof_source,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicServer(std::move(proof_source), quic_simple_server_backend) {}
+
+QuicTestServer::QuicTestServer(
+ std::unique_ptr<ProofSource> proof_source,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicTestServer(std::move(proof_source),
+ config,
+ supported_versions,
+ quic_simple_server_backend,
+ kQuicDefaultConnectionIdLength) {}
+
+QuicTestServer::QuicTestServer(
+ std::unique_ptr<ProofSource> proof_source,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicSimpleServerBackend* quic_simple_server_backend,
+ uint8_t expected_connection_id_length)
+ : QuicServer(std::move(proof_source),
+ config,
+ QuicCryptoServerConfig::ConfigOptions(),
+ supported_versions,
+ quic_simple_server_backend,
+ expected_connection_id_length) {}
+
+QuicDispatcher* QuicTestServer::CreateQuicDispatcher() {
+ return new QuicTestDispatcher(
+ &config(), &crypto_config(), version_manager(),
+ QuicMakeUnique<QuicEpollConnectionHelper>(epoll_server(),
+ QuicAllocator::BUFFER_POOL),
+ std::unique_ptr<QuicCryptoServerStream::Helper>(
+ new QuicSimpleCryptoServerStreamHelper(QuicRandom::GetInstance())),
+ QuicMakeUnique<QuicEpollAlarmFactory>(epoll_server()), server_backend(),
+ expected_connection_id_length());
+}
+
+void QuicTestServer::SetSessionFactory(SessionFactory* factory) {
+ DCHECK(dispatcher());
+ static_cast<QuicTestDispatcher*>(dispatcher())->SetSessionFactory(factory);
+}
+
+void QuicTestServer::SetSpdyStreamFactory(StreamFactory* factory) {
+ static_cast<QuicTestDispatcher*>(dispatcher())->SetStreamFactory(factory);
+}
+
+void QuicTestServer::SetCryptoStreamFactory(CryptoStreamFactory* factory) {
+ static_cast<QuicTestDispatcher*>(dispatcher())
+ ->SetCryptoStreamFactory(factory);
+}
+
+/////////////////////////// TEST SESSIONS ///////////////////////////////
+
+ImmediateGoAwaySession::ImmediateGoAwaySession(
+ const QuicConfig& config,
+ QuicConnection* connection,
+ QuicSession::Visitor* visitor,
+ QuicCryptoServerStream::Helper* helper,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicSimpleServerSession(config,
+ CurrentSupportedVersions(),
+ connection,
+ visitor,
+ helper,
+ crypto_config,
+ compressed_certs_cache,
+ quic_simple_server_backend) {}
+
+void ImmediateGoAwaySession::OnStreamFrame(const QuicStreamFrame& frame) {
+ SendGoAway(QUIC_PEER_GOING_AWAY, "");
+ QuicSimpleServerSession::OnStreamFrame(frame);
+}
+
+} // namespace test
+
+} // namespace quic
diff --git a/quic/test_tools/quic_test_server.h b/quic/test_tools/quic_test_server.h
new file mode 100644
index 0000000..52fb7c5
--- /dev/null
+++ b/quic/test_tools/quic_test_server.h
@@ -0,0 +1,113 @@
+// Copyright (c) 2015 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_SERVER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_SERVER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_dispatcher.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/tools/quic_server.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_session.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h"
+
+namespace quic {
+
+namespace test {
+
+// A test server which enables easy creation of custom QuicServerSessions
+//
+// Eventually this may be extended to allow custom QuicConnections etc.
+class QuicTestServer : public QuicServer {
+ public:
+ // Factory for creating QuicServerSessions.
+ class SessionFactory {
+ public:
+ virtual ~SessionFactory() {}
+
+ // Returns a new session owned by the caller.
+ virtual QuicServerSessionBase* CreateSession(
+ const QuicConfig& config,
+ QuicConnection* connection,
+ QuicSession::Visitor* visitor,
+ QuicCryptoServerStream::Helper* helper,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicSimpleServerBackend* quic_simple_server_backend) = 0;
+ };
+
+ // Factory for creating QuicSimpleServerStreams.
+ class StreamFactory {
+ public:
+ virtual ~StreamFactory() {}
+
+ // Returns a new stream owned by the caller.
+ virtual QuicSimpleServerStream* CreateStream(
+ QuicStreamId id,
+ QuicSpdySession* session,
+ QuicSimpleServerBackend* quic_simple_server_backend) = 0;
+ };
+
+ class CryptoStreamFactory {
+ public:
+ virtual ~CryptoStreamFactory() {}
+
+ // Returns a new QuicCryptoServerStreamBase owned by the caller
+ virtual QuicCryptoServerStreamBase* CreateCryptoStream(
+ const QuicCryptoServerConfig* crypto_config,
+ QuicServerSessionBase* session) = 0;
+ };
+
+ QuicTestServer(std::unique_ptr<ProofSource> proof_source,
+ QuicSimpleServerBackend* quic_simple_server_backend);
+ QuicTestServer(std::unique_ptr<ProofSource> proof_source,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicSimpleServerBackend* quic_simple_server_backend);
+ QuicTestServer(std::unique_ptr<ProofSource> proof_source,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicSimpleServerBackend* quic_simple_server_backend,
+ uint8_t expected_connection_id_length);
+
+ // Create a custom dispatcher which creates custom sessions.
+ QuicDispatcher* CreateQuicDispatcher() override;
+
+ // Sets a custom session factory, owned by the caller, for easy custom
+ // session logic. This is incompatible with setting a stream factory or a
+ // crypto stream factory.
+ void SetSessionFactory(SessionFactory* factory);
+
+ // Sets a custom stream factory, owned by the caller, for easy custom
+ // stream logic. This is incompatible with setting a session factory.
+ void SetSpdyStreamFactory(StreamFactory* factory);
+
+ // Sets a custom crypto stream factory, owned by the caller, for easy custom
+ // crypto logic. This is incompatible with setting a session factory.
+ void SetCryptoStreamFactory(CryptoStreamFactory* factory);
+};
+
+// Useful test sessions for the QuicTestServer.
+
+// Test session which sends a GOAWAY immedaitely on creation, before crypto
+// credentials have even been established.
+class ImmediateGoAwaySession : public QuicSimpleServerSession {
+ public:
+ ImmediateGoAwaySession(const QuicConfig& config,
+ QuicConnection* connection,
+ QuicSession::Visitor* visitor,
+ QuicCryptoServerStream::Helper* helper,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicSimpleServerBackend* quic_simple_server_backend);
+
+ // Override to send GoAway.
+ void OnStreamFrame(const QuicStreamFrame& frame) override;
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_SERVER_H_
diff --git a/quic/test_tools/quic_test_utils.cc b/quic/test_tools/quic_test_utils.cc
new file mode 100644
index 0000000..6e3c2a5
--- /dev/null
+++ b/quic/test_tools/quic_test_utils.cc
@@ -0,0 +1,1177 @@
+// Copyright (c) 2012 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 "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "third_party/boringssl/src/include/openssl/sha.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_creator.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_frame_builder.h"
+
+using testing::_;
+using testing::Invoke;
+
+namespace quic {
+namespace test {
+
+QuicConnectionId TestConnectionId() {
+ // Chosen by fair dice roll.
+ // Guaranteed to be random.
+ return TestConnectionId(42);
+}
+
+QuicConnectionId TestConnectionId(uint64_t connection_number) {
+ const uint64_t connection_id64_net =
+ QuicEndian::HostToNet64(connection_number);
+ return QuicConnectionId(reinterpret_cast<const char*>(&connection_id64_net),
+ sizeof(connection_id64_net));
+}
+
+uint64_t TestConnectionIdToUInt64(QuicConnectionId connection_id) {
+ DCHECK_EQ(connection_id.length(), kQuicDefaultConnectionIdLength);
+ uint64_t connection_id64_net = 0;
+ memcpy(&connection_id64_net, connection_id.data(),
+ std::min<size_t>(static_cast<size_t>(connection_id.length()),
+ sizeof(connection_id64_net)));
+ return QuicEndian::NetToHost64(connection_id64_net);
+}
+
+QuicAckFrame InitAckFrame(const std::vector<QuicAckBlock>& ack_blocks) {
+ DCHECK_GT(ack_blocks.size(), 0u);
+
+ QuicAckFrame ack;
+ QuicPacketNumber end_of_previous_block(1);
+ for (const QuicAckBlock& block : ack_blocks) {
+ DCHECK_GE(block.start, end_of_previous_block);
+ DCHECK_GT(block.limit, block.start);
+ ack.packets.AddRange(block.start, block.limit);
+ end_of_previous_block = block.limit;
+ }
+
+ ack.largest_acked = ack.packets.Max();
+
+ return ack;
+}
+
+QuicAckFrame InitAckFrame(uint64_t largest_acked) {
+ return InitAckFrame(QuicPacketNumber(largest_acked));
+}
+
+QuicAckFrame InitAckFrame(QuicPacketNumber largest_acked) {
+ return InitAckFrame({{QuicPacketNumber(1), largest_acked + 1}});
+}
+
+QuicAckFrame MakeAckFrameWithAckBlocks(size_t num_ack_blocks,
+ uint64_t least_unacked) {
+ QuicAckFrame ack;
+ ack.largest_acked = QuicPacketNumber(2 * num_ack_blocks + least_unacked);
+ // Add enough received packets to get num_ack_blocks ack blocks.
+ for (QuicPacketNumber i = QuicPacketNumber(2);
+ i < QuicPacketNumber(2 * num_ack_blocks + 1); i += 2) {
+ ack.packets.Add(i + least_unacked);
+ }
+ return ack;
+}
+
+std::unique_ptr<QuicPacket> BuildUnsizedDataPacket(
+ QuicFramer* framer,
+ const QuicPacketHeader& header,
+ const QuicFrames& frames) {
+ const size_t max_plaintext_size = framer->GetMaxPlaintextSize(kMaxPacketSize);
+ size_t packet_size = GetPacketHeaderSize(framer->transport_version(), header);
+ for (size_t i = 0; i < frames.size(); ++i) {
+ DCHECK_LE(packet_size, max_plaintext_size);
+ bool first_frame = i == 0;
+ bool last_frame = i == frames.size() - 1;
+ const size_t frame_size = framer->GetSerializedFrameLength(
+ frames[i], max_plaintext_size - packet_size, first_frame, last_frame,
+ header.packet_number_length);
+ DCHECK(frame_size);
+ packet_size += frame_size;
+ }
+ return BuildUnsizedDataPacket(framer, header, frames, packet_size);
+}
+
+std::unique_ptr<QuicPacket> BuildUnsizedDataPacket(
+ QuicFramer* framer,
+ const QuicPacketHeader& header,
+ const QuicFrames& frames,
+ size_t packet_size) {
+ char* buffer = new char[packet_size];
+ size_t length = framer->BuildDataPacket(header, frames, buffer, packet_size,
+ ENCRYPTION_NONE);
+ DCHECK_NE(0u, length);
+ // Re-construct the data packet with data ownership.
+ return QuicMakeUnique<QuicPacket>(
+ buffer, length, /* owns_buffer */ true,
+ GetIncludedDestinationConnectionIdLength(header),
+ GetIncludedSourceConnectionIdLength(header), header.version_flag,
+ header.nonce != nullptr, header.packet_number_length,
+ header.retry_token_length_length, header.retry_token.length(),
+ header.length_length);
+}
+
+QuicString Sha1Hash(QuicStringPiece data) {
+ char buffer[SHA_DIGEST_LENGTH];
+ SHA1(reinterpret_cast<const uint8_t*>(data.data()), data.size(),
+ reinterpret_cast<uint8_t*>(buffer));
+ return QuicString(buffer, QUIC_ARRAYSIZE(buffer));
+}
+
+uint64_t SimpleRandom::RandUint64() {
+ QuicString hash =
+ Sha1Hash(QuicStringPiece(reinterpret_cast<char*>(&seed_), sizeof(seed_)));
+ DCHECK_EQ(static_cast<size_t>(SHA_DIGEST_LENGTH), hash.length());
+ memcpy(&seed_, hash.data(), sizeof(seed_));
+ return seed_;
+}
+
+void SimpleRandom::RandBytes(void* data, size_t len) {
+ uint8_t* real_data = static_cast<uint8_t*>(data);
+ for (size_t offset = 0; offset < len; offset++) {
+ real_data[offset] = RandUint64() & 0xff;
+ }
+}
+
+MockFramerVisitor::MockFramerVisitor() {
+ // By default, we want to accept packets.
+ ON_CALL(*this, OnProtocolVersionMismatch(_, _))
+ .WillByDefault(testing::Return(false));
+
+ // By default, we want to accept packets.
+ ON_CALL(*this, OnUnauthenticatedHeader(_))
+ .WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnUnauthenticatedPublicHeader(_))
+ .WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnPacketHeader(_)).WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnStreamFrame(_)).WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnCryptoFrame(_)).WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnStopWaitingFrame(_)).WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnPaddingFrame(_)).WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnPingFrame(_)).WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnRstStreamFrame(_)).WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnConnectionCloseFrame(_))
+ .WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnApplicationCloseFrame(_))
+ .WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnStopSendingFrame(_)).WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnPathChallengeFrame(_)).WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnPathResponseFrame(_)).WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnGoAwayFrame(_)).WillByDefault(testing::Return(true));
+ ON_CALL(*this, OnMaxStreamIdFrame(_)).WillByDefault(testing::Return(true));
+ ON_CALL(*this, OnStreamIdBlockedFrame(_))
+ .WillByDefault(testing::Return(true));
+}
+
+MockFramerVisitor::~MockFramerVisitor() {}
+
+bool NoOpFramerVisitor::OnProtocolVersionMismatch(ParsedQuicVersion version,
+ PacketHeaderFormat form) {
+ return false;
+}
+
+bool NoOpFramerVisitor::OnUnauthenticatedPublicHeader(
+ const QuicPacketHeader& header) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnUnauthenticatedHeader(
+ const QuicPacketHeader& header) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnPacketHeader(const QuicPacketHeader& header) {
+ return true;
+}
+
+void NoOpFramerVisitor::OnCoalescedPacket(const QuicEncryptedPacket& packet) {}
+
+bool NoOpFramerVisitor::OnStreamFrame(const QuicStreamFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnCryptoFrame(const QuicCryptoFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnAckFrameStart(QuicPacketNumber largest_acked,
+ QuicTime::Delta ack_delay_time) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnAckRange(QuicPacketNumber start,
+ QuicPacketNumber end) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnAckTimestamp(QuicPacketNumber packet_number,
+ QuicTime timestamp) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnAckFrameEnd(QuicPacketNumber start) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnStopWaitingFrame(const QuicStopWaitingFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnPaddingFrame(const QuicPaddingFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnPingFrame(const QuicPingFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnRstStreamFrame(const QuicRstStreamFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnApplicationCloseFrame(
+ const QuicApplicationCloseFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnNewConnectionIdFrame(
+ const QuicNewConnectionIdFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnRetireConnectionIdFrame(
+ const QuicRetireConnectionIdFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnNewTokenFrame(const QuicNewTokenFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnStopSendingFrame(const QuicStopSendingFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnPathChallengeFrame(
+ const QuicPathChallengeFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnPathResponseFrame(
+ const QuicPathResponseFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnStreamIdBlockedFrame(
+ const QuicStreamIdBlockedFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnWindowUpdateFrame(
+ const QuicWindowUpdateFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnBlockedFrame(const QuicBlockedFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnMessageFrame(const QuicMessageFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::IsValidStatelessResetToken(QuicUint128 token) const {
+ return false;
+}
+
+MockQuicConnectionVisitor::MockQuicConnectionVisitor() {}
+
+MockQuicConnectionVisitor::~MockQuicConnectionVisitor() {}
+
+MockQuicConnectionHelper::MockQuicConnectionHelper() {}
+
+MockQuicConnectionHelper::~MockQuicConnectionHelper() {}
+
+const QuicClock* MockQuicConnectionHelper::GetClock() const {
+ return &clock_;
+}
+
+QuicRandom* MockQuicConnectionHelper::GetRandomGenerator() {
+ return &random_generator_;
+}
+
+QuicAlarm* MockAlarmFactory::CreateAlarm(QuicAlarm::Delegate* delegate) {
+ return new MockAlarmFactory::TestAlarm(
+ QuicArenaScopedPtr<QuicAlarm::Delegate>(delegate));
+}
+
+QuicArenaScopedPtr<QuicAlarm> MockAlarmFactory::CreateAlarm(
+ QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
+ QuicConnectionArena* arena) {
+ if (arena != nullptr) {
+ return arena->New<TestAlarm>(std::move(delegate));
+ } else {
+ return QuicArenaScopedPtr<TestAlarm>(new TestAlarm(std::move(delegate)));
+ }
+}
+
+QuicBufferAllocator* MockQuicConnectionHelper::GetStreamSendBufferAllocator() {
+ return &buffer_allocator_;
+}
+
+void MockQuicConnectionHelper::AdvanceTime(QuicTime::Delta delta) {
+ clock_.AdvanceTime(delta);
+}
+
+MockQuicConnection::MockQuicConnection(MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective)
+ : MockQuicConnection(TestConnectionId(),
+ QuicSocketAddress(TestPeerIPAddress(), kTestPort),
+ helper,
+ alarm_factory,
+ perspective,
+ ParsedVersionOfIndex(CurrentSupportedVersions(), 0)) {}
+
+MockQuicConnection::MockQuicConnection(QuicSocketAddress address,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective)
+ : MockQuicConnection(TestConnectionId(),
+ address,
+ helper,
+ alarm_factory,
+ perspective,
+ ParsedVersionOfIndex(CurrentSupportedVersions(), 0)) {}
+
+MockQuicConnection::MockQuicConnection(QuicConnectionId connection_id,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective)
+ : MockQuicConnection(connection_id,
+ QuicSocketAddress(TestPeerIPAddress(), kTestPort),
+ helper,
+ alarm_factory,
+ perspective,
+ ParsedVersionOfIndex(CurrentSupportedVersions(), 0)) {}
+
+MockQuicConnection::MockQuicConnection(
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective,
+ const ParsedQuicVersionVector& supported_versions)
+ : MockQuicConnection(TestConnectionId(),
+ QuicSocketAddress(TestPeerIPAddress(), kTestPort),
+ helper,
+ alarm_factory,
+ perspective,
+ supported_versions) {}
+
+MockQuicConnection::MockQuicConnection(
+ QuicConnectionId connection_id,
+ QuicSocketAddress address,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective,
+ const ParsedQuicVersionVector& supported_versions)
+ : QuicConnection(connection_id,
+ address,
+ helper,
+ alarm_factory,
+ new testing::NiceMock<MockPacketWriter>(),
+ /* owns_writer= */ true,
+ perspective,
+ supported_versions) {
+ ON_CALL(*this, OnError(_))
+ .WillByDefault(
+ Invoke(this, &PacketSavingConnection::QuicConnection_OnError));
+ ON_CALL(*this, SendCryptoData(_, _, _))
+ .WillByDefault(
+ Invoke(this, &MockQuicConnection::QuicConnection_SendCryptoData));
+
+ SetSelfAddress(QuicSocketAddress(QuicIpAddress::Any4(), 5));
+}
+
+MockQuicConnection::~MockQuicConnection() {}
+
+void MockQuicConnection::AdvanceTime(QuicTime::Delta delta) {
+ static_cast<MockQuicConnectionHelper*>(helper())->AdvanceTime(delta);
+}
+
+bool MockQuicConnection::OnProtocolVersionMismatch(ParsedQuicVersion version,
+ PacketHeaderFormat form) {
+ return false;
+}
+
+PacketSavingConnection::PacketSavingConnection(MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective)
+ : MockQuicConnection(helper, alarm_factory, perspective) {}
+
+PacketSavingConnection::PacketSavingConnection(
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective,
+ const ParsedQuicVersionVector& supported_versions)
+ : MockQuicConnection(helper,
+ alarm_factory,
+ perspective,
+ supported_versions) {}
+
+PacketSavingConnection::~PacketSavingConnection() {}
+
+void PacketSavingConnection::SendOrQueuePacket(SerializedPacket* packet) {
+ encrypted_packets_.push_back(QuicMakeUnique<QuicEncryptedPacket>(
+ CopyBuffer(*packet), packet->encrypted_length, true));
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+ // Transfer ownership of the packet to the SentPacketManager and the
+ // ack notifier to the AckNotifierManager.
+ QuicConnectionPeer::GetSentPacketManager(this)->OnPacketSent(
+ packet, QuicPacketNumber(), clock_.ApproximateNow(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
+}
+
+MockQuicSession::MockQuicSession(QuicConnection* connection)
+ : MockQuicSession(connection, true) {}
+
+MockQuicSession::MockQuicSession(QuicConnection* connection,
+ bool create_mock_crypto_stream)
+ : QuicSession(connection,
+ nullptr,
+ DefaultQuicConfig(),
+ CurrentSupportedVersions()) {
+ if (create_mock_crypto_stream) {
+ crypto_stream_ = QuicMakeUnique<MockQuicCryptoStream>(this);
+ }
+ ON_CALL(*this, WritevData(_, _, _, _, _))
+ .WillByDefault(testing::Return(QuicConsumedData(0, false)));
+}
+
+MockQuicSession::~MockQuicSession() {
+ delete connection();
+}
+
+QuicCryptoStream* MockQuicSession::GetMutableCryptoStream() {
+ return crypto_stream_.get();
+}
+
+const QuicCryptoStream* MockQuicSession::GetCryptoStream() const {
+ return crypto_stream_.get();
+}
+
+void MockQuicSession::SetCryptoStream(QuicCryptoStream* crypto_stream) {
+ crypto_stream_.reset(crypto_stream);
+}
+
+// static
+QuicConsumedData MockQuicSession::ConsumeData(QuicStream* stream,
+ QuicStreamId /*id*/,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state) {
+ if (write_length > 0) {
+ auto buf = QuicMakeUnique<char[]>(write_length);
+ QuicDataWriter writer(write_length, buf.get(), HOST_BYTE_ORDER);
+ stream->WriteStreamData(offset, write_length, &writer);
+ } else {
+ DCHECK(state != NO_FIN);
+ }
+ return QuicConsumedData(write_length, state != NO_FIN);
+}
+
+MockQuicCryptoStream::MockQuicCryptoStream(QuicSession* session)
+ : QuicCryptoStream(session), params_(new QuicCryptoNegotiatedParameters) {}
+
+MockQuicCryptoStream::~MockQuicCryptoStream() {}
+
+bool MockQuicCryptoStream::encryption_established() const {
+ return false;
+}
+
+bool MockQuicCryptoStream::handshake_confirmed() const {
+ return false;
+}
+
+const QuicCryptoNegotiatedParameters&
+MockQuicCryptoStream::crypto_negotiated_params() const {
+ return *params_;
+}
+
+CryptoMessageParser* MockQuicCryptoStream::crypto_message_parser() {
+ return &crypto_framer_;
+}
+
+MockQuicSpdySession::MockQuicSpdySession(QuicConnection* connection)
+ : MockQuicSpdySession(connection, true) {}
+
+MockQuicSpdySession::MockQuicSpdySession(QuicConnection* connection,
+ bool create_mock_crypto_stream)
+ : QuicSpdySession(connection,
+ nullptr,
+ DefaultQuicConfig(),
+ connection->supported_versions()) {
+ if (create_mock_crypto_stream) {
+ crypto_stream_ = QuicMakeUnique<MockQuicCryptoStream>(this);
+ }
+
+ ON_CALL(*this, WritevData(_, _, _, _, _))
+ .WillByDefault(testing::Return(QuicConsumedData(0, false)));
+}
+
+MockQuicSpdySession::~MockQuicSpdySession() {
+ delete connection();
+}
+
+QuicCryptoStream* MockQuicSpdySession::GetMutableCryptoStream() {
+ return crypto_stream_.get();
+}
+
+const QuicCryptoStream* MockQuicSpdySession::GetCryptoStream() const {
+ return crypto_stream_.get();
+}
+
+void MockQuicSpdySession::SetCryptoStream(QuicCryptoStream* crypto_stream) {
+ crypto_stream_.reset(crypto_stream);
+}
+
+TestQuicSpdyServerSession::TestQuicSpdyServerSession(
+ QuicConnection* connection,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache)
+ : QuicServerSessionBase(config,
+ supported_versions,
+ connection,
+ &visitor_,
+ &helper_,
+ crypto_config,
+ compressed_certs_cache) {
+ Initialize();
+ ON_CALL(helper_, GenerateConnectionIdForReject(_, _))
+ .WillByDefault(testing::Return(
+ QuicUtils::CreateRandomConnectionId(connection->random_generator())));
+ ON_CALL(helper_, CanAcceptClientHello(_, _, _, _, _))
+ .WillByDefault(testing::Return(true));
+}
+
+TestQuicSpdyServerSession::~TestQuicSpdyServerSession() {
+ delete connection();
+}
+
+QuicCryptoServerStreamBase*
+TestQuicSpdyServerSession::CreateQuicCryptoServerStream(
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache) {
+ return new QuicCryptoServerStream(
+ crypto_config, compressed_certs_cache,
+ GetQuicReloadableFlag(enable_quic_stateless_reject_support), this,
+ &helper_);
+}
+
+void TestQuicSpdyServerSession::OnCryptoHandshakeEvent(
+ CryptoHandshakeEvent event) {
+ QuicSession::OnCryptoHandshakeEvent(event);
+}
+
+QuicCryptoServerStream* TestQuicSpdyServerSession::GetMutableCryptoStream() {
+ return static_cast<QuicCryptoServerStream*>(
+ QuicServerSessionBase::GetMutableCryptoStream());
+}
+
+const QuicCryptoServerStream* TestQuicSpdyServerSession::GetCryptoStream()
+ const {
+ return static_cast<const QuicCryptoServerStream*>(
+ QuicServerSessionBase::GetCryptoStream());
+}
+
+TestQuicSpdyClientSession::TestQuicSpdyClientSession(
+ QuicConnection* connection,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicServerId& server_id,
+ QuicCryptoClientConfig* crypto_config)
+ : QuicSpdyClientSessionBase(connection,
+ &push_promise_index_,
+ config,
+ supported_versions) {
+ crypto_stream_ = QuicMakeUnique<QuicCryptoClientStream>(
+ server_id, this, crypto_test_utils::ProofVerifyContextForTesting(),
+ crypto_config, this);
+ Initialize();
+}
+
+TestQuicSpdyClientSession::~TestQuicSpdyClientSession() {}
+
+bool TestQuicSpdyClientSession::IsAuthorized(const QuicString& authority) {
+ return true;
+}
+
+void TestQuicSpdyClientSession::OnCryptoHandshakeEvent(
+ CryptoHandshakeEvent event) {
+ QuicSession::OnCryptoHandshakeEvent(event);
+}
+
+QuicCryptoClientStream* TestQuicSpdyClientSession::GetMutableCryptoStream() {
+ return crypto_stream_.get();
+}
+
+const QuicCryptoClientStream* TestQuicSpdyClientSession::GetCryptoStream()
+ const {
+ return crypto_stream_.get();
+}
+
+TestPushPromiseDelegate::TestPushPromiseDelegate(bool match)
+ : match_(match), rendezvous_fired_(false), rendezvous_stream_(nullptr) {}
+
+bool TestPushPromiseDelegate::CheckVary(
+ const spdy::SpdyHeaderBlock& client_request,
+ const spdy::SpdyHeaderBlock& promise_request,
+ const spdy::SpdyHeaderBlock& promise_response) {
+ QUIC_DVLOG(1) << "match " << match_;
+ return match_;
+}
+
+void TestPushPromiseDelegate::OnRendezvousResult(QuicSpdyStream* stream) {
+ rendezvous_fired_ = true;
+ rendezvous_stream_ = stream;
+}
+
+MockPacketWriter::MockPacketWriter() {
+ ON_CALL(*this, GetMaxPacketSize(_))
+ .WillByDefault(testing::Return(kMaxPacketSize));
+ ON_CALL(*this, IsBatchMode()).WillByDefault(testing::Return(false));
+ ON_CALL(*this, GetNextWriteLocation(_, _))
+ .WillByDefault(testing::Return(nullptr));
+ ON_CALL(*this, Flush())
+ .WillByDefault(testing::Return(WriteResult(WRITE_STATUS_OK, 0)));
+}
+
+MockPacketWriter::~MockPacketWriter() {}
+
+MockSendAlgorithm::MockSendAlgorithm() {}
+
+MockSendAlgorithm::~MockSendAlgorithm() {}
+
+MockLossAlgorithm::MockLossAlgorithm() {}
+
+MockLossAlgorithm::~MockLossAlgorithm() {}
+
+MockAckListener::MockAckListener() {}
+
+MockAckListener::~MockAckListener() {}
+
+MockNetworkChangeVisitor::MockNetworkChangeVisitor() {}
+
+MockNetworkChangeVisitor::~MockNetworkChangeVisitor() {}
+
+namespace {
+
+QuicString HexDumpWithMarks(const char* data,
+ int length,
+ const bool* marks,
+ int mark_length) {
+ static const char kHexChars[] = "0123456789abcdef";
+ static const int kColumns = 4;
+
+ const int kSizeLimit = 1024;
+ if (length > kSizeLimit || mark_length > kSizeLimit) {
+ QUIC_LOG(ERROR) << "Only dumping first " << kSizeLimit << " bytes.";
+ length = std::min(length, kSizeLimit);
+ mark_length = std::min(mark_length, kSizeLimit);
+ }
+
+ QuicString hex;
+ for (const char* row = data; length > 0;
+ row += kColumns, length -= kColumns) {
+ for (const char* p = row; p < row + 4; ++p) {
+ if (p < row + length) {
+ const bool mark =
+ (marks && (p - data) < mark_length && marks[p - data]);
+ hex += mark ? '*' : ' ';
+ hex += kHexChars[(*p & 0xf0) >> 4];
+ hex += kHexChars[*p & 0x0f];
+ hex += mark ? '*' : ' ';
+ } else {
+ hex += " ";
+ }
+ }
+ hex = hex + " ";
+
+ for (const char* p = row; p < row + 4 && p < row + length; ++p) {
+ hex += (*p >= 0x20 && *p <= 0x7f) ? (*p) : '.';
+ }
+
+ hex = hex + '\n';
+ }
+ return hex;
+}
+
+} // namespace
+
+QuicIpAddress TestPeerIPAddress() {
+ return QuicIpAddress::Loopback4();
+}
+
+ParsedQuicVersion QuicVersionMax() {
+ return AllSupportedVersions().front();
+}
+
+ParsedQuicVersion QuicVersionMin() {
+ return AllSupportedVersions().back();
+}
+
+QuicTransportVersion QuicTransportVersionMax() {
+ return AllSupportedTransportVersions().front();
+}
+
+QuicTransportVersion QuicTransportVersionMin() {
+ return AllSupportedTransportVersions().back();
+}
+
+QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId destination_connection_id,
+ QuicConnectionId source_connection_id,
+ bool version_flag,
+ bool reset_flag,
+ uint64_t packet_number,
+ const QuicString& data) {
+ return ConstructEncryptedPacket(
+ destination_connection_id, source_connection_id, version_flag, reset_flag,
+ packet_number, data, CONNECTION_ID_PRESENT, CONNECTION_ID_ABSENT,
+ PACKET_4BYTE_PACKET_NUMBER);
+}
+
+QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId destination_connection_id,
+ QuicConnectionId source_connection_id,
+ bool version_flag,
+ bool reset_flag,
+ uint64_t packet_number,
+ const QuicString& data,
+ QuicConnectionIdIncluded destination_connection_id_included,
+ QuicConnectionIdIncluded source_connection_id_included,
+ QuicPacketNumberLength packet_number_length) {
+ return ConstructEncryptedPacket(
+ destination_connection_id, source_connection_id, version_flag, reset_flag,
+ packet_number, data, destination_connection_id_included,
+ source_connection_id_included, packet_number_length, nullptr);
+}
+
+QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId destination_connection_id,
+ QuicConnectionId source_connection_id,
+ bool version_flag,
+ bool reset_flag,
+ uint64_t packet_number,
+ const QuicString& data,
+ QuicConnectionIdIncluded destination_connection_id_included,
+ QuicConnectionIdIncluded source_connection_id_included,
+ QuicPacketNumberLength packet_number_length,
+ ParsedQuicVersionVector* versions) {
+ return ConstructEncryptedPacket(
+ destination_connection_id, source_connection_id, version_flag, reset_flag,
+ packet_number, data, destination_connection_id_included,
+ source_connection_id_included, packet_number_length, versions,
+ Perspective::IS_CLIENT);
+}
+QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId destination_connection_id,
+ QuicConnectionId source_connection_id,
+ bool version_flag,
+ bool reset_flag,
+ uint64_t packet_number,
+ const QuicString& data,
+ QuicConnectionIdIncluded destination_connection_id_included,
+ QuicConnectionIdIncluded source_connection_id_included,
+ QuicPacketNumberLength packet_number_length,
+ ParsedQuicVersionVector* versions,
+ Perspective perspective) {
+ QuicPacketHeader header;
+ header.destination_connection_id = destination_connection_id;
+ header.destination_connection_id_included =
+ destination_connection_id_included;
+ header.source_connection_id = source_connection_id;
+ header.source_connection_id_included = source_connection_id_included;
+ header.version_flag = version_flag;
+ header.reset_flag = reset_flag;
+ header.packet_number_length = packet_number_length;
+ header.packet_number = QuicPacketNumber(packet_number);
+ ParsedQuicVersionVector supported_versions = CurrentSupportedVersions();
+ if (!versions) {
+ versions = &supported_versions;
+ }
+ if (QuicVersionHasLongHeaderLengths((*versions)[0].transport_version) &&
+ version_flag) {
+ header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1;
+ header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+ }
+
+ QuicFrames frames;
+ QuicFramer framer(*versions, QuicTime::Zero(), perspective,
+ kQuicDefaultConnectionIdLength);
+ if ((*versions)[0].transport_version < QUIC_VERSION_47) {
+ QuicFrame frame(QuicStreamFrame(
+ QuicUtils::GetCryptoStreamId((*versions)[0].transport_version), false,
+ 0, QuicStringPiece(data)));
+ frames.push_back(frame);
+ } else {
+ QuicFrame frame(new QuicCryptoFrame(ENCRYPTION_NONE, 0, data));
+ frames.push_back(frame);
+ }
+
+ std::unique_ptr<QuicPacket> packet(
+ BuildUnsizedDataPacket(&framer, header, frames));
+ EXPECT_TRUE(packet != nullptr);
+ char* buffer = new char[kMaxPacketSize];
+ size_t encrypted_length =
+ framer.EncryptPayload(ENCRYPTION_NONE, QuicPacketNumber(packet_number),
+ *packet, buffer, kMaxPacketSize);
+ EXPECT_NE(0u, encrypted_length);
+ DeleteFrames(&frames);
+ return new QuicEncryptedPacket(buffer, encrypted_length, true);
+}
+
+QuicReceivedPacket* ConstructReceivedPacket(
+ const QuicEncryptedPacket& encrypted_packet,
+ QuicTime receipt_time) {
+ char* buffer = new char[encrypted_packet.length()];
+ memcpy(buffer, encrypted_packet.data(), encrypted_packet.length());
+ return new QuicReceivedPacket(buffer, encrypted_packet.length(), receipt_time,
+ true);
+}
+
+QuicEncryptedPacket* ConstructMisFramedEncryptedPacket(
+ QuicConnectionId destination_connection_id,
+ QuicConnectionId source_connection_id,
+ bool version_flag,
+ bool reset_flag,
+ uint64_t packet_number,
+ const QuicString& data,
+ QuicConnectionIdIncluded destination_connection_id_included,
+ QuicConnectionIdIncluded source_connection_id_included,
+ QuicPacketNumberLength packet_number_length,
+ ParsedQuicVersionVector* versions,
+ Perspective perspective) {
+ QuicPacketHeader header;
+ header.destination_connection_id = destination_connection_id;
+ header.destination_connection_id_included =
+ destination_connection_id_included;
+ header.source_connection_id = source_connection_id;
+ header.source_connection_id_included = source_connection_id_included;
+ header.version_flag = version_flag;
+ header.reset_flag = reset_flag;
+ header.packet_number_length = packet_number_length;
+ header.packet_number = QuicPacketNumber(packet_number);
+ QuicFrame frame(QuicStreamFrame(1, false, 0, QuicStringPiece(data)));
+ QuicFrames frames;
+ frames.push_back(frame);
+ QuicFramer framer(versions != nullptr ? *versions : AllSupportedVersions(),
+ QuicTime::Zero(), perspective,
+ kQuicDefaultConnectionIdLength);
+
+ std::unique_ptr<QuicPacket> packet(
+ BuildUnsizedDataPacket(&framer, header, frames));
+ EXPECT_TRUE(packet != nullptr);
+
+ // Now set the frame type to 0x1F, which is an invalid frame type.
+ reinterpret_cast<unsigned char*>(
+ packet->mutable_data())[GetStartOfEncryptedData(
+ framer.transport_version(),
+ GetIncludedDestinationConnectionIdLength(header),
+ GetIncludedSourceConnectionIdLength(header), version_flag,
+ false /* no diversification nonce */, packet_number_length,
+ VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0)] =
+ 0x1F;
+
+ char* buffer = new char[kMaxPacketSize];
+ size_t encrypted_length =
+ framer.EncryptPayload(ENCRYPTION_NONE, QuicPacketNumber(packet_number),
+ *packet, buffer, kMaxPacketSize);
+ EXPECT_NE(0u, encrypted_length);
+ return new QuicEncryptedPacket(buffer, encrypted_length, true);
+}
+
+void CompareCharArraysWithHexError(const QuicString& description,
+ const char* actual,
+ const int actual_len,
+ const char* expected,
+ const int expected_len) {
+ EXPECT_EQ(actual_len, expected_len);
+ const int min_len = std::min(actual_len, expected_len);
+ const int max_len = std::max(actual_len, expected_len);
+ std::unique_ptr<bool[]> marks(new bool[max_len]);
+ bool identical = (actual_len == expected_len);
+ for (int i = 0; i < min_len; ++i) {
+ if (actual[i] != expected[i]) {
+ marks[i] = true;
+ identical = false;
+ } else {
+ marks[i] = false;
+ }
+ }
+ for (int i = min_len; i < max_len; ++i) {
+ marks[i] = true;
+ }
+ if (identical)
+ return;
+ ADD_FAILURE() << "Description:\n"
+ << description << "\n\nExpected:\n"
+ << HexDumpWithMarks(expected, expected_len, marks.get(),
+ max_len)
+ << "\nActual:\n"
+ << HexDumpWithMarks(actual, actual_len, marks.get(), max_len);
+}
+
+size_t GetPacketLengthForOneStream(
+ QuicTransportVersion version,
+ bool include_version,
+ bool include_diversification_nonce,
+ QuicConnectionIdLength destination_connection_id_length,
+ QuicConnectionIdLength source_connection_id_length,
+ QuicPacketNumberLength packet_number_length,
+ QuicVariableLengthIntegerLength retry_token_length_length,
+ QuicVariableLengthIntegerLength length_length,
+ size_t* payload_length) {
+ *payload_length = 1;
+ const size_t stream_length =
+ NullEncrypter(Perspective::IS_CLIENT).GetCiphertextSize(*payload_length) +
+ QuicPacketCreator::StreamFramePacketOverhead(
+ version, destination_connection_id_length,
+ source_connection_id_length, include_version,
+ include_diversification_nonce, packet_number_length,
+ retry_token_length_length, length_length, 0u);
+ const size_t ack_length =
+ NullEncrypter(Perspective::IS_CLIENT)
+ .GetCiphertextSize(QuicFramer::GetMinAckFrameSize(
+ version, PACKET_1BYTE_PACKET_NUMBER)) +
+ GetPacketHeaderSize(version, destination_connection_id_length,
+ source_connection_id_length, include_version,
+ include_diversification_nonce, packet_number_length,
+ retry_token_length_length, 0, length_length);
+ if (stream_length < ack_length) {
+ *payload_length = 1 + ack_length - stream_length;
+ }
+
+ return NullEncrypter(Perspective::IS_CLIENT)
+ .GetCiphertextSize(*payload_length) +
+ QuicPacketCreator::StreamFramePacketOverhead(
+ version, destination_connection_id_length,
+ source_connection_id_length, include_version,
+ include_diversification_nonce, packet_number_length,
+ retry_token_length_length, length_length, 0u);
+}
+
+QuicConfig DefaultQuicConfig() {
+ QuicConfig config;
+ config.SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ config.SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ QuicConfigPeer::SetReceivedMaxIncomingDynamicStreams(
+ &config, kDefaultMaxStreamsPerConnection);
+ // Default enable NSTP.
+ // This is unnecessary for versions > 44
+ if (!config.HasClientSentConnectionOption(quic::kNSTP,
+ quic::Perspective::IS_CLIENT)) {
+ quic::QuicTagVector connection_options;
+ connection_options.push_back(quic::kNSTP);
+ config.SetConnectionOptionsToSend(connection_options);
+ }
+ return config;
+}
+
+QuicConfig DefaultQuicConfigStatelessRejects() {
+ QuicConfig config = DefaultQuicConfig();
+ QuicTagVector copt;
+ copt.push_back(kSREJ);
+ config.SetConnectionOptionsToSend(copt);
+ return config;
+}
+
+QuicTransportVersionVector SupportedTransportVersions(
+ QuicTransportVersion version) {
+ QuicTransportVersionVector versions;
+ versions.push_back(version);
+ return versions;
+}
+
+ParsedQuicVersionVector SupportedVersions(ParsedQuicVersion version) {
+ ParsedQuicVersionVector versions;
+ versions.push_back(version);
+ return versions;
+}
+
+MockQuicConnectionDebugVisitor::MockQuicConnectionDebugVisitor() {}
+
+MockQuicConnectionDebugVisitor::~MockQuicConnectionDebugVisitor() {}
+
+MockReceivedPacketManager::MockReceivedPacketManager(QuicConnectionStats* stats)
+ : QuicReceivedPacketManager(stats) {}
+
+MockReceivedPacketManager::~MockReceivedPacketManager() {}
+
+MockConnectionCloseDelegate::MockConnectionCloseDelegate() {}
+
+MockConnectionCloseDelegate::~MockConnectionCloseDelegate() {}
+
+MockPacketCreatorDelegate::MockPacketCreatorDelegate() {}
+MockPacketCreatorDelegate::~MockPacketCreatorDelegate() {}
+
+MockSessionNotifier::MockSessionNotifier() {}
+MockSessionNotifier::~MockSessionNotifier() {}
+
+void CreateClientSessionForTest(
+ QuicServerId server_id,
+ bool supports_stateless_rejects,
+ QuicTime::Delta connection_start_time,
+ const ParsedQuicVersionVector& supported_versions,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ QuicCryptoClientConfig* crypto_client_config,
+ PacketSavingConnection** client_connection,
+ TestQuicSpdyClientSession** client_session) {
+ CHECK(crypto_client_config);
+ CHECK(client_connection);
+ CHECK(client_session);
+ CHECK(!connection_start_time.IsZero())
+ << "Connections must start at non-zero times, otherwise the "
+ << "strike-register will be unhappy.";
+
+ QuicConfig config = supports_stateless_rejects
+ ? DefaultQuicConfigStatelessRejects()
+ : DefaultQuicConfig();
+ *client_connection = new PacketSavingConnection(
+ helper, alarm_factory, Perspective::IS_CLIENT, supported_versions);
+ *client_session = new TestQuicSpdyClientSession(*client_connection, config,
+ supported_versions, server_id,
+ crypto_client_config);
+ (*client_connection)->AdvanceTime(connection_start_time);
+}
+
+void CreateServerSessionForTest(
+ QuicServerId server_id,
+ QuicTime::Delta connection_start_time,
+ ParsedQuicVersionVector supported_versions,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ QuicCryptoServerConfig* server_crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ PacketSavingConnection** server_connection,
+ TestQuicSpdyServerSession** server_session) {
+ CHECK(server_crypto_config);
+ CHECK(server_connection);
+ CHECK(server_session);
+ CHECK(!connection_start_time.IsZero())
+ << "Connections must start at non-zero times, otherwise the "
+ << "strike-register will be unhappy.";
+
+ *server_connection =
+ new PacketSavingConnection(helper, alarm_factory, Perspective::IS_SERVER,
+ ParsedVersionOfIndex(supported_versions, 0));
+ *server_session = new TestQuicSpdyServerSession(
+ *server_connection, DefaultQuicConfig(), supported_versions,
+ server_crypto_config, compressed_certs_cache);
+
+ // We advance the clock initially because the default time is zero and the
+ // strike register worries that we've just overflowed a uint32_t time.
+ (*server_connection)->AdvanceTime(connection_start_time);
+}
+
+QuicStreamId GetNthClientInitiatedBidirectionalStreamId(
+ QuicTransportVersion version,
+ int n) {
+ return QuicUtils::GetFirstBidirectionalStreamId(version,
+ Perspective::IS_CLIENT) +
+ // + 1 because spdy_session contains headers stream.
+ QuicUtils::StreamIdDelta(version) * (n + 1);
+}
+
+QuicStreamId GetNthServerInitiatedBidirectionalStreamId(
+ QuicTransportVersion version,
+ int n) {
+ return QuicUtils::GetFirstBidirectionalStreamId(version,
+ Perspective::IS_SERVER) +
+ QuicUtils::StreamIdDelta(version) * n;
+}
+
+QuicStreamId GetNthServerInitiatedUnidirectionalStreamId(
+ QuicTransportVersion version,
+ int n) {
+ return QuicUtils::GetFirstUnidirectionalStreamId(version,
+ Perspective::IS_SERVER) +
+ QuicUtils::StreamIdDelta(version) * n;
+}
+
+StreamType DetermineStreamType(QuicStreamId id,
+ QuicTransportVersion version,
+ Perspective perspective,
+ bool is_incoming,
+ StreamType default_type) {
+ return version == QUIC_VERSION_99
+ ? QuicUtils::GetStreamType(id, perspective, is_incoming)
+ : default_type;
+}
+
+QuicMemSliceSpan MakeSpan(QuicBufferAllocator* allocator,
+ QuicStringPiece message_data,
+ QuicMemSliceStorage* storage) {
+ if (message_data.length() == 0) {
+ *storage = QuicMemSliceStorage(nullptr, 0, allocator, kMaxPacketSize);
+ return storage->ToSpan();
+ }
+ struct iovec iov = {const_cast<char*>(message_data.data()),
+ message_data.length()};
+ *storage = QuicMemSliceStorage(&iov, 1, allocator, kMaxPacketSize);
+ return storage->ToSpan();
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h
new file mode 100644
index 0000000..2cca768
--- /dev/null
+++ b/quic/test_tools/quic_test_utils.h
@@ -0,0 +1,1201 @@
+// Copyright (c) 2012 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.
+
+// Common utilities for Quic tests
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "net/third_party/quiche/src/quic/core/congestion_control/loss_detection_interface.h"
+#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_server_session_base.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_close_delegate_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h"
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_random.h"
+
+namespace quic {
+
+namespace test {
+
+// A generic predictable connection ID suited for testing.
+QuicConnectionId TestConnectionId();
+
+// A generic predictable connection ID suited for testing, generated from a
+// given number, such as an index.
+QuicConnectionId TestConnectionId(uint64_t connection_number);
+
+// Extracts the connection number passed to TestConnectionId().
+uint64_t TestConnectionIdToUInt64(QuicConnectionId connection_id);
+
+static const uint16_t kTestPort = 12345;
+static const uint32_t kInitialStreamFlowControlWindowForTest =
+ 1024 * 1024; // 1 MB
+static const uint32_t kInitialSessionFlowControlWindowForTest =
+ 1536 * 1024; // 1.5 MB
+
+// Returns the test peer IP address.
+QuicIpAddress TestPeerIPAddress();
+
+// Upper limit on versions we support.
+ParsedQuicVersion QuicVersionMax();
+
+// Lower limit on versions we support.
+ParsedQuicVersion QuicVersionMin();
+
+// Upper limit on versions we support.
+// TODO(nharper): Remove this function when it is no longer used.
+QuicTransportVersion QuicTransportVersionMax();
+
+// Lower limit on versions we support.
+// TODO(nharper): Remove this function when it is no longer used.
+QuicTransportVersion QuicTransportVersionMin();
+
+// Create an encrypted packet for testing.
+// If versions == nullptr, uses &AllSupportedVersions().
+// Note that the packet is encrypted with NullEncrypter, so to decrypt the
+// constructed packet, the framer must be set to use NullDecrypter.
+QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId destination_connection_id,
+ QuicConnectionId source_connection_id,
+ bool version_flag,
+ bool reset_flag,
+ uint64_t packet_number,
+ const QuicString& data,
+ QuicConnectionIdIncluded destination_connection_id_included,
+ QuicConnectionIdIncluded source_connection_id_included,
+ QuicPacketNumberLength packet_number_length,
+ ParsedQuicVersionVector* versions,
+ Perspective perspective);
+
+// Create an encrypted packet for testing.
+// If versions == nullptr, uses &AllSupportedVersions().
+// Note that the packet is encrypted with NullEncrypter, so to decrypt the
+// constructed packet, the framer must be set to use NullDecrypter.
+QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId destination_connection_id,
+ QuicConnectionId source_connection_id,
+ bool version_flag,
+ bool reset_flag,
+ uint64_t packet_number,
+ const QuicString& data,
+ QuicConnectionIdIncluded destination_connection_id_included,
+ QuicConnectionIdIncluded source_connection_id_included,
+ QuicPacketNumberLength packet_number_length,
+ ParsedQuicVersionVector* versions);
+
+// This form assumes |versions| == nullptr.
+QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId destination_connection_id,
+ QuicConnectionId source_connection_id,
+ bool version_flag,
+ bool reset_flag,
+ uint64_t packet_number,
+ const QuicString& data,
+ QuicConnectionIdIncluded destination_connection_id_included,
+ QuicConnectionIdIncluded source_connection_id_included,
+ QuicPacketNumberLength packet_number_length);
+
+// This form assumes |connection_id_length| == PACKET_8BYTE_CONNECTION_ID,
+// |packet_number_length| == PACKET_4BYTE_PACKET_NUMBER and
+// |versions| == nullptr.
+QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId destination_connection_id,
+ QuicConnectionId source_connection_id,
+ bool version_flag,
+ bool reset_flag,
+ uint64_t packet_number,
+ const QuicString& data);
+
+// Constructs a received packet for testing. The caller must take ownership of
+// the returned pointer.
+QuicReceivedPacket* ConstructReceivedPacket(
+ const QuicEncryptedPacket& encrypted_packet,
+ QuicTime receipt_time);
+
+// Create an encrypted packet for testing whose data portion erroneous.
+// The specific way the data portion is erroneous is not specified, but
+// it is an error that QuicFramer detects.
+// Note that the packet is encrypted with NullEncrypter, so to decrypt the
+// constructed packet, the framer must be set to use NullDecrypter.
+QuicEncryptedPacket* ConstructMisFramedEncryptedPacket(
+ QuicConnectionId destination_connection_id,
+ QuicConnectionId source_connection_id,
+ bool version_flag,
+ bool reset_flag,
+ uint64_t packet_number,
+ const QuicString& data,
+ QuicConnectionIdIncluded destination_connection_id_included,
+ QuicConnectionIdIncluded source_connection_id_included,
+ QuicPacketNumberLength packet_number_length,
+ ParsedQuicVersionVector* versions,
+ Perspective perspective);
+
+void CompareCharArraysWithHexError(const QuicString& description,
+ const char* actual,
+ const int actual_len,
+ const char* expected,
+ const int expected_len);
+
+// Returns the length of a QuicPacket that is capable of holding either a
+// stream frame or a minimal ack frame. Sets |*payload_length| to the number
+// of bytes of stream data that will fit in such a packet.
+size_t GetPacketLengthForOneStream(
+ QuicTransportVersion version,
+ bool include_version,
+ bool include_diversification_nonce,
+ QuicConnectionIdLength destination_connection_id_length,
+ QuicConnectionIdLength source_connection_id_length,
+ QuicPacketNumberLength packet_number_length,
+ QuicVariableLengthIntegerLength retry_token_length_length,
+ QuicVariableLengthIntegerLength length_length,
+ size_t* payload_length);
+
+// Returns QuicConfig set to default values.
+QuicConfig DefaultQuicConfig();
+
+// Returns a QuicConfig set to default values that supports stateless rejects.
+QuicConfig DefaultQuicConfigStatelessRejects();
+
+// Returns a version vector consisting of |version|.
+QuicTransportVersionVector SupportedTransportVersions(
+ QuicTransportVersion version);
+
+ParsedQuicVersionVector SupportedVersions(ParsedQuicVersion version);
+
+struct QuicAckBlock {
+ QuicPacketNumber start; // Included
+ QuicPacketNumber limit; // Excluded
+};
+
+// Testing convenience method to construct a QuicAckFrame with arbitrary ack
+// blocks. Each block is given by a (closed-open) range of packet numbers. e.g.:
+// InitAckFrame({{1, 10}})
+// => 1 ack block acking packet numbers 1 to 9.
+//
+// InitAckFrame({{1, 2}, {3, 4}})
+// => 2 ack blocks acking packet 1 and 3. Packet 2 is missing.
+QuicAckFrame InitAckFrame(const std::vector<QuicAckBlock>& ack_blocks);
+
+// Testing convenience method to construct a QuicAckFrame with 1 ack block which
+// covers packet number range [1, |largest_acked| + 1).
+// Equivalent to InitAckFrame({{1, largest_acked + 1}})
+QuicAckFrame InitAckFrame(uint64_t largest_acked);
+QuicAckFrame InitAckFrame(QuicPacketNumber largest_acked);
+
+// Testing convenience method to construct a QuicAckFrame with |num_ack_blocks|
+// ack blocks of width 1 packet, starting from |least_unacked| + 2.
+QuicAckFrame MakeAckFrameWithAckBlocks(size_t num_ack_blocks,
+ uint64_t least_unacked);
+
+// Returns a QuicPacket that is owned by the caller, and
+// is populated with the fields in |header| and |frames|, or is nullptr if the
+// packet could not be created.
+std::unique_ptr<QuicPacket> BuildUnsizedDataPacket(
+ QuicFramer* framer,
+ const QuicPacketHeader& header,
+ const QuicFrames& frames);
+// Returns a QuicPacket that is owned by the caller, and of size |packet_size|.
+std::unique_ptr<QuicPacket> BuildUnsizedDataPacket(
+ QuicFramer* framer,
+ const QuicPacketHeader& header,
+ const QuicFrames& frames,
+ size_t packet_size);
+
+// Compute SHA-1 hash of the supplied QuicString.
+QuicString Sha1Hash(QuicStringPiece data);
+
+// Simple random number generator used to compute random numbers suitable
+// for pseudo-randomly dropping packets in tests. It works by computing
+// the sha1 hash of the current seed, and using the first 64 bits as
+// the next random number, and the next seed.
+class SimpleRandom : public QuicRandom {
+ public:
+ SimpleRandom() : seed_(0) {}
+ SimpleRandom(const SimpleRandom&) = delete;
+ SimpleRandom& operator=(const SimpleRandom&) = delete;
+ ~SimpleRandom() override {}
+
+ // Returns a random number in the range [0, kuint64max].
+ uint64_t RandUint64() override;
+
+ void RandBytes(void* data, size_t len) override;
+
+ void set_seed(uint64_t seed) { seed_ = seed; }
+
+ private:
+ uint64_t seed_;
+};
+
+class MockFramerVisitor : public QuicFramerVisitorInterface {
+ public:
+ MockFramerVisitor();
+ MockFramerVisitor(const MockFramerVisitor&) = delete;
+ MockFramerVisitor& operator=(const MockFramerVisitor&) = delete;
+ ~MockFramerVisitor() override;
+
+ MOCK_METHOD1(OnError, void(QuicFramer* framer));
+ // The constructor sets this up to return false by default.
+ MOCK_METHOD2(OnProtocolVersionMismatch,
+ bool(ParsedQuicVersion version, PacketHeaderFormat form));
+ MOCK_METHOD0(OnPacket, void());
+ MOCK_METHOD1(OnPublicResetPacket, void(const QuicPublicResetPacket& header));
+ MOCK_METHOD1(OnVersionNegotiationPacket,
+ void(const QuicVersionNegotiationPacket& packet));
+ // The constructor sets this up to return true by default.
+ MOCK_METHOD1(OnUnauthenticatedHeader, bool(const QuicPacketHeader& header));
+ // The constructor sets this up to return true by default.
+ MOCK_METHOD1(OnUnauthenticatedPublicHeader,
+ bool(const QuicPacketHeader& header));
+ MOCK_METHOD1(OnDecryptedPacket, void(EncryptionLevel level));
+ MOCK_METHOD1(OnPacketHeader, bool(const QuicPacketHeader& header));
+ MOCK_METHOD1(OnCoalescedPacket, void(const QuicEncryptedPacket& packet));
+ MOCK_METHOD1(OnStreamFrame, bool(const QuicStreamFrame& frame));
+ MOCK_METHOD1(OnCryptoFrame, bool(const QuicCryptoFrame& frame));
+ MOCK_METHOD2(OnAckFrameStart, bool(QuicPacketNumber, QuicTime::Delta));
+ MOCK_METHOD2(OnAckRange, bool(QuicPacketNumber, QuicPacketNumber));
+ MOCK_METHOD2(OnAckTimestamp, bool(QuicPacketNumber, QuicTime));
+ MOCK_METHOD1(OnAckFrameEnd, bool(QuicPacketNumber));
+ MOCK_METHOD1(OnStopWaitingFrame, bool(const QuicStopWaitingFrame& frame));
+ MOCK_METHOD1(OnPaddingFrame, bool(const QuicPaddingFrame& frame));
+ MOCK_METHOD1(OnPingFrame, bool(const QuicPingFrame& frame));
+ MOCK_METHOD1(OnRstStreamFrame, bool(const QuicRstStreamFrame& frame));
+ MOCK_METHOD1(OnConnectionCloseFrame,
+ bool(const QuicConnectionCloseFrame& frame));
+ MOCK_METHOD1(OnApplicationCloseFrame,
+ bool(const QuicApplicationCloseFrame& frame));
+ MOCK_METHOD1(OnNewConnectionIdFrame,
+ bool(const QuicNewConnectionIdFrame& frame));
+ MOCK_METHOD1(OnRetireConnectionIdFrame,
+ bool(const QuicRetireConnectionIdFrame& frame));
+ MOCK_METHOD1(OnNewTokenFrame, bool(const QuicNewTokenFrame& frame));
+ MOCK_METHOD1(OnStopSendingFrame, bool(const QuicStopSendingFrame& frame));
+ MOCK_METHOD1(OnPathChallengeFrame, bool(const QuicPathChallengeFrame& frame));
+ MOCK_METHOD1(OnPathResponseFrame, bool(const QuicPathResponseFrame& frame));
+ MOCK_METHOD1(OnGoAwayFrame, bool(const QuicGoAwayFrame& frame));
+ MOCK_METHOD1(OnMaxStreamIdFrame, bool(const QuicMaxStreamIdFrame& frame));
+ MOCK_METHOD1(OnStreamIdBlockedFrame,
+ bool(const QuicStreamIdBlockedFrame& frame));
+ MOCK_METHOD1(OnWindowUpdateFrame, bool(const QuicWindowUpdateFrame& frame));
+ MOCK_METHOD1(OnBlockedFrame, bool(const QuicBlockedFrame& frame));
+ MOCK_METHOD1(OnMessageFrame, bool(const QuicMessageFrame& frame));
+ MOCK_METHOD0(OnPacketComplete, void());
+ MOCK_CONST_METHOD1(IsValidStatelessResetToken, bool(QuicUint128));
+ MOCK_METHOD1(OnAuthenticatedIetfStatelessResetPacket,
+ void(const QuicIetfStatelessResetPacket&));
+};
+
+class NoOpFramerVisitor : public QuicFramerVisitorInterface {
+ public:
+ NoOpFramerVisitor() {}
+ NoOpFramerVisitor(const NoOpFramerVisitor&) = delete;
+ NoOpFramerVisitor& operator=(const NoOpFramerVisitor&) = delete;
+
+ void OnError(QuicFramer* framer) override {}
+ void OnPacket() override {}
+ void OnPublicResetPacket(const QuicPublicResetPacket& packet) override {}
+ void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& packet) override {}
+ bool OnProtocolVersionMismatch(ParsedQuicVersion version,
+ PacketHeaderFormat form) override;
+ bool OnUnauthenticatedHeader(const QuicPacketHeader& header) override;
+ bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) override;
+ void OnDecryptedPacket(EncryptionLevel level) override {}
+ bool OnPacketHeader(const QuicPacketHeader& header) override;
+ void OnCoalescedPacket(const QuicEncryptedPacket& packet) override;
+ bool OnStreamFrame(const QuicStreamFrame& frame) override;
+ bool OnCryptoFrame(const QuicCryptoFrame& frame) override;
+ bool OnAckFrameStart(QuicPacketNumber largest_acked,
+ QuicTime::Delta ack_delay_time) override;
+ bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override;
+ bool OnAckTimestamp(QuicPacketNumber packet_number,
+ QuicTime timestamp) override;
+ bool OnAckFrameEnd(QuicPacketNumber start) override;
+ bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override;
+ bool OnPaddingFrame(const QuicPaddingFrame& frame) override;
+ bool OnPingFrame(const QuicPingFrame& frame) override;
+ bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override;
+ bool OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override;
+ bool OnApplicationCloseFrame(const QuicApplicationCloseFrame& frame) override;
+ bool OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame) override;
+ bool OnRetireConnectionIdFrame(
+ const QuicRetireConnectionIdFrame& frame) override;
+ bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override;
+ bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override;
+ bool OnPathChallengeFrame(const QuicPathChallengeFrame& frame) override;
+ bool OnPathResponseFrame(const QuicPathResponseFrame& frame) override;
+ bool OnGoAwayFrame(const QuicGoAwayFrame& frame) override;
+ bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) override;
+ bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame) override;
+ bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override;
+ bool OnBlockedFrame(const QuicBlockedFrame& frame) override;
+ bool OnMessageFrame(const QuicMessageFrame& frame) override;
+ void OnPacketComplete() override {}
+ bool IsValidStatelessResetToken(QuicUint128 token) const override;
+ void OnAuthenticatedIetfStatelessResetPacket(
+ const QuicIetfStatelessResetPacket& packet) override {}
+};
+
+class MockQuicConnectionVisitor : public QuicConnectionVisitorInterface {
+ public:
+ MockQuicConnectionVisitor();
+ MockQuicConnectionVisitor(const MockQuicConnectionVisitor&) = delete;
+ MockQuicConnectionVisitor& operator=(const MockQuicConnectionVisitor&) =
+ delete;
+ ~MockQuicConnectionVisitor() override;
+
+ MOCK_METHOD1(OnStreamFrame, void(const QuicStreamFrame& frame));
+ MOCK_METHOD1(OnCryptoFrame, void(const QuicCryptoFrame& frame));
+ MOCK_METHOD1(OnWindowUpdateFrame, void(const QuicWindowUpdateFrame& frame));
+ MOCK_METHOD1(OnBlockedFrame, void(const QuicBlockedFrame& frame));
+ MOCK_METHOD1(OnRstStream, void(const QuicRstStreamFrame& frame));
+ MOCK_METHOD1(OnGoAway, void(const QuicGoAwayFrame& frame));
+ MOCK_METHOD1(OnMessageReceived, void(QuicStringPiece message));
+ MOCK_METHOD3(OnConnectionClosed,
+ void(QuicErrorCode error,
+ const QuicString& error_details,
+ ConnectionCloseSource source));
+ MOCK_METHOD0(OnWriteBlocked, void());
+ MOCK_METHOD0(OnCanWrite, void());
+ MOCK_METHOD1(OnCongestionWindowChange, void(QuicTime now));
+ MOCK_METHOD1(OnConnectionMigration, void(AddressChangeType type));
+ MOCK_METHOD0(OnPathDegrading, void());
+ MOCK_CONST_METHOD0(WillingAndAbleToWrite, bool());
+ MOCK_CONST_METHOD0(HasPendingHandshake, bool());
+ MOCK_CONST_METHOD0(ShouldKeepConnectionAlive, bool());
+ MOCK_METHOD1(OnSuccessfulVersionNegotiation,
+ void(const ParsedQuicVersion& version));
+ MOCK_METHOD2(OnConnectivityProbeReceived,
+ void(const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address));
+ MOCK_METHOD0(OnConfigNegotiated, void());
+ MOCK_METHOD0(OnAckNeedsRetransmittableFrame, void());
+ MOCK_METHOD0(SendPing, void());
+ MOCK_CONST_METHOD0(AllowSelfAddressChange, bool());
+ MOCK_METHOD0(OnForwardProgressConfirmed, void());
+ MOCK_METHOD1(OnMaxStreamIdFrame, bool(const QuicMaxStreamIdFrame& frame));
+ MOCK_METHOD1(OnStreamIdBlockedFrame,
+ bool(const QuicStreamIdBlockedFrame& frame));
+ MOCK_METHOD1(OnStopSendingFrame, bool(const QuicStopSendingFrame& frame));
+};
+
+class MockQuicConnectionHelper : public QuicConnectionHelperInterface {
+ public:
+ MockQuicConnectionHelper();
+ MockQuicConnectionHelper(const MockQuicConnectionHelper&) = delete;
+ MockQuicConnectionHelper& operator=(const MockQuicConnectionHelper&) = delete;
+ ~MockQuicConnectionHelper() override;
+ const QuicClock* GetClock() const override;
+ QuicRandom* GetRandomGenerator() override;
+ QuicBufferAllocator* GetStreamSendBufferAllocator() override;
+ void AdvanceTime(QuicTime::Delta delta);
+
+ private:
+ MockClock clock_;
+ MockRandom random_generator_;
+ SimpleBufferAllocator buffer_allocator_;
+};
+
+class MockAlarmFactory : public QuicAlarmFactory {
+ public:
+ QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) override;
+ QuicArenaScopedPtr<QuicAlarm> CreateAlarm(
+ QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
+ QuicConnectionArena* arena) override;
+
+ // No-op alarm implementation
+ class TestAlarm : public QuicAlarm {
+ public:
+ explicit TestAlarm(QuicArenaScopedPtr<QuicAlarm::Delegate> delegate)
+ : QuicAlarm(std::move(delegate)) {}
+
+ void SetImpl() override {}
+ void CancelImpl() override {}
+
+ using QuicAlarm::Fire;
+ };
+
+ void FireAlarm(QuicAlarm* alarm) {
+ reinterpret_cast<TestAlarm*>(alarm)->Fire();
+ }
+};
+
+class MockQuicConnection : public QuicConnection {
+ public:
+ // Uses a ConnectionId of 42 and 127.0.0.1:123.
+ MockQuicConnection(MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective);
+
+ // Uses a ConnectionId of 42.
+ MockQuicConnection(QuicSocketAddress address,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective);
+
+ // Uses 127.0.0.1:123.
+ MockQuicConnection(QuicConnectionId connection_id,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective);
+
+ // Uses a ConnectionId of 42, and 127.0.0.1:123.
+ MockQuicConnection(MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective,
+ const ParsedQuicVersionVector& supported_versions);
+
+ MockQuicConnection(QuicConnectionId connection_id,
+ QuicSocketAddress address,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective,
+ const ParsedQuicVersionVector& supported_versions);
+ MockQuicConnection(const MockQuicConnection&) = delete;
+ MockQuicConnection& operator=(const MockQuicConnection&) = delete;
+
+ ~MockQuicConnection() override;
+
+ // If the constructor that uses a MockQuicConnectionHelper has been used then
+ // this method
+ // will advance the time of the MockClock.
+ void AdvanceTime(QuicTime::Delta delta);
+
+ MOCK_METHOD3(ProcessUdpPacket,
+ void(const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ const QuicReceivedPacket& packet));
+ MOCK_METHOD1(SendConnectionClose, void(QuicErrorCode error));
+ MOCK_METHOD3(CloseConnection,
+ void(QuicErrorCode error,
+ const QuicString& details,
+ ConnectionCloseBehavior connection_close_behavior));
+ MOCK_METHOD3(SendConnectionClosePacket,
+ void(QuicErrorCode error,
+ const QuicString& details,
+ AckBundling ack_mode));
+ MOCK_METHOD3(SendRstStream,
+ void(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written));
+ MOCK_METHOD3(SendGoAway,
+ void(QuicErrorCode error,
+ QuicStreamId last_good_stream_id,
+ const QuicString& reason));
+ MOCK_METHOD1(SendBlocked, void(QuicStreamId id));
+ MOCK_METHOD2(SendWindowUpdate,
+ void(QuicStreamId id, QuicStreamOffset byte_offset));
+ MOCK_METHOD0(OnCanWrite, void());
+ MOCK_METHOD1(SendConnectivityProbingResponsePacket,
+ void(const QuicSocketAddress& peer_address));
+ MOCK_METHOD2(SendConnectivityProbingPacket,
+ bool(QuicPacketWriter* probing_writer,
+ const QuicSocketAddress& peer_address));
+
+ MOCK_METHOD1(OnSendConnectionState, void(const CachedNetworkParameters&));
+ MOCK_METHOD2(ResumeConnectionState,
+ void(const CachedNetworkParameters&, bool));
+ MOCK_METHOD1(SetMaxPacingRate, void(QuicBandwidth));
+
+ MOCK_METHOD2(OnStreamReset, void(QuicStreamId, QuicRstStreamErrorCode));
+ MOCK_METHOD1(SendControlFrame, bool(const QuicFrame& frame));
+ MOCK_METHOD2(SendMessage, MessageStatus(QuicMessageId, QuicMemSliceSpan));
+ MOCK_METHOD3(OnConnectionClosed,
+ void(QuicErrorCode error,
+ const QuicString& error_details,
+ ConnectionCloseSource source));
+
+ MOCK_METHOD1(OnError, void(QuicFramer* framer));
+ void QuicConnection_OnError(QuicFramer* framer) {
+ QuicConnection::OnError(framer);
+ }
+
+ void ReallyCloseConnection(
+ QuicErrorCode error,
+ const QuicString& details,
+ ConnectionCloseBehavior connection_close_behavior) {
+ QuicConnection::CloseConnection(error, details, connection_close_behavior);
+ }
+
+ void ReallyProcessUdpPacket(const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ const QuicReceivedPacket& packet) {
+ QuicConnection::ProcessUdpPacket(self_address, peer_address, packet);
+ }
+
+ bool OnProtocolVersionMismatch(ParsedQuicVersion version,
+ PacketHeaderFormat form) override;
+
+ bool ReallySendControlFrame(const QuicFrame& frame) {
+ return QuicConnection::SendControlFrame(frame);
+ }
+
+ bool ReallySendConnectivityProbingPacket(
+ QuicPacketWriter* probing_writer,
+ const QuicSocketAddress& peer_address) {
+ return QuicConnection::SendConnectivityProbingPacket(probing_writer,
+ peer_address);
+ }
+
+ void ReallySendConnectivityProbingResponsePacket(
+ const QuicSocketAddress& peer_address) {
+ QuicConnection::SendConnectivityProbingResponsePacket(peer_address);
+ }
+ MOCK_METHOD1(OnPathResponseFrame, bool(const QuicPathResponseFrame&));
+ MOCK_METHOD1(OnStopSendingFrame, bool(const QuicStopSendingFrame& frame));
+ MOCK_METHOD3(SendCryptoData,
+ size_t(EncryptionLevel, size_t, QuicStreamOffset));
+ size_t QuicConnection_SendCryptoData(EncryptionLevel level,
+ size_t write_length,
+ QuicStreamOffset offset) {
+ return QuicConnection::SendCryptoData(level, write_length, offset);
+ }
+};
+
+class PacketSavingConnection : public MockQuicConnection {
+ public:
+ PacketSavingConnection(MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective);
+
+ PacketSavingConnection(MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective,
+ const ParsedQuicVersionVector& supported_versions);
+ PacketSavingConnection(const PacketSavingConnection&) = delete;
+ PacketSavingConnection& operator=(const PacketSavingConnection&) = delete;
+
+ ~PacketSavingConnection() override;
+
+ void SendOrQueuePacket(SerializedPacket* packet) override;
+
+ std::vector<std::unique_ptr<QuicEncryptedPacket>> encrypted_packets_;
+ MockClock clock_;
+};
+
+class MockQuicSession : public QuicSession {
+ public:
+ // Takes ownership of |connection|.
+ MockQuicSession(QuicConnection* connection, bool create_mock_crypto_stream);
+
+ // Takes ownership of |connection|.
+ explicit MockQuicSession(QuicConnection* connection);
+ MockQuicSession(const MockQuicSession&) = delete;
+ MockQuicSession& operator=(const MockQuicSession&) = delete;
+ ~MockQuicSession() override;
+
+ QuicCryptoStream* GetMutableCryptoStream() override;
+ const QuicCryptoStream* GetCryptoStream() const override;
+ void SetCryptoStream(QuicCryptoStream* crypto_stream);
+
+ MOCK_METHOD3(OnConnectionClosed,
+ void(QuicErrorCode error,
+ const QuicString& error_details,
+ ConnectionCloseSource source));
+ MOCK_METHOD1(CreateIncomingStream, QuicStream*(QuicStreamId id));
+ MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(PendingStream stream));
+ MOCK_METHOD1(ShouldCreateIncomingStream2, bool(QuicStreamId id));
+ MOCK_METHOD0(ShouldCreateOutgoingBidirectionalStream, bool());
+ MOCK_METHOD0(ShouldCreateOutgoingUnidirectionalStream, bool());
+ MOCK_METHOD5(WritevData,
+ QuicConsumedData(QuicStream* stream,
+ QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state));
+
+ MOCK_METHOD3(SendRstStream,
+ void(QuicStreamId stream_id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written));
+
+ MOCK_METHOD2(OnStreamHeaders,
+ void(QuicStreamId stream_id, QuicStringPiece headers_data));
+ MOCK_METHOD2(OnStreamHeadersPriority,
+ void(QuicStreamId stream_id, spdy::SpdyPriority priority));
+ MOCK_METHOD3(OnStreamHeadersComplete,
+ void(QuicStreamId stream_id, bool fin, size_t frame_len));
+ MOCK_CONST_METHOD0(IsCryptoHandshakeConfirmed, bool());
+ MOCK_CONST_METHOD0(ShouldKeepConnectionAlive, bool());
+ MOCK_METHOD2(SendStopSending, void(uint16_t code, QuicStreamId stream_id));
+
+ using QuicSession::ActivateStream;
+
+ // Returns a QuicConsumedData that indicates all of |write_length| (and |fin|
+ // if set) has been consumed.
+ static QuicConsumedData ConsumeData(QuicStream* stream,
+ QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state);
+
+ private:
+ std::unique_ptr<QuicCryptoStream> crypto_stream_;
+};
+
+class MockQuicCryptoStream : public QuicCryptoStream {
+ public:
+ explicit MockQuicCryptoStream(QuicSession* session);
+
+ ~MockQuicCryptoStream() override;
+
+ bool encryption_established() const override;
+ bool handshake_confirmed() const override;
+ const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
+ const override;
+ CryptoMessageParser* crypto_message_parser() override;
+
+ private:
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_;
+ CryptoFramer crypto_framer_;
+};
+
+class MockQuicSpdySession : public QuicSpdySession {
+ public:
+ // Takes ownership of |connection|.
+ explicit MockQuicSpdySession(QuicConnection* connection);
+ // Takes ownership of |connection|.
+ MockQuicSpdySession(QuicConnection* connection,
+ bool create_mock_crypto_stream);
+ MockQuicSpdySession(const MockQuicSpdySession&) = delete;
+ MockQuicSpdySession& operator=(const MockQuicSpdySession&) = delete;
+ ~MockQuicSpdySession() override;
+
+ QuicCryptoStream* GetMutableCryptoStream() override;
+ const QuicCryptoStream* GetCryptoStream() const override;
+ void SetCryptoStream(QuicCryptoStream* crypto_stream);
+
+ // From QuicSession.
+ MOCK_METHOD3(OnConnectionClosed,
+ void(QuicErrorCode error,
+ const QuicString& error_details,
+ ConnectionCloseSource source));
+ MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(QuicStreamId id));
+ MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(PendingStream stream));
+ MOCK_METHOD0(CreateOutgoingBidirectionalStream, QuicSpdyStream*());
+ MOCK_METHOD0(CreateOutgoingUnidirectionalStream, QuicSpdyStream*());
+ MOCK_METHOD1(ShouldCreateIncomingStream, bool(QuicStreamId id));
+ MOCK_METHOD0(ShouldCreateOutgoingBidirectionalStream, bool());
+ MOCK_METHOD0(ShouldCreateOutgoingUnidirectionalStream, bool());
+ MOCK_METHOD5(WritevData,
+ QuicConsumedData(QuicStream* stream,
+ QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state));
+
+ MOCK_METHOD3(SendRstStream,
+ void(QuicStreamId stream_id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written));
+
+ MOCK_METHOD2(OnStreamHeaders,
+ void(QuicStreamId stream_id, QuicStringPiece headers_data));
+ MOCK_METHOD2(OnStreamHeadersPriority,
+ void(QuicStreamId stream_id, spdy::SpdyPriority priority));
+ MOCK_METHOD3(OnStreamHeadersComplete,
+ void(QuicStreamId stream_id, bool fin, size_t frame_len));
+ MOCK_METHOD4(OnStreamHeaderList,
+ void(QuicStreamId stream_id,
+ bool fin,
+ size_t frame_len,
+ const QuicHeaderList& header_list));
+ MOCK_CONST_METHOD0(IsCryptoHandshakeConfirmed, bool());
+ MOCK_METHOD2(OnPromiseHeaders,
+ void(QuicStreamId stream_id, QuicStringPiece headers_data));
+ MOCK_METHOD3(OnPromiseHeadersComplete,
+ void(QuicStreamId stream_id,
+ QuicStreamId promised_stream_id,
+ size_t frame_len));
+ MOCK_METHOD4(OnPromiseHeaderList,
+ void(QuicStreamId stream_id,
+ QuicStreamId promised_stream_id,
+ size_t frame_len,
+ const QuicHeaderList& header_list));
+ MOCK_METHOD2(OnPriorityFrame,
+ void(QuicStreamId id, spdy::SpdyPriority priority));
+
+ MOCK_METHOD1(OnHeadersHeadOfLineBlocking, void(QuicTime::Delta delta));
+ MOCK_METHOD4(
+ OnStreamFrameData,
+ void(QuicStreamId stream_id, const char* data, size_t len, bool fin));
+
+ using QuicSession::ActivateStream;
+
+ private:
+ std::unique_ptr<QuicCryptoStream> crypto_stream_;
+};
+
+class TestQuicSpdyServerSession : public QuicServerSessionBase {
+ public:
+ // Takes ownership of |connection|.
+ TestQuicSpdyServerSession(QuicConnection* connection,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache);
+ TestQuicSpdyServerSession(const TestQuicSpdyServerSession&) = delete;
+ TestQuicSpdyServerSession& operator=(const TestQuicSpdyServerSession&) =
+ delete;
+ ~TestQuicSpdyServerSession() override;
+
+ MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(QuicStreamId id));
+ MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(PendingStream stream));
+ MOCK_METHOD0(CreateOutgoingBidirectionalStream, QuicSpdyStream*());
+ MOCK_METHOD0(CreateOutgoingUnidirectionalStream, QuicSpdyStream*());
+ QuicCryptoServerStreamBase* CreateQuicCryptoServerStream(
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache) override;
+
+ // Override to not send max header list size.
+ void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override;
+
+ QuicCryptoServerStream* GetMutableCryptoStream() override;
+
+ const QuicCryptoServerStream* GetCryptoStream() const override;
+
+ MockQuicCryptoServerStreamHelper* helper() { return &helper_; }
+
+ private:
+ MockQuicSessionVisitor visitor_;
+ MockQuicCryptoServerStreamHelper helper_;
+};
+
+// A test implementation of QuicClientPushPromiseIndex::Delegate.
+class TestPushPromiseDelegate : public QuicClientPushPromiseIndex::Delegate {
+ public:
+ // |match| sets the validation result for checking whether designated header
+ // fields match for promise request and client request.
+ explicit TestPushPromiseDelegate(bool match);
+
+ bool CheckVary(const spdy::SpdyHeaderBlock& client_request,
+ const spdy::SpdyHeaderBlock& promise_request,
+ const spdy::SpdyHeaderBlock& promise_response) override;
+
+ void OnRendezvousResult(QuicSpdyStream* stream) override;
+
+ QuicSpdyStream* rendezvous_stream() { return rendezvous_stream_; }
+ bool rendezvous_fired() { return rendezvous_fired_; }
+
+ private:
+ bool match_;
+ bool rendezvous_fired_;
+ QuicSpdyStream* rendezvous_stream_;
+};
+
+class TestQuicSpdyClientSession : public QuicSpdyClientSessionBase {
+ public:
+ TestQuicSpdyClientSession(QuicConnection* connection,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicServerId& server_id,
+ QuicCryptoClientConfig* crypto_config);
+ TestQuicSpdyClientSession(const TestQuicSpdyClientSession&) = delete;
+ TestQuicSpdyClientSession& operator=(const TestQuicSpdyClientSession&) =
+ delete;
+ ~TestQuicSpdyClientSession() override;
+
+ bool IsAuthorized(const QuicString& authority) override;
+
+ // QuicSpdyClientSessionBase
+ MOCK_METHOD1(OnProofValid,
+ void(const QuicCryptoClientConfig::CachedState& cached));
+ MOCK_METHOD1(OnProofVerifyDetailsAvailable,
+ void(const ProofVerifyDetails& verify_details));
+
+ // TestQuicSpdyClientSession
+ MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(QuicStreamId id));
+ MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(PendingStream stream));
+ MOCK_METHOD0(CreateOutgoingBidirectionalStream, QuicSpdyStream*());
+ MOCK_METHOD0(CreateOutgoingUnidirectionalStream, QuicSpdyStream*());
+ MOCK_METHOD1(ShouldCreateIncomingStream, bool(QuicStreamId id));
+ MOCK_METHOD0(ShouldCreateOutgoingBidirectionalStream, bool());
+ MOCK_METHOD0(ShouldCreateOutgoingUnidirectionalStream, bool());
+
+ // Override to not send max header list size.
+ void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override;
+ QuicCryptoClientStream* GetMutableCryptoStream() override;
+ const QuicCryptoClientStream* GetCryptoStream() const override;
+
+ // Override to save sent crypto handshake messages.
+ void OnCryptoHandshakeMessageSent(
+ const CryptoHandshakeMessage& message) override {
+ sent_crypto_handshake_messages_.push_back(message);
+ }
+
+ const std::vector<CryptoHandshakeMessage>& sent_crypto_handshake_messages()
+ const {
+ return sent_crypto_handshake_messages_;
+ }
+
+ private:
+ std::unique_ptr<QuicCryptoClientStream> crypto_stream_;
+ QuicClientPushPromiseIndex push_promise_index_;
+ std::vector<CryptoHandshakeMessage> sent_crypto_handshake_messages_;
+};
+
+class MockPacketWriter : public QuicPacketWriter {
+ public:
+ MockPacketWriter();
+ MockPacketWriter(const MockPacketWriter&) = delete;
+ MockPacketWriter& operator=(const MockPacketWriter&) = delete;
+ ~MockPacketWriter() override;
+
+ MOCK_METHOD5(WritePacket,
+ WriteResult(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options));
+ MOCK_CONST_METHOD0(IsWriteBlocked, bool());
+ MOCK_METHOD0(SetWritable, void());
+ MOCK_CONST_METHOD1(GetMaxPacketSize,
+ QuicByteCount(const QuicSocketAddress& peer_address));
+ MOCK_CONST_METHOD0(SupportsReleaseTime, bool());
+ MOCK_CONST_METHOD0(IsBatchMode, bool());
+ MOCK_METHOD2(GetNextWriteLocation,
+ char*(const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address));
+ MOCK_METHOD0(Flush, WriteResult());
+};
+
+class MockSendAlgorithm : public SendAlgorithmInterface {
+ public:
+ MockSendAlgorithm();
+ MockSendAlgorithm(const MockSendAlgorithm&) = delete;
+ MockSendAlgorithm& operator=(const MockSendAlgorithm&) = delete;
+ ~MockSendAlgorithm() override;
+
+ MOCK_METHOD2(SetFromConfig,
+ void(const QuicConfig& config, Perspective perspective));
+ MOCK_METHOD1(SetNumEmulatedConnections, void(int num_connections));
+ MOCK_METHOD1(SetInitialCongestionWindowInPackets,
+ void(QuicPacketCount packets));
+ MOCK_METHOD1(SetMaxCongestionWindow,
+ void(QuicByteCount max_congestion_window));
+ MOCK_METHOD5(OnCongestionEvent,
+ void(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ QuicTime event_time,
+ const AckedPacketVector& acked_packets,
+ const LostPacketVector& lost_packets));
+ MOCK_METHOD5(OnPacketSent,
+ void(QuicTime,
+ QuicByteCount,
+ QuicPacketNumber,
+ QuicByteCount,
+ HasRetransmittableData));
+ MOCK_METHOD1(OnRetransmissionTimeout, void(bool));
+ MOCK_METHOD0(OnConnectionMigration, void());
+ MOCK_METHOD0(RevertRetransmissionTimeout, void());
+ MOCK_METHOD1(CanSend, bool(QuicByteCount));
+ MOCK_CONST_METHOD1(PacingRate, QuicBandwidth(QuicByteCount));
+ MOCK_CONST_METHOD0(BandwidthEstimate, QuicBandwidth(void));
+ MOCK_CONST_METHOD0(HasReliableBandwidthEstimate, bool());
+ MOCK_METHOD1(OnRttUpdated, void(QuicPacketNumber));
+ MOCK_CONST_METHOD0(GetCongestionWindow, QuicByteCount());
+ MOCK_CONST_METHOD0(GetDebugState, QuicString());
+ MOCK_CONST_METHOD0(InSlowStart, bool());
+ MOCK_CONST_METHOD0(InRecovery, bool());
+ MOCK_CONST_METHOD0(ShouldSendProbingPacket, bool());
+ MOCK_CONST_METHOD0(GetSlowStartThreshold, QuicByteCount());
+ MOCK_CONST_METHOD0(GetCongestionControlType, CongestionControlType());
+ MOCK_METHOD2(AdjustNetworkParameters, void(QuicBandwidth, QuicTime::Delta));
+ MOCK_METHOD1(OnApplicationLimited, void(QuicByteCount));
+};
+
+class MockLossAlgorithm : public LossDetectionInterface {
+ public:
+ MockLossAlgorithm();
+ MockLossAlgorithm(const MockLossAlgorithm&) = delete;
+ MockLossAlgorithm& operator=(const MockLossAlgorithm&) = delete;
+ ~MockLossAlgorithm() override;
+
+ MOCK_CONST_METHOD0(GetLossDetectionType, LossDetectionType());
+ MOCK_METHOD6(DetectLosses,
+ void(const QuicUnackedPacketMap& unacked_packets,
+ QuicTime time,
+ const RttStats& rtt_stats,
+ QuicPacketNumber largest_recently_acked,
+ const AckedPacketVector& packets_acked,
+ LostPacketVector* packets_lost));
+ MOCK_CONST_METHOD0(GetLossTimeout, QuicTime());
+ MOCK_METHOD4(SpuriousRetransmitDetected,
+ void(const QuicUnackedPacketMap&,
+ QuicTime,
+ const RttStats&,
+ QuicPacketNumber));
+};
+
+class MockAckListener : public QuicAckListenerInterface {
+ public:
+ MockAckListener();
+ MockAckListener(const MockAckListener&) = delete;
+ MockAckListener& operator=(const MockAckListener&) = delete;
+
+ MOCK_METHOD2(OnPacketAcked,
+ void(int acked_bytes, QuicTime::Delta ack_delay_time));
+
+ MOCK_METHOD1(OnPacketRetransmitted, void(int retransmitted_bytes));
+
+ protected:
+ // Object is ref counted.
+ ~MockAckListener() override;
+};
+
+class MockNetworkChangeVisitor
+ : public QuicSentPacketManager::NetworkChangeVisitor {
+ public:
+ MockNetworkChangeVisitor();
+ MockNetworkChangeVisitor(const MockNetworkChangeVisitor&) = delete;
+ MockNetworkChangeVisitor& operator=(const MockNetworkChangeVisitor&) = delete;
+ ~MockNetworkChangeVisitor() override;
+
+ MOCK_METHOD0(OnCongestionChange, void());
+ MOCK_METHOD1(OnPathMtuIncreased, void(QuicPacketLength));
+};
+
+class MockQuicConnectionDebugVisitor : public QuicConnectionDebugVisitor {
+ public:
+ MockQuicConnectionDebugVisitor();
+ ~MockQuicConnectionDebugVisitor() override;
+
+ MOCK_METHOD1(OnFrameAddedToPacket, void(const QuicFrame&));
+
+ MOCK_METHOD4(OnPacketSent,
+ void(const SerializedPacket&,
+ QuicPacketNumber,
+ TransmissionType,
+ QuicTime));
+
+ MOCK_METHOD0(OnPingSent, void());
+
+ MOCK_METHOD3(OnPacketReceived,
+ void(const QuicSocketAddress&,
+ const QuicSocketAddress&,
+ const QuicEncryptedPacket&));
+
+ MOCK_METHOD1(OnIncorrectConnectionId, void(QuicConnectionId));
+
+ MOCK_METHOD1(OnProtocolVersionMismatch, void(ParsedQuicVersion));
+
+ MOCK_METHOD1(OnPacketHeader, void(const QuicPacketHeader& header));
+
+ MOCK_METHOD1(OnSuccessfulVersionNegotiation, void(const ParsedQuicVersion&));
+
+ MOCK_METHOD1(OnStreamFrame, void(const QuicStreamFrame&));
+
+ MOCK_METHOD1(OnStopWaitingFrame, void(const QuicStopWaitingFrame&));
+
+ MOCK_METHOD1(OnRstStreamFrame, void(const QuicRstStreamFrame&));
+
+ MOCK_METHOD1(OnConnectionCloseFrame, void(const QuicConnectionCloseFrame&));
+
+ MOCK_METHOD1(OnApplicationCloseFrame, void(const QuicApplicationCloseFrame&));
+
+ MOCK_METHOD1(OnStopSendingFrame, void(const QuicStopSendingFrame&));
+
+ MOCK_METHOD1(OnPathChallengeFrame, void(const QuicPathChallengeFrame&));
+
+ MOCK_METHOD1(OnPathResponseFrame, void(const QuicPathResponseFrame&));
+
+ MOCK_METHOD1(OnPublicResetPacket, void(const QuicPublicResetPacket&));
+
+ MOCK_METHOD1(OnVersionNegotiationPacket,
+ void(const QuicVersionNegotiationPacket&));
+};
+
+class MockReceivedPacketManager : public QuicReceivedPacketManager {
+ public:
+ explicit MockReceivedPacketManager(QuicConnectionStats* stats);
+ ~MockReceivedPacketManager() override;
+
+ MOCK_METHOD2(RecordPacketReceived,
+ void(const QuicPacketHeader& header, QuicTime receipt_time));
+ MOCK_METHOD1(IsMissing, bool(QuicPacketNumber packet_number));
+ MOCK_METHOD1(IsAwaitingPacket, bool(QuicPacketNumber packet_number));
+ MOCK_METHOD1(UpdatePacketInformationSentByPeer,
+ void(const QuicStopWaitingFrame& stop_waiting));
+ MOCK_CONST_METHOD0(HasNewMissingPackets, bool(void));
+ MOCK_CONST_METHOD0(ack_frame_updated, bool(void));
+};
+
+class MockConnectionCloseDelegate
+ : public QuicConnectionCloseDelegateInterface {
+ public:
+ MockConnectionCloseDelegate();
+ ~MockConnectionCloseDelegate() override;
+
+ MOCK_METHOD3(OnUnrecoverableError,
+ void(QuicErrorCode,
+ const QuicString&,
+ ConnectionCloseSource source));
+};
+
+class MockPacketCreatorDelegate : public QuicPacketCreator::DelegateInterface {
+ public:
+ MockPacketCreatorDelegate();
+ MockPacketCreatorDelegate(const MockPacketCreatorDelegate&) = delete;
+ MockPacketCreatorDelegate& operator=(const MockPacketCreatorDelegate&) =
+ delete;
+ ~MockPacketCreatorDelegate() override;
+
+ MOCK_METHOD0(GetPacketBuffer, char*());
+ MOCK_METHOD1(OnSerializedPacket, void(SerializedPacket* packet));
+ MOCK_METHOD3(OnUnrecoverableError,
+ void(QuicErrorCode,
+ const QuicString&,
+ ConnectionCloseSource source));
+};
+
+class MockSessionNotifier : public SessionNotifierInterface {
+ public:
+ MockSessionNotifier();
+ ~MockSessionNotifier() override;
+
+ MOCK_METHOD2(OnFrameAcked, bool(const QuicFrame&, QuicTime::Delta));
+ MOCK_METHOD1(OnStreamFrameRetransmitted, void(const QuicStreamFrame&));
+ MOCK_METHOD1(OnFrameLost, void(const QuicFrame&));
+ MOCK_METHOD2(RetransmitFrames,
+ void(const QuicFrames&, TransmissionType type));
+ MOCK_CONST_METHOD1(IsFrameOutstanding, bool(const QuicFrame&));
+ MOCK_CONST_METHOD0(HasUnackedCryptoData, bool());
+};
+
+// Creates a client session for testing.
+//
+// server_id: The server id associated with this stream.
+// supports_stateless_rejects: Does this client support stateless rejects.
+// connection_start_time: The time to set for the connection clock.
+// Needed for strike-register nonce verification. The client
+// connection_start_time should be synchronized witht the server
+// start time, otherwise nonce verification will fail.
+// supported_versions: Set of QUIC versions this client supports.
+// helper: Pointer to the MockQuicConnectionHelper to use for the session.
+// crypto_client_config: Pointer to the crypto client config.
+// client_connection: Pointer reference for newly created
+// connection. This object will be owned by the
+// client_session.
+// client_session: Pointer reference for the newly created client
+// session. The new object will be owned by the caller.
+void CreateClientSessionForTest(
+ QuicServerId server_id,
+ bool supports_stateless_rejects,
+ QuicTime::Delta connection_start_time,
+ const ParsedQuicVersionVector& supported_versions,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ QuicCryptoClientConfig* crypto_client_config,
+ PacketSavingConnection** client_connection,
+ TestQuicSpdyClientSession** client_session);
+
+// Creates a server session for testing.
+//
+// server_id: The server id associated with this stream.
+// connection_start_time: The time to set for the connection clock.
+// Needed for strike-register nonce verification. The server
+// connection_start_time should be synchronized witht the client
+// start time, otherwise nonce verification will fail.
+// supported_versions: Set of QUIC versions this server supports.
+// helper: Pointer to the MockQuicConnectionHelper to use for the session.
+// crypto_server_config: Pointer to the crypto server config.
+// server_connection: Pointer reference for newly created
+// connection. This object will be owned by the
+// server_session.
+// server_session: Pointer reference for the newly created server
+// session. The new object will be owned by the caller.
+void CreateServerSessionForTest(
+ QuicServerId server_id,
+ QuicTime::Delta connection_start_time,
+ ParsedQuicVersionVector supported_versions,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ QuicCryptoServerConfig* crypto_server_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ PacketSavingConnection** server_connection,
+ TestQuicSpdyServerSession** server_session);
+
+// Verifies that the relative error of |actual| with respect to |expected| is
+// no more than |margin|.
+
+template <typename T>
+void ExpectApproxEq(T expected, T actual, float relative_margin) {
+ // If |relative_margin| > 1 and T is an unsigned type, the comparison will
+ // underflow.
+ ASSERT_LE(relative_margin, 1);
+ ASSERT_GE(relative_margin, 0);
+
+ T absolute_margin = expected * relative_margin;
+
+ EXPECT_GE(expected + absolute_margin, actual);
+ EXPECT_LE(expected - absolute_margin, actual);
+}
+
+template <typename T>
+QuicHeaderList AsHeaderList(const T& container) {
+ QuicHeaderList l;
+ // No need to enforce header list size limits again in this handler.
+ l.set_max_header_list_size(UINT_MAX);
+ l.OnHeaderBlockStart();
+ size_t total_size = 0;
+ for (auto p : container) {
+ total_size += p.first.size() + p.second.size();
+ l.OnHeader(p.first, p.second);
+ }
+ l.OnHeaderBlockEnd(total_size, total_size);
+ return l;
+}
+
+// Utility function that stores |str|'s data in |iov|.
+inline void MakeIOVector(QuicStringPiece str, struct iovec* iov) {
+ iov->iov_base = const_cast<char*>(str.data());
+ iov->iov_len = static_cast<size_t>(str.size());
+}
+
+// Helper functions for stream ids, to allow test logic to abstract over the
+// HTTP stream numbering scheme (i.e. whether one or two QUIC streams are used
+// per HTTP transaction).
+QuicStreamId GetNthClientInitiatedBidirectionalStreamId(
+ QuicTransportVersion version,
+ int n);
+QuicStreamId GetNthServerInitiatedBidirectionalStreamId(
+ QuicTransportVersion version,
+ int n);
+QuicStreamId GetNthServerInitiatedUnidirectionalStreamId(
+ QuicTransportVersion version,
+ int n);
+
+StreamType DetermineStreamType(QuicStreamId id,
+ QuicTransportVersion version,
+ Perspective perspective,
+ bool is_incoming,
+ StreamType default_type);
+
+// Utility function that stores message_data in |storage| and returns a
+// QuicMemSliceSpan.
+QuicMemSliceSpan MakeSpan(QuicBufferAllocator* allocator,
+ QuicStringPiece message_data,
+ QuicMemSliceStorage* storage);
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_
diff --git a/quic/test_tools/quic_test_utils_test.cc b/quic/test_tools/quic_test_utils_test.cc
new file mode 100644
index 0000000..79d2af8
--- /dev/null
+++ b/quic/test_tools/quic_test_utils_test.cc
@@ -0,0 +1,61 @@
+// Copyright 2016 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 "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+#include "testing/gtest/include/gtest/gtest-spi.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+
+class QuicTestUtilsTest : public QuicTest {};
+
+TEST_F(QuicTestUtilsTest, ConnectionId) {
+ EXPECT_NE(EmptyQuicConnectionId(), TestConnectionId());
+ EXPECT_NE(EmptyQuicConnectionId(), TestConnectionId(1));
+ EXPECT_EQ(TestConnectionId(), TestConnectionId());
+ EXPECT_EQ(TestConnectionId(33), TestConnectionId(33));
+ EXPECT_NE(TestConnectionId(0xdead), TestConnectionId(0xbeef));
+ EXPECT_EQ(0x1337u, TestConnectionIdToUInt64(TestConnectionId(0x1337)));
+ EXPECT_NE(0xdeadu, TestConnectionIdToUInt64(TestConnectionId(0xbeef)));
+}
+
+TEST_F(QuicTestUtilsTest, BasicApproxEq) {
+ ExpectApproxEq(10, 10, 1e-6f);
+ ExpectApproxEq(1000, 1001, 0.01f);
+ EXPECT_NONFATAL_FAILURE(ExpectApproxEq(1000, 1100, 0.01f), "");
+
+ ExpectApproxEq(64, 31, 0.55f);
+ EXPECT_NONFATAL_FAILURE(ExpectApproxEq(31, 64, 0.55f), "");
+}
+
+TEST_F(QuicTestUtilsTest, QuicTimeDelta) {
+ ExpectApproxEq(QuicTime::Delta::FromMicroseconds(1000),
+ QuicTime::Delta::FromMicroseconds(1003), 0.01f);
+ EXPECT_NONFATAL_FAILURE(
+ ExpectApproxEq(QuicTime::Delta::FromMicroseconds(1000),
+ QuicTime::Delta::FromMicroseconds(1200), 0.01f),
+ "");
+}
+
+TEST_F(QuicTestUtilsTest, QuicBandwidth) {
+ ExpectApproxEq(QuicBandwidth::FromBytesPerSecond(1000),
+ QuicBandwidth::FromBitsPerSecond(8005), 0.01f);
+ EXPECT_NONFATAL_FAILURE(
+ ExpectApproxEq(QuicBandwidth::FromBytesPerSecond(1000),
+ QuicBandwidth::FromBitsPerSecond(9005), 0.01f),
+ "");
+}
+
+// Ensure that SimpleRandom does not change its output for a fixed seed.
+TEST_F(QuicTestUtilsTest, SimpleRandomStability) {
+ SimpleRandom rng;
+ rng.set_seed(UINT64_C(0x1234567800010001));
+ EXPECT_EQ(UINT64_C(14865409841904857791), rng.RandUint64());
+ EXPECT_EQ(UINT64_C(12139094019410129741), rng.RandUint64());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_time_wait_list_manager_peer.cc b/quic/test_tools/quic_time_wait_list_manager_peer.cc
new file mode 100644
index 0000000..7c90321
--- /dev/null
+++ b/quic/test_tools/quic_time_wait_list_manager_peer.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2016 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 "net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.h"
+
+namespace quic {
+namespace test {
+
+bool QuicTimeWaitListManagerPeer::ShouldSendResponse(
+ QuicTimeWaitListManager* manager,
+ int received_packet_count) {
+ return manager->ShouldSendResponse(received_packet_count);
+}
+
+QuicTime::Delta QuicTimeWaitListManagerPeer::time_wait_period(
+ QuicTimeWaitListManager* manager) {
+ return manager->time_wait_period_;
+}
+
+QuicAlarm* QuicTimeWaitListManagerPeer::expiration_alarm(
+ QuicTimeWaitListManager* manager) {
+ return manager->connection_id_clean_up_alarm_.get();
+}
+
+void QuicTimeWaitListManagerPeer::set_clock(QuicTimeWaitListManager* manager,
+ const QuicClock* clock) {
+ manager->clock_ = clock;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_time_wait_list_manager_peer.h b/quic/test_tools/quic_time_wait_list_manager_peer.h
new file mode 100644
index 0000000..0fdf976
--- /dev/null
+++ b/quic/test_tools/quic_time_wait_list_manager_peer.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2016 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_TIME_WAIT_LIST_MANAGER_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_TIME_WAIT_LIST_MANAGER_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h"
+
+namespace quic {
+namespace test {
+
+class QuicTimeWaitListManagerPeer {
+ public:
+ static bool ShouldSendResponse(QuicTimeWaitListManager* manager,
+ int received_packet_count);
+
+ static QuicTime::Delta time_wait_period(QuicTimeWaitListManager* manager);
+
+ static QuicAlarm* expiration_alarm(QuicTimeWaitListManager* manager);
+
+ static void set_clock(QuicTimeWaitListManager* manager,
+ const QuicClock* clock);
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_TIME_WAIT_LIST_MANAGER_PEER_H_
diff --git a/quic/test_tools/quic_unacked_packet_map_peer.cc b/quic/test_tools/quic_unacked_packet_map_peer.cc
new file mode 100644
index 0000000..1ecc65f
--- /dev/null
+++ b/quic/test_tools/quic_unacked_packet_map_peer.cc
@@ -0,0 +1,24 @@
+// 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 "net/third_party/quiche/src/quic/test_tools/quic_unacked_packet_map_peer.h"
+
+namespace quic {
+namespace test {
+
+// static
+const QuicStreamFrame& QuicUnackedPacketMapPeer::GetAggregatedStreamFrame(
+ const QuicUnackedPacketMap& unacked_packets) {
+ return unacked_packets.aggregated_stream_frame_;
+}
+
+// static
+void QuicUnackedPacketMapPeer::SetPerspective(
+ QuicUnackedPacketMap* unacked_packets,
+ Perspective perspective) {
+ *const_cast<Perspective*>(&unacked_packets->perspective_) = perspective;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/quic_unacked_packet_map_peer.h b/quic/test_tools/quic_unacked_packet_map_peer.h
new file mode 100644
index 0000000..3bba0b6
--- /dev/null
+++ b/quic/test_tools/quic_unacked_packet_map_peer.h
@@ -0,0 +1,25 @@
+// 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_UNACKED_PACKET_MAP_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_UNACKED_PACKET_MAP_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h"
+
+namespace quic {
+namespace test {
+
+class QuicUnackedPacketMapPeer {
+ public:
+ static const QuicStreamFrame& GetAggregatedStreamFrame(
+ const QuicUnackedPacketMap& unacked_packets);
+
+ static void SetPerspective(QuicUnackedPacketMap* unacked_packets,
+ Perspective perspective);
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_UNACKED_PACKET_MAP_PEER_H_
diff --git a/quic/test_tools/rtt_stats_peer.cc b/quic/test_tools/rtt_stats_peer.cc
new file mode 100644
index 0000000..e8c6810
--- /dev/null
+++ b/quic/test_tools/rtt_stats_peer.cc
@@ -0,0 +1,21 @@
+// Copyright 2015 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 "net/third_party/quiche/src/quic/test_tools/rtt_stats_peer.h"
+
+namespace quic {
+namespace test {
+
+// static
+void RttStatsPeer::SetSmoothedRtt(RttStats* rtt_stats, QuicTime::Delta rtt_ms) {
+ rtt_stats->smoothed_rtt_ = rtt_ms;
+}
+
+// static
+void RttStatsPeer::SetMinRtt(RttStats* rtt_stats, QuicTime::Delta rtt_ms) {
+ rtt_stats->min_rtt_ = rtt_ms;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/rtt_stats_peer.h b/quic/test_tools/rtt_stats_peer.h
new file mode 100644
index 0000000..4ea7ad0
--- /dev/null
+++ b/quic/test_tools/rtt_stats_peer.h
@@ -0,0 +1,27 @@
+// Copyright 2015 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_RTT_STATS_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_RTT_STATS_PEER_H_
+
+#include "base/macros.h"
+#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+
+namespace quic {
+namespace test {
+
+class RttStatsPeer {
+ public:
+ RttStatsPeer() = delete;
+
+ static void SetSmoothedRtt(RttStats* rtt_stats, QuicTime::Delta rtt_ms);
+
+ static void SetMinRtt(RttStats* rtt_stats, QuicTime::Delta rtt_ms);
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_RTT_STATS_PEER_H_
diff --git a/quic/test_tools/server_thread.cc b/quic/test_tools/server_thread.cc
new file mode 100644
index 0000000..ca15913
--- /dev/null
+++ b/quic/test_tools/server_thread.cc
@@ -0,0 +1,121 @@
+// Copyright 2013 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 "net/third_party/quiche/src/quic/test_tools/server_thread.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_dispatcher.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.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"
+
+namespace quic {
+namespace test {
+
+ServerThread::ServerThread(QuicServer* server, const QuicSocketAddress& address)
+ : QuicThread("server_thread"),
+ server_(server),
+ address_(address),
+ port_(0),
+ initialized_(false) {}
+
+ServerThread::~ServerThread() = default;
+
+void ServerThread::Initialize() {
+ if (initialized_) {
+ return;
+ }
+
+ server_->CreateUDPSocketAndListen(address_);
+
+ QuicWriterMutexLock lock(&port_lock_);
+ port_ = server_->port();
+
+ initialized_ = true;
+}
+
+void ServerThread::Run() {
+ if (!initialized_) {
+ Initialize();
+ }
+
+ while (!quit_.HasBeenNotified()) {
+ if (pause_.HasBeenNotified() && !resume_.HasBeenNotified()) {
+ paused_.Notify();
+ resume_.WaitForNotification();
+ }
+ server_->WaitForEvents();
+ ExecuteScheduledActions();
+ MaybeNotifyOfHandshakeConfirmation();
+ }
+
+ server_->Shutdown();
+}
+
+int ServerThread::GetPort() {
+ QuicReaderMutexLock lock(&port_lock_);
+ int rc = port_;
+ return rc;
+}
+
+void ServerThread::Schedule(std::function<void()> action) {
+ DCHECK(!quit_.HasBeenNotified());
+ QuicWriterMutexLock lock(&scheduled_actions_lock_);
+ scheduled_actions_.push_back(std::move(action));
+}
+
+void ServerThread::WaitForCryptoHandshakeConfirmed() {
+ confirmed_.WaitForNotification();
+}
+
+void ServerThread::Pause() {
+ DCHECK(!pause_.HasBeenNotified());
+ pause_.Notify();
+ paused_.WaitForNotification();
+}
+
+void ServerThread::Resume() {
+ DCHECK(!resume_.HasBeenNotified());
+ DCHECK(pause_.HasBeenNotified());
+ resume_.Notify();
+}
+
+void ServerThread::Quit() {
+ if (pause_.HasBeenNotified() && !resume_.HasBeenNotified()) {
+ resume_.Notify();
+ }
+ if (!quit_.HasBeenNotified()) {
+ quit_.Notify();
+ }
+}
+
+void ServerThread::MaybeNotifyOfHandshakeConfirmation() {
+ if (confirmed_.HasBeenNotified()) {
+ // Only notify once.
+ return;
+ }
+ QuicDispatcher* dispatcher = QuicServerPeer::GetDispatcher(server());
+ if (dispatcher->session_map().empty()) {
+ // Wait for a session to be created.
+ return;
+ }
+ QuicSession* session = dispatcher->session_map().begin()->second.get();
+ if (session->IsCryptoHandshakeConfirmed()) {
+ confirmed_.Notify();
+ }
+}
+
+void ServerThread::ExecuteScheduledActions() {
+ QuicDeque<std::function<void()>> actions;
+ {
+ QuicWriterMutexLock lock(&scheduled_actions_lock_);
+ actions.swap(scheduled_actions_);
+ }
+ while (!actions.empty()) {
+ actions.front()();
+ actions.pop_front();
+ }
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/server_thread.h b/quic/test_tools/server_thread.h
new file mode 100644
index 0000000..ffbce7f
--- /dev/null
+++ b/quic/test_tools/server_thread.h
@@ -0,0 +1,88 @@
+// Copyright 2013 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SERVER_THREAD_H_
+#define QUICHE_QUIC_TEST_TOOLS_SERVER_THREAD_H_
+
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mutex.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_thread.h"
+#include "net/third_party/quiche/src/quic/tools/quic_server.h"
+
+namespace quic {
+namespace test {
+
+// Simple wrapper class to run QuicServer in a dedicated thread.
+class ServerThread : public QuicThread {
+ public:
+ ServerThread(QuicServer* server, const QuicSocketAddress& address);
+ ServerThread(const ServerThread&) = delete;
+ ServerThread& operator=(const ServerThread&) = delete;
+
+ ~ServerThread() override;
+
+ // Prepares the server, but does not start accepting connections. Useful for
+ // injecting mocks.
+ void Initialize();
+
+ // Runs the event loop. Will initialize if necessary.
+ void Run() override;
+
+ // Schedules the given action for execution in the event loop.
+ void Schedule(std::function<void()> action);
+
+ // Waits for the handshake to be confirmed for the first session created.
+ void WaitForCryptoHandshakeConfirmed();
+
+ // Pauses execution of the server until Resume() is called. May only be
+ // called once.
+ void Pause();
+
+ // Resumes execution of the server after Pause() has been called. May only
+ // be called once.
+ void Resume();
+
+ // Stops the server from executing and shuts it down, destroying all
+ // server objects.
+ void Quit();
+
+ // Returns the underlying server. Care must be taken to avoid data races
+ // when accessing the server. It is always safe to access the server
+ // after calling Pause() and before calling Resume().
+ QuicServer* server() { return server_.get(); }
+
+ // Returns the port that the server is listening on.
+ int GetPort();
+
+ private:
+ void MaybeNotifyOfHandshakeConfirmation();
+ void ExecuteScheduledActions();
+
+ QuicNotification
+ confirmed_; // Notified when the first handshake is confirmed.
+ QuicNotification pause_; // Notified when the server should pause.
+ QuicNotification paused_; // Notitied when the server has paused
+ QuicNotification resume_; // Notified when the server should resume.
+ QuicNotification quit_; // Notified when the server should quit.
+
+ std::unique_ptr<QuicServer> server_;
+ QuicSocketAddress address_;
+ mutable QuicMutex port_lock_;
+ int port_ GUARDED_BY(port_lock_);
+
+ bool initialized_;
+
+ QuicMutex scheduled_actions_lock_;
+ QuicDeque<std::function<void()>> scheduled_actions_
+ GUARDED_BY(scheduled_actions_lock_);
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SERVER_THREAD_H_
diff --git a/quic/test_tools/simple_data_producer.cc b/quic/test_tools/simple_data_producer.cc
new file mode 100644
index 0000000..ab98fd1
--- /dev/null
+++ b/quic/test_tools/simple_data_producer.cc
@@ -0,0 +1,70 @@
+// Copyright (c) 2017 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 "net/third_party/quiche/src/quic/test_tools/simple_data_producer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+
+namespace quic {
+
+namespace test {
+
+SimpleDataProducer::SimpleDataProducer() {}
+
+SimpleDataProducer::~SimpleDataProducer() {}
+
+void SimpleDataProducer::SaveStreamData(QuicStreamId id,
+ const struct iovec* iov,
+ int iov_count,
+ size_t iov_offset,
+ QuicByteCount data_length) {
+ if (data_length == 0) {
+ return;
+ }
+ if (!QuicContainsKey(send_buffer_map_, id)) {
+ send_buffer_map_[id] = QuicMakeUnique<QuicStreamSendBuffer>(&allocator_);
+ }
+ send_buffer_map_[id]->SaveStreamData(iov, iov_count, iov_offset, data_length);
+}
+
+void SimpleDataProducer::SaveCryptoData(EncryptionLevel level,
+ QuicStreamOffset offset,
+ QuicStringPiece data) {
+ auto key = std::make_pair(level, offset);
+ crypto_buffer_map_[key] = data;
+}
+
+WriteStreamDataResult SimpleDataProducer::WriteStreamData(
+ QuicStreamId id,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) {
+ auto iter = send_buffer_map_.find(id);
+ if (iter == send_buffer_map_.end()) {
+ return STREAM_MISSING;
+ }
+ if (iter->second->WriteStreamData(offset, data_length, writer)) {
+ return WRITE_SUCCESS;
+ }
+ return WRITE_FAILED;
+}
+
+bool SimpleDataProducer::WriteCryptoData(EncryptionLevel level,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) {
+ auto it = crypto_buffer_map_.find(std::make_pair(level, offset));
+ if (it == crypto_buffer_map_.end() || it->second.length() != data_length) {
+ return false;
+ }
+ return writer->WriteStringPiece(it->second);
+}
+
+} // namespace test
+
+} // namespace quic
diff --git a/quic/test_tools/simple_data_producer.h b/quic/test_tools/simple_data_producer.h
new file mode 100644
index 0000000..d51ec69
--- /dev/null
+++ b/quic/test_tools/simple_data_producer.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2017 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMPLE_DATA_PRODUCER_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMPLE_DATA_PRODUCER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_frame_data_producer.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+
+namespace quic {
+
+namespace test {
+
+// A simple data producer which copies stream data into a map from stream
+// id to send buffer.
+class SimpleDataProducer : public QuicStreamFrameDataProducer {
+ public:
+ SimpleDataProducer();
+ ~SimpleDataProducer() override;
+
+ // Saves data to be provided when WriteStreamData is called. Data of length
+ // |data_length| is buffered to be provided for stream |id|. Multiple calls to
+ // SaveStreamData for the same stream ID append to the buffer for that stream.
+ // The data to be buffered is provided in |iov_count| iovec structs, with
+ // |iov| pointing to the first, and |iov_offset| indicating how many bytes
+ // into the iovec structs the data starts.
+ void SaveStreamData(QuicStreamId id,
+ const struct iovec* iov,
+ int iov_count,
+ size_t iov_offset,
+ QuicByteCount data_length);
+
+ void SaveCryptoData(EncryptionLevel level,
+ QuicStreamOffset offset,
+ QuicStringPiece data);
+
+ // QuicStreamFrameDataProducer
+ WriteStreamDataResult WriteStreamData(QuicStreamId id,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) override;
+ bool WriteCryptoData(EncryptionLevel level,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) override;
+
+ // TODO(wub): Allow QuicDefaultHasher to accept a pair. Then remove this.
+ class PairHash {
+ public:
+ template <class T1, class T2>
+ size_t operator()(const std::pair<T1, T2>& pair) const {
+ return std::hash<T1>()(pair.first) ^ std::hash<T2>()(pair.second);
+ }
+ };
+
+ private:
+ using SendBufferMap =
+ QuicUnorderedMap<QuicStreamId, std::unique_ptr<QuicStreamSendBuffer>>;
+
+ using CryptoBufferMap =
+ QuicUnorderedMap<std::pair<EncryptionLevel, QuicStreamOffset>,
+ QuicStringPiece,
+ PairHash>;
+
+ SimpleBufferAllocator allocator_;
+
+ SendBufferMap send_buffer_map_;
+
+ // |crypto_buffer_map_| stores data provided by SaveCryptoData to later write
+ // in WriteCryptoData. The level and data passed into SaveCryptoData are used
+ // as the key to identify the data when WriteCryptoData is called.
+ // WriteCryptoData will only succeed if there is data in the map for the
+ // provided level and offset, and the data in the map matches the data_length
+ // passed into WriteCryptoData.
+ //
+ // Unlike SaveStreamData/WriteStreamData which uses a map of
+ // QuicStreamSendBuffers (for each stream ID), this map provides data for
+ // specific offsets. Using a QuicStreamSendBuffer requires that all data
+ // before an offset exist, whereas this allows providing data that exists at
+ // arbitrary offsets for testing.
+ CryptoBufferMap crypto_buffer_map_;
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMPLE_DATA_PRODUCER_H_
diff --git a/quic/test_tools/simple_quic_framer.cc b/quic/test_tools/simple_quic_framer.cc
new file mode 100644
index 0000000..bf7184e
--- /dev/null
+++ b/quic/test_tools/simple_quic_framer.cc
@@ -0,0 +1,411 @@
+// Copyright (c) 2012 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 "net/third_party/quiche/src/quic/test_tools/simple_quic_framer.h"
+
+#include <memory>
+
+#include "base/macros.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+namespace test {
+
+class SimpleFramerVisitor : public QuicFramerVisitorInterface {
+ public:
+ SimpleFramerVisitor() : error_(QUIC_NO_ERROR) {}
+ SimpleFramerVisitor(const SimpleFramerVisitor&) = delete;
+ SimpleFramerVisitor& operator=(const SimpleFramerVisitor&) = delete;
+
+ ~SimpleFramerVisitor() override {}
+
+ void OnError(QuicFramer* framer) override { error_ = framer->error(); }
+
+ bool OnProtocolVersionMismatch(ParsedQuicVersion version,
+ PacketHeaderFormat form) override {
+ return false;
+ }
+
+ void OnPacket() override {}
+ void OnPublicResetPacket(const QuicPublicResetPacket& packet) override {
+ public_reset_packet_ = QuicMakeUnique<QuicPublicResetPacket>((packet));
+ }
+ void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& packet) override {
+ version_negotiation_packet_ =
+ QuicMakeUnique<QuicVersionNegotiationPacket>((packet));
+ }
+
+ bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) override {
+ return true;
+ }
+ bool OnUnauthenticatedHeader(const QuicPacketHeader& header) override {
+ return true;
+ }
+ void OnDecryptedPacket(EncryptionLevel level) override {
+ last_decrypted_level_ = level;
+ }
+ bool OnPacketHeader(const QuicPacketHeader& header) override {
+ has_header_ = true;
+ header_ = header;
+ return true;
+ }
+
+ void OnCoalescedPacket(const QuicEncryptedPacket& packet) override {}
+
+ bool OnStreamFrame(const QuicStreamFrame& frame) override {
+ // Save a copy of the data so it is valid after the packet is processed.
+ QuicString* string_data =
+ new QuicString(frame.data_buffer, frame.data_length);
+ stream_data_.push_back(QuicWrapUnique(string_data));
+ // TODO(ianswett): A pointer isn't necessary with emplace_back.
+ stream_frames_.push_back(QuicMakeUnique<QuicStreamFrame>(
+ frame.stream_id, frame.fin, frame.offset,
+ QuicStringPiece(*string_data)));
+ return true;
+ }
+
+ bool OnCryptoFrame(const QuicCryptoFrame& frame) override {
+ // Save a copy of the data so it is valid after the packet is processed.
+ QuicString* string_data =
+ new QuicString(frame.data_buffer, frame.data_length);
+ crypto_data_.push_back(QuicWrapUnique(string_data));
+ crypto_frames_.push_back(QuicMakeUnique<QuicCryptoFrame>(
+ frame.level, frame.offset, QuicStringPiece(*string_data)));
+ return true;
+ }
+
+ bool OnAckFrameStart(QuicPacketNumber largest_acked,
+ QuicTime::Delta ack_delay_time) override {
+ QuicAckFrame ack_frame;
+ ack_frame.largest_acked = largest_acked;
+ ack_frame.ack_delay_time = ack_delay_time;
+ ack_frames_.push_back(ack_frame);
+ return true;
+ }
+
+ bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override {
+ DCHECK(!ack_frames_.empty());
+ ack_frames_[ack_frames_.size() - 1].packets.AddRange(start, end);
+ return true;
+ }
+
+ bool OnAckTimestamp(QuicPacketNumber packet_number,
+ QuicTime timestamp) override {
+ return true;
+ }
+
+ bool OnAckFrameEnd(QuicPacketNumber /*start*/) override { return true; }
+
+ bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override {
+ stop_waiting_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnPaddingFrame(const QuicPaddingFrame& frame) override {
+ padding_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnPingFrame(const QuicPingFrame& frame) override {
+ ping_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override {
+ rst_stream_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override {
+ connection_close_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnApplicationCloseFrame(
+ const QuicApplicationCloseFrame& frame) override {
+ application_close_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame) override {
+ new_connection_id_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnRetireConnectionIdFrame(
+ const QuicRetireConnectionIdFrame& frame) override {
+ retire_connection_id_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override {
+ new_token_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override {
+ stop_sending_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnPathChallengeFrame(const QuicPathChallengeFrame& frame) override {
+ path_challenge_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnPathResponseFrame(const QuicPathResponseFrame& frame) override {
+ path_response_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnGoAwayFrame(const QuicGoAwayFrame& frame) override {
+ goaway_frames_.push_back(frame);
+ return true;
+ }
+ bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) override {
+ max_stream_id_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame) override {
+ stream_id_blocked_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override {
+ window_update_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnBlockedFrame(const QuicBlockedFrame& frame) override {
+ blocked_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnMessageFrame(const QuicMessageFrame& frame) override {
+ message_frames_.emplace_back(frame.data, frame.message_length);
+ return true;
+ }
+
+ void OnPacketComplete() override {}
+
+ bool IsValidStatelessResetToken(QuicUint128 token) const override {
+ return false;
+ }
+
+ void OnAuthenticatedIetfStatelessResetPacket(
+ const QuicIetfStatelessResetPacket& packet) override {
+ stateless_reset_packet_ =
+ QuicMakeUnique<QuicIetfStatelessResetPacket>(packet);
+ }
+
+ const QuicPacketHeader& header() const { return header_; }
+ const std::vector<QuicAckFrame>& ack_frames() const { return ack_frames_; }
+ const std::vector<QuicConnectionCloseFrame>& connection_close_frames() const {
+ return connection_close_frames_;
+ }
+ const std::vector<QuicApplicationCloseFrame>& application_close_frames()
+ const {
+ return application_close_frames_;
+ }
+ const std::vector<QuicGoAwayFrame>& goaway_frames() const {
+ return goaway_frames_;
+ }
+ const std::vector<QuicMaxStreamIdFrame>& max_stream_id_frames() const {
+ return max_stream_id_frames_;
+ }
+ const std::vector<QuicStreamIdBlockedFrame>& stream_id_blocked_frames()
+ const {
+ return stream_id_blocked_frames_;
+ }
+ const std::vector<QuicRstStreamFrame>& rst_stream_frames() const {
+ return rst_stream_frames_;
+ }
+ const std::vector<std::unique_ptr<QuicStreamFrame>>& stream_frames() const {
+ return stream_frames_;
+ }
+ const std::vector<std::unique_ptr<QuicCryptoFrame>>& crypto_frames() const {
+ return crypto_frames_;
+ }
+ const std::vector<QuicStopWaitingFrame>& stop_waiting_frames() const {
+ return stop_waiting_frames_;
+ }
+ const std::vector<QuicPingFrame>& ping_frames() const { return ping_frames_; }
+ const std::vector<QuicMessageFrame>& message_frames() const {
+ return message_frames_;
+ }
+ const std::vector<QuicWindowUpdateFrame>& window_update_frames() const {
+ return window_update_frames_;
+ }
+ const std::vector<QuicPaddingFrame>& padding_frames() const {
+ return padding_frames_;
+ }
+ const std::vector<QuicPathChallengeFrame>& path_challenge_frames() const {
+ return path_challenge_frames_;
+ }
+ const std::vector<QuicPathResponseFrame>& path_response_frames() const {
+ return path_response_frames_;
+ }
+ const QuicVersionNegotiationPacket* version_negotiation_packet() const {
+ return version_negotiation_packet_.get();
+ }
+ EncryptionLevel last_decrypted_level() const { return last_decrypted_level_; }
+
+ private:
+ QuicErrorCode error_;
+ bool has_header_;
+ QuicPacketHeader header_;
+ std::unique_ptr<QuicVersionNegotiationPacket> version_negotiation_packet_;
+ std::unique_ptr<QuicPublicResetPacket> public_reset_packet_;
+ std::unique_ptr<QuicIetfStatelessResetPacket> stateless_reset_packet_;
+ std::vector<QuicAckFrame> ack_frames_;
+ std::vector<QuicStopWaitingFrame> stop_waiting_frames_;
+ std::vector<QuicPaddingFrame> padding_frames_;
+ std::vector<QuicPingFrame> ping_frames_;
+ std::vector<std::unique_ptr<QuicStreamFrame>> stream_frames_;
+ std::vector<std::unique_ptr<QuicCryptoFrame>> crypto_frames_;
+ std::vector<QuicRstStreamFrame> rst_stream_frames_;
+ std::vector<QuicGoAwayFrame> goaway_frames_;
+ std::vector<QuicStreamIdBlockedFrame> stream_id_blocked_frames_;
+ std::vector<QuicMaxStreamIdFrame> max_stream_id_frames_;
+ std::vector<QuicConnectionCloseFrame> connection_close_frames_;
+ std::vector<QuicApplicationCloseFrame> application_close_frames_;
+ std::vector<QuicStopSendingFrame> stop_sending_frames_;
+ std::vector<QuicPathChallengeFrame> path_challenge_frames_;
+ std::vector<QuicPathResponseFrame> path_response_frames_;
+ std::vector<QuicWindowUpdateFrame> window_update_frames_;
+ std::vector<QuicBlockedFrame> blocked_frames_;
+ std::vector<QuicNewConnectionIdFrame> new_connection_id_frames_;
+ std::vector<QuicRetireConnectionIdFrame> retire_connection_id_frames_;
+ std::vector<QuicNewTokenFrame> new_token_frames_;
+ std::vector<QuicMessageFrame> message_frames_;
+ std::vector<std::unique_ptr<QuicString>> stream_data_;
+ std::vector<std::unique_ptr<QuicString>> crypto_data_;
+ EncryptionLevel last_decrypted_level_;
+};
+
+SimpleQuicFramer::SimpleQuicFramer()
+ : framer_(AllSupportedVersions(),
+ QuicTime::Zero(),
+ Perspective::IS_SERVER,
+ kQuicDefaultConnectionIdLength) {}
+
+SimpleQuicFramer::SimpleQuicFramer(
+ const ParsedQuicVersionVector& supported_versions)
+ : framer_(supported_versions,
+ QuicTime::Zero(),
+ Perspective::IS_SERVER,
+ kQuicDefaultConnectionIdLength) {}
+
+SimpleQuicFramer::SimpleQuicFramer(
+ const ParsedQuicVersionVector& supported_versions,
+ Perspective perspective)
+ : framer_(supported_versions,
+ QuicTime::Zero(),
+ perspective,
+ kQuicDefaultConnectionIdLength) {}
+
+SimpleQuicFramer::~SimpleQuicFramer() {}
+
+bool SimpleQuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) {
+ visitor_ = QuicMakeUnique<SimpleFramerVisitor>();
+ framer_.set_visitor(visitor_.get());
+ return framer_.ProcessPacket(packet);
+}
+
+void SimpleQuicFramer::Reset() {
+ visitor_ = QuicMakeUnique<SimpleFramerVisitor>();
+}
+
+const QuicPacketHeader& SimpleQuicFramer::header() const {
+ return visitor_->header();
+}
+
+const QuicVersionNegotiationPacket*
+SimpleQuicFramer::version_negotiation_packet() const {
+ return visitor_->version_negotiation_packet();
+}
+
+EncryptionLevel SimpleQuicFramer::last_decrypted_level() const {
+ return visitor_->last_decrypted_level();
+}
+
+QuicFramer* SimpleQuicFramer::framer() {
+ return &framer_;
+}
+
+size_t SimpleQuicFramer::num_frames() const {
+ return ack_frames().size() + goaway_frames().size() +
+ rst_stream_frames().size() + stop_waiting_frames().size() +
+ path_challenge_frames().size() + path_response_frames().size() +
+ stream_frames().size() + ping_frames().size() +
+ connection_close_frames().size() + padding_frames().size() +
+ crypto_frames().size();
+}
+
+const std::vector<QuicAckFrame>& SimpleQuicFramer::ack_frames() const {
+ return visitor_->ack_frames();
+}
+
+const std::vector<QuicStopWaitingFrame>& SimpleQuicFramer::stop_waiting_frames()
+ const {
+ return visitor_->stop_waiting_frames();
+}
+
+const std::vector<QuicPathChallengeFrame>&
+SimpleQuicFramer::path_challenge_frames() const {
+ return visitor_->path_challenge_frames();
+}
+const std::vector<QuicPathResponseFrame>&
+SimpleQuicFramer::path_response_frames() const {
+ return visitor_->path_response_frames();
+}
+
+const std::vector<QuicPingFrame>& SimpleQuicFramer::ping_frames() const {
+ return visitor_->ping_frames();
+}
+
+const std::vector<QuicMessageFrame>& SimpleQuicFramer::message_frames() const {
+ return visitor_->message_frames();
+}
+
+const std::vector<QuicWindowUpdateFrame>&
+SimpleQuicFramer::window_update_frames() const {
+ return visitor_->window_update_frames();
+}
+
+const std::vector<std::unique_ptr<QuicStreamFrame>>&
+SimpleQuicFramer::stream_frames() const {
+ return visitor_->stream_frames();
+}
+
+const std::vector<std::unique_ptr<QuicCryptoFrame>>&
+SimpleQuicFramer::crypto_frames() const {
+ return visitor_->crypto_frames();
+}
+
+const std::vector<QuicRstStreamFrame>& SimpleQuicFramer::rst_stream_frames()
+ const {
+ return visitor_->rst_stream_frames();
+}
+
+const std::vector<QuicGoAwayFrame>& SimpleQuicFramer::goaway_frames() const {
+ return visitor_->goaway_frames();
+}
+
+const std::vector<QuicConnectionCloseFrame>&
+SimpleQuicFramer::connection_close_frames() const {
+ return visitor_->connection_close_frames();
+}
+
+const std::vector<QuicPaddingFrame>& SimpleQuicFramer::padding_frames() const {
+ return visitor_->padding_frames();
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/simple_quic_framer.h b/quic/test_tools/simple_quic_framer.h
new file mode 100644
index 0000000..2edf9d3
--- /dev/null
+++ b/quic/test_tools/simple_quic_framer.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMPLE_QUIC_FRAMER_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMPLE_QUIC_FRAMER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+
+struct QuicAckFrame;
+
+namespace test {
+
+class SimpleFramerVisitor;
+
+// Peer to make public a number of otherwise private QuicFramer methods.
+class SimpleQuicFramer {
+ public:
+ SimpleQuicFramer();
+ explicit SimpleQuicFramer(const ParsedQuicVersionVector& supported_versions);
+ SimpleQuicFramer(const ParsedQuicVersionVector& supported_versions,
+ Perspective perspective);
+ SimpleQuicFramer(const SimpleQuicFramer&) = delete;
+ SimpleQuicFramer& operator=(const SimpleQuicFramer&) = delete;
+ ~SimpleQuicFramer();
+
+ bool ProcessPacket(const QuicEncryptedPacket& packet);
+ void Reset();
+
+ const QuicPacketHeader& header() const;
+ size_t num_frames() const;
+ const std::vector<QuicAckFrame>& ack_frames() const;
+ const std::vector<QuicConnectionCloseFrame>& connection_close_frames() const;
+ const std::vector<QuicStopWaitingFrame>& stop_waiting_frames() const;
+ const std::vector<QuicPathChallengeFrame>& path_challenge_frames() const;
+ const std::vector<QuicPathResponseFrame>& path_response_frames() const;
+ const std::vector<QuicPingFrame>& ping_frames() const;
+ const std::vector<QuicMessageFrame>& message_frames() const;
+ const std::vector<QuicWindowUpdateFrame>& window_update_frames() const;
+ const std::vector<QuicGoAwayFrame>& goaway_frames() const;
+ const std::vector<QuicRstStreamFrame>& rst_stream_frames() const;
+ const std::vector<std::unique_ptr<QuicStreamFrame>>& stream_frames() const;
+ const std::vector<std::unique_ptr<QuicCryptoFrame>>& crypto_frames() const;
+ const std::vector<QuicPaddingFrame>& padding_frames() const;
+ const QuicVersionNegotiationPacket* version_negotiation_packet() const;
+ EncryptionLevel last_decrypted_level() const;
+
+ QuicFramer* framer();
+
+ void SetSupportedVersions(const ParsedQuicVersionVector& versions) {
+ framer_.SetSupportedVersions(versions);
+ }
+
+ private:
+ QuicFramer framer_;
+ std::unique_ptr<SimpleFramerVisitor> visitor_;
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMPLE_QUIC_FRAMER_H_
diff --git a/quic/test_tools/simple_session_notifier.cc b/quic/test_tools/simple_session_notifier.cc
new file mode 100644
index 0000000..c56831e
--- /dev/null
+++ b/quic/test_tools/simple_session_notifier.cc
@@ -0,0 +1,633 @@
+// Copyright (c) 2018 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 "net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+
+namespace test {
+
+SimpleSessionNotifier::SimpleSessionNotifier(QuicConnection* connection)
+ : last_control_frame_id_(kInvalidControlFrameId),
+ least_unacked_(1),
+ least_unsent_(1),
+ connection_(connection) {}
+
+SimpleSessionNotifier::~SimpleSessionNotifier() {
+ while (!control_frames_.empty()) {
+ DeleteFrame(&control_frames_.front());
+ control_frames_.pop_front();
+ }
+}
+
+SimpleSessionNotifier::StreamState::StreamState()
+ : bytes_total(0),
+ bytes_sent(0),
+ fin_buffered(false),
+ fin_sent(false),
+ fin_outstanding(false),
+ fin_lost(false) {}
+
+SimpleSessionNotifier::StreamState::~StreamState() {}
+
+QuicConsumedData SimpleSessionNotifier::WriteOrBufferData(
+ QuicStreamId id,
+ QuicByteCount data_length,
+ StreamSendingState state) {
+ if (!QuicContainsKey(stream_map_, id)) {
+ stream_map_[id] = StreamState();
+ }
+ StreamState& stream_state = stream_map_.find(id)->second;
+ const bool had_buffered_data =
+ HasBufferedStreamData() || HasBufferedControlFrames();
+ QuicConsumedData total_consumed(0, false);
+ QuicStreamOffset offset = stream_state.bytes_sent;
+ QUIC_DVLOG(1) << "WriteOrBuffer stream_id: " << id << " [" << offset << ", "
+ << offset + data_length << "), fin: " << (state != NO_FIN);
+ stream_state.bytes_total += data_length;
+ stream_state.fin_buffered = state != NO_FIN;
+ if (had_buffered_data) {
+ QUIC_DLOG(WARNING) << "Connection is write blocked";
+ return {0, false};
+ }
+ const size_t length = stream_state.bytes_total - stream_state.bytes_sent;
+ connection_->SetTransmissionType(NOT_RETRANSMISSION);
+ QuicConsumedData consumed =
+ connection_->SendStreamData(id, length, stream_state.bytes_sent,
+ stream_state.fin_buffered ? FIN : NO_FIN);
+ QUIC_DVLOG(1) << "consumed: " << consumed;
+ OnStreamDataConsumed(id, stream_state.bytes_sent, consumed.bytes_consumed,
+ consumed.fin_consumed);
+ return consumed;
+}
+
+void SimpleSessionNotifier::OnStreamDataConsumed(QuicStreamId id,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin) {
+ StreamState& state = stream_map_.find(id)->second;
+ if (id == QuicUtils::GetCryptoStreamId(connection_->transport_version()) &&
+ data_length > 0) {
+ crypto_bytes_transferred_[connection_->encryption_level()].Add(
+ offset, offset + data_length);
+ }
+ state.bytes_sent += data_length;
+ state.fin_sent = fin;
+ state.fin_outstanding = fin;
+}
+
+size_t SimpleSessionNotifier::WriteCryptoData(EncryptionLevel level,
+ QuicByteCount data_length,
+ QuicStreamOffset offset) {
+ crypto_state_[level].bytes_total += data_length;
+ size_t bytes_written =
+ connection_->SendCryptoData(level, data_length, offset);
+ crypto_state_[level].bytes_sent += bytes_written;
+ crypto_bytes_transferred_[level].Add(offset, offset + bytes_written);
+ return bytes_written;
+}
+
+void SimpleSessionNotifier::WriteOrBufferRstStream(
+ QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written) {
+ QUIC_DVLOG(1) << "Writing RST_STREAM_FRAME";
+ const bool had_buffered_data =
+ HasBufferedStreamData() || HasBufferedControlFrames();
+ control_frames_.emplace_back((QuicFrame(new QuicRstStreamFrame(
+ ++last_control_frame_id_, id, error, bytes_written))));
+ if (error != QUIC_STREAM_NO_ERROR) {
+ // Delete stream to avoid retransmissions.
+ stream_map_.erase(id);
+ }
+ if (had_buffered_data) {
+ QUIC_DLOG(WARNING) << "Connection is write blocked";
+ return;
+ }
+ WriteBufferedControlFrames();
+}
+
+void SimpleSessionNotifier::NeuterUnencryptedData() {
+ for (const auto& interval : crypto_bytes_transferred_[ENCRYPTION_NONE]) {
+ // TODO(nharper): Handle CRYPTO frame case.
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()), false,
+ interval.min(), interval.max() - interval.min());
+ OnFrameAcked(QuicFrame(stream_frame), QuicTime::Delta::Zero());
+ }
+}
+
+void SimpleSessionNotifier::OnCanWrite() {
+ if (!RetransmitLostCryptoData() || !RetransmitLostControlFrames() ||
+ !RetransmitLostStreamData()) {
+ return;
+ }
+ // Write buffered control frames.
+ if (!WriteBufferedControlFrames()) {
+ return;
+ }
+ // Write new data.
+ // TODO(nharper): Write CRYPTO frames.
+ for (const auto& pair : stream_map_) {
+ const auto& state = pair.second;
+ if (!StreamHasBufferedData(pair.first)) {
+ continue;
+ }
+
+ const size_t length = state.bytes_total - state.bytes_sent;
+ const bool can_bundle_fin =
+ state.fin_buffered && (state.bytes_sent + length == state.bytes_total);
+ connection_->SetTransmissionType(NOT_RETRANSMISSION);
+ QuicConsumedData consumed = connection_->SendStreamData(
+ pair.first, length, state.bytes_sent, can_bundle_fin ? FIN : NO_FIN);
+ QUIC_DVLOG(1) << "Tries to write stream_id: " << pair.first << " ["
+ << state.bytes_sent << ", " << state.bytes_sent + length
+ << "), fin: " << can_bundle_fin
+ << ", and consumed: " << consumed;
+ OnStreamDataConsumed(pair.first, state.bytes_sent, consumed.bytes_consumed,
+ consumed.fin_consumed);
+ if (length != consumed.bytes_consumed ||
+ (can_bundle_fin && !consumed.fin_consumed)) {
+ break;
+ }
+ }
+}
+
+bool SimpleSessionNotifier::WillingToWrite() const {
+ QUIC_DVLOG(1) << "has_buffered_control_frames: " << HasBufferedControlFrames()
+ << " as_lost_control_frames: " << !lost_control_frames_.empty()
+ << " has_buffered_stream_data: " << HasBufferedStreamData()
+ << " has_lost_stream_data: " << HasLostStreamData();
+ return HasBufferedControlFrames() || !lost_control_frames_.empty() ||
+ HasBufferedStreamData() || HasLostStreamData();
+}
+
+QuicByteCount SimpleSessionNotifier::StreamBytesSent() const {
+ QuicByteCount bytes_sent = 0;
+ for (const auto& pair : stream_map_) {
+ const auto& state = pair.second;
+ bytes_sent += state.bytes_sent;
+ }
+ return bytes_sent;
+}
+
+QuicByteCount SimpleSessionNotifier::StreamBytesToSend() const {
+ QuicByteCount bytes_to_send = 0;
+ for (const auto& pair : stream_map_) {
+ const auto& state = pair.second;
+ bytes_to_send += (state.bytes_total - state.bytes_sent);
+ }
+ return bytes_to_send;
+}
+
+bool SimpleSessionNotifier::OnFrameAcked(const QuicFrame& frame,
+ QuicTime::Delta /*ack_delay_time*/) {
+ QUIC_DVLOG(1) << "Acking " << frame;
+ if (frame.type == CRYPTO_FRAME) {
+ StreamState* state = &crypto_state_[frame.crypto_frame->level];
+ QuicStreamOffset offset = frame.crypto_frame->offset;
+ QuicByteCount data_length = frame.crypto_frame->data_length;
+ QuicIntervalSet<QuicStreamOffset> newly_acked(offset, offset + data_length);
+ newly_acked.Difference(state->bytes_acked);
+ if (newly_acked.Empty()) {
+ return false;
+ }
+ state->bytes_acked.Add(offset, offset + data_length);
+ state->pending_retransmissions.Difference(offset, offset + data_length);
+ return true;
+ }
+ if (frame.type != STREAM_FRAME) {
+ return OnControlFrameAcked(frame);
+ }
+ if (!QuicContainsKey(stream_map_, frame.stream_frame.stream_id)) {
+ return false;
+ }
+ auto* state = &stream_map_.find(frame.stream_frame.stream_id)->second;
+ QuicStreamOffset offset = frame.stream_frame.offset;
+ QuicByteCount data_length = frame.stream_frame.data_length;
+ QuicIntervalSet<QuicStreamOffset> newly_acked(offset, offset + data_length);
+ newly_acked.Difference(state->bytes_acked);
+ const bool fin_newly_acked = frame.stream_frame.fin && state->fin_outstanding;
+ if (newly_acked.Empty() && !fin_newly_acked) {
+ return false;
+ }
+ state->bytes_acked.Add(offset, offset + data_length);
+ if (fin_newly_acked) {
+ state->fin_outstanding = false;
+ state->fin_lost = false;
+ }
+ state->pending_retransmissions.Difference(offset, offset + data_length);
+ return true;
+}
+
+void SimpleSessionNotifier::OnFrameLost(const QuicFrame& frame) {
+ QUIC_DVLOG(1) << "Losting " << frame;
+ if (frame.type == CRYPTO_FRAME) {
+ StreamState* state = &crypto_state_[frame.crypto_frame->level];
+ QuicStreamOffset offset = frame.crypto_frame->offset;
+ QuicByteCount data_length = frame.crypto_frame->data_length;
+ QuicIntervalSet<QuicStreamOffset> bytes_lost(offset, offset + data_length);
+ bytes_lost.Difference(state->bytes_acked);
+ if (bytes_lost.Empty()) {
+ return;
+ }
+ for (const auto& lost : bytes_lost) {
+ state->pending_retransmissions.Add(lost.min(), lost.max());
+ }
+ return;
+ }
+ if (frame.type != STREAM_FRAME) {
+ OnControlFrameLost(frame);
+ return;
+ }
+ if (!QuicContainsKey(stream_map_, frame.stream_frame.stream_id)) {
+ return;
+ }
+ auto* state = &stream_map_.find(frame.stream_frame.stream_id)->second;
+ QuicStreamOffset offset = frame.stream_frame.offset;
+ QuicByteCount data_length = frame.stream_frame.data_length;
+ QuicIntervalSet<QuicStreamOffset> bytes_lost(offset, offset + data_length);
+ bytes_lost.Difference(state->bytes_acked);
+ const bool fin_lost = state->fin_outstanding && frame.stream_frame.fin;
+ if (bytes_lost.Empty() && !fin_lost) {
+ return;
+ }
+ for (const auto& lost : bytes_lost) {
+ state->pending_retransmissions.Add(lost.min(), lost.max());
+ }
+ state->fin_lost = fin_lost;
+}
+
+void SimpleSessionNotifier::RetransmitFrames(const QuicFrames& frames,
+ TransmissionType type) {
+ QuicConnection::ScopedPacketFlusher retransmission_flusher(
+ connection_, QuicConnection::SEND_ACK_IF_QUEUED);
+ connection_->SetTransmissionType(type);
+ for (const QuicFrame& frame : frames) {
+ if (frame.type == CRYPTO_FRAME) {
+ const StreamState& state = crypto_state_[frame.crypto_frame->level];
+ QuicIntervalSet<QuicStreamOffset> retransmission(
+ frame.crypto_frame->offset,
+ frame.crypto_frame->offset + frame.crypto_frame->data_length);
+ retransmission.Difference(state.bytes_acked);
+ for (const auto& interval : retransmission) {
+ QuicStreamOffset offset = interval.min();
+ QuicByteCount length = interval.max() - interval.min();
+ size_t consumed = connection_->SendCryptoData(frame.crypto_frame->level,
+ length, offset);
+ // CRYPTO frames should never be write blocked.
+ DCHECK_EQ(consumed, length);
+ }
+ }
+ if (frame.type != STREAM_FRAME) {
+ if (GetControlFrameId(frame) == kInvalidControlFrameId) {
+ continue;
+ }
+ QuicFrame copy = CopyRetransmittableControlFrame(frame);
+ if (!connection_->SendControlFrame(copy)) {
+ // Connection is write blocked.
+ DeleteFrame(©);
+ return;
+ }
+ continue;
+ }
+ if (!QuicContainsKey(stream_map_, frame.stream_frame.stream_id)) {
+ continue;
+ }
+ const auto& state = stream_map_.find(frame.stream_frame.stream_id)->second;
+ QuicIntervalSet<QuicStreamOffset> retransmission(
+ frame.stream_frame.offset,
+ frame.stream_frame.offset + frame.stream_frame.data_length);
+ EncryptionLevel retransmission_encryption_level =
+ connection_->encryption_level();
+ EncryptionLevel current_encryption_level = connection_->encryption_level();
+ if (frame.stream_frame.stream_id ==
+ QuicUtils::GetCryptoStreamId(connection_->transport_version())) {
+ for (size_t i = 0; i < NUM_ENCRYPTION_LEVELS; ++i) {
+ if (retransmission.Intersects(crypto_bytes_transferred_[i])) {
+ retransmission_encryption_level = static_cast<EncryptionLevel>(i);
+ retransmission.Intersection(crypto_bytes_transferred_[i]);
+ break;
+ }
+ }
+ }
+ retransmission.Difference(state.bytes_acked);
+ bool retransmit_fin = frame.stream_frame.fin && state.fin_outstanding;
+ QuicConsumedData consumed(0, false);
+ for (const auto& interval : retransmission) {
+ QuicStreamOffset retransmission_offset = interval.min();
+ QuicByteCount retransmission_length = interval.max() - interval.min();
+ const bool can_bundle_fin =
+ retransmit_fin &&
+ (retransmission_offset + retransmission_length == state.bytes_sent);
+ if (frame.stream_frame.stream_id ==
+ QuicUtils::GetCryptoStreamId(connection_->transport_version())) {
+ // Set appropriate encryption level for crypto stream.
+ connection_->SetDefaultEncryptionLevel(retransmission_encryption_level);
+ }
+ consumed = connection_->SendStreamData(
+ frame.stream_frame.stream_id, retransmission_length,
+ retransmission_offset, can_bundle_fin ? FIN : NO_FIN);
+ QUIC_DVLOG(1) << "stream " << frame.stream_frame.stream_id
+ << " is forced to retransmit stream data ["
+ << retransmission_offset << ", "
+ << retransmission_offset + retransmission_length
+ << ") and fin: " << can_bundle_fin
+ << ", consumed: " << consumed;
+ if (can_bundle_fin) {
+ retransmit_fin = !consumed.fin_consumed;
+ }
+ if (frame.stream_frame.stream_id ==
+ QuicUtils::GetCryptoStreamId(connection_->transport_version())) {
+ // Restore encryption level.
+ connection_->SetDefaultEncryptionLevel(current_encryption_level);
+ }
+ if (consumed.bytes_consumed < retransmission_length ||
+ (can_bundle_fin && !consumed.fin_consumed)) {
+ // Connection is write blocked.
+ return;
+ }
+ }
+ if (retransmit_fin) {
+ QUIC_DVLOG(1) << "stream " << frame.stream_frame.stream_id
+ << " retransmits fin only frame.";
+ consumed = connection_->SendStreamData(frame.stream_frame.stream_id, 0,
+ state.bytes_sent, FIN);
+ }
+ }
+}
+
+bool SimpleSessionNotifier::IsFrameOutstanding(const QuicFrame& frame) const {
+ if (frame.type == CRYPTO_FRAME) {
+ QuicStreamOffset offset = frame.crypto_frame->offset;
+ QuicByteCount data_length = frame.crypto_frame->data_length;
+ bool ret = data_length > 0 &&
+ !crypto_state_[frame.crypto_frame->level].bytes_acked.Contains(
+ offset, offset + data_length);
+ return ret;
+ }
+ if (frame.type != STREAM_FRAME) {
+ return IsControlFrameOutstanding(frame);
+ }
+ if (!QuicContainsKey(stream_map_, frame.stream_frame.stream_id)) {
+ return false;
+ }
+ const auto& state = stream_map_.find(frame.stream_frame.stream_id)->second;
+ QuicStreamOffset offset = frame.stream_frame.offset;
+ QuicByteCount data_length = frame.stream_frame.data_length;
+ return (data_length > 0 &&
+ !state.bytes_acked.Contains(offset, offset + data_length)) ||
+ (frame.stream_frame.fin && state.fin_outstanding);
+}
+
+bool SimpleSessionNotifier::HasUnackedCryptoData() const {
+ if (connection_->transport_version() >= QUIC_VERSION_47) {
+ for (size_t i = 0; i < NUM_ENCRYPTION_LEVELS; ++i) {
+ const StreamState& state = crypto_state_[i];
+ if (state.bytes_total > state.bytes_sent) {
+ return true;
+ }
+ QuicIntervalSet<QuicStreamOffset> bytes_to_ack(0, state.bytes_total);
+ bytes_to_ack.Difference(state.bytes_acked);
+ if (!bytes_to_ack.Empty()) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (!QuicContainsKey(stream_map_, QuicUtils::GetCryptoStreamId(
+ connection_->transport_version()))) {
+ return false;
+ }
+ const auto& state =
+ stream_map_
+ .find(QuicUtils::GetCryptoStreamId(connection_->transport_version()))
+ ->second;
+ if (state.bytes_total > state.bytes_sent) {
+ return true;
+ }
+ QuicIntervalSet<QuicStreamOffset> bytes_to_ack(0, state.bytes_total);
+ bytes_to_ack.Difference(state.bytes_acked);
+ return !bytes_to_ack.Empty();
+}
+
+bool SimpleSessionNotifier::OnControlFrameAcked(const QuicFrame& frame) {
+ QuicControlFrameId id = GetControlFrameId(frame);
+ if (id == kInvalidControlFrameId) {
+ return false;
+ }
+ DCHECK(id < least_unacked_ + control_frames_.size());
+ if (id < least_unacked_ ||
+ GetControlFrameId(control_frames_.at(id - least_unacked_)) ==
+ kInvalidControlFrameId) {
+ return false;
+ }
+ SetControlFrameId(kInvalidControlFrameId,
+ &control_frames_.at(id - least_unacked_));
+ lost_control_frames_.erase(id);
+ while (!control_frames_.empty() &&
+ GetControlFrameId(control_frames_.front()) == kInvalidControlFrameId) {
+ DeleteFrame(&control_frames_.front());
+ control_frames_.pop_front();
+ ++least_unacked_;
+ }
+ return true;
+}
+
+void SimpleSessionNotifier::OnControlFrameLost(const QuicFrame& frame) {
+ QuicControlFrameId id = GetControlFrameId(frame);
+ if (id == kInvalidControlFrameId) {
+ return;
+ }
+ DCHECK(id < least_unacked_ + control_frames_.size());
+ if (id < least_unacked_ ||
+ GetControlFrameId(control_frames_.at(id - least_unacked_)) ==
+ kInvalidControlFrameId) {
+ return;
+ }
+ if (!QuicContainsKey(lost_control_frames_, id)) {
+ lost_control_frames_[id] = true;
+ }
+}
+
+bool SimpleSessionNotifier::IsControlFrameOutstanding(
+ const QuicFrame& frame) const {
+ QuicControlFrameId id = GetControlFrameId(frame);
+ if (id == kInvalidControlFrameId) {
+ return false;
+ }
+ return id < least_unacked_ + control_frames_.size() && id >= least_unacked_ &&
+ GetControlFrameId(control_frames_.at(id - least_unacked_)) !=
+ kInvalidControlFrameId;
+}
+
+bool SimpleSessionNotifier::RetransmitLostControlFrames() {
+ while (!lost_control_frames_.empty()) {
+ QuicFrame pending = control_frames_.at(lost_control_frames_.begin()->first -
+ least_unacked_);
+ QuicFrame copy = CopyRetransmittableControlFrame(pending);
+ connection_->SetTransmissionType(LOSS_RETRANSMISSION);
+ if (!connection_->SendControlFrame(copy)) {
+ // Connection is write blocked.
+ DeleteFrame(©);
+ break;
+ }
+ lost_control_frames_.pop_front();
+ }
+ return lost_control_frames_.empty();
+}
+
+bool SimpleSessionNotifier::RetransmitLostCryptoData() {
+ // TODO(nharper): Handle CRYPTO frame case.
+ if (!QuicContainsKey(stream_map_, QuicUtils::GetCryptoStreamId(
+ connection_->transport_version()))) {
+ return true;
+ }
+ auto& state =
+ stream_map_
+ .find(QuicUtils::GetCryptoStreamId(connection_->transport_version()))
+ ->second;
+ while (!state.pending_retransmissions.Empty()) {
+ connection_->SetTransmissionType(HANDSHAKE_RETRANSMISSION);
+ QuicIntervalSet<QuicStreamOffset> retransmission(
+ state.pending_retransmissions.begin()->min(),
+ state.pending_retransmissions.begin()->max());
+ EncryptionLevel retransmission_encryption_level = ENCRYPTION_NONE;
+ for (size_t i = 0; i < NUM_ENCRYPTION_LEVELS; ++i) {
+ if (retransmission.Intersects(crypto_bytes_transferred_[i])) {
+ retransmission_encryption_level = static_cast<EncryptionLevel>(i);
+ retransmission.Intersection(crypto_bytes_transferred_[i]);
+ break;
+ }
+ }
+ QuicStreamOffset retransmission_offset = retransmission.begin()->min();
+ QuicByteCount retransmission_length =
+ retransmission.begin()->max() - retransmission.begin()->min();
+ EncryptionLevel current_encryption_level = connection_->encryption_level();
+ // Set appropriate encryption level.
+ connection_->SetDefaultEncryptionLevel(retransmission_encryption_level);
+ QuicConsumedData consumed = connection_->SendStreamData(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ retransmission_length, retransmission_offset, NO_FIN);
+ // Restore encryption level.
+ connection_->SetDefaultEncryptionLevel(current_encryption_level);
+ state.pending_retransmissions.Difference(
+ retransmission_offset, retransmission_offset + consumed.bytes_consumed);
+ if (consumed.bytes_consumed < retransmission_length) {
+ break;
+ }
+ }
+ return state.pending_retransmissions.Empty();
+}
+
+bool SimpleSessionNotifier::RetransmitLostStreamData() {
+ for (auto& pair : stream_map_) {
+ StreamState& state = pair.second;
+ QuicConsumedData consumed(0, false);
+ while (!state.pending_retransmissions.Empty() || state.fin_lost) {
+ connection_->SetTransmissionType(LOSS_RETRANSMISSION);
+ if (state.pending_retransmissions.Empty()) {
+ QUIC_DVLOG(1) << "stream " << pair.first
+ << " retransmits fin only frame.";
+ consumed =
+ connection_->SendStreamData(pair.first, 0, state.bytes_sent, FIN);
+ state.fin_lost = !consumed.fin_consumed;
+ if (state.fin_lost) {
+ DLOG(INFO) << "Connection is write blocked";
+ return false;
+ }
+ } else {
+ QuicStreamOffset offset = state.pending_retransmissions.begin()->min();
+ QuicByteCount length = state.pending_retransmissions.begin()->max() -
+ state.pending_retransmissions.begin()->min();
+ const bool can_bundle_fin =
+ state.fin_lost && (offset + length == state.bytes_sent);
+ consumed = connection_->SendStreamData(pair.first, length, offset,
+ can_bundle_fin ? FIN : NO_FIN);
+ QUIC_DVLOG(1) << "stream " << pair.first
+ << " tries to retransmit stream data [" << offset << ", "
+ << offset + length << ") and fin: " << can_bundle_fin
+ << ", consumed: " << consumed;
+ state.pending_retransmissions.Difference(
+ offset, offset + consumed.bytes_consumed);
+ if (consumed.fin_consumed) {
+ state.fin_lost = false;
+ }
+ if (length > consumed.bytes_consumed ||
+ (can_bundle_fin && !consumed.fin_consumed)) {
+ DVLOG(1) << "Connection is write blocked";
+ break;
+ }
+ }
+ }
+ }
+ return !HasLostStreamData();
+}
+
+bool SimpleSessionNotifier::WriteBufferedControlFrames() {
+ while (HasBufferedControlFrames()) {
+ QuicFrame frame_to_send =
+ control_frames_.at(least_unsent_ - least_unacked_);
+ QuicFrame copy = CopyRetransmittableControlFrame(frame_to_send);
+ connection_->SetTransmissionType(NOT_RETRANSMISSION);
+ if (!connection_->SendControlFrame(copy)) {
+ // Connection is write blocked.
+ DeleteFrame(©);
+ break;
+ }
+ ++least_unsent_;
+ }
+ return !HasBufferedControlFrames();
+}
+
+bool SimpleSessionNotifier::HasBufferedControlFrames() const {
+ return least_unsent_ < least_unacked_ + control_frames_.size();
+}
+
+bool SimpleSessionNotifier::HasBufferedStreamData() const {
+ for (const auto& pair : stream_map_) {
+ const auto& state = pair.second;
+ if (state.bytes_total > state.bytes_sent ||
+ (state.fin_buffered && !state.fin_sent)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SimpleSessionNotifier::StreamIsWaitingForAcks(QuicStreamId id) const {
+ if (!QuicContainsKey(stream_map_, id)) {
+ return false;
+ }
+ const StreamState& state = stream_map_.find(id)->second;
+ return !state.bytes_acked.Contains(0, state.bytes_sent) ||
+ state.fin_outstanding;
+}
+
+bool SimpleSessionNotifier::StreamHasBufferedData(QuicStreamId id) const {
+ if (!QuicContainsKey(stream_map_, id)) {
+ return false;
+ }
+ const StreamState& state = stream_map_.find(id)->second;
+ return state.bytes_total > state.bytes_sent ||
+ (state.fin_buffered && !state.fin_sent);
+}
+
+bool SimpleSessionNotifier::HasLostStreamData() const {
+ for (const auto& pair : stream_map_) {
+ const auto& state = pair.second;
+ if (!state.pending_retransmissions.Empty() || state.fin_lost) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace test
+
+} // namespace quic
diff --git a/quic/test_tools/simple_session_notifier.h b/quic/test_tools/simple_session_notifier.h
new file mode 100644
index 0000000..f7982d2
--- /dev/null
+++ b/quic/test_tools/simple_session_notifier.h
@@ -0,0 +1,151 @@
+// Copyright (c) 2018 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMPLE_SESSION_NOTIFIER_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMPLE_SESSION_NOTIFIER_H_
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval_set.h"
+#include "net/third_party/quiche/src/quic/core/session_notifier_interface.h"
+
+namespace quic {
+
+class QuicConnection;
+
+namespace test {
+
+// SimpleSessionNotifier implements the basic functionalities of a session, and
+// it manages stream data and control frames.
+class SimpleSessionNotifier : public SessionNotifierInterface {
+ public:
+ explicit SimpleSessionNotifier(QuicConnection* connection);
+ ~SimpleSessionNotifier() override;
+
+ // Tries to write stream data and returns data consumed.
+ QuicConsumedData WriteOrBufferData(QuicStreamId id,
+ QuicByteCount data_length,
+ StreamSendingState state);
+
+ // Tries to write RST_STREAM_FRAME.
+ void WriteOrBufferRstStream(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written);
+
+ // Tries to write CRYPTO data and returns the number of bytes written.
+ size_t WriteCryptoData(EncryptionLevel level,
+ QuicByteCount data_length,
+ QuicStreamOffset offset);
+
+ // Neuters unencrypted data of crypto stream.
+ void NeuterUnencryptedData();
+
+ // Called when connection_ becomes writable.
+ void OnCanWrite();
+
+ // Returns true if there are 1) unsent control frames and stream data, or 2)
+ // lost control frames and stream data.
+ bool WillingToWrite() const;
+
+ // Number of sent stream bytes. Please note, this does not count
+ // retransmissions.
+ QuicByteCount StreamBytesSent() const;
+
+ // Number of stream bytes waiting to be sent for the first time.
+ QuicByteCount StreamBytesToSend() const;
+
+ // Returns true if there is any stream data waiting to be sent for the first
+ // time.
+ bool HasBufferedStreamData() const;
+
+ // Returns true if stream |id| has any outstanding data.
+ bool StreamIsWaitingForAcks(QuicStreamId id) const;
+
+ // SessionNotifierInterface methods:
+ bool OnFrameAcked(const QuicFrame& frame,
+ QuicTime::Delta ack_delay_time) override;
+ void OnStreamFrameRetransmitted(const QuicStreamFrame& frame) override {}
+ void OnFrameLost(const QuicFrame& frame) override;
+ void RetransmitFrames(const QuicFrames& frames,
+ TransmissionType type) override;
+ bool IsFrameOutstanding(const QuicFrame& frame) const override;
+ bool HasUnackedCryptoData() const override;
+
+ private:
+ struct StreamState {
+ StreamState();
+ ~StreamState();
+
+ // Total number of bytes.
+ QuicByteCount bytes_total;
+ // Number of sent bytes.
+ QuicByteCount bytes_sent;
+ // Record of acked offsets.
+ QuicIntervalSet<QuicStreamOffset> bytes_acked;
+ // Data considered as lost and needs to be retransmitted.
+ QuicIntervalSet<QuicStreamOffset> pending_retransmissions;
+
+ bool fin_buffered;
+ bool fin_sent;
+ bool fin_outstanding;
+ bool fin_lost;
+ };
+
+ friend std::ostream& operator<<(std::ostream& os, const StreamState& s);
+
+ using StreamMap = QuicUnorderedMap<QuicStreamId, StreamState>;
+
+ void OnStreamDataConsumed(QuicStreamId id,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin);
+
+ bool OnControlFrameAcked(const QuicFrame& frame);
+
+ void OnControlFrameLost(const QuicFrame& frame);
+
+ bool RetransmitLostControlFrames();
+
+ bool RetransmitLostCryptoData();
+
+ bool RetransmitLostStreamData();
+
+ bool WriteBufferedControlFrames();
+
+ bool IsControlFrameOutstanding(const QuicFrame& frame) const;
+
+ bool HasBufferedControlFrames() const;
+
+ bool HasLostStreamData() const;
+
+ bool StreamHasBufferedData(QuicStreamId id) const;
+
+ QuicDeque<QuicFrame> control_frames_;
+
+ QuicLinkedHashMap<QuicControlFrameId, bool> lost_control_frames_;
+
+ // Id of latest saved control frame. 0 if no control frame has been saved.
+ QuicControlFrameId last_control_frame_id_;
+
+ // The control frame at the 0th index of control_frames_.
+ QuicControlFrameId least_unacked_;
+
+ // ID of the least unsent control frame.
+ QuicControlFrameId least_unsent_;
+
+ StreamMap stream_map_;
+
+ // Transferred crypto bytes according to encryption levels.
+ QuicIntervalSet<QuicStreamOffset>
+ crypto_bytes_transferred_[NUM_ENCRYPTION_LEVELS];
+
+ StreamState crypto_state_[NUM_ENCRYPTION_LEVELS];
+
+ QuicConnection* connection_;
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMPLE_SESSION_NOTIFIER_H_
diff --git a/quic/test_tools/simple_session_notifier_test.cc b/quic/test_tools/simple_session_notifier_test.cc
new file mode 100644
index 0000000..b20fee6
--- /dev/null
+++ b/quic/test_tools/simple_session_notifier_test.cc
@@ -0,0 +1,246 @@
+// Copyright (c) 2018 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 "net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+using testing::_;
+using testing::InSequence;
+using testing::Return;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+class MockQuicConnectionWithSendStreamData : public MockQuicConnection {
+ public:
+ MockQuicConnectionWithSendStreamData(MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective)
+ : MockQuicConnection(helper, alarm_factory, perspective) {}
+
+ MOCK_METHOD4(SendStreamData,
+ QuicConsumedData(QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state));
+};
+
+class SimpleSessionNotifierTest : public QuicTest {
+ public:
+ SimpleSessionNotifierTest()
+ : connection_(&helper_, &alarm_factory_, Perspective::IS_CLIENT),
+ notifier_(&connection_) {
+ connection_.set_visitor(&visitor_);
+ QuicConnectionPeer::SetSessionDecidesWhatToWrite(&connection_);
+ EXPECT_FALSE(notifier_.WillingToWrite());
+ EXPECT_EQ(0u, notifier_.StreamBytesSent());
+ EXPECT_FALSE(notifier_.HasBufferedStreamData());
+ }
+
+ bool ControlFrameConsumed(const QuicFrame& frame) {
+ DeleteFrame(&const_cast<QuicFrame&>(frame));
+ return true;
+ }
+
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ MockQuicConnectionVisitor visitor_;
+ StrictMock<MockQuicConnectionWithSendStreamData> connection_;
+ SimpleSessionNotifier notifier_;
+};
+
+TEST_F(SimpleSessionNotifierTest, WriteOrBufferData) {
+ InSequence s;
+ EXPECT_CALL(connection_, SendStreamData(3, 1024, 0, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(1024, false)));
+ notifier_.WriteOrBufferData(3, 1024, NO_FIN);
+ EXPECT_EQ(0u, notifier_.StreamBytesToSend());
+ EXPECT_CALL(connection_, SendStreamData(5, 512, 0, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(512, false)));
+ notifier_.WriteOrBufferData(5, 512, NO_FIN);
+ EXPECT_FALSE(notifier_.WillingToWrite());
+ // Connection is blocked.
+ EXPECT_CALL(connection_, SendStreamData(5, 512, 512, FIN))
+ .WillOnce(Return(QuicConsumedData(256, false)));
+ notifier_.WriteOrBufferData(5, 512, FIN);
+ EXPECT_TRUE(notifier_.WillingToWrite());
+ EXPECT_EQ(1792u, notifier_.StreamBytesSent());
+ EXPECT_EQ(256u, notifier_.StreamBytesToSend());
+ EXPECT_TRUE(notifier_.HasBufferedStreamData());
+
+ // New data cannot be sent as connection is blocked.
+ EXPECT_CALL(connection_, SendStreamData(7, 1024, 0, FIN)).Times(0);
+ notifier_.WriteOrBufferData(7, 1024, FIN);
+ EXPECT_EQ(1792u, notifier_.StreamBytesSent());
+}
+
+TEST_F(SimpleSessionNotifierTest, WriteOrBufferRstStream) {
+ InSequence s;
+ EXPECT_CALL(connection_, SendStreamData(5, 1024, 0, FIN))
+ .WillOnce(Return(QuicConsumedData(1024, true)));
+ notifier_.WriteOrBufferData(5, 1024, FIN);
+
+ // Reset stream 5 with no error.
+ EXPECT_CALL(connection_, SendControlFrame(_))
+ .WillRepeatedly(
+ Invoke(this, &SimpleSessionNotifierTest::ControlFrameConsumed));
+ notifier_.WriteOrBufferRstStream(5, QUIC_STREAM_NO_ERROR, 1024);
+ // Verify stream 5 is waiting for acks.
+ EXPECT_TRUE(notifier_.StreamIsWaitingForAcks(5));
+
+ // Reset stream 5 with error.
+ notifier_.WriteOrBufferRstStream(5, QUIC_ERROR_PROCESSING_STREAM, 1024);
+ EXPECT_FALSE(notifier_.StreamIsWaitingForAcks(5));
+}
+
+TEST_F(SimpleSessionNotifierTest, NeuterUnencryptedData) {
+ InSequence s;
+ // Send crypto data [0, 1024) in ENCRYPTION_NONE.
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_NONE);
+ EXPECT_CALL(connection_, SendStreamData(QuicUtils::GetCryptoStreamId(
+ connection_.transport_version()),
+ 1024, 0, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(1024, false)));
+ notifier_.WriteOrBufferData(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), 1024,
+ NO_FIN);
+ // Send crypto data [1024, 2048) in ENCRYPTION_ZERO_RTT.
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+ EXPECT_CALL(connection_, SendStreamData(QuicUtils::GetCryptoStreamId(
+ connection_.transport_version()),
+ 1024, 1024, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(1024, false)));
+ notifier_.WriteOrBufferData(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), 1024,
+ NO_FIN);
+ // Ack [1024, 2048).
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), false,
+ 1024, 1024);
+ notifier_.OnFrameAcked(QuicFrame(stream_frame), QuicTime::Delta::Zero());
+ EXPECT_TRUE(notifier_.StreamIsWaitingForAcks(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version())));
+ // Neuters unencrypted data.
+ notifier_.NeuterUnencryptedData();
+ EXPECT_FALSE(notifier_.StreamIsWaitingForAcks(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version())));
+}
+
+TEST_F(SimpleSessionNotifierTest, OnCanWrite) {
+ InSequence s;
+ // Send crypto data [0, 1024) in ENCRYPTION_NONE.
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_NONE);
+ EXPECT_CALL(connection_, SendStreamData(QuicUtils::GetCryptoStreamId(
+ connection_.transport_version()),
+ 1024, 0, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(1024, false)));
+ notifier_.WriteOrBufferData(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), 1024,
+ NO_FIN);
+ // Send crypto data [1024, 2048) in ENCRYPTION_ZERO_RTT.
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+ EXPECT_CALL(connection_, SendStreamData(QuicUtils::GetCryptoStreamId(
+ connection_.transport_version()),
+ 1024, 1024, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(1024, false)));
+ notifier_.WriteOrBufferData(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), 1024,
+ NO_FIN);
+ // Send stream 3 [0, 1024) and connection is blocked.
+ EXPECT_CALL(connection_, SendStreamData(3, 1024, 0, FIN))
+ .WillOnce(Return(QuicConsumedData(512, false)));
+ notifier_.WriteOrBufferData(3, 1024, FIN);
+ // Send stream 5 [0, 1024).
+ EXPECT_CALL(connection_, SendStreamData(5, _, _, _)).Times(0);
+ notifier_.WriteOrBufferData(5, 1024, NO_FIN);
+ // Reset stream 5 with error.
+ EXPECT_CALL(connection_, SendControlFrame(_)).Times(0);
+ notifier_.WriteOrBufferRstStream(5, QUIC_ERROR_PROCESSING_STREAM, 1024);
+
+ // Lost crypto data [500, 1500) and stream 3 [0, 512).
+ QuicStreamFrame frame1(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 500,
+ 1000);
+ QuicStreamFrame frame2(3, false, 0, 512);
+ notifier_.OnFrameLost(QuicFrame(frame1));
+ notifier_.OnFrameLost(QuicFrame(frame2));
+
+ // Connection becomes writable.
+ // Lost crypto data gets retransmitted as [500, 1024) and [1024, 1500), as
+ // they are in different encryption levels.
+ EXPECT_CALL(connection_, SendStreamData(QuicUtils::GetCryptoStreamId(
+ connection_.transport_version()),
+ 524, 500, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(524, false)));
+ EXPECT_CALL(connection_, SendStreamData(QuicUtils::GetCryptoStreamId(
+ connection_.transport_version()),
+ 476, 1024, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(476, false)));
+ // Lost stream 3 data gets retransmitted.
+ EXPECT_CALL(connection_, SendStreamData(3, 512, 0, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(512, false)));
+ // Buffered control frames get sent.
+ EXPECT_CALL(connection_, SendControlFrame(_))
+ .WillOnce(Invoke(this, &SimpleSessionNotifierTest::ControlFrameConsumed));
+ // Buffered stream 3 data [512, 1024) gets sent.
+ EXPECT_CALL(connection_, SendStreamData(3, 512, 512, FIN))
+ .WillOnce(Return(QuicConsumedData(512, true)));
+ notifier_.OnCanWrite();
+ EXPECT_FALSE(notifier_.WillingToWrite());
+}
+
+TEST_F(SimpleSessionNotifierTest, RetransmitFrames) {
+ InSequence s;
+ // Send stream 3 data [0, 10) and fin.
+ EXPECT_CALL(connection_, SendStreamData(3, 10, 0, FIN))
+ .WillOnce(Return(QuicConsumedData(10, true)));
+ notifier_.WriteOrBufferData(3, 10, FIN);
+ QuicStreamFrame frame1(3, true, 0, 10);
+ // Send stream 5 [0, 10) and fin.
+ EXPECT_CALL(connection_, SendStreamData(5, 10, 0, FIN))
+ .WillOnce(Return(QuicConsumedData(10, true)));
+ notifier_.WriteOrBufferData(5, 10, FIN);
+ QuicStreamFrame frame2(5, true, 0, 10);
+ // Reset stream 5 with no error.
+ EXPECT_CALL(connection_, SendControlFrame(_))
+ .WillOnce(Invoke(this, &SimpleSessionNotifierTest::ControlFrameConsumed));
+ notifier_.WriteOrBufferRstStream(5, QUIC_STREAM_NO_ERROR, 10);
+
+ // Ack stream 3 [3, 7), and stream 5 [8, 10).
+ QuicStreamFrame ack_frame1(3, false, 3, 4);
+ QuicStreamFrame ack_frame2(5, false, 8, 2);
+ notifier_.OnFrameAcked(QuicFrame(ack_frame1), QuicTime::Delta::Zero());
+ notifier_.OnFrameAcked(QuicFrame(ack_frame2), QuicTime::Delta::Zero());
+ EXPECT_FALSE(notifier_.WillingToWrite());
+
+ // Force to send.
+ QuicRstStreamFrame rst_stream(1, 5, QUIC_STREAM_NO_ERROR, 10);
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame2));
+ frames.push_back(QuicFrame(&rst_stream));
+ frames.push_back(QuicFrame(frame1));
+ // stream 5 data [0, 8), fin only are retransmitted.
+ EXPECT_CALL(connection_, SendStreamData(5, 8, 0, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(8, false)));
+ EXPECT_CALL(connection_, SendStreamData(5, 0, 10, FIN))
+ .WillOnce(Return(QuicConsumedData(0, true)));
+ // rst_stream is retransmitted.
+ EXPECT_CALL(connection_, SendControlFrame(_))
+ .WillOnce(Invoke(this, &SimpleSessionNotifierTest::ControlFrameConsumed));
+ // stream 3 data [0, 3) is retransmitted and connection is blocked.
+ EXPECT_CALL(connection_, SendStreamData(3, 3, 0, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(2, false)));
+ notifier_.RetransmitFrames(frames, RTO_RETRANSMISSION);
+ EXPECT_FALSE(notifier_.WillingToWrite());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/simulator/actor.cc b/quic/test_tools/simulator/actor.cc
new file mode 100644
index 0000000..546875d
--- /dev/null
+++ b/quic/test_tools/simulator/actor.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2012 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 "net/third_party/quiche/src/quic/test_tools/simulator/actor.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+
+namespace quic {
+namespace simulator {
+
+Actor::Actor(Simulator* simulator, QuicString name)
+ : simulator_(simulator),
+ clock_(simulator->GetClock()),
+ name_(std::move(name)) {
+ simulator_->AddActor(this);
+}
+
+Actor::~Actor() {
+ simulator_->RemoveActor(this);
+}
+
+void Actor::Schedule(QuicTime next_tick) {
+ simulator_->Schedule(this, next_tick);
+}
+
+void Actor::Unschedule() {
+ simulator_->Unschedule(this);
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/quic/test_tools/simulator/actor.h b/quic/test_tools/simulator/actor.h
new file mode 100644
index 0000000..0c24913
--- /dev/null
+++ b/quic/test_tools/simulator/actor.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMULATOR_ACTOR_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_ACTOR_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+
+namespace quic {
+namespace simulator {
+
+class Simulator;
+
+// Actor is the base class for all participants of the simulation which can
+// schedule events to be triggered at the specified time. Every actor has a
+// name assigned to it, which can be used for debugging and addressing purposes.
+//
+// The Actor object is scheduled as follows:
+// 1. Every Actor object appears at most once in the event queue, for one
+// specific time.
+// 2. Actor is scheduled by calling Schedule() method.
+// 3. If Schedule() method is called with multiple different times specified,
+// Act() method will be called at the earliest time specified.
+// 4. Before Act() is called, the Actor is removed from the event queue. Act()
+// will not be called again unless Schedule() is called.
+class Actor {
+ public:
+ Actor(Simulator* simulator, QuicString name);
+ virtual ~Actor();
+
+ // Trigger all the events the actor can potentially handle at this point.
+ // Before Act() is called, the actor is removed from the event queue, and has
+ // to schedule the next call manually.
+ virtual void Act() = 0;
+
+ inline QuicString name() const { return name_; }
+ inline Simulator* simulator() const { return simulator_; }
+
+ protected:
+ // Calls Schedule() on the associated simulator.
+ void Schedule(QuicTime next_tick);
+
+ // Calls Unschedule() on the associated simulator.
+ void Unschedule();
+
+ Simulator* simulator_;
+ const QuicClock* clock_;
+ QuicString name_;
+
+ private:
+ // Since the Actor object registers itself with a simulator using a pointer to
+ // itself, do not allow it to be moved.
+ Actor(Actor&&) = delete;
+ Actor(const Actor&) = delete;
+ Actor& operator=(const Actor&) = delete;
+ Actor& operator=(Actor&&) = delete;
+};
+
+} // namespace simulator
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMULATOR_ACTOR_H_
diff --git a/quic/test_tools/simulator/alarm_factory.cc b/quic/test_tools/simulator/alarm_factory.cc
new file mode 100644
index 0000000..736f9ea
--- /dev/null
+++ b/quic/test_tools/simulator/alarm_factory.cc
@@ -0,0 +1,81 @@
+// Copyright (c) 2012 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 "net/third_party/quiche/src/quic/test_tools/simulator/alarm_factory.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_alarm.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+
+namespace quic {
+namespace simulator {
+
+// Alarm is an implementation of QuicAlarm which can schedule alarms in the
+// simulation timeline.
+class Alarm : public QuicAlarm {
+ public:
+ Alarm(Simulator* simulator,
+ QuicString name,
+ QuicArenaScopedPtr<QuicAlarm::Delegate> delegate)
+ : QuicAlarm(std::move(delegate)), adapter_(simulator, name, this) {}
+ ~Alarm() override {}
+
+ void SetImpl() override {
+ DCHECK(deadline().IsInitialized());
+ adapter_.Set(deadline());
+ }
+
+ void CancelImpl() override { adapter_.Cancel(); }
+
+ private:
+ // An adapter class triggering a QuicAlarm using a simulation time system.
+ // An adapter is required here because neither Actor nor QuicAlarm are pure
+ // interfaces.
+ class Adapter : public Actor {
+ public:
+ Adapter(Simulator* simulator, QuicString name, Alarm* parent)
+ : Actor(simulator, name), parent_(parent) {}
+ ~Adapter() override {}
+
+ void Set(QuicTime time) { Schedule(std::max(time, clock_->Now())); }
+ void Cancel() { Unschedule(); }
+
+ void Act() override {
+ DCHECK(clock_->Now() >= parent_->deadline());
+ parent_->Fire();
+ }
+
+ private:
+ Alarm* parent_;
+ };
+ Adapter adapter_;
+};
+
+AlarmFactory::AlarmFactory(Simulator* simulator, QuicString name)
+ : simulator_(simulator), name_(std::move(name)), counter_(0) {}
+
+AlarmFactory::~AlarmFactory() {}
+
+QuicString AlarmFactory::GetNewAlarmName() {
+ ++counter_;
+ return QuicStringPrintf("%s (alarm %i)", name_.c_str(), counter_);
+}
+
+QuicAlarm* AlarmFactory::CreateAlarm(QuicAlarm::Delegate* delegate) {
+ return new Alarm(simulator_, GetNewAlarmName(),
+ QuicArenaScopedPtr<QuicAlarm::Delegate>(delegate));
+}
+
+QuicArenaScopedPtr<QuicAlarm> AlarmFactory::CreateAlarm(
+ QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
+ QuicConnectionArena* arena) {
+ if (arena != nullptr) {
+ return arena->New<Alarm>(simulator_, GetNewAlarmName(),
+ std::move(delegate));
+ }
+ return QuicArenaScopedPtr<QuicAlarm>(
+ new Alarm(simulator_, GetNewAlarmName(), std::move(delegate)));
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/quic/test_tools/simulator/alarm_factory.h b/quic/test_tools/simulator/alarm_factory.h
new file mode 100644
index 0000000..535095a
--- /dev/null
+++ b/quic/test_tools/simulator/alarm_factory.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMULATOR_ALARM_FACTORY_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_ALARM_FACTORY_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/actor.h"
+
+namespace quic {
+namespace simulator {
+
+// AlarmFactory allows to schedule QuicAlarms using the simulation event queue.
+class AlarmFactory : public QuicAlarmFactory {
+ public:
+ AlarmFactory(Simulator* simulator, QuicString name);
+ AlarmFactory(const AlarmFactory&) = delete;
+ AlarmFactory& operator=(const AlarmFactory&) = delete;
+ ~AlarmFactory() override;
+
+ QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) override;
+ QuicArenaScopedPtr<QuicAlarm> CreateAlarm(
+ QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
+ QuicConnectionArena* arena) override;
+
+ private:
+ // Automatically generate a name for a new alarm.
+ QuicString GetNewAlarmName();
+
+ Simulator* simulator_;
+ QuicString name_;
+ int counter_;
+};
+
+} // namespace simulator
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMULATOR_ALARM_FACTORY_H_
diff --git a/quic/test_tools/simulator/link.cc b/quic/test_tools/simulator/link.cc
new file mode 100644
index 0000000..879de26
--- /dev/null
+++ b/quic/test_tools/simulator/link.cc
@@ -0,0 +1,121 @@
+// Copyright (c) 2012 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 "net/third_party/quiche/src/quic/test_tools/simulator/link.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+
+namespace quic {
+namespace simulator {
+
+// Parameters for random noise delay.
+const uint64_t kMaxRandomDelayUs = 10;
+
+OneWayLink::OneWayLink(Simulator* simulator,
+ QuicString name,
+ UnconstrainedPortInterface* sink,
+ QuicBandwidth bandwidth,
+ QuicTime::Delta propagation_delay)
+ : Actor(simulator, name),
+ sink_(sink),
+ bandwidth_(bandwidth),
+ propagation_delay_(propagation_delay),
+ next_write_at_(QuicTime::Zero()) {}
+
+OneWayLink::~OneWayLink() {}
+
+OneWayLink::QueuedPacket::QueuedPacket(std::unique_ptr<Packet> packet,
+ QuicTime dequeue_time)
+ : packet(std::move(packet)), dequeue_time(dequeue_time) {}
+
+OneWayLink::QueuedPacket::QueuedPacket(QueuedPacket&& other) = default;
+
+OneWayLink::QueuedPacket::~QueuedPacket() {}
+
+void OneWayLink::AcceptPacket(std::unique_ptr<Packet> packet) {
+ DCHECK(TimeUntilAvailable().IsZero());
+ QuicTime::Delta transfer_time = bandwidth_.TransferTime(packet->size);
+ next_write_at_ = clock_->Now() + transfer_time;
+
+ packets_in_transit_.emplace(
+ std::move(packet),
+ next_write_at_ + propagation_delay_ + GetRandomDelay(transfer_time));
+ ScheduleNextPacketDeparture();
+}
+
+QuicTime::Delta OneWayLink::TimeUntilAvailable() {
+ const QuicTime now = clock_->Now();
+ if (next_write_at_ <= now) {
+ return QuicTime::Delta::Zero();
+ }
+
+ return next_write_at_ - now;
+}
+
+void OneWayLink::Act() {
+ DCHECK(!packets_in_transit_.empty());
+ DCHECK(packets_in_transit_.front().dequeue_time >= clock_->Now());
+
+ sink_->AcceptPacket(std::move(packets_in_transit_.front().packet));
+ packets_in_transit_.pop();
+
+ ScheduleNextPacketDeparture();
+}
+
+void OneWayLink::ScheduleNextPacketDeparture() {
+ if (packets_in_transit_.empty()) {
+ return;
+ }
+
+ Schedule(packets_in_transit_.front().dequeue_time);
+}
+
+QuicTime::Delta OneWayLink::GetRandomDelay(QuicTime::Delta transfer_time) {
+ if (!simulator_->enable_random_delays()) {
+ return QuicTime::Delta::Zero();
+ }
+
+ QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(
+ simulator_->GetRandomGenerator()->RandUint64() % (kMaxRandomDelayUs + 1));
+ // Have an upper bound on the delay to ensure packets do not go out of order.
+ delta = std::min(delta, transfer_time * 0.5);
+ return delta;
+}
+
+SymmetricLink::SymmetricLink(Simulator* simulator,
+ QuicString name,
+ UnconstrainedPortInterface* sink_a,
+ UnconstrainedPortInterface* sink_b,
+ QuicBandwidth bandwidth,
+ QuicTime::Delta propagation_delay)
+ : a_to_b_link_(simulator,
+ QuicStringPrintf("%s (A-to-B)", name.c_str()),
+ sink_b,
+ bandwidth,
+ propagation_delay),
+ b_to_a_link_(simulator,
+ QuicStringPrintf("%s (B-to-A)", name.c_str()),
+ sink_a,
+ bandwidth,
+ propagation_delay) {}
+
+SymmetricLink::SymmetricLink(Endpoint* endpoint_a,
+ Endpoint* endpoint_b,
+ QuicBandwidth bandwidth,
+ QuicTime::Delta propagation_delay)
+ : SymmetricLink(endpoint_a->simulator(),
+ QuicStringPrintf("Link [%s]<->[%s]",
+ endpoint_a->name().c_str(),
+ endpoint_b->name().c_str()),
+ endpoint_a->GetRxPort(),
+ endpoint_b->GetRxPort(),
+ bandwidth,
+ propagation_delay) {
+ endpoint_a->SetTxPort(&a_to_b_link_);
+ endpoint_b->SetTxPort(&b_to_a_link_);
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/quic/test_tools/simulator/link.h b/quic/test_tools/simulator/link.h
new file mode 100644
index 0000000..4553324
--- /dev/null
+++ b/quic/test_tools/simulator/link.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMULATOR_LINK_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_LINK_H_
+
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/actor.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/port.h"
+
+namespace quic {
+namespace simulator {
+
+// A reliable simplex link between two endpoints with constrained bandwidth. A
+// few microseconds of random delay are added for every packet to avoid
+// synchronization issues.
+class OneWayLink : public Actor, public ConstrainedPortInterface {
+ public:
+ OneWayLink(Simulator* simulator,
+ QuicString name,
+ UnconstrainedPortInterface* sink,
+ QuicBandwidth bandwidth,
+ QuicTime::Delta propagation_delay);
+ OneWayLink(const OneWayLink&) = delete;
+ OneWayLink& operator=(const OneWayLink&) = delete;
+ ~OneWayLink() override;
+
+ void AcceptPacket(std::unique_ptr<Packet> packet) override;
+ QuicTime::Delta TimeUntilAvailable() override;
+ void Act() override;
+
+ inline QuicBandwidth bandwidth() const { return bandwidth_; }
+
+ private:
+ struct QueuedPacket {
+ std::unique_ptr<Packet> packet;
+ QuicTime dequeue_time;
+
+ QueuedPacket(std::unique_ptr<Packet> packet, QuicTime dequeue_time);
+ QueuedPacket(QueuedPacket&& other);
+ ~QueuedPacket();
+ };
+
+ // Schedule the next packet to be egressed out of the link if there are
+ // packets on the link.
+ void ScheduleNextPacketDeparture();
+
+ // Get the value of a random delay imposed on each packet in order to avoid
+ // artifical synchronization artifacts during the simulation.
+ QuicTime::Delta GetRandomDelay(QuicTime::Delta transfer_time);
+
+ UnconstrainedPortInterface* sink_;
+ QuicQueue<QueuedPacket> packets_in_transit_;
+
+ const QuicBandwidth bandwidth_;
+ const QuicTime::Delta propagation_delay_;
+
+ QuicTime next_write_at_;
+};
+
+// A full-duplex link between two endpoints, functionally equivalent to two
+// OneWayLink objects tied together.
+class SymmetricLink {
+ public:
+ SymmetricLink(Simulator* simulator,
+ QuicString name,
+ UnconstrainedPortInterface* sink_a,
+ UnconstrainedPortInterface* sink_b,
+ QuicBandwidth bandwidth,
+ QuicTime::Delta propagation_delay);
+ SymmetricLink(Endpoint* endpoint_a,
+ Endpoint* endpoint_b,
+ QuicBandwidth bandwidth,
+ QuicTime::Delta propagation_delay);
+ SymmetricLink(const SymmetricLink&) = delete;
+ SymmetricLink& operator=(const SymmetricLink&) = delete;
+
+ inline QuicBandwidth bandwidth() { return a_to_b_link_.bandwidth(); }
+
+ private:
+ OneWayLink a_to_b_link_;
+ OneWayLink b_to_a_link_;
+};
+
+} // namespace simulator
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMULATOR_LINK_H_
diff --git a/quic/test_tools/simulator/packet_filter.cc b/quic/test_tools/simulator/packet_filter.cc
new file mode 100644
index 0000000..8ee038a
--- /dev/null
+++ b/quic/test_tools/simulator/packet_filter.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2016 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 "net/third_party/quiche/src/quic/test_tools/simulator/packet_filter.h"
+
+namespace quic {
+namespace simulator {
+
+PacketFilter::PacketFilter(Simulator* simulator,
+ QuicString name,
+ Endpoint* input)
+ : Endpoint(simulator, name), input_(input) {
+ input_->SetTxPort(this);
+}
+
+PacketFilter::~PacketFilter() {}
+
+void PacketFilter::AcceptPacket(std::unique_ptr<Packet> packet) {
+ if (FilterPacket(*packet)) {
+ output_tx_port_->AcceptPacket(std::move(packet));
+ }
+}
+
+QuicTime::Delta PacketFilter::TimeUntilAvailable() {
+ return output_tx_port_->TimeUntilAvailable();
+}
+
+void PacketFilter::Act() {}
+
+UnconstrainedPortInterface* PacketFilter::GetRxPort() {
+ return input_->GetRxPort();
+}
+
+void PacketFilter::SetTxPort(ConstrainedPortInterface* port) {
+ output_tx_port_ = port;
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/quic/test_tools/simulator/packet_filter.h b/quic/test_tools/simulator/packet_filter.h
new file mode 100644
index 0000000..e79b2cb
--- /dev/null
+++ b/quic/test_tools/simulator/packet_filter.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2016 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMULATOR_PACKET_FILTER_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_PACKET_FILTER_H_
+
+#include "net/third_party/quiche/src/quic/test_tools/simulator/port.h"
+
+namespace quic {
+namespace simulator {
+
+// Packet filter allows subclasses to filter out the packets that enter the
+// input port and exit the output port. Packets in the other direction are
+// always passed through.
+//
+// The filter wraps around the input endpoint, and exposes the resulting
+// filtered endpoint via the output() method. For example, if initially there
+// are two endpoints, A and B, connected via a symmetric link:
+//
+// QuicEndpoint endpoint_a;
+// QuicEndpoint endpoint_b;
+//
+// [...]
+//
+// SymmetricLink a_b_link(&endpoint_a, &endpoint_b, ...);
+//
+// and the goal is to filter the traffic from A to B, then the new invocation
+// would be as follows:
+//
+// PacketFilter filter(&simulator, "A-to-B packet filter", endpoint_a);
+// SymmetricLink a_b_link(&filter, &endpoint_b, ...);
+//
+// Note that the filter drops the packet instanteneously, without it ever
+// reaching the output wire. This means that in a direct endpoint-to-endpoint
+// scenario, whenever the packet is dropped, the link would become immediately
+// available for the next packet.
+class PacketFilter : public Endpoint, public ConstrainedPortInterface {
+ public:
+ // Initialize the filter by wrapping around |input|. Does not take the
+ // ownership of |input|.
+ PacketFilter(Simulator* simulator, QuicString name, Endpoint* input);
+ PacketFilter(const PacketFilter&) = delete;
+ PacketFilter& operator=(const PacketFilter&) = delete;
+ ~PacketFilter() override;
+
+ // Implementation of ConstrainedPortInterface.
+ void AcceptPacket(std::unique_ptr<Packet> packet) override;
+ QuicTime::Delta TimeUntilAvailable() override;
+
+ // Implementation of Endpoint interface methods.
+ UnconstrainedPortInterface* GetRxPort() override;
+ void SetTxPort(ConstrainedPortInterface* port) override;
+
+ // Implementation of Actor interface methods.
+ void Act() override;
+
+ protected:
+ // Returns true if the packet should be passed through, and false if it should
+ // be dropped. The function is called once per packet, in the order that the
+ // packets arrive, so it is safe for the function to alter the internal state
+ // of the filter.
+ virtual bool FilterPacket(const Packet& packet) = 0;
+
+ private:
+ // The port onto which the filtered packets are egressed.
+ ConstrainedPortInterface* output_tx_port_;
+
+ // The original network endpoint wrapped by the class.
+ Endpoint* input_;
+};
+
+} // namespace simulator
+} // namespace quic
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMULATOR_PACKET_FILTER_H_
diff --git a/quic/test_tools/simulator/port.cc b/quic/test_tools/simulator/port.cc
new file mode 100644
index 0000000..3db8888
--- /dev/null
+++ b/quic/test_tools/simulator/port.cc
@@ -0,0 +1,21 @@
+// Copyright (c) 2012 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 "net/third_party/quiche/src/quic/test_tools/simulator/port.h"
+
+namespace quic {
+namespace simulator {
+
+Packet::Packet()
+ : source(), destination(), tx_timestamp(QuicTime::Zero()), size(0) {}
+
+Packet::~Packet() {}
+
+Packet::Packet(const Packet& packet) = default;
+
+Endpoint::Endpoint(Simulator* simulator, QuicString name)
+ : Actor(simulator, name) {}
+
+} // namespace simulator
+} // namespace quic
diff --git a/quic/test_tools/simulator/port.h b/quic/test_tools/simulator/port.h
new file mode 100644
index 0000000..5cd4a7f
--- /dev/null
+++ b/quic/test_tools/simulator/port.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMULATOR_PORT_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_PORT_H_
+
+#include <string>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/actor.h"
+
+namespace quic {
+namespace simulator {
+
+struct Packet {
+ Packet();
+ ~Packet();
+ Packet(const Packet& packet);
+
+ QuicString source;
+ QuicString destination;
+ QuicTime tx_timestamp;
+
+ QuicString contents;
+ QuicByteCount size;
+};
+
+// An interface for anything that accepts packets at arbitrary rate.
+class UnconstrainedPortInterface {
+ public:
+ virtual ~UnconstrainedPortInterface() {}
+ virtual void AcceptPacket(std::unique_ptr<Packet> packet) = 0;
+};
+
+// An interface for any device that accepts packets at a specific rate.
+// Typically one would use a Queue object in order to write into a constrained
+// port.
+class ConstrainedPortInterface {
+ public:
+ virtual ~ConstrainedPortInterface() {}
+
+ // Accept a packet for a port. TimeUntilAvailable() must be zero before this
+ // method is called.
+ virtual void AcceptPacket(std::unique_ptr<Packet> packet) = 0;
+
+ // Time until write for the next port is available. Cannot be infinite.
+ virtual QuicTime::Delta TimeUntilAvailable() = 0;
+};
+
+// A convenience class for any network endpoints, i.e. the objects which can
+// both accept and send packets.
+class Endpoint : public Actor {
+ public:
+ virtual UnconstrainedPortInterface* GetRxPort() = 0;
+ virtual void SetTxPort(ConstrainedPortInterface* port) = 0;
+
+ protected:
+ Endpoint(Simulator* simulator, QuicString name);
+};
+
+} // namespace simulator
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMULATOR_PORT_H_
diff --git a/quic/test_tools/simulator/queue.cc b/quic/test_tools/simulator/queue.cc
new file mode 100644
index 0000000..4236481
--- /dev/null
+++ b/quic/test_tools/simulator/queue.cc
@@ -0,0 +1,123 @@
+// Copyright (c) 2012 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 "net/third_party/quiche/src/quic/test_tools/simulator/queue.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+
+namespace quic {
+namespace simulator {
+
+Queue::ListenerInterface::~ListenerInterface() {}
+
+Queue::Queue(Simulator* simulator, QuicString name, QuicByteCount capacity)
+ : Actor(simulator, name),
+ capacity_(capacity),
+ bytes_queued_(0),
+ aggregation_threshold_(0),
+ aggregation_timeout_(QuicTime::Delta::Infinite()),
+ current_bundle_(0),
+ current_bundle_bytes_(0),
+ listener_(nullptr) {
+ aggregation_timeout_alarm_.reset(simulator_->GetAlarmFactory()->CreateAlarm(
+ new AggregationAlarmDelegate(this)));
+}
+
+Queue::~Queue() {}
+
+void Queue::set_tx_port(ConstrainedPortInterface* port) {
+ tx_port_ = port;
+}
+
+void Queue::AcceptPacket(std::unique_ptr<Packet> packet) {
+ if (packet->size + bytes_queued_ > capacity_) {
+ QUIC_DVLOG(1) << "Queue [" << name() << "] has received a packet from ["
+ << packet->source << "] to [" << packet->destination
+ << "] which is over capacity. Dropping it.";
+ QUIC_DVLOG(1) << "Queue size: " << bytes_queued_ << " out of " << capacity_
+ << ". Packet size: " << packet->size;
+ return;
+ }
+
+ bytes_queued_ += packet->size;
+ queue_.emplace(std::move(packet), current_bundle_);
+
+ if (IsAggregationEnabled()) {
+ current_bundle_bytes_ += queue_.front().packet->size;
+ if (!aggregation_timeout_alarm_->IsSet()) {
+ aggregation_timeout_alarm_->Set(clock_->Now() + aggregation_timeout_);
+ }
+ if (current_bundle_bytes_ >= aggregation_threshold_) {
+ NextBundle();
+ }
+ }
+
+ ScheduleNextPacketDequeue();
+}
+
+void Queue::Act() {
+ DCHECK(!queue_.empty());
+ if (tx_port_->TimeUntilAvailable().IsZero()) {
+ DCHECK(bytes_queued_ >= queue_.front().packet->size);
+ bytes_queued_ -= queue_.front().packet->size;
+
+ tx_port_->AcceptPacket(std::move(queue_.front().packet));
+ queue_.pop();
+ if (listener_ != nullptr) {
+ listener_->OnPacketDequeued();
+ }
+ }
+
+ ScheduleNextPacketDequeue();
+}
+
+void Queue::EnableAggregation(QuicByteCount aggregation_threshold,
+ QuicTime::Delta aggregation_timeout) {
+ DCHECK_EQ(bytes_queued_, 0u);
+ DCHECK_GT(aggregation_threshold, 0u);
+ DCHECK(!aggregation_timeout.IsZero());
+ DCHECK(!aggregation_timeout.IsInfinite());
+
+ aggregation_threshold_ = aggregation_threshold;
+ aggregation_timeout_ = aggregation_timeout;
+}
+
+Queue::AggregationAlarmDelegate::AggregationAlarmDelegate(Queue* queue)
+ : queue_(queue) {}
+
+void Queue::AggregationAlarmDelegate::OnAlarm() {
+ queue_->NextBundle();
+ queue_->ScheduleNextPacketDequeue();
+}
+
+Queue::EnqueuedPacket::EnqueuedPacket(std::unique_ptr<Packet> packet,
+ AggregationBundleNumber bundle)
+ : packet(std::move(packet)), bundle(bundle) {}
+
+Queue::EnqueuedPacket::EnqueuedPacket(EnqueuedPacket&& other) = default;
+
+Queue::EnqueuedPacket::~EnqueuedPacket() = default;
+
+void Queue::NextBundle() {
+ current_bundle_++;
+ current_bundle_bytes_ = 0;
+ aggregation_timeout_alarm_->Cancel();
+}
+
+void Queue::ScheduleNextPacketDequeue() {
+ if (queue_.empty()) {
+ DCHECK_EQ(bytes_queued_, 0u);
+ return;
+ }
+
+ if (IsAggregationEnabled() && queue_.front().bundle == current_bundle_) {
+ return;
+ }
+
+ Schedule(clock_->Now() + tx_port_->TimeUntilAvailable());
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/quic/test_tools/simulator/queue.h b/quic/test_tools/simulator/queue.h
new file mode 100644
index 0000000..f9fa483
--- /dev/null
+++ b/quic/test_tools/simulator/queue.h
@@ -0,0 +1,120 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMULATOR_QUEUE_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_QUEUE_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_alarm.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/link.h"
+
+namespace quic {
+namespace simulator {
+
+// A finitely sized queue which egresses packets onto a constrained link. The
+// capacity of the queue is measured in bytes as opposed to packets.
+class Queue : public Actor, public UnconstrainedPortInterface {
+ public:
+ class ListenerInterface {
+ public:
+ virtual ~ListenerInterface();
+
+ // Called whenever a packet is removed from the queue.
+ virtual void OnPacketDequeued() = 0;
+ };
+
+ Queue(Simulator* simulator, QuicString name, QuicByteCount capacity);
+ Queue(const Queue&) = delete;
+ Queue& operator=(const Queue&) = delete;
+ ~Queue() override;
+
+ void set_tx_port(ConstrainedPortInterface* port);
+
+ void AcceptPacket(std::unique_ptr<Packet> packet) override;
+
+ void Act() override;
+
+ inline QuicByteCount capacity() const { return capacity_; }
+ inline QuicByteCount bytes_queued() const { return bytes_queued_; }
+ inline QuicPacketCount packets_queued() const { return queue_.size(); }
+
+ inline void set_listener_interface(ListenerInterface* listener) {
+ listener_ = listener;
+ }
+
+ // Enables packet aggregation on the queue. Packet aggregation makes the
+ // queue bundle packets up until they reach certain size. When the
+ // aggregation is enabled, the packets are not dequeued until the total size
+ // of packets in the queue reaches |aggregation_threshold|. The packets are
+ // automatically flushed from the queue if the oldest packet has been in it
+ // for |aggregation_timeout|.
+ //
+ // This method may only be called when the queue is empty. Once enabled,
+ // aggregation cannot be disabled.
+ void EnableAggregation(QuicByteCount aggregation_threshold,
+ QuicTime::Delta aggregation_timeout);
+
+ private:
+ typedef uint64_t AggregationBundleNumber;
+
+ // In order to implement packet aggregation, each packet is tagged with a
+ // bundle number. The queue keeps a bundle counter, and whenever a bundle is
+ // ready, it increments the number of the current bundle. Only the packets
+ // outside of the current bundle are allowed to leave the queue.
+ struct EnqueuedPacket {
+ EnqueuedPacket(std::unique_ptr<Packet> packet,
+ AggregationBundleNumber bundle);
+ EnqueuedPacket(EnqueuedPacket&& other);
+ ~EnqueuedPacket();
+
+ std::unique_ptr<Packet> packet;
+ AggregationBundleNumber bundle;
+ };
+
+ // Alarm handler for aggregation timeout.
+ class AggregationAlarmDelegate : public QuicAlarm::Delegate {
+ public:
+ explicit AggregationAlarmDelegate(Queue* queue);
+
+ void OnAlarm() override;
+
+ private:
+ Queue* queue_;
+ };
+
+ inline bool IsAggregationEnabled() const {
+ return aggregation_threshold_ > 0;
+ }
+
+ // Increment the bundle counter and reset the bundle state. This causes all
+ // packets currently in the bundle to be flushed onto the link.
+ void NextBundle();
+
+ void ScheduleNextPacketDequeue();
+
+ const QuicByteCount capacity_;
+ QuicByteCount bytes_queued_;
+
+ QuicByteCount aggregation_threshold_;
+ QuicTime::Delta aggregation_timeout_;
+ // The number of the current aggregation bundle. Monotonically increasing.
+ // All packets in the previous bundles are allowed to leave the queue, and
+ // none of the packets in the current one are.
+ AggregationBundleNumber current_bundle_;
+ // Size of the current bundle. Whenever it exceeds |aggregation_threshold_|,
+ // the next bundle is created.
+ QuicByteCount current_bundle_bytes_;
+ // Alarm responsible for flushing the current bundle upon timeout. Set when
+ // the first packet in the bundle is enqueued.
+ std::unique_ptr<QuicAlarm> aggregation_timeout_alarm_;
+
+ ConstrainedPortInterface* tx_port_;
+ QuicQueue<EnqueuedPacket> queue_;
+
+ ListenerInterface* listener_;
+};
+
+} // namespace simulator
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMULATOR_QUEUE_H_
diff --git a/quic/test_tools/simulator/quic_endpoint.cc b/quic/test_tools/simulator/quic_endpoint.cc
new file mode 100644
index 0000000..1bacdf2
--- /dev/null
+++ b/quic/test_tools/simulator/quic_endpoint.cc
@@ -0,0 +1,415 @@
+// Copyright (c) 2012 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 "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h"
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test_output.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+
+namespace quic {
+namespace simulator {
+
+const QuicStreamId kDataStream = 3;
+const QuicByteCount kWriteChunkSize = 128 * 1024;
+const char kStreamDataContents = 'Q';
+
+// Takes a SHA-1 hash of the name and converts it into five 32-bit integers.
+static std::vector<uint32_t> HashNameIntoFive32BitIntegers(QuicString name) {
+ const QuicString hash = test::Sha1Hash(name);
+
+ std::vector<uint32_t> output;
+ uint32_t current_number = 0;
+ for (size_t i = 0; i < hash.size(); i++) {
+ current_number = (current_number << 8) + hash[i];
+ if (i % 4 == 3) {
+ output.push_back(i);
+ current_number = 0;
+ }
+ }
+
+ return output;
+}
+
+QuicSocketAddress GetAddressFromName(QuicString name) {
+ const std::vector<uint32_t> hash = HashNameIntoFive32BitIntegers(name);
+
+ // Generate a random port between 1025 and 65535.
+ const uint16_t port = 1025 + hash[0] % (65535 - 1025 + 1);
+
+ // Generate a random 10.x.x.x address, where x is between 1 and 254.
+ QuicString ip_address{"\xa\0\0\0", 4};
+ for (size_t i = 1; i < 4; i++) {
+ ip_address[i] = 1 + hash[i] % 254;
+ }
+ QuicIpAddress host;
+ host.FromPackedString(ip_address.c_str(), ip_address.length());
+ return QuicSocketAddress(host, port);
+}
+
+QuicEndpoint::QuicEndpoint(Simulator* simulator,
+ QuicString name,
+ QuicString peer_name,
+ Perspective perspective,
+ QuicConnectionId connection_id)
+ : Endpoint(simulator, name),
+ peer_name_(peer_name),
+ writer_(this),
+ nic_tx_queue_(simulator,
+ QuicStringPrintf("%s (TX Queue)", name.c_str()),
+ kMaxPacketSize * kTxQueueSize),
+ connection_(connection_id,
+ GetAddressFromName(peer_name),
+ simulator,
+ simulator->GetAlarmFactory(),
+ &writer_,
+ false,
+ perspective,
+ ParsedVersionOfIndex(CurrentSupportedVersions(), 0)),
+ bytes_to_transfer_(0),
+ bytes_transferred_(0),
+ write_blocked_count_(0),
+ wrong_data_received_(false),
+ drop_next_packet_(false),
+ notifier_(nullptr) {
+ nic_tx_queue_.set_listener_interface(this);
+
+ connection_.SetSelfAddress(GetAddressFromName(name));
+ connection_.set_visitor(this);
+ connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<NullEncrypter>(perspective));
+ connection_.SetDecrypter(ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<NullDecrypter>(perspective));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ if (perspective == Perspective::IS_SERVER) {
+ // Skip version negotiation.
+ test::QuicConnectionPeer::SetNegotiatedVersion(&connection_);
+ }
+ connection_.SetDataProducer(&producer_);
+ connection_.SetSessionNotifier(this);
+ if (connection_.session_decides_what_to_write()) {
+ notifier_ = QuicMakeUnique<test::SimpleSessionNotifier>(&connection_);
+ }
+
+ // Configure the connection as if it received a handshake. This is important
+ // primarily because
+ // - this enables pacing, and
+ // - this sets the non-handshake timeouts.
+ QuicString error;
+ CryptoHandshakeMessage peer_hello;
+ peer_hello.SetValue(kICSL,
+ static_cast<uint32_t>(kMaximumIdleTimeoutSecs - 1));
+ peer_hello.SetValue(kMIDS,
+ static_cast<uint32_t>(kDefaultMaxStreamsPerConnection));
+ QuicConfig config;
+ QuicErrorCode error_code = config.ProcessPeerHello(
+ peer_hello, perspective == Perspective::IS_CLIENT ? SERVER : CLIENT,
+ &error);
+ DCHECK_EQ(error_code, QUIC_NO_ERROR) << "Configuration failed: " << error;
+ connection_.SetFromConfig(config);
+}
+
+QuicEndpoint::~QuicEndpoint() {
+ if (trace_visitor_ != nullptr) {
+ const char* perspective_prefix =
+ connection_.perspective() == Perspective::IS_CLIENT ? "C" : "S";
+
+ QuicString identifier =
+ QuicStrCat(perspective_prefix, connection_.connection_id().ToString());
+ QuicRecordTestOutput(identifier,
+ trace_visitor_->trace()->SerializeAsString());
+ }
+}
+
+QuicByteCount QuicEndpoint::bytes_received() const {
+ QuicByteCount total = 0;
+ for (auto& interval : offsets_received_) {
+ total += interval.max() - interval.min();
+ }
+ return total;
+}
+
+QuicByteCount QuicEndpoint::bytes_to_transfer() const {
+ if (notifier_ != nullptr) {
+ return notifier_->StreamBytesToSend();
+ }
+ return bytes_to_transfer_;
+}
+
+QuicByteCount QuicEndpoint::bytes_transferred() const {
+ if (notifier_ != nullptr) {
+ return notifier_->StreamBytesSent();
+ }
+ return bytes_transferred_;
+}
+
+void QuicEndpoint::AddBytesToTransfer(QuicByteCount bytes) {
+ if (notifier_ != nullptr) {
+ if (notifier_->HasBufferedStreamData()) {
+ Schedule(clock_->Now());
+ }
+ notifier_->WriteOrBufferData(kDataStream, bytes, NO_FIN);
+ return;
+ }
+
+ if (bytes_to_transfer_ > 0) {
+ Schedule(clock_->Now());
+ }
+
+ bytes_to_transfer_ += bytes;
+ WriteStreamData();
+}
+
+void QuicEndpoint::DropNextIncomingPacket() {
+ drop_next_packet_ = true;
+}
+
+void QuicEndpoint::RecordTrace() {
+ trace_visitor_ = QuicMakeUnique<QuicTraceVisitor>(&connection_);
+ connection_.set_debug_visitor(trace_visitor_.get());
+}
+
+void QuicEndpoint::AcceptPacket(std::unique_ptr<Packet> packet) {
+ if (packet->destination != name_) {
+ return;
+ }
+ if (drop_next_packet_) {
+ drop_next_packet_ = false;
+ return;
+ }
+
+ QuicReceivedPacket received_packet(packet->contents.data(),
+ packet->contents.size(), clock_->Now());
+ connection_.ProcessUdpPacket(connection_.self_address(),
+ connection_.peer_address(), received_packet);
+}
+
+UnconstrainedPortInterface* QuicEndpoint::GetRxPort() {
+ return this;
+}
+
+void QuicEndpoint::SetTxPort(ConstrainedPortInterface* port) {
+ // Any egress done by the endpoint is actually handled by a queue on an NIC.
+ nic_tx_queue_.set_tx_port(port);
+}
+
+void QuicEndpoint::OnPacketDequeued() {
+ if (writer_.IsWriteBlocked() &&
+ (nic_tx_queue_.capacity() - nic_tx_queue_.bytes_queued()) >=
+ kMaxPacketSize) {
+ writer_.SetWritable();
+ connection_.OnCanWrite();
+ }
+}
+
+void QuicEndpoint::OnStreamFrame(const QuicStreamFrame& frame) {
+ // Verify that the data received always matches the expected.
+ DCHECK(frame.stream_id == kDataStream);
+ for (size_t i = 0; i < frame.data_length; i++) {
+ if (frame.data_buffer[i] != kStreamDataContents) {
+ wrong_data_received_ = true;
+ }
+ }
+ offsets_received_.Add(frame.offset, frame.offset + frame.data_length);
+ // Sanity check against very pathological connections.
+ DCHECK_LE(offsets_received_.Size(), 1000u);
+}
+
+void QuicEndpoint::OnCryptoFrame(const QuicCryptoFrame& frame) {}
+
+void QuicEndpoint::OnCanWrite() {
+ if (notifier_ != nullptr) {
+ notifier_->OnCanWrite();
+ return;
+ }
+ WriteStreamData();
+}
+bool QuicEndpoint::WillingAndAbleToWrite() const {
+ if (notifier_ != nullptr) {
+ return notifier_->WillingToWrite();
+ }
+ return bytes_to_transfer_ != 0;
+}
+bool QuicEndpoint::HasPendingHandshake() const {
+ return false;
+}
+bool QuicEndpoint::ShouldKeepConnectionAlive() const {
+ return true;
+}
+
+bool QuicEndpoint::AllowSelfAddressChange() const {
+ return false;
+}
+
+bool QuicEndpoint::OnFrameAcked(const QuicFrame& frame,
+ QuicTime::Delta ack_delay_time) {
+ if (notifier_ != nullptr) {
+ return notifier_->OnFrameAcked(frame, ack_delay_time);
+ }
+ return false;
+}
+
+void QuicEndpoint::OnFrameLost(const QuicFrame& frame) {
+ DCHECK(notifier_);
+ notifier_->OnFrameLost(frame);
+}
+
+void QuicEndpoint::RetransmitFrames(const QuicFrames& frames,
+ TransmissionType type) {
+ DCHECK(notifier_);
+ notifier_->RetransmitFrames(frames, type);
+}
+
+bool QuicEndpoint::IsFrameOutstanding(const QuicFrame& frame) const {
+ DCHECK(notifier_);
+ return notifier_->IsFrameOutstanding(frame);
+}
+
+bool QuicEndpoint::HasUnackedCryptoData() const {
+ return false;
+}
+
+QuicEndpoint::Writer::Writer(QuicEndpoint* endpoint)
+ : endpoint_(endpoint), is_blocked_(false) {}
+
+QuicEndpoint::Writer::~Writer() {}
+
+WriteResult QuicEndpoint::Writer::WritePacket(
+ const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) {
+ DCHECK(!IsWriteBlocked());
+ DCHECK(options == nullptr);
+ DCHECK(buf_len <= kMaxPacketSize);
+
+ // Instead of losing a packet, become write-blocked when the egress queue is
+ // full.
+ if (endpoint_->nic_tx_queue_.packets_queued() > kTxQueueSize) {
+ is_blocked_ = true;
+ endpoint_->write_blocked_count_++;
+ return WriteResult(WRITE_STATUS_BLOCKED, 0);
+ }
+
+ auto packet = QuicMakeUnique<Packet>();
+ packet->source = endpoint_->name();
+ packet->destination = endpoint_->peer_name_;
+ packet->tx_timestamp = endpoint_->clock_->Now();
+
+ packet->contents = QuicString(buffer, buf_len);
+ packet->size = buf_len;
+
+ endpoint_->nic_tx_queue_.AcceptPacket(std::move(packet));
+
+ return WriteResult(WRITE_STATUS_OK, buf_len);
+}
+
+bool QuicEndpoint::Writer::IsWriteBlocked() const {
+ return is_blocked_;
+}
+
+void QuicEndpoint::Writer::SetWritable() {
+ is_blocked_ = false;
+}
+
+QuicByteCount QuicEndpoint::Writer::GetMaxPacketSize(
+ const QuicSocketAddress& /*peer_address*/) const {
+ return kMaxPacketSize;
+}
+
+bool QuicEndpoint::Writer::SupportsReleaseTime() const {
+ return false;
+}
+
+bool QuicEndpoint::Writer::IsBatchMode() const {
+ return false;
+}
+
+char* QuicEndpoint::Writer::GetNextWriteLocation(
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address) {
+ return nullptr;
+}
+
+WriteResult QuicEndpoint::Writer::Flush() {
+ return WriteResult(WRITE_STATUS_OK, 0);
+}
+
+WriteStreamDataResult QuicEndpoint::DataProducer::WriteStreamData(
+ QuicStreamId id,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) {
+ writer->WriteRepeatedByte(kStreamDataContents, data_length);
+ return WRITE_SUCCESS;
+}
+
+bool QuicEndpoint::DataProducer::WriteCryptoData(EncryptionLevel leve,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) {
+ QUIC_BUG << "QuicEndpoint::DataProducer::WriteCryptoData is unimplemented";
+ return false;
+}
+
+void QuicEndpoint::WriteStreamData() {
+ // Instantiate a flusher which would normally be here due to QuicSession.
+ QuicConnection::ScopedPacketFlusher flusher(
+ &connection_, QuicConnection::SEND_ACK_IF_QUEUED);
+
+ while (bytes_to_transfer_ > 0) {
+ // Transfer data in chunks of size at most |kWriteChunkSize|.
+ const size_t transmission_size =
+ std::min(kWriteChunkSize, bytes_to_transfer_);
+
+ QuicConsumedData consumed_data = connection_.SendStreamData(
+ kDataStream, transmission_size, bytes_transferred_, NO_FIN);
+
+ DCHECK(consumed_data.bytes_consumed <= transmission_size);
+ bytes_transferred_ += consumed_data.bytes_consumed;
+ bytes_to_transfer_ -= consumed_data.bytes_consumed;
+ if (consumed_data.bytes_consumed != transmission_size) {
+ return;
+ }
+ }
+}
+
+QuicEndpointMultiplexer::QuicEndpointMultiplexer(
+ QuicString name,
+ std::initializer_list<QuicEndpoint*> endpoints)
+ : Endpoint((*endpoints.begin())->simulator(), name) {
+ for (QuicEndpoint* endpoint : endpoints) {
+ mapping_.insert(std::make_pair(endpoint->name(), endpoint));
+ }
+}
+
+QuicEndpointMultiplexer::~QuicEndpointMultiplexer() {}
+
+void QuicEndpointMultiplexer::AcceptPacket(std::unique_ptr<Packet> packet) {
+ auto key_value_pair_it = mapping_.find(packet->destination);
+ if (key_value_pair_it == mapping_.end()) {
+ return;
+ }
+
+ key_value_pair_it->second->GetRxPort()->AcceptPacket(std::move(packet));
+}
+UnconstrainedPortInterface* QuicEndpointMultiplexer::GetRxPort() {
+ return this;
+}
+void QuicEndpointMultiplexer::SetTxPort(ConstrainedPortInterface* port) {
+ for (auto& key_value_pair : mapping_) {
+ key_value_pair.second->SetTxPort(port);
+ }
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/quic/test_tools/simulator/quic_endpoint.h b/quic/test_tools/simulator/quic_endpoint.h
new file mode 100644
index 0000000..ab4ee4b
--- /dev/null
+++ b/quic/test_tools/simulator/quic_endpoint.h
@@ -0,0 +1,234 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMULATOR_QUIC_ENDPOINT_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_QUIC_ENDPOINT_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/null_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_default_packet_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_frame_data_producer.h"
+#include "net/third_party/quiche/src/quic/core/quic_trace_visitor.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/link.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/queue.h"
+
+namespace quic {
+namespace simulator {
+
+// Size of the TX queue used by the kernel/NIC. 1000 is the Linux
+// kernel default.
+const QuicByteCount kTxQueueSize = 1000;
+
+// Generate a random local network host-port tuple based on the name of the
+// endpoint.
+QuicSocketAddress GetAddressFromName(QuicString name);
+
+// A QUIC connection endpoint. Wraps around QuicConnection. In order to
+// initiate a transfer, the caller has to call AddBytesToTransfer(). The data
+// transferred is always the same and is always transferred on a single stream.
+// The endpoint receives all packets addressed to it, and verifies that the data
+// received is what it's supposed to be.
+class QuicEndpoint : public Endpoint,
+ public UnconstrainedPortInterface,
+ public Queue::ListenerInterface,
+ public QuicConnectionVisitorInterface,
+ public SessionNotifierInterface {
+ public:
+ QuicEndpoint(Simulator* simulator,
+ QuicString name,
+ QuicString peer_name,
+ Perspective perspective,
+ QuicConnectionId connection_id);
+ ~QuicEndpoint() override;
+
+ inline QuicConnection* connection() { return &connection_; }
+ QuicByteCount bytes_to_transfer() const;
+ QuicByteCount bytes_transferred() const;
+ QuicByteCount bytes_received() const;
+ inline size_t write_blocked_count() { return write_blocked_count_; }
+ inline bool wrong_data_received() const { return wrong_data_received_; }
+
+ // Send |bytes| bytes. Initiates the transfer if one is not already in
+ // progress.
+ void AddBytesToTransfer(QuicByteCount bytes);
+
+ // Drop the next packet upon receipt.
+ void DropNextIncomingPacket();
+
+ // UnconstrainedPortInterface method. Called whenever the endpoint receives a
+ // packet.
+ void AcceptPacket(std::unique_ptr<Packet> packet) override;
+
+ // Enables logging of the connection trace at the end of the unit test.
+ void RecordTrace();
+
+ // Begin Endpoint implementation.
+ UnconstrainedPortInterface* GetRxPort() override;
+ void SetTxPort(ConstrainedPortInterface* port) override;
+ // End Endpoint implementation.
+
+ // Actor method.
+ void Act() override {}
+
+ // Queue::ListenerInterface method.
+ void OnPacketDequeued() override;
+
+ // Begin QuicConnectionVisitorInterface implementation.
+ void OnStreamFrame(const QuicStreamFrame& frame) override;
+ void OnCryptoFrame(const QuicCryptoFrame& frame) override;
+ void OnCanWrite() override;
+ bool WillingAndAbleToWrite() const override;
+ bool HasPendingHandshake() const override;
+ bool ShouldKeepConnectionAlive() const override;
+
+ void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override {}
+ void OnBlockedFrame(const QuicBlockedFrame& frame) override {}
+ void OnRstStream(const QuicRstStreamFrame& frame) override {}
+ void OnGoAway(const QuicGoAwayFrame& frame) override {}
+ void OnMessageReceived(QuicStringPiece message) override {}
+ void OnConnectionClosed(QuicErrorCode error,
+ const QuicString& error_details,
+ ConnectionCloseSource source) override {}
+ void OnWriteBlocked() override {}
+ void OnSuccessfulVersionNegotiation(
+ const ParsedQuicVersion& version) override {}
+ void OnConnectivityProbeReceived(
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address) override {}
+ void OnCongestionWindowChange(QuicTime now) override {}
+ void OnConnectionMigration(AddressChangeType type) override {}
+ void OnPathDegrading() override {}
+ void OnAckNeedsRetransmittableFrame() override {}
+ void SendPing() override {}
+ bool AllowSelfAddressChange() const override;
+ void OnForwardProgressConfirmed() override {}
+ bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) override {
+ return true;
+ }
+ bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame) override {
+ return true;
+ }
+ bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override {
+ return true;
+ }
+
+ // End QuicConnectionVisitorInterface implementation.
+
+ // Begin SessionNotifierInterface methods:
+ bool OnFrameAcked(const QuicFrame& frame,
+ QuicTime::Delta ack_delay_time) override;
+ void OnStreamFrameRetransmitted(const QuicStreamFrame& frame) override {}
+ void OnFrameLost(const QuicFrame& frame) override;
+ void RetransmitFrames(const QuicFrames& frames,
+ TransmissionType type) override;
+ bool IsFrameOutstanding(const QuicFrame& frame) const override;
+ bool HasUnackedCryptoData() const override;
+ // End SessionNotifierInterface implementation.
+
+ private:
+ // A Writer object that writes into the |nic_tx_queue_|.
+ class Writer : public QuicPacketWriter {
+ public:
+ explicit Writer(QuicEndpoint* endpoint);
+ ~Writer() override;
+
+ WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) override;
+ bool IsWriteBlocked() const override;
+ void SetWritable() override;
+ QuicByteCount GetMaxPacketSize(
+ const QuicSocketAddress& peer_address) const override;
+ bool SupportsReleaseTime() const override;
+ bool IsBatchMode() const override;
+ char* GetNextWriteLocation(const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address) override;
+ WriteResult Flush() override;
+
+ private:
+ QuicEndpoint* endpoint_;
+
+ bool is_blocked_;
+ };
+
+ // The producer outputs the repetition of the same byte. That sequence is
+ // verified by the receiver.
+ class DataProducer : public QuicStreamFrameDataProducer {
+ public:
+ WriteStreamDataResult WriteStreamData(QuicStreamId id,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) override;
+ bool WriteCryptoData(EncryptionLevel level,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) override;
+ };
+
+ // Write stream data until |bytes_to_transfer_| is zero or the connection is
+ // write-blocked.
+ void WriteStreamData();
+
+ QuicString peer_name_;
+
+ Writer writer_;
+ DataProducer producer_;
+ // The queue for the outgoing packets. In reality, this might be either on
+ // the network card, or in the kernel, but for concreteness we assume it's on
+ // the network card.
+ Queue nic_tx_queue_;
+ QuicConnection connection_;
+
+ QuicByteCount bytes_to_transfer_;
+ QuicByteCount bytes_transferred_;
+
+ // Counts the number of times the writer became write-blocked.
+ size_t write_blocked_count_;
+
+ // Set to true if the endpoint receives stream data different from what it
+ // expects.
+ bool wrong_data_received_;
+
+ // If true, drop the next packet when receiving it.
+ bool drop_next_packet_;
+
+ // Record of received offsets in the data stream.
+ QuicIntervalSet<QuicStreamOffset> offsets_received_;
+
+ std::unique_ptr<test::SimpleSessionNotifier> notifier_;
+ std::unique_ptr<QuicTraceVisitor> trace_visitor_;
+};
+
+// Multiplexes multiple connections at the same host on the network.
+class QuicEndpointMultiplexer : public Endpoint,
+ public UnconstrainedPortInterface {
+ public:
+ QuicEndpointMultiplexer(QuicString name,
+ std::initializer_list<QuicEndpoint*> endpoints);
+ ~QuicEndpointMultiplexer() override;
+
+ // Receives a packet and passes it to the specified endpoint if that endpoint
+ // is one of the endpoints being multiplexed, otherwise ignores the packet.
+ void AcceptPacket(std::unique_ptr<Packet> packet) override;
+ UnconstrainedPortInterface* GetRxPort() override;
+
+ // Sets the egress port for all the endpoints being multiplexed.
+ void SetTxPort(ConstrainedPortInterface* port) override;
+
+ void Act() override {}
+
+ private:
+ QuicUnorderedMap<QuicString, QuicEndpoint*> mapping_;
+};
+
+} // namespace simulator
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMULATOR_QUIC_ENDPOINT_H_
diff --git a/quic/test_tools/simulator/quic_endpoint_test.cc b/quic/test_tools/simulator/quic_endpoint_test.cc
new file mode 100644
index 0000000..0b25096
--- /dev/null
+++ b/quic/test_tools/simulator/quic_endpoint_test.cc
@@ -0,0 +1,209 @@
+// Copyright (c) 2012 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 "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h"
+
+using ::testing::_;
+using ::testing::NiceMock;
+using ::testing::Return;
+
+namespace quic {
+namespace simulator {
+
+const QuicBandwidth kDefaultBandwidth =
+ QuicBandwidth::FromKBitsPerSecond(10 * 1000);
+const QuicTime::Delta kDefaultPropagationDelay =
+ QuicTime::Delta::FromMilliseconds(20);
+const QuicByteCount kDefaultBdp = kDefaultBandwidth * kDefaultPropagationDelay;
+
+// A simple test harness where all hosts are connected to a switch with
+// identical links.
+class QuicEndpointTest : public QuicTest {
+ public:
+ QuicEndpointTest()
+ : simulator_(), switch_(&simulator_, "Switch", 8, kDefaultBdp * 2) {}
+
+ protected:
+ Simulator simulator_;
+ Switch switch_;
+
+ std::unique_ptr<SymmetricLink> Link(Endpoint* a, Endpoint* b) {
+ return QuicMakeUnique<SymmetricLink>(a, b, kDefaultBandwidth,
+ kDefaultPropagationDelay);
+ }
+
+ std::unique_ptr<SymmetricLink> CustomLink(Endpoint* a,
+ Endpoint* b,
+ uint64_t extra_rtt_ms) {
+ return QuicMakeUnique<SymmetricLink>(
+ a, b, kDefaultBandwidth,
+ kDefaultPropagationDelay +
+ QuicTime::Delta::FromMilliseconds(extra_rtt_ms));
+ }
+};
+
+// Test transmission from one host to another.
+TEST_F(QuicEndpointTest, OneWayTransmission) {
+ QuicEndpoint endpoint_a(&simulator_, "Endpoint A", "Endpoint B",
+ Perspective::IS_CLIENT, test::TestConnectionId(42));
+ QuicEndpoint endpoint_b(&simulator_, "Endpoint B", "Endpoint A",
+ Perspective::IS_SERVER, test::TestConnectionId(42));
+ auto link_a = Link(&endpoint_a, switch_.port(1));
+ auto link_b = Link(&endpoint_b, switch_.port(2));
+
+ // First transmit a small, packet-size chunk of data.
+ endpoint_a.AddBytesToTransfer(600);
+ QuicTime end_time =
+ simulator_.GetClock()->Now() + QuicTime::Delta::FromMilliseconds(1000);
+ simulator_.RunUntil(
+ [this, end_time]() { return simulator_.GetClock()->Now() >= end_time; });
+
+ EXPECT_EQ(600u, endpoint_a.bytes_transferred());
+ ASSERT_EQ(600u, endpoint_b.bytes_received());
+ EXPECT_FALSE(endpoint_a.wrong_data_received());
+ EXPECT_FALSE(endpoint_b.wrong_data_received());
+
+ // After a small chunk succeeds, try to transfer 2 MiB.
+ endpoint_a.AddBytesToTransfer(2 * 1024 * 1024);
+ end_time = simulator_.GetClock()->Now() + QuicTime::Delta::FromSeconds(5);
+ simulator_.RunUntil(
+ [this, end_time]() { return simulator_.GetClock()->Now() >= end_time; });
+
+ const QuicByteCount total_bytes_transferred = 600 + 2 * 1024 * 1024;
+ EXPECT_EQ(total_bytes_transferred, endpoint_a.bytes_transferred());
+ EXPECT_EQ(total_bytes_transferred, endpoint_b.bytes_received());
+ EXPECT_EQ(0u, endpoint_a.write_blocked_count());
+ EXPECT_FALSE(endpoint_a.wrong_data_received());
+ EXPECT_FALSE(endpoint_b.wrong_data_received());
+}
+
+// Test the situation in which the writer becomes write-blocked.
+TEST_F(QuicEndpointTest, WriteBlocked) {
+ QuicEndpoint endpoint_a(&simulator_, "Endpoint A", "Endpoint B",
+ Perspective::IS_CLIENT, test::TestConnectionId(42));
+ QuicEndpoint endpoint_b(&simulator_, "Endpoint B", "Endpoint A",
+ Perspective::IS_SERVER, test::TestConnectionId(42));
+ auto link_a = Link(&endpoint_a, switch_.port(1));
+ auto link_b = Link(&endpoint_b, switch_.port(2));
+
+ // Will be owned by the sent packet manager.
+ auto* sender = new NiceMock<test::MockSendAlgorithm>();
+ EXPECT_CALL(*sender, CanSend(_)).WillRepeatedly(Return(true));
+ EXPECT_CALL(*sender, PacingRate(_))
+ .WillRepeatedly(Return(10 * kDefaultBandwidth));
+ EXPECT_CALL(*sender, BandwidthEstimate())
+ .WillRepeatedly(Return(10 * kDefaultBandwidth));
+ EXPECT_CALL(*sender, GetCongestionWindow())
+ .WillRepeatedly(
+ Return(kMaxPacketSize * kDefaultMaxCongestionWindowPackets));
+ test::QuicConnectionPeer::SetSendAlgorithm(endpoint_a.connection(), sender);
+
+ // First transmit a small, packet-size chunk of data.
+ QuicByteCount bytes_to_transfer = 3 * 1024 * 1024;
+ endpoint_a.AddBytesToTransfer(bytes_to_transfer);
+ QuicTime end_time =
+ simulator_.GetClock()->Now() + QuicTime::Delta::FromSeconds(30);
+ simulator_.RunUntil([this, &endpoint_b, bytes_to_transfer, end_time]() {
+ return endpoint_b.bytes_received() == bytes_to_transfer ||
+ simulator_.GetClock()->Now() >= end_time;
+ });
+
+ EXPECT_EQ(bytes_to_transfer, endpoint_a.bytes_transferred());
+ EXPECT_EQ(bytes_to_transfer, endpoint_b.bytes_received());
+ EXPECT_GT(endpoint_a.write_blocked_count(), 0u);
+ EXPECT_FALSE(endpoint_a.wrong_data_received());
+ EXPECT_FALSE(endpoint_b.wrong_data_received());
+}
+
+// Test transmission of 1 MiB of data between two hosts simultaneously in both
+// directions.
+TEST_F(QuicEndpointTest, TwoWayTransmission) {
+ QuicEndpoint endpoint_a(&simulator_, "Endpoint A", "Endpoint B",
+ Perspective::IS_CLIENT, test::TestConnectionId(42));
+ QuicEndpoint endpoint_b(&simulator_, "Endpoint B", "Endpoint A",
+ Perspective::IS_SERVER, test::TestConnectionId(42));
+ auto link_a = Link(&endpoint_a, switch_.port(1));
+ auto link_b = Link(&endpoint_b, switch_.port(2));
+
+ endpoint_a.RecordTrace();
+ endpoint_b.RecordTrace();
+
+ endpoint_a.AddBytesToTransfer(1024 * 1024);
+ endpoint_b.AddBytesToTransfer(1024 * 1024);
+ QuicTime end_time =
+ simulator_.GetClock()->Now() + QuicTime::Delta::FromSeconds(5);
+ simulator_.RunUntil(
+ [this, end_time]() { return simulator_.GetClock()->Now() >= end_time; });
+
+ EXPECT_EQ(1024u * 1024u, endpoint_a.bytes_transferred());
+ EXPECT_EQ(1024u * 1024u, endpoint_b.bytes_transferred());
+ EXPECT_EQ(1024u * 1024u, endpoint_a.bytes_received());
+ EXPECT_EQ(1024u * 1024u, endpoint_b.bytes_received());
+ EXPECT_FALSE(endpoint_a.wrong_data_received());
+ EXPECT_FALSE(endpoint_b.wrong_data_received());
+}
+
+// Simulate three hosts trying to send data to a fourth one simultaneously.
+TEST_F(QuicEndpointTest, Competition) {
+ // TODO(63765788): Turn back on this flag when the issue if fixed.
+ SetQuicReloadableFlag(quic_bbr_one_mss_conservation, false);
+ auto endpoint_a = QuicMakeUnique<QuicEndpoint>(
+ &simulator_, "Endpoint A", "Endpoint D (A)", Perspective::IS_CLIENT,
+ test::TestConnectionId(42));
+ auto endpoint_b = QuicMakeUnique<QuicEndpoint>(
+ &simulator_, "Endpoint B", "Endpoint D (B)", Perspective::IS_CLIENT,
+ test::TestConnectionId(43));
+ auto endpoint_c = QuicMakeUnique<QuicEndpoint>(
+ &simulator_, "Endpoint C", "Endpoint D (C)", Perspective::IS_CLIENT,
+ test::TestConnectionId(44));
+ auto endpoint_d_a = QuicMakeUnique<QuicEndpoint>(
+ &simulator_, "Endpoint D (A)", "Endpoint A", Perspective::IS_SERVER,
+ test::TestConnectionId(42));
+ auto endpoint_d_b = QuicMakeUnique<QuicEndpoint>(
+ &simulator_, "Endpoint D (B)", "Endpoint B", Perspective::IS_SERVER,
+ test::TestConnectionId(43));
+ auto endpoint_d_c = QuicMakeUnique<QuicEndpoint>(
+ &simulator_, "Endpoint D (C)", "Endpoint C", Perspective::IS_SERVER,
+ test::TestConnectionId(44));
+ QuicEndpointMultiplexer endpoint_d(
+ "Endpoint D",
+ {endpoint_d_a.get(), endpoint_d_b.get(), endpoint_d_c.get()});
+
+ // Create links with slightly different RTTs in order to avoid pathological
+ // side-effects of packets entering the queue at the exactly same time.
+ auto link_a = CustomLink(endpoint_a.get(), switch_.port(1), 0);
+ auto link_b = CustomLink(endpoint_b.get(), switch_.port(2), 1);
+ auto link_c = CustomLink(endpoint_c.get(), switch_.port(3), 2);
+ auto link_d = Link(&endpoint_d, switch_.port(4));
+
+ endpoint_a->AddBytesToTransfer(2 * 1024 * 1024);
+ endpoint_b->AddBytesToTransfer(2 * 1024 * 1024);
+ endpoint_c->AddBytesToTransfer(2 * 1024 * 1024);
+ QuicTime end_time =
+ simulator_.GetClock()->Now() + QuicTime::Delta::FromSeconds(10);
+ simulator_.RunUntil(
+ [this, end_time]() { return simulator_.GetClock()->Now() >= end_time; });
+
+ for (QuicEndpoint* endpoint :
+ {endpoint_a.get(), endpoint_b.get(), endpoint_c.get()}) {
+ EXPECT_EQ(2u * 1024u * 1024u, endpoint->bytes_transferred());
+ EXPECT_GE(endpoint->connection()->GetStats().packets_lost, 0u);
+ }
+ for (QuicEndpoint* endpoint :
+ {endpoint_d_a.get(), endpoint_d_b.get(), endpoint_d_c.get()}) {
+ EXPECT_EQ(2u * 1024u * 1024u, endpoint->bytes_received());
+ EXPECT_FALSE(endpoint->wrong_data_received());
+ }
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/quic/test_tools/simulator/simulator.cc b/quic/test_tools/simulator/simulator.cc
new file mode 100644
index 0000000..fdb59db
--- /dev/null
+++ b/quic/test_tools/simulator/simulator.cc
@@ -0,0 +1,167 @@
+// Copyright (c) 2012 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 "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+namespace simulator {
+
+Simulator::Simulator()
+ : random_generator_(nullptr),
+ alarm_factory_(this, "Default Alarm Manager"),
+ run_for_should_stop_(false),
+ enable_random_delays_(false) {
+ run_for_alarm_.reset(
+ alarm_factory_.CreateAlarm(new RunForDelegate(&run_for_should_stop_)));
+}
+
+Simulator::~Simulator() {
+ // Ensure that Actor under run_for_alarm_ is removed before Simulator data
+ // structures are destructed.
+ run_for_alarm_.reset();
+}
+
+Simulator::Clock::Clock() : now_(kStartTime) {}
+
+QuicTime Simulator::Clock::ApproximateNow() const {
+ return now_;
+}
+
+QuicTime Simulator::Clock::Now() const {
+ return now_;
+}
+
+QuicWallTime Simulator::Clock::WallNow() const {
+ return QuicWallTime::FromUNIXMicroseconds(
+ (now_ - QuicTime::Zero()).ToMicroseconds());
+}
+
+void Simulator::AddActor(Actor* actor) {
+ auto emplace_times_result =
+ scheduled_times_.insert(std::make_pair(actor, QuicTime::Infinite()));
+ auto emplace_names_result = actor_names_.insert(actor->name());
+
+ // Ensure that the object was actually placed into the map.
+ DCHECK(emplace_times_result.second);
+ DCHECK(emplace_names_result.second);
+}
+
+void Simulator::RemoveActor(Actor* actor) {
+ auto scheduled_time_it = scheduled_times_.find(actor);
+ auto actor_names_it = actor_names_.find(actor->name());
+ DCHECK(scheduled_time_it != scheduled_times_.end());
+ DCHECK(actor_names_it != actor_names_.end());
+
+ QuicTime scheduled_time = scheduled_time_it->second;
+ if (scheduled_time != QuicTime::Infinite()) {
+ Unschedule(actor);
+ }
+
+ scheduled_times_.erase(scheduled_time_it);
+ actor_names_.erase(actor_names_it);
+}
+
+void Simulator::Schedule(Actor* actor, QuicTime new_time) {
+ auto scheduled_time_it = scheduled_times_.find(actor);
+ DCHECK(scheduled_time_it != scheduled_times_.end());
+ QuicTime scheduled_time = scheduled_time_it->second;
+
+ if (scheduled_time <= new_time) {
+ return;
+ }
+
+ if (scheduled_time != QuicTime::Infinite()) {
+ Unschedule(actor);
+ }
+
+ scheduled_time_it->second = new_time;
+ schedule_.insert(std::make_pair(new_time, actor));
+}
+
+void Simulator::Unschedule(Actor* actor) {
+ auto scheduled_time_it = scheduled_times_.find(actor);
+ DCHECK(scheduled_time_it != scheduled_times_.end());
+ QuicTime scheduled_time = scheduled_time_it->second;
+
+ DCHECK(scheduled_time != QuicTime::Infinite());
+ auto range = schedule_.equal_range(scheduled_time);
+ for (auto it = range.first; it != range.second; ++it) {
+ if (it->second == actor) {
+ schedule_.erase(it);
+ scheduled_time_it->second = QuicTime::Infinite();
+ return;
+ }
+ }
+ DCHECK(false);
+}
+
+const QuicClock* Simulator::GetClock() const {
+ return &clock_;
+}
+
+QuicRandom* Simulator::GetRandomGenerator() {
+ if (random_generator_ == nullptr) {
+ random_generator_ = QuicRandom::GetInstance();
+ }
+
+ return random_generator_;
+}
+
+QuicBufferAllocator* Simulator::GetStreamSendBufferAllocator() {
+ return &buffer_allocator_;
+}
+
+QuicAlarmFactory* Simulator::GetAlarmFactory() {
+ return &alarm_factory_;
+}
+
+Simulator::RunForDelegate::RunForDelegate(bool* run_for_should_stop)
+ : run_for_should_stop_(run_for_should_stop) {}
+
+void Simulator::RunForDelegate::OnAlarm() {
+ *run_for_should_stop_ = true;
+}
+
+void Simulator::RunFor(QuicTime::Delta time_span) {
+ DCHECK(!run_for_alarm_->IsSet());
+
+ // RunFor() ensures that the simulation stops at the exact time specified by
+ // scheduling an alarm at that point and using that alarm to abort the
+ // simulation. An alarm is necessary because otherwise it is possible that
+ // nothing is scheduled at |end_time|, so the simulation will either go
+ // further than requested or stop before reaching |end_time|.
+ const QuicTime end_time = clock_.Now() + time_span;
+ run_for_alarm_->Set(end_time);
+ run_for_should_stop_ = false;
+ bool simulation_result = RunUntil([this]() { return run_for_should_stop_; });
+
+ DCHECK(simulation_result);
+ DCHECK(clock_.Now() == end_time);
+}
+
+void Simulator::HandleNextScheduledActor() {
+ const auto current_event_it = schedule_.begin();
+ QuicTime event_time = current_event_it->first;
+ Actor* actor = current_event_it->second;
+ QUIC_DVLOG(3) << "At t = " << event_time.ToDebuggingValue() << ", calling "
+ << actor->name();
+
+ Unschedule(actor);
+
+ if (clock_.Now() > event_time) {
+ QUIC_BUG << "Error: event registered by [" << actor->name()
+ << "] requires travelling back in time. Current time: "
+ << clock_.Now().ToDebuggingValue()
+ << ", scheduled time: " << event_time.ToDebuggingValue();
+ }
+ clock_.now_ = event_time;
+
+ actor->Act();
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/quic/test_tools/simulator/simulator.h b/quic/test_tools/simulator/simulator.h
new file mode 100644
index 0000000..0180dcb
--- /dev/null
+++ b/quic/test_tools/simulator/simulator.h
@@ -0,0 +1,166 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMULATOR_SIMULATOR_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_SIMULATOR_H_
+
+#include <map>
+
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/actor.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/alarm_factory.h"
+
+namespace quic {
+namespace simulator {
+
+// Simulator is responsible for scheduling actors in the simulation and
+// providing basic utility interfaces (clock, alarms, RNG and others).
+class Simulator : public QuicConnectionHelperInterface {
+ public:
+ Simulator();
+ Simulator(const Simulator&) = delete;
+ Simulator& operator=(const Simulator&) = delete;
+ ~Simulator() override;
+
+ // Schedule the specified actor. This method will ensure that |actor| is
+ // called at |new_time| at latest. If Schedule() is called multiple times
+ // before the Actor is called, Act() is called exactly once, at the earliest
+ // time requested, and the Actor has to reschedule itself manually for the
+ // subsequent times if they are still necessary.
+ void Schedule(Actor* actor, QuicTime new_time);
+
+ // Remove the specified actor from the schedule.
+ void Unschedule(Actor* actor);
+
+ // Begin QuicConnectionHelperInterface implementation.
+ const QuicClock* GetClock() const override;
+ QuicRandom* GetRandomGenerator() override;
+ QuicBufferAllocator* GetStreamSendBufferAllocator() override;
+ // End QuicConnectionHelperInterface implementation.
+
+ QuicAlarmFactory* GetAlarmFactory();
+
+ inline void set_random_generator(QuicRandom* random) {
+ random_generator_ = random;
+ }
+
+ inline bool enable_random_delays() const { return enable_random_delays_; }
+
+ // Run the simulation until either no actors are scheduled or
+ // |termination_predicate| returns true. Returns true if terminated due to
+ // predicate, and false otherwise.
+ template <class TerminationPredicate>
+ bool RunUntil(TerminationPredicate termination_predicate);
+
+ // Same as RunUntil, except this function also accepts a |deadline|, and will
+ // return false if the deadline is exceeded.
+ template <class TerminationPredicate>
+ bool RunUntilOrTimeout(TerminationPredicate termination_predicate,
+ QuicTime::Delta deadline);
+
+ // Runs the simulation for exactly the specified |time_span|.
+ void RunFor(QuicTime::Delta time_span);
+
+ private:
+ friend class Actor;
+
+ class Clock : public QuicClock {
+ public:
+ // Do not start at zero as certain code can treat zero as an invalid
+ // timestamp.
+ const QuicTime kStartTime =
+ QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(1);
+
+ Clock();
+
+ QuicTime ApproximateNow() const override;
+ QuicTime Now() const override;
+ QuicWallTime WallNow() const override;
+
+ QuicTime now_;
+ };
+
+ // The delegate used for RunFor().
+ class RunForDelegate : public QuicAlarm::Delegate {
+ public:
+ explicit RunForDelegate(bool* run_for_should_stop);
+ void OnAlarm() override;
+
+ private:
+ // Pointer to |run_for_should_stop_| in the parent simulator.
+ bool* run_for_should_stop_;
+ };
+
+ // Register an actor with the simulator. Invoked by Actor constructor.
+ void AddActor(Actor* actor);
+
+ // Unregister an actor with the simulator. Invoked by Actor destructor.
+ void RemoveActor(Actor* actor);
+
+ // Finds the next scheduled actor, advances time to the schedule time and
+ // notifies the actor.
+ void HandleNextScheduledActor();
+
+ Clock clock_;
+ QuicRandom* random_generator_;
+ SimpleBufferAllocator buffer_allocator_;
+ AlarmFactory alarm_factory_;
+
+ // Alarm for RunFor() method.
+ std::unique_ptr<QuicAlarm> run_for_alarm_;
+ // Flag used to stop simulations ran via RunFor().
+ bool run_for_should_stop_;
+
+ // Indicates whether the simulator should add random delays on the links in
+ // order to avoid synchronization issues.
+ bool enable_random_delays_;
+
+ // Schedule of when the actors will be executed via an Act() call. The
+ // schedule is subject to the following invariants:
+ // - An actor cannot be scheduled for a later time than it's currently in the
+ // schedule.
+ // - An actor is removed from schedule either immediately before Act() is
+ // called or by explicitly calling Unschedule().
+ // - Each Actor appears in the map at most once.
+ std::multimap<QuicTime, Actor*> schedule_;
+ // For each actor, maintain the time it is scheduled at. The value for
+ // unscheduled actors is QuicTime::Infinite().
+ QuicUnorderedMap<Actor*, QuicTime> scheduled_times_;
+ QuicUnorderedSet<QuicString> actor_names_;
+};
+
+template <class TerminationPredicate>
+bool Simulator::RunUntil(TerminationPredicate termination_predicate) {
+ bool predicate_value = false;
+ while (true) {
+ predicate_value = termination_predicate();
+ if (predicate_value || schedule_.empty()) {
+ break;
+ }
+ HandleNextScheduledActor();
+ }
+ return predicate_value;
+}
+
+template <class TerminationPredicate>
+bool Simulator::RunUntilOrTimeout(TerminationPredicate termination_predicate,
+ QuicTime::Delta timeout) {
+ QuicTime end_time = clock_.Now() + timeout;
+ bool return_value = RunUntil([end_time, &termination_predicate, this]() {
+ return termination_predicate() || clock_.Now() >= end_time;
+ });
+
+ if (clock_.Now() >= end_time) {
+ return false;
+ }
+ return return_value;
+}
+
+} // namespace simulator
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMULATOR_SIMULATOR_H_
diff --git a/quic/test_tools/simulator/simulator_test.cc b/quic/test_tools/simulator/simulator_test.cc
new file mode 100644
index 0000000..d9986ad
--- /dev/null
+++ b/quic/test_tools/simulator/simulator_test.cc
@@ -0,0 +1,829 @@
+// Copyright (c) 2012 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 "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/alarm_factory.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/link.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/packet_filter.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/queue.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/traffic_policer.h"
+
+using testing::_;
+using testing::Return;
+using testing::StrictMock;
+
+namespace quic {
+namespace simulator {
+
+// A simple counter that increments its value by 1 every specified period.
+class Counter : public Actor {
+ public:
+ Counter(Simulator* simulator, QuicString name, QuicTime::Delta period)
+ : Actor(simulator, name), value_(-1), period_(period) {
+ Schedule(clock_->Now());
+ }
+ ~Counter() override {}
+
+ inline int get_value() const { return value_; }
+
+ void Act() override {
+ ++value_;
+ QUIC_DVLOG(1) << name_ << " has value " << value_ << " at time "
+ << clock_->Now().ToDebuggingValue();
+ Schedule(clock_->Now() + period_);
+ }
+
+ private:
+ int value_;
+ QuicTime::Delta period_;
+};
+
+class SimulatorTest : public QuicTest {};
+
+// Test that the basic event handling works, and that Actors can be created and
+// destroyed mid-simulation.
+TEST_F(SimulatorTest, Counters) {
+ Simulator simulator;
+ for (int i = 0; i < 2; ++i) {
+ Counter fast_counter(&simulator, "fast_counter",
+ QuicTime::Delta::FromSeconds(3));
+ Counter slow_counter(&simulator, "slow_counter",
+ QuicTime::Delta::FromSeconds(10));
+
+ simulator.RunUntil(
+ [&slow_counter]() { return slow_counter.get_value() >= 10; });
+
+ EXPECT_EQ(10, slow_counter.get_value());
+ EXPECT_EQ(10 * 10 / 3, fast_counter.get_value());
+ }
+}
+
+// A port which counts the number of packets received on it, both total and
+// per-destination.
+class CounterPort : public UnconstrainedPortInterface {
+ public:
+ CounterPort() { Reset(); }
+ ~CounterPort() override {}
+
+ inline QuicByteCount bytes() const { return bytes_; }
+ inline QuicPacketCount packets() const { return packets_; }
+
+ void AcceptPacket(std::unique_ptr<Packet> packet) override {
+ bytes_ += packet->size;
+ packets_ += 1;
+
+ per_destination_packet_counter_[packet->destination] += 1;
+ }
+
+ void Reset() {
+ bytes_ = 0;
+ packets_ = 0;
+ per_destination_packet_counter_.clear();
+ }
+
+ QuicPacketCount CountPacketsForDestination(QuicString destination) const {
+ auto result_it = per_destination_packet_counter_.find(destination);
+ if (result_it == per_destination_packet_counter_.cend()) {
+ return 0;
+ }
+ return result_it->second;
+ }
+
+ private:
+ QuicByteCount bytes_;
+ QuicPacketCount packets_;
+
+ QuicUnorderedMap<QuicString, QuicPacketCount> per_destination_packet_counter_;
+};
+
+// Sends the packet to the specified destination at the uplink rate. Provides a
+// CounterPort as an Rx interface.
+class LinkSaturator : public Endpoint {
+ public:
+ LinkSaturator(Simulator* simulator,
+ QuicString name,
+ QuicByteCount packet_size,
+ QuicString destination)
+ : Endpoint(simulator, name),
+ packet_size_(packet_size),
+ destination_(std::move(destination)),
+ bytes_transmitted_(0),
+ packets_transmitted_(0) {
+ Schedule(clock_->Now());
+ }
+
+ void Act() override {
+ if (tx_port_->TimeUntilAvailable().IsZero()) {
+ auto packet = QuicMakeUnique<Packet>();
+ packet->source = name_;
+ packet->destination = destination_;
+ packet->tx_timestamp = clock_->Now();
+ packet->size = packet_size_;
+
+ tx_port_->AcceptPacket(std::move(packet));
+
+ bytes_transmitted_ += packet_size_;
+ packets_transmitted_ += 1;
+ }
+
+ Schedule(clock_->Now() + tx_port_->TimeUntilAvailable());
+ }
+
+ UnconstrainedPortInterface* GetRxPort() override {
+ return static_cast<UnconstrainedPortInterface*>(&rx_port_);
+ }
+
+ void SetTxPort(ConstrainedPortInterface* port) override { tx_port_ = port; }
+
+ CounterPort* counter() { return &rx_port_; }
+
+ inline QuicByteCount bytes_transmitted() const { return bytes_transmitted_; }
+ inline QuicPacketCount packets_transmitted() const {
+ return packets_transmitted_;
+ }
+
+ void Pause() { Unschedule(); }
+ void Resume() { Schedule(clock_->Now()); }
+
+ private:
+ QuicByteCount packet_size_;
+ QuicString destination_;
+
+ ConstrainedPortInterface* tx_port_;
+ CounterPort rx_port_;
+
+ QuicByteCount bytes_transmitted_;
+ QuicPacketCount packets_transmitted_;
+};
+
+// Saturate a symmetric link and verify that the number of packets sent and
+// received is correct.
+TEST_F(SimulatorTest, DirectLinkSaturation) {
+ Simulator simulator;
+ LinkSaturator saturator_a(&simulator, "Saturator A", 1000, "Saturator B");
+ LinkSaturator saturator_b(&simulator, "Saturator B", 100, "Saturator A");
+ SymmetricLink link(&saturator_a, &saturator_b,
+ QuicBandwidth::FromKBytesPerSecond(1000),
+ QuicTime::Delta::FromMilliseconds(100) +
+ QuicTime::Delta::FromMicroseconds(1));
+
+ const QuicTime start_time = simulator.GetClock()->Now();
+ const QuicTime after_first_50_ms =
+ start_time + QuicTime::Delta::FromMilliseconds(50);
+ simulator.RunUntil([&simulator, after_first_50_ms]() {
+ return simulator.GetClock()->Now() >= after_first_50_ms;
+ });
+ EXPECT_LE(1000u * 50u, saturator_a.bytes_transmitted());
+ EXPECT_GE(1000u * 51u, saturator_a.bytes_transmitted());
+ EXPECT_LE(1000u * 50u, saturator_b.bytes_transmitted());
+ EXPECT_GE(1000u * 51u, saturator_b.bytes_transmitted());
+ EXPECT_LE(50u, saturator_a.packets_transmitted());
+ EXPECT_GE(51u, saturator_a.packets_transmitted());
+ EXPECT_LE(500u, saturator_b.packets_transmitted());
+ EXPECT_GE(501u, saturator_b.packets_transmitted());
+ EXPECT_EQ(0u, saturator_a.counter()->bytes());
+ EXPECT_EQ(0u, saturator_b.counter()->bytes());
+
+ simulator.RunUntil([&saturator_a, &saturator_b]() {
+ if (saturator_a.counter()->packets() > 1000 ||
+ saturator_b.counter()->packets() > 100) {
+ ADD_FAILURE() << "The simulation did not arrive at the expected "
+ "termination contidition. Saturator A counter: "
+ << saturator_a.counter()->packets()
+ << ", saturator B counter: "
+ << saturator_b.counter()->packets();
+ return true;
+ }
+
+ return saturator_a.counter()->packets() == 1000 &&
+ saturator_b.counter()->packets() == 100;
+ });
+ EXPECT_EQ(201u, saturator_a.packets_transmitted());
+ EXPECT_EQ(2001u, saturator_b.packets_transmitted());
+ EXPECT_EQ(201u * 1000, saturator_a.bytes_transmitted());
+ EXPECT_EQ(2001u * 100, saturator_b.bytes_transmitted());
+
+ EXPECT_EQ(1000u,
+ saturator_a.counter()->CountPacketsForDestination("Saturator A"));
+ EXPECT_EQ(100u,
+ saturator_b.counter()->CountPacketsForDestination("Saturator B"));
+ EXPECT_EQ(0u,
+ saturator_a.counter()->CountPacketsForDestination("Saturator B"));
+ EXPECT_EQ(0u,
+ saturator_b.counter()->CountPacketsForDestination("Saturator A"));
+
+ const QuicTime end_time = simulator.GetClock()->Now();
+ const QuicBandwidth observed_bandwidth = QuicBandwidth::FromBytesAndTimeDelta(
+ saturator_a.bytes_transmitted(), end_time - start_time);
+ test::ExpectApproxEq(link.bandwidth(), observed_bandwidth, 0.01f);
+}
+
+// Accepts packets and stores them internally.
+class PacketAcceptor : public ConstrainedPortInterface {
+ public:
+ void AcceptPacket(std::unique_ptr<Packet> packet) override {
+ packets_.emplace_back(std::move(packet));
+ }
+
+ QuicTime::Delta TimeUntilAvailable() override {
+ return QuicTime::Delta::Zero();
+ }
+
+ std::vector<std::unique_ptr<Packet>>* packets() { return &packets_; }
+
+ private:
+ std::vector<std::unique_ptr<Packet>> packets_;
+};
+
+// Ensure the queue behaves correctly with accepting packets.
+TEST_F(SimulatorTest, Queue) {
+ Simulator simulator;
+ Queue queue(&simulator, "Queue", 1000);
+ PacketAcceptor acceptor;
+ queue.set_tx_port(&acceptor);
+
+ EXPECT_EQ(0u, queue.bytes_queued());
+ EXPECT_EQ(0u, queue.packets_queued());
+ EXPECT_EQ(0u, acceptor.packets()->size());
+
+ auto first_packet = QuicMakeUnique<Packet>();
+ first_packet->size = 600;
+ queue.AcceptPacket(std::move(first_packet));
+ EXPECT_EQ(600u, queue.bytes_queued());
+ EXPECT_EQ(1u, queue.packets_queued());
+ EXPECT_EQ(0u, acceptor.packets()->size());
+
+ // The second packet does not fit and is dropped.
+ auto second_packet = QuicMakeUnique<Packet>();
+ second_packet->size = 500;
+ queue.AcceptPacket(std::move(second_packet));
+ EXPECT_EQ(600u, queue.bytes_queued());
+ EXPECT_EQ(1u, queue.packets_queued());
+ EXPECT_EQ(0u, acceptor.packets()->size());
+
+ auto third_packet = QuicMakeUnique<Packet>();
+ third_packet->size = 400;
+ queue.AcceptPacket(std::move(third_packet));
+ EXPECT_EQ(1000u, queue.bytes_queued());
+ EXPECT_EQ(2u, queue.packets_queued());
+ EXPECT_EQ(0u, acceptor.packets()->size());
+
+ // Run until there is nothing scheduled, so that the queue can deplete.
+ simulator.RunUntil([]() { return false; });
+ EXPECT_EQ(0u, queue.bytes_queued());
+ EXPECT_EQ(0u, queue.packets_queued());
+ ASSERT_EQ(2u, acceptor.packets()->size());
+ EXPECT_EQ(600u, acceptor.packets()->at(0)->size);
+ EXPECT_EQ(400u, acceptor.packets()->at(1)->size);
+}
+
+// Simulate a situation where the bottleneck link is 10 times slower than the
+// uplink, and they are separated by a queue.
+TEST_F(SimulatorTest, QueueBottleneck) {
+ const QuicBandwidth local_bandwidth =
+ QuicBandwidth::FromKBytesPerSecond(1000);
+ const QuicBandwidth bottleneck_bandwidth = 0.1f * local_bandwidth;
+ const QuicTime::Delta local_propagation_delay =
+ QuicTime::Delta::FromMilliseconds(1);
+ const QuicTime::Delta bottleneck_propagation_delay =
+ QuicTime::Delta::FromMilliseconds(20);
+ const QuicByteCount bdp =
+ bottleneck_bandwidth *
+ (local_propagation_delay + bottleneck_propagation_delay);
+
+ Simulator simulator;
+ LinkSaturator saturator(&simulator, "Saturator", 1000, "Counter");
+ ASSERT_GE(bdp, 1000u);
+ Queue queue(&simulator, "Queue", bdp);
+ CounterPort counter;
+
+ OneWayLink local_link(&simulator, "Local link", &queue, local_bandwidth,
+ local_propagation_delay);
+ OneWayLink bottleneck_link(&simulator, "Bottleneck link", &counter,
+ bottleneck_bandwidth,
+ bottleneck_propagation_delay);
+ saturator.SetTxPort(&local_link);
+ queue.set_tx_port(&bottleneck_link);
+
+ static const QuicPacketCount packets_received = 1000;
+ simulator.RunUntil(
+ [&counter]() { return counter.packets() == packets_received; });
+ const double loss_ratio = 1 - static_cast<double>(packets_received) /
+ saturator.packets_transmitted();
+ EXPECT_NEAR(loss_ratio, 0.9, 0.001);
+}
+
+// Verify that the queue of exactly one packet allows the transmission to
+// actually go through.
+TEST_F(SimulatorTest, OnePacketQueue) {
+ const QuicBandwidth local_bandwidth =
+ QuicBandwidth::FromKBytesPerSecond(1000);
+ const QuicBandwidth bottleneck_bandwidth = 0.1f * local_bandwidth;
+ const QuicTime::Delta local_propagation_delay =
+ QuicTime::Delta::FromMilliseconds(1);
+ const QuicTime::Delta bottleneck_propagation_delay =
+ QuicTime::Delta::FromMilliseconds(20);
+
+ Simulator simulator;
+ LinkSaturator saturator(&simulator, "Saturator", 1000, "Counter");
+ Queue queue(&simulator, "Queue", 1000);
+ CounterPort counter;
+
+ OneWayLink local_link(&simulator, "Local link", &queue, local_bandwidth,
+ local_propagation_delay);
+ OneWayLink bottleneck_link(&simulator, "Bottleneck link", &counter,
+ bottleneck_bandwidth,
+ bottleneck_propagation_delay);
+ saturator.SetTxPort(&local_link);
+ queue.set_tx_port(&bottleneck_link);
+
+ static const QuicPacketCount packets_received = 10;
+ // The deadline here is to prevent this tests from looping infinitely in case
+ // the packets never reach the receiver.
+ const QuicTime deadline =
+ simulator.GetClock()->Now() + QuicTime::Delta::FromSeconds(10);
+ simulator.RunUntil([&simulator, &counter, deadline]() {
+ return counter.packets() == packets_received ||
+ simulator.GetClock()->Now() > deadline;
+ });
+ ASSERT_EQ(packets_received, counter.packets());
+}
+
+// Simulate a network where three endpoints are connected to a switch and they
+// are sending traffic in circle (1 -> 2, 2 -> 3, 3 -> 1).
+TEST_F(SimulatorTest, SwitchedNetwork) {
+ const QuicBandwidth bandwidth = QuicBandwidth::FromBytesPerSecond(10000);
+ const QuicTime::Delta base_propagation_delay =
+ QuicTime::Delta::FromMilliseconds(50);
+
+ Simulator simulator;
+ LinkSaturator saturator1(&simulator, "Saturator 1", 1000, "Saturator 2");
+ LinkSaturator saturator2(&simulator, "Saturator 2", 1000, "Saturator 3");
+ LinkSaturator saturator3(&simulator, "Saturator 3", 1000, "Saturator 1");
+ Switch network_switch(&simulator, "Switch", 8,
+ bandwidth * base_propagation_delay * 10);
+
+ // For determinicity, make it so that the first packet will arrive from
+ // Saturator 1, then from Saturator 2, and then from Saturator 3.
+ SymmetricLink link1(&saturator1, network_switch.port(1), bandwidth,
+ base_propagation_delay);
+ SymmetricLink link2(&saturator2, network_switch.port(2), bandwidth,
+ base_propagation_delay * 2);
+ SymmetricLink link3(&saturator3, network_switch.port(3), bandwidth,
+ base_propagation_delay * 3);
+
+ const QuicTime start_time = simulator.GetClock()->Now();
+ static const QuicPacketCount bytes_received = 64 * 1000;
+ simulator.RunUntil([&saturator1]() {
+ return saturator1.counter()->bytes() >= bytes_received;
+ });
+ const QuicTime end_time = simulator.GetClock()->Now();
+
+ const QuicBandwidth observed_bandwidth = QuicBandwidth::FromBytesAndTimeDelta(
+ bytes_received, end_time - start_time);
+ const double bandwidth_ratio =
+ static_cast<double>(observed_bandwidth.ToBitsPerSecond()) /
+ bandwidth.ToBitsPerSecond();
+ EXPECT_NEAR(1, bandwidth_ratio, 0.1);
+
+ const double normalized_received_packets_for_saturator_2 =
+ static_cast<double>(saturator2.counter()->packets()) /
+ saturator1.counter()->packets();
+ const double normalized_received_packets_for_saturator_3 =
+ static_cast<double>(saturator3.counter()->packets()) /
+ saturator1.counter()->packets();
+ EXPECT_NEAR(1, normalized_received_packets_for_saturator_2, 0.1);
+ EXPECT_NEAR(1, normalized_received_packets_for_saturator_3, 0.1);
+
+ // Since Saturator 1 has its packet arrive first into the switch, switch will
+ // always know how to route traffic to it.
+ EXPECT_EQ(0u,
+ saturator2.counter()->CountPacketsForDestination("Saturator 1"));
+ EXPECT_EQ(0u,
+ saturator3.counter()->CountPacketsForDestination("Saturator 1"));
+
+ // Packets from the other saturators will be broadcast at least once.
+ EXPECT_EQ(1u,
+ saturator1.counter()->CountPacketsForDestination("Saturator 2"));
+ EXPECT_EQ(1u,
+ saturator3.counter()->CountPacketsForDestination("Saturator 2"));
+ EXPECT_EQ(1u,
+ saturator1.counter()->CountPacketsForDestination("Saturator 3"));
+ EXPECT_EQ(1u,
+ saturator2.counter()->CountPacketsForDestination("Saturator 3"));
+}
+
+// Toggle an alarm on and off at the specified interval. Assumes that alarm is
+// initially set and unsets it almost immediately after the object is
+// instantiated.
+class AlarmToggler : public Actor {
+ public:
+ AlarmToggler(Simulator* simulator,
+ QuicString name,
+ QuicAlarm* alarm,
+ QuicTime::Delta interval)
+ : Actor(simulator, name),
+ alarm_(alarm),
+ interval_(interval),
+ deadline_(alarm->deadline()),
+ times_set_(0),
+ times_cancelled_(0) {
+ EXPECT_TRUE(alarm->IsSet());
+ EXPECT_GE(alarm->deadline(), clock_->Now());
+ Schedule(clock_->Now());
+ }
+
+ void Act() override {
+ if (deadline_ <= clock_->Now()) {
+ return;
+ }
+
+ if (alarm_->IsSet()) {
+ alarm_->Cancel();
+ times_cancelled_++;
+ } else {
+ alarm_->Set(deadline_);
+ times_set_++;
+ }
+
+ Schedule(clock_->Now() + interval_);
+ }
+
+ inline int times_set() { return times_set_; }
+ inline int times_cancelled() { return times_cancelled_; }
+
+ private:
+ QuicAlarm* alarm_;
+ QuicTime::Delta interval_;
+ QuicTime deadline_;
+
+ // Counts the number of times the alarm was set.
+ int times_set_;
+ // Counts the number of times the alarm was cancelled.
+ int times_cancelled_;
+};
+
+// Counts the number of times an alarm has fired.
+class CounterDelegate : public QuicAlarm::Delegate {
+ public:
+ explicit CounterDelegate(size_t* counter) : counter_(counter) {}
+
+ void OnAlarm() override { *counter_ += 1; }
+
+ private:
+ size_t* counter_;
+};
+
+// Verifies that the alarms work correctly, even when they are repeatedly
+// toggled.
+TEST_F(SimulatorTest, Alarms) {
+ Simulator simulator;
+ QuicAlarmFactory* alarm_factory = simulator.GetAlarmFactory();
+
+ size_t fast_alarm_counter = 0;
+ size_t slow_alarm_counter = 0;
+ std::unique_ptr<QuicAlarm> alarm_fast(
+ alarm_factory->CreateAlarm(new CounterDelegate(&fast_alarm_counter)));
+ std::unique_ptr<QuicAlarm> alarm_slow(
+ alarm_factory->CreateAlarm(new CounterDelegate(&slow_alarm_counter)));
+
+ const QuicTime start_time = simulator.GetClock()->Now();
+ alarm_fast->Set(start_time + QuicTime::Delta::FromMilliseconds(100));
+ alarm_slow->Set(start_time + QuicTime::Delta::FromMilliseconds(750));
+ AlarmToggler toggler(&simulator, "Toggler", alarm_slow.get(),
+ QuicTime::Delta::FromMilliseconds(100));
+
+ const QuicTime end_time =
+ start_time + QuicTime::Delta::FromMilliseconds(1000);
+ EXPECT_FALSE(simulator.RunUntil([&simulator, end_time]() {
+ return simulator.GetClock()->Now() >= end_time;
+ }));
+ EXPECT_EQ(1u, slow_alarm_counter);
+ EXPECT_EQ(1u, fast_alarm_counter);
+
+ EXPECT_EQ(4, toggler.times_set());
+ EXPECT_EQ(4, toggler.times_cancelled());
+}
+
+// Verifies that a cancelled alarm is never fired.
+TEST_F(SimulatorTest, AlarmCancelling) {
+ Simulator simulator;
+ QuicAlarmFactory* alarm_factory = simulator.GetAlarmFactory();
+
+ size_t alarm_counter = 0;
+ std::unique_ptr<QuicAlarm> alarm(
+ alarm_factory->CreateAlarm(new CounterDelegate(&alarm_counter)));
+
+ const QuicTime start_time = simulator.GetClock()->Now();
+ const QuicTime alarm_at = start_time + QuicTime::Delta::FromMilliseconds(300);
+ const QuicTime end_time = start_time + QuicTime::Delta::FromMilliseconds(400);
+
+ alarm->Set(alarm_at);
+ alarm->Cancel();
+ EXPECT_FALSE(alarm->IsSet());
+
+ EXPECT_FALSE(simulator.RunUntil([&simulator, end_time]() {
+ return simulator.GetClock()->Now() >= end_time;
+ }));
+
+ EXPECT_FALSE(alarm->IsSet());
+ EXPECT_EQ(0u, alarm_counter);
+}
+
+// Verifies that alarms can be scheduled into the past.
+TEST_F(SimulatorTest, AlarmInPast) {
+ Simulator simulator;
+ QuicAlarmFactory* alarm_factory = simulator.GetAlarmFactory();
+
+ size_t alarm_counter = 0;
+ std::unique_ptr<QuicAlarm> alarm(
+ alarm_factory->CreateAlarm(new CounterDelegate(&alarm_counter)));
+
+ const QuicTime start_time = simulator.GetClock()->Now();
+ simulator.RunFor(QuicTime::Delta::FromMilliseconds(400));
+
+ alarm->Set(start_time);
+ simulator.RunFor(QuicTime::Delta::FromMilliseconds(1));
+ EXPECT_FALSE(alarm->IsSet());
+ EXPECT_EQ(1u, alarm_counter);
+}
+
+// Tests Simulator::RunUntilOrTimeout() interface.
+TEST_F(SimulatorTest, RunUntilOrTimeout) {
+ Simulator simulator;
+ bool simulation_result;
+
+ // Count the number of seconds since the beginning of the simulation.
+ Counter counter(&simulator, "counter", QuicTime::Delta::FromSeconds(1));
+
+ // Ensure that the counter reaches the value of 10 given a 20 second deadline.
+ simulation_result = simulator.RunUntilOrTimeout(
+ [&counter]() { return counter.get_value() == 10; },
+ QuicTime::Delta::FromSeconds(20));
+ ASSERT_TRUE(simulation_result);
+
+ // Ensure that the counter will not reach the value of 100 given that the
+ // starting value is 10 and the deadline is 20 seconds.
+ simulation_result = simulator.RunUntilOrTimeout(
+ [&counter]() { return counter.get_value() == 100; },
+ QuicTime::Delta::FromSeconds(20));
+ ASSERT_FALSE(simulation_result);
+}
+
+// Tests Simulator::RunFor() interface.
+TEST_F(SimulatorTest, RunFor) {
+ Simulator simulator;
+
+ Counter counter(&simulator, "counter", QuicTime::Delta::FromSeconds(3));
+
+ simulator.RunFor(QuicTime::Delta::FromSeconds(100));
+
+ EXPECT_EQ(33, counter.get_value());
+}
+
+class MockPacketFilter : public PacketFilter {
+ public:
+ MockPacketFilter(Simulator* simulator, QuicString name, Endpoint* endpoint)
+ : PacketFilter(simulator, name, endpoint) {}
+ MOCK_METHOD1(FilterPacket, bool(const Packet&));
+};
+
+// Set up two trivial packet filters, one allowing any packets, and one dropping
+// all of them.
+TEST_F(SimulatorTest, PacketFilter) {
+ const QuicBandwidth bandwidth =
+ QuicBandwidth::FromBytesPerSecond(1024 * 1024);
+ const QuicTime::Delta base_propagation_delay =
+ QuicTime::Delta::FromMilliseconds(5);
+
+ Simulator simulator;
+ LinkSaturator saturator_a(&simulator, "Saturator A", 1000, "Saturator B");
+ LinkSaturator saturator_b(&simulator, "Saturator B", 1000, "Saturator A");
+
+ // Attach packets to the switch to create a delay between the point at which
+ // the packet is generated and the point at which it is filtered. Note that
+ // if the saturators were connected directly, the link would be always
+ // available for the endpoint which has all of its packets dropped, resulting
+ // in saturator looping infinitely.
+ Switch network_switch(&simulator, "Switch", 8,
+ bandwidth * base_propagation_delay * 10);
+ StrictMock<MockPacketFilter> a_to_b_filter(&simulator, "A -> B filter",
+ network_switch.port(1));
+ StrictMock<MockPacketFilter> b_to_a_filter(&simulator, "B -> A filter",
+ network_switch.port(2));
+ SymmetricLink link_a(&a_to_b_filter, &saturator_b, bandwidth,
+ base_propagation_delay);
+ SymmetricLink link_b(&b_to_a_filter, &saturator_a, bandwidth,
+ base_propagation_delay);
+
+ // Allow packets from A to B, but not from B to A.
+ EXPECT_CALL(a_to_b_filter, FilterPacket(_)).WillRepeatedly(Return(true));
+ EXPECT_CALL(b_to_a_filter, FilterPacket(_)).WillRepeatedly(Return(false));
+
+ // Run the simulation for a while, and expect that only B will receive any
+ // packets.
+ simulator.RunFor(QuicTime::Delta::FromSeconds(10));
+ EXPECT_GE(saturator_b.counter()->packets(), 1u);
+ EXPECT_EQ(saturator_a.counter()->packets(), 0u);
+}
+
+// Set up a traffic policer in one direction that throttles at 25% of link
+// bandwidth, and put two link saturators at each endpoint.
+TEST_F(SimulatorTest, TrafficPolicer) {
+ const QuicBandwidth bandwidth =
+ QuicBandwidth::FromBytesPerSecond(1024 * 1024);
+ const QuicTime::Delta base_propagation_delay =
+ QuicTime::Delta::FromMilliseconds(5);
+ const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(10);
+
+ Simulator simulator;
+ LinkSaturator saturator1(&simulator, "Saturator 1", 1000, "Saturator 2");
+ LinkSaturator saturator2(&simulator, "Saturator 2", 1000, "Saturator 1");
+ Switch network_switch(&simulator, "Switch", 8,
+ bandwidth * base_propagation_delay * 10);
+
+ static const QuicByteCount initial_burst = 1000 * 10;
+ static const QuicByteCount max_bucket_size = 1000 * 100;
+ static const QuicBandwidth target_bandwidth = bandwidth * 0.25;
+ TrafficPolicer policer(&simulator, "Policer", initial_burst, max_bucket_size,
+ target_bandwidth, network_switch.port(2));
+
+ SymmetricLink link1(&saturator1, network_switch.port(1), bandwidth,
+ base_propagation_delay);
+ SymmetricLink link2(&saturator2, &policer, bandwidth, base_propagation_delay);
+
+ // Ensure the initial burst passes without being dropped at all.
+ bool simulator_result = simulator.RunUntilOrTimeout(
+ [&saturator1]() {
+ return saturator1.bytes_transmitted() == initial_burst;
+ },
+ timeout);
+ ASSERT_TRUE(simulator_result);
+ saturator1.Pause();
+ simulator_result = simulator.RunUntilOrTimeout(
+ [&saturator2]() {
+ return saturator2.counter()->bytes() == initial_burst;
+ },
+ timeout);
+ ASSERT_TRUE(simulator_result);
+ saturator1.Resume();
+
+ // Run for some time so that the initial burst is not visible.
+ const QuicTime::Delta simulation_time = QuicTime::Delta::FromSeconds(10);
+ simulator.RunFor(simulation_time);
+
+ // Ensure we've transmitted the amount of data we expected.
+ for (auto* saturator : {&saturator1, &saturator2}) {
+ test::ExpectApproxEq(bandwidth * simulation_time,
+ saturator->bytes_transmitted(), 0.01f);
+ }
+
+ // Check that only one direction is throttled.
+ test::ExpectApproxEq(saturator1.bytes_transmitted() / 4,
+ saturator2.counter()->bytes(), 0.1f);
+ test::ExpectApproxEq(saturator2.bytes_transmitted(),
+ saturator1.counter()->bytes(), 0.1f);
+}
+
+// Ensure that a larger burst is allowed when the policed saturator exits
+// quiescence.
+TEST_F(SimulatorTest, TrafficPolicerBurst) {
+ const QuicBandwidth bandwidth =
+ QuicBandwidth::FromBytesPerSecond(1024 * 1024);
+ const QuicTime::Delta base_propagation_delay =
+ QuicTime::Delta::FromMilliseconds(5);
+ const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(10);
+
+ Simulator simulator;
+ LinkSaturator saturator1(&simulator, "Saturator 1", 1000, "Saturator 2");
+ LinkSaturator saturator2(&simulator, "Saturator 2", 1000, "Saturator 1");
+ Switch network_switch(&simulator, "Switch", 8,
+ bandwidth * base_propagation_delay * 10);
+
+ const QuicByteCount initial_burst = 1000 * 10;
+ const QuicByteCount max_bucket_size = 1000 * 100;
+ const QuicBandwidth target_bandwidth = bandwidth * 0.25;
+ TrafficPolicer policer(&simulator, "Policer", initial_burst, max_bucket_size,
+ target_bandwidth, network_switch.port(2));
+
+ SymmetricLink link1(&saturator1, network_switch.port(1), bandwidth,
+ base_propagation_delay);
+ SymmetricLink link2(&saturator2, &policer, bandwidth, base_propagation_delay);
+
+ // Ensure at least one packet is sent on each side.
+ bool simulator_result = simulator.RunUntilOrTimeout(
+ [&saturator1, &saturator2]() {
+ return saturator1.packets_transmitted() > 0 &&
+ saturator2.packets_transmitted() > 0;
+ },
+ timeout);
+ ASSERT_TRUE(simulator_result);
+
+ // Wait until the bucket fills up.
+ saturator1.Pause();
+ saturator2.Pause();
+ simulator.RunFor(1.5f * target_bandwidth.TransferTime(max_bucket_size));
+
+ // Send a burst.
+ saturator1.Resume();
+ simulator.RunFor(bandwidth.TransferTime(max_bucket_size));
+ saturator1.Pause();
+ simulator.RunFor(2 * base_propagation_delay);
+
+ // Expect the burst to pass without losses.
+ test::ExpectApproxEq(saturator1.bytes_transmitted(),
+ saturator2.counter()->bytes(), 0.1f);
+
+ // Expect subsequent traffic to be policed.
+ saturator1.Resume();
+ simulator.RunFor(QuicTime::Delta::FromSeconds(10));
+ test::ExpectApproxEq(saturator1.bytes_transmitted() / 4,
+ saturator2.counter()->bytes(), 0.1f);
+}
+
+// Test that the packet aggregation support in queues work.
+TEST_F(SimulatorTest, PacketAggregation) {
+ // Model network where the delays are dominated by transfer delay.
+ const QuicBandwidth bandwidth = QuicBandwidth::FromBytesPerSecond(1000);
+ const QuicTime::Delta base_propagation_delay =
+ QuicTime::Delta::FromMicroseconds(1);
+ const QuicByteCount aggregation_threshold = 1000;
+ const QuicTime::Delta aggregation_timeout = QuicTime::Delta::FromSeconds(30);
+
+ Simulator simulator;
+ LinkSaturator saturator1(&simulator, "Saturator 1", 10, "Saturator 2");
+ LinkSaturator saturator2(&simulator, "Saturator 2", 10, "Saturator 1");
+ Switch network_switch(&simulator, "Switch", 8, 10 * aggregation_threshold);
+
+ // Make links with asymmetric propagation delay so that Saturator 2 only
+ // receives packets addressed to it.
+ SymmetricLink link1(&saturator1, network_switch.port(1), bandwidth,
+ base_propagation_delay);
+ SymmetricLink link2(&saturator2, network_switch.port(2), bandwidth,
+ 2 * base_propagation_delay);
+
+ // Enable aggregation in 1 -> 2 direction.
+ Queue* queue = network_switch.port_queue(2);
+ queue->EnableAggregation(aggregation_threshold, aggregation_timeout);
+
+ // Enable aggregation in 2 -> 1 direction in a way that all packets are larger
+ // than the threshold, so that aggregation is effectively a no-op.
+ network_switch.port_queue(1)->EnableAggregation(5, aggregation_timeout);
+
+ // Fill up the aggregation buffer up to 90% (900 bytes).
+ simulator.RunFor(0.9 * bandwidth.TransferTime(aggregation_threshold));
+ EXPECT_EQ(0u, saturator2.counter()->bytes());
+
+ // Stop sending, ensure that given a timespan much shorter than timeout, the
+ // packets remain in the queue.
+ saturator1.Pause();
+ saturator2.Pause();
+ simulator.RunFor(QuicTime::Delta::FromSeconds(10));
+ EXPECT_EQ(0u, saturator2.counter()->bytes());
+ EXPECT_EQ(900u, queue->bytes_queued());
+
+ // Ensure that all packets have reached the saturator not affected by
+ // aggregation. Here, 10 extra bytes account for a misrouted packet in the
+ // beginning.
+ EXPECT_EQ(910u, saturator1.counter()->bytes());
+
+ // Send 500 more bytes. Since the aggregation threshold is 1000 bytes, and
+ // queue already has 900 bytes, 1000 bytes will be send and 400 will be in the
+ // queue.
+ saturator1.Resume();
+ simulator.RunFor(0.5 * bandwidth.TransferTime(aggregation_threshold));
+ saturator1.Pause();
+ simulator.RunFor(QuicTime::Delta::FromSeconds(10));
+ EXPECT_EQ(1000u, saturator2.counter()->bytes());
+ EXPECT_EQ(400u, queue->bytes_queued());
+
+ // Actually time out, and cause all of the data to be received.
+ simulator.RunFor(aggregation_timeout);
+ EXPECT_EQ(1400u, saturator2.counter()->bytes());
+ EXPECT_EQ(0u, queue->bytes_queued());
+
+ // Run saturator for a longer time, to ensure that the logic to cancel and
+ // reset alarms works correctly.
+ saturator1.Resume();
+ simulator.RunFor(5.5 * bandwidth.TransferTime(aggregation_threshold));
+ saturator1.Pause();
+ simulator.RunFor(QuicTime::Delta::FromSeconds(10));
+ EXPECT_EQ(6400u, saturator2.counter()->bytes());
+ EXPECT_EQ(500u, queue->bytes_queued());
+
+ // Time out again.
+ simulator.RunFor(aggregation_timeout);
+ EXPECT_EQ(6900u, saturator2.counter()->bytes());
+ EXPECT_EQ(0u, queue->bytes_queued());
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/quic/test_tools/simulator/switch.cc b/quic/test_tools/simulator/switch.cc
new file mode 100644
index 0000000..638fa20
--- /dev/null
+++ b/quic/test_tools/simulator/switch.cc
@@ -0,0 +1,85 @@
+// Copyright (c) 2012 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 <cinttypes>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h"
+
+namespace quic {
+namespace simulator {
+
+Switch::Switch(Simulator* simulator,
+ QuicString name,
+ SwitchPortNumber port_count,
+ QuicByteCount queue_capacity) {
+ for (size_t port_number = 1; port_number <= port_count; port_number++) {
+ ports_.emplace_back(simulator,
+ QuicStrCat(name, " (port ", port_number, ")"), this,
+ port_number, queue_capacity);
+ }
+}
+
+Switch::~Switch() {}
+
+Switch::Port::Port(Simulator* simulator,
+ QuicString name,
+ Switch* parent,
+ SwitchPortNumber port_number,
+ QuicByteCount queue_capacity)
+ : Endpoint(simulator, name),
+ parent_(parent),
+ port_number_(port_number),
+ connected_(false),
+ queue_(simulator,
+ QuicStringPrintf("%s (queue)", name.c_str()),
+ queue_capacity) {}
+
+void Switch::Port::AcceptPacket(std::unique_ptr<Packet> packet) {
+ parent_->DispatchPacket(port_number_, std::move(packet));
+}
+
+void Switch::Port::EnqueuePacket(std::unique_ptr<Packet> packet) {
+ queue_.AcceptPacket(std::move(packet));
+}
+
+UnconstrainedPortInterface* Switch::Port::GetRxPort() {
+ return this;
+}
+
+void Switch::Port::SetTxPort(ConstrainedPortInterface* port) {
+ queue_.set_tx_port(port);
+ connected_ = true;
+}
+
+void Switch::Port::Act() {}
+
+void Switch::DispatchPacket(SwitchPortNumber port_number,
+ std::unique_ptr<Packet> packet) {
+ Port* source_port = &ports_[port_number - 1];
+ const auto source_mapping_it = switching_table_.find(packet->source);
+ if (source_mapping_it == switching_table_.end()) {
+ switching_table_.insert(std::make_pair(packet->source, source_port));
+ }
+
+ const auto destination_mapping_it =
+ switching_table_.find(packet->destination);
+ if (destination_mapping_it != switching_table_.end()) {
+ destination_mapping_it->second->EnqueuePacket(std::move(packet));
+ return;
+ }
+
+ // If no mapping is available yet, broadcast the packet to all ports
+ // different from the source.
+ for (Port& egress_port : ports_) {
+ if (!egress_port.connected()) {
+ continue;
+ }
+ egress_port.EnqueuePacket(QuicMakeUnique<Packet>(*packet));
+ }
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/quic/test_tools/simulator/switch.h b/quic/test_tools/simulator/switch.h
new file mode 100644
index 0000000..4956c01
--- /dev/null
+++ b/quic/test_tools/simulator/switch.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2012 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMULATOR_SWITCH_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_SWITCH_H_
+
+#include <deque>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/queue.h"
+
+namespace quic {
+namespace simulator {
+
+typedef size_t SwitchPortNumber;
+
+// Simulates a network switch with simple persistent learning scheme and queues
+// on every output port.
+class Switch {
+ public:
+ Switch(Simulator* simulator,
+ QuicString name,
+ SwitchPortNumber port_count,
+ QuicByteCount queue_capacity);
+ Switch(const Switch&) = delete;
+ Switch& operator=(const Switch&) = delete;
+ ~Switch();
+
+ // Returns Endpoint associated with the port under number |port_number|. Just
+ // like on most real switches, port numbering starts with 1.
+ inline Endpoint* port(SwitchPortNumber port_number) {
+ DCHECK_NE(port_number, 0u);
+ return &ports_[port_number - 1];
+ }
+
+ inline Queue* port_queue(SwitchPortNumber port_number) {
+ return ports_[port_number - 1].queue();
+ }
+
+ private:
+ class Port : public Endpoint, public UnconstrainedPortInterface {
+ public:
+ Port(Simulator* simulator,
+ QuicString name,
+ Switch* parent,
+ SwitchPortNumber port_number,
+ QuicByteCount queue_capacity);
+ Port(Port&&) = delete;
+ Port(const Port&) = delete;
+ Port& operator=(const Port&) = delete;
+ ~Port() override {}
+
+ // Accepts packet to be routed into the switch.
+ void AcceptPacket(std::unique_ptr<Packet> packet) override;
+ // Enqueue packet to be routed out of the switch.
+ void EnqueuePacket(std::unique_ptr<Packet> packet);
+
+ UnconstrainedPortInterface* GetRxPort() override;
+ void SetTxPort(ConstrainedPortInterface* port) override;
+
+ void Act() override;
+
+ inline bool connected() const { return connected_; }
+ inline Queue* queue() { return &queue_; }
+
+ private:
+ Switch* parent_;
+ SwitchPortNumber port_number_;
+ bool connected_;
+
+ Queue queue_;
+ };
+
+ // Sends the packet to the appropriate port, or to all ports if the
+ // appropriate port is not known.
+ void DispatchPacket(SwitchPortNumber port_number,
+ std::unique_ptr<Packet> packet);
+
+ // This can not be a QuicDeque since pointers into this are
+ // assumed to be stable.
+ std::deque<Port> ports_;
+ QuicUnorderedMap<QuicString, Port*> switching_table_;
+};
+
+} // namespace simulator
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMULATOR_SWITCH_H_
diff --git a/quic/test_tools/simulator/traffic_policer.cc b/quic/test_tools/simulator/traffic_policer.cc
new file mode 100644
index 0000000..e416a7d
--- /dev/null
+++ b/quic/test_tools/simulator/traffic_policer.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2016 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 "net/third_party/quiche/src/quic/test_tools/simulator/traffic_policer.h"
+
+#include <algorithm>
+
+namespace quic {
+namespace simulator {
+
+TrafficPolicer::TrafficPolicer(Simulator* simulator,
+ QuicString name,
+ QuicByteCount initial_bucket_size,
+ QuicByteCount max_bucket_size,
+ QuicBandwidth target_bandwidth,
+ Endpoint* input)
+ : PacketFilter(simulator, name, input),
+ initial_bucket_size_(initial_bucket_size),
+ max_bucket_size_(max_bucket_size),
+ target_bandwidth_(target_bandwidth),
+ last_refill_time_(clock_->Now()) {}
+
+TrafficPolicer::~TrafficPolicer() {}
+
+void TrafficPolicer::Refill() {
+ QuicTime::Delta time_passed = clock_->Now() - last_refill_time_;
+ QuicByteCount refill_size = time_passed * target_bandwidth_;
+
+ for (auto& bucket : token_buckets_) {
+ bucket.second = std::min(bucket.second + refill_size, max_bucket_size_);
+ }
+
+ last_refill_time_ = clock_->Now();
+}
+
+bool TrafficPolicer::FilterPacket(const Packet& packet) {
+ // Refill existing buckets.
+ Refill();
+
+ // Create a new bucket if one does not exist.
+ if (token_buckets_.count(packet.destination) == 0) {
+ token_buckets_.insert(
+ std::make_pair(packet.destination, initial_bucket_size_));
+ }
+
+ auto bucket = token_buckets_.find(packet.destination);
+ DCHECK(bucket != token_buckets_.end());
+
+ // Silently drop the packet on the floor if out of tokens
+ if (bucket->second < packet.size) {
+ return false;
+ }
+
+ bucket->second -= packet.size;
+ return true;
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/quic/test_tools/simulator/traffic_policer.h b/quic/test_tools/simulator/traffic_policer.h
new file mode 100644
index 0000000..ee77576
--- /dev/null
+++ b/quic/test_tools/simulator/traffic_policer.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2016 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMULATOR_TRAFFIC_POLICER_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_TRAFFIC_POLICER_H_
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/packet_filter.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/port.h"
+
+namespace quic {
+namespace simulator {
+
+// Traffic policer uses a token bucket to limit the bandwidth of the traffic
+// passing through. It wraps around an input port and exposes an output port.
+// Only the traffic from input to the output is policed, so in case when
+// bidirectional policing is desired, two policers have to be used. The flows
+// are hashed by the destination only.
+class TrafficPolicer : public PacketFilter {
+ public:
+ TrafficPolicer(Simulator* simulator,
+ QuicString name,
+ QuicByteCount initial_bucket_size,
+ QuicByteCount max_bucket_size,
+ QuicBandwidth target_bandwidth,
+ Endpoint* input);
+ TrafficPolicer(const TrafficPolicer&) = delete;
+ TrafficPolicer& operator=(const TrafficPolicer&) = delete;
+ ~TrafficPolicer() override;
+
+ protected:
+ bool FilterPacket(const Packet& packet) override;
+
+ private:
+ // Refill the token buckets with all the tokens that have been granted since
+ // |last_refill_time_|.
+ void Refill();
+
+ QuicByteCount initial_bucket_size_;
+ QuicByteCount max_bucket_size_;
+ QuicBandwidth target_bandwidth_;
+
+ // The time at which the token buckets were last refilled.
+ QuicTime last_refill_time_;
+
+ // Maps each destination to the number of tokens it has left.
+ QuicUnorderedMap<QuicString, QuicByteCount> token_buckets_;
+};
+
+} // namespace simulator
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMULATOR_TRAFFIC_POLICER_H_