Commit b96caf16 authored by Seth Hampson's avatar Seth Hampson Committed by Commit Bot

QUIC core library shim for RTCQuicTransport.

The RTCQuicTransport web API needs to use the underlying core QUIC
library. In order to use this API we need to create a shim of the
library that subclasses the QuicSession and QuicStream. This change
subclasses the QuicSession and has some basic implementation for
establishing a connection.

Bug: 874296
Change-Id: I63acd4913abc23733521157bd1aca4a3d1301c65
Reviewed-on: https://chromium-review.googlesource.com/1170125Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Reviewed-by: default avatarHenrik Boström <hbos@chromium.org>
Commit-Queue: Seth Hampson <shampson@chromium.org>
Cr-Commit-Position: refs/heads/master@{#594845}
parent 3225e1d5
...@@ -83,6 +83,7 @@ target("jumbo_" + modules_target_type, "modules") { ...@@ -83,6 +83,7 @@ target("jumbo_" + modules_target_type, "modules") {
deps = [ deps = [
":make_modules_generated", ":make_modules_generated",
":module_names", ":module_names",
"//net:net",
"//third_party/blink/renderer/bindings/modules:generated", "//third_party/blink/renderer/bindings/modules:generated",
"//third_party/blink/renderer/bindings/modules/v8:bindings_modules_impl", "//third_party/blink/renderer/bindings/modules/v8:bindings_modules_impl",
"//third_party/blink/renderer/bindings/modules/v8:bindings_modules_origin_trial_features", "//third_party/blink/renderer/bindings/modules/v8:bindings_modules_origin_trial_features",
...@@ -164,6 +165,7 @@ target("jumbo_" + modules_target_type, "modules") { ...@@ -164,6 +165,7 @@ target("jumbo_" + modules_target_type, "modules") {
"//third_party/blink/renderer/modules/xr", "//third_party/blink/renderer/modules/xr",
"//third_party/icu", "//third_party/icu",
"//third_party/webrtc/pc:libjingle_peerconnection", "//third_party/webrtc/pc:libjingle_peerconnection",
"//third_party/webrtc/rtc_base:rtc_base",
"//third_party/webrtc_overrides:init_webrtc", "//third_party/webrtc_overrides:init_webrtc",
"//third_party/zlib", "//third_party/zlib",
] ]
...@@ -297,6 +299,7 @@ jumbo_source_set("unit_tests") { ...@@ -297,6 +299,7 @@ jumbo_source_set("unit_tests") {
"payments/payment_test_helper.cc", "payments/payment_test_helper.cc",
"payments/payment_test_helper.h", "payments/payment_test_helper.h",
"payments/payments_validators_test.cc", "payments/payments_validators_test.cc",
"peerconnection/adapters/p2p_quic_transport_test.cc",
"peerconnection/rtc_data_channel_test.cc", "peerconnection/rtc_data_channel_test.cc",
"peerconnection/rtc_ice_transport_test.cc", "peerconnection/rtc_ice_transport_test.cc",
"peerconnection/rtc_peer_connection_test.cc", "peerconnection/rtc_peer_connection_test.cc",
...@@ -339,6 +342,7 @@ jumbo_source_set("unit_tests") { ...@@ -339,6 +342,7 @@ jumbo_source_set("unit_tests") {
deps = [ deps = [
":modules", ":modules",
":modules_testing", ":modules_testing",
"//net:quic_test_tools",
"//services/device/public/cpp/test:test_support", "//services/device/public/cpp/test:test_support",
"//services/viz/public/interfaces:interfaces_blink", "//services/viz/public/interfaces:interfaces_blink",
"//skia", "//skia",
...@@ -349,6 +353,7 @@ jumbo_source_set("unit_tests") { ...@@ -349,6 +353,7 @@ jumbo_source_set("unit_tests") {
"//third_party/blink/renderer/modules/storage:unit_tests", "//third_party/blink/renderer/modules/storage:unit_tests",
"//third_party/blink/renderer/platform", "//third_party/blink/renderer/platform",
"//third_party/blink/renderer/platform/wtf", "//third_party/blink/renderer/platform/wtf",
"//third_party/webrtc/rtc_base:rtc_base",
"//v8", "//v8",
] ]
} }
...@@ -14,6 +14,13 @@ blink_modules_sources("peerconnection") { ...@@ -14,6 +14,13 @@ blink_modules_sources("peerconnection") {
"adapters/ice_transport_host.h", "adapters/ice_transport_host.h",
"adapters/ice_transport_proxy.cc", "adapters/ice_transport_proxy.cc",
"adapters/ice_transport_proxy.h", "adapters/ice_transport_proxy.h",
"adapters/p2p_quic_packet_transport.h",
"adapters/p2p_quic_transport.h",
"adapters/p2p_quic_transport_factory.h",
"adapters/p2p_quic_transport_factory_impl.cc",
"adapters/p2p_quic_transport_factory_impl.h",
"adapters/p2p_quic_transport_impl.cc",
"adapters/p2p_quic_transport_impl.h",
"adapters/web_rtc_cross_thread_copier.cc", "adapters/web_rtc_cross_thread_copier.cc",
"adapters/web_rtc_cross_thread_copier.h", "adapters/web_rtc_cross_thread_copier.h",
"rtc_certificate.cc", "rtc_certificate.cc",
......
...@@ -7,4 +7,8 @@ include_rules = [ ...@@ -7,4 +7,8 @@ include_rules = [
"+third_party/blink/renderer/modules/mediastream", "+third_party/blink/renderer/modules/mediastream",
"+third_party/blink/renderer/modules/modules_export.h", "+third_party/blink/renderer/modules/modules_export.h",
"+third_party/blink/renderer/modules/peerconnection", "+third_party/blink/renderer/modules/peerconnection",
"+net/third_party/quic",
"+net/quic/chromium",
"+net/quic",
"+net/test",
] ]
// Copyright 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 THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_P2P_QUIC_PACKET_TRANSPORT_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_P2P_QUIC_PACKET_TRANSPORT_H_
#include "net/third_party/quic/core/quic_packet_writer.h"
#include "net/third_party/quic/core/quic_types.h"
#include "net/third_party/quic/platform/api/quic_export.h"
namespace blink {
// This is the interface for the underlying packet transport used by the
// P2PQuicTransport for receiving and writing data. The standard
// implementation of this interface uses an ICE transport.
//
// This object should be run entirely on the webrtc worker thread.
class P2PQuicPacketTransport {
public:
// This is subclassed by the P2PQuicTransport so that it can receive incoming
// data. The standard case is for this to be the P2PQuicTransport.
// The P2PQuicPacketTransport will outlive the ReceiveDelegate.
class ReceiveDelegate {
public:
virtual ~ReceiveDelegate() = default;
virtual void OnPacketDataReceived(const char* data, size_t data_len) = 0;
};
// This is subclassed by the Writer, so that it is aware when the
// P2PQuicPacketTransport is ready to write data. The
// P2PQuicPacketTransport will outlive the WriteObserver.
class WriteObserver {
public:
virtual ~WriteObserver() = default;
virtual void OnCanWrite() = 0;
};
struct QuicPacket {
// This is taken from the quic::QuicConnection, and 0 means that it is not
// set. Packet numbers are used to provide metadata to the implementation of
// the P2PQuicPacketTransport, but this number is not used by the QUIC
// library itself.
quic::QuicPacketNumber packet_number;
const char* buffer;
size_t buf_len;
};
virtual ~P2PQuicPacketTransport() = default;
// Called by the P2PQuicPacketWriter (in quic_transport_factory_impl.cc) when
// writing QUIC packets to the network. Return the number of written bytes.
// Return 0 if the write is blocked.
virtual int WritePacket(const QuicPacket& packet) = 0;
// Sets the ReceiveDelegate for receiving packets.
// Since the ReceiveDelegate has a shorter lifetime than the
// P2PQuicPacketTransport, it must unset itself upon destruction.
virtual void SetReceiveDelegate(ReceiveDelegate* receive_delegate) = 0;
// Sets the WriteObserver for obsererving when it can write to the
// P2PQuicPacketTransport. Since the WriteObserver has a shorter lifetime than
// the P2PQuicPacketTransport, it must unset itself upon destruction.
virtual void SetWriteObserver(WriteObserver* write_observer) = 0;
// Returns true if the P2PQuicPacketTransport can write.
virtual bool Writable() = 0;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_P2P_QUIC_PACKET_TRANSPORT_H_
// Copyright 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 THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_P2P_QUIC_TRANSPORT_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_P2P_QUIC_TRANSPORT_H_
#include "third_party/webrtc/rtc_base/sslfingerprint.h"
namespace blink {
// Used by the RTCQuicTransport Web API. This transport creates and manages
// streams, handles negotiation, state changes and errors. Every
// P2PQuicTransport function maps directly to a method in the RTCQuicTransport
// Web API, i.e. RTCQuicTransport::stop() -->
// P2PQuicTransport::Stop(). This allows posting just one task across
// thread boundaries to execute a function.
//
// This object should be run entirely on the webrtc worker thread.
class P2PQuicTransport {
public:
// Used for receiving callbacks from the P2PQuicTransport regarding QUIC
// connection changes, handshake success/failures and new QuicStreams being
// added from the remote side.
class Delegate {
public:
virtual ~Delegate() = default;
// Called when receiving a close frame from the remote side, due to
// calling P2PQuicTransport::Stop().
virtual void OnRemoteStopped() {}
// Called when the connection is closed due to a QUIC error. This can happen
// locally by the framer, or remotely by the peer.
virtual void OnConnectionFailed(const std::string& error_details,
bool from_remote) {}
// Called when the crypto handshake has completed and fingerprints have been
// verified.
virtual void OnConnected() {}
// TODO(https://crbug.com/874296): Add OnStream once streams are
// implemented.
};
virtual ~P2PQuicTransport() = default;
// Closes the QuicConnection and sends a close frame to the remote side.
// This will trigger P2PQuicTransport::Delegate::OnRemoteClosed() on the
// remote side.
virtual void Stop() = 0;
// Starts the QUIC handshake negotiation and sets the remote fingerprints
// that were signaled through a secure channel. These fingerprints are used to
// verify the self signed remote certificate used in the QUIC handshake. See:
// https://w3c.github.io/webrtc-quic/#quic-transport*
virtual void Start(std::vector<std::unique_ptr<rtc::SSLFingerprint>>
remote_fingerprints) = 0;
// TODO(https://crbug.com/874296): Consider adding a getter for the
// local fingerprints of the certificate(s) set in the constructor.
// TODO(https://crbug.com/874296): Add CreateStream once streams are
// implemented.
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_P2P_QUIC_TRANSPORT_H_
// Copyright 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 THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_P2P_QUIC_TRANSPORT_FACTORY_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_P2P_QUIC_TRANSPORT_FACTORY_H_
#include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_packet_transport.h"
#include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h"
#include "third_party/webrtc/rtc_base/rtccertificate.h"
#include "third_party/webrtc/rtc_base/scoped_ref_ptr.h"
namespace blink {
// A simple config object for creating a P2PQuicTransport. It's constructor
// guarantees that the required objects for creating a P2PQuicTransport are part
// of the P2PQuicTransportConfig.
struct P2PQuicTransportConfig final {
// This object is only moveable.
explicit P2PQuicTransportConfig(
P2PQuicTransport::Delegate* const delegate_in,
P2PQuicPacketTransport* const packet_transport_in,
const std::vector<rtc::scoped_refptr<rtc::RTCCertificate>>
certificates_in)
: packet_transport(packet_transport_in),
certificates(certificates_in),
delegate(delegate_in) {
DCHECK_GT(certificates.size(), 0u);
DCHECK(packet_transport);
DCHECK(delegate);
}
P2PQuicTransportConfig(const P2PQuicTransportConfig&) = delete;
P2PQuicTransportConfig& operator=(const P2PQuicTransportConfig&) = delete;
P2PQuicTransportConfig(P2PQuicTransportConfig&&) = default;
P2PQuicTransportConfig& operator=(P2PQuicTransportConfig&&) = default;
~P2PQuicTransportConfig() = default;
// The standard case is an ICE transport. It's lifetime will be managed by
// the ICE transport objects and outlive the P2PQuicTransport.
P2PQuicPacketTransport* const packet_transport;
bool is_server = true;
// The certificates are owned by the P2PQuicTransport. These come from
// blink::RTCCertificates: https://www.w3.org/TR/webrtc/#dom-rtccertificate
const std::vector<rtc::scoped_refptr<rtc::RTCCertificate>> certificates;
// Mandatory for creating a P2PQuicTransport and must outlive
// the P2PQuicTransport. In the standard case the |delegate_| will be
// the object that owns the P2PQuicTransport.
P2PQuicTransport::Delegate* const delegate;
// When set to true the P2PQuicTransport will immediately be able
// to listen and respond to a crypto handshake upon construction.
// This will NOT start a handshake.
bool can_respond_to_crypto_handshake = true;
};
// For creating a P2PQuicTransport. This factory should be injected into
// whichever object plans to own and use a P2PQuicTransport. The
// P2PQuicTransportFactory needs to outlive the P2PQuicTransport it creates.
//
// This object should be run entirely on the webrtc worker thread.
class P2PQuicTransportFactory {
public:
virtual ~P2PQuicTransportFactory() = default;
// Creates the P2PQuicTransport. This should be called on the same
// thread that the P2PQuicTransport will be used on.
virtual std::unique_ptr<P2PQuicTransport> CreateQuicTransport(
P2PQuicTransportConfig config) = 0;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_P2P_QUIC_TRANSPORT_FACTORY_H_
// Copyright 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 "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory_impl.h"
#include "net/third_party/quic/core/quic_packet_writer.h"
#include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_packet_transport.h"
#include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h"
#include "third_party/webrtc/rtc_base/rtccertificate.h"
namespace blink {
namespace {
// The P2PQuicPacketWriter is a private helper class that implements the
// QuicPacketWriter using a P2PQuicPacketTransport. This allows us to
// connect our own packet transport for writing into the QuicConnection.
// The normal case is using an ICE transport (packet_transport) for writing.
class P2PQuicPacketWriter : public quic::QuicPacketWriter,
public P2PQuicPacketTransport::WriteObserver {
public:
P2PQuicPacketWriter(P2PQuicPacketTransport* packet_transport)
: packet_transport_(packet_transport) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(packet_transport_);
packet_transport_->SetWriteObserver(this);
}
// This way the packet transport knows it no longer has a write observer and
// can DCHECK this on destruction.
~P2PQuicPacketWriter() override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
packet_transport_->SetWriteObserver(nullptr);
}
// Sets the QuicConnection (which owns this packet writer). This allows us
// to get the packet numbers of QUIC packets we write. The QuicConnection
// is created with a quic::QuicPacketWriter, so we can't set the connection
// in the constructor.
void InitializeWithQuicConnection(
const quic::QuicConnection* const connection) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(connection);
if (packet_transport_->Writable()) {
SetWritable();
}
connection_ = connection;
}
// quic::QuicPacketWriter overrides.
// Writes a QUIC packet to the network with the packet number as additional
// packet info.
quic::WriteResult WritePacket(const char* buffer,
size_t buf_len,
const quic::QuicIpAddress& self_address,
const quic::QuicSocketAddress& peer_address,
quic::PerPacketOptions* options) override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(connection_);
if (IsWriteBlocked()) {
return quic::WriteResult(quic::WRITE_STATUS_BLOCKED, EWOULDBLOCK);
}
P2PQuicPacketTransport::QuicPacket packet;
packet.packet_number = connection_->packet_generator().packet_number();
packet.buffer = buffer;
packet.buf_len = buf_len;
int bytes_written = packet_transport_->WritePacket(packet);
if (bytes_written <= 0) {
writable_ = false;
return quic::WriteResult(quic::WRITE_STATUS_BLOCKED, EWOULDBLOCK);
}
return quic::WriteResult(quic::WRITE_STATUS_OK, bytes_written);
}
bool IsWriteBlockedDataBuffered() const override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return false;
}
bool IsWriteBlocked() const override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return !writable_;
}
quic::QuicByteCount GetMaxPacketSize(
const quic::QuicSocketAddress& peer_address) const override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// This can be configured later.
return 1200;
}
void SetWritable() override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
writable_ = true;
}
bool SupportsReleaseTime() const override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return false;
}
bool IsBatchMode() const override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return false;
}
char* GetNextWriteLocation(
const quic::QuicIpAddress& self_address,
const quic::QuicSocketAddress& peer_address) override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return nullptr;
}
quic::WriteResult Flush() override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return quic::WriteResult(quic::WRITE_STATUS_OK, 0);
}
// P2PQuicPacketTransport::WriteDelegate override.
void OnCanWrite() override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
SetWritable();
}
private:
// The packet transport is owned by the P2PQuicSession, not the
// BlinkPacketWriter.
P2PQuicPacketTransport* packet_transport_;
// The QuicConnection owns this packet writer and will outlive it.
const quic::QuicConnection* connection_;
bool writable_ = false;
THREAD_CHECKER(thread_checker_);
};
// Creates the QuicConnection for the QuicSession. Currently this connection
// uses a dummy address and ID. The |packet_writer| is a basic implementation
// using the QuicTransportConfig::packet_transport for writing. The |helper|
// and |alarm_factory| should be chromium specific implementations.
std::unique_ptr<quic::QuicConnection> CreateQuicConnection(
bool is_server,
quic::QuicConnectionHelperInterface* helper,
quic::QuicPacketWriter* packet_writer,
quic::QuicAlarmFactory* alarm_factory) {
quic::QuicIpAddress ip;
ip.FromString("0.0.0.0");
quic::QuicSocketAddress dummy_address(ip, 0 /* Port */);
quic::Perspective perspective =
is_server ? quic::Perspective::IS_SERVER : quic::Perspective::IS_CLIENT;
return std::make_unique<quic::QuicConnection>(
0 /* dummy ID */, dummy_address, helper, alarm_factory, packet_writer,
/* owns_writer */ true, perspective, quic::CurrentSupportedVersions());
}
} // namespace
P2PQuicTransportFactoryImpl::P2PQuicTransportFactoryImpl(
quic::QuicClock* clock,
std::unique_ptr<quic::QuicAlarmFactory> alarm_factory)
: clock_(clock), alarm_factory_(std::move(alarm_factory)) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
}
// The P2PQuicTransportImpl is created with Chromium specific QUIC objects:
// QuicClock, QuicRandom, QuicConnectionHelper and QuicAlarmFactory.
std::unique_ptr<P2PQuicTransport>
P2PQuicTransportFactoryImpl::CreateQuicTransport(
P2PQuicTransportConfig config) {
DCHECK(config.packet_transport);
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
quic::QuicRandom* quic_random = quic::QuicRandom::GetInstance();
// The P2PQuicSession owns these chromium specific objects required
// by the QuicConnection. These outlive the QuicConnection itself.
std::unique_ptr<net::QuicChromiumConnectionHelper> helper =
std::make_unique<net::QuicChromiumConnectionHelper>(clock_, quic_random);
P2PQuicPacketWriter* packet_writer =
new P2PQuicPacketWriter(config.packet_transport);
std::unique_ptr<quic::QuicConnection> quic_connection = CreateQuicConnection(
config.is_server, helper.get(), packet_writer, alarm_factory_.get());
// It's okay for the quic::QuicConnection to have a P2PQuicPacketWriter before
// the P2PQuicPacketWriter is initialized, because the P2QuicPacketWriter
// won't be writable until this occurs.
packet_writer->InitializeWithQuicConnection(quic_connection.get());
// QUIC configurations for the session are specified here.
quic::QuicConfig quic_config;
return std::make_unique<P2PQuicTransportImpl>(
std::move(config), std::move(helper), std::move(quic_connection),
quic_config, clock_);
}
} // namespace blink
// Copyright 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 THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_P2P_QUIC_TRANSPORT_FACTORY_IMPL_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_P2P_QUIC_TRANSPORT_FACTORY_IMPL_H_
#include "net/third_party/quic/core/quic_connection.h"
#include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory.h"
#include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h"
namespace blink {
// For creating a P2PQuicTransportImpl to be used for the blink Web API -
// RTCQuicTransport.
//
// This object should be run entirely on the webrtc worker thread.
class MODULES_EXPORT P2PQuicTransportFactoryImpl final
: public P2PQuicTransportFactory {
public:
P2PQuicTransportFactoryImpl(
quic::QuicClock* clock,
std::unique_ptr<quic::QuicAlarmFactory> alarm_factory);
~P2PQuicTransportFactoryImpl() override {}
// QuicTransportFactoryInterface override.
std::unique_ptr<P2PQuicTransport> CreateQuicTransport(
P2PQuicTransportConfig config) override;
private:
// This is used to create a QuicChromiumConnectionHelper for the session.
// Should outlive the P2PQuicTransportFactoryImpl.
quic::QuicClock* clock_;
// Used for alarms that drive the underlying QUIC library. Should use the same
// clock as |clock_|.
std::unique_ptr<quic::QuicAlarmFactory> alarm_factory_;
THREAD_CHECKER(thread_checker_);
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_P2P_QUIC_TRANSPORT_FACTORY_IMPL_H_
// Copyright 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 "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h"
#include "net/quic/quic_chromium_connection_helper.h"
#include "net/third_party/quic/core/crypto/proof_source.h"
#include "net/third_party/quic/core/crypto/quic_random.h"
#include "net/third_party/quic/core/quic_config.h"
#include "net/third_party/quic/core/tls_client_handshaker.h"
#include "net/third_party/quic/core/tls_server_handshaker.h"
#include "net/third_party/quic/tools/quic_simple_crypto_server_stream_helper.h"
namespace blink {
namespace {
static const char kClosingDetails[] = "Application closed connection.";
static const size_t kHostnameLength = 32;
// TODO(https://crbug.com/874300): Create a secure connection by implementing a
// P2PProofSource and P2PProofVerifier and remove these once the TLS 1.3
// handshake is implemented for QUIC. This will allow us to verify for both the
// server and client:
// - The self signed certificate fingerprint matches the remote
// fingerprint that was signaled.
// - The peer owns the certificate, by verifying the signature of the hash of
// the handshake context.
//
// Used by QuicCryptoServerConfig to provide dummy proof credentials
// (taken from quic/quartc).
class DummyProofSource : public quic::ProofSource {
public:
DummyProofSource() {}
~DummyProofSource() override {}
// ProofSource override.
void GetProof(const quic::QuicSocketAddress& server_addr,
const quic::QuicString& hostname,
const quic::QuicString& server_config,
quic::QuicTransportVersion transport_version,
quic::QuicStringPiece chlo_hash,
std::unique_ptr<Callback> callback) override {
quic::QuicReferenceCountedPointer<ProofSource::Chain> chain;
quic::QuicCryptoProof proof;
std::vector<quic::QuicString> certs;
certs.push_back("Dummy cert");
chain = new ProofSource::Chain(certs);
proof.signature = "Dummy signature";
proof.leaf_cert_scts = "Dummy timestamp";
callback->Run(true, chain, proof, nullptr /* details */);
}
quic::QuicReferenceCountedPointer<Chain> GetCertChain(
const quic::QuicSocketAddress& server_address,
const quic::QuicString& hostname) override {
return quic::QuicReferenceCountedPointer<Chain>();
}
void ComputeTlsSignature(
const quic::QuicSocketAddress& server_address,
const quic::QuicString& hostname,
uint16_t signature_algorithm,
quic::QuicStringPiece in,
std::unique_ptr<SignatureCallback> callback) override {
callback->Run(true, "Dummy signature");
}
};
// Used by QuicCryptoClientConfig to ignore the peer's credentials
// and establish an insecure QUIC connection (taken from quic/quartc).
class InsecureProofVerifier : public quic::ProofVerifier {
public:
InsecureProofVerifier() {}
~InsecureProofVerifier() override {}
// ProofVerifier override.
quic::QuicAsyncStatus VerifyProof(
const quic::QuicString& hostname,
const uint16_t port,
const quic::QuicString& server_config,
quic::QuicTransportVersion transport_version,
quic::QuicStringPiece chlo_hash,
const std::vector<quic::QuicString>& certs,
const quic::QuicString& cert_sct,
const quic::QuicString& signature,
const quic::ProofVerifyContext* context,
quic::QuicString* error_details,
std::unique_ptr<quic::ProofVerifyDetails>* verify_details,
std::unique_ptr<quic::ProofVerifierCallback> callback) override {
return quic::QUIC_SUCCESS;
}
quic::QuicAsyncStatus VerifyCertChain(
const quic::QuicString& hostname,
const std::vector<quic::QuicString>& certs,
const quic::ProofVerifyContext* context,
quic::QuicString* error_details,
std::unique_ptr<quic::ProofVerifyDetails>* details,
std::unique_ptr<quic::ProofVerifierCallback> callback) override {
return quic::QUIC_SUCCESS;
}
std::unique_ptr<quic::ProofVerifyContext> CreateDefaultContext() override {
return nullptr;
}
};
// A dummy helper for a server crypto stream that accepts all client hellos
// and generates a random connection ID.
class DummyCryptoServerStreamHelper
: public quic::QuicCryptoServerStream::Helper {
public:
explicit DummyCryptoServerStreamHelper(quic::QuicRandom* random)
: random_(random) {}
~DummyCryptoServerStreamHelper() override {}
quic::QuicConnectionId GenerateConnectionIdForReject(
quic::QuicConnectionId connection_id) const override {
return random_->RandUint64();
}
bool CanAcceptClientHello(const quic::CryptoHandshakeMessage& message,
const quic::QuicSocketAddress& client_address,
const quic::QuicSocketAddress& peer_address,
const quic::QuicSocketAddress& self_address,
quic::QuicString* error_details) const override {
return true;
}
private:
// Used to generate random connection IDs. Needs to outlive this.
quic::QuicRandom* random_;
};
} // namespace
P2PQuicTransportImpl::P2PQuicTransportImpl(
P2PQuicTransportConfig p2p_transport_config,
std::unique_ptr<net::QuicChromiumConnectionHelper> helper,
std::unique_ptr<quic::QuicConnection> connection,
const quic::QuicConfig& quic_config,
quic::QuicClock* clock)
: quic::QuicSession(connection.get(), nullptr /* visitor */, quic_config),
helper_(std::move(helper)),
connection_(std::move(connection)),
perspective_(p2p_transport_config.is_server
? quic::Perspective::IS_SERVER
: quic::Perspective::IS_CLIENT),
packet_transport_(p2p_transport_config.packet_transport),
delegate_(p2p_transport_config.delegate),
clock_(clock) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(delegate_);
DCHECK(clock_);
DCHECK(packet_transport_);
DCHECK_GT(p2p_transport_config.certificates.size(), 0u);
if (p2p_transport_config.can_respond_to_crypto_handshake) {
InitializeCryptoStream();
}
// TODO(https://crbug.com/874296): The web API accepts multiple certificates,
// and we might want to pass these down to let QUIC decide on what to use.
certificate_ = p2p_transport_config.certificates[0];
packet_transport_->SetReceiveDelegate(this);
}
P2PQuicTransportImpl::~P2PQuicTransportImpl() {
packet_transport_->SetReceiveDelegate(nullptr);
}
void P2PQuicTransportImpl::Stop() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (IsClosed()) {
return;
}
// The error code used for the connection closing is
// quic::QUIC_CONNECTION_CANCELLED. This allows us to distinguish that the
// application closed the connection, as opposed to it closing from a
// failure/error.
connection_->CloseConnection(
quic::QuicErrorCode::QUIC_CONNECTION_CANCELLED, kClosingDetails,
quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
}
void P2PQuicTransportImpl::Start(
std::vector<std::unique_ptr<rtc::SSLFingerprint>> remote_fingerprints) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK_EQ(remote_fingerprints_.size(), 0u);
DCHECK_GT(remote_fingerprints.size(), 0u);
if (IsClosed()) {
// We could have received a close from the remote side before calling this.
return;
}
// These will be used to verify the remote certificate during the handshake.
remote_fingerprints_ = std::move(remote_fingerprints);
if (perspective_ == quic::Perspective::IS_CLIENT) {
quic::QuicCryptoClientStream* client_crypto_stream =
static_cast<quic::QuicCryptoClientStream*>(crypto_stream_.get());
client_crypto_stream->CryptoConnect();
}
}
void P2PQuicTransportImpl::OnPacketDataReceived(const char* data,
size_t data_len) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Received data from the |packet_transport_|. Create a QUIC packet and send
// it to be processed by the QuicSession/Connection.
quic::QuicReceivedPacket packet(data, data_len, clock_->Now());
ProcessUdpPacket(connection()->self_address(), connection()->peer_address(),
packet);
}
quic::QuicCryptoStream* P2PQuicTransportImpl::GetMutableCryptoStream() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return crypto_stream_.get();
}
const quic::QuicCryptoStream* P2PQuicTransportImpl::GetCryptoStream() const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return crypto_stream_.get();
}
quic::QuicStream* P2PQuicTransportImpl::CreateOutgoingDynamicStream() {
NOTIMPLEMENTED();
return nullptr;
}
quic::QuicStream* P2PQuicTransportImpl::CreateIncomingDynamicStream(
quic::QuicStreamId id) {
NOTIMPLEMENTED();
return nullptr;
}
void P2PQuicTransportImpl::InitializeCryptoStream() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!crypto_stream_);
switch (perspective_) {
case quic::Perspective::IS_CLIENT: {
if (!crypto_client_config_) {
// The |crypto_client_config_| has not already been set (by the test).
std::unique_ptr<quic::ProofVerifier> proof_verifier(
new InsecureProofVerifier);
crypto_client_config_ = std::make_unique<quic::QuicCryptoClientConfig>(
std::move(proof_verifier),
quic::TlsClientHandshaker::CreateSslCtx());
}
// The host must be unique for every endpoint the client communicates
// with.
char random_hostname[kHostnameLength];
helper_->GetRandomGenerator()->RandBytes(random_hostname,
kHostnameLength);
quic::QuicServerId server_id(
/*host=*/quic::QuicString(random_hostname, kHostnameLength),
/*port=*/0,
/*privacy_mode_enabled=*/false);
crypto_stream_ = std::make_unique<quic::QuicCryptoClientStream>(
server_id, /*QuicSession=*/this,
crypto_client_config_->proof_verifier()->CreateDefaultContext(),
crypto_client_config_.get(), /*ProofHandler=*/this);
QuicSession::Initialize();
break;
}
case quic::Perspective::IS_SERVER: {
std::unique_ptr<quic::ProofSource> proof_source(new DummyProofSource);
crypto_server_config_ = std::make_unique<quic::QuicCryptoServerConfig>(
quic::QuicCryptoServerConfig::TESTING, helper_->GetRandomGenerator(),
std::move(proof_source), quic::KeyExchangeSource::Default(),
quic::TlsServerHandshaker::CreateSslCtx());
// Provide server with serialized config string to prove ownership.
quic::QuicCryptoServerConfig::ConfigOptions options;
// The |message| is used to handle the return value of AddDefaultConfig
// which is raw pointer of the CryptoHandshakeMessage.
std::unique_ptr<quic::CryptoHandshakeMessage> message(
crypto_server_config_->AddDefaultConfig(
helper_->GetRandomGenerator(), helper_->GetClock(), options));
compressed_certs_cache_.reset(new quic::QuicCompressedCertsCache(
quic::QuicCompressedCertsCache::kQuicCompressedCertsCacheSize));
bool use_stateless_rejects_if_peer_supported = false;
server_stream_helper_ = std::make_unique<DummyCryptoServerStreamHelper>(
helper_->GetRandomGenerator());
crypto_stream_ = std::make_unique<quic::QuicCryptoServerStream>(
crypto_server_config_.get(), compressed_certs_cache_.get(),
use_stateless_rejects_if_peer_supported, this,
server_stream_helper_.get());
QuicSession::Initialize();
break;
}
default:
NOTREACHED();
break;
}
}
void P2PQuicTransportImpl::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
QuicSession::OnCryptoHandshakeEvent(event);
if (event == HANDSHAKE_CONFIRMED) {
DCHECK(IsEncryptionEstablished());
DCHECK(IsCryptoHandshakeConfirmed());
delegate_->OnConnected();
}
}
void P2PQuicTransportImpl::OnConnectionClosed(
quic::QuicErrorCode error,
const std::string& error_details,
quic::ConnectionCloseSource source) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
quic::QuicSession::OnConnectionClosed(error, error_details, source);
if (error != quic::QuicErrorCode::QUIC_CONNECTION_CANCELLED) {
delegate_->OnConnectionFailed(
error_details, source == quic::ConnectionCloseSource::FROM_PEER);
} else if (source == quic::ConnectionCloseSource::FROM_PEER) {
// This connection was closed by the application of the remote side.
delegate_->OnRemoteStopped();
}
}
bool P2PQuicTransportImpl::IsClosed() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return !connection_->connected();
}
void P2PQuicTransportImpl::set_crypto_client_config(
std::unique_ptr<quic::QuicCryptoClientConfig> crypto_client_config) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
crypto_client_config_ = std::move(crypto_client_config);
}
} // namespace blink
// Copyright 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 THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_P2P_QUIC_TRANSPORT_IMPL_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_P2P_QUIC_TRANSPORT_IMPL_H_
#include "base/threading/thread_checker.h"
#include "net/quic/quic_chromium_connection_helper.h"
#include "net/third_party/quic/core/crypto/quic_crypto_client_config.h"
#include "net/third_party/quic/core/crypto/quic_crypto_server_config.h"
#include "net/third_party/quic/core/quic_crypto_client_stream.h"
#include "net/third_party/quic/core/quic_crypto_server_stream.h"
#include "net/third_party/quic/core/quic_packet_writer.h"
#include "net/third_party/quic/core/quic_session.h"
#include "net/third_party/quic/tools/quic_simple_crypto_server_stream_helper.h"
#include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_packet_transport.h"
#include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h"
#include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory.h"
#include "third_party/webrtc/rtc_base/rtccertificate.h"
namespace blink {
// The P2PQuicTransportImpl subclasses the quic::QuicSession in order to expose
// QUIC as a P2P transport. This specific subclass implements the crypto
// handshake for a peer to peer connection, which requires verifying the remote
// certificate's fingerprint, but otherwise exposes a raw quic::QuicSession.
//
// At a high level, the quic::QuicConnection manages the actual connection
// between two endpoints, while the quic::QuicSession owns and manages the
// quic::QuicStreams. The quic::QuicSession also handles the negotiation between
// endpoints, session control (reset, window updates, control frames, etc.), and
// callbacks from either the connection (quic::QuicConnectionVisitorInterface),
// frames being acked or lost (quic::SessionNotifierInterface), or handshake
// state changes.
//
// This object should be run entirely on the webrtc worker thread.
class MODULES_EXPORT P2PQuicTransportImpl final
: public quic::QuicSession,
public P2PQuicTransport,
public P2PQuicPacketTransport::ReceiveDelegate,
public quic::QuicCryptoClientStream::ProofHandler {
public:
P2PQuicTransportImpl(
P2PQuicTransportConfig p2p_transport_config,
std::unique_ptr<net::QuicChromiumConnectionHelper> helper,
std::unique_ptr<quic::QuicConnection> connection,
const quic::QuicConfig& quic_config,
quic::QuicClock* clock);
~P2PQuicTransportImpl() override;
// P2PQuicTransport overrides.
void Stop() override;
// Sets the remote fingerprints, and if the the P2PQuicTransportImpl is a
// client starts the QUIC handshake . This handshake is currently insecure,
// meaning that the certificates used are fake and are not verified. It also
// assumes a handshake for a server/client case. This must be called before
// creating any streams.
//
// TODO(https://crbug.com/874300): Verify both the client and server
// certificates with the signaled remote fingerprints. Until the TLS 1.3
// handshake is supported in the QUIC core library we can only verify the
// server's certificate, but not the client's. Note that this means
// implementing the handshake for a P2P case, in which case verification
// completes after both receiving the signaled remote fingerprint and getting
// a client hello. Because either can come first, a synchronous call to verify
// the remote fingerprint is not possible.
void Start(std::vector<std::unique_ptr<rtc::SSLFingerprint>>
remote_fingerprints) override;
// P2PQuicPacketTransport::Delegate override.
void OnPacketDataReceived(const char* data, size_t data_len) override;
// quic::QuicCryptoClientStream::ProofHandler overrides used in a client
// connection to get certificate verification details.
// Called when the proof verification completes. This information is used
// for 0 RTT handshakes, which isn't relevant for our P2P handshake.
void OnProofValid(
const quic::QuicCryptoClientConfig::CachedState& cached) override{};
// Called when proof verification become available.
void OnProofVerifyDetailsAvailable(
const quic::ProofVerifyDetails& verify_details) override{};
// quic::QuicConnectionVisitorInterface override.
void OnConnectionClosed(quic::QuicErrorCode error,
const std::string& error_details,
quic::ConnectionCloseSource source) override;
protected:
// quic::QuicSession overrides.
// TODO(https://crbug.com/874296): Subclass QuicStream and implement these
// functions.
// Creates a new stream initiated from the remote side. The caller does not
// own the stream, so the stream is activated and ownership is moved to the
// quic::QuicSession.
quic::QuicStream* CreateIncomingDynamicStream(quic::QuicStreamId id) override;
// Creates a new outgoing stream. The caller does not own the
// stream, so the stream is activated and ownership is moved to the
// quic::QuicSession.
quic::QuicStream* CreateOutgoingDynamicStream() override;
void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override;
private:
// This is for testing connection failures and handshake failures.
friend class P2PQuicTransportTest;
// These functions are used for testing.
//
// Returns true if the quic::QuicConnection has been closed remotely or
// locally.
bool IsClosed();
quic::QuicConnection* connection() { return connection_.get(); }
// Allows the test to set its own proof verification.
void set_crypto_client_config(
std::unique_ptr<quic::QuicCryptoClientConfig> crypto_client_config);
// quic::QuicSession overrides.
const quic::QuicCryptoStream* GetCryptoStream() const override;
quic::QuicCryptoStream* GetMutableCryptoStream() override;
// Creates the crypto stream necessary for handshake negotiation, and
// initializes the parent class (quic::QuicSession). This must be called on
// both sides before communicating between endpoints (Start, Close, etc.).
void InitializeCryptoStream();
// The server_config and client_config are used for setting up the crypto
// connection. The ownership of these objects or the objects they own
// (quic::ProofSource, quic::ProofVerifier, etc.), are not passed on to the
// QUIC library for the handshake, so we must own them here. A client
// |perspective_| will not have a crypto_server_config and vice versa.
std::unique_ptr<quic::QuicCryptoServerConfig> crypto_server_config_;
std::unique_ptr<quic::QuicCryptoClientConfig> crypto_client_config_;
// Used by server |crypto_stream_| to track most recently compressed certs.
std::unique_ptr<quic::QuicCompressedCertsCache> compressed_certs_cache_;
std::unique_ptr<quic::QuicCryptoServerStream::Helper> server_stream_helper_;
// Owned by the P2PQuicTransportImpl. |helper_| is placed before
// |connection_| to ensure it outlives it.
std::unique_ptr<net::QuicChromiumConnectionHelper> helper_;
std::unique_ptr<quic::QuicConnection> connection_;
std::unique_ptr<quic::QuicCryptoStream> crypto_stream_;
// Crypto information. Note that currently the handshake is insecure and these
// are not used...
rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
std::vector<std::unique_ptr<rtc::SSLFingerprint>> remote_fingerprints_;
quic::Perspective perspective_;
// Outlives the P2PQuicTransport.
P2PQuicPacketTransport* packet_transport_;
P2PQuicTransport::Delegate* delegate_ = nullptr;
// Owned by whatever creates the P2PQuicTransportImpl. The |clock_| needs to
// outlive the P2PQuicTransportImpl.
quic::QuicClock* clock_ = nullptr;
THREAD_CHECKER(thread_checker_);
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_P2P_QUIC_TRANSPORT_IMPL_H_
// Copyright 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/quic/quic_chromium_alarm_factory.h"
#include "net/quic/test_task_runner.h"
#include "net/test/gtest_util.h"
#include "net/third_party/quic/core/tls_client_handshaker.h"
#include "net/third_party/quic/test_tools/mock_clock.h"
#include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_packet_transport.h"
#include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory_impl.h"
#include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h"
#include "third_party/webrtc/rtc_base/rtccertificate.h"
#include "third_party/webrtc/rtc_base/sslfingerprint.h"
#include "third_party/webrtc/rtc_base/sslidentity.h"
namespace blink {
namespace {
// The types of callbacks that can be fired on a P2PQuicTransport::Delegate.
enum TransportCallbackType {
kOnRemoteStopped,
kOnConnectionFailed,
kOnConnected,
kNone
};
// Implements counters for callbacks. It can also set expectations for a
// specific callback. When an expectation is set the quic::TestTaskRunner
// drives the test until the callbacks have been fired, and we are no longer
// expecting the callback.
//
// TODO(https://crbug.com/874296): If these files get moved to the platform
// directory we will run the tests in a different test environment. In that case
// it will make more sense to use the TestCompletionCallback and the RunLoop for
// driving the test.
class QuicTransportDelegateForTest final : public P2PQuicTransport::Delegate {
public:
~QuicTransportDelegateForTest() override {}
void OnRemoteStopped() override {
if (callback_type_expected_ == TransportCallbackType::kOnRemoteStopped) {
callback_type_expected_ = TransportCallbackType::kNone;
}
stopped_++;
}
void OnConnectionFailed(const std::string& error_details,
bool from_remote) override {
if (callback_type_expected_ == TransportCallbackType::kOnConnectionFailed) {
callback_type_expected_ = TransportCallbackType::kNone;
}
connection_failed_++;
}
void OnConnected() override {
if (callback_type_expected_ == TransportCallbackType::kOnConnected) {
callback_type_expected_ = TransportCallbackType::kNone;
}
connected_++;
}
int stopped() const { return stopped_; }
int connection_failed() const { return connection_failed_; }
int connected() const { return connected_; }
// Sets the type of callback expected to be called.
void ExpectCallback(TransportCallbackType callback_type) {
callback_type_expected_ = callback_type;
}
// Returns if ExpectingCallback() has been called and the callback hasn't been
// fired yet.
bool IsExpectingCallback() { return callback_type_expected_ != kNone; }
private:
TransportCallbackType callback_type_expected_ = kNone;
int stopped_ = 0;
int connection_failed_ = 0;
int connected_ = 0;
};
// This is a fake packet transport to be used by the P2PQuicTransportImpl. It
// allows to easily connect two packet transports together. We send packets
// asynchronously, by using the same alarm factory that is being used for the
// underlying QUIC library.
class FakePacketTransport : public P2PQuicPacketTransport,
public quic::QuicAlarm::Delegate {
public:
FakePacketTransport(quic::QuicAlarmFactory* alarm_factory,
quic::MockClock* clock)
: alarm_(alarm_factory->CreateAlarm(new AlarmDelegate(this))),
clock_(clock) {}
~FakePacketTransport() override {
// The write observer should be unset when it is destroyed.
DCHECK(!write_observer_);
};
// Called by QUIC for writing data to the other side. The flow for writing a
// packet is P2PQuicTransportImpl --> quic::QuicConnection -->
// quic::QuicPacketWriter --> FakePacketTransport. In this case the
// FakePacketTransport just writes directly to the FakePacketTransport on the
// other side.
int WritePacket(const QuicPacket& packet) override {
// For the test there should always be a peer_packet_transport_ connected at
// this point.
if (!peer_packet_transport_) {
return 0;
}
last_packet_num_ = packet.packet_number;
packet_queue_.emplace_back(packet.buffer, packet.buf_len);
alarm_->Cancel();
// We don't want get 0 RTT.
alarm_->Set(clock_->Now() + quic::QuicTime::Delta::FromMicroseconds(10));
return packet.buf_len;
}
// Sets the P2PQuicTransportImpl as the delegate.
void SetReceiveDelegate(
P2PQuicPacketTransport::ReceiveDelegate* delegate) override {
// We can't set two ReceiveDelegates for one packet transport.
DCHECK(!delegate_ || !delegate);
delegate_ = delegate;
}
void SetWriteObserver(
P2PQuicPacketTransport::WriteObserver* write_observer) override {
// We can't set two WriteObservers for one packet transport.
DCHECK(!write_observer_ || !write_observer);
write_observer_ = write_observer;
}
bool Writable() override { return true; }
// Connects the other FakePacketTransport, so we can write to the peer.
void ConnectPeerTransport(FakePacketTransport* peer_packet_transport) {
DCHECK(!peer_packet_transport_);
peer_packet_transport_ = peer_packet_transport;
}
// Disconnects the delegate, so we no longer write to it. The test must call
// this before destructing either of the packet transports!
void DisconnectPeerTransport(FakePacketTransport* peer_packet_transport) {
DCHECK(peer_packet_transport_ == peer_packet_transport);
peer_packet_transport_ = nullptr;
}
// The callback used in order for us to communicate between
// FakePacketTransports.
void OnDataReceivedFromPeer(const char* data, size_t data_len) {
DCHECK(delegate_);
delegate_->OnPacketDataReceived(data, data_len);
}
int last_packet_num() { return last_packet_num_; }
private:
// Wraps the FakePacketTransport so that we can pass in a raw pointer that can
// be reference counted when calling CreateAlarm().
class AlarmDelegate : public quic::QuicAlarm::Delegate {
public:
explicit AlarmDelegate(FakePacketTransport* packet_transport)
: packet_transport_(packet_transport) {}
void OnAlarm() override { packet_transport_->OnAlarm(); }
private:
FakePacketTransport* packet_transport_;
};
// Called when we should write any buffered data.
void OnAlarm() override {
// Send the data to the peer at this point.
peer_packet_transport_->OnDataReceivedFromPeer(
packet_queue_.front().c_str(), packet_queue_.front().length());
packet_queue_.pop_front();
// If there's more packets to be sent out, reset the alarm to send it as the
// next task.
if (!packet_queue_.empty()) {
alarm_->Cancel();
alarm_->Set(clock_->Now());
}
}
// If async, packets are queued here to send.
quic::QuicDeque<quic::QuicString> packet_queue_;
// Alarm used to send data asynchronously.
quic::QuicArenaScopedPtr<quic::QuicAlarm> alarm_;
// The P2PQuicTransportImpl, which sets itself as the delegate in its
// constructor. After receiving data it forwards it along to QUIC.
P2PQuicPacketTransport::ReceiveDelegate* delegate_ = nullptr;
// The P2PQuicPacketWriter, which sets itself as a write observer
// during the P2PQuicTransportFactoryImpl::CreateQuicTransport. It is
// owned by the QuicConnection and will
P2PQuicPacketTransport::WriteObserver* write_observer_ = nullptr;
// The other FakePacketTransport that we are writing to. It's the
// responsibility of the test to disconnect this delegate
// (set_delegate(nullptr);) before it is destructed.
FakePacketTransport* peer_packet_transport_ = nullptr;
quic::QuicPacketNumber last_packet_num_;
quic::MockClock* clock_;
};
// A helper class to bundle test objects together.
class QuicPeerForTest {
public:
QuicPeerForTest(
std::unique_ptr<FakePacketTransport> packet_transport,
std::unique_ptr<QuicTransportDelegateForTest> quic_transport_delegate,
std::unique_ptr<P2PQuicTransportImpl> quic_transport,
rtc::scoped_refptr<rtc::RTCCertificate> certificate)
: packet_transport_(std::move(packet_transport)),
quic_transport_delegate_(std::move(quic_transport_delegate)),
quic_transport_(std::move(quic_transport)),
certificate_(certificate) {}
FakePacketTransport* packet_transport() { return packet_transport_.get(); }
QuicTransportDelegateForTest* quic_transport_delegate() {
return quic_transport_delegate_.get();
}
P2PQuicTransportImpl* quic_transport() { return quic_transport_.get(); }
rtc::scoped_refptr<rtc::RTCCertificate> certificate() { return certificate_; }
private:
std::unique_ptr<FakePacketTransport> packet_transport_;
std::unique_ptr<QuicTransportDelegateForTest> quic_transport_delegate_;
std::unique_ptr<P2PQuicTransportImpl> quic_transport_;
rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
};
rtc::scoped_refptr<rtc::RTCCertificate> CreateTestCertificate() {
rtc::KeyParams params;
rtc::SSLIdentity* ssl_identity =
rtc::SSLIdentity::Generate("dummy_certificate", params);
return rtc::RTCCertificate::Create(
std::unique_ptr<rtc::SSLIdentity>(ssl_identity));
}
// Allows faking a failing handshake.
class FailingProofVerifier : public quic::ProofVerifier {
public:
FailingProofVerifier() {}
~FailingProofVerifier() override {}
// ProofVerifier override.
quic::QuicAsyncStatus VerifyProof(
const quic::QuicString& hostname,
const uint16_t port,
const quic::QuicString& server_config,
quic::QuicTransportVersion transport_version,
quic::QuicStringPiece chlo_hash,
const std::vector<quic::QuicString>& certs,
const quic::QuicString& cert_sct,
const quic::QuicString& signature,
const quic::ProofVerifyContext* context,
quic::QuicString* error_details,
std::unique_ptr<quic::ProofVerifyDetails>* verify_details,
std::unique_ptr<quic::ProofVerifierCallback> callback) override {
return quic::QUIC_FAILURE;
}
quic::QuicAsyncStatus VerifyCertChain(
const quic::QuicString& hostname,
const std::vector<quic::QuicString>& certs,
const quic::ProofVerifyContext* context,
quic::QuicString* error_details,
std::unique_ptr<quic::ProofVerifyDetails>* details,
std::unique_ptr<quic::ProofVerifierCallback> callback) override {
return quic::QUIC_FAILURE;
}
std::unique_ptr<quic::ProofVerifyContext> CreateDefaultContext() override {
return nullptr;
}
};
} // namespace
// Unit tests for the P2PQuicTransport, using an underlying fake packet
// transport that sends packets directly between endpoints. The test is driven
// using the quic::TestTaskRunner to run posted tasks until callbacks have been
// fired.
class P2PQuicTransportTest : public testing::Test {
public:
P2PQuicTransportTest() {}
~P2PQuicTransportTest() override {
// This must be done before desctructing the transports so that we don't
// have any dangling pointers.
client_peer_->packet_transport()->DisconnectPeerTransport(
server_peer_->packet_transport());
server_peer_->packet_transport()->DisconnectPeerTransport(
client_peer_->packet_transport());
}
// Connects both peer's underlying transports and creates both
// P2PQuicTransportImpls.
void Initialize(bool can_respond_to_crypto_handshake = true) {
// Quic crashes if packets are sent at time 0, and the clock defaults to 0.
clock_.AdvanceTime(quic::QuicTime::Delta::FromMilliseconds(1000));
runner_ = new net::test::TestTaskRunner(&clock_);
net::QuicChromiumAlarmFactory* alarm_factory =
new net::QuicChromiumAlarmFactory(runner_.get(), &clock_);
quic_transport_factory_ = std::make_unique<P2PQuicTransportFactoryImpl>(
&clock_, std::unique_ptr<net::QuicChromiumAlarmFactory>(alarm_factory));
std::unique_ptr<FakePacketTransport> client_packet_transport =
std::make_unique<FakePacketTransport>(alarm_factory, &clock_);
std::unique_ptr<FakePacketTransport> server_packet_transport =
std::make_unique<FakePacketTransport>(alarm_factory, &clock_);
// Connect the transports so that they can speak to each other.
client_packet_transport->ConnectPeerTransport(
server_packet_transport.get());
server_packet_transport->ConnectPeerTransport(
client_packet_transport.get());
rtc::scoped_refptr<rtc::RTCCertificate> client_cert =
CreateTestCertificate();
std::unique_ptr<QuicTransportDelegateForTest>
client_quic_transport_delegate =
std::make_unique<QuicTransportDelegateForTest>();
std::vector<rtc::scoped_refptr<rtc::RTCCertificate>> client_certificates;
client_certificates.push_back(client_cert);
P2PQuicTransportConfig client_config(client_quic_transport_delegate.get(),
client_packet_transport.get(),
client_certificates);
client_config.is_server = false;
client_config.can_respond_to_crypto_handshake =
can_respond_to_crypto_handshake;
// We can't downcast a unique_ptr to an object, so we have to release, cast
// it, then create a unique_ptr of the downcasted pointer.
P2PQuicTransportImpl* client_quic_transport_ptr =
static_cast<P2PQuicTransportImpl*>(
quic_transport_factory_
->CreateQuicTransport(std::move(client_config))
.release());
std::unique_ptr<P2PQuicTransportImpl> client_quic_transport =
std::unique_ptr<P2PQuicTransportImpl>(client_quic_transport_ptr);
client_peer_ = std::make_unique<QuicPeerForTest>(
std::move(client_packet_transport),
std::move(client_quic_transport_delegate),
std::move(client_quic_transport), client_cert);
std::unique_ptr<QuicTransportDelegateForTest>
server_quic_transport_delegate =
std::make_unique<QuicTransportDelegateForTest>();
rtc::scoped_refptr<rtc::RTCCertificate> server_cert =
CreateTestCertificate();
std::vector<rtc::scoped_refptr<rtc::RTCCertificate>> server_certificates;
server_certificates.push_back(server_cert);
P2PQuicTransportConfig server_config(server_quic_transport_delegate.get(),
server_packet_transport.get(),
server_certificates);
server_config.is_server = true;
server_config.can_respond_to_crypto_handshake =
can_respond_to_crypto_handshake;
P2PQuicTransportImpl* server_quic_transport_ptr =
static_cast<P2PQuicTransportImpl*>(
quic_transport_factory_
->CreateQuicTransport(std::move(server_config))
.release());
std::unique_ptr<P2PQuicTransportImpl> server_quic_transport =
std::unique_ptr<P2PQuicTransportImpl>(server_quic_transport_ptr);
server_peer_ = std::make_unique<QuicPeerForTest>(
std::move(server_packet_transport),
std::move(server_quic_transport_delegate),
std::move(server_quic_transport), server_cert);
}
// Sets a FailingProofVerifier to the client transport before initializing
// the its crypto stream. This allows the client to fail the proof
// verification step during the crypto handshake.
void InitializeWithFailingProofVerification() {
// Allows us to initialize the crypto streams after constructing the
// objects.
Initialize(false);
// Create the client crypto config and insert it into the client transport.
std::unique_ptr<quic::ProofVerifier> proof_verifier(
new FailingProofVerifier);
std::unique_ptr<quic::QuicCryptoClientConfig> crypto_client_config =
std::make_unique<quic::QuicCryptoClientConfig>(
std::move(proof_verifier),
quic::TlsClientHandshaker::CreateSslCtx());
client_peer_->quic_transport()->set_crypto_client_config(
std::move(crypto_client_config));
// Now initialize the crypto streams.
client_peer_->quic_transport()->InitializeCryptoStream();
server_peer_->quic_transport()->InitializeCryptoStream();
}
// Drives the test until we are't expecting any more callbacks to be fired.
// This is done using the net::test::TestTaskRunner, which runs the tasks
// in the correct order and then advances the quic::MockClock to the time the
// task is run.
void RunUntilCallbacksFired() {
while (server_peer_->quic_transport_delegate()->IsExpectingCallback() ||
client_peer_->quic_transport_delegate()->IsExpectingCallback()) {
// We shouldn't enter a case where we are expecting a callback
// and we're out of tasks to run.
ASSERT_GT(runner_->GetPostedTasks().size(), 0u);
runner_->RunNextTask();
}
}
// Drives the test by running the current tasks that are posted.
void RunCurrentTasks() {
size_t posted_tasks_size = runner_->GetPostedTasks().size();
for (size_t i = 0; i < posted_tasks_size; ++i) {
runner_->RunNextTask();
}
}
// Starts the handshake, by setting the remote fingerprints and kicking off
// the handshake from the client.
void StartHandshake() {
std::vector<std::unique_ptr<rtc::SSLFingerprint>> server_fingerprints;
server_fingerprints.emplace_back(rtc::SSLFingerprint::Create(
"sha-256", server_peer_->certificate()->identity()));
// The server side doesn't currently need call this to set the remote
// fingerprints, but once P2P certificate verification is supported in the
// TLS 1.3 handshake this will ben necessary.
server_peer_->quic_transport()->Start(std::move(server_fingerprints));
std::vector<std::unique_ptr<rtc::SSLFingerprint>> client_fingerprints;
client_fingerprints.emplace_back(rtc::SSLFingerprint::Create(
"sha-256", client_peer_->certificate()->identity()));
client_peer_->quic_transport()->Start(std::move(client_fingerprints));
}
// Sets up an initial handshake and connection between peers.
void Connect() {
client_peer_->quic_transport_delegate()->ExpectCallback(kOnConnected);
server_peer_->quic_transport_delegate()->ExpectCallback(kOnConnected);
StartHandshake();
RunUntilCallbacksFired();
ExpectSecureConnection();
}
void ExpectSecureConnection() {
EXPECT_TRUE(client_peer_->quic_transport()->IsEncryptionEstablished());
EXPECT_TRUE(client_peer_->quic_transport()->IsCryptoHandshakeConfirmed());
EXPECT_TRUE(server_peer_->quic_transport()->IsCryptoHandshakeConfirmed());
EXPECT_TRUE(server_peer_->quic_transport()->IsEncryptionEstablished());
}
void ExpectConnectionNotEstablished() {
EXPECT_FALSE(client_peer_->quic_transport()->IsEncryptionEstablished());
EXPECT_FALSE(client_peer_->quic_transport()->IsCryptoHandshakeConfirmed());
EXPECT_FALSE(server_peer_->quic_transport()->IsCryptoHandshakeConfirmed());
EXPECT_FALSE(server_peer_->quic_transport()->IsEncryptionEstablished());
}
// Test that the callbacks were called appropriately after a successful
// crypto handshake.
void ExpectSuccessfulHandshake() {
EXPECT_EQ(1, client_peer_->quic_transport_delegate()->connected());
EXPECT_EQ(0, client_peer_->quic_transport_delegate()->stopped());
EXPECT_EQ(0, client_peer_->quic_transport_delegate()->connection_failed());
EXPECT_EQ(1, server_peer_->quic_transport_delegate()->connected());
EXPECT_EQ(0, server_peer_->quic_transport_delegate()->stopped());
EXPECT_EQ(0, server_peer_->quic_transport_delegate()->connection_failed());
}
void ExpectClosed() {
EXPECT_TRUE(client_peer_->quic_transport()->IsClosed());
EXPECT_TRUE(server_peer_->quic_transport()->IsClosed());
}
// Exposes these private functions to the test.
bool IsClientClosed() { return client_peer_->quic_transport()->IsClosed(); }
bool IsServerClosed() { return server_peer_->quic_transport()->IsClosed(); }
// Tests that the callbacks were appropriately called after the client
// stops the connection. Only the server should receive the OnRemoteStopped()
// callback.
void ExpectClientStopped() {
ExpectClosed();
EXPECT_EQ(0, client_peer_->quic_transport_delegate()->stopped());
EXPECT_EQ(0, client_peer_->quic_transport_delegate()->connection_failed());
EXPECT_EQ(1, server_peer_->quic_transport_delegate()->stopped());
EXPECT_EQ(0, server_peer_->quic_transport_delegate()->connection_failed());
}
// Tests that the callbacks were appropriately called after the server
// stops the connection. Only the client should receive the OnRemoteStopped()
// callback.
void ExpectServerStopped() {
ExpectClosed();
EXPECT_EQ(1, client_peer_->quic_transport_delegate()->stopped());
EXPECT_EQ(0, client_peer_->quic_transport_delegate()->connection_failed());
EXPECT_EQ(0, server_peer_->quic_transport_delegate()->stopped());
EXPECT_EQ(0, server_peer_->quic_transport_delegate()->connection_failed());
}
QuicPeerForTest* client_peer() { return client_peer_.get(); }
quic::QuicConnection* client_connection() {
return client_peer_->quic_transport()->connection();
}
QuicPeerForTest* server_peer() { return server_peer_.get(); }
quic::QuicConnection* server_connection() {
return server_peer_->quic_transport()->connection();
}
private:
quic::MockClock clock_;
// The TestTaskRunner is used by the QUIC library for setting/firing alarms.
// We are able to explicitly run these tasks ourselves with the
// TestTaskRunner.
scoped_refptr<net::test::TestTaskRunner> runner_;
std::unique_ptr<P2PQuicTransportFactoryImpl> quic_transport_factory_;
std::unique_ptr<QuicPeerForTest> client_peer_;
std::unique_ptr<QuicPeerForTest> server_peer_;
};
// Tests that we can connect two quic transports.
TEST_F(P2PQuicTransportTest, HandshakeConnectsPeers) {
Initialize();
Connect();
ExpectSuccessfulHandshake();
}
// Tests the standard case for the server side closing the connection.
TEST_F(P2PQuicTransportTest, ServerStops) {
Initialize();
Connect();
client_peer()->quic_transport_delegate()->ExpectCallback(kOnRemoteStopped);
server_peer()->quic_transport()->Stop();
RunUntilCallbacksFired();
ExpectServerStopped();
}
// Tests the standard case for the client side closing the connection.
TEST_F(P2PQuicTransportTest, ClientStops) {
Initialize();
Connect();
server_peer()->quic_transport_delegate()->ExpectCallback(kOnRemoteStopped);
client_peer()->quic_transport()->Stop();
RunUntilCallbacksFired();
ExpectClientStopped();
}
// Tests that if either side tries to close the connection a second time, it
// will be ignored because the connection has already been closed.
TEST_F(P2PQuicTransportTest, StopAfterStopped) {
Initialize();
Connect();
server_peer()->quic_transport_delegate()->ExpectCallback(kOnRemoteStopped);
client_peer()->quic_transport()->Stop();
RunUntilCallbacksFired();
client_peer()->quic_transport()->Stop();
server_peer()->quic_transport()->Stop();
RunCurrentTasks();
ExpectClientStopped();
}
// Tests that when the client closes the connection the subsequent call to
// Start() will be ignored.
TEST_F(P2PQuicTransportTest, ClientStopsBeforeClientStarts) {
Initialize();
server_peer()->quic_transport_delegate()->ExpectCallback(kOnRemoteStopped);
client_peer()->quic_transport()->Stop();
StartHandshake();
RunUntilCallbacksFired();
ExpectConnectionNotEstablished();
ExpectClientStopped();
}
// Tests that if the server closes the connection before the client starts the
// handshake, the client side will already be closed and Start() will be
// ignored.
TEST_F(P2PQuicTransportTest, ServerStopsBeforeClientStarts) {
Initialize();
client_peer()->quic_transport_delegate()->ExpectCallback(kOnRemoteStopped);
server_peer()->quic_transport()->Stop();
StartHandshake();
RunUntilCallbacksFired();
ExpectConnectionNotEstablished();
ExpectServerStopped();
}
// Tests that when the server's connection fails and then a handshake is
// attempted the transports will not become connected.
TEST_F(P2PQuicTransportTest, ClientConnectionClosesBeforeHandshake) {
Initialize();
client_peer()->quic_transport_delegate()->ExpectCallback(kOnConnectionFailed);
server_peer()->quic_transport_delegate()->ExpectCallback(kOnConnectionFailed);
client_connection()->CloseConnection(
quic::QuicErrorCode::QUIC_INTERNAL_ERROR, "internal error",
quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
StartHandshake();
RunUntilCallbacksFired();
ExpectConnectionNotEstablished();
}
// Tests that when the server's connection fails and then a handshake is
// attempted the transports will not become connected.
TEST_F(P2PQuicTransportTest, ServerConnectionClosesBeforeHandshake) {
Initialize();
client_peer()->quic_transport_delegate()->ExpectCallback(kOnConnectionFailed);
server_peer()->quic_transport_delegate()->ExpectCallback(kOnConnectionFailed);
server_connection()->CloseConnection(
quic::QuicErrorCode::QUIC_INTERNAL_ERROR, "internal error",
quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
StartHandshake();
RunUntilCallbacksFired();
ExpectConnectionNotEstablished();
}
// Tests that the appropriate callbacks are fired when the handshake fails.
TEST_F(P2PQuicTransportTest, HandshakeFailure) {
InitializeWithFailingProofVerification();
client_peer()->quic_transport_delegate()->ExpectCallback(kOnConnectionFailed);
server_peer()->quic_transport_delegate()->ExpectCallback(kOnConnectionFailed);
StartHandshake();
RunUntilCallbacksFired();
EXPECT_EQ(1, client_peer()->quic_transport_delegate()->connection_failed());
EXPECT_EQ(1, server_peer()->quic_transport_delegate()->connection_failed());
ExpectConnectionNotEstablished();
ExpectClosed();
}
// Tests that the appropriate callbacks are fired when the client's connection
// fails after the transports have connected.
TEST_F(P2PQuicTransportTest, ClientConnectionFailureAfterConnected) {
Initialize();
Connect();
// Close the connection with an internal QUIC error.
client_peer()->quic_transport_delegate()->ExpectCallback(kOnConnectionFailed);
server_peer()->quic_transport_delegate()->ExpectCallback(kOnConnectionFailed);
client_connection()->CloseConnection(
quic::QuicErrorCode::QUIC_INTERNAL_ERROR, "internal error",
quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
RunUntilCallbacksFired();
ExpectClosed();
EXPECT_EQ(1, client_peer()->quic_transport_delegate()->connection_failed());
EXPECT_EQ(1, server_peer()->quic_transport_delegate()->connection_failed());
}
// Tests that the appropriate callbacks are fired when the server's connection
// fails after the transports have connected.
TEST_F(P2PQuicTransportTest, ServerConnectionFailureAfterConnected) {
Initialize();
Connect();
// Close the connection with an internal QUIC error.
client_peer()->quic_transport_delegate()->ExpectCallback(kOnConnectionFailed);
server_peer()->quic_transport_delegate()->ExpectCallback(kOnConnectionFailed);
server_connection()->CloseConnection(
quic::QuicErrorCode::QUIC_INTERNAL_ERROR, "internal error",
quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
RunUntilCallbacksFired();
ExpectClosed();
EXPECT_EQ(1, client_peer()->quic_transport_delegate()->connection_failed());
EXPECT_EQ(1, server_peer()->quic_transport_delegate()->connection_failed());
}
// Tests that closing the connection with no ACK frame does not make any
// difference in the closing procedure.
TEST_F(P2PQuicTransportTest, ConnectionFailureNoAckFrame) {
Initialize();
Connect();
client_peer()->quic_transport_delegate()->ExpectCallback(kOnConnectionFailed);
server_peer()->quic_transport_delegate()->ExpectCallback(kOnConnectionFailed);
client_connection()->CloseConnection(
quic::QuicErrorCode::QUIC_INTERNAL_ERROR, "internal error",
quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET_WITH_NO_ACK);
RunUntilCallbacksFired();
ExpectClosed();
EXPECT_EQ(1, client_peer()->quic_transport_delegate()->connection_failed());
EXPECT_EQ(1, server_peer()->quic_transport_delegate()->connection_failed());
}
// Tests that a silent failure will only close on one side.
TEST_F(P2PQuicTransportTest, ConnectionSilentFailure) {
Initialize();
Connect();
client_peer()->quic_transport_delegate()->ExpectCallback(kOnConnectionFailed);
client_connection()->CloseConnection(
quic::QuicErrorCode::QUIC_INTERNAL_ERROR, "internal error",
quic::ConnectionCloseBehavior::SILENT_CLOSE);
RunUntilCallbacksFired();
EXPECT_TRUE(IsClientClosed());
EXPECT_EQ(1, client_peer()->quic_transport_delegate()->connection_failed());
EXPECT_FALSE(IsServerClosed());
EXPECT_EQ(0, server_peer()->quic_transport_delegate()->connection_failed());
}
} // namespace blink
...@@ -422,6 +422,7 @@ _CONFIG = [ ...@@ -422,6 +422,7 @@ _CONFIG = [
'cricket::.*', 'cricket::.*',
'rtc::.+', 'rtc::.+',
'webrtc::.+', 'webrtc::.+',
'quic::.+',
] ]
}, },
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment