
To allow end-to-end QuicDataChannel usage with a PeerConnection, RTCConfiguration has been modified to include a boolean for whether to do QUIC, since negotiation of QUIC is not implemented. If one peer does QUIC, then it will be assumed that the other peer must do QUIC or the connection will fail. PeerConnection has been modified to create data channels of type QuicDataChannel when the peer wants to do QUIC. WebRtcSession has ben modified to use a QuicDataTransport instead of a DtlsTransportChannelWrapper/DataChannel when QUIC should be used QuicDataTransport implements the generic functions of BaseChannel to manage the QuicTransportChannel. Review-Url: https://codereview.webrtc.org/2166873002 Cr-Commit-Position: refs/heads/master@{#13645}
351 lines
14 KiB
C++
351 lines
14 KiB
C++
/*
|
|
* Copyright 2016 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#include "webrtc/api/quicdatatransport.h"
|
|
|
|
#include <memory>
|
|
#include <set>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#include "webrtc/api/quicdatachannel.h"
|
|
#include "webrtc/base/bytebuffer.h"
|
|
#include "webrtc/base/gunit.h"
|
|
#include "webrtc/p2p/base/faketransportcontroller.h"
|
|
#include "webrtc/p2p/quic/quictransportchannel.h"
|
|
#include "webrtc/p2p/quic/reliablequicstream.h"
|
|
|
|
using webrtc::DataBuffer;
|
|
using webrtc::DataChannelInit;
|
|
using webrtc::DataChannelInterface;
|
|
using webrtc::DataChannelObserver;
|
|
using webrtc::QuicDataChannel;
|
|
using webrtc::QuicDataTransport;
|
|
using cricket::FakeTransportChannel;
|
|
using cricket::FakeTransportController;
|
|
using cricket::QuicTransportChannel;
|
|
using cricket::ReliableQuicStream;
|
|
|
|
namespace {
|
|
|
|
// Timeout for asynchronous operations.
|
|
static const int kTimeoutMs = 1000; // milliseconds
|
|
static const char kTransportName[] = "data";
|
|
|
|
// FakeObserver receives messages from the data channel.
|
|
class FakeObserver : public DataChannelObserver {
|
|
public:
|
|
FakeObserver() {}
|
|
|
|
void OnStateChange() override {}
|
|
|
|
void OnBufferedAmountChange(uint64_t previous_amount) override {}
|
|
|
|
void OnMessage(const webrtc::DataBuffer& buffer) override {
|
|
messages_.push_back(std::string(buffer.data.data<char>(), buffer.size()));
|
|
}
|
|
|
|
const std::vector<std::string>& messages() const { return messages_; }
|
|
|
|
size_t messages_received() const { return messages_.size(); }
|
|
|
|
private:
|
|
std::vector<std::string> messages_;
|
|
};
|
|
|
|
// A peer who uses a QUIC transport channel and fake ICE transport channel to
|
|
// send or receive data.
|
|
class QuicDataTransportPeer {
|
|
public:
|
|
QuicDataTransportPeer()
|
|
: fake_transport_controller_(new FakeTransportController()),
|
|
quic_data_transport_(rtc::Thread::Current(),
|
|
rtc::Thread::Current(),
|
|
rtc::Thread::Current(),
|
|
fake_transport_controller_.get()) {
|
|
fake_transport_controller_->use_quic();
|
|
quic_data_transport_.set_content_name("data");
|
|
quic_data_transport_.SetTransport(kTransportName);
|
|
ice_transport_channel_ = static_cast<FakeTransportChannel*>(
|
|
quic_data_transport_.quic_transport_channel()->ice_transport_channel());
|
|
ice_transport_channel_->SetAsync(true);
|
|
}
|
|
|
|
void GenerateCertificateAndFingerprint() {
|
|
rtc::scoped_refptr<rtc::RTCCertificate> local_cert =
|
|
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
|
|
rtc::SSLIdentity::Generate("cert_name", rtc::KT_DEFAULT)));
|
|
quic_data_transport_.quic_transport_channel()->SetLocalCertificate(
|
|
local_cert);
|
|
local_fingerprint_.reset(CreateFingerprint(local_cert.get()));
|
|
}
|
|
|
|
// Connects |ice_transport_channel_| to that of the other peer.
|
|
void Connect(QuicDataTransportPeer* other_peer) {
|
|
ice_transport_channel_->SetDestination(other_peer->ice_transport_channel_);
|
|
}
|
|
|
|
std::unique_ptr<rtc::SSLFingerprint>& local_fingerprint() {
|
|
return local_fingerprint_;
|
|
}
|
|
|
|
QuicTransportChannel* quic_transport_channel() {
|
|
return quic_data_transport_.quic_transport_channel();
|
|
}
|
|
|
|
// Write a messge directly to the ReliableQuicStream.
|
|
void WriteMessage(int data_channel_id,
|
|
uint64_t message_id,
|
|
const std::string& message) {
|
|
ReliableQuicStream* stream =
|
|
quic_data_transport_.quic_transport_channel()->CreateQuicStream();
|
|
rtc::CopyOnWriteBuffer payload;
|
|
webrtc::WriteQuicDataChannelMessageHeader(data_channel_id, message_id,
|
|
&payload);
|
|
stream->Write(payload.data<char>(), payload.size(), false);
|
|
stream->Write(message.data(), message.size(), true);
|
|
}
|
|
|
|
rtc::scoped_refptr<DataChannelInterface> CreateDataChannel(
|
|
const DataChannelInit* config) {
|
|
return quic_data_transport_.CreateDataChannel("testing", config);
|
|
}
|
|
|
|
QuicDataTransport* quic_data_transport() { return &quic_data_transport_; }
|
|
|
|
private:
|
|
// Creates a fingerprint from a certificate.
|
|
rtc::SSLFingerprint* CreateFingerprint(rtc::RTCCertificate* cert) {
|
|
std::string digest_algorithm;
|
|
cert->ssl_certificate().GetSignatureDigestAlgorithm(&digest_algorithm);
|
|
std::unique_ptr<rtc::SSLFingerprint> fingerprint(
|
|
rtc::SSLFingerprint::Create(digest_algorithm, cert->identity()));
|
|
return fingerprint.release();
|
|
}
|
|
|
|
std::unique_ptr<FakeTransportController> fake_transport_controller_;
|
|
QuicDataTransport quic_data_transport_;
|
|
FakeTransportChannel* ice_transport_channel_;
|
|
std::unique_ptr<rtc::SSLFingerprint> local_fingerprint_;
|
|
};
|
|
|
|
class QuicDataTransportTest : public testing::Test {
|
|
public:
|
|
QuicDataTransportTest() {}
|
|
|
|
void ConnectTransportChannels() {
|
|
SetCryptoParameters();
|
|
peer1_.Connect(&peer2_);
|
|
ASSERT_TRUE_WAIT(peer1_.quic_transport_channel()->writable() &&
|
|
peer2_.quic_transport_channel()->writable(),
|
|
kTimeoutMs);
|
|
}
|
|
|
|
// Sets crypto parameters required for the QUIC handshake.
|
|
void SetCryptoParameters() {
|
|
peer1_.GenerateCertificateAndFingerprint();
|
|
peer2_.GenerateCertificateAndFingerprint();
|
|
|
|
peer1_.quic_transport_channel()->SetSslRole(rtc::SSL_CLIENT);
|
|
peer2_.quic_transport_channel()->SetSslRole(rtc::SSL_SERVER);
|
|
|
|
std::unique_ptr<rtc::SSLFingerprint>& peer1_fingerprint =
|
|
peer1_.local_fingerprint();
|
|
std::unique_ptr<rtc::SSLFingerprint>& peer2_fingerprint =
|
|
peer2_.local_fingerprint();
|
|
|
|
peer1_.quic_transport_channel()->SetRemoteFingerprint(
|
|
peer2_fingerprint->algorithm,
|
|
reinterpret_cast<const uint8_t*>(peer2_fingerprint->digest.data()),
|
|
peer2_fingerprint->digest.size());
|
|
peer2_.quic_transport_channel()->SetRemoteFingerprint(
|
|
peer1_fingerprint->algorithm,
|
|
reinterpret_cast<const uint8_t*>(peer1_fingerprint->digest.data()),
|
|
peer1_fingerprint->digest.size());
|
|
}
|
|
|
|
protected:
|
|
QuicDataTransportPeer peer1_;
|
|
QuicDataTransportPeer peer2_;
|
|
};
|
|
|
|
// Tests creation and destruction of data channels.
|
|
TEST_F(QuicDataTransportTest, CreateAndDestroyDataChannels) {
|
|
QuicDataTransport* quic_data_transport = peer2_.quic_data_transport();
|
|
EXPECT_FALSE(quic_data_transport->HasDataChannels());
|
|
for (int data_channel_id = 0; data_channel_id < 5; ++data_channel_id) {
|
|
EXPECT_FALSE(quic_data_transport->HasDataChannel(data_channel_id));
|
|
webrtc::DataChannelInit config;
|
|
config.id = data_channel_id;
|
|
rtc::scoped_refptr<DataChannelInterface> data_channel =
|
|
peer2_.CreateDataChannel(&config);
|
|
EXPECT_NE(nullptr, data_channel);
|
|
EXPECT_EQ(data_channel_id, data_channel->id());
|
|
EXPECT_TRUE(quic_data_transport->HasDataChannel(data_channel_id));
|
|
}
|
|
EXPECT_TRUE(quic_data_transport->HasDataChannels());
|
|
for (int data_channel_id = 0; data_channel_id < 5; ++data_channel_id) {
|
|
quic_data_transport->DestroyDataChannel(data_channel_id);
|
|
EXPECT_FALSE(quic_data_transport->HasDataChannel(data_channel_id));
|
|
}
|
|
EXPECT_FALSE(quic_data_transport->HasDataChannels());
|
|
}
|
|
|
|
// Tests that the QuicDataTransport does not allow creating multiple
|
|
// QuicDataChannels with the same id.
|
|
TEST_F(QuicDataTransportTest, CannotCreateDataChannelsWithSameId) {
|
|
webrtc::DataChannelInit config;
|
|
config.id = 2;
|
|
EXPECT_NE(nullptr, peer2_.CreateDataChannel(&config));
|
|
EXPECT_EQ(nullptr, peer2_.CreateDataChannel(&config));
|
|
}
|
|
|
|
// Tests that any data channels created by the QuicDataTransport are in state
|
|
// kConnecting before the QuicTransportChannel is set, then transition to state
|
|
// kOpen when the transport channel becomes writable.
|
|
TEST_F(QuicDataTransportTest, DataChannelsOpenWhenTransportChannelWritable) {
|
|
webrtc::DataChannelInit config1;
|
|
config1.id = 7;
|
|
rtc::scoped_refptr<DataChannelInterface> data_channel1 =
|
|
peer2_.CreateDataChannel(&config1);
|
|
EXPECT_EQ(webrtc::DataChannelInterface::kConnecting, data_channel1->state());
|
|
EXPECT_EQ(webrtc::DataChannelInterface::kConnecting, data_channel1->state());
|
|
webrtc::DataChannelInit config2;
|
|
config2.id = 14;
|
|
rtc::scoped_refptr<DataChannelInterface> data_channel2 =
|
|
peer2_.CreateDataChannel(&config2);
|
|
EXPECT_EQ(webrtc::DataChannelInterface::kConnecting, data_channel2->state());
|
|
// Existing data channels should open once the transport channel is writable.
|
|
ConnectTransportChannels();
|
|
EXPECT_EQ_WAIT(webrtc::DataChannelInterface::kOpen, data_channel1->state(),
|
|
kTimeoutMs);
|
|
EXPECT_EQ_WAIT(webrtc::DataChannelInterface::kOpen, data_channel2->state(),
|
|
kTimeoutMs);
|
|
// Any data channels created afterwards should start in state kOpen.
|
|
webrtc::DataChannelInit config3;
|
|
config3.id = 21;
|
|
rtc::scoped_refptr<DataChannelInterface> data_channel3 =
|
|
peer2_.CreateDataChannel(&config3);
|
|
EXPECT_EQ(webrtc::DataChannelInterface::kOpen, data_channel3->state());
|
|
}
|
|
|
|
// Tests that the QuicTransport dispatches messages for one QuicDataChannel.
|
|
TEST_F(QuicDataTransportTest, ReceiveMessagesForSingleDataChannel) {
|
|
ConnectTransportChannels();
|
|
|
|
int data_channel_id = 1337;
|
|
webrtc::DataChannelInit config;
|
|
config.id = data_channel_id;
|
|
rtc::scoped_refptr<DataChannelInterface> peer2_data_channel =
|
|
peer2_.CreateDataChannel(&config);
|
|
FakeObserver observer;
|
|
peer2_data_channel->RegisterObserver(&observer);
|
|
|
|
uint64_t message1_id = 26u;
|
|
peer1_.WriteMessage(data_channel_id, message1_id, "Testing");
|
|
ASSERT_EQ_WAIT(1, observer.messages_received(), kTimeoutMs);
|
|
EXPECT_EQ("Testing", observer.messages()[0]);
|
|
|
|
uint64_t message2_id = 402u;
|
|
peer1_.WriteMessage(data_channel_id, message2_id, "Hello, World!");
|
|
ASSERT_EQ_WAIT(2, observer.messages_received(), kTimeoutMs);
|
|
EXPECT_EQ("Hello, World!", observer.messages()[1]);
|
|
|
|
uint64_t message3_id = 100260415u;
|
|
peer1_.WriteMessage(data_channel_id, message3_id, "Third message");
|
|
ASSERT_EQ_WAIT(3, observer.messages_received(), kTimeoutMs);
|
|
EXPECT_EQ("Third message", observer.messages()[2]);
|
|
}
|
|
|
|
// Tests that the QuicTransport dispatches messages to the correct data channel
|
|
// when multiple are in use.
|
|
TEST_F(QuicDataTransportTest, ReceiveMessagesForMultipleDataChannels) {
|
|
ConnectTransportChannels();
|
|
|
|
std::vector<rtc::scoped_refptr<DataChannelInterface>> data_channels;
|
|
for (int data_channel_id = 0; data_channel_id < 5; ++data_channel_id) {
|
|
webrtc::DataChannelInit config;
|
|
config.id = data_channel_id;
|
|
data_channels.push_back(peer2_.CreateDataChannel(&config));
|
|
}
|
|
|
|
for (int data_channel_id = 0; data_channel_id < 5; ++data_channel_id) {
|
|
uint64_t message1_id = 48023u;
|
|
FakeObserver observer;
|
|
DataChannelInterface* peer2_data_channel =
|
|
data_channels[data_channel_id].get();
|
|
peer2_data_channel->RegisterObserver(&observer);
|
|
peer1_.WriteMessage(data_channel_id, message1_id, "Testing");
|
|
ASSERT_EQ_WAIT(1, observer.messages_received(), kTimeoutMs);
|
|
EXPECT_EQ("Testing", observer.messages()[0]);
|
|
|
|
uint64_t message2_id = 1372643095u;
|
|
peer1_.WriteMessage(data_channel_id, message2_id, "Hello, World!");
|
|
ASSERT_EQ_WAIT(2, observer.messages_received(), kTimeoutMs);
|
|
EXPECT_EQ("Hello, World!", observer.messages()[1]);
|
|
}
|
|
}
|
|
|
|
// Tests end-to-end that both peers can use multiple QuicDataChannels to
|
|
// send/receive messages using a QuicDataTransport.
|
|
TEST_F(QuicDataTransportTest, EndToEndSendReceiveMessages) {
|
|
ConnectTransportChannels();
|
|
|
|
std::vector<rtc::scoped_refptr<DataChannelInterface>> peer1_data_channels;
|
|
std::vector<rtc::scoped_refptr<DataChannelInterface>> peer2_data_channels;
|
|
|
|
for (int data_channel_id = 0; data_channel_id < 5; ++data_channel_id) {
|
|
webrtc::DataChannelInit config;
|
|
config.id = data_channel_id;
|
|
peer1_data_channels.push_back(peer1_.CreateDataChannel(&config));
|
|
peer2_data_channels.push_back(peer2_.CreateDataChannel(&config));
|
|
}
|
|
|
|
for (int data_channel_id = 0; data_channel_id < 5; ++data_channel_id) {
|
|
DataChannelInterface* peer1_data_channel =
|
|
peer1_data_channels[data_channel_id].get();
|
|
FakeObserver observer1;
|
|
peer1_data_channel->RegisterObserver(&observer1);
|
|
DataChannelInterface* peer2_data_channel =
|
|
peer2_data_channels[data_channel_id].get();
|
|
FakeObserver observer2;
|
|
peer2_data_channel->RegisterObserver(&observer2);
|
|
|
|
peer1_data_channel->Send(webrtc::DataBuffer("Peer 1 message 1"));
|
|
ASSERT_EQ_WAIT(1, observer2.messages_received(), kTimeoutMs);
|
|
EXPECT_EQ("Peer 1 message 1", observer2.messages()[0]);
|
|
|
|
peer1_data_channel->Send(webrtc::DataBuffer("Peer 1 message 2"));
|
|
ASSERT_EQ_WAIT(2, observer2.messages_received(), kTimeoutMs);
|
|
EXPECT_EQ("Peer 1 message 2", observer2.messages()[1]);
|
|
|
|
peer2_data_channel->Send(webrtc::DataBuffer("Peer 2 message 1"));
|
|
ASSERT_EQ_WAIT(1, observer1.messages_received(), kTimeoutMs);
|
|
EXPECT_EQ("Peer 2 message 1", observer1.messages()[0]);
|
|
|
|
peer2_data_channel->Send(webrtc::DataBuffer("Peer 2 message 2"));
|
|
ASSERT_EQ_WAIT(2, observer1.messages_received(), kTimeoutMs);
|
|
EXPECT_EQ("Peer 2 message 2", observer1.messages()[1]);
|
|
}
|
|
}
|
|
|
|
// Tests that SetTransport returns false when setting a transport that is not
|
|
// equivalent to the one already set.
|
|
TEST_F(QuicDataTransportTest, SetTransportReturnValue) {
|
|
QuicDataTransport* quic_data_transport = peer1_.quic_data_transport();
|
|
// Ignore the same transport name.
|
|
EXPECT_TRUE(quic_data_transport->SetTransport(kTransportName));
|
|
// Return false when setting a different transport name.
|
|
EXPECT_FALSE(quic_data_transport->SetTransport("another transport name"));
|
|
}
|
|
|
|
} // namespace
|