summaryrefslogtreecommitdiff
path: root/chromium/net/third_party/quiche/src/quic/quartc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/third_party/quiche/src/quic/quartc')
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/counting_packet_filter.h43
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_connection_helper.cc24
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h33
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.cc162
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h122
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.cc89
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h73
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint.cc117
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint.h181
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint_test.cc214
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.cc242
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.h67
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_fakes.h139
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_interval_counter.h122
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_interval_counter_test.cc110
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_packet_writer.cc76
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h113
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_session.cc397
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_session.h284
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_session_test.cc559
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.cc171
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.h142
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_stream_test.cc640
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport.cc95
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h84
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport_test.cc165
26 files changed, 4464 insertions, 0 deletions
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/counting_packet_filter.h b/chromium/net/third_party/quiche/src/quic/quartc/counting_packet_filter.h
new file mode 100644
index 00000000000..32feffc7497
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/counting_packet_filter.h
@@ -0,0 +1,43 @@
+// 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_QUARTC_COUNTING_PACKET_FILTER_H_
+#define QUICHE_QUIC_QUARTC_COUNTING_PACKET_FILTER_H_
+
+#include <string>
+
+#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"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+
+namespace quic {
+namespace simulator {
+
+// Simple packet filter which drops the first N packets it observes.
+class CountingPacketFilter : public simulator::PacketFilter {
+ public:
+ CountingPacketFilter(simulator::Simulator* simulator,
+ const std::string& name,
+ simulator::Endpoint* endpoint)
+ : PacketFilter(simulator, name, endpoint) {}
+
+ void set_packets_to_drop(int count) { packets_to_drop_ = count; }
+
+ protected:
+ bool FilterPacket(const simulator::Packet& packet) override {
+ if (packets_to_drop_ > 0) {
+ --packets_to_drop_;
+ return false;
+ }
+ return true;
+ }
+
+ private:
+ int packets_to_drop_ = 0;
+};
+
+} // namespace simulator
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_COUNTING_PACKET_FILTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_connection_helper.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_connection_helper.cc
new file mode 100644
index 00000000000..6c6ee97c29f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_connection_helper.cc
@@ -0,0 +1,24 @@
+// 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/quartc/quartc_connection_helper.h"
+
+namespace quic {
+
+QuartcConnectionHelper::QuartcConnectionHelper(const QuicClock* clock)
+ : clock_(clock) {}
+
+const QuicClock* QuartcConnectionHelper::GetClock() const {
+ return clock_;
+}
+
+QuicRandom* QuartcConnectionHelper::GetRandomGenerator() {
+ return QuicRandom::GetInstance();
+}
+
+QuicBufferAllocator* QuartcConnectionHelper::GetStreamSendBufferAllocator() {
+ return &buffer_allocator_;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h
new file mode 100644
index 00000000000..2b1d62f9a53
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h
@@ -0,0 +1,33 @@
+// 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_QUARTC_QUARTC_CONNECTION_HELPER_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_CONNECTION_HELPER_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h"
+#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_clock.h"
+
+namespace quic {
+
+// Simple implementation of QuicConnectionHelperInterface for Quartc.
+class QuartcConnectionHelper : public QuicConnectionHelperInterface {
+ public:
+ explicit QuartcConnectionHelper(const QuicClock* clock);
+
+ // QuicConnectionHelperInterface overrides.
+ const QuicClock* GetClock() const override;
+ QuicRandom* GetRandomGenerator() override;
+ QuicBufferAllocator* GetStreamSendBufferAllocator() override;
+
+ private:
+ const QuicClock* clock_;
+ SimpleBufferAllocator buffer_allocator_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_QUARTC_CONNECTION_HELPER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.cc
new file mode 100644
index 00000000000..93023e72d3f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.cc
@@ -0,0 +1,162 @@
+// 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/quartc/quartc_crypto_helpers.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"
+
+namespace quic {
+
+void DummyProofSource::GetProof(const QuicSocketAddress& server_address,
+ const std::string& hostname,
+ const std::string& server_config,
+ QuicTransportVersion transport_version,
+ QuicStringPiece chlo_hash,
+ std::unique_ptr<Callback> callback) {
+ QuicReferenceCountedPointer<ProofSource::Chain> chain =
+ GetCertChain(server_address, hostname);
+ QuicCryptoProof proof;
+ proof.signature = "Dummy signature";
+ proof.leaf_cert_scts = "Dummy timestamp";
+ callback->Run(true, chain, proof, nullptr /* details */);
+}
+
+QuicReferenceCountedPointer<DummyProofSource::Chain>
+DummyProofSource::GetCertChain(const QuicSocketAddress& server_address,
+ const std::string& hostname) {
+ std::vector<std::string> certs;
+ certs.push_back(kDummyCertName);
+ return QuicReferenceCountedPointer<ProofSource::Chain>(
+ new ProofSource::Chain(certs));
+}
+
+void DummyProofSource::ComputeTlsSignature(
+ const QuicSocketAddress& server_address,
+ const std::string& hostname,
+ uint16_t signature_algorithm,
+ QuicStringPiece in,
+ std::unique_ptr<SignatureCallback> callback) {
+ callback->Run(true, "Dummy signature");
+}
+
+QuicAsyncStatus InsecureProofVerifier::VerifyProof(
+ const std::string& hostname,
+ const uint16_t port,
+ const std::string& server_config,
+ QuicTransportVersion transport_version,
+ QuicStringPiece chlo_hash,
+ const std::vector<std::string>& certs,
+ const std::string& cert_sct,
+ const std::string& signature,
+ const ProofVerifyContext* context,
+ std::string* error_details,
+ std::unique_ptr<ProofVerifyDetails>* verify_details,
+ std::unique_ptr<ProofVerifierCallback> callback) {
+ return QUIC_SUCCESS;
+}
+
+QuicAsyncStatus InsecureProofVerifier::VerifyCertChain(
+ const std::string& hostname,
+ const std::vector<std::string>& certs,
+ const ProofVerifyContext* context,
+ std::string* error_details,
+ std::unique_ptr<ProofVerifyDetails>* details,
+ std::unique_ptr<ProofVerifierCallback> callback) {
+ return QUIC_SUCCESS;
+}
+
+std::unique_ptr<ProofVerifyContext>
+InsecureProofVerifier::CreateDefaultContext() {
+ return nullptr;
+}
+
+QuicConnectionId QuartcCryptoServerStreamHelper::GenerateConnectionIdForReject(
+ QuicTransportVersion version,
+ QuicConnectionId connection_id) const {
+ // TODO(b/124399417): Request a zero-length connection id here when the QUIC
+ // server perspective supports it. Right now, the stateless rejector requires
+ // a connection id that is not the same as the client-chosen connection id.
+ return QuicUtils::CreateRandomConnectionId();
+}
+
+bool QuartcCryptoServerStreamHelper::CanAcceptClientHello(
+ const CryptoHandshakeMessage& message,
+ const QuicSocketAddress& client_address,
+ const QuicSocketAddress& peer_address,
+ const QuicSocketAddress& self_address,
+ std::string* error_details) const {
+ return true;
+}
+
+std::unique_ptr<QuicCryptoClientConfig> CreateCryptoClientConfig(
+ QuicStringPiece pre_shared_key) {
+ auto config = QuicMakeUnique<QuicCryptoClientConfig>(
+ QuicMakeUnique<InsecureProofVerifier>(),
+ TlsClientHandshaker::CreateSslCtx());
+ config->set_pad_inchoate_hello(false);
+ config->set_pad_full_hello(false);
+ if (!pre_shared_key.empty()) {
+ config->set_pre_shared_key(pre_shared_key);
+ }
+ return config;
+}
+
+CryptoServerConfig CreateCryptoServerConfig(QuicRandom* random,
+ const QuicClock* clock,
+ QuicStringPiece pre_shared_key) {
+ CryptoServerConfig crypto_server_config;
+
+ // Generate a random source address token secret. For long-running servers
+ // it's better to not regenerate it for each connection to enable zero-RTT
+ // handshakes, but for transient clients it does not matter.
+ char source_address_token_secret[kInputKeyingMaterialLength];
+ random->RandBytes(source_address_token_secret, kInputKeyingMaterialLength);
+ auto config = QuicMakeUnique<QuicCryptoServerConfig>(
+ std::string(source_address_token_secret, kInputKeyingMaterialLength),
+ random, QuicMakeUnique<DummyProofSource>(), KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx());
+
+ // We run QUIC over ICE, and ICE is verifying remote side with STUN pings.
+ // We disable source address token validation in order to allow for 0-rtt
+ // setup (plus source ip addresses are changing even during the connection
+ // when ICE is used).
+ config->set_validate_source_address_token(false);
+
+ // Effectively disables the anti-amplification measures (we don't need
+ // them because we use ICE, and we need to disable them because we disable
+ // padding of crypto packets).
+ // This multiplier must be large enough so that the crypto handshake packet
+ // (approx. 300 bytes) multiplied by this multiplier is larger than a fully
+ // sized packet (currently 1200 bytes).
+ // 1500 is a bit extreme: if you can imagine sending a 1 byte packet, and
+ // your largest MTU would be below 1500 bytes, 1500*1 >=
+ // any_packet_that_you_can_imagine_sending.
+ // (again, we hardcode packet size to 1200, so we are not dealing with jumbo
+ // frames).
+ config->set_chlo_multiplier(1500);
+
+ // We are sending small client hello, we must not validate its size.
+ config->set_validate_chlo_size(false);
+
+ // Provide server with serialized config string to prove ownership.
+ QuicCryptoServerConfig::ConfigOptions options;
+ // The |message| is used to handle the return value of AddDefaultConfig
+ // which is raw pointer of the CryptoHandshakeMessage.
+ std::unique_ptr<CryptoHandshakeMessage> message(
+ config->AddDefaultConfig(random, clock, options));
+ config->set_pad_rej(false);
+ config->set_pad_shlo(false);
+ if (!pre_shared_key.empty()) {
+ config->set_pre_shared_key(pre_shared_key);
+ }
+ crypto_server_config.config = std::move(config);
+ const QuicData& data = message->GetSerialized();
+
+ crypto_server_config.serialized_crypto_config =
+ std::string(data.data(), data.length());
+ return crypto_server_config;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h
new file mode 100644
index 00000000000..2dba7ac7d06
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h
@@ -0,0 +1,122 @@
+// 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_QUARTC_QUARTC_CRYPTO_HELPERS_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_CRYPTO_HELPERS_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
+#include "net/third_party/quiche/src/quic/core/crypto/proof_source.h"
+#include "net/third_party/quiche/src/quic/core/crypto/proof_verifier.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.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 {
+
+// Never, ever, change this certificate name. You will break 0-rtt handshake if
+// you do.
+static constexpr char kDummyCertName[] = "Dummy cert";
+
+struct CryptoServerConfig {
+ std::unique_ptr<QuicCryptoServerConfig> config;
+ std::string serialized_crypto_config;
+};
+
+// Length of HKDF input keying material, equal to its number of bytes.
+// https://tools.ietf.org/html/rfc5869#section-2.2.
+// TODO(zhihuang): Verify that input keying material length is correct.
+constexpr size_t kInputKeyingMaterialLength = 32;
+
+// Used by QuicCryptoServerConfig to provide dummy proof credentials.
+// TODO(zhihuang): Remove when secure P2P QUIC handshake is possible.
+class DummyProofSource : public ProofSource {
+ public:
+ DummyProofSource() {}
+ ~DummyProofSource() override {}
+
+ // ProofSource overrides.
+ void GetProof(const QuicSocketAddress& server_address,
+ const std::string& hostname,
+ const std::string& server_config,
+ QuicTransportVersion transport_version,
+ QuicStringPiece chlo_hash,
+ std::unique_ptr<Callback> callback) override;
+
+ QuicReferenceCountedPointer<Chain> GetCertChain(
+ const QuicSocketAddress& server_address,
+ const std::string& hostname) override;
+
+ void ComputeTlsSignature(
+ const QuicSocketAddress& server_address,
+ const std::string& hostname,
+ uint16_t signature_algorithm,
+ QuicStringPiece in,
+ std::unique_ptr<SignatureCallback> callback) override;
+};
+
+// Used by QuicCryptoClientConfig to ignore the peer's credentials
+// and establish an insecure QUIC connection.
+// TODO(zhihuang): Remove when secure P2P QUIC handshake is possible.
+class InsecureProofVerifier : public ProofVerifier {
+ public:
+ InsecureProofVerifier() {}
+ ~InsecureProofVerifier() override {}
+
+ // ProofVerifier overrides.
+ QuicAsyncStatus VerifyProof(
+ const std::string& hostname,
+ const uint16_t port,
+ const std::string& server_config,
+ QuicTransportVersion transport_version,
+ QuicStringPiece chlo_hash,
+ const std::vector<std::string>& certs,
+ const std::string& cert_sct,
+ const std::string& signature,
+ const ProofVerifyContext* context,
+ std::string* error_details,
+ std::unique_ptr<ProofVerifyDetails>* verify_details,
+ std::unique_ptr<ProofVerifierCallback> callback) override;
+
+ QuicAsyncStatus VerifyCertChain(
+ const std::string& hostname,
+ const std::vector<std::string>& certs,
+ const ProofVerifyContext* context,
+ std::string* error_details,
+ std::unique_ptr<ProofVerifyDetails>* details,
+ std::unique_ptr<ProofVerifierCallback> callback) override;
+
+ std::unique_ptr<ProofVerifyContext> CreateDefaultContext() override;
+};
+
+// Implementation of the server-side crypto stream helper.
+class QuartcCryptoServerStreamHelper : public QuicCryptoServerStream::Helper {
+ public:
+ QuicConnectionId GenerateConnectionIdForReject(
+ QuicTransportVersion version,
+ QuicConnectionId connection_id) const override;
+
+ bool CanAcceptClientHello(const CryptoHandshakeMessage& message,
+ const QuicSocketAddress& client_address,
+ const QuicSocketAddress& peer_address,
+ const QuicSocketAddress& self_address,
+ std::string* error_details) const override;
+};
+
+std::unique_ptr<QuicCryptoClientConfig> CreateCryptoClientConfig(
+ QuicStringPiece pre_shared_key);
+
+CryptoServerConfig CreateCryptoServerConfig(QuicRandom* random,
+ const QuicClock* clock,
+ QuicStringPiece pre_shared_key);
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_QUARTC_CRYPTO_HELPERS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.cc
new file mode 100644
index 00000000000..fd4f289f3a6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.cc
@@ -0,0 +1,89 @@
+// 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/quartc/quartc_dispatcher.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_factory.h"
+
+namespace quic {
+
+QuartcDispatcher::QuartcDispatcher(
+ std::unique_ptr<QuicConfig> config,
+ std::unique_ptr<QuicCryptoServerConfig> crypto_config,
+ QuicStringPiece crypto_config_serialized,
+ QuicVersionManager* version_manager,
+ std::unique_ptr<QuicConnectionHelperInterface> helper,
+ std::unique_ptr<QuicCryptoServerStream::Helper> session_helper,
+ std::unique_ptr<QuicAlarmFactory> alarm_factory,
+ std::unique_ptr<QuartcPacketWriter> packet_writer,
+ Delegate* delegate)
+ : QuicDispatcher(
+ config.get(),
+ crypto_config.get(),
+ version_manager,
+ std::move(helper),
+ std::move(session_helper),
+ std::move(alarm_factory),
+ QuicUtils::CreateZeroConnectionId(
+ version_manager->GetSupportedVersions()[0].transport_version)
+ .length()),
+ owned_quic_config_(std::move(config)),
+ owned_crypto_config_(std::move(crypto_config)),
+ crypto_config_(crypto_config_serialized),
+ delegate_(delegate),
+ packet_writer_(packet_writer.get()) {
+ // Allow incoming packets to set our expected connection ID length.
+ SetShouldUpdateExpectedConnectionIdLength(true);
+ // Allow incoming packets with connection ID lengths shorter than allowed.
+ SetAllowShortInitialConnectionIds(true);
+ // QuicDispatcher takes ownership of the writer.
+ QuicDispatcher::InitializeWithWriter(packet_writer.release());
+ // NB: This must happen *after* InitializeWithWriter. It can call us back
+ // with OnTransportCanWrite() immediately, and the dispatcher needs to be
+ // fully initialized to handle that.
+ packet_writer_->SetPacketTransportDelegate(this);
+}
+
+QuartcDispatcher::~QuartcDispatcher() {
+ packet_writer_->SetPacketTransportDelegate(nullptr);
+}
+
+QuartcSession* QuartcDispatcher::CreateQuicSession(
+ QuicConnectionId connection_id,
+ const QuicSocketAddress& client_address,
+ QuicStringPiece alpn,
+ const ParsedQuicVersion& version) {
+ // Make our expected connection ID non-mutable since we have a connection.
+ SetShouldUpdateExpectedConnectionIdLength(false);
+ std::unique_ptr<QuicConnection> connection = CreateQuicConnection(
+ connection_id, client_address, helper(), alarm_factory(), writer(),
+ Perspective::IS_SERVER, ParsedQuicVersionVector{version});
+ QuartcSession* session = new QuartcServerSession(
+ std::move(connection), /*visitor=*/this, config(), GetSupportedVersions(),
+ helper()->GetClock(), crypto_config(), compressed_certs_cache(),
+ session_helper());
+ delegate_->OnSessionCreated(session);
+ return session;
+}
+
+void QuartcDispatcher::OnTransportCanWrite() {
+ OnCanWrite();
+}
+
+void QuartcDispatcher::OnTransportReceived(const char* data, size_t data_len) {
+ // QuartcPacketTransport does not surface real peer addresses, so the
+ // dispatcher uses a dummy address when processing incoming packets. Note that
+ // the dispatcher refuses to process anything with port 0.
+ static const QuicSocketAddress* dummy_address =
+ new QuicSocketAddress(QuicIpAddress::Any4(), /*port=*/1);
+
+ QuicReceivedPacket packet(data, data_len, helper()->GetClock()->Now());
+ ProcessPacket(/*self_address=*/*dummy_address,
+ /*peer_address=*/*dummy_address, packet);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h
new file mode 100644
index 00000000000..26b52ac4f0c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h
@@ -0,0 +1,73 @@
+// 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_QUARTC_QUARTC_DISPATCHER_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_DISPATCHER_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_dispatcher.h"
+#include "net/third_party/quiche/src/quic/core/quic_version_manager.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.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"
+#include "net/third_party/quiche/src/quic/quartc/quartc_session.h"
+
+namespace quic {
+
+class QuartcDispatcher : public QuicDispatcher,
+ QuartcPacketTransport::Delegate {
+ public:
+ class Delegate {
+ public:
+ virtual ~Delegate() = default;
+ virtual void OnSessionCreated(QuartcSession* session) = 0;
+ };
+
+ QuartcDispatcher(
+ std::unique_ptr<QuicConfig> config,
+ std::unique_ptr<QuicCryptoServerConfig> crypto_config,
+ QuicStringPiece crypto_config_serialized,
+ QuicVersionManager* version_manager,
+ std::unique_ptr<QuicConnectionHelperInterface> helper,
+ std::unique_ptr<QuicCryptoServerStream::Helper> session_helper,
+ std::unique_ptr<QuicAlarmFactory> alarm_factory,
+ std::unique_ptr<QuartcPacketWriter> packet_writer,
+ Delegate* delegate);
+ ~QuartcDispatcher() override;
+
+ QuartcSession* CreateQuicSession(QuicConnectionId connection_id,
+ const QuicSocketAddress& client_address,
+ QuicStringPiece alpn,
+ const ParsedQuicVersion& version) override;
+
+ // QuartcPacketTransport::Delegate overrides.
+ void OnTransportCanWrite() override;
+ void OnTransportReceived(const char* data, size_t data_len) override;
+
+ // A serialized server config in quic wire format.
+ QuicStringPiece server_crypto_config() const { return crypto_config_; }
+
+ private:
+ // Members owned by QuartcDispatcher but not QuicDispatcher.
+ std::unique_ptr<QuicConfig> owned_quic_config_;
+ std::unique_ptr<QuicCryptoServerConfig> owned_crypto_config_;
+ std::string crypto_config_;
+
+ // Delegate invoked when the dispatcher creates a new session.
+ Delegate* delegate_;
+
+ // The packet writer used by this dispatcher. Owned by the base class, but
+ // the base class upcasts it to QuicPacketWriter (which prevents detaching the
+ // transport delegate without a downcast).
+ QuartcPacketWriter* packet_writer_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_QUARTC_DISPATCHER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint.cc
new file mode 100644
index 00000000000..459ceba0618
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint.cc
@@ -0,0 +1,117 @@
+// 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/quartc/quartc_endpoint.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_version_manager.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h"
+
+namespace quic {
+
+namespace {
+
+// Wrapper around a QuicAlarmFactory which delegates to the wrapped factory.
+// Usee to convert an unowned pointer into an owned pointer, so that the new
+// "owner" does not delete the underlying factory. Note that this is only valid
+// when the unowned pointer is already guaranteed to outlive the new "owner".
+class QuartcAlarmFactoryWrapper : public QuicAlarmFactory {
+ public:
+ explicit QuartcAlarmFactoryWrapper(QuicAlarmFactory* impl) : impl_(impl) {}
+
+ QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) override;
+ QuicArenaScopedPtr<QuicAlarm> CreateAlarm(
+ QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
+ QuicConnectionArena* arena) override;
+
+ private:
+ QuicAlarmFactory* impl_;
+};
+
+QuicAlarm* QuartcAlarmFactoryWrapper::CreateAlarm(
+ QuicAlarm::Delegate* delegate) {
+ return impl_->CreateAlarm(delegate);
+}
+
+QuicArenaScopedPtr<QuicAlarm> QuartcAlarmFactoryWrapper::CreateAlarm(
+ QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
+ QuicConnectionArena* arena) {
+ return impl_->CreateAlarm(std::move(delegate), arena);
+}
+
+} // namespace
+
+QuartcClientEndpoint::QuartcClientEndpoint(
+ QuicAlarmFactory* alarm_factory,
+ const QuicClock* clock,
+ QuartcEndpoint::Delegate* delegate,
+ const QuartcSessionConfig& config,
+ QuicStringPiece serialized_server_config,
+ std::unique_ptr<QuicVersionManager> version_manager)
+ : alarm_factory_(alarm_factory),
+ clock_(clock),
+ delegate_(delegate),
+ serialized_server_config_(serialized_server_config),
+ version_manager_(version_manager ? std::move(version_manager)
+ : QuicMakeUnique<QuicVersionManager>(
+ AllSupportedVersions())),
+ create_session_alarm_(QuicWrapUnique(
+ alarm_factory_->CreateAlarm(new CreateSessionDelegate(this)))),
+ connection_helper_(QuicMakeUnique<QuartcConnectionHelper>(clock)),
+ config_(config) {}
+
+void QuartcClientEndpoint::Connect(QuartcPacketTransport* packet_transport) {
+ packet_transport_ = packet_transport;
+ create_session_alarm_->Set(clock_->Now());
+}
+
+void QuartcClientEndpoint::OnCreateSessionAlarm() {
+ session_ = CreateQuartcClientSession(
+ config_, clock_, alarm_factory_, connection_helper_.get(),
+ version_manager_->GetSupportedVersions(), serialized_server_config_,
+ packet_transport_);
+ delegate_->OnSessionCreated(session_.get());
+}
+
+QuartcServerEndpoint::QuartcServerEndpoint(
+ QuicAlarmFactory* alarm_factory,
+ const QuicClock* clock,
+ QuartcEndpoint::Delegate* delegate,
+ const QuartcSessionConfig& config,
+ std::unique_ptr<QuicVersionManager> version_manager)
+ : alarm_factory_(alarm_factory),
+ delegate_(delegate),
+ config_(config),
+ version_manager_(version_manager ? std::move(version_manager)
+ : QuicMakeUnique<QuicVersionManager>(
+ AllSupportedVersions())),
+ pre_connection_helper_(QuicMakeUnique<QuartcConnectionHelper>(clock)),
+ crypto_config_(
+ CreateCryptoServerConfig(pre_connection_helper_->GetRandomGenerator(),
+ clock,
+ config.pre_shared_key)) {}
+
+void QuartcServerEndpoint::Connect(QuartcPacketTransport* packet_transport) {
+ DCHECK(pre_connection_helper_ != nullptr);
+ dispatcher_ = QuicMakeUnique<QuartcDispatcher>(
+ QuicMakeUnique<QuicConfig>(CreateQuicConfig(config_)),
+ std::move(crypto_config_.config), crypto_config_.serialized_crypto_config,
+ version_manager_.get(), std::move(pre_connection_helper_),
+ QuicMakeUnique<QuartcCryptoServerStreamHelper>(),
+ QuicMakeUnique<QuartcAlarmFactoryWrapper>(alarm_factory_),
+ QuicMakeUnique<QuartcPacketWriter>(packet_transport,
+ config_.max_packet_size),
+ this);
+ // The dispatcher requires at least one call to |ProcessBufferedChlos| to
+ // set the number of connections it is allowed to create.
+ dispatcher_->ProcessBufferedChlos(/*max_connections_to_create=*/1);
+}
+
+void QuartcServerEndpoint::OnSessionCreated(QuartcSession* session) {
+ delegate_->OnSessionCreated(session);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint.h
new file mode 100644
index 00000000000..97f30609ec0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint.h
@@ -0,0 +1,181 @@
+// 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_QUARTC_QUARTC_ENDPOINT_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_ENDPOINT_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_factory.h"
+
+namespace quic {
+
+// Private implementation of QuartcEndpoint. Enables different implementations
+// for client and server endpoints.
+class QuartcEndpointImpl {
+ public:
+ virtual ~QuartcEndpointImpl() = default;
+
+ virtual QuicStringPiece server_crypto_config() const = 0;
+};
+
+// Endpoint (client or server) in a peer-to-peer Quartc connection.
+class QuartcEndpoint {
+ public:
+ class Delegate {
+ public:
+ virtual ~Delegate() = default;
+
+ // Called when an endpoint creates a new session, before any packets are
+ // processed or sent. The callee should perform any additional
+ // configuration required, such as setting a session delegate, before
+ // returning. |session| is owned by the endpoint, but remains safe to use
+ // until another call to |OnSessionCreated| occurs, at which point previous
+ // session is destroyed.
+ virtual void OnSessionCreated(QuartcSession* session) = 0;
+
+ // Called if the endpoint fails to establish a session after a call to
+ // Connect. (The most likely cause is a network idle timeout.)
+ virtual void OnConnectError(QuicErrorCode error,
+ const std::string& error_details) = 0;
+ };
+
+ virtual ~QuartcEndpoint() = default;
+
+ // Connects the endpoint using the given session config. After |Connect| is
+ // called, the endpoint will asynchronously create a session, then call
+ // |Delegate::OnSessionCreated|.
+ virtual void Connect(QuartcPacketTransport* packet_transport) = 0;
+};
+
+// Implementation of QuartcEndpoint which immediately (but asynchronously)
+// creates a session by scheduling a QuicAlarm. Only suitable for use with the
+// client perspective.
+class QuartcClientEndpoint : public QuartcEndpoint {
+ public:
+ // |alarm_factory|, |clock|, and |delegate| are owned by the caller and must
+ // outlive the endpoint.
+ QuartcClientEndpoint(
+ QuicAlarmFactory* alarm_factory,
+ const QuicClock* clock,
+ Delegate* delegate,
+ const QuartcSessionConfig& config,
+ QuicStringPiece serialized_server_config,
+ std::unique_ptr<QuicVersionManager> version_manager = nullptr);
+
+ void Connect(QuartcPacketTransport* packet_transport) override;
+
+ private:
+ friend class CreateSessionDelegate;
+ class CreateSessionDelegate : public QuicAlarm::Delegate {
+ public:
+ CreateSessionDelegate(QuartcClientEndpoint* endpoint)
+ : endpoint_(endpoint) {}
+
+ void OnAlarm() override { endpoint_->OnCreateSessionAlarm(); }
+
+ private:
+ QuartcClientEndpoint* endpoint_;
+ };
+
+ // Callback which occurs when |create_session_alarm_| fires.
+ void OnCreateSessionAlarm();
+
+ // Implementation of QuicAlarmFactory used by this endpoint. Unowned.
+ QuicAlarmFactory* alarm_factory_;
+
+ // Implementation of QuicClock used by this endpoint. Unowned.
+ const QuicClock* clock_;
+
+ // Delegate which receives callbacks for newly created sessions.
+ QuartcEndpoint::Delegate* delegate_;
+
+ // Server config. If valid, used to perform a 0-RTT connection.
+ const std::string serialized_server_config_;
+
+ // Version manager. May be injected to control version negotiation in tests.
+ std::unique_ptr<QuicVersionManager> version_manager_;
+
+ // Alarm for creating sessions asynchronously. The alarm is set when
+ // Connect() is called. When it fires, the endpoint creates a session and
+ // calls the delegate.
+ std::unique_ptr<QuicAlarm> create_session_alarm_;
+
+ // Helper used by QuicConnection.
+ std::unique_ptr<QuicConnectionHelperInterface> connection_helper_;
+
+ // Config to be used for new sessions.
+ QuartcSessionConfig config_;
+
+ // The currently-active session. Nullptr until |Connect| and
+ // |Delegate::OnSessionCreated| are called.
+ std::unique_ptr<QuartcSession> session_;
+
+ QuartcPacketTransport* packet_transport_;
+};
+
+// Implementation of QuartcEndpoint which uses a QuartcDispatcher to listen for
+// an incoming CHLO and create a session when one arrives. Only suitable for
+// use with the server perspective.
+class QuartcServerEndpoint : public QuartcEndpoint,
+ public QuartcDispatcher::Delegate {
+ public:
+ QuartcServerEndpoint(
+ QuicAlarmFactory* alarm_factory,
+ const QuicClock* clock,
+ QuartcEndpoint::Delegate* delegate,
+ const QuartcSessionConfig& config,
+ std::unique_ptr<QuicVersionManager> version_manager = nullptr);
+
+ // Implements QuartcEndpoint.
+ void Connect(QuartcPacketTransport* packet_transport) override;
+
+ // Implements QuartcDispatcher::Delegate.
+ void OnSessionCreated(QuartcSession* session) override;
+
+ // Accessor to retrieve the server crypto config. May only be called after
+ // Connect().
+ QuicStringPiece server_crypto_config() const {
+ return crypto_config_.serialized_crypto_config;
+ }
+
+ const std::vector<ParsedQuicVersion> GetSupportedQuicVersions() const {
+ return version_manager_->GetSupportedVersions();
+ }
+
+ private:
+ // Implementation of QuicAlarmFactory used by this endpoint. Unowned.
+ QuicAlarmFactory* alarm_factory_;
+
+ // Delegate which receives callbacks for newly created sessions.
+ QuartcEndpoint::Delegate* delegate_;
+
+ // Config to be used for new sessions.
+ QuartcSessionConfig config_;
+
+ // Version manager. May be injected to control version negotiation in tests.
+ std::unique_ptr<QuicVersionManager> version_manager_;
+
+ // QuartcDispatcher waits for an incoming CHLO, then either rejects it or
+ // creates a session to respond to it. The dispatcher owns all sessions it
+ // creates.
+ std::unique_ptr<QuartcDispatcher> dispatcher_;
+
+ // This field is only available before connection was started.
+ std::unique_ptr<QuartcConnectionHelper> pre_connection_helper_;
+
+ // A configuration, containing public key, that may need to be passed to the
+ // client to enable 0rtt.
+ CryptoServerConfig crypto_config_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_QUARTC_ENDPOINT_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint_test.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint_test.cc
new file mode 100644
index 00000000000..3d7d85e8ed8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint_test.cc
@@ -0,0 +1,214 @@
+// 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/quartc/quartc_endpoint.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_fakes.h"
+#include "net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/link.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+
+namespace quic {
+namespace {
+
+class QuartcEndpointTest : public QuicTest {
+ protected:
+ QuartcEndpointTest()
+ : client_transport_(&simulator_,
+ "client_transport",
+ "server_transport",
+ 10 * kDefaultMaxPacketSize),
+ server_transport_(&simulator_,
+ "server_transport",
+ "client_transport",
+ 10 * kDefaultMaxPacketSize),
+ client_server_link_(&client_transport_,
+ &server_transport_,
+ QuicBandwidth::FromKBitsPerSecond(10000),
+ QuicTime::Delta::FromMilliseconds(1)),
+ server_session_delegate_(&server_stream_delegate_,
+ simulator_.GetClock()),
+ server_endpoint_delegate_(&server_session_delegate_),
+ server_endpoint_(
+ QuicMakeUnique<QuartcServerEndpoint>(simulator_.GetAlarmFactory(),
+ simulator_.GetClock(),
+ &server_endpoint_delegate_,
+ QuartcSessionConfig())),
+ client_session_delegate_(&client_stream_delegate_,
+ simulator_.GetClock()),
+ client_endpoint_delegate_(&client_session_delegate_),
+ client_endpoint_(QuicMakeUnique<QuartcClientEndpoint>(
+ simulator_.GetAlarmFactory(),
+ simulator_.GetClock(),
+ &client_endpoint_delegate_,
+ QuartcSessionConfig(),
+ /*serialized_server_config=*/"")) {}
+
+ simulator::Simulator simulator_;
+
+ simulator::SimulatedQuartcPacketTransport client_transport_;
+ simulator::SimulatedQuartcPacketTransport server_transport_;
+ simulator::SymmetricLink client_server_link_;
+
+ FakeQuartcStreamDelegate server_stream_delegate_;
+ FakeQuartcSessionDelegate server_session_delegate_;
+ FakeQuartcEndpointDelegate server_endpoint_delegate_;
+
+ std::unique_ptr<QuartcServerEndpoint> server_endpoint_;
+
+ FakeQuartcStreamDelegate client_stream_delegate_;
+ FakeQuartcSessionDelegate client_session_delegate_;
+ FakeQuartcEndpointDelegate client_endpoint_delegate_;
+
+ std::unique_ptr<QuartcClientEndpoint> client_endpoint_;
+};
+
+// After calling Connect, the client endpoint must wait for an async callback.
+// The callback occurs after a finite amount of time and produces a session.
+TEST_F(QuartcEndpointTest, ClientCreatesSessionAsynchronously) {
+ client_endpoint_->Connect(&client_transport_);
+
+ EXPECT_EQ(client_endpoint_delegate_.session(), nullptr);
+
+ EXPECT_TRUE(simulator_.RunUntil(
+ [this] { return client_endpoint_delegate_.session() != nullptr; }));
+}
+
+// Tests that the server can negotiate for an older QUIC version if the client
+// attempts to connect using a newer version.
+TEST_F(QuartcEndpointTest,
+ QUIC_TEST_DISABLED_IN_CHROME(ServerNegotiatesForOldVersion)) {
+ // Note: for this test, we need support for two versions. Which two shouldn't
+ // matter, but they must be enabled so that the version manager doesn't filter
+ // them out.
+ SetQuicReloadableFlag(quic_enable_version_46, true);
+ SetQuicReloadableFlag(quic_enable_version_43, true);
+
+ // Reset the client endpoint to prefer version 46 but also be capable of
+ // speaking version 43.
+ ParsedQuicVersionVector client_versions;
+ client_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46});
+ client_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43});
+ client_endpoint_ = QuicMakeUnique<QuartcClientEndpoint>(
+ simulator_.GetAlarmFactory(), simulator_.GetClock(),
+ &client_endpoint_delegate_, QuartcSessionConfig(),
+ /*serialized_server_config=*/"",
+ QuicMakeUnique<QuicVersionManager>(client_versions));
+
+ // Reset the server endpoint to only speak version 43.
+ ParsedQuicVersionVector server_versions;
+ server_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43});
+ server_endpoint_ = QuicMakeUnique<QuartcServerEndpoint>(
+ simulator_.GetAlarmFactory(), simulator_.GetClock(),
+ &server_endpoint_delegate_, QuartcSessionConfig(),
+ QuicMakeUnique<QuicVersionManager>(server_versions));
+
+ // The endpoints should be able to establish a connection using version 46.
+ server_endpoint_->Connect(&server_transport_);
+ client_endpoint_->Connect(&client_transport_);
+
+ ASSERT_TRUE(simulator_.RunUntil([this] {
+ return client_endpoint_delegate_.session() != nullptr &&
+ client_endpoint_delegate_.session()->IsEncryptionEstablished() &&
+ server_endpoint_delegate_.session() != nullptr &&
+ server_endpoint_delegate_.session()->IsEncryptionEstablished();
+ }));
+ EXPECT_EQ(client_endpoint_delegate_.session()->connection()->version(),
+ server_versions[0]);
+ EXPECT_EQ(server_endpoint_delegate_.session()->connection()->version(),
+ server_versions[0]);
+}
+
+// Tests that the server can accept connections from clients that use older
+// QUIC versions.
+TEST_F(QuartcEndpointTest,
+ QUIC_TEST_DISABLED_IN_CHROME(ServerAcceptsOldVersion)) {
+ // Note: for this test, we need support for two versions. Which two shouldn't
+ // matter, but they must be enabled so that the version manager doesn't filter
+ // them out.
+ SetQuicReloadableFlag(quic_enable_version_46, true);
+ SetQuicReloadableFlag(quic_enable_version_43, true);
+
+ // Reset the client endpoint to only speak version 43.
+ ParsedQuicVersionVector client_versions;
+ client_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43});
+ client_endpoint_ = QuicMakeUnique<QuartcClientEndpoint>(
+ simulator_.GetAlarmFactory(), simulator_.GetClock(),
+ &client_endpoint_delegate_, QuartcSessionConfig(),
+ /*serialized_server_config=*/"",
+ QuicMakeUnique<QuicVersionManager>(client_versions));
+
+ // Reset the server endpoint to prefer version 46 but also be capable of
+ // speaking version 43.
+ ParsedQuicVersionVector server_versions;
+ server_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46});
+ server_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43});
+ server_endpoint_ = QuicMakeUnique<QuartcServerEndpoint>(
+ simulator_.GetAlarmFactory(), simulator_.GetClock(),
+ &server_endpoint_delegate_, QuartcSessionConfig(),
+ QuicMakeUnique<QuicVersionManager>(server_versions));
+
+ // The endpoints should be able to establish a connection using version 46.
+ server_endpoint_->Connect(&server_transport_);
+ client_endpoint_->Connect(&client_transport_);
+
+ ASSERT_TRUE(simulator_.RunUntil([this] {
+ return client_endpoint_delegate_.session() != nullptr &&
+ client_endpoint_delegate_.session()->IsEncryptionEstablished() &&
+ server_endpoint_delegate_.session() != nullptr &&
+ server_endpoint_delegate_.session()->IsEncryptionEstablished();
+ }));
+ EXPECT_EQ(client_endpoint_delegate_.session()->connection()->version(),
+ client_versions[0]);
+ EXPECT_EQ(server_endpoint_delegate_.session()->connection()->version(),
+ client_versions[0]);
+}
+
+// Tests that version negotiation fails when the client and server support
+// completely disjoint sets of versions.
+TEST_F(QuartcEndpointTest,
+ QUIC_TEST_DISABLED_IN_CHROME(VersionNegotiationWithDisjointVersions)) {
+ // Note: for this test, we need support for two versions. Which two shouldn't
+ // matter, but they must be enabled so that the version manager doesn't filter
+ // them out.
+ SetQuicReloadableFlag(quic_enable_version_46, true);
+ SetQuicReloadableFlag(quic_enable_version_43, true);
+
+ // Reset the client endpoint to only speak version 43.
+ ParsedQuicVersionVector client_versions;
+ client_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43});
+ client_endpoint_ = QuicMakeUnique<QuartcClientEndpoint>(
+ simulator_.GetAlarmFactory(), simulator_.GetClock(),
+ &client_endpoint_delegate_, QuartcSessionConfig(),
+ /*serialized_server_config=*/"",
+ QuicMakeUnique<QuicVersionManager>(client_versions));
+
+ // Reset the server endpoint to only speak version 46.
+ ParsedQuicVersionVector server_versions;
+ server_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46});
+ server_endpoint_ = QuicMakeUnique<QuartcServerEndpoint>(
+ simulator_.GetAlarmFactory(), simulator_.GetClock(),
+ &server_endpoint_delegate_, QuartcSessionConfig(),
+ QuicMakeUnique<QuicVersionManager>(server_versions));
+
+ // The endpoints should be unable to establish a connection.
+ server_endpoint_->Connect(&server_transport_);
+ client_endpoint_->Connect(&client_transport_);
+
+ // Note that the error is reported from the client and *not* the server. The
+ // server sees an invalid version, sends a version negotiation packet, and
+ // never gets a response, because the client stops sending when it can't find
+ // a mutually supported versions.
+ ASSERT_TRUE(simulator_.RunUntil([this] {
+ return client_endpoint_delegate_.session() != nullptr &&
+ client_endpoint_delegate_.session()->error() != QUIC_NO_ERROR;
+ }));
+ EXPECT_EQ(client_endpoint_delegate_.session()->error(), QUIC_INVALID_VERSION);
+}
+
+} // namespace
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.cc
new file mode 100644
index 00000000000..436212fd7a1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.cc
@@ -0,0 +1,242 @@
+// 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/quartc/quartc_factory.h"
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.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_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_session.h"
+
+namespace quic {
+
+std::unique_ptr<QuartcSession> CreateQuartcClientSession(
+ const QuartcSessionConfig& quartc_session_config,
+ const QuicClock* clock,
+ QuicAlarmFactory* alarm_factory,
+ QuicConnectionHelperInterface* connection_helper,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicStringPiece server_crypto_config,
+ QuartcPacketTransport* packet_transport) {
+ DCHECK(packet_transport);
+
+ // QuartcSession will eventually own both |writer| and |quic_connection|.
+ auto writer = QuicMakeUnique<QuartcPacketWriter>(
+ packet_transport, quartc_session_config.max_packet_size);
+
+ // While the QuicConfig is not directly used by the connection, creating it
+ // also sets flag values which must be set before creating the connection.
+ QuicConfig quic_config = CreateQuicConfig(quartc_session_config);
+
+ // |dummy_id| and |dummy_address| are used because Quartc network layer will
+ // not use these two.
+ QuicConnectionId dummy_id = QuicUtils::CreateZeroConnectionId(
+ supported_versions[0].transport_version);
+ QuicSocketAddress dummy_address(QuicIpAddress::Any4(), /*port=*/0);
+ std::unique_ptr<QuicConnection> quic_connection = CreateQuicConnection(
+ dummy_id, dummy_address, connection_helper, alarm_factory, writer.get(),
+ Perspective::IS_CLIENT, supported_versions);
+
+ return QuicMakeUnique<QuartcClientSession>(
+ std::move(quic_connection), quic_config, supported_versions, clock,
+ std::move(writer),
+ CreateCryptoClientConfig(quartc_session_config.pre_shared_key),
+ server_crypto_config);
+}
+
+void ConfigureGlobalQuicSettings() {
+ // Fixes behavior of StopReading() with level-triggered stream sequencers.
+ SetQuicReloadableFlag(quic_stop_reading_when_level_triggered, true);
+
+ // Fix b/110259444.
+ SetQuicReloadableFlag(quic_fix_spurious_ack_alarm, true);
+
+ // Enable version 46 to enable SendMessage API and 'quic bit' per draft 17.
+ SetQuicReloadableFlag(quic_enable_version_46, true);
+
+ // Enable version 47 to enable variable-length connection ids.
+ SetQuicReloadableFlag(quic_enable_version_47, true);
+
+ // Fix for inconsistent reporting of crypto handshake.
+ SetQuicReloadableFlag(quic_fix_has_pending_crypto_data, true);
+
+ // Ensure that we don't drop data because QUIC streams refuse to buffer it.
+ // TODO(b/120099046): Replace this with correct handling of WriteMemSlices().
+ SetQuicFlag(&FLAGS_quic_buffered_data_threshold,
+ std::numeric_limits<int>::max());
+
+ // TODO(b/117157454): Perform version negotiation for Quartc outside of
+ // QuicSession/QuicConnection. Currently default of
+ // quic_restart_flag_quic_no_server_conn_ver_negotiation2 is false,
+ // but we fail blueprint test that sets all QUIC flags to true.
+ //
+ // Forcing flag to false to pass blueprint tests, but eventually we'll have
+ // to implement negotiation outside of QuicConnection.
+ SetQuicRestartFlag(quic_no_server_conn_ver_negotiation2, false);
+ SetQuicReloadableFlag(quic_no_client_conn_ver_negotiation, false);
+
+ // Enable and request QUIC to include receive timestamps in ACK frames.
+ SetQuicReloadableFlag(quic_send_timestamps, true);
+
+ // Enable ACK_DECIMATION_WITH_REORDERING. It requires ack_decimation to be
+ // false.
+ SetQuicReloadableFlag(quic_enable_ack_decimation, false);
+
+ // Note: flag settings have no effect for Exoblaze builds since
+ // SetQuicReloadableFlag() gets stubbed out.
+ SetQuicReloadableFlag(quic_bbr_less_probe_rtt, true); // Enable BBR6,7,8.
+ SetQuicReloadableFlag(quic_unified_iw_options, true); // Enable IWXX opts.
+ SetQuicReloadableFlag(quic_bbr_slower_startup3, true); // Enable BBQX opts.
+ SetQuicReloadableFlag(quic_bbr_flexible_app_limited, true); // Enable BBR9.
+}
+
+QuicConfig CreateQuicConfig(const QuartcSessionConfig& quartc_session_config) {
+ // TODO(b/124398962): Figure out a better way to initialize QUIC flags.
+ // Creating a config shouldn't have global side-effects on flags. However,
+ // this has the advantage of ensuring that flag values stay in sync with the
+ // options requested by configs, so simply splitting the config and flag
+ // settings doesn't seem preferable.
+ ConfigureGlobalQuicSettings();
+
+ // In exoblaze this may return false. DCHECK to avoid problems caused by
+ // incorrect flags configuration.
+ DCHECK(GetQuicReloadableFlag(quic_enable_version_46))
+ << "Your build does not support quic reloadable flags and shouldn't "
+ "place Quartc calls";
+
+ QuicTagVector copt;
+ copt.push_back(kNSTP);
+
+ // Enable and request QUIC to include receive timestamps in ACK frames.
+ copt.push_back(kSTMP);
+
+ // Enable ACK_DECIMATION_WITH_REORDERING. It requires ack_decimation to be
+ // false.
+ copt.push_back(kAKD2);
+
+ // Use unlimited decimation in order to reduce number of unbundled ACKs.
+ copt.push_back(kAKDU);
+
+ // Enable time-based loss detection.
+ copt.push_back(kTIME);
+
+ copt.push_back(kBBR3); // Stay in low-gain until in-flight < BDP.
+ copt.push_back(kBBR5); // 40 RTT ack aggregation.
+ copt.push_back(kBBR6); // Use a 0.75 * BDP cwnd during PROBE_RTT.
+ copt.push_back(kBBR8); // Skip PROBE_RTT if app-limited.
+ copt.push_back(kBBR9); // Ignore app-limited if enough data is in flight.
+ copt.push_back(kBBQ1); // 2.773 pacing gain in STARTUP.
+ copt.push_back(kBBQ2); // 2.0 CWND gain in STARTUP.
+ copt.push_back(kBBQ4); // 0.75 pacing gain in DRAIN.
+ copt.push_back(k1RTT); // Exit STARTUP after 1 RTT with no gains.
+ copt.push_back(kIW10); // 10-packet (14600 byte) initial cwnd.
+
+ if (!quartc_session_config.enable_tail_loss_probe) {
+ copt.push_back(kNTLP);
+ }
+
+ // TODO(b/112192153): Test and possible enable slower startup when pipe
+ // filling is ready to use. Slower startup is kBBRS.
+
+ QuicConfig quic_config;
+
+ // Use the limits for the session & stream flow control. The default 16KB
+ // limit leads to significantly undersending (not reaching BWE on the outgoing
+ // bitrate) due to blocked frames, and it leads to high latency (and one-way
+ // delay). Setting it to its limits is not going to cause issues (our streams
+ // are small generally, and if we were to buffer 24MB it wouldn't be the end
+ // of the world). We can consider setting different limits in future (e.g. 1MB
+ // stream, 1.5MB session). It's worth noting that on 1mbps bitrate, limit of
+ // 24MB can capture approx 4 minutes of the call, and the default increase in
+ // size of the window (half of the window size) is approximately 2 minutes of
+ // the call.
+ quic_config.SetInitialSessionFlowControlWindowToSend(
+ kSessionReceiveWindowLimit);
+ quic_config.SetInitialStreamFlowControlWindowToSend(
+ kStreamReceiveWindowLimit);
+ quic_config.SetConnectionOptionsToSend(copt);
+ quic_config.SetClientConnectionOptions(copt);
+ if (quartc_session_config.max_time_before_crypto_handshake >
+ QuicTime::Delta::Zero()) {
+ quic_config.set_max_time_before_crypto_handshake(
+ quartc_session_config.max_time_before_crypto_handshake);
+ }
+ if (quartc_session_config.max_idle_time_before_crypto_handshake >
+ QuicTime::Delta::Zero()) {
+ quic_config.set_max_idle_time_before_crypto_handshake(
+ quartc_session_config.max_idle_time_before_crypto_handshake);
+ }
+ if (quartc_session_config.idle_network_timeout > QuicTime::Delta::Zero()) {
+ quic_config.SetIdleNetworkTimeout(
+ quartc_session_config.idle_network_timeout,
+ quartc_session_config.idle_network_timeout);
+ }
+
+ // The ICE transport provides a unique 5-tuple for each connection. Save
+ // overhead by omitting the connection id.
+ quic_config.SetBytesForConnectionIdToSend(0);
+
+ // Allow up to 1000 incoming streams at once. Quartc streams typically contain
+ // one audio or video frame and close immediately. However, when a video frame
+ // becomes larger than one packet, there is some delay between the start and
+ // end of each stream. The default maximum of 100 only leaves about 1 second
+ // of headroom (Quartc sends ~30 video frames per second) before QUIC starts
+ // to refuse incoming streams. Back-pressure should clear backlogs of
+ // incomplete streams, but targets 1 second for recovery. Increasing the
+ // number of open streams gives sufficient headroom to recover before QUIC
+ // refuses new streams.
+ quic_config.SetMaxIncomingDynamicStreamsToSend(1000);
+
+ return quic_config;
+}
+
+std::unique_ptr<QuicConnection> CreateQuicConnection(
+ QuicConnectionId connection_id,
+ const QuicSocketAddress& peer_address,
+ QuicConnectionHelperInterface* connection_helper,
+ QuicAlarmFactory* alarm_factory,
+ QuicPacketWriter* packet_writer,
+ Perspective perspective,
+ ParsedQuicVersionVector supported_versions) {
+ auto quic_connection = QuicMakeUnique<QuicConnection>(
+ connection_id, peer_address, connection_helper, alarm_factory,
+ packet_writer,
+ /*owns_writer=*/false, perspective, supported_versions);
+ quic_connection->SetMaxPacketLength(
+ packet_writer->GetMaxPacketSize(peer_address));
+
+ QuicSentPacketManager& sent_packet_manager =
+ quic_connection->sent_packet_manager();
+
+ // Default delayed ack time is 25ms.
+ // If data packets are sent less often (e.g. because p-time was modified),
+ // we would force acks to be sent every 25ms regardless, increasing
+ // overhead. Since generally we guarantee a packet every 20ms, changing
+ // this value should have miniscule effect on quality on good connections,
+ // but on poor connections, changing this number significantly reduced the
+ // number of ack-only packets.
+ // The p-time can go up to as high as 120ms, and when it does, it's
+ // when the low overhead is the most important thing. Ideally it should be
+ // above 120ms, but it cannot be higher than 0.5*RTO, which equals to 100ms.
+ sent_packet_manager.set_delayed_ack_time(
+ QuicTime::Delta::FromMilliseconds(100));
+
+ quic_connection->set_fill_up_link_during_probing(true);
+
+ // We start ack decimation after 15 packets. Typically, we would see
+ // 1-2 crypto handshake packets, one media packet, and 10 probing packets.
+ // We want to get acks for the probing packets as soon as possible,
+ // but we can start using ack decimation right after first probing completes.
+ // The default was to not start ack decimation for the first 100 packets.
+ quic_connection->set_min_received_before_ack_decimation(15);
+
+ return quic_connection;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.h
new file mode 100644
index 00000000000..df55cd3a3ba
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.h
@@ -0,0 +1,67 @@
+// 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_QUARTC_QUARTC_FACTORY_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_FACTORY_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h"
+#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/quartc/quartc_packet_writer.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_session.h"
+
+namespace quic {
+
+struct QuartcSessionConfig {
+ // If a pre-shared cryptographic key is available for this session, specify it
+ // here. This value will only be used if non-empty.
+ std::string pre_shared_key;
+
+ // The maximum size of the packet can be written with the packet writer.
+ // 1200 bytes by default.
+ QuicPacketLength max_packet_size = 1200;
+
+ // Timeouts for the crypto handshake. Set them to higher values to
+ // prevent closing the session before it started on a slow network.
+ // Zero entries are ignored and QUIC defaults are used in that case.
+ QuicTime::Delta max_idle_time_before_crypto_handshake =
+ QuicTime::Delta::Zero();
+ QuicTime::Delta max_time_before_crypto_handshake = QuicTime::Delta::Zero();
+ QuicTime::Delta idle_network_timeout = QuicTime::Delta::Zero();
+
+ // Tail loss probes (TLP) are enabled by default, but it may be useful to
+ // disable them in tests. We can also consider disabling them in production
+ // if we discover that tail loss probes add overhead in low bitrate audio.
+ bool enable_tail_loss_probe = true;
+};
+
+// Creates a new QuartcClientSession using the given configuration.
+std::unique_ptr<QuartcSession> CreateQuartcClientSession(
+ const QuartcSessionConfig& quartc_session_config,
+ const QuicClock* clock,
+ QuicAlarmFactory* alarm_factory,
+ QuicConnectionHelperInterface* connection_helper,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicStringPiece server_crypto_config,
+ QuartcPacketTransport* packet_transport);
+
+// Configures global settings, such as supported quic versions.
+// Must execute on QUIC thread.
+void ConfigureGlobalQuicSettings();
+
+// Must execute on QUIC thread.
+QuicConfig CreateQuicConfig(const QuartcSessionConfig& quartc_session_config);
+
+std::unique_ptr<QuicConnection> CreateQuicConnection(
+ QuicConnectionId connection_id,
+ const QuicSocketAddress& peer_address,
+ QuicConnectionHelperInterface* connection_helper,
+ QuicAlarmFactory* alarm_factory,
+ QuicPacketWriter* packet_writer,
+ Perspective perspective,
+ ParsedQuicVersionVector supported_versions);
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_QUARTC_FACTORY_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_fakes.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_fakes.h
new file mode 100644
index 00000000000..17e3874051c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_fakes.h
@@ -0,0 +1,139 @@
+// 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_QUARTC_QUARTC_FAKES_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_FAKES_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_endpoint.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_session.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_stream.h"
+
+namespace quic {
+
+class FakeQuartcEndpointDelegate : public QuartcEndpoint::Delegate {
+ public:
+ explicit FakeQuartcEndpointDelegate(QuartcSession::Delegate* session_delegate)
+ : session_delegate_(session_delegate) {}
+
+ void OnSessionCreated(QuartcSession* session) override {
+ CHECK_EQ(session_, nullptr);
+ CHECK_NE(session, nullptr);
+ session_ = session;
+ session_->SetDelegate(session_delegate_);
+ session_->StartCryptoHandshake();
+ }
+
+ void OnConnectError(QuicErrorCode error,
+ const std::string& error_details) override {
+ LOG(FATAL) << "Unexpected error during QuartcEndpoint::Connect(); error="
+ << error << ", error_details=" << error_details;
+ }
+
+ QuartcSession* session() { return session_; }
+
+ private:
+ QuartcSession::Delegate* session_delegate_;
+ QuartcSession* session_ = nullptr;
+};
+
+class FakeQuartcSessionDelegate : public QuartcSession::Delegate {
+ public:
+ explicit FakeQuartcSessionDelegate(QuartcStream::Delegate* stream_delegate,
+ const QuicClock* clock)
+ : stream_delegate_(stream_delegate), clock_(clock) {}
+
+ void OnConnectionWritable() override {
+ LOG(INFO) << "Connection writable!";
+ if (!writable_time_.IsInitialized()) {
+ writable_time_ = clock_->Now();
+ }
+ }
+
+ // Called when peers have established forward-secure encryption
+ void OnCryptoHandshakeComplete() override {
+ LOG(INFO) << "Crypto handshake complete!";
+ crypto_handshake_time_ = clock_->Now();
+ }
+
+ // Called when connection closes locally, or remotely by peer.
+ void OnConnectionClosed(QuicErrorCode error_code,
+ const std::string& error_details,
+ ConnectionCloseSource source) override {
+ connected_ = false;
+ }
+
+ // Called when an incoming QUIC stream is created.
+ void OnIncomingStream(QuartcStream* quartc_stream) override {
+ last_incoming_stream_ = quartc_stream;
+ last_incoming_stream_->SetDelegate(stream_delegate_);
+ }
+
+ void OnMessageReceived(QuicStringPiece message) override {
+ incoming_messages_.emplace_back(message);
+ }
+
+ void OnCongestionControlChange(QuicBandwidth bandwidth_estimate,
+ QuicBandwidth pacing_rate,
+ QuicTime::Delta latest_rtt) override {}
+
+ QuartcStream* last_incoming_stream() { return last_incoming_stream_; }
+
+ // Returns all received messages.
+ const std::vector<std::string>& incoming_messages() {
+ return incoming_messages_;
+ }
+
+ bool connected() { return connected_; }
+ QuicTime writable_time() const { return writable_time_; }
+ QuicTime crypto_handshake_time() const { return crypto_handshake_time_; }
+
+ private:
+ QuartcStream* last_incoming_stream_;
+ std::vector<std::string> incoming_messages_;
+ bool connected_ = true;
+ QuartcStream::Delegate* stream_delegate_;
+ QuicTime writable_time_ = QuicTime::Zero();
+ QuicTime crypto_handshake_time_ = QuicTime::Zero();
+ const QuicClock* clock_;
+};
+
+class FakeQuartcStreamDelegate : public QuartcStream::Delegate {
+ public:
+ size_t OnReceived(QuartcStream* stream,
+ iovec* iov,
+ size_t iov_length,
+ bool fin) override {
+ size_t bytes_consumed = 0;
+ for (size_t i = 0; i < iov_length; ++i) {
+ received_data_[stream->id()] += std::string(
+ static_cast<const char*>(iov[i].iov_base), iov[i].iov_len);
+ bytes_consumed += iov[i].iov_len;
+ }
+ return bytes_consumed;
+ }
+
+ void OnClose(QuartcStream* stream) override {
+ errors_[stream->id()] = stream->stream_error();
+ }
+
+ void OnBufferChanged(QuartcStream* stream) override {}
+
+ bool has_data() { return !received_data_.empty(); }
+ std::map<QuicStreamId, std::string> data() { return received_data_; }
+
+ QuicRstStreamErrorCode stream_error(QuicStreamId id) { return errors_[id]; }
+
+ private:
+ std::map<QuicStreamId, std::string> received_data_;
+ std::map<QuicStreamId, QuicRstStreamErrorCode> errors_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_QUARTC_FAKES_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_interval_counter.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_interval_counter.h
new file mode 100644
index 00000000000..fe3b083c695
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_interval_counter.h
@@ -0,0 +1,122 @@
+// 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_QUARTC_QUARTC_INTERVAL_COUNTER_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_INTERVAL_COUNTER_H_
+
+#include <stddef.h>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/quic_interval.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval_set.h"
+
+namespace quic {
+
+// QuartcIntervalCounter counts the number of times each value appears within
+// a set of potentially overlapping intervals.
+//
+// QuartcIntervalCounter is not intended for widespread use. Consider replacing
+// it with a full interval-map if more use cases arise.
+//
+// QuartcIntervalCounter is only suitable for cases where the maximum count is
+// expected to remain low. (For example, counting the number of times the same
+// portions of stream data are lost.) It is inefficient when the maximum count
+// becomes high.
+template <typename T>
+class QuartcIntervalCounter {
+ public:
+ // Adds |interval| to the counter. The count associated with each value in
+ // |interval| is incremented by one. |interval| may overlap with previous
+ // intervals added to the counter.
+ //
+ // For each possible value:
+ // - If the value is present in both |interval| and the counter, the count
+ // associated with that value is incremented by one.
+ // - If the value is present in |interval| but not counter, the count
+ // associated with that value is set to one (incremented from zero).
+ // - If the value is absent from |interval|, the count is unchanged.
+ //
+ // Time complexity is O(|MaxCount| * the complexity of adding an interval to a
+ // QuicIntervalSet).
+ void AddInterval(QuicInterval<T> interval);
+
+ // Removes an interval from the counter. This method may be called to prune
+ // irrelevant intervals from the counter. This is useful to prevent unbounded
+ // growth.
+ //
+ // Time complexity is O(|MaxCount| * the complexity of removing an interval
+ // from a QuicIntervalSet).
+ void RemoveInterval(QuicInterval<T> interval);
+
+ // Returns the maximum number of times any single value has appeared in
+ // intervals added to the counter.
+ //
+ // Time complexity is constant.
+ size_t MaxCount() const { return intervals_by_count_.size(); }
+
+ // Returns the maximum number of times a particular value has appeared in
+ // intervals added to the counter.
+ //
+ // Time complexity is O(|MaxCount| * log(number of non-contiguous intervals)).
+ size_t Count(const T& value) const;
+
+ private:
+ // Each entry in this vector represents the intervals of values counted at
+ // least i + 1 times, where i is the index of the entry.
+ //
+ // Whenever an interval is added to the counter, each value in the interval is
+ // added to the first entry which does not already contain that value. If
+ // part of an interval is already present in the last entry, a new entry is
+ // added containing that part.
+ //
+ // Note that this means each value present in one of the interval sets will be
+ // present in all previous sets.
+ std::vector<QuicIntervalSet<T>> intervals_by_count_;
+};
+
+template <typename T>
+void QuartcIntervalCounter<T>::AddInterval(QuicInterval<T> interval) {
+ // After the Nth iteration, |leftover| contains the parts of |interval| that
+ // are already present in the first N entries. These parts of |interval| have
+ // been added to the counter more than N times.
+ QuicIntervalSet<T> leftover(interval);
+ for (auto& intervals : intervals_by_count_) {
+ QuicIntervalSet<T> tmp = leftover;
+ leftover.Intersection(intervals);
+ intervals.Union(tmp);
+ }
+
+ // Whatever ranges are still in |leftover| are already in all the entries
+ // Add a new entry containing |leftover|.
+ if (!leftover.Empty()) {
+ intervals_by_count_.push_back(leftover);
+ }
+}
+
+template <typename T>
+void QuartcIntervalCounter<T>::RemoveInterval(QuicInterval<T> interval) {
+ // Remove the interval from each entry in the vector, popping any entries that
+ // become empty.
+ for (size_t i = intervals_by_count_.size(); i > 0; --i) {
+ intervals_by_count_[i - 1].Difference(interval);
+ if (intervals_by_count_[i - 1].Empty()) {
+ intervals_by_count_.pop_back();
+ }
+ }
+}
+
+template <typename T>
+size_t QuartcIntervalCounter<T>::Count(const T& value) const {
+ // The index of the last entry containing |value| gives its count.
+ for (size_t i = intervals_by_count_.size(); i > 0; --i) {
+ if (intervals_by_count_[i - 1].Contains(value)) {
+ return i;
+ }
+ }
+ return 0;
+}
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_QUARTC_INTERVAL_COUNTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_interval_counter_test.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_interval_counter_test.cc
new file mode 100644
index 00000000000..9bc9af5e55b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_interval_counter_test.cc
@@ -0,0 +1,110 @@
+// 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/quartc/quartc_interval_counter.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace {
+
+class QuartcIntervalCounterTest : public QuicTest {
+ protected:
+ QuartcIntervalCounter<int> counter_;
+};
+
+void ExpectCount(const QuartcIntervalCounter<int>& counter,
+ QuicInterval<int> interval,
+ size_t count) {
+ for (int i = interval.min(); i < interval.max(); ++i) {
+ EXPECT_EQ(counter.Count(i), count) << "i=" << i;
+ }
+}
+
+TEST_F(QuartcIntervalCounterTest, InitiallyEmpty) {
+ EXPECT_EQ(counter_.MaxCount(), 0u);
+}
+
+TEST_F(QuartcIntervalCounterTest, SameInterval) {
+ counter_.AddInterval(QuicInterval<int>(0, 6));
+ EXPECT_EQ(counter_.MaxCount(), 1u);
+ ExpectCount(counter_, QuicInterval<int>(0, 6), 1);
+
+ counter_.AddInterval(QuicInterval<int>(0, 6));
+ EXPECT_EQ(counter_.MaxCount(), 2u);
+ ExpectCount(counter_, QuicInterval<int>(0, 6), 2);
+}
+
+TEST_F(QuartcIntervalCounterTest, DisjointIntervals) {
+ counter_.AddInterval(QuicInterval<int>(0, 5));
+ EXPECT_EQ(counter_.MaxCount(), 1u);
+ ExpectCount(counter_, QuicInterval<int>(0, 5), 1);
+ ExpectCount(counter_, QuicInterval<int>(5, 10), 0);
+
+ counter_.AddInterval(QuicInterval<int>(5, 10));
+ EXPECT_EQ(counter_.MaxCount(), 1u);
+ ExpectCount(counter_, QuicInterval<int>(0, 5), 1);
+ ExpectCount(counter_, QuicInterval<int>(5, 10), 1);
+}
+
+TEST_F(QuartcIntervalCounterTest, OverlappingIntervals) {
+ counter_.AddInterval(QuicInterval<int>(0, 6));
+ EXPECT_EQ(counter_.MaxCount(), 1u);
+ ExpectCount(counter_, QuicInterval<int>(0, 6), 1);
+ ExpectCount(counter_, QuicInterval<int>(6, 10), 0);
+
+ counter_.AddInterval(QuicInterval<int>(5, 10));
+ EXPECT_EQ(counter_.MaxCount(), 2u);
+ ExpectCount(counter_, QuicInterval<int>(0, 5), 1);
+ EXPECT_EQ(counter_.Count(5), 2u);
+ ExpectCount(counter_, QuicInterval<int>(6, 10), 1);
+}
+
+TEST_F(QuartcIntervalCounterTest, IntervalsWithGapThenOverlap) {
+ counter_.AddInterval(QuicInterval<int>(0, 4));
+ EXPECT_EQ(counter_.MaxCount(), 1u);
+ ExpectCount(counter_, QuicInterval<int>(0, 4), 1);
+ ExpectCount(counter_, QuicInterval<int>(4, 10), 0);
+
+ counter_.AddInterval(QuicInterval<int>(7, 10));
+ EXPECT_EQ(counter_.MaxCount(), 1u);
+ ExpectCount(counter_, QuicInterval<int>(0, 4), 1);
+ ExpectCount(counter_, QuicInterval<int>(4, 7), 0);
+ ExpectCount(counter_, QuicInterval<int>(7, 10), 1);
+
+ counter_.AddInterval(QuicInterval<int>(3, 8));
+ EXPECT_EQ(counter_.MaxCount(), 2u);
+ ExpectCount(counter_, QuicInterval<int>(0, 3), 1);
+ EXPECT_EQ(counter_.Count(3), 2u);
+ ExpectCount(counter_, QuicInterval<int>(4, 7), 1);
+ EXPECT_EQ(counter_.Count(7), 2u);
+ ExpectCount(counter_, QuicInterval<int>(8, 10), 1);
+}
+
+TEST_F(QuartcIntervalCounterTest, RemoveIntervals) {
+ counter_.AddInterval(QuicInterval<int>(0, 5));
+ EXPECT_EQ(counter_.MaxCount(), 1u);
+ ExpectCount(counter_, QuicInterval<int>(0, 5), 1);
+
+ counter_.AddInterval(QuicInterval<int>(4, 10));
+ EXPECT_EQ(counter_.MaxCount(), 2u);
+ ExpectCount(counter_, QuicInterval<int>(0, 4), 1);
+ EXPECT_EQ(counter_.Count(4), 2u);
+ ExpectCount(counter_, QuicInterval<int>(5, 10), 1);
+
+ counter_.RemoveInterval(QuicInterval<int>(0, 5));
+ EXPECT_EQ(counter_.MaxCount(), 1u);
+ ExpectCount(counter_, QuicInterval<int>(0, 5), 0);
+ ExpectCount(counter_, QuicInterval<int>(5, 10), 1);
+
+ counter_.RemoveInterval(QuicInterval<int>(5, 10));
+ EXPECT_EQ(counter_.MaxCount(), 0u);
+ ExpectCount(counter_, QuicInterval<int>(0, 10), 0);
+}
+
+} // namespace
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_packet_writer.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_packet_writer.cc
new file mode 100644
index 00000000000..64a72a89410
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_packet_writer.cc
@@ -0,0 +1,76 @@
+// 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/quartc/quartc_packet_writer.h"
+
+namespace quic {
+
+std::unique_ptr<PerPacketOptions> QuartcPerPacketOptions::Clone() const {
+ return QuicMakeUnique<QuartcPerPacketOptions>(*this);
+}
+
+QuartcPacketWriter::QuartcPacketWriter(QuartcPacketTransport* packet_transport,
+ QuicByteCount max_packet_size)
+ : packet_transport_(packet_transport), max_packet_size_(max_packet_size) {}
+
+WriteResult QuartcPacketWriter::WritePacket(
+ const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) {
+ DCHECK(packet_transport_);
+
+ QuartcPacketTransport::PacketInfo info;
+ QuartcPerPacketOptions* quartc_options =
+ static_cast<QuartcPerPacketOptions*>(options);
+ if (quartc_options && quartc_options->connection) {
+ info.packet_number =
+ quartc_options->connection->packet_generator().packet_number();
+ }
+ int bytes_written = packet_transport_->Write(buffer, buf_len, info);
+ if (bytes_written <= 0) {
+ writable_ = false;
+ return WriteResult(WRITE_STATUS_BLOCKED, EWOULDBLOCK);
+ }
+ return WriteResult(WRITE_STATUS_OK, bytes_written);
+}
+
+bool QuartcPacketWriter::IsWriteBlocked() const {
+ return !writable_;
+}
+
+QuicByteCount QuartcPacketWriter::GetMaxPacketSize(
+ const QuicSocketAddress& peer_address) const {
+ return max_packet_size_;
+}
+
+void QuartcPacketWriter::SetWritable() {
+ writable_ = true;
+}
+
+bool QuartcPacketWriter::SupportsReleaseTime() const {
+ return false;
+}
+
+bool QuartcPacketWriter::IsBatchMode() const {
+ return false;
+}
+
+char* QuartcPacketWriter::GetNextWriteLocation(
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address) {
+ return nullptr;
+}
+
+WriteResult QuartcPacketWriter::Flush() {
+ return WriteResult(WRITE_STATUS_OK, 0);
+}
+
+void QuartcPacketWriter::SetPacketTransportDelegate(
+ QuartcPacketTransport::Delegate* delegate) {
+ packet_transport_->SetDelegate(delegate);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h
new file mode 100644
index 00000000000..f3eb885fd53
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h
@@ -0,0 +1,113 @@
+// 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_QUARTC_QUARTC_PACKET_WRITER_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_PACKET_WRITER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+
+namespace quic {
+
+// Send and receive packets, like a virtual UDP socket. For example, this
+// could be implemented by WebRTC's IceTransport.
+class QuartcPacketTransport {
+ public:
+ // Additional metadata provided for each packet written.
+ struct PacketInfo {
+ QuicPacketNumber packet_number;
+ };
+
+ // Delegate for packet transport callbacks. Note that the delegate is not
+ // thread-safe. Packet transport implementations must ensure that callbacks
+ // are synchronized with all other work done by QUIC.
+ class Delegate {
+ public:
+ virtual ~Delegate() = default;
+
+ // Called whenever the transport can write.
+ virtual void OnTransportCanWrite() = 0;
+
+ // Called when the transport receives a packet.
+ virtual void OnTransportReceived(const char* data, size_t data_len) = 0;
+ };
+
+ virtual ~QuartcPacketTransport() {}
+
+ // Called by the QuartcPacketWriter when writing packets to the network.
+ // Return the number of written bytes. Return 0 if the write is blocked.
+ virtual int Write(const char* buffer,
+ size_t buf_len,
+ const PacketInfo& info) = 0;
+
+ // Sets the delegate which must be called when the transport can write or
+ // a packet is received. QUIC sets |delegate| to a nonnull pointer when it
+ // is ready to process incoming packets and sets |delegate| to nullptr before
+ // QUIC is deleted. Implementations may assume |delegate| remains valid until
+ // it is set to nullptr.
+ virtual void SetDelegate(Delegate* delegate) = 0;
+};
+
+struct QuartcPerPacketOptions : public PerPacketOptions {
+ std::unique_ptr<PerPacketOptions> Clone() const override;
+
+ // The connection which is sending this packet.
+ QuicConnection* connection = nullptr;
+};
+
+// Implements a QuicPacketWriter using a QuartcPacketTransport, which allows a
+// QuicConnection to use (for example), a WebRTC IceTransport.
+class QuartcPacketWriter : public QuicPacketWriter {
+ public:
+ QuartcPacketWriter(QuartcPacketTransport* packet_transport,
+ QuicByteCount max_packet_size);
+ ~QuartcPacketWriter() override {}
+
+ // The QuicConnection calls WritePacket and the QuicPacketWriter writes them
+ // to the QuartcSession::PacketTransport.
+ WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) override;
+
+ // Whether the underneath |transport_| is blocked. If this returns true,
+ // outgoing QUIC packets are queued by QuicConnection until SetWritable() is
+ // called.
+ bool IsWriteBlocked() const override;
+
+ // Maximum size of the QUIC packet which can be written. Users such as WebRTC
+ // can set the value through the QuartcFactoryConfig without updating the QUIC
+ // code.
+ QuicByteCount GetMaxPacketSize(
+ const QuicSocketAddress& peer_address) const override;
+
+ // Sets the packet writer to a writable (non-blocked) state.
+ void SetWritable() override;
+
+ bool SupportsReleaseTime() const override;
+
+ bool IsBatchMode() const override;
+
+ char* GetNextWriteLocation(const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address) override;
+
+ WriteResult Flush() override;
+
+ void SetPacketTransportDelegate(QuartcPacketTransport::Delegate* delegate);
+
+ private:
+ // QuartcPacketWriter will not own the transport.
+ QuartcPacketTransport* packet_transport_;
+ // The maximum size of the packet can be written by this writer.
+ QuicByteCount max_packet_size_;
+
+ // Whether packets can be written.
+ bool writable_ = false;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_QUARTC_PACKET_WRITER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_session.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_session.cc
new file mode 100644
index 00000000000..80031dd0f2c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_session.cc
@@ -0,0 +1,397 @@
+// 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/quartc/quartc_session.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_mem_slice_storage.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h"
+
+namespace quic {
+namespace {
+
+// Arbitrary server port number for net::QuicCryptoClientConfig.
+const int kQuicServerPort = 0;
+
+} // namespace
+
+QuartcSession::QuartcSession(std::unique_ptr<QuicConnection> connection,
+ Visitor* visitor,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicClock* clock)
+ : QuicSession(connection.get(), visitor, config, supported_versions),
+ connection_(std::move(connection)),
+ clock_(clock),
+ per_packet_options_(QuicMakeUnique<QuartcPerPacketOptions>()) {
+ per_packet_options_->connection = connection_.get();
+ connection_->set_per_packet_options(per_packet_options_.get());
+}
+
+QuartcSession::~QuartcSession() {}
+
+QuartcStream* QuartcSession::CreateOutgoingBidirectionalStream() {
+ // Use default priority for incoming QUIC streams.
+ // TODO(zhihuang): Determine if this value is correct.
+ return ActivateDataStream(CreateDataStream(
+ GetNextOutgoingBidirectionalStreamId(), QuicStream::kDefaultPriority));
+}
+
+bool QuartcSession::SendOrQueueMessage(std::string message) {
+ if (!CanSendMessage()) {
+ QUIC_LOG(ERROR) << "Quic session does not support SendMessage";
+ return false;
+ }
+
+ if (message.size() > GetCurrentLargestMessagePayload()) {
+ QUIC_LOG(ERROR) << "Message is too big, message_size=" << message.size()
+ << ", GetCurrentLargestMessagePayload="
+ << GetCurrentLargestMessagePayload();
+ return false;
+ }
+
+ // There may be other messages in send queue, so we have to add message
+ // to the queue and call queue processing helper.
+ send_message_queue_.emplace_back(std::move(message));
+
+ ProcessSendMessageQueue();
+
+ return true;
+}
+
+void QuartcSession::ProcessSendMessageQueue() {
+ while (!send_message_queue_.empty()) {
+ struct iovec iov = {const_cast<char*>(send_message_queue_.front().data()),
+ send_message_queue_.front().length()};
+ QuicMemSliceStorage storage(
+ &iov, 1, connection()->helper()->GetStreamSendBufferAllocator(),
+ send_message_queue_.front().length());
+ MessageResult result = SendMessage(storage.ToSpan());
+
+ const size_t message_size = send_message_queue_.front().size();
+
+ // Handle errors.
+ switch (result.status) {
+ case MESSAGE_STATUS_SUCCESS:
+ QUIC_VLOG(1) << "Quartc message sent, message_id=" << result.message_id
+ << ", message_size=" << message_size;
+ break;
+
+ // If connection is congestion controlled or not writable yet, stop
+ // send loop and we'll retry again when we get OnCanWrite notification.
+ case MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED:
+ case MESSAGE_STATUS_BLOCKED:
+ QUIC_VLOG(1) << "Quartc message not sent because connection is blocked"
+ << ", message will be retried later, status="
+ << result.status << ", message_size=" << message_size;
+
+ return;
+
+ // Other errors are unexpected. We do not propagate error to Quartc,
+ // because writes can be delayed.
+ case MESSAGE_STATUS_UNSUPPORTED:
+ case MESSAGE_STATUS_TOO_LARGE:
+ case MESSAGE_STATUS_INTERNAL_ERROR:
+ QUIC_DLOG(DFATAL)
+ << "Failed to send quartc message due to unexpected error"
+ << ", message will not be retried, status=" << result.status
+ << ", message_size=" << message_size;
+ break;
+ }
+
+ send_message_queue_.pop_front();
+ }
+}
+
+void QuartcSession::OnCanWrite() {
+ // TODO(b/119640244): Since we currently use messages for audio and streams
+ // for video, it makes sense to process queued messages first, then call quic
+ // core OnCanWrite, which will resend queued streams. Long term we may need
+ // better solution especially if quic connection is used for both data and
+ // media.
+
+ // Process quartc messages that were previously blocked.
+ ProcessSendMessageQueue();
+
+ QuicSession::OnCanWrite();
+}
+
+void QuartcSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
+ QuicSession::OnCryptoHandshakeEvent(event);
+ switch (event) {
+ case ENCRYPTION_FIRST_ESTABLISHED:
+ case ENCRYPTION_REESTABLISHED:
+ // 1-rtt setup triggers 'ENCRYPTION_REESTABLISHED' (after REJ, when the
+ // CHLO is sent).
+ DCHECK(IsEncryptionEstablished());
+ DCHECK(session_delegate_);
+ session_delegate_->OnConnectionWritable();
+ break;
+ case HANDSHAKE_CONFIRMED:
+ // On the server, handshake confirmed is the first time when you can start
+ // writing packets.
+ DCHECK(IsEncryptionEstablished());
+ DCHECK(IsCryptoHandshakeConfirmed());
+
+ DCHECK(session_delegate_);
+ session_delegate_->OnConnectionWritable();
+ session_delegate_->OnCryptoHandshakeComplete();
+ break;
+ }
+}
+
+void QuartcSession::CancelStream(QuicStreamId stream_id) {
+ ResetStream(stream_id, QuicRstStreamErrorCode::QUIC_STREAM_CANCELLED);
+}
+
+void QuartcSession::ResetStream(QuicStreamId stream_id,
+ QuicRstStreamErrorCode error) {
+ if (!IsOpenStream(stream_id)) {
+ return;
+ }
+ QuicStream* stream = QuicSession::GetOrCreateStream(stream_id);
+ if (stream) {
+ stream->Reset(error);
+ }
+}
+
+void QuartcSession::OnCongestionWindowChange(QuicTime now) {
+ DCHECK(session_delegate_);
+ const RttStats* rtt_stats = connection_->sent_packet_manager().GetRttStats();
+
+ QuicBandwidth bandwidth_estimate =
+ connection_->sent_packet_manager().BandwidthEstimate();
+
+ QuicByteCount in_flight =
+ connection_->sent_packet_manager().GetBytesInFlight();
+ QuicBandwidth pacing_rate =
+ connection_->sent_packet_manager().GetSendAlgorithm()->PacingRate(
+ in_flight);
+
+ session_delegate_->OnCongestionControlChange(bandwidth_estimate, pacing_rate,
+ rtt_stats->latest_rtt());
+}
+
+bool QuartcSession::ShouldKeepConnectionAlive() const {
+ // TODO(mellem): Quartc may want different keepalive logic than HTTP.
+ return GetNumOpenDynamicStreams() > 0;
+}
+
+void QuartcSession::OnConnectionClosed(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source) {
+ QuicSession::OnConnectionClosed(error, error_details, source);
+ DCHECK(session_delegate_);
+ session_delegate_->OnConnectionClosed(error, error_details, source);
+}
+
+void QuartcSession::CloseConnection(const std::string& details) {
+ connection_->CloseConnection(
+ QuicErrorCode::QUIC_CONNECTION_CANCELLED, details,
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET_WITH_NO_ACK);
+}
+
+void QuartcSession::SetDelegate(Delegate* session_delegate) {
+ if (session_delegate_) {
+ LOG(WARNING) << "The delegate for the session has already been set.";
+ }
+ session_delegate_ = session_delegate;
+ DCHECK(session_delegate_);
+}
+
+void QuartcSession::OnTransportCanWrite() {
+ connection()->writer()->SetWritable();
+ if (HasDataToWrite()) {
+ connection()->OnCanWrite();
+ }
+}
+
+void QuartcSession::OnTransportReceived(const char* data, size_t data_len) {
+ QuicReceivedPacket packet(data, data_len, clock_->Now());
+ ProcessUdpPacket(connection()->self_address(), connection()->peer_address(),
+ packet);
+}
+
+void QuartcSession::OnMessageReceived(QuicStringPiece message) {
+ session_delegate_->OnMessageReceived(message);
+}
+
+QuicStream* QuartcSession::CreateIncomingStream(QuicStreamId id) {
+ return ActivateDataStream(CreateDataStream(id, QuicStream::kDefaultPriority));
+}
+
+QuicStream* QuartcSession::CreateIncomingStream(PendingStream pending) {
+ return ActivateDataStream(
+ CreateDataStream(std::move(pending), QuicStream::kDefaultPriority));
+}
+
+std::unique_ptr<QuartcStream> QuartcSession::CreateDataStream(
+ QuicStreamId id,
+ spdy::SpdyPriority priority) {
+ if (GetCryptoStream() == nullptr ||
+ !GetCryptoStream()->encryption_established()) {
+ // Encryption not active so no stream created
+ return nullptr;
+ }
+ return InitializeDataStream(QuicMakeUnique<QuartcStream>(id, this), priority);
+}
+
+std::unique_ptr<QuartcStream> QuartcSession::CreateDataStream(
+ PendingStream pending,
+ spdy::SpdyPriority priority) {
+ return InitializeDataStream(QuicMakeUnique<QuartcStream>(std::move(pending)),
+ priority);
+}
+
+std::unique_ptr<QuartcStream> QuartcSession::InitializeDataStream(
+ std::unique_ptr<QuartcStream> stream,
+ spdy::SpdyPriority priority) {
+ // Register the stream to the QuicWriteBlockedList. |priority| is clamped
+ // between 0 and 7, with 0 being the highest priority and 7 the lowest
+ // priority.
+ write_blocked_streams()->UpdateStreamPriority(stream->id(), priority);
+
+ if (IsIncomingStream(stream->id())) {
+ DCHECK(session_delegate_);
+ // Incoming streams need to be registered with the session_delegate_.
+ session_delegate_->OnIncomingStream(stream.get());
+ }
+ return stream;
+}
+
+QuartcStream* QuartcSession::ActivateDataStream(
+ std::unique_ptr<QuartcStream> stream) {
+ // Transfer ownership of the data stream to the session via ActivateStream().
+ QuartcStream* raw = stream.release();
+ if (raw) {
+ // Make QuicSession take ownership of the stream.
+ ActivateStream(std::unique_ptr<QuicStream>(raw));
+ }
+ return raw;
+}
+
+QuartcClientSession::QuartcClientSession(
+ std::unique_ptr<QuicConnection> connection,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicClock* clock,
+ std::unique_ptr<QuartcPacketWriter> packet_writer,
+ std::unique_ptr<QuicCryptoClientConfig> client_crypto_config,
+ QuicStringPiece server_crypto_config)
+ : QuartcSession(std::move(connection),
+ /*visitor=*/nullptr,
+ config,
+ supported_versions,
+ clock),
+ packet_writer_(std::move(packet_writer)),
+ client_crypto_config_(std::move(client_crypto_config)),
+ server_config_(server_crypto_config) {
+ DCHECK_EQ(QuartcSession::connection()->perspective(), Perspective::IS_CLIENT);
+}
+
+QuartcClientSession::~QuartcClientSession() {
+ // The client session is the packet transport delegate, so it must be unset
+ // before the session is deleted.
+ packet_writer_->SetPacketTransportDelegate(nullptr);
+}
+
+void QuartcClientSession::Initialize() {
+ DCHECK(crypto_stream_) << "Do not call QuartcSession::Initialize(), call "
+ "StartCryptoHandshake() instead.";
+ QuartcSession::Initialize();
+
+ // QUIC is ready to process incoming packets after Initialize().
+ // Set the packet transport delegate to begin receiving packets.
+ packet_writer_->SetPacketTransportDelegate(this);
+}
+
+const QuicCryptoStream* QuartcClientSession::GetCryptoStream() const {
+ return crypto_stream_.get();
+}
+
+QuicCryptoStream* QuartcClientSession::GetMutableCryptoStream() {
+ return crypto_stream_.get();
+}
+
+void QuartcClientSession::StartCryptoHandshake() {
+ QuicServerId server_id(/*host=*/"", kQuicServerPort,
+ /*privacy_mode_enabled=*/false);
+
+ if (!server_config_.empty()) {
+ QuicCryptoServerConfig::ConfigOptions options;
+
+ std::string error;
+ QuicWallTime now = clock()->WallNow();
+ QuicCryptoClientConfig::CachedState::ServerConfigState result =
+ client_crypto_config_->LookupOrCreate(server_id)->SetServerConfig(
+ server_config_, now,
+ /*expiry_time=*/now.Add(QuicTime::Delta::Infinite()), &error);
+
+ if (result == QuicCryptoClientConfig::CachedState::SERVER_CONFIG_VALID) {
+ DCHECK_EQ(error, "");
+ client_crypto_config_->LookupOrCreate(server_id)->SetProof(
+ std::vector<std::string>{kDummyCertName}, /*cert_sct=*/"",
+ /*chlo_hash=*/"", /*signature=*/"anything");
+ } else {
+ LOG(DFATAL) << "Unable to set server config, error=" << error;
+ }
+ }
+
+ crypto_stream_ = QuicMakeUnique<QuicCryptoClientStream>(
+ server_id, this,
+ client_crypto_config_->proof_verifier()->CreateDefaultContext(),
+ client_crypto_config_.get(), this);
+ Initialize();
+ crypto_stream_->CryptoConnect();
+}
+
+void QuartcClientSession::OnProofValid(
+ const QuicCryptoClientConfig::CachedState& cached) {
+ // TODO(zhihuang): Handle the proof verification.
+}
+
+void QuartcClientSession::OnProofVerifyDetailsAvailable(
+ const ProofVerifyDetails& verify_details) {
+ // TODO(zhihuang): Handle the proof verification.
+}
+
+QuartcServerSession::QuartcServerSession(
+ std::unique_ptr<QuicConnection> connection,
+ Visitor* visitor,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicClock* clock,
+ const QuicCryptoServerConfig* server_crypto_config,
+ QuicCompressedCertsCache* const compressed_certs_cache,
+ QuicCryptoServerStream::Helper* const stream_helper)
+ : QuartcSession(std::move(connection),
+ visitor,
+ config,
+ supported_versions,
+ clock),
+ server_crypto_config_(server_crypto_config),
+ compressed_certs_cache_(compressed_certs_cache),
+ stream_helper_(stream_helper) {
+ DCHECK_EQ(QuartcSession::connection()->perspective(), Perspective::IS_SERVER);
+}
+
+const QuicCryptoStream* QuartcServerSession::GetCryptoStream() const {
+ return crypto_stream_.get();
+}
+
+QuicCryptoStream* QuartcServerSession::GetMutableCryptoStream() {
+ return crypto_stream_.get();
+}
+
+void QuartcServerSession::StartCryptoHandshake() {
+ crypto_stream_ = QuicMakeUnique<QuicCryptoServerStream>(
+ server_crypto_config_, compressed_certs_cache_,
+ /*use_stateless_rejects_if_peer_supported=*/false, this, stream_helper_);
+ Initialize();
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_session.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_session.h
new file mode 100644
index 00000000000..29e63066efe
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_session.h
@@ -0,0 +1,284 @@
+// 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_QUARTC_QUARTC_SESSION_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_SESSION_H_
+
+#include <memory>
+#include <string>
+
+#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_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_stream.h"
+
+namespace quic {
+
+// QuartcSession owns and manages a QUIC connection.
+class QuartcSession : public QuicSession,
+ public QuartcPacketTransport::Delegate {
+ public:
+ QuartcSession(std::unique_ptr<QuicConnection> connection,
+ Visitor* visitor,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicClock* clock);
+ QuartcSession(const QuartcSession&) = delete;
+ QuartcSession& operator=(const QuartcSession&) = delete;
+ ~QuartcSession() override;
+
+ // QuicSession overrides.
+ QuartcStream* CreateOutgoingBidirectionalStream();
+
+ // Sends short unreliable message using quic message frame (message must fit
+ // in one quic packet). If connection is blocked by congestion control,
+ // message will be queued and resent later after receiving an OnCanWrite
+ // notification.
+ //
+ // Message size must be <= GetLargestMessagePayload().
+ //
+ // Supported in quic version 45 or later.
+ //
+ // Returns false and logs error if message is too long or session does not
+ // support SendMessage API. Other unexpected errors during send will not be
+ // returned, because messages can be sent later if connection is congestion
+ // controlled.
+ bool SendOrQueueMessage(std::string message);
+
+ // Returns largest message payload acceptable in SendQuartcMessage.
+ QuicPacketLength GetCurrentLargestMessagePayload() const {
+ return connection()->GetCurrentLargestMessagePayload();
+ }
+
+ // Return true if transport support message frame.
+ bool CanSendMessage() const {
+ return connection()->transport_version() > QUIC_VERSION_44;
+ }
+
+ void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override;
+
+ // QuicConnectionVisitorInterface overrides.
+ void OnCongestionWindowChange(QuicTime now) override;
+ bool ShouldKeepConnectionAlive() const override;
+
+ void OnCanWrite() override;
+
+ void OnConnectionClosed(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source) override;
+
+ // QuartcSession methods.
+ virtual void StartCryptoHandshake() = 0;
+
+ // Closes the connection with the given human-readable error details.
+ // The connection closes with the QUIC_CONNECTION_CANCELLED error code to
+ // indicate the application closed it.
+ //
+ // Informs the peer that the connection has been closed. This prevents the
+ // peer from waiting until the connection times out.
+ //
+ // Cleans up the underlying QuicConnection's state. Closing the connection
+ // makes it safe to delete the QuartcSession.
+ void CloseConnection(const std::string& details);
+
+ // If the given stream is still open, sends a reset frame to cancel it.
+ // Note: This method cancels a stream by QuicStreamId rather than by pointer
+ // (or by a method on QuartcStream) because QuartcSession (and not
+ // the caller) owns the streams. Streams may finish and be deleted before the
+ // caller tries to cancel them, rendering the caller's pointers invalid.
+ void CancelStream(QuicStreamId stream_id);
+
+ // Callbacks called by the QuartcSession to notify the user of the
+ // QuartcSession of certain events.
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ // Called when the crypto handshake is complete. Crypto handshake on the
+ // client is only completed _after_ SHLO is received, but we can actually
+ // start sending media data right after CHLO is sent.
+ virtual void OnCryptoHandshakeComplete() = 0;
+
+ // Connection can be writable even before crypto handshake is complete.
+ // In particular, on the client, we can start sending data after sending
+ // full CHLO, without waiting for SHLO. This reduces a send delay by 1-rtt.
+ //
+ // This may be called multiple times.
+ virtual void OnConnectionWritable() = 0;
+
+ // Called when a new stream is received from the remote endpoint.
+ virtual void OnIncomingStream(QuartcStream* stream) = 0;
+
+ // Called when network parameters change in response to an ack frame.
+ virtual void OnCongestionControlChange(QuicBandwidth bandwidth_estimate,
+ QuicBandwidth pacing_rate,
+ QuicTime::Delta latest_rtt) = 0;
+
+ // Called when the connection is closed. This means all of the streams will
+ // be closed and no new streams can be created.
+ virtual void OnConnectionClosed(QuicErrorCode error_code,
+ const std::string& error_details,
+ ConnectionCloseSource source) = 0;
+
+ // Called when message (sent as SendMessage) is received.
+ virtual void OnMessageReceived(QuicStringPiece message) = 0;
+
+ // TODO(zhihuang): Add proof verification.
+ };
+
+ // The |delegate| is not owned by QuartcSession.
+ void SetDelegate(Delegate* session_delegate);
+
+ // Called when CanWrite() changes from false to true.
+ void OnTransportCanWrite() override;
+
+ // Called when a packet has been received and should be handled by the
+ // QuicConnection.
+ void OnTransportReceived(const char* data, size_t data_len) override;
+
+ void OnMessageReceived(QuicStringPiece message) override;
+
+ // Returns number of queued (not sent) messages submitted by
+ // SendOrQueueMessage. Messages are queued if connection is congestion
+ // controlled.
+ size_t send_message_queue_size() const { return send_message_queue_.size(); }
+
+ protected:
+ // QuicSession override.
+ QuicStream* CreateIncomingStream(QuicStreamId id) override;
+ QuicStream* CreateIncomingStream(PendingStream pending) override;
+
+ std::unique_ptr<QuartcStream> CreateDataStream(QuicStreamId id,
+ spdy::SpdyPriority priority);
+ std::unique_ptr<QuartcStream> CreateDataStream(PendingStream pending,
+ spdy::SpdyPriority priority);
+ // Activates a QuartcStream. The session takes ownership of the stream, but
+ // returns an unowned pointer to the stream for convenience.
+ QuartcStream* ActivateDataStream(std::unique_ptr<QuartcStream> stream);
+
+ void ResetStream(QuicStreamId stream_id, QuicRstStreamErrorCode error);
+
+ const QuicClock* clock() { return clock_; }
+
+ private:
+ std::unique_ptr<QuartcStream> InitializeDataStream(
+ std::unique_ptr<QuartcStream> stream,
+ spdy::SpdyPriority priority);
+
+ void ProcessSendMessageQueue();
+
+ // Take ownership of the QuicConnection. Note: if |connection_| changes,
+ // the new value of |connection_| must be given to |packet_writer_| before any
+ // packets are written. Otherwise, |packet_writer_| will crash.
+ std::unique_ptr<QuicConnection> connection_;
+
+ // For recording packet receipt time
+ const QuicClock* clock_;
+
+ // Not owned by QuartcSession.
+ Delegate* session_delegate_ = nullptr;
+
+ // Options passed to the packet writer for each packet.
+ std::unique_ptr<QuartcPerPacketOptions> per_packet_options_;
+
+ // Queue of pending messages sent by SendQuartcMessage that were not sent
+ // yet or blocked by congestion control. Messages are queued in the order
+ // of sent by SendOrQueueMessage().
+ QuicDeque<std::string> send_message_queue_;
+};
+
+class QuartcClientSession : public QuartcSession,
+ public QuicCryptoClientStream::ProofHandler {
+ public:
+ QuartcClientSession(
+ std::unique_ptr<QuicConnection> connection,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicClock* clock,
+ std::unique_ptr<QuartcPacketWriter> packet_writer,
+ std::unique_ptr<QuicCryptoClientConfig> client_crypto_config,
+ QuicStringPiece server_crypto_config);
+ QuartcClientSession(const QuartcClientSession&) = delete;
+ QuartcClientSession& operator=(const QuartcClientSession&) = delete;
+
+ ~QuartcClientSession() override;
+
+ // Initialize should not be called on a QuartcSession. Instead, call
+ // StartCryptoHandshake().
+ // TODO(mellem): Move creation of the crypto stream into Initialize() and
+ // remove StartCryptoHandshake() to bring QuartcSession in line with other
+ // implementations of QuicSession, which can be started by calling
+ // Initialize().
+ void Initialize() override;
+
+ // Accessors for the client crypto stream.
+ QuicCryptoStream* GetMutableCryptoStream() override;
+ const QuicCryptoStream* GetCryptoStream() const override;
+
+ // Initializes the session and sends a handshake.
+ void StartCryptoHandshake() override;
+
+ // ProofHandler overrides.
+ void OnProofValid(const QuicCryptoClientConfig::CachedState& cached) override;
+
+ // Called by the client crypto handshake when proof verification details
+ // become available, either because proof verification is complete, or when
+ // cached details are used.
+ void OnProofVerifyDetailsAvailable(
+ const ProofVerifyDetails& verify_details) override;
+
+ private:
+ // Packet writer used by |connection_|.
+ std::unique_ptr<QuartcPacketWriter> packet_writer_;
+
+ // Config for QUIC crypto stream.
+ std::unique_ptr<QuicCryptoClientConfig> client_crypto_config_;
+
+ // Client perspective crypto stream.
+ std::unique_ptr<QuicCryptoClientStream> crypto_stream_;
+
+ const std::string server_config_;
+};
+
+class QuartcServerSession : public QuartcSession {
+ public:
+ QuartcServerSession(std::unique_ptr<QuicConnection> connection,
+ Visitor* visitor,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicClock* clock,
+ const QuicCryptoServerConfig* server_crypto_config,
+ QuicCompressedCertsCache* const compressed_certs_cache,
+ QuicCryptoServerStream::Helper* const stream_helper);
+ QuartcServerSession(const QuartcServerSession&) = delete;
+ QuartcServerSession& operator=(const QuartcServerSession&) = delete;
+
+ // Accessors for the server crypto stream.
+ QuicCryptoStream* GetMutableCryptoStream() override;
+ const QuicCryptoStream* GetCryptoStream() const override;
+
+ // Initializes the session and prepares to receive a handshake.
+ void StartCryptoHandshake() override;
+
+ private:
+ // Config for QUIC crypto stream.
+ const QuicCryptoServerConfig* server_crypto_config_;
+
+ // Used by QUIC crypto server stream to track most recently compressed certs.
+ QuicCompressedCertsCache* const compressed_certs_cache_;
+
+ // This helper is needed to create QuicCryptoServerStream.
+ QuicCryptoServerStream::Helper* const stream_helper_;
+
+ // Server perspective crypto stream.
+ std::unique_ptr<QuicCryptoServerStream> crypto_stream_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_QUARTC_SESSION_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_session_test.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_session_test.cc
new file mode 100644
index 00000000000..31abd57231e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_session_test.cc
@@ -0,0 +1,559 @@
+// 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/quartc/quartc_session.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.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_clock.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_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h"
+#include "net/third_party/quiche/src/quic/quartc/counting_packet_filter.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_endpoint.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_fakes.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h"
+#include "net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/packet_filter.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+
+namespace quic {
+
+namespace {
+
+constexpr QuicTime::Delta kPropagationDelay =
+ QuicTime::Delta::FromMilliseconds(10);
+// Propagation delay and a bit, but no more than full RTT.
+constexpr QuicTime::Delta kPropagationDelayAndABit =
+ QuicTime::Delta::FromMilliseconds(12);
+
+static QuicByteCount kDefaultMaxPacketSize = 1200;
+
+class QuartcSessionTest : public QuicTest {
+ public:
+ ~QuartcSessionTest() override {}
+
+ void Init(bool create_client_endpoint = true) {
+ client_transport_ =
+ QuicMakeUnique<simulator::SimulatedQuartcPacketTransport>(
+ &simulator_, "client_transport", "server_transport",
+ 10 * kDefaultMaxPacketSize);
+ server_transport_ =
+ QuicMakeUnique<simulator::SimulatedQuartcPacketTransport>(
+ &simulator_, "server_transport", "client_transport",
+ 10 * kDefaultMaxPacketSize);
+
+ client_filter_ = QuicMakeUnique<simulator::CountingPacketFilter>(
+ &simulator_, "client_filter", client_transport_.get());
+
+ client_server_link_ = QuicMakeUnique<simulator::SymmetricLink>(
+ client_filter_.get(), server_transport_.get(),
+ QuicBandwidth::FromKBitsPerSecond(10 * 1000), kPropagationDelay);
+
+ client_stream_delegate_ = QuicMakeUnique<FakeQuartcStreamDelegate>();
+ client_session_delegate_ = QuicMakeUnique<FakeQuartcSessionDelegate>(
+ client_stream_delegate_.get(), simulator_.GetClock());
+ client_endpoint_delegate_ = QuicMakeUnique<FakeQuartcEndpointDelegate>(
+ client_session_delegate_.get());
+
+ server_stream_delegate_ = QuicMakeUnique<FakeQuartcStreamDelegate>();
+ server_session_delegate_ = QuicMakeUnique<FakeQuartcSessionDelegate>(
+ server_stream_delegate_.get(), simulator_.GetClock());
+ server_endpoint_delegate_ = QuicMakeUnique<FakeQuartcEndpointDelegate>(
+ server_session_delegate_.get());
+
+ // No 0-rtt setup, because server config is empty.
+ // CannotCreateDataStreamBeforeHandshake depends on 1-rtt setup.
+ if (create_client_endpoint) {
+ client_endpoint_ = QuicMakeUnique<QuartcClientEndpoint>(
+ simulator_.GetAlarmFactory(), simulator_.GetClock(),
+ client_endpoint_delegate_.get(), quic::QuartcSessionConfig(),
+ /*serialized_server_config=*/"");
+ }
+ server_endpoint_ = QuicMakeUnique<QuartcServerEndpoint>(
+ simulator_.GetAlarmFactory(), simulator_.GetClock(),
+ server_endpoint_delegate_.get(), quic::QuartcSessionConfig());
+ }
+
+ // Note that input session config will apply to both server and client.
+ // Perspective and packet_transport will be overwritten.
+ void CreateClientAndServerSessions(const QuartcSessionConfig& session_config,
+ bool init = true) {
+ if (init) {
+ Init();
+ }
+
+ server_endpoint_->Connect(server_transport_.get());
+ client_endpoint_->Connect(client_transport_.get());
+
+ CHECK(simulator_.RunUntil([this] {
+ return client_endpoint_delegate_->session() != nullptr &&
+ server_endpoint_delegate_->session() != nullptr;
+ }));
+
+ client_peer_ = client_endpoint_delegate_->session();
+ server_peer_ = server_endpoint_delegate_->session();
+ }
+
+ // Runs all tasks scheduled in the next 200 ms.
+ void RunTasks() { simulator_.RunFor(QuicTime::Delta::FromMilliseconds(200)); }
+
+ void AwaitHandshake() {
+ simulator_.RunUntil([this] {
+ return client_peer_->IsCryptoHandshakeConfirmed() &&
+ server_peer_->IsCryptoHandshakeConfirmed();
+ });
+ }
+
+ // Test handshake establishment and sending/receiving of data for two
+ // directions.
+ void TestSendReceiveStreams() {
+ ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
+ ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
+ ASSERT_TRUE(server_peer_->IsEncryptionEstablished());
+ ASSERT_TRUE(client_peer_->IsEncryptionEstablished());
+
+ // Now we can establish encrypted outgoing stream.
+ QuartcStream* outgoing_stream =
+ server_peer_->CreateOutgoingBidirectionalStream();
+ QuicStreamId stream_id = outgoing_stream->id();
+ ASSERT_NE(nullptr, outgoing_stream);
+ EXPECT_TRUE(server_peer_->ShouldKeepConnectionAlive());
+
+ outgoing_stream->SetDelegate(server_stream_delegate_.get());
+
+ // Send a test message from peer 1 to peer 2.
+ char kTestMessage[] = "Hello";
+ test::QuicTestMemSliceVector data(
+ {std::make_pair(kTestMessage, strlen(kTestMessage))});
+ outgoing_stream->WriteMemSlices(data.span(), /*fin=*/false);
+ RunTasks();
+
+ // Wait for peer 2 to receive messages.
+ ASSERT_TRUE(client_stream_delegate_->has_data());
+
+ QuartcStream* incoming = client_session_delegate_->last_incoming_stream();
+ ASSERT_TRUE(incoming);
+ EXPECT_EQ(incoming->id(), stream_id);
+ EXPECT_TRUE(client_peer_->ShouldKeepConnectionAlive());
+
+ EXPECT_EQ(client_stream_delegate_->data()[stream_id], kTestMessage);
+ // Send a test message from peer 2 to peer 1.
+ char kTestResponse[] = "Response";
+ test::QuicTestMemSliceVector response(
+ {std::make_pair(kTestResponse, strlen(kTestResponse))});
+ incoming->WriteMemSlices(response.span(), /*fin=*/false);
+ RunTasks();
+ // Wait for peer 1 to receive messages.
+ ASSERT_TRUE(server_stream_delegate_->has_data());
+
+ EXPECT_EQ(server_stream_delegate_->data()[stream_id], kTestResponse);
+ }
+
+ // Test sending/receiving of messages for two directions.
+ void TestSendReceiveMessage() {
+ ASSERT_TRUE(server_peer_->CanSendMessage());
+ ASSERT_TRUE(client_peer_->CanSendMessage());
+
+ // Send message from peer 1 to peer 2.
+ ASSERT_TRUE(server_peer_->SendOrQueueMessage("Message from server"));
+
+ // First message in each direction should not be queued.
+ EXPECT_EQ(server_peer_->send_message_queue_size(), 0u);
+
+ // Wait for peer 2 to receive message.
+ RunTasks();
+
+ EXPECT_THAT(client_session_delegate_->incoming_messages(),
+ testing::ElementsAre("Message from server"));
+
+ // Send message from peer 2 to peer 1.
+ ASSERT_TRUE(client_peer_->SendOrQueueMessage("Message from client"));
+
+ // First message in each direction should not be queued.
+ EXPECT_EQ(client_peer_->send_message_queue_size(), 0u);
+
+ // Wait for peer 1 to receive message.
+ RunTasks();
+
+ EXPECT_THAT(server_session_delegate_->incoming_messages(),
+ testing::ElementsAre("Message from client"));
+ }
+
+ // Test for sending multiple messages that also result in queueing.
+ // This is one-way test, which is run in given direction.
+ void TestSendReceiveQueuedMessages(bool direction_from_server) {
+ // Send until queue_size number of messages are queued.
+ constexpr size_t queue_size = 10;
+
+ ASSERT_TRUE(server_peer_->CanSendMessage());
+ ASSERT_TRUE(client_peer_->CanSendMessage());
+
+ QuartcSession* const peer_sending =
+ direction_from_server ? server_peer_ : client_peer_;
+
+ FakeQuartcSessionDelegate* const delegate_receiving =
+ direction_from_server ? client_session_delegate_.get()
+ : server_session_delegate_.get();
+
+ // There should be no messages in the queue before we start sending.
+ EXPECT_EQ(peer_sending->send_message_queue_size(), 0u);
+
+ // Send messages from peer 1 to peer 2 until required number of messages
+ // are queued in unsent message queue.
+ std::vector<std::string> sent_messages;
+ while (peer_sending->send_message_queue_size() < queue_size) {
+ sent_messages.push_back(
+ QuicStrCat("Sending message, index=", sent_messages.size()));
+ ASSERT_TRUE(peer_sending->SendOrQueueMessage(sent_messages.back()));
+ }
+
+ // Wait for peer 2 to receive all messages.
+ RunTasks();
+
+ EXPECT_EQ(delegate_receiving->incoming_messages(), sent_messages);
+ }
+
+ // Test sending long messages:
+ // - message of maximum allowed length should succeed
+ // - message of > maximum allowed length should fail.
+ void TestSendLongMessage() {
+ ASSERT_TRUE(server_peer_->CanSendMessage());
+ ASSERT_TRUE(client_peer_->CanSendMessage());
+
+ // Send message of maximum allowed length.
+ std::string message_max_long =
+ std::string(server_peer_->GetCurrentLargestMessagePayload(), 'A');
+ ASSERT_TRUE(server_peer_->SendOrQueueMessage(message_max_long));
+
+ // Send long message which should fail.
+ std::string message_too_long =
+ std::string(server_peer_->GetCurrentLargestMessagePayload() + 1, 'B');
+ ASSERT_FALSE(server_peer_->SendOrQueueMessage(message_too_long));
+
+ // Wait for peer 2 to receive message.
+ RunTasks();
+
+ // Client should only receive one message of allowed length.
+ EXPECT_THAT(client_session_delegate_->incoming_messages(),
+ testing::ElementsAre(message_max_long));
+ }
+
+ // Test that client and server are not connected after handshake failure.
+ void TestDisconnectAfterFailedHandshake() {
+ EXPECT_TRUE(!client_session_delegate_->connected());
+ EXPECT_TRUE(!server_session_delegate_->connected());
+
+ EXPECT_FALSE(client_peer_->IsEncryptionEstablished());
+ EXPECT_FALSE(client_peer_->IsCryptoHandshakeConfirmed());
+
+ EXPECT_FALSE(server_peer_->IsEncryptionEstablished());
+ EXPECT_FALSE(server_peer_->IsCryptoHandshakeConfirmed());
+ }
+
+ protected:
+ simulator::Simulator simulator_;
+
+ std::unique_ptr<simulator::SimulatedQuartcPacketTransport> client_transport_;
+ std::unique_ptr<simulator::SimulatedQuartcPacketTransport> server_transport_;
+ std::unique_ptr<simulator::CountingPacketFilter> client_filter_;
+ std::unique_ptr<simulator::SymmetricLink> client_server_link_;
+
+ std::unique_ptr<FakeQuartcStreamDelegate> client_stream_delegate_;
+ std::unique_ptr<FakeQuartcSessionDelegate> client_session_delegate_;
+ std::unique_ptr<FakeQuartcEndpointDelegate> client_endpoint_delegate_;
+ std::unique_ptr<FakeQuartcStreamDelegate> server_stream_delegate_;
+ std::unique_ptr<FakeQuartcSessionDelegate> server_session_delegate_;
+ std::unique_ptr<FakeQuartcEndpointDelegate> server_endpoint_delegate_;
+
+ std::unique_ptr<QuartcClientEndpoint> client_endpoint_;
+ std::unique_ptr<QuartcServerEndpoint> server_endpoint_;
+
+ QuartcSession* client_peer_ = nullptr;
+ QuartcSession* server_peer_ = nullptr;
+};
+
+TEST_F(QuartcSessionTest, SendReceiveStreams) {
+ CreateClientAndServerSessions(QuartcSessionConfig());
+ AwaitHandshake();
+ TestSendReceiveStreams();
+}
+
+TEST_F(QuartcSessionTest, SendReceiveMessages) {
+ CreateClientAndServerSessions(QuartcSessionConfig());
+ AwaitHandshake();
+ TestSendReceiveMessage();
+}
+
+TEST_F(QuartcSessionTest, SendReceiveQueuedMessages) {
+ CreateClientAndServerSessions(QuartcSessionConfig());
+ AwaitHandshake();
+ TestSendReceiveQueuedMessages(/*direction_from_server=*/true);
+ TestSendReceiveQueuedMessages(/*direction_from_server=*/false);
+}
+
+TEST_F(QuartcSessionTest, SendMessageFails) {
+ CreateClientAndServerSessions(QuartcSessionConfig());
+ AwaitHandshake();
+ TestSendLongMessage();
+}
+
+TEST_F(QuartcSessionTest, TestCryptoHandshakeCanWriteTriggers) {
+ CreateClientAndServerSessions(QuartcSessionConfig());
+
+ AwaitHandshake();
+
+ RunTasks();
+
+ ASSERT_TRUE(client_session_delegate_->writable_time().IsInitialized());
+ ASSERT_TRUE(
+ client_session_delegate_->crypto_handshake_time().IsInitialized());
+ // On client, we are writable 1-rtt before crypto handshake is complete.
+ ASSERT_LT(client_session_delegate_->writable_time(),
+ client_session_delegate_->crypto_handshake_time());
+
+ ASSERT_TRUE(server_session_delegate_->writable_time().IsInitialized());
+ ASSERT_TRUE(
+ server_session_delegate_->crypto_handshake_time().IsInitialized());
+ // On server, the writable time and crypto handshake are the same. (when SHLO
+ // is sent).
+ ASSERT_EQ(server_session_delegate_->writable_time(),
+ server_session_delegate_->crypto_handshake_time());
+}
+
+TEST_F(QuartcSessionTest, PreSharedKeyHandshake) {
+ QuartcSessionConfig config;
+ config.pre_shared_key = "foo";
+ CreateClientAndServerSessions(config);
+ AwaitHandshake();
+ TestSendReceiveStreams();
+ TestSendReceiveMessage();
+}
+
+// Test that data streams are not created before handshake.
+TEST_F(QuartcSessionTest, CannotCreateDataStreamBeforeHandshake) {
+ CreateClientAndServerSessions(QuartcSessionConfig());
+ EXPECT_EQ(nullptr, server_peer_->CreateOutgoingBidirectionalStream());
+ EXPECT_EQ(nullptr, client_peer_->CreateOutgoingBidirectionalStream());
+}
+
+TEST_F(QuartcSessionTest, CancelQuartcStream) {
+ CreateClientAndServerSessions(QuartcSessionConfig());
+ AwaitHandshake();
+ ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
+ ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
+
+ QuartcStream* stream = client_peer_->CreateOutgoingBidirectionalStream();
+ ASSERT_NE(nullptr, stream);
+
+ uint32_t id = stream->id();
+ EXPECT_FALSE(client_peer_->IsClosedStream(id));
+ stream->SetDelegate(client_stream_delegate_.get());
+ client_peer_->CancelStream(id);
+ EXPECT_EQ(stream->stream_error(),
+ QuicRstStreamErrorCode::QUIC_STREAM_CANCELLED);
+ EXPECT_TRUE(client_peer_->IsClosedStream(id));
+}
+
+// TODO(b/112561077): This is the wrong layer for this test. We should write a
+// test specifically for QuartcPacketWriter with a stubbed-out
+// QuartcPacketTransport and remove
+// SimulatedQuartcPacketTransport::last_packet_number().
+TEST_F(QuartcSessionTest, WriterGivesPacketNumberToTransport) {
+ CreateClientAndServerSessions(QuartcSessionConfig());
+ AwaitHandshake();
+ ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
+ ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
+
+ QuartcStream* stream = client_peer_->CreateOutgoingBidirectionalStream();
+ stream->SetDelegate(client_stream_delegate_.get());
+
+ char kClientMessage[] = "Hello";
+ test::QuicTestMemSliceVector stream_data(
+ {std::make_pair(kClientMessage, strlen(kClientMessage))});
+ stream->WriteMemSlices(stream_data.span(), /*fin=*/false);
+ RunTasks();
+
+ // The transport should see the latest packet number sent by QUIC.
+ EXPECT_EQ(
+ client_transport_->last_packet_number(),
+ client_peer_->connection()->sent_packet_manager().GetLargestSentPacket());
+}
+
+TEST_F(QuartcSessionTest, CloseConnection) {
+ CreateClientAndServerSessions(QuartcSessionConfig());
+ AwaitHandshake();
+ ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
+ ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
+
+ client_peer_->CloseConnection("Connection closed by client");
+ EXPECT_FALSE(client_session_delegate_->connected());
+ RunTasks();
+ EXPECT_FALSE(server_session_delegate_->connected());
+}
+
+TEST_F(QuartcSessionTest, StreamRetransmissionEnabled) {
+ CreateClientAndServerSessions(QuartcSessionConfig());
+ AwaitHandshake();
+ ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
+ ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
+
+ QuartcStream* stream = client_peer_->CreateOutgoingBidirectionalStream();
+ QuicStreamId stream_id = stream->id();
+ stream->SetDelegate(client_stream_delegate_.get());
+ stream->set_cancel_on_loss(false);
+
+ client_filter_->set_packets_to_drop(1);
+
+ char kClientMessage[] = "Hello";
+ test::QuicTestMemSliceVector stream_data(
+ {std::make_pair(kClientMessage, strlen(kClientMessage))});
+ stream->WriteMemSlices(stream_data.span(), /*fin=*/false);
+ RunTasks();
+
+ // Stream data should make it despite packet loss.
+ ASSERT_TRUE(server_stream_delegate_->has_data());
+ EXPECT_EQ(server_stream_delegate_->data()[stream_id], kClientMessage);
+}
+
+TEST_F(QuartcSessionTest, StreamRetransmissionDisabled) {
+ // Disable tail loss probe, otherwise test maybe flaky because dropped
+ // message will be retransmitted to detect tail loss.
+ QuartcSessionConfig session_config;
+ session_config.enable_tail_loss_probe = false;
+ CreateClientAndServerSessions(session_config);
+
+ // Disable probing retransmissions, otherwise test maybe flaky because dropped
+ // message will be retransmitted to to probe for more bandwidth.
+ client_peer_->connection()->set_fill_up_link_during_probing(false);
+
+ AwaitHandshake();
+ ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
+ ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
+
+ // The client sends an ACK for the crypto handshake next. This must be
+ // flushed before we set the filter to drop the next packet, in order to
+ // ensure that the filter drops a data-bearing packet instead of just an ack.
+ RunTasks();
+
+ QuartcStream* stream = client_peer_->CreateOutgoingBidirectionalStream();
+ QuicStreamId stream_id = stream->id();
+ stream->SetDelegate(client_stream_delegate_.get());
+ stream->set_cancel_on_loss(true);
+
+ client_filter_->set_packets_to_drop(1);
+
+ char kMessage[] = "Hello";
+ test::QuicTestMemSliceVector stream_data(
+ {std::make_pair(kMessage, strlen(kMessage))});
+ stream->WriteMemSlices(stream_data.span(), /*fin=*/false);
+ simulator_.RunFor(QuicTime::Delta::FromMilliseconds(1));
+
+ // Send another packet to trigger loss detection.
+ QuartcStream* stream_1 = client_peer_->CreateOutgoingBidirectionalStream();
+ stream_1->SetDelegate(client_stream_delegate_.get());
+
+ char kMessage1[] = "Second message";
+ test::QuicTestMemSliceVector stream_data_1(
+ {std::make_pair(kMessage1, strlen(kMessage1))});
+ stream_1->WriteMemSlices(stream_data_1.span(), /*fin=*/false);
+ RunTasks();
+
+ // QUIC should try to retransmit the first stream by loss detection. Instead,
+ // it will cancel itself.
+ EXPECT_THAT(server_stream_delegate_->data()[stream_id], testing::IsEmpty());
+
+ EXPECT_TRUE(client_peer_->IsClosedStream(stream_id));
+ EXPECT_TRUE(server_peer_->IsClosedStream(stream_id));
+ EXPECT_EQ(client_stream_delegate_->stream_error(stream_id),
+ QUIC_STREAM_CANCELLED);
+ EXPECT_EQ(server_stream_delegate_->stream_error(stream_id),
+ QUIC_STREAM_CANCELLED);
+}
+
+TEST_F(QuartcSessionTest, ServerRegistersAsWriteBlocked) {
+ // Initialize client and server session, but with the server write-blocked.
+ Init();
+ server_transport_->SetWritable(false);
+ CreateClientAndServerSessions(QuartcSessionConfig(), /*init=*/false);
+
+ // Let the client send a few copies of the CHLO. The server can't respond, as
+ // it's still write-blocked.
+ RunTasks();
+
+ // Making the server's transport writable should trigger a callback that
+ // reaches the server session, allowing it to write packets.
+ server_transport_->SetWritable(true);
+
+ // Now the server should respond with the SHLO, encryption should be
+ // established, and data should flow normally.
+ // Note that if the server is *not* correctly registered as write-blocked,
+ // it will crash here (see b/124527328 for details).
+ AwaitHandshake();
+ TestSendReceiveStreams();
+}
+
+TEST_F(QuartcSessionTest, PreSharedKeyHandshakeIs0RTT) {
+ QuartcSessionConfig session_config;
+ session_config.pre_shared_key = "foo";
+
+ // Client endpoint is created below. Destructing client endpoint
+ // causes issues with the simulator.
+ Init(/*create_client_endpoint=*/false);
+
+ server_endpoint_->Connect(server_transport_.get());
+
+ client_endpoint_ = QuicMakeUnique<QuartcClientEndpoint>(
+ simulator_.GetAlarmFactory(), simulator_.GetClock(),
+ client_endpoint_delegate_.get(), QuartcSessionConfig(),
+ // This is the key line here. It passes through the server config
+ // from the server to the client.
+ server_endpoint_->server_crypto_config());
+
+ client_endpoint_->Connect(client_transport_.get());
+
+ // Running for 1ms. This is shorter than the RTT, so the
+ // client session should be created, but server won't be created yet.
+ simulator_.RunFor(QuicTime::Delta::FromMilliseconds(1));
+
+ client_peer_ = client_endpoint_delegate_->session();
+ server_peer_ = server_endpoint_delegate_->session();
+
+ ASSERT_NE(client_peer_, nullptr);
+ ASSERT_EQ(server_peer_, nullptr);
+
+ // Write data to the client before running tasks. This should be sent by the
+ // client and received by the server if the handshake is 0RTT.
+ // If this test fails, add 'RunTasks()' above, and see what error is sent
+ // by the server in the rejection message.
+ QuartcStream* stream = client_peer_->CreateOutgoingBidirectionalStream();
+ ASSERT_NE(stream, nullptr);
+ QuicStreamId stream_id = stream->id();
+ stream->SetDelegate(client_stream_delegate_.get());
+
+ char message[] = "Hello in 0RTTs!";
+ test::QuicTestMemSliceVector data({std::make_pair(message, strlen(message))});
+ stream->WriteMemSlices(data.span(), /*fin=*/false);
+
+ // This will now run the rest of the connection. But the
+ // Server peer will receive the CHLO and message after 1 delay.
+ simulator_.RunFor(kPropagationDelayAndABit);
+
+ // If we can decrypt the data, it means that 0 rtt was successful.
+ // This is because we waited only a propagation delay. So if the decryption
+ // failed, we would send sREJ instead of SHLO, but it wouldn't be delivered to
+ // the client yet.
+ ASSERT_TRUE(server_stream_delegate_->has_data());
+ EXPECT_EQ(server_stream_delegate_->data()[stream_id], message);
+}
+
+} // namespace
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.cc
new file mode 100644
index 00000000000..49ade235765
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.cc
@@ -0,0 +1,171 @@
+// 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/quartc/quartc_stream.h"
+
+#include <memory>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/quic_ack_listener_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.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/core/quic_stream_sequencer_buffer.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+QuartcStream::QuartcStream(QuicStreamId id, QuicSession* session)
+ : QuicStream(id, session, /*is_static=*/false, BIDIRECTIONAL) {
+ sequencer()->set_level_triggered(true);
+}
+
+QuartcStream::QuartcStream(PendingStream pending)
+ : QuicStream(std::move(pending), BIDIRECTIONAL) {
+ sequencer()->set_level_triggered(true);
+}
+
+QuartcStream::~QuartcStream() {}
+
+void QuartcStream::OnDataAvailable() {
+ bool fin = sequencer()->ReadableBytes() + sequencer()->NumBytesConsumed() ==
+ sequencer()->close_offset();
+
+ // Upper bound on number of readable regions. Each complete block's worth of
+ // data crosses at most one region boundary. The remainder may cross one more
+ // boundary. Number of regions is one more than the number of region
+ // boundaries crossed.
+ size_t iov_length = sequencer()->ReadableBytes() /
+ QuicStreamSequencerBuffer::kBlockSizeBytes +
+ 2;
+ std::unique_ptr<iovec[]> iovecs = QuicMakeUnique<iovec[]>(iov_length);
+ iov_length = sequencer()->GetReadableRegions(iovecs.get(), iov_length);
+
+ sequencer()->MarkConsumed(
+ delegate_->OnReceived(this, iovecs.get(), iov_length, fin));
+ if (sequencer()->IsClosed()) {
+ OnFinRead();
+ }
+}
+
+void QuartcStream::OnClose() {
+ QuicStream::OnClose();
+ DCHECK(delegate_);
+ delegate_->OnClose(this);
+}
+
+void QuartcStream::OnStreamDataConsumed(size_t bytes_consumed) {
+ QuicStream::OnStreamDataConsumed(bytes_consumed);
+
+ DCHECK(delegate_);
+ delegate_->OnBufferChanged(this);
+}
+
+void QuartcStream::OnDataBuffered(
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ const QuicReferenceCountedPointer<QuicAckListenerInterface>& ack_listener) {
+ DCHECK(delegate_);
+ delegate_->OnBufferChanged(this);
+}
+
+bool QuartcStream::OnStreamFrameAcked(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_acked,
+ QuicTime::Delta ack_delay_time,
+ QuicByteCount* newly_acked_length) {
+ // Previous losses of acked data are no longer relevant to the retransmission
+ // count. Once data is acked, it will never be retransmitted.
+ lost_frame_counter_.RemoveInterval(
+ QuicInterval<QuicStreamOffset>(offset, offset + data_length));
+
+ return QuicStream::OnStreamFrameAcked(offset, data_length, fin_acked,
+ ack_delay_time, newly_acked_length);
+}
+
+void QuartcStream::OnStreamFrameRetransmitted(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_retransmitted) {
+ QuicStream::OnStreamFrameRetransmitted(offset, data_length,
+ fin_retransmitted);
+
+ DCHECK(delegate_);
+ delegate_->OnBufferChanged(this);
+}
+
+void QuartcStream::OnStreamFrameLost(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_lost) {
+ QuicStream::OnStreamFrameLost(offset, data_length, fin_lost);
+
+ lost_frame_counter_.AddInterval(
+ QuicInterval<QuicStreamOffset>(offset, offset + data_length));
+
+ DCHECK(delegate_);
+ delegate_->OnBufferChanged(this);
+}
+
+void QuartcStream::OnCanWrite() {
+ if (lost_frame_counter_.MaxCount() >
+ static_cast<size_t>(max_retransmission_count_) &&
+ HasPendingRetransmission()) {
+ Reset(QUIC_STREAM_CANCELLED);
+ return;
+ }
+ QuicStream::OnCanWrite();
+}
+
+bool QuartcStream::cancel_on_loss() {
+ return max_retransmission_count_ == 0;
+}
+
+void QuartcStream::set_cancel_on_loss(bool cancel_on_loss) {
+ if (cancel_on_loss) {
+ max_retransmission_count_ = 0;
+ } else {
+ max_retransmission_count_ = std::numeric_limits<int>::max();
+ }
+}
+
+int QuartcStream::max_retransmission_count() const {
+ return max_retransmission_count_;
+}
+
+void QuartcStream::set_max_retransmission_count(int max_retransmission_count) {
+ max_retransmission_count_ = max_retransmission_count;
+}
+
+QuicByteCount QuartcStream::BytesPendingRetransmission() {
+ if (lost_frame_counter_.MaxCount() >
+ static_cast<size_t>(max_retransmission_count_)) {
+ return 0; // Lost bytes will never be retransmitted.
+ }
+ QuicByteCount bytes = 0;
+ for (const auto& interval : send_buffer().pending_retransmissions()) {
+ bytes += interval.Length();
+ }
+ return bytes;
+}
+
+QuicStreamOffset QuartcStream::ReadOffset() {
+ return sequencer()->NumBytesConsumed();
+}
+
+void QuartcStream::FinishWriting() {
+ WriteOrBufferData(QuicStringPiece(nullptr, 0), true, nullptr);
+}
+
+void QuartcStream::SetDelegate(Delegate* delegate) {
+ if (delegate_) {
+ LOG(WARNING) << "The delegate for Stream " << id()
+ << " has already been set.";
+ }
+ delegate_ = delegate;
+ DCHECK(delegate_);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.h
new file mode 100644
index 00000000000..bf15a684732
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.h
@@ -0,0 +1,142 @@
+// 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_QUARTC_QUARTC_STREAM_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_STREAM_H_
+
+#include <stddef.h>
+#include <limits>
+
+#include "net/third_party/quiche/src/quic/core/quic_ack_listener_interface.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/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h"
+#include "net/quic/platform/impl/quic_export_impl.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_interval_counter.h"
+
+namespace quic {
+
+// Sends and receives data with a particular QUIC stream ID, reliably and
+// in-order. To send/receive data out of order, use separate streams. To
+// send/receive unreliably, close a stream after reliability is no longer
+// needed.
+class QuartcStream : public QuicStream {
+ public:
+ QuartcStream(QuicStreamId id, QuicSession* session);
+ explicit QuartcStream(PendingStream pending);
+
+ ~QuartcStream() override;
+
+ // QuicStream overrides.
+ void OnDataAvailable() override;
+
+ void OnClose() override;
+
+ void OnStreamDataConsumed(size_t bytes_consumed) override;
+
+ void OnDataBuffered(
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ const QuicReferenceCountedPointer<QuicAckListenerInterface>& ack_listener)
+ override;
+
+ bool OnStreamFrameAcked(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_acked,
+ QuicTime::Delta ack_delay_time,
+ QuicByteCount* newly_acked_length) override;
+
+ void OnStreamFrameRetransmitted(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_retransmitted) override;
+
+ void OnStreamFrameLost(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_lost) override;
+
+ void OnCanWrite() override;
+
+ // QuartcStream interface methods.
+
+ // Whether the stream should be cancelled instead of retransmitted on loss.
+ // If set to true, the stream will reset itself instead of retransmitting lost
+ // stream frames. Defaults to false. Setting it to true is equivalent to
+ // setting |max_retransmission_count| to zero.
+ bool cancel_on_loss();
+ void set_cancel_on_loss(bool cancel_on_loss);
+
+ // Maximum number of times this stream's data may be retransmitted. Each byte
+ // of stream data may be retransmitted this many times. If any byte (or range
+ // of bytes) is lost and would be retransmitted more than this number of
+ // times, the stream resets itself instead of retransmitting the data again.
+ // Setting this value to zero disables retransmissions.
+ //
+ // Note that this limit applies only to stream data, not to the FIN bit. If
+ // only the FIN bit needs to be retransmitted, there is no benefit to
+ // cancelling the stream and sending a reset frame instead.
+ int max_retransmission_count() const;
+ void set_max_retransmission_count(int max_retransmission_count);
+
+ QuicByteCount BytesPendingRetransmission();
+
+ // Returns the current read offset for this stream. During a call to
+ // Delegate::OnReceived, this value is the offset of the first byte read.
+ QuicStreamOffset ReadOffset();
+
+ // Marks this stream as finished writing. Asynchronously sends a FIN and
+ // closes the write-side. It is not necessary to call FinishWriting() if the
+ // last call to Write() sends a FIN.
+ void FinishWriting();
+
+ // Implemented by the user of the QuartcStream to receive incoming
+ // data and be notified of state changes.
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ // Called when the stream receives data. |iov| is a pointer to the first of
+ // |iov_length| readable regions. |iov| points to readable data within
+ // |stream|'s sequencer buffer. QUIC may modify or delete this data after
+ // the application consumes it. |fin| indicates the end of stream data.
+ // Returns the number of bytes consumed. May return 0 if the delegate is
+ // unable to consume any bytes at this time.
+ virtual size_t OnReceived(QuartcStream* stream,
+ iovec* iov,
+ size_t iov_length,
+ bool fin) = 0;
+
+ // Called when the stream is closed, either locally or by the remote
+ // endpoint. Streams close when (a) fin bits are both sent and received,
+ // (b) Close() is called, or (c) the stream is reset.
+ // TODO(zhihuang) Creates a map from the integer error_code to WebRTC native
+ // error code.
+ virtual void OnClose(QuartcStream* stream) = 0;
+
+ // Called when the contents of the stream's buffer changes.
+ virtual void OnBufferChanged(QuartcStream* stream) = 0;
+ };
+
+ // The |delegate| is not owned by QuartcStream.
+ void SetDelegate(Delegate* delegate);
+
+ private:
+ Delegate* delegate_ = nullptr;
+
+ // Maximum number of times this stream's data may be retransmitted.
+ int max_retransmission_count_ = std::numeric_limits<int>::max();
+
+ // Counter which tracks the number of times each frame has been lost
+ // (accounting for the possibility of overlapping frames).
+ //
+ // If the maximum count of any lost frame exceeds |max_retransmission_count_|,
+ // the stream will cancel itself on the next attempt to retransmit data (the
+ // next call to |OnCanWrite|).
+ QuartcIntervalCounter<QuicStreamOffset> lost_frame_counter_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_QUARTC_STREAM_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream_test.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream_test.cc
new file mode 100644
index 00000000000..3b5458c7870
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream_test.cc
@@ -0,0 +1,640 @@
+// 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/quartc/quartc_stream.h"
+
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.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_ip_address.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_test_mem_slice_vector.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_factory.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
+
+namespace quic {
+
+namespace {
+
+static const QuicStreamId kStreamId = 5;
+
+// MockQuicSession that does not create streams and writes data from
+// QuicStream to a string.
+class MockQuicSession : public QuicSession {
+ public:
+ MockQuicSession(QuicConnection* connection,
+ const QuicConfig& config,
+ std::string* write_buffer)
+ : QuicSession(connection,
+ nullptr /*visitor*/,
+ config,
+ CurrentSupportedVersions()),
+ write_buffer_(write_buffer) {}
+
+ ~MockQuicSession() override {}
+
+ // Writes outgoing data from QuicStream to a string.
+ QuicConsumedData WritevData(QuicStream* stream,
+ QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state) override {
+ if (!writable_) {
+ return QuicConsumedData(0, false);
+ }
+
+ // WritevData does not pass down a iovec, data is saved in stream before
+ // data is consumed. Retrieve data from stream.
+ char* buf = new char[write_length];
+ QuicDataWriter writer(write_length, buf, NETWORK_BYTE_ORDER);
+ if (write_length > 0) {
+ stream->WriteStreamData(offset, write_length, &writer);
+ }
+ write_buffer_->append(buf, write_length);
+ delete[] buf;
+ return QuicConsumedData(write_length, state != StreamSendingState::NO_FIN);
+ }
+
+ QuartcStream* CreateIncomingStream(QuicStreamId id) override {
+ return nullptr;
+ }
+
+ QuartcStream* CreateIncomingStream(PendingStream pending) override {
+ return nullptr;
+ }
+
+ const QuicCryptoStream* GetCryptoStream() const override { return nullptr; }
+ QuicCryptoStream* GetMutableCryptoStream() override { return nullptr; }
+ bool ShouldKeepConnectionAlive() const override {
+ return GetNumOpenDynamicStreams() > 0;
+ }
+
+ // Called by QuicStream when they want to close stream.
+ void SendRstStream(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written) override {}
+
+ // Sets whether data is written to buffer, or else if this is write blocked.
+ void set_writable(bool writable) { writable_ = writable; }
+
+ // Tracks whether the stream is write blocked and its priority.
+ void RegisterReliableStream(QuicStreamId stream_id,
+ spdy::SpdyPriority priority) {
+ write_blocked_streams()->RegisterStream(stream_id,
+ /*is_static_stream=*/false,
+ priority);
+ }
+
+ // The session take ownership of the stream.
+ void ActivateReliableStream(std::unique_ptr<QuicStream> stream) {
+ ActivateStream(std::move(stream));
+ }
+
+ private:
+ // Stores written data from ReliableQuicStreamAdapter.
+ std::string* write_buffer_;
+ // Whether data is written to write_buffer_.
+ bool writable_ = true;
+};
+
+// Packet writer that does nothing. This is required for QuicConnection but
+// isn't used for writing data.
+class DummyPacketWriter : public QuicPacketWriter {
+ public:
+ DummyPacketWriter() {}
+
+ // QuicPacketWriter overrides.
+ WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) override {
+ return WriteResult(WRITE_STATUS_ERROR, 0);
+ }
+
+ bool IsWriteBlocked() const override { return false; }
+
+ void SetWritable() override {}
+
+ QuicByteCount GetMaxPacketSize(
+ const QuicSocketAddress& peer_address) const override {
+ return 0;
+ }
+
+ bool SupportsReleaseTime() const override { return false; }
+
+ bool IsBatchMode() const override { return false; }
+
+ char* GetNextWriteLocation(const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address) override {
+ return nullptr;
+ }
+
+ WriteResult Flush() override { return WriteResult(WRITE_STATUS_OK, 0); }
+};
+
+class MockQuartcStreamDelegate : public QuartcStream::Delegate {
+ public:
+ MockQuartcStreamDelegate(QuicStreamId id, std::string* read_buffer)
+ : id_(id), read_buffer_(read_buffer) {}
+
+ void OnBufferChanged(QuartcStream* stream) override {
+ last_bytes_buffered_ = stream->BufferedDataBytes();
+ last_bytes_pending_retransmission_ = stream->BytesPendingRetransmission();
+ }
+
+ size_t OnReceived(QuartcStream* stream,
+ iovec* iov,
+ size_t iov_length,
+ bool fin) override {
+ EXPECT_EQ(id_, stream->id());
+ EXPECT_EQ(stream->ReadOffset(), read_buffer_->size());
+ size_t bytes_consumed = 0;
+ for (size_t i = 0; i < iov_length; ++i) {
+ read_buffer_->append(static_cast<const char*>(iov[i].iov_base),
+ iov[i].iov_len);
+ bytes_consumed += iov[i].iov_len;
+ }
+ return bytes_consumed;
+ }
+
+ void OnClose(QuartcStream* stream) override { closed_ = true; }
+
+ bool closed() { return closed_; }
+
+ QuicByteCount last_bytes_buffered() { return last_bytes_buffered_; }
+ QuicByteCount last_bytes_pending_retransmission() {
+ return last_bytes_pending_retransmission_;
+ }
+
+ protected:
+ QuicStreamId id_;
+ // Data read by the QuicStream.
+ std::string* read_buffer_;
+ // Whether the QuicStream is closed.
+ bool closed_ = false;
+
+ // Last amount of data observed as buffered.
+ QuicByteCount last_bytes_buffered_ = 0;
+ QuicByteCount last_bytes_pending_retransmission_ = 0;
+};
+
+class QuartcStreamTest : public QuicTest, public QuicConnectionHelperInterface {
+ public:
+ QuartcStreamTest() {
+ // Required to correctly handle StopReading().
+ SetQuicReloadableFlag(quic_stop_reading_when_level_triggered, true);
+ }
+
+ ~QuartcStreamTest() override = default;
+
+ void CreateReliableQuicStream() {
+ // Arbitrary values for QuicConnection.
+ Perspective perspective = Perspective::IS_SERVER;
+ QuicIpAddress ip;
+ ip.FromString("0.0.0.0");
+ bool owns_writer = true;
+
+ alarm_factory_ = QuicMakeUnique<test::MockAlarmFactory>();
+
+ connection_ = QuicMakeUnique<QuicConnection>(
+ QuicUtils::CreateZeroConnectionId(
+ CurrentSupportedVersions()[0].transport_version),
+ QuicSocketAddress(ip, 0), this /*QuicConnectionHelperInterface*/,
+ alarm_factory_.get(), new DummyPacketWriter(), owns_writer, perspective,
+ ParsedVersionOfIndex(CurrentSupportedVersions(), 0));
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ session_ = QuicMakeUnique<MockQuicSession>(connection_.get(), QuicConfig(),
+ &write_buffer_);
+ mock_stream_delegate_ =
+ QuicMakeUnique<MockQuartcStreamDelegate>(kStreamId, &read_buffer_);
+ stream_ = new QuartcStream(kStreamId, session_.get());
+ stream_->SetDelegate(mock_stream_delegate_.get());
+ session_->ActivateReliableStream(std::unique_ptr<QuartcStream>(stream_));
+ }
+
+ const QuicClock* GetClock() const override { return &clock_; }
+
+ QuicRandom* GetRandomGenerator() override {
+ return QuicRandom::GetInstance();
+ }
+
+ QuicBufferAllocator* GetStreamSendBufferAllocator() override {
+ return &buffer_allocator_;
+ }
+
+ protected:
+ // The QuicSession will take the ownership.
+ QuartcStream* stream_;
+ std::unique_ptr<MockQuartcStreamDelegate> mock_stream_delegate_;
+ std::unique_ptr<MockQuicSession> session_;
+ // Data written by the ReliableQuicStreamAdapterTest.
+ std::string write_buffer_;
+ // Data read by the ReliableQuicStreamAdapterTest.
+ std::string read_buffer_;
+ std::unique_ptr<QuicAlarmFactory> alarm_factory_;
+ std::unique_ptr<QuicConnection> connection_;
+ // Used to implement the QuicConnectionHelperInterface.
+ SimpleBufferAllocator buffer_allocator_;
+ MockClock clock_;
+};
+
+// Write an entire string.
+TEST_F(QuartcStreamTest, WriteDataWhole) {
+ CreateReliableQuicStream();
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+ EXPECT_EQ("Foo bar", write_buffer_);
+}
+
+// Write part of a string.
+TEST_F(QuartcStreamTest, WriteDataPartial) {
+ CreateReliableQuicStream();
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 5)});
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+ EXPECT_EQ("Foo b", write_buffer_);
+}
+
+// Test that a QuartcStream buffers writes correctly.
+TEST_F(QuartcStreamTest, StreamBuffersData) {
+ CreateReliableQuicStream();
+
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+
+ // The stream is not yet writable, so data will be buffered.
+ session_->set_writable(false);
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+
+ // Check that data is buffered.
+ EXPECT_TRUE(stream_->HasBufferedData());
+ EXPECT_EQ(7u, stream_->BufferedDataBytes());
+
+ // Check that the stream told its delegate about the buffer change.
+ EXPECT_EQ(7u, mock_stream_delegate_->last_bytes_buffered());
+
+ // Check that none of the data was written yet.
+ // Note that |write_buffer_| actually holds data written by the QuicSession
+ // (not data buffered by the stream).
+ EXPECT_EQ(0ul, write_buffer_.size());
+
+ char message1[] = "xyzzy";
+ test::QuicTestMemSliceVector data1({std::make_pair(message1, 5)});
+
+ // More writes go into the buffer.
+ stream_->WriteMemSlices(data1.span(), /*fin=*/false);
+
+ EXPECT_TRUE(stream_->HasBufferedData());
+ EXPECT_EQ(12u, stream_->BufferedDataBytes());
+ EXPECT_EQ(12u, mock_stream_delegate_->last_bytes_buffered());
+ EXPECT_EQ(0ul, write_buffer_.size());
+
+ // The stream becomes writable, so it sends the buffered data.
+ session_->set_writable(true);
+ stream_->OnCanWrite();
+
+ EXPECT_FALSE(stream_->HasBufferedData());
+ EXPECT_EQ(0u, stream_->BufferedDataBytes());
+ EXPECT_EQ(0u, mock_stream_delegate_->last_bytes_buffered());
+ EXPECT_EQ("Foo barxyzzy", write_buffer_);
+}
+
+// Finish writing to a stream.
+// It delivers the fin bit and closes the write-side as soon as possible.
+TEST_F(QuartcStreamTest, FinishWriting) {
+ CreateReliableQuicStream();
+
+ session_->set_writable(false);
+ stream_->FinishWriting();
+ EXPECT_FALSE(stream_->fin_sent());
+
+ // Fin is sent as soon as the stream becomes writable.
+ session_->set_writable(true);
+ stream_->OnCanWrite();
+ EXPECT_TRUE(stream_->fin_sent());
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+// Read an entire string.
+TEST_F(QuartcStreamTest, ReadDataWhole) {
+ CreateReliableQuicStream();
+ QuicStreamFrame frame(kStreamId, false, 0, "Hello, World!");
+ stream_->OnStreamFrame(frame);
+
+ EXPECT_EQ("Hello, World!", read_buffer_);
+}
+
+// Read part of a string.
+TEST_F(QuartcStreamTest, ReadDataPartial) {
+ CreateReliableQuicStream();
+ QuicStreamFrame frame(kStreamId, false, 0, "Hello, World!");
+ frame.data_length = 5;
+ stream_->OnStreamFrame(frame);
+
+ EXPECT_EQ("Hello", read_buffer_);
+}
+
+// Streams do not call OnReceived() after StopReading().
+// Note: this is tested here because Quartc relies on this behavior.
+TEST_F(QuartcStreamTest, StopReading) {
+ CreateReliableQuicStream();
+ stream_->StopReading();
+
+ QuicStreamFrame frame(kStreamId, false, 0, "Hello, World!");
+ stream_->OnStreamFrame(frame);
+
+ EXPECT_EQ(0ul, read_buffer_.size());
+
+ QuicStreamFrame frame2(kStreamId, true, 0, "Hello, World!");
+ stream_->OnStreamFrame(frame2);
+
+ EXPECT_EQ(0ul, read_buffer_.size());
+ EXPECT_TRUE(stream_->fin_received());
+}
+
+// Test that closing the stream results in a callback.
+TEST_F(QuartcStreamTest, CloseStream) {
+ CreateReliableQuicStream();
+ EXPECT_FALSE(mock_stream_delegate_->closed());
+ stream_->OnClose();
+ EXPECT_TRUE(mock_stream_delegate_->closed());
+}
+
+// Both sending and receiving fin automatically closes a stream.
+TEST_F(QuartcStreamTest, CloseOnFins) {
+ CreateReliableQuicStream();
+ QuicStreamFrame frame(kStreamId, true, 0, 0);
+ stream_->OnStreamFrame(frame);
+
+ test::QuicTestMemSliceVector data({});
+ stream_->WriteMemSlices(data.span(), /*fin=*/true);
+
+ // Check that the OnClose() callback occurred.
+ EXPECT_TRUE(mock_stream_delegate_->closed());
+}
+
+TEST_F(QuartcStreamTest, TestCancelOnLossDisabled) {
+ CreateReliableQuicStream();
+
+ // This should be the default state.
+ EXPECT_FALSE(stream_->cancel_on_loss());
+
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+
+ EXPECT_EQ("Foo bar", write_buffer_);
+
+ stream_->OnStreamFrameLost(0, 7, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo bar", write_buffer_);
+ EXPECT_EQ(stream_->stream_error(), QUIC_STREAM_NO_ERROR);
+}
+
+TEST_F(QuartcStreamTest, TestCancelOnLossEnabled) {
+ CreateReliableQuicStream();
+ stream_->set_cancel_on_loss(true);
+
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+
+ EXPECT_EQ("Foo bar", write_buffer_);
+
+ stream_->OnStreamFrameLost(0, 7, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo bar", write_buffer_);
+ EXPECT_EQ(stream_->stream_error(), QUIC_STREAM_CANCELLED);
+}
+
+TEST_F(QuartcStreamTest, MaxRetransmissionsAbsent) {
+ CreateReliableQuicStream();
+
+ // This should be the default state.
+ EXPECT_EQ(stream_->max_retransmission_count(),
+ std::numeric_limits<int>::max());
+
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+
+ EXPECT_EQ("Foo bar", write_buffer_);
+
+ stream_->OnStreamFrameLost(0, 7, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo bar", write_buffer_);
+ EXPECT_EQ(stream_->stream_error(), QUIC_STREAM_NO_ERROR);
+}
+
+TEST_F(QuartcStreamTest, MaxRetransmissionsSet) {
+ CreateReliableQuicStream();
+ stream_->set_max_retransmission_count(2);
+
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+
+ EXPECT_EQ("Foo bar", write_buffer_);
+
+ stream_->OnStreamFrameLost(0, 7, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo bar", write_buffer_);
+
+ stream_->OnStreamFrameLost(0, 7, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo barFoo bar", write_buffer_);
+
+ stream_->OnStreamFrameLost(0, 7, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo barFoo bar", write_buffer_);
+ EXPECT_EQ(stream_->stream_error(), QUIC_STREAM_CANCELLED);
+}
+
+TEST_F(QuartcStreamTest, MaxRetransmissionsDisjointFrames) {
+ CreateReliableQuicStream();
+ stream_->set_max_retransmission_count(2);
+
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+
+ EXPECT_EQ("Foo bar", write_buffer_);
+
+ // Retransmit bytes [0, 3].
+ stream_->OnStreamFrameLost(0, 4, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo ", write_buffer_);
+
+ // Retransmit bytes [4, 6]. Everything has been retransmitted once.
+ stream_->OnStreamFrameLost(4, 3, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo bar", write_buffer_);
+
+ // Retransmit bytes [0, 6]. Everything can be retransmitted a second time.
+ stream_->OnStreamFrameLost(0, 7, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo barFoo bar", write_buffer_);
+}
+
+TEST_F(QuartcStreamTest, MaxRetransmissionsOverlappingFrames) {
+ CreateReliableQuicStream();
+ stream_->set_max_retransmission_count(2);
+
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+
+ EXPECT_EQ("Foo bar", write_buffer_);
+
+ // Retransmit bytes 0 to 3.
+ stream_->OnStreamFrameLost(0, 4, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo ", write_buffer_);
+
+ // Retransmit bytes 3 to 6. Byte 3 has been retransmitted twice.
+ stream_->OnStreamFrameLost(3, 4, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo bar", write_buffer_);
+
+ // Retransmit byte 3 a third time. This should cause cancellation.
+ stream_->OnStreamFrameLost(3, 1, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo bar", write_buffer_);
+ EXPECT_EQ(stream_->stream_error(), QUIC_STREAM_CANCELLED);
+}
+
+TEST_F(QuartcStreamTest, MaxRetransmissionsWithAckedFrame) {
+ CreateReliableQuicStream();
+ stream_->set_max_retransmission_count(1);
+
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+
+ EXPECT_EQ("Foo bar", write_buffer_);
+
+ // Retransmit bytes [0, 7).
+ stream_->OnStreamFrameLost(0, 7, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo bar", write_buffer_);
+
+ // Ack bytes [0, 7). These bytes should be pruned from the data tracked by
+ // the stream.
+ QuicByteCount newly_acked_length = 0;
+ stream_->OnStreamFrameAcked(0, 7, false, QuicTime::Delta::FromMilliseconds(1),
+ &newly_acked_length);
+ EXPECT_EQ(7u, newly_acked_length);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo bar", write_buffer_);
+
+ // Retransmit bytes [0, 7) again.
+ // QUIC will never mark frames as lost after they've been acked, but this lets
+ // us test that QuartcStream stopped tracking these bytes after the acked.
+ stream_->OnStreamFrameLost(0, 7, false);
+ stream_->OnCanWrite();
+
+ // QuartcStream should be cancelled, but it stopped tracking the lost bytes
+ // after they were acked, so it's not.
+ EXPECT_EQ(stream_->stream_error(), QUIC_STREAM_NO_ERROR);
+}
+
+TEST_F(QuartcStreamTest, TestBytesPendingRetransmission) {
+ CreateReliableQuicStream();
+ stream_->set_cancel_on_loss(false);
+
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+
+ EXPECT_EQ("Foo bar", write_buffer_);
+
+ stream_->OnStreamFrameLost(0, 4, false);
+ EXPECT_EQ(stream_->BytesPendingRetransmission(), 4u);
+ EXPECT_EQ(mock_stream_delegate_->last_bytes_pending_retransmission(), 4u);
+
+ stream_->OnStreamFrameLost(4, 3, false);
+ EXPECT_EQ(stream_->BytesPendingRetransmission(), 7u);
+ EXPECT_EQ(mock_stream_delegate_->last_bytes_pending_retransmission(), 7u);
+
+ stream_->OnCanWrite();
+ EXPECT_EQ(stream_->BytesPendingRetransmission(), 0u);
+ EXPECT_EQ(mock_stream_delegate_->last_bytes_pending_retransmission(), 0u);
+
+ EXPECT_EQ("Foo barFoo bar", write_buffer_);
+ EXPECT_EQ(stream_->stream_error(), QUIC_STREAM_NO_ERROR);
+}
+
+TEST_F(QuartcStreamTest, TestBytesPendingRetransmissionWithCancelOnLoss) {
+ CreateReliableQuicStream();
+ stream_->set_cancel_on_loss(true);
+
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+
+ EXPECT_EQ("Foo bar", write_buffer_);
+
+ stream_->OnStreamFrameLost(0, 4, false);
+ EXPECT_EQ(stream_->BytesPendingRetransmission(), 0u);
+ EXPECT_EQ(mock_stream_delegate_->last_bytes_pending_retransmission(), 0u);
+
+ stream_->OnStreamFrameLost(4, 3, false);
+ EXPECT_EQ(stream_->BytesPendingRetransmission(), 0u);
+ EXPECT_EQ(mock_stream_delegate_->last_bytes_pending_retransmission(), 0u);
+
+ stream_->OnCanWrite();
+ EXPECT_EQ(stream_->BytesPendingRetransmission(), 0u);
+ EXPECT_EQ(mock_stream_delegate_->last_bytes_pending_retransmission(), 0u);
+
+ EXPECT_EQ("Foo bar", write_buffer_);
+ EXPECT_EQ(stream_->stream_error(), QUIC_STREAM_CANCELLED);
+}
+
+} // namespace
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport.cc b/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport.cc
new file mode 100644
index 00000000000..8e82d98b62d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport.cc
@@ -0,0 +1,95 @@
+// 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/quartc/simulated_packet_transport.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+
+namespace quic {
+namespace simulator {
+
+SimulatedQuartcPacketTransport::SimulatedQuartcPacketTransport(
+ Simulator* simulator,
+ const std::string& name,
+ const std::string& peer_name,
+ QuicByteCount queue_capacity)
+ : Endpoint(simulator, name),
+ peer_name_(peer_name),
+ egress_queue_(simulator,
+ QuicStringPrintf("%s (TX Queue)", name.c_str()),
+ queue_capacity) {
+ egress_queue_.set_listener_interface(this);
+}
+
+int SimulatedQuartcPacketTransport::Write(const char* buffer,
+ size_t buf_len,
+ const PacketInfo& info) {
+ if (!writable_) {
+ return 0;
+ }
+ if (egress_queue_.bytes_queued() + buf_len > egress_queue_.capacity()) {
+ return 0;
+ }
+
+ last_packet_number_ = info.packet_number;
+
+ auto packet = QuicMakeUnique<Packet>();
+ packet->contents = std::string(buffer, buf_len);
+ packet->size = buf_len;
+ packet->tx_timestamp = clock_->Now();
+ packet->source = name();
+ packet->destination = peer_name_;
+
+ egress_queue_.AcceptPacket(std::move(packet));
+ return buf_len;
+}
+
+void SimulatedQuartcPacketTransport::SetDelegate(Delegate* delegate) {
+ delegate_ = delegate;
+ Schedule(clock_->Now());
+}
+
+UnconstrainedPortInterface* SimulatedQuartcPacketTransport::GetRxPort() {
+ return this;
+}
+
+void SimulatedQuartcPacketTransport::SetTxPort(ConstrainedPortInterface* port) {
+ egress_queue_.set_tx_port(port);
+ Schedule(clock_->Now());
+}
+
+void SimulatedQuartcPacketTransport::AcceptPacket(
+ std::unique_ptr<Packet> packet) {
+ // Simulated switches broadcast packets to all ports if the cannot determine
+ // the recipient, so we need to drop packets that aren't intended for us.
+ if (packet->destination != name()) {
+ return;
+ }
+
+ if (delegate_) {
+ delegate_->OnTransportReceived(packet->contents.data(), packet->size);
+ }
+}
+
+void SimulatedQuartcPacketTransport::OnPacketDequeued() {
+ if (delegate_ && writable_) {
+ delegate_->OnTransportCanWrite();
+ }
+}
+
+void SimulatedQuartcPacketTransport::Act() {
+ if (delegate_ && writable_) {
+ delegate_->OnTransportCanWrite();
+ }
+}
+
+void SimulatedQuartcPacketTransport::SetWritable(bool writable) {
+ writable_ = writable;
+ if (writable_) {
+ // May need to call |Delegate::OnTransportCanWrite|.
+ Schedule(clock_->Now());
+ }
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h b/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h
new file mode 100644
index 00000000000..185668b528a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h
@@ -0,0 +1,84 @@
+// 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_QUARTC_SIMULATED_PACKET_TRANSPORT_H_
+#define QUICHE_QUIC_QUARTC_SIMULATED_PACKET_TRANSPORT_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/port.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/queue.h"
+
+namespace quic {
+namespace simulator {
+
+// Simulated implementation of QuartcPacketTransport. This packet transport
+// implementation connects Quartc to a QUIC simulator's network fabric.
+// Assumes that its caller and delegate run on the same thread as the network
+// simulation and therefore require no additional synchronization.
+class SimulatedQuartcPacketTransport : public Endpoint,
+ public QuartcPacketTransport,
+ public UnconstrainedPortInterface,
+ public Queue::ListenerInterface {
+ public:
+ SimulatedQuartcPacketTransport(Simulator* simulator,
+ const std::string& name,
+ const std::string& peer_name,
+ QuicByteCount queue_capacity);
+
+ // QuartcPacketTransport methods.
+ int Write(const char* buffer,
+ size_t buf_len,
+ const PacketInfo& info) override;
+ void SetDelegate(Delegate* delegate) override;
+
+ // Simulation methods below. These are implementation details.
+
+ // Endpoint methods. Called by the simulation to connect the transport.
+ UnconstrainedPortInterface* GetRxPort() override;
+ void SetTxPort(ConstrainedPortInterface* port) override;
+
+ // UnconstrainedPortInterface method. Called by the simulation to deliver a
+ // packet to this transport.
+ void AcceptPacket(std::unique_ptr<Packet> packet) override;
+
+ // Queue::ListenerInterface method. Called when the internal egress queue has
+ // dispatched a packet and may have room for more.
+ void OnPacketDequeued() override;
+
+ // Actor method. The transport schedules this to run when the delegate is set
+ // in order to trigger an initial call to |Delegate::OnTransportCanWrite()|.
+ // (The Quartc packet writer starts in a blocked state and needs an initial
+ // callback to unblock it.)
+ void Act() override;
+
+ // Changes whether the transport is writable. If |writable| is false, the
+ // transport will reject calls to |Write| and will not call
+ // |Delegate::OnTransportCanWrite|. If |writable| is true, the transport will
+ // allow calls to |Write| and will call |Delegate::OnTransportCanWrite|
+ // whenever it is able to write another packet.
+ void SetWritable(bool writable);
+
+ // Last packet number sent over this simulated transport.
+ // TODO(b/112561077): Reorganize tests so that this method can be deleted.
+ // This exists purely for use by quartc_session_test.cc, to test that the
+ // packet writer passes packet numbers to the transport.
+ QuicPacketNumber last_packet_number() { return last_packet_number_; }
+
+ private:
+ std::string peer_name_;
+ Delegate* delegate_ = nullptr;
+ Queue egress_queue_;
+ QuicPacketNumber last_packet_number_;
+
+ // Controls whether the transport is considered to be writable. Used to
+ // simulate behavior that arises when the transport is blocked.
+ bool writable_ = true;
+};
+
+} // namespace simulator
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_SIMULATED_PACKET_TRANSPORT_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport_test.cc b/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport_test.cc
new file mode 100644
index 00000000000..cae966f4a3e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport_test.cc
@@ -0,0 +1,165 @@
+// 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/quartc/simulated_packet_transport.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_packet_writer.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"
+
+namespace quic {
+namespace simulator {
+namespace {
+
+using ::testing::ElementsAre;
+
+const QuicBandwidth kDefaultBandwidth =
+ QuicBandwidth::FromKBitsPerSecond(10 * 1000);
+const QuicTime::Delta kDefaultPropagationDelay =
+ QuicTime::Delta::FromMilliseconds(20);
+const QuicByteCount kDefaultBdp = kDefaultBandwidth * kDefaultPropagationDelay;
+const QuicByteCount kDefaultPacketSize = 1200;
+const QuicPacketCount kDefaultQueueLength = 10;
+
+class FakeDelegate : public QuartcPacketTransport::Delegate {
+ public:
+ explicit FakeDelegate(QuartcPacketTransport* transport)
+ : transport_(transport) {
+ transport_->SetDelegate(this);
+ }
+
+ ~FakeDelegate() { transport_->SetDelegate(nullptr); }
+
+ void OnTransportCanWrite() override {
+ while (!packets_to_send_.empty()) {
+ const std::string& packet = packets_to_send_.front();
+ if (transport_->Write(packet.data(), packet.size(),
+ QuartcPacketTransport::PacketInfo()) <
+ static_cast<int>(packet.size())) {
+ ++write_blocked_count_;
+ return;
+ }
+ packets_to_send_.pop();
+ }
+ }
+
+ void OnTransportReceived(const char* data, size_t data_len) override {
+ packets_received_.emplace_back(data, data_len);
+ }
+
+ void AddPacketToSend(const std::string& packet) {
+ packets_to_send_.push(packet);
+ }
+
+ size_t packets_to_send() { return packets_to_send_.size(); }
+ const std::vector<std::string>& packets_received() {
+ return packets_received_;
+ }
+ int write_blocked_count() { return write_blocked_count_; }
+
+ private:
+ QuartcPacketTransport* const transport_ = nullptr;
+ std::queue<std::string> packets_to_send_;
+ std::vector<std::string> packets_received_;
+ int write_blocked_count_ = 0;
+};
+
+class SimulatedPacketTransportTest : public QuicTest {
+ protected:
+ SimulatedPacketTransportTest()
+ : switch_(&simulator_, "Switch", /*port_count=*/8, 2 * kDefaultBdp),
+ client_(&simulator_,
+ "sender",
+ "receiver",
+ kDefaultQueueLength * kDefaultPacketSize),
+ server_(&simulator_,
+ "receiver",
+ "sender",
+ kDefaultQueueLength * kDefaultPacketSize),
+ client_link_(&client_,
+ switch_.port(1),
+ kDefaultBandwidth,
+ kDefaultPropagationDelay),
+ server_link_(&server_,
+ switch_.port(2),
+ kDefaultBandwidth,
+ kDefaultPropagationDelay),
+ client_delegate_(&client_),
+ server_delegate_(&server_) {}
+
+ Simulator simulator_;
+ Switch switch_;
+
+ SimulatedQuartcPacketTransport client_;
+ SimulatedQuartcPacketTransport server_;
+
+ SymmetricLink client_link_;
+ SymmetricLink server_link_;
+
+ FakeDelegate client_delegate_;
+ FakeDelegate server_delegate_;
+};
+
+TEST_F(SimulatedPacketTransportTest, OneWayTransmission) {
+ std::string packet_1(kDefaultPacketSize, 'a');
+ std::string packet_2(kDefaultPacketSize, 'b');
+ client_delegate_.AddPacketToSend(packet_1);
+ client_delegate_.AddPacketToSend(packet_2);
+
+ simulator_.RunUntil(
+ [this] { return client_delegate_.packets_to_send() == 0; });
+ simulator_.RunFor(3 * kDefaultPropagationDelay);
+
+ EXPECT_THAT(server_delegate_.packets_received(),
+ ElementsAre(packet_1, packet_2));
+ EXPECT_THAT(client_delegate_.packets_received(), ElementsAre());
+}
+
+TEST_F(SimulatedPacketTransportTest, TwoWayTransmission) {
+ std::string packet_1(kDefaultPacketSize, 'a');
+ std::string packet_2(kDefaultPacketSize, 'b');
+ std::string packet_3(kDefaultPacketSize, 'c');
+ std::string packet_4(kDefaultPacketSize, 'd');
+
+ client_delegate_.AddPacketToSend(packet_1);
+ client_delegate_.AddPacketToSend(packet_2);
+ server_delegate_.AddPacketToSend(packet_3);
+ server_delegate_.AddPacketToSend(packet_4);
+
+ simulator_.RunUntil(
+ [this] { return client_delegate_.packets_to_send() == 0; });
+ simulator_.RunUntil(
+ [this] { return server_delegate_.packets_to_send() == 0; });
+ simulator_.RunFor(3 * kDefaultPropagationDelay);
+
+ EXPECT_THAT(server_delegate_.packets_received(),
+ ElementsAre(packet_1, packet_2));
+ EXPECT_THAT(client_delegate_.packets_received(),
+ ElementsAre(packet_3, packet_4));
+}
+
+TEST_F(SimulatedPacketTransportTest, TestWriteBlocked) {
+ // Add 10 packets beyond what fits in the egress queue.
+ std::vector<std::string> packets;
+ for (unsigned int i = 0; i < kDefaultQueueLength + 10; ++i) {
+ packets.push_back(std::string(kDefaultPacketSize, 'a' + i));
+ client_delegate_.AddPacketToSend(packets.back());
+ }
+
+ simulator_.RunUntil(
+ [this] { return client_delegate_.packets_to_send() == 0; });
+ simulator_.RunFor(3 * kDefaultPropagationDelay);
+
+ // Each of the 10 packets in excess of the sender's egress queue length will
+ // block the first time |client_delegate_| tries to write them.
+ EXPECT_EQ(client_delegate_.write_blocked_count(), 10);
+ EXPECT_EQ(server_delegate_.packets_received(), packets);
+}
+
+} // namespace
+} // namespace simulator
+} // namespace quic