blob: c889ea9d2a2e83cd9dc2cd001dbfc179e48d733c [file] [log] [blame]
// 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 class TransportCallbackType {
kNone,
kOnRemoteStopped,
kOnConnectionFailed,
kOnConnected,
kOnStream
};
// The types of callbacks that can be fired on a P2PQuicStream::Delegate.
enum class StreamCallbackType { kNone, kOnRemoteReset, kOnRemoteFinish };
// The QuicStreamDelegate 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.
class QuicStreamDelegateForTesting final : public P2PQuicStream::Delegate {
public:
~QuicStreamDelegateForTesting() override {}
void OnRemoteReset() override {
if (callback_type_expected_ == StreamCallbackType::kOnRemoteReset) {
callback_type_expected_ = StreamCallbackType::kNone;
}
remote_reset_count_++;
};
void OnRemoteFinish() override {
if (callback_type_expected_ == StreamCallbackType::kOnRemoteFinish) {
callback_type_expected_ = StreamCallbackType::kNone;
}
remote_finish_count_++;
};
int remote_reset_count() { return remote_reset_count_; }
int remote_finish_count() { return remote_finish_count_; }
// Sets the type of callback expected to be called.
void ExpectCallback(StreamCallbackType callback_type) {
callback_type_expected_ = callback_type;
}
// Returns if we are expecting a callback that hasn't been fired yet.
bool IsExpectingCallback() const {
return callback_type_expected_ != StreamCallbackType::kNone;
}
private:
int remote_reset_count_ = 0;
int remote_finish_count_ = 0;
StreamCallbackType callback_type_expected_ = StreamCallbackType::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_count_++;
}
void OnConnectionFailed(const std::string& error_details,
bool from_remote) override {
if (callback_type_expected_ == TransportCallbackType::kOnConnectionFailed) {
callback_type_expected_ = TransportCallbackType::kNone;
}
connection_failed_count_++;
}
void OnConnected() override {
if (callback_type_expected_ == TransportCallbackType::kOnConnected) {
callback_type_expected_ = TransportCallbackType::kNone;
}
connected_count_++;
}
// We store the remotely created stream.
void OnStream(P2PQuicStream* stream) override {
if (callback_type_expected_ == TransportCallbackType::kOnStream) {
callback_type_expected_ = TransportCallbackType::kNone;
}
streams_.push_back(static_cast<P2PQuicStreamImpl*>(stream));
on_stream_count_++;
}
int stopped_count() const { return stopped_count_; }
int connection_failed_count() const { return connection_failed_count_; }
int connected_count() const { return connected_count_; }
int on_stream_count() const { return on_stream_count_; }
// Sets the type of callback expected to be called.
void ExpectCallback(TransportCallbackType callback_type) {
callback_type_expected_ = callback_type;
}
// Returns if we are expecting a callback that hasn't been fired yet.
bool IsExpectingCallback() const {
return callback_type_expected_ != TransportCallbackType::kNone;
}
std::vector<P2PQuicStreamImpl*> streams() const { return streams_; }
private:
TransportCallbackType callback_type_expected_ = TransportCallbackType::kNone;
int stopped_count_ = 0;
int connection_failed_count_ = 0;
int connected_count_ = 0;
int on_stream_count_ = 0;
// The delegates created for each stream as a result of the remote side
// creating streams and sending data (triggering OnStream). P2PQuicStreamsImpl
// are owned by the P2PQuicTransport.
std::vector<P2PQuicStreamImpl*> streams_;
};
// 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. This also tests
// P2PQuicStreams for test cases that involve two streams connected between
// separate endpoints. This is because the P2PQuicStream is highly coupled to
// the P2PQuicSession for communicating between endpoints, so we would like to
// test it with the real session object.
//
// 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() ||
ExpectingStreamCallback()) {
// 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();
}
}
bool ExpectingStreamCallback() {
return streams_setup_ && (client_stream_delegate_->IsExpectingCallback() ||
server_stream_delegate_->IsExpectingCallback());
}
// 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(
TransportCallbackType::kOnConnected);
server_peer_->quic_transport_delegate()->ExpectCallback(
TransportCallbackType::kOnConnected);
StartHandshake();
RunUntilCallbacksFired();
ExpectSecureConnection();
}
// Creates a P2PQuicStreamImpl on both the client and server side that are
// connected to each other.
void SetupConnectedStreams() {
// We must already have a secure connection before streams are created.
ASSERT_TRUE(client_peer_->quic_transport()->IsEncryptionEstablished());
ASSERT_TRUE(server_peer_->quic_transport()->IsEncryptionEstablished());
client_stream_ = client_peer_->quic_transport()->CreateStream();
ASSERT_TRUE(client_stream_);
client_stream_id_ = client_stream_->id();
client_stream_delegate_ = std::make_unique<QuicStreamDelegateForTesting>();
client_stream_->SetDelegate(client_stream_delegate_.get());
// Send some data to trigger the remote side (server side) to get an
// incoming stream.
server_peer_->quic_transport_delegate()->ExpectCallback(
TransportCallbackType::kOnStream);
client_stream_->WriteOrBufferData("hello", false, nullptr);
RunUntilCallbacksFired();
ASSERT_EQ(1u, server_peer_->quic_transport()->GetNumActiveStreams());
ASSERT_EQ(1u, client_peer_->quic_transport()->GetNumActiveStreams());
ASSERT_EQ(1u, server_peer_->quic_transport_delegate()->streams().size());
server_stream_ = server_peer_->quic_transport_delegate()->streams()[0];
ASSERT_TRUE(server_stream_);
server_stream_id_ = server_stream_->id();
server_stream_delegate_ = std::make_unique<QuicStreamDelegateForTesting>();
server_stream_->SetDelegate(server_stream_delegate_.get());
streams_setup_ = true;
}
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_count());
EXPECT_EQ(0, client_peer_->quic_transport_delegate()->stopped_count());
EXPECT_EQ(
0, client_peer_->quic_transport_delegate()->connection_failed_count());
EXPECT_EQ(1, server_peer_->quic_transport_delegate()->connected_count());
EXPECT_EQ(0, server_peer_->quic_transport_delegate()->stopped_count());
EXPECT_EQ(
0, server_peer_->quic_transport_delegate()->connection_failed_count());
}
void ExpectTransportsClosed() {
EXPECT_TRUE(client_peer_->quic_transport()->IsClosed());
EXPECT_TRUE(server_peer_->quic_transport()->IsClosed());
}
void ExpectStreamsClosed() {
ASSERT_TRUE(streams_setup_);
EXPECT_EQ(0u, client_peer_->quic_transport()->GetNumActiveStreams());
EXPECT_TRUE(
client_peer_->quic_transport()->IsClosedStream(client_stream_id_));
EXPECT_EQ(0u, server_peer_->quic_transport()->GetNumActiveStreams());
EXPECT_TRUE(
server_peer()->quic_transport()->IsClosedStream(server_stream_id_));
}
// 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() {
ExpectTransportsClosed();
EXPECT_EQ(0, client_peer_->quic_transport_delegate()->stopped_count());
EXPECT_EQ(
0, client_peer_->quic_transport_delegate()->connection_failed_count());
EXPECT_EQ(1, server_peer_->quic_transport_delegate()->stopped_count());
EXPECT_EQ(
0, server_peer_->quic_transport_delegate()->connection_failed_count());
}
// Tests that the callbacks were appropriately called after the server
// stops the connection. Only the client should receive the OnRemoteStopped()
// callback.
void ExpectServerStopped() {
ExpectTransportsClosed();
EXPECT_EQ(1, client_peer_->quic_transport_delegate()->stopped_count());
EXPECT_EQ(
0, client_peer_->quic_transport_delegate()->connection_failed_count());
EXPECT_EQ(0, server_peer_->quic_transport_delegate()->stopped_count());
EXPECT_EQ(
0, server_peer_->quic_transport_delegate()->connection_failed_count());
}
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();
}
P2PQuicStreamImpl* server_stream() { return server_stream_; }
P2PQuicStreamImpl* client_stream() { return client_stream_; }
quic::QuicStreamId server_stream_id() { return server_stream_id_; }
quic::QuicStreamId client_stream_id() { return client_stream_id_; }
QuicStreamDelegateForTesting* server_stream_delegate() {
return server_stream_delegate_.get();
}
QuicStreamDelegateForTesting* client_stream_delegate() {
return client_stream_delegate_.get();
}
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_;
// Stream objects, which are created with SetupConnectedStream().
bool streams_setup_ = false;
std::unique_ptr<QuicStreamDelegateForTesting> client_stream_delegate_;
std::unique_ptr<QuicStreamDelegateForTesting> server_stream_delegate_;
// The P2PQuicStreamImpls are owned by the P2PQuicTransport.
P2PQuicStreamImpl* client_stream_ = nullptr;
P2PQuicStreamImpl* server_stream_ = nullptr;
// We cache the values before the streams are potentially closed and deleted.
quic::QuicStreamId server_stream_id_;
quic::QuicStreamId client_stream_id_;
};
// 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(
TransportCallbackType::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(
TransportCallbackType::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(
TransportCallbackType::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(
TransportCallbackType::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(
TransportCallbackType::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(
TransportCallbackType::kOnConnectionFailed);
server_peer()->quic_transport_delegate()->ExpectCallback(
TransportCallbackType::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(
TransportCallbackType::kOnConnectionFailed);
server_peer()->quic_transport_delegate()->ExpectCallback(
TransportCallbackType::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(
TransportCallbackType::kOnConnectionFailed);
server_peer()->quic_transport_delegate()->ExpectCallback(
TransportCallbackType::kOnConnectionFailed);
StartHandshake();
RunUntilCallbacksFired();
EXPECT_EQ(
1, client_peer()->quic_transport_delegate()->connection_failed_count());
EXPECT_EQ(
1, server_peer()->quic_transport_delegate()->connection_failed_count());
ExpectConnectionNotEstablished();
ExpectTransportsClosed();
}
// 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(
TransportCallbackType::kOnConnectionFailed);
server_peer()->quic_transport_delegate()->ExpectCallback(
TransportCallbackType::kOnConnectionFailed);
client_connection()->CloseConnection(
quic::QuicErrorCode::QUIC_INTERNAL_ERROR, "internal error",
quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
RunUntilCallbacksFired();
ExpectTransportsClosed();
EXPECT_EQ(
1, client_peer()->quic_transport_delegate()->connection_failed_count());
EXPECT_EQ(
1, server_peer()->quic_transport_delegate()->connection_failed_count());
}
// 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(
TransportCallbackType::kOnConnectionFailed);
server_peer()->quic_transport_delegate()->ExpectCallback(
TransportCallbackType::kOnConnectionFailed);
server_connection()->CloseConnection(
quic::QuicErrorCode::QUIC_INTERNAL_ERROR, "internal error",
quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
RunUntilCallbacksFired();
ExpectTransportsClosed();
EXPECT_EQ(
1, client_peer()->quic_transport_delegate()->connection_failed_count());
EXPECT_EQ(
1, server_peer()->quic_transport_delegate()->connection_failed_count());
}
// 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(
TransportCallbackType::kOnConnectionFailed);
server_peer()->quic_transport_delegate()->ExpectCallback(
TransportCallbackType::kOnConnectionFailed);
client_connection()->CloseConnection(
quic::QuicErrorCode::QUIC_INTERNAL_ERROR, "internal error",
quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET_WITH_NO_ACK);
RunUntilCallbacksFired();
ExpectTransportsClosed();
EXPECT_EQ(
1, client_peer()->quic_transport_delegate()->connection_failed_count());
EXPECT_EQ(
1, server_peer()->quic_transport_delegate()->connection_failed_count());
}
// Tests that a silent failure will only close on one side.
TEST_F(P2PQuicTransportTest, ConnectionSilentFailure) {
Initialize();
Connect();
client_peer()->quic_transport_delegate()->ExpectCallback(
TransportCallbackType::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_count());
EXPECT_FALSE(IsServerClosed());
EXPECT_EQ(
0, server_peer()->quic_transport_delegate()->connection_failed_count());
}
// Tests that the client transport can create a stream and an incoming stream
// will be created on the remote server.
TEST_F(P2PQuicTransportTest, ClientCreatesStream) {
Initialize();
Connect();
P2PQuicStreamImpl* client_stream =
client_peer()->quic_transport()->CreateStream();
RunCurrentTasks();
ASSERT_TRUE(client_stream);
EXPECT_TRUE(client_peer()->quic_transport()->HasOpenDynamicStreams());
EXPECT_EQ(0, server_peer()->quic_transport_delegate()->on_stream_count());
EXPECT_FALSE(server_peer()->quic_transport()->HasOpenDynamicStreams());
// After sending data across it will trigger a stream to be created on the
// server side.
server_peer()->quic_transport_delegate()->ExpectCallback(
TransportCallbackType::kOnStream);
client_stream->WriteOrBufferData("hello", false, nullptr);
RunUntilCallbacksFired();
EXPECT_EQ(1, server_peer()->quic_transport_delegate()->on_stream_count());
EXPECT_TRUE(server_peer()->quic_transport()->HasOpenDynamicStreams());
}
// Tests that the server transport can create a stream and an incoming stream
// will be created on the remote client.
TEST_F(P2PQuicTransportTest, ServerCreatesStream) {
Initialize();
Connect();
P2PQuicStreamImpl* server_stream =
server_peer()->quic_transport()->CreateStream();
RunCurrentTasks();
ASSERT_TRUE(server_stream);
EXPECT_TRUE(server_peer()->quic_transport()->HasOpenDynamicStreams());
EXPECT_EQ(0, client_peer()->quic_transport_delegate()->on_stream_count());
EXPECT_FALSE(client_peer()->quic_transport()->HasOpenDynamicStreams());
// After sending data across it will trigger a stream to be created on the
// client side.
client_peer()->quic_transport_delegate()->ExpectCallback(
TransportCallbackType::kOnStream);
server_stream->WriteOrBufferData("hello", false, nullptr);
RunUntilCallbacksFired();
EXPECT_EQ(1, client_peer()->quic_transport_delegate()->on_stream_count());
EXPECT_TRUE(client_peer()->quic_transport()->HasOpenDynamicStreams());
}
// Tests that when the client transport calls Stop() it closes its outgoing
// stream, which, in turn closes the incoming stream on the server quic
// transport.
TEST_F(P2PQuicTransportTest, ClientClosingConnectionClosesStreams) {
Initialize();
Connect();
SetupConnectedStreams();
client_peer()->quic_transport()->Stop();
RunCurrentTasks();
ExpectTransportsClosed();
ExpectStreamsClosed();
}
// Tests that when the server transport calls Stop() it closes its incoming
// stream, which, in turn closes the outgoing stream on the client quic
// transport.
TEST_F(P2PQuicTransportTest, ServerClosingConnectionClosesStreams) {
Initialize();
Connect();
SetupConnectedStreams();
server_peer()->quic_transport()->Stop();
RunCurrentTasks();
ExpectTransportsClosed();
ExpectStreamsClosed();
}
// Tests that calling Reset() will close both side's streams for reading and
// writing.
TEST_F(P2PQuicTransportTest, ClientStreamReset) {
Initialize();
Connect();
SetupConnectedStreams();
server_stream_delegate()->ExpectCallback(StreamCallbackType::kOnRemoteReset);
client_stream()->Reset();
RunUntilCallbacksFired();
ExpectStreamsClosed();
}
// Tests that calling Reset() will close both side's streams for reading and
// writing.
TEST_F(P2PQuicTransportTest, ServerStreamReset) {
Initialize();
Connect();
SetupConnectedStreams();
client_stream_delegate()->ExpectCallback(StreamCallbackType::kOnRemoteReset);
server_stream()->Reset();
RunUntilCallbacksFired();
ExpectStreamsClosed();
}
// Tests the basic case for calling Finish() on both sides.
TEST_F(P2PQuicTransportTest, StreamFinishHandshake) {
Initialize();
Connect();
SetupConnectedStreams();
server_stream_delegate()->ExpectCallback(StreamCallbackType::kOnRemoteFinish);
client_stream()->Finish();
RunUntilCallbacksFired();
ASSERT_EQ(1u, server_peer()->quic_transport()->GetNumActiveStreams());
ASSERT_EQ(1u, client_peer()->quic_transport()->GetNumActiveStreams());
EXPECT_EQ(0, client_stream_delegate()->remote_finish_count());
EXPECT_TRUE(client_stream()->write_side_closed());
EXPECT_FALSE(client_stream()->reading_stopped());
EXPECT_FALSE(server_stream()->write_side_closed());
EXPECT_TRUE(server_stream()->reading_stopped());
EXPECT_FALSE(
server_peer()->quic_transport()->IsClosedStream(server_stream_id()));
EXPECT_FALSE(
client_peer()->quic_transport()->IsClosedStream(client_stream_id()));
client_stream_delegate()->ExpectCallback(StreamCallbackType::kOnRemoteFinish);
server_stream()->Finish();
RunUntilCallbacksFired();
// This is required so that the client acks the FIN back to the server side
// and the server side removes its zombie streams.
RunCurrentTasks();
ASSERT_EQ(0u, server_peer()->quic_transport()->GetNumActiveStreams());
ASSERT_EQ(0u, client_peer()->quic_transport()->GetNumActiveStreams());
EXPECT_EQ(1, server_stream_delegate()->remote_finish_count());
EXPECT_EQ(1, client_stream_delegate()->remote_finish_count());
EXPECT_TRUE(
server_peer()->quic_transport()->IsClosedStream(server_stream_id()));
EXPECT_TRUE(
client_peer()->quic_transport()->IsClosedStream(client_stream_id()));
}
// Tests that if a Reset() is called after Finish(), both sides close down
// properly.
TEST_F(P2PQuicTransportTest, StreamResetAfterFinish) {
Initialize();
Connect();
SetupConnectedStreams();
server_stream_delegate()->ExpectCallback(StreamCallbackType::kOnRemoteFinish);
client_stream()->Finish();
RunUntilCallbacksFired();
server_stream_delegate()->ExpectCallback(StreamCallbackType::kOnRemoteReset);
client_stream()->Reset();
RunUntilCallbacksFired();
ExpectStreamsClosed();
EXPECT_EQ(0, client_stream_delegate()->remote_reset_count());
}
// Tests that if a Reset() is called after receiving a stream frame with the FIN
// bit set from the remote side, both sides close down properly.
TEST_F(P2PQuicTransportTest, StreamResetAfterRemoteFinish) {
Initialize();
Connect();
SetupConnectedStreams();
server_stream_delegate()->ExpectCallback(StreamCallbackType::kOnRemoteFinish);
client_stream()->Finish();
RunUntilCallbacksFired();
client_stream_delegate()->ExpectCallback(StreamCallbackType::kOnRemoteReset);
// The server stream has received its FIN bit from the remote side, and
// responds with a Reset() to close everything down.
server_stream()->Reset();
RunUntilCallbacksFired();
ExpectStreamsClosed();
EXPECT_EQ(0, server_stream_delegate()->remote_reset_count());
}
// The following unit tests are more isolated to the P2PQuicStreamImpl
// implementation. They only test a stream's behavior on one side (not any
// interactions between two connected streams).
TEST_F(P2PQuicTransportTest, StreamFinishSendsFinAndCanNoLongerWrite) {
Initialize();
Connect();
P2PQuicStreamImpl* stream = client_peer()->quic_transport()->CreateStream();
stream->Finish();
EXPECT_TRUE(stream->fin_sent());
EXPECT_TRUE(stream->write_side_closed());
EXPECT_FALSE(stream->reading_stopped());
}
TEST_F(P2PQuicTransportTest, StreamResetSendsRstAndBecomesClosed) {
Initialize();
Connect();
P2PQuicStreamImpl* stream = client_peer()->quic_transport()->CreateStream();
quic::QuicStreamId stream_id = stream->id();
stream->Reset();
EXPECT_TRUE(client_peer()->quic_transport()->IsClosedStream(stream_id));
}
// Tests that when a stream receives a stream frame with the FIN bit set it
// will fire the appropriate callback and close the stream for reading.
TEST_F(P2PQuicTransportTest, StreamOnStreamFrameWithFin) {
Initialize();
Connect();
P2PQuicStreamImpl* stream = client_peer()->quic_transport()->CreateStream();
QuicStreamDelegateForTesting stream_delegate;
stream->SetDelegate(&stream_delegate);
quic::QuicStreamFrame fin_frame(stream->id(), /*fin=*/true, 0, 0);
stream->OnStreamFrame(fin_frame);
EXPECT_EQ(1, stream_delegate.remote_finish_count());
EXPECT_TRUE(stream->reading_stopped());
EXPECT_FALSE(stream->write_side_closed());
}
// Tests that when a stream receives a stream frame with the FIN bit set after
// it has called Finish(), then the stream will close.
TEST_F(P2PQuicTransportTest, StreamClosedAfterReceivesFin) {
Initialize();
Connect();
P2PQuicStreamImpl* stream = client_peer()->quic_transport()->CreateStream();
quic::QuicStreamId stream_id = stream->id();
QuicStreamDelegateForTesting stream_delegate;
stream->SetDelegate(&stream_delegate);
stream->Finish();
EXPECT_FALSE(client_peer()->quic_transport()->IsClosedStream(stream_id));
quic::QuicStreamFrame fin_frame(stream->id(), /*fin=*/true, 0, 0);
stream->OnStreamFrame(fin_frame);
EXPECT_TRUE(client_peer()->quic_transport()->IsClosedStream(stream_id));
}
// Tests that when a stream calls Finish() after receiving a stream frame with
// the FIN bit then the stream will close.
TEST_F(P2PQuicTransportTest, StreamClosedAfterFinish) {
Initialize();
Connect();
P2PQuicStreamImpl* stream = client_peer()->quic_transport()->CreateStream();
quic::QuicStreamId stream_id = stream->id();
QuicStreamDelegateForTesting stream_delegate;
stream->SetDelegate(&stream_delegate);
quic::QuicStreamFrame fin_frame(stream->id(), /*fin=*/true, 0, 0);
stream->OnStreamFrame(fin_frame);
EXPECT_FALSE(client_peer()->quic_transport()->IsClosedStream(stream_id));
stream->Finish();
EXPECT_TRUE(client_peer()->quic_transport()->IsClosedStream(stream_id));
}
// Tests that when a stream receives a RST_STREAM frame it will fire the
// appropriate callback and the stream will become closed.
TEST_F(P2PQuicTransportTest, StreamClosedAfterReceivingReset) {
Initialize();
Connect();
P2PQuicStreamImpl* stream = client_peer()->quic_transport()->CreateStream();
quic::QuicStreamId stream_id = stream->id();
QuicStreamDelegateForTesting stream_delegate;
stream->SetDelegate(&stream_delegate);
quic::QuicRstStreamFrame rst_frame(quic::kInvalidControlFrameId, stream_id,
quic::QUIC_STREAM_CANCELLED, 0);
stream->OnStreamReset(rst_frame);
EXPECT_EQ(1, stream_delegate.remote_reset_count());
EXPECT_TRUE(client_peer()->quic_transport()->IsClosedStream(stream_id));
}
} // namespace blink