Remove QUIC transport/data channel

Originally, the idea was to implement QUIC data channels as a
PeerConnection API. Now, the effort has shifted to implementing it as a
part of ORTC which will live in Chromium. Since this code has not been
maintained and is not currently being used, remove it to reduce
maintenance overhead while a copy will be retained in the Git history.

Bug: webrtc:8385
Change-Id: I2719c007a0de0118b67d41a425f900b66c52f65a
Reviewed-on: https://webrtc-review.googlesource.com/14100
Reviewed-by: Patrik Höglund <phoglund@webrtc.org>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Reviewed-by: Zhi Huang <zhihuang@webrtc.org>
Commit-Queue: Steve Anton <steveanton@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20412}
This commit is contained in:
Steve Anton 2017-10-23 14:44:03 -07:00 committed by Commit Bot
parent a0b66c7566
commit c4faa9c4e1
32 changed files with 2 additions and 5337 deletions

View File

@ -436,11 +436,6 @@ class PeerConnectionInterface : public rtc::RefCountInterface {
struct cricket::MediaConfig media_config; struct cricket::MediaConfig media_config;
// This doesn't currently work. For a while we were working on adding QUIC
// data channel support to PeerConnection, but decided on a different
// approach, and that code hasn't been updated for a while.
bool enable_quic = false;
// If set to true, only one preferred TURN allocation will be used per // If set to true, only one preferred TURN allocation will be used per
// network interface. UDP is preferred over TCP and IPv6 over IPv4. This // network interface. UDP is preferred over TCP and IPv6 over IPv4. This
// can be used to cut down on the number of candidate pairings. // can be used to cut down on the number of candidate pairings.
@ -469,7 +464,6 @@ class PeerConnectionInterface : public rtc::RefCountInterface {
// (STUN pings), in milliseconds. // (STUN pings), in milliseconds.
rtc::Optional<int> ice_check_min_interval; rtc::Optional<int> ice_check_min_interval;
// ICE Periodic Regathering // ICE Periodic Regathering
// If set, WebRTC will periodically create and propose candidates without // If set, WebRTC will periodically create and propose candidates without
// starting a new ICE generation. The regathering happens continuously with // starting a new ICE generation. The regathering happens continuously with

View File

@ -170,7 +170,7 @@ class CompositeMediaEngine : public MediaEngineInterface {
std::pair<VOICE, VIDEO> engines_; std::pair<VOICE, VIDEO> engines_;
}; };
enum DataChannelType { DCT_NONE = 0, DCT_RTP = 1, DCT_SCTP = 2, DCT_QUIC = 3 }; enum DataChannelType { DCT_NONE = 0, DCT_RTP = 1, DCT_SCTP = 2 };
class DataEngineInterface { class DataEngineInterface {
public: public:

View File

@ -116,28 +116,6 @@ rtc_static_library("rtc_p2p") {
suppressed_configs += [ "//build/config/clang:find_bad_constructs" ] suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
} }
} }
# TODO(deadbeef): code under p2p/quic and base/transportchannelimpl.h should be
# removed. See bugs.webrtc.org/8385.
if (rtc_use_quic) {
deps = [
"//third_party/libquic",
]
sources += [
"base/transportchannelimpl.h",
"quic/quicconnectionhelper.cc",
"quic/quicconnectionhelper.h",
"quic/quicsession.cc",
"quic/quicsession.h",
"quic/quictransport.cc",
"quic/quictransport.h",
"quic/quictransportchannel.cc",
"quic/quictransportchannel.h",
"quic/reliablequicstream.cc",
"quic/reliablequicstream.h",
]
public_deps += [ "//third_party/libquic" ]
}
} }
if (rtc_include_tests) { if (rtc_include_tests) {
@ -197,15 +175,6 @@ if (rtc_include_tests) {
"base/udptransport_unittest.cc", "base/udptransport_unittest.cc",
"client/basicportallocator_unittest.cc", "client/basicportallocator_unittest.cc",
] ]
if (rtc_use_quic) {
sources += [
"quic/quicconnectionhelper_unittest.cc",
"quic/quicsession_unittest.cc",
"quic/quictransport_unittest.cc",
"quic/quictransportchannel_unittest.cc",
"quic/reliablequicstream_unittest.cc",
]
}
deps = [ deps = [
":p2p_test_utils", ":p2p_test_utils",
":rtc_p2p", ":rtc_p2p",

View File

@ -1,86 +0,0 @@
/*
* 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 "p2p/quic/quicconnectionhelper.h"
namespace cricket {
QuicAlarm* QuicConnectionHelper::CreateAlarm(
net::QuicAlarm::Delegate* delegate) {
return new QuicAlarm(GetClock(), thread_,
net::QuicArenaScopedPtr<QuicAlarm::Delegate>(delegate));
}
net::QuicArenaScopedPtr<net::QuicAlarm> QuicConnectionHelper::CreateAlarm(
net::QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
net::QuicConnectionArena* arena) {
return net::QuicArenaScopedPtr<QuicAlarm>(
new QuicAlarm(GetClock(), thread_, std::move(delegate)));
}
QuicAlarm::QuicAlarm(const net::QuicClock* clock,
rtc::Thread* thread,
net::QuicArenaScopedPtr<net::QuicAlarm::Delegate> delegate)
: net::QuicAlarm(std::move(delegate)), clock_(clock), thread_(thread) {}
QuicAlarm::~QuicAlarm() {}
void QuicAlarm::OnMessage(rtc::Message* msg) {
// The alarm may have been cancelled.
if (!deadline().IsInitialized()) {
return;
}
// The alarm may have been re-set to a later time.
if (clock_->Now() < deadline()) {
SetImpl();
return;
}
Fire();
}
int64_t QuicAlarm::GetDelay() const {
return deadline().Subtract(clock_->Now()).ToMilliseconds();
}
void QuicAlarm::SetImpl() {
DCHECK(deadline().IsInitialized());
CancelImpl(); // Unregister if already posted.
int64_t delay_ms = GetDelay();
if (delay_ms < 0) {
delay_ms = 0;
}
thread_->PostDelayed(RTC_FROM_HERE, delay_ms, this);
}
void QuicAlarm::CancelImpl() {
thread_->Clear(this);
}
QuicConnectionHelper::QuicConnectionHelper(rtc::Thread* thread)
: thread_(thread) {}
QuicConnectionHelper::~QuicConnectionHelper() {}
const net::QuicClock* QuicConnectionHelper::GetClock() const {
return &clock_;
}
net::QuicRandom* QuicConnectionHelper::GetRandomGenerator() {
return net::QuicRandom::GetInstance();
}
net::QuicBufferAllocator* QuicConnectionHelper::GetBufferAllocator() {
return &buffer_allocator_;
}
} // namespace cricket

View File

@ -1,72 +0,0 @@
/*
* 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.
*/
#ifndef P2P_QUIC_QUICCONNECTIONHELPER_H_
#define P2P_QUIC_QUICCONNECTIONHELPER_H_
#include "net/quic/crypto/quic_random.h"
#include "net/quic/quic_alarm.h"
#include "net/quic/quic_clock.h"
#include "net/quic/quic_connection.h"
#include "net/quic/quic_simple_buffer_allocator.h"
#include "rtc_base/thread.h"
namespace cricket {
// An alarm which will go off at a scheduled time, and execute the |OnAlarm|
// method of the delegate.
class QuicAlarm : public net::QuicAlarm, public rtc::MessageHandler {
public:
QuicAlarm(const net::QuicClock* clock,
rtc::Thread* thread,
net::QuicArenaScopedPtr<net::QuicAlarm::Delegate> delegate);
~QuicAlarm() override;
// rtc::MessageHandler override.
void OnMessage(rtc::Message* msg) override;
// Helper method to get the delay in ms for posting task.
int64_t GetDelay() const;
protected:
// net::QuicAlarm overrides.
void SetImpl() override;
void CancelImpl() override;
private:
const net::QuicClock* clock_;
rtc::Thread* thread_;
};
// Helper methods for QuicConnection timing and random number generation.
class QuicConnectionHelper : public net::QuicConnectionHelperInterface {
public:
explicit QuicConnectionHelper(rtc::Thread* thread);
~QuicConnectionHelper() override;
// QuicConnectionHelperInterface overrides.
const net::QuicClock* GetClock() const override;
net::QuicRandom* GetRandomGenerator() override;
QuicAlarm* CreateAlarm(net::QuicAlarm::Delegate* delegate) override;
net::QuicArenaScopedPtr<net::QuicAlarm> CreateAlarm(
net::QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
net::QuicConnectionArena* arena) override;
net::QuicBufferAllocator* GetBufferAllocator() override;
private:
net::QuicClock clock_;
net::SimpleBufferAllocator buffer_allocator_;
rtc::Thread* thread_;
};
} // namespace cricket
#endif // P2P_QUIC_QUICCONNECTIONHELPER_H_

View File

@ -1,125 +0,0 @@
/*
* 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 <memory>
#include "p2p/quic/quicconnectionhelper.h"
#include "net/quic/quic_time.h"
#include "rtc_base/gunit.h"
using cricket::QuicAlarm;
using cricket::QuicConnectionHelper;
using net::QuicClock;
using net::QuicTime;
using net::QuicWallTime;
// Clock that can be set to arbitrary times.
class MockClock : public QuicClock {
public:
MockClock() : now_(QuicTime::Zero()) {}
void AdvanceTime(QuicTime::Delta delta) { now_ = now_.Add(delta); }
QuicTime Now() const override { return now_; }
QuicTime ApproximateNow() const override { return now_; }
QuicWallTime WallNow() const override {
return QuicWallTime::FromUNIXSeconds(
now_.Subtract(QuicTime::Zero()).ToSeconds());
}
base::TimeTicks NowInTicks() const {
base::TimeTicks ticks;
return ticks + base::TimeDelta::FromMicroseconds(
now_.Subtract(QuicTime::Zero()).ToMicroseconds());
}
private:
QuicTime now_;
};
// Implements OnAlarm() event which alarm triggers.
class MockAlarmDelegate : public QuicAlarm::Delegate {
public:
MockAlarmDelegate() : fired_(false) {}
void OnAlarm() override { fired_ = true; }
bool fired() const { return fired_; }
void Clear() { fired_ = false; }
private:
bool fired_;
};
class QuicAlarmTest : public ::testing::Test {
public:
QuicAlarmTest()
: delegate_(new MockAlarmDelegate()),
alarm_(new QuicAlarm(
&clock_,
rtc::Thread::Current(),
net::QuicArenaScopedPtr<net::QuicAlarm::Delegate>(delegate_))) {}
// Make the alarm fire after the given microseconds (us). Negative values
// imply the alarm should fire immediately.
void SetTime(int us) {
QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(us);
alarm_->Set(clock_.Now().Add(delta));
}
// Make rtc::Thread::Current() process the next message.
void ProcessNextMessage() { rtc::Thread::Current()->ProcessMessages(0); }
protected:
// Handles event that alarm fires.
MockAlarmDelegate* delegate_;
// Used for setting clock time relative to alarm.
MockClock clock_;
std::unique_ptr<QuicAlarm> alarm_;
};
// Test that the alarm is fired.
TEST_F(QuicAlarmTest, FireAlarm) {
SetTime(-1);
ProcessNextMessage();
ASSERT_TRUE(delegate_->fired());
ASSERT_EQ(QuicTime::Zero(), alarm_->deadline());
}
// Test cancellation of alarm when it is set to fire.
TEST_F(QuicAlarmTest, CancelAlarmAfterSet) {
// TODO(mikescarlett): Test will fail in the future if
// rtc::Thread::PostDelayed calls the delegate synchronously for times <= 0.
// Rewrite this when rtc::Thread is able to use a mock clock.
SetTime(-1);
alarm_->Cancel();
ProcessNextMessage();
ASSERT_FALSE(delegate_->fired());
}
// Test cancellation of alarm when it is not set to fire.
TEST_F(QuicAlarmTest, CancelAlarmBeforeSet) {
alarm_->Cancel();
ProcessNextMessage();
ASSERT_FALSE(delegate_->fired());
}
// Test that timing for posting task is accurate.
TEST_F(QuicAlarmTest, AlarmGetDelay) {
SetTime(1000000);
EXPECT_EQ(1000, alarm_->GetDelay());
clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(300000));
EXPECT_EQ(700, alarm_->GetDelay());
}

View File

@ -1,126 +0,0 @@
/*
* 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 "p2p/quic/quicsession.h"
#include <string>
#include <utility>
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/messagehandler.h"
#include "rtc_base/messagequeue.h"
namespace cricket {
// Default priority for incoming QUIC streams.
// TODO(mikescarlett): Determine if this value is correct.
static const net::SpdyPriority kDefaultPriority = 3;
QuicSession::QuicSession(std::unique_ptr<net::QuicConnection> connection,
const net::QuicConfig& config)
: net::QuicSession(connection.release(), config) {}
QuicSession::~QuicSession() {}
void QuicSession::StartClientHandshake(
net::QuicCryptoClientStream* crypto_stream) {
SetCryptoStream(crypto_stream);
net::QuicSession::Initialize();
crypto_stream->CryptoConnect();
}
void QuicSession::StartServerHandshake(
net::QuicCryptoServerStream* crypto_stream) {
SetCryptoStream(crypto_stream);
net::QuicSession::Initialize();
}
void QuicSession::SetCryptoStream(net::QuicCryptoStream* crypto_stream) {
crypto_stream_.reset(crypto_stream);
}
bool QuicSession::ExportKeyingMaterial(base::StringPiece label,
base::StringPiece context,
size_t result_len,
std::string* result) {
return crypto_stream_->ExportKeyingMaterial(label, context, result_len,
result);
}
void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
net::QuicSession::OnCryptoHandshakeEvent(event);
if (event == HANDSHAKE_CONFIRMED) {
LOG(LS_INFO) << "QuicSession handshake complete";
RTC_DCHECK(IsEncryptionEstablished());
RTC_DCHECK(IsCryptoHandshakeConfirmed());
SignalHandshakeComplete();
}
}
void QuicSession::CloseStream(net::QuicStreamId stream_id) {
if (IsClosedStream(stream_id)) {
// When CloseStream has been called recursively (via
// ReliableQuicStream::OnClose), the stream is already closed so return.
return;
}
write_blocked_streams()->UnregisterStream(stream_id);
net::QuicSession::CloseStream(stream_id);
}
ReliableQuicStream* QuicSession::CreateIncomingDynamicStream(
net::QuicStreamId id) {
ReliableQuicStream* stream = CreateDataStream(id, kDefaultPriority);
if (stream) {
SignalIncomingStream(stream);
}
return stream;
}
ReliableQuicStream* QuicSession::CreateOutgoingDynamicStream(
net::SpdyPriority priority) {
return CreateDataStream(GetNextOutgoingStreamId(), priority);
}
ReliableQuicStream* QuicSession::CreateDataStream(net::QuicStreamId id,
net::SpdyPriority priority) {
if (crypto_stream_ == nullptr || !crypto_stream_->encryption_established()) {
// Encryption not active so no stream created
return nullptr;
}
ReliableQuicStream* stream = new ReliableQuicStream(id, this);
if (stream) {
// Make QuicSession take ownership of the stream.
ActivateStream(stream);
// Register the stream to the QuicWriteBlockedList. |priority| is clamped
// between 0 and 7, with 0 being the highest priority and 7 the lowest
// priority.
write_blocked_streams()->RegisterStream(stream->id(), priority);
}
return stream;
}
void QuicSession::OnConnectionClosed(net::QuicErrorCode error,
const std::string& error_details,
net::ConnectionCloseSource source) {
net::QuicSession::OnConnectionClosed(error, error_details, source);
SignalConnectionClosed(error,
source == net::ConnectionCloseSource::FROM_PEER);
}
bool QuicSession::OnReadPacket(const char* data, size_t data_len) {
net::QuicReceivedPacket packet(data, data_len, clock_.Now());
ProcessUdpPacket(connection()->self_address(), connection()->peer_address(),
packet);
return true;
}
} // namespace cricket

View File

@ -1,97 +0,0 @@
/*
* 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.
*/
#ifndef P2P_QUIC_QUICSESSION_H_
#define P2P_QUIC_QUICSESSION_H_
#include <memory>
#include <string>
#include "net/quic/quic_crypto_client_stream.h"
#include "net/quic/quic_crypto_server_stream.h"
#include "net/quic/quic_crypto_stream.h"
#include "net/quic/quic_session.h"
#include "p2p/quic/reliablequicstream.h"
#include "rtc_base/constructormagic.h"
#include "rtc_base/sigslot.h"
#include "rtc_base/sslidentity.h"
namespace cricket {
// This class provides a QUIC session over peer-to-peer transport that
// negotiates the crypto handshake (using QuicCryptoHandshake) and provides
// reading/writing of data using QUIC packets.
class QuicSession : public net::QuicSession, public sigslot::has_slots<> {
public:
QuicSession(std::unique_ptr<net::QuicConnection> connection,
const net::QuicConfig& config);
~QuicSession() override;
// Initiates client crypto handshake by sending client hello.
void StartClientHandshake(net::QuicCryptoClientStream* crypto_stream);
// Responds to a client who has inititated the crypto handshake.
void StartServerHandshake(net::QuicCryptoServerStream* crypto_stream);
// QuicSession overrides.
net::QuicCryptoStream* GetCryptoStream() override {
return crypto_stream_.get();
}
ReliableQuicStream* CreateOutgoingDynamicStream(
net::SpdyPriority priority) override;
// QuicSession optional overrides.
void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override;
void CloseStream(net::QuicStreamId stream_id) override;
// QuicConnectionVisitorInterface overrides.
void OnConnectionClosed(net::QuicErrorCode error,
const std::string& error_details,
net::ConnectionCloseSource source) override;
// Exports keying material for SRTP.
bool ExportKeyingMaterial(base::StringPiece label,
base::StringPiece context,
size_t result_len,
std::string* result);
// Decrypts an incoming QUIC packet to a data stream.
bool OnReadPacket(const char* data, size_t data_len);
// Called when peers have established forward-secure encryption
sigslot::signal0<> SignalHandshakeComplete;
// Called when connection closes locally, or remotely by peer.
sigslot::signal2<net::QuicErrorCode, bool> SignalConnectionClosed;
// Called when an incoming QUIC stream is created so we can process data
// from it by registering a listener to
// ReliableQuicStream::SignalDataReceived.
sigslot::signal1<ReliableQuicStream*> SignalIncomingStream;
protected:
// Sets the QUIC crypto stream and takes ownership of it.
void SetCryptoStream(net::QuicCryptoStream* crypto_stream);
// QuicSession override.
ReliableQuicStream* CreateIncomingDynamicStream(
net::QuicStreamId id) override;
virtual ReliableQuicStream* CreateDataStream(net::QuicStreamId id,
net::SpdyPriority priority);
private:
std::unique_ptr<net::QuicCryptoStream> crypto_stream_;
net::QuicClock clock_; // For recording packet receipt time
RTC_DISALLOW_COPY_AND_ASSIGN(QuicSession);
};
} // namespace cricket
#endif // P2P_QUIC_QUICSESSION_H_

View File

@ -1,475 +0,0 @@
/*
* 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 "p2p/quic/quicsession.h"
#include <memory>
#include <string>
#include <vector>
#include "net/base/ip_endpoint.h"
#include "net/quic/crypto/crypto_server_config_protobuf.h"
#include "net/quic/crypto/proof_source.h"
#include "net/quic/crypto/proof_verifier.h"
#include "net/quic/crypto/quic_crypto_client_config.h"
#include "net/quic/crypto/quic_crypto_server_config.h"
#include "net/quic/crypto/quic_random.h"
#include "net/quic/quic_crypto_client_stream.h"
#include "net/quic/quic_crypto_server_stream.h"
#include "p2p/base/faketransportcontroller.h"
#include "p2p/quic/quicconnectionhelper.h"
#include "p2p/quic/reliablequicstream.h"
#include "rtc_base/gunit.h"
using net::IPAddress;
using net::IPEndPoint;
using net::PerPacketOptions;
using net::Perspective;
using net::ProofVerifyContext;
using net::ProofVerifyDetails;
using net::QuicByteCount;
using net::QuicClock;
using net::QuicCompressedCertsCache;
using net::QuicConfig;
using net::QuicConnection;
using net::QuicCryptoClientConfig;
using net::QuicCryptoServerConfig;
using net::QuicCryptoClientStream;
using net::QuicCryptoServerStream;
using net::QuicCryptoStream;
using net::QuicErrorCode;
using net::QuicPacketWriter;
using net::QuicRandom;
using net::QuicServerConfigProtobuf;
using net::QuicServerId;
using net::QuicStreamId;
using net::WriteResult;
using net::WriteStatus;
using cricket::FakeTransportChannel;
using cricket::QuicConnectionHelper;
using cricket::QuicSession;
using cricket::ReliableQuicStream;
using cricket::TransportChannel;
using rtc::Thread;
// Timeout for running asynchronous operations within unit tests.
static const int kTimeoutMs = 1000;
// Testing SpdyPriority value for creating outgoing ReliableQuicStream.
static const uint8_t kDefaultPriority = 3;
// TExport keying material function
static const char kExporterLabel[] = "label";
static const char kExporterContext[] = "context";
static const size_t kExporterContextLen = sizeof(kExporterContext);
// Identifies QUIC server session
static const QuicServerId kServerId("www.google.com", 443);
// Used by QuicCryptoServerConfig to provide server credentials, returning a
// canned response equal to |success|.
class FakeProofSource : public net::ProofSource {
public:
explicit FakeProofSource(bool success) : success_(success) {}
// ProofSource override.
bool GetProof(const IPAddress& server_ip,
const std::string& hostname,
const std::string& server_config,
net::QuicVersion quic_version,
base::StringPiece chlo_hash,
bool ecdsa_ok,
scoped_refptr<net::ProofSource::Chain>* out_certs,
std::string* out_signature,
std::string* out_leaf_cert_sct) override {
if (success_) {
std::vector<std::string> certs;
certs.push_back("Required to establish handshake");
*out_certs = new ProofSource::Chain(certs);
*out_signature = "Signature";
*out_leaf_cert_sct = "Time";
}
return success_;
}
private:
// Whether or not obtaining proof source succeeds.
bool success_;
};
// Used by QuicCryptoClientConfig to verify server credentials, returning a
// canned response of QUIC_SUCCESS if |success| is true.
class FakeProofVerifier : public net::ProofVerifier {
public:
explicit FakeProofVerifier(bool success) : success_(success) {}
// ProofVerifier override
net::QuicAsyncStatus VerifyProof(
const std::string& hostname,
const uint16_t port,
const std::string& server_config,
net::QuicVersion quic_version,
base::StringPiece chlo_hash,
const std::vector<std::string>& certs,
const std::string& cert_sct,
const std::string& signature,
const ProofVerifyContext* context,
std::string* error_details,
std::unique_ptr<net::ProofVerifyDetails>* verify_details,
net::ProofVerifierCallback* callback) override {
return success_ ? net::QUIC_SUCCESS : net::QUIC_FAILURE;
}
private:
// Whether or not proof verification succeeds.
bool success_;
};
// Writes QUIC packets to a fake transport channel that simulates a network.
class FakeQuicPacketWriter : public QuicPacketWriter {
public:
explicit FakeQuicPacketWriter(FakeTransportChannel* fake_channel)
: fake_channel_(fake_channel) {}
// Sends packets across the network.
WriteResult WritePacket(const char* buffer,
size_t buf_len,
const IPAddress& self_address,
const IPEndPoint& peer_address,
PerPacketOptions* options) override {
rtc::PacketOptions packet_options;
int rv = fake_channel_->SendPacket(buffer, buf_len, packet_options, 0);
net::WriteStatus status;
if (rv > 0) {
status = net::WRITE_STATUS_OK;
} else if (fake_channel_->GetError() == EWOULDBLOCK) {
status = net::WRITE_STATUS_BLOCKED;
} else {
status = net::WRITE_STATUS_ERROR;
}
return net::WriteResult(status, rv);
}
// Returns true if the writer buffers and subsequently rewrites data
// when an attempt to write results in the underlying socket becoming
// write blocked.
bool IsWriteBlockedDataBuffered() const override { return true; }
// Returns true if the network socket is not writable.
bool IsWriteBlocked() const override { return !fake_channel_->writable(); }
// Records that the socket has become writable, for example when an EPOLLOUT
// is received or an asynchronous write completes.
void SetWritable() override { fake_channel_->SetWritable(true); }
// Returns the maximum size of the packet which can be written using this
// writer for the supplied peer address. This size may actually exceed the
// size of a valid QUIC packet.
QuicByteCount GetMaxPacketSize(
const IPEndPoint& peer_address) const override {
return net::kMaxPacketSize;
}
private:
FakeTransportChannel* fake_channel_;
};
// Wrapper for QuicSession and transport channel that stores incoming data.
class QuicSessionForTest : public QuicSession {
public:
QuicSessionForTest(std::unique_ptr<net::QuicConnection> connection,
const net::QuicConfig& config,
std::unique_ptr<FakeTransportChannel> channel)
: QuicSession(std::move(connection), config),
channel_(std::move(channel)) {
channel_->SignalReadPacket.connect(
this, &QuicSessionForTest::OnChannelReadPacket);
}
// Called when channel has packets to read.
void OnChannelReadPacket(TransportChannel* channel,
const char* data,
size_t size,
const rtc::PacketTime& packet_time,
int flags) {
OnReadPacket(data, size);
}
// Called when peer receives incoming stream from another peer.
void OnIncomingStream(ReliableQuicStream* stream) {
stream->SignalDataReceived.connect(this,
&QuicSessionForTest::OnDataReceived);
last_incoming_stream_ = stream;
}
// Called when peer has data to read from incoming stream.
void OnDataReceived(net::QuicStreamId id, const char* data, size_t length) {
last_received_data_ = std::string(data, length);
}
std::string data() { return last_received_data_; }
bool has_data() { return data().size() > 0; }
FakeTransportChannel* channel() { return channel_.get(); }
ReliableQuicStream* incoming_stream() { return last_incoming_stream_; }
private:
// Transports QUIC packets to/from peer.
std::unique_ptr<FakeTransportChannel> channel_;
// Stores data received by peer once it is sent from the other peer.
std::string last_received_data_;
// Handles incoming streams from sender.
ReliableQuicStream* last_incoming_stream_ = nullptr;
};
// Simulates data transfer between two peers using QUIC.
class QuicSessionTest : public ::testing::Test,
public QuicCryptoClientStream::ProofHandler {
public:
QuicSessionTest()
: quic_helper_(rtc::Thread::Current()),
quic_compressed_certs_cache_(
QuicCompressedCertsCache::kQuicCompressedCertsCacheSize) {}
// Instantiates |client_peer_| and |server_peer_|.
void CreateClientAndServerSessions();
std::unique_ptr<QuicSessionForTest> CreateSession(
std::unique_ptr<FakeTransportChannel> channel,
Perspective perspective);
QuicCryptoClientStream* CreateCryptoClientStream(QuicSessionForTest* session,
bool handshake_success);
QuicCryptoServerStream* CreateCryptoServerStream(QuicSessionForTest* session,
bool handshake_success);
std::unique_ptr<QuicConnection> CreateConnection(
FakeTransportChannel* channel,
Perspective perspective);
void StartHandshake(bool client_handshake_success,
bool server_handshake_success);
// Test handshake establishment and sending/receiving of data.
void TestStreamConnection(QuicSessionForTest* from_session,
QuicSessionForTest* to_session);
// Test that client and server are not connected after handshake failure.
void TestDisconnectAfterFailedHandshake();
// QuicCryptoClientStream::ProofHelper overrides.
void OnProofValid(
const QuicCryptoClientConfig::CachedState& cached) override {}
void OnProofVerifyDetailsAvailable(
const ProofVerifyDetails& verify_details) override {}
protected:
QuicConnectionHelper quic_helper_;
QuicConfig config_;
QuicClock clock_;
QuicCompressedCertsCache quic_compressed_certs_cache_;
std::unique_ptr<QuicSessionForTest> client_peer_;
std::unique_ptr<QuicSessionForTest> server_peer_;
};
// Initializes "client peer" who begins crypto handshake and "server peer" who
// establishes encryption with client.
void QuicSessionTest::CreateClientAndServerSessions() {
std::unique_ptr<FakeTransportChannel> channel1(
new FakeTransportChannel("channel1", 0));
std::unique_ptr<FakeTransportChannel> channel2(
new FakeTransportChannel("channel2", 0));
// Prevent channel1->OnReadPacket and channel2->OnReadPacket from calling
// themselves in a loop, which causes to future packets to be recursively
// consumed while the current thread blocks consumption of current ones.
channel2->SetAsync(true);
// Configure peers to send packets to each other.
channel1->SetDestination(channel2.get());
client_peer_ = CreateSession(std::move(channel1), Perspective::IS_CLIENT);
server_peer_ = CreateSession(std::move(channel2), Perspective::IS_SERVER);
}
std::unique_ptr<QuicSessionForTest> QuicSessionTest::CreateSession(
std::unique_ptr<FakeTransportChannel> channel,
Perspective perspective) {
std::unique_ptr<QuicConnection> quic_connection =
CreateConnection(channel.get(), perspective);
return std::unique_ptr<QuicSessionForTest>(new QuicSessionForTest(
std::move(quic_connection), config_, std::move(channel)));
}
QuicCryptoClientStream* QuicSessionTest::CreateCryptoClientStream(
QuicSessionForTest* session,
bool handshake_success) {
QuicCryptoClientConfig* client_config =
new QuicCryptoClientConfig(new FakeProofVerifier(handshake_success));
return new QuicCryptoClientStream(
kServerId, session, new ProofVerifyContext(), client_config, this);
}
QuicCryptoServerStream* QuicSessionTest::CreateCryptoServerStream(
QuicSessionForTest* session,
bool handshake_success) {
QuicCryptoServerConfig* server_config =
new QuicCryptoServerConfig("TESTING", QuicRandom::GetInstance(),
new FakeProofSource(handshake_success));
// Provide server with serialized config string to prove ownership.
QuicCryptoServerConfig::ConfigOptions options;
QuicServerConfigProtobuf* primary_config = server_config->GenerateConfig(
QuicRandom::GetInstance(), &clock_, options);
server_config->AddConfig(primary_config, clock_.WallNow());
bool use_stateless_rejects_if_peer_supported = false;
return new QuicCryptoServerStream(
server_config, &quic_compressed_certs_cache_,
use_stateless_rejects_if_peer_supported, session);
}
std::unique_ptr<QuicConnection> QuicSessionTest::CreateConnection(
FakeTransportChannel* channel,
Perspective perspective) {
FakeQuicPacketWriter* writer = new FakeQuicPacketWriter(channel);
IPAddress ip(0, 0, 0, 0);
bool owns_writer = true;
return std::unique_ptr<QuicConnection>(new QuicConnection(
0, net::IPEndPoint(ip, 0), &quic_helper_, writer, owns_writer,
perspective, net::QuicSupportedVersions()));
}
void QuicSessionTest::StartHandshake(bool client_handshake_success,
bool server_handshake_success) {
server_peer_->StartServerHandshake(
CreateCryptoServerStream(server_peer_.get(), server_handshake_success));
client_peer_->StartClientHandshake(
CreateCryptoClientStream(client_peer_.get(), client_handshake_success));
}
void QuicSessionTest::TestStreamConnection(QuicSessionForTest* from_session,
QuicSessionForTest* to_session) {
// Wait for crypto handshake to finish then check if encryption established.
ASSERT_TRUE_WAIT(from_session->IsCryptoHandshakeConfirmed() &&
to_session->IsCryptoHandshakeConfirmed(),
kTimeoutMs);
ASSERT_TRUE(from_session->IsEncryptionEstablished());
ASSERT_TRUE(to_session->IsEncryptionEstablished());
std::string from_key;
std::string to_key;
bool from_success = from_session->ExportKeyingMaterial(
kExporterLabel, kExporterContext, kExporterContextLen, &from_key);
ASSERT_TRUE(from_success);
bool to_success = to_session->ExportKeyingMaterial(
kExporterLabel, kExporterContext, kExporterContextLen, &to_key);
ASSERT_TRUE(to_success);
EXPECT_EQ(from_key.size(), kExporterContextLen);
EXPECT_EQ(from_key, to_key);
// Now we can establish encrypted outgoing stream.
ReliableQuicStream* outgoing_stream =
from_session->CreateOutgoingDynamicStream(kDefaultPriority);
ASSERT_NE(nullptr, outgoing_stream);
EXPECT_TRUE(from_session->HasOpenDynamicStreams());
outgoing_stream->SignalDataReceived.connect(
from_session, &QuicSessionForTest::OnDataReceived);
to_session->SignalIncomingStream.connect(
to_session, &QuicSessionForTest::OnIncomingStream);
// Send a test message from peer 1 to peer 2.
const char kTestMessage[] = "Hello, World!";
outgoing_stream->Write(kTestMessage, strlen(kTestMessage));
// Wait for peer 2 to receive messages.
ASSERT_TRUE_WAIT(to_session->has_data(), kTimeoutMs);
ReliableQuicStream* incoming = to_session->incoming_stream();
ASSERT_TRUE(incoming);
EXPECT_TRUE(to_session->HasOpenDynamicStreams());
EXPECT_EQ(to_session->data(), kTestMessage);
// Send a test message from peer 2 to peer 1.
const char kTestResponse[] = "Response";
incoming->Write(kTestResponse, strlen(kTestResponse));
// Wait for peer 1 to receive messages.
ASSERT_TRUE_WAIT(from_session->has_data(), kTimeoutMs);
EXPECT_EQ(from_session->data(), kTestResponse);
}
// Client and server should disconnect when proof verification fails.
void QuicSessionTest::TestDisconnectAfterFailedHandshake() {
EXPECT_TRUE_WAIT(!client_peer_->connection()->connected(), kTimeoutMs);
EXPECT_TRUE_WAIT(!server_peer_->connection()->connected(), kTimeoutMs);
EXPECT_FALSE(client_peer_->IsEncryptionEstablished());
EXPECT_FALSE(client_peer_->IsCryptoHandshakeConfirmed());
EXPECT_FALSE(server_peer_->IsEncryptionEstablished());
EXPECT_FALSE(server_peer_->IsCryptoHandshakeConfirmed());
}
// Establish encryption then send message from client to server.
TEST_F(QuicSessionTest, ClientToServer) {
CreateClientAndServerSessions();
StartHandshake(true, true);
TestStreamConnection(client_peer_.get(), server_peer_.get());
}
// Establish encryption then send message from server to client.
TEST_F(QuicSessionTest, ServerToClient) {
CreateClientAndServerSessions();
StartHandshake(true, true);
TestStreamConnection(server_peer_.get(), client_peer_.get());
}
// Make client fail to verify proof from server.
TEST_F(QuicSessionTest, ClientRejection) {
CreateClientAndServerSessions();
StartHandshake(false, true);
TestDisconnectAfterFailedHandshake();
}
// Make server fail to give proof to client.
TEST_F(QuicSessionTest, ServerRejection) {
CreateClientAndServerSessions();
StartHandshake(true, false);
TestDisconnectAfterFailedHandshake();
}
// Test that data streams are not created before handshake.
TEST_F(QuicSessionTest, CannotCreateDataStreamBeforeHandshake) {
CreateClientAndServerSessions();
EXPECT_EQ(nullptr, server_peer_->CreateOutgoingDynamicStream(5));
EXPECT_EQ(nullptr, client_peer_->CreateOutgoingDynamicStream(5));
}
// Test that closing a QUIC stream causes the QuicSession to remove it.
TEST_F(QuicSessionTest, CloseQuicStream) {
CreateClientAndServerSessions();
StartHandshake(true, true);
ASSERT_TRUE_WAIT(client_peer_->IsCryptoHandshakeConfirmed() &&
server_peer_->IsCryptoHandshakeConfirmed(),
kTimeoutMs);
ReliableQuicStream* stream = client_peer_->CreateOutgoingDynamicStream(5);
ASSERT_NE(nullptr, stream);
EXPECT_FALSE(client_peer_->IsClosedStream(stream->id()));
stream->Close();
EXPECT_TRUE(client_peer_->IsClosedStream(stream->id()));
}

View File

@ -1,115 +0,0 @@
/*
* 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 "p2p/quic/quictransport.h"
#include "p2p/base/p2ptransportchannel.h"
#include "rtc_base/checks.h"
namespace cricket {
QuicTransport::QuicTransport(
const std::string& name,
PortAllocator* allocator,
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate)
: Transport(name, allocator), local_certificate_(certificate) {}
QuicTransport::~QuicTransport() {
DestroyAllChannels();
}
void QuicTransport::SetLocalCertificate(
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
local_certificate_ = certificate;
}
bool QuicTransport::GetLocalCertificate(
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) {
if (!local_certificate_) {
return false;
}
*certificate = local_certificate_;
return true;
}
bool QuicTransport::ApplyLocalTransportDescription(
TransportChannelImpl* channel,
std::string* error_desc) {
rtc::SSLFingerprint* local_fp =
local_description()->identity_fingerprint.get();
if (!VerifyCertificateFingerprint(local_certificate_.get(), local_fp,
error_desc)) {
return false;
}
if (!channel->SetLocalCertificate(local_certificate_)) {
return BadTransportDescription("Failed to set local identity.", error_desc);
}
return Transport::ApplyLocalTransportDescription(channel, error_desc);
}
bool QuicTransport::NegotiateTransportDescription(ContentAction action,
std::string* error_desc) {
if (!local_description() || !remote_description()) {
const std::string msg =
"Local and Remote description must be set before "
"transport descriptions are negotiated";
return BadTransportDescription(msg, error_desc);
}
rtc::SSLFingerprint* local_fp =
local_description()->identity_fingerprint.get();
rtc::SSLFingerprint* remote_fp =
remote_description()->identity_fingerprint.get();
if (!local_fp || !remote_fp) {
return BadTransportDescription("Fingerprints must be supplied for QUIC.",
error_desc);
}
remote_fingerprint_.reset(new rtc::SSLFingerprint(*remote_fp));
if (!NegotiateRole(action, &local_role_, error_desc)) {
return false;
}
// Now run the negotiation for the Transport class.
return Transport::NegotiateTransportDescription(action, error_desc);
}
QuicTransportChannel* QuicTransport::CreateTransportChannel(int component) {
P2PTransportChannel* ice_channel =
new P2PTransportChannel(name(), component, port_allocator());
return new QuicTransportChannel(ice_channel);
}
void QuicTransport::DestroyTransportChannel(TransportChannelImpl* channel) {
delete channel;
}
bool QuicTransport::GetSslRole(rtc::SSLRole* ssl_role) const {
RTC_DCHECK(ssl_role != NULL);
*ssl_role = local_role_;
return true;
}
bool QuicTransport::ApplyNegotiatedTransportDescription(
TransportChannelImpl* channel,
std::string* error_desc) {
// Set ssl role and remote fingerprint. These are required for QUIC setup.
if (!channel->SetSslRole(local_role_)) {
return BadTransportDescription("Failed to set ssl role for the channel.",
error_desc);
}
// Apply remote fingerprint.
if (!channel->SetRemoteFingerprint(
remote_fingerprint_->algorithm,
reinterpret_cast<const uint8_t*>(remote_fingerprint_->digest.data()),
remote_fingerprint_->digest.size())) {
return BadTransportDescription("Failed to apply remote fingerprint.",
error_desc);
}
return Transport::ApplyNegotiatedTransportDescription(channel, error_desc);
}
} // namespace cricket

View File

@ -1,68 +0,0 @@
/*
* 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.
*/
#ifndef P2P_QUIC_QUICTRANSPORT_H_
#define P2P_QUIC_QUICTRANSPORT_H_
#include <string>
#include <map>
#include <memory>
#include "p2p/base/jseptransport.h"
#include "p2p/quic/quictransportchannel.h"
namespace cricket {
class P2PTransportChannel;
class PortAllocator;
// TODO(deadbeef): To get QUIC working with TransportController again, would
// need to merge this class with Transport (or make separate DTLS/QUIC
// subclasses). The only difference between the two (as of typing this) is that
// the QUIC channel *requires* a fingerprint, whereas the DTLS channel can
// operate in a passthrough mode when SDES is used.
class QuicTransport : public Transport {
public:
QuicTransport(const std::string& name,
PortAllocator* allocator,
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
~QuicTransport() override;
// Transport overrides.
void SetLocalCertificate(
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) override;
bool GetLocalCertificate(
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) override;
bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) override {
return true; // Not needed by QUIC
}
bool GetSslRole(rtc::SSLRole* ssl_role) const override;
protected:
// Transport overrides.
QuicTransportChannel* CreateTransportChannel(int component) override;
void DestroyTransportChannel(TransportChannelImpl* channel) override;
bool ApplyLocalTransportDescription(TransportChannelImpl* channel,
std::string* error_desc) override;
bool NegotiateTransportDescription(ContentAction action,
std::string* error_desc) override;
bool ApplyNegotiatedTransportDescription(TransportChannelImpl* channel,
std::string* error_desc) override;
private:
rtc::scoped_refptr<rtc::RTCCertificate> local_certificate_;
rtc::SSLRole local_role_ = rtc::SSL_CLIENT;
std::unique_ptr<rtc::SSLFingerprint> remote_fingerprint_;
};
} // namespace cricket
#endif // P2P_QUIC_QUICTRANSPORT_H_

View File

@ -1,160 +0,0 @@
/*
* 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 "p2p/quic/quictransport.h"
#include <memory>
#include <string>
#include <vector>
#include "rtc_base/gunit.h"
#include "rtc_base/rtccertificate.h"
#include "rtc_base/sslidentity.h"
using cricket::TransportChannelImpl;
using cricket::QuicTransport;
using cricket::Transport;
using cricket::TransportDescription;
static const char kIceUfrag1[] = "TESTICEUFRAG0001";
static const char kIcePwd1[] = "TESTICEPWD00000000000001";
static const char kIceUfrag2[] = "TESTICEUFRAG0002";
static const char kIcePwd2[] = "TESTICEPWD00000000000002";
static rtc::scoped_refptr<rtc::RTCCertificate> CreateCertificate(
std::string name) {
return rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate(name, rtc::KT_DEFAULT)));
}
static std::unique_ptr<rtc::SSLFingerprint> CreateFingerprint(
rtc::RTCCertificate* cert) {
std::string digest_algorithm;
cert->ssl_certificate().GetSignatureDigestAlgorithm(&digest_algorithm);
return std::unique_ptr<rtc::SSLFingerprint>(
rtc::SSLFingerprint::Create(digest_algorithm, cert->identity()));
}
class QuicTransportTest : public testing::Test {
public:
QuicTransportTest() : transport_("testing", nullptr, nullptr) {}
void SetTransportDescription(cricket::ConnectionRole local_role,
cricket::ConnectionRole remote_role,
cricket::ContentAction local_action,
cricket::ContentAction remote_action,
rtc::SSLRole expected_ssl_role) {
TransportChannelImpl* channel = transport_.CreateChannel(1);
ASSERT_NE(nullptr, channel);
rtc::scoped_refptr<rtc::RTCCertificate> local_certificate(
CreateCertificate("local"));
ASSERT_NE(nullptr, local_certificate);
transport_.SetLocalCertificate(local_certificate);
std::unique_ptr<rtc::SSLFingerprint> local_fingerprint =
CreateFingerprint(local_certificate.get());
ASSERT_NE(nullptr, local_fingerprint);
TransportDescription local_desc(std::vector<std::string>(), kIceUfrag1,
kIcePwd1, cricket::ICEMODE_FULL, local_role,
local_fingerprint.get());
ASSERT_TRUE(transport_.SetLocalTransportDescription(local_desc,
local_action, nullptr));
// The certificate is applied to QuicTransportChannel when the local
// description is set.
rtc::scoped_refptr<rtc::RTCCertificate> channel_local_certificate =
channel->GetLocalCertificate();
ASSERT_NE(nullptr, channel_local_certificate);
EXPECT_EQ(local_certificate, channel_local_certificate);
std::unique_ptr<rtc::SSLFingerprint> remote_fingerprint =
CreateFingerprint(CreateCertificate("remote").get());
// NegotiateTransportDescription was not called yet. The SSL role should
// not be set and neither should the remote fingerprint.
std::unique_ptr<rtc::SSLRole> role(new rtc::SSLRole());
EXPECT_FALSE(channel->GetSslRole(role.get()));
// Setting the remote description should set the SSL role.
ASSERT_NE(nullptr, remote_fingerprint);
TransportDescription remote_desc(std::vector<std::string>(), kIceUfrag2,
kIcePwd2, cricket::ICEMODE_FULL,
remote_role, remote_fingerprint.get());
ASSERT_TRUE(transport_.SetRemoteTransportDescription(
remote_desc, remote_action, nullptr));
ASSERT_TRUE(channel->GetSslRole(role.get()));
// SSL role should be client because the remote description is an ANSWER.
EXPECT_EQ(expected_ssl_role, *role);
}
protected:
QuicTransport transport_;
};
// Test setting the local certificate.
TEST_F(QuicTransportTest, SetLocalCertificate) {
rtc::scoped_refptr<rtc::RTCCertificate> local_certificate(
CreateCertificate("local"));
ASSERT_NE(nullptr, local_certificate);
rtc::scoped_refptr<rtc::RTCCertificate> transport_local_certificate;
EXPECT_FALSE(transport_.GetLocalCertificate(&transport_local_certificate));
transport_.SetLocalCertificate(local_certificate);
ASSERT_TRUE(transport_.GetLocalCertificate(&transport_local_certificate));
ASSERT_NE(nullptr, transport_local_certificate);
EXPECT_EQ(local_certificate, transport_local_certificate);
}
// Test setting the ICE role.
TEST_F(QuicTransportTest, SetIceRole) {
TransportChannelImpl* channel1 = transport_.CreateChannel(1);
ASSERT_NE(nullptr, channel1);
transport_.SetIceRole(cricket::ICEROLE_CONTROLLING);
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_.ice_role());
TransportChannelImpl* channel2 = transport_.CreateChannel(2);
ASSERT_NE(nullptr, channel2);
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel1->GetIceRole());
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel2->GetIceRole());
}
// Test setting the ICE tie breaker.
TEST_F(QuicTransportTest, SetIceTiebreaker) {
transport_.SetIceTiebreaker(1u);
EXPECT_EQ(1u, transport_.IceTiebreaker());
}
// Test setting the local and remote descriptions for a SSL client.
TEST_F(QuicTransportTest, SetLocalAndRemoteTransportDescriptionClient) {
SetTransportDescription(cricket::CONNECTIONROLE_ACTPASS,
cricket::CONNECTIONROLE_PASSIVE, cricket::CA_OFFER,
cricket::CA_ANSWER, rtc::SSL_CLIENT);
}
// Test setting the local and remote descriptions for a SSL server.
TEST_F(QuicTransportTest, SetLocalAndRemoteTransportDescriptionServer) {
SetTransportDescription(cricket::CONNECTIONROLE_ACTPASS,
cricket::CONNECTIONROLE_ACTIVE, cricket::CA_OFFER,
cricket::CA_ANSWER, rtc::SSL_SERVER);
}
// Test creation and destruction of channels.
TEST_F(QuicTransportTest, CreateAndDestroyChannels) {
TransportChannelImpl* channel1 = transport_.CreateChannel(1);
ASSERT_NE(nullptr, channel1);
EXPECT_TRUE(transport_.HasChannel(1));
EXPECT_EQ(channel1, transport_.GetChannel(1));
TransportChannelImpl* channel2 = transport_.CreateChannel(2);
ASSERT_NE(nullptr, channel2);
EXPECT_TRUE(transport_.HasChannel(2));
EXPECT_EQ(channel2, transport_.GetChannel(2));
transport_.DestroyChannel(1);
EXPECT_FALSE(transport_.HasChannel(1));
EXPECT_EQ(nullptr, transport_.GetChannel(1));
transport_.DestroyChannel(2);
EXPECT_FALSE(transport_.HasChannel(2));
EXPECT_EQ(nullptr, transport_.GetChannel(2));
}

View File

@ -1,598 +0,0 @@
/*
* 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 "p2p/quic/quictransportchannel.h"
#include <utility>
#include "net/quic/crypto/proof_source.h"
#include "net/quic/crypto/proof_verifier.h"
#include "net/quic/crypto/quic_crypto_client_config.h"
#include "net/quic/crypto/quic_crypto_server_config.h"
#include "net/quic/quic_connection.h"
#include "net/quic/quic_crypto_client_stream.h"
#include "net/quic/quic_crypto_server_stream.h"
#include "net/quic/quic_packet_writer.h"
#include "net/quic/quic_protocol.h"
#include "p2p/base/common.h"
#include "rtc_base/checks.h"
#include "rtc_base/helpers.h"
#include "rtc_base/logging.h"
#include "rtc_base/socket.h"
#include "rtc_base/thread.h"
namespace {
// QUIC public header constants for net::QuicConnection. These are arbitrary
// given that |channel_| only receives packets specific to this channel,
// in which case we already know the QUIC packets have the correct destination.
const net::QuicConnectionId kConnectionId = 0;
const net::IPAddress kConnectionIpAddress(0, 0, 0, 0);
const net::IPEndPoint kConnectionIpEndpoint(kConnectionIpAddress, 0);
// Arbitrary server port number for net::QuicCryptoClientConfig.
const int kQuicServerPort = 0;
// QUIC connection timeout. This is large so that |channel_| can
// be responsible for connection timeout.
const int kIdleConnectionStateLifetime = 1000; // seconds
// Length of HKDF input keying material, equal to its number of bytes.
// https://tools.ietf.org/html/rfc5869#section-2.2.
// TODO(mikescarlett): Verify that input keying material length is correct.
const size_t kInputKeyingMaterialLength = 32;
// We don't pull the RTP constants from rtputils.h, to avoid a layer violation.
const size_t kMinRtpPacketLen = 12;
bool IsRtpPacket(const char* data, size_t len) {
const uint8_t* u = reinterpret_cast<const uint8_t*>(data);
return (len >= kMinRtpPacketLen && (u[0] & 0xC0) == 0x80);
}
// Function for detecting QUIC packets based off
// https://tools.ietf.org/html/draft-tsvwg-quic-protocol-02#section-6.
const size_t kMinQuicPacketLen = 2;
bool IsQuicPacket(const char* data, size_t len) {
const uint8_t* u = reinterpret_cast<const uint8_t*>(data);
return (len >= kMinQuicPacketLen && (u[0] & 0x80) == 0);
}
// Used by QuicCryptoServerConfig to provide dummy proof credentials.
// TODO(mikescarlett): Remove when secure P2P QUIC handshake is possible.
class DummyProofSource : public net::ProofSource {
public:
DummyProofSource() {}
~DummyProofSource() override {}
// ProofSource override.
bool GetProof(const net::IPAddress& server_ip,
const std::string& hostname,
const std::string& server_config,
net::QuicVersion quic_version,
base::StringPiece chlo_hash,
bool ecdsa_ok,
scoped_refptr<net::ProofSource::Chain>* out_chain,
std::string* out_signature,
std::string* out_leaf_cert_sct) override {
LOG(LS_INFO) << "GetProof() providing dummy credentials for insecure QUIC";
std::vector<std::string> certs;
certs.push_back("Dummy cert");
*out_chain = new ProofSource::Chain(certs);
*out_signature = "Dummy signature";
*out_leaf_cert_sct = "Dummy timestamp";
return true;
}
};
// Used by QuicCryptoClientConfig to ignore the peer's credentials
// and establish an insecure QUIC connection.
// TODO(mikescarlett): Remove when secure P2P QUIC handshake is possible.
class InsecureProofVerifier : public net::ProofVerifier {
public:
InsecureProofVerifier() {}
~InsecureProofVerifier() override {}
// ProofVerifier override.
net::QuicAsyncStatus VerifyProof(
const std::string& hostname,
const uint16_t port,
const std::string& server_config,
net::QuicVersion quic_version,
base::StringPiece chlo_hash,
const std::vector<std::string>& certs,
const std::string& cert_sct,
const std::string& signature,
const net::ProofVerifyContext* context,
std::string* error_details,
std::unique_ptr<net::ProofVerifyDetails>* verify_details,
net::ProofVerifierCallback* callback) override {
LOG(LS_INFO) << "VerifyProof() ignoring credentials and returning success";
return net::QUIC_SUCCESS;
}
};
} // namespace
namespace cricket {
QuicTransportChannel::QuicTransportChannel(TransportChannelImpl* channel)
: TransportChannelImpl(channel->transport_name(), channel->component()),
network_thread_(rtc::Thread::Current()),
channel_(channel),
helper_(network_thread_) {
channel_->SignalWritableState.connect(this,
&QuicTransportChannel::OnWritableState);
channel_->SignalReadPacket.connect(this, &QuicTransportChannel::OnReadPacket);
channel_->SignalSentPacket.connect(this, &QuicTransportChannel::OnSentPacket);
channel_->SignalReadyToSend.connect(this,
&QuicTransportChannel::OnReadyToSend);
channel_->SignalGatheringState.connect(
this, &QuicTransportChannel::OnGatheringState);
channel_->SignalCandidateGathered.connect(
this, &QuicTransportChannel::OnCandidateGathered);
channel_->SignalRoleConflict.connect(this,
&QuicTransportChannel::OnRoleConflict);
channel_->SignalRouteChange.connect(this,
&QuicTransportChannel::OnRouteChange);
channel_->SignalSelectedCandidatePairChanged.connect(
this, &QuicTransportChannel::OnSelectedCandidatePairChanged);
channel_->SignalStateChanged.connect(
this, &QuicTransportChannel::OnChannelStateChanged);
channel_->SignalReceivingState.connect(
this, &QuicTransportChannel::OnReceivingState);
// Set the QUIC connection timeout.
config_.SetIdleConnectionStateLifetime(
net::QuicTime::Delta::FromSeconds(kIdleConnectionStateLifetime),
net::QuicTime::Delta::FromSeconds(kIdleConnectionStateLifetime));
// Set the bytes reserved for the QUIC connection ID to zero.
config_.SetBytesForConnectionIdToSend(0);
}
QuicTransportChannel::~QuicTransportChannel() {}
bool QuicTransportChannel::SetLocalCertificate(
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
if (!certificate) {
LOG_J(LS_ERROR, this)
<< "No local certificate was supplied. Not doing QUIC.";
return false;
}
if (!local_certificate_) {
local_certificate_ = certificate;
return true;
}
if (certificate == local_certificate_) {
// This may happen during renegotiation.
LOG_J(LS_INFO, this) << "Ignoring identical certificate";
return true;
}
LOG_J(LS_ERROR, this)
<< "Local certificate of the QUIC connection already set. "
"Can't change the local certificate once it's active.";
return false;
}
rtc::scoped_refptr<rtc::RTCCertificate>
QuicTransportChannel::GetLocalCertificate() const {
return local_certificate_;
}
bool QuicTransportChannel::SetSslRole(rtc::SSLRole role) {
if (ssl_role_ && *ssl_role_ == role) {
LOG_J(LS_WARNING, this) << "Ignoring SSL Role identical to current role.";
return true;
}
if (quic_state_ != QUIC_TRANSPORT_CONNECTED) {
ssl_role_ = rtc::Optional<rtc::SSLRole>(role);
return true;
}
LOG_J(LS_ERROR, this)
<< "SSL Role can't be reversed after the session is setup.";
return false;
}
bool QuicTransportChannel::GetSslRole(rtc::SSLRole* role) const {
if (!ssl_role_) {
return false;
}
*role = *ssl_role_;
return true;
}
bool QuicTransportChannel::SetRemoteFingerprint(const std::string& digest_alg,
const uint8_t* digest,
size_t digest_len) {
if (digest_alg.empty()) {
RTC_DCHECK(!digest_len);
LOG_J(LS_ERROR, this) << "Remote peer doesn't support digest algorithm.";
return false;
}
std::string remote_fingerprint_value(reinterpret_cast<const char*>(digest),
digest_len);
// Once we have the local certificate, the same remote fingerprint can be set
// multiple times. This may happen during renegotiation.
if (remote_fingerprint_ &&
remote_fingerprint_->value == remote_fingerprint_value &&
remote_fingerprint_->algorithm == digest_alg) {
LOG_J(LS_INFO, this)
<< "Ignoring identical remote fingerprint and algorithm";
return true;
}
remote_fingerprint_ = rtc::Optional<RemoteFingerprint>(RemoteFingerprint());
remote_fingerprint_->value = remote_fingerprint_value;
remote_fingerprint_->algorithm = digest_alg;
return true;
}
bool QuicTransportChannel::ExportKeyingMaterial(const std::string& label,
const uint8_t* context,
size_t context_len,
bool use_context,
uint8_t* result,
size_t result_len) {
std::string quic_context(reinterpret_cast<const char*>(context), context_len);
std::string quic_result;
if (!quic_->ExportKeyingMaterial(label, quic_context, result_len,
&quic_result)) {
return false;
}
quic_result.copy(reinterpret_cast<char*>(result), result_len);
return true;
}
bool QuicTransportChannel::GetSrtpCryptoSuite(int* cipher) {
*cipher = rtc::SRTP_AES128_CM_SHA1_80;
return true;
}
// Called from upper layers to send a media packet.
int QuicTransportChannel::SendPacket(const char* data,
size_t size,
const rtc::PacketOptions& options,
int flags) {
if ((flags & PF_SRTP_BYPASS) && IsRtpPacket(data, size)) {
return channel_->SendPacket(data, size, options);
}
LOG(LS_ERROR) << "Failed to send an invalid SRTP bypass packet using QUIC.";
return -1;
}
// The state transition logic here is as follows:
// - Before the QUIC handshake is complete, the QUIC channel is unwritable.
// - When |channel_| goes writable we start the QUIC handshake.
// - Once the QUIC handshake completes, the state is that of the
// |channel_| again.
void QuicTransportChannel::OnWritableState(TransportChannel* channel) {
RTC_DCHECK(rtc::Thread::Current() == network_thread_);
RTC_DCHECK(channel == channel_.get());
LOG_J(LS_VERBOSE, this)
<< "QuicTransportChannel: channel writable state changed to "
<< channel_->writable();
switch (quic_state_) {
case QUIC_TRANSPORT_NEW:
// Start the QUIC handshake when |channel_| is writable.
// This will fail if the SSL role or remote fingerprint are not set.
// Otherwise failure could result from network or QUIC errors.
MaybeStartQuic();
break;
case QUIC_TRANSPORT_CONNECTED:
// Note: SignalWritableState fired by set_writable.
set_writable(channel_->writable());
if (HasDataToWrite()) {
OnCanWrite();
}
break;
case QUIC_TRANSPORT_CONNECTING:
// This channel is not writable until the QUIC handshake finishes. It
// might have been write blocked.
if (HasDataToWrite()) {
OnCanWrite();
}
break;
case QUIC_TRANSPORT_CLOSED:
// TODO(mikescarlett): Allow the QUIC connection to be reset if it drops
// due to a non-failure.
break;
}
}
void QuicTransportChannel::OnReceivingState(TransportChannel* channel) {
RTC_DCHECK(rtc::Thread::Current() == network_thread_);
RTC_DCHECK(channel == channel_.get());
LOG_J(LS_VERBOSE, this)
<< "QuicTransportChannel: channel receiving state changed to "
<< channel_->receiving();
if (quic_state_ == QUIC_TRANSPORT_CONNECTED) {
// Note: SignalReceivingState fired by set_receiving.
set_receiving(channel_->receiving());
}
}
void QuicTransportChannel::OnReadPacket(TransportChannel* channel,
const char* data,
size_t size,
const rtc::PacketTime& packet_time,
int flags) {
RTC_DCHECK(rtc::Thread::Current() == network_thread_);
RTC_DCHECK(channel == channel_.get());
RTC_DCHECK(flags == 0);
switch (quic_state_) {
case QUIC_TRANSPORT_NEW:
// This would occur if other peer is ready to start QUIC but this peer
// hasn't started QUIC.
LOG_J(LS_INFO, this) << "Dropping packet received before QUIC started.";
break;
case QUIC_TRANSPORT_CONNECTING:
case QUIC_TRANSPORT_CONNECTED:
// We should only get QUIC or SRTP packets; STUN's already been demuxed.
// Is this potentially a QUIC packet?
if (IsQuicPacket(data, size)) {
if (!HandleQuicPacket(data, size)) {
LOG_J(LS_ERROR, this) << "Failed to handle QUIC packet.";
return;
}
} else {
// If this is an RTP packet, signal upwards as a bypass packet.
if (!IsRtpPacket(data, size)) {
LOG_J(LS_ERROR, this)
<< "Received unexpected non-QUIC, non-RTP packet.";
return;
}
SignalReadPacket(this, data, size, packet_time, PF_SRTP_BYPASS);
}
break;
case QUIC_TRANSPORT_CLOSED:
// This shouldn't be happening. Drop the packet.
break;
}
}
void QuicTransportChannel::OnSentPacket(TransportChannel* channel,
const rtc::SentPacket& sent_packet) {
RTC_DCHECK(rtc::Thread::Current() == network_thread_);
SignalSentPacket(this, sent_packet);
}
void QuicTransportChannel::OnReadyToSend(TransportChannel* channel) {
if (writable()) {
SignalReadyToSend(this);
}
}
void QuicTransportChannel::OnGatheringState(TransportChannelImpl* channel) {
RTC_DCHECK(channel == channel_.get());
SignalGatheringState(this);
}
void QuicTransportChannel::OnCandidateGathered(TransportChannelImpl* channel,
const Candidate& c) {
RTC_DCHECK(channel == channel_.get());
SignalCandidateGathered(this, c);
}
void QuicTransportChannel::OnRoleConflict(TransportChannelImpl* channel) {
RTC_DCHECK(channel == channel_.get());
SignalRoleConflict(this);
}
void QuicTransportChannel::OnRouteChange(TransportChannel* channel,
const Candidate& candidate) {
RTC_DCHECK(channel == channel_.get());
SignalRouteChange(this, candidate);
}
void QuicTransportChannel::OnSelectedCandidatePairChanged(
TransportChannel* channel,
CandidatePairInterface* selected_candidate_pair,
int last_sent_packet_id,
bool ready_to_send) {
RTC_DCHECK(channel == channel_.get());
SignalSelectedCandidatePairChanged(this, selected_candidate_pair,
last_sent_packet_id, ready_to_send);
}
void QuicTransportChannel::OnChannelStateChanged(
TransportChannelImpl* channel) {
RTC_DCHECK(channel == channel_.get());
SignalStateChanged(this);
}
bool QuicTransportChannel::MaybeStartQuic() {
if (!channel_->writable()) {
LOG_J(LS_ERROR, this) << "Couldn't start QUIC handshake.";
return false;
}
if (!CreateQuicSession() || !StartQuicHandshake()) {
LOG_J(LS_WARNING, this)
<< "Underlying channel is writable but cannot start "
"the QUIC handshake.";
return false;
}
// Verify connection is not closed due to QUIC bug or network failure.
// A closed connection should not happen since |channel_| is writable.
if (!quic_->connection()->connected()) {
LOG_J(LS_ERROR, this)
<< "QUIC connection should not be closed if underlying "
"channel is writable.";
return false;
}
// Indicate that |quic_| is ready to receive QUIC packets.
set_quic_state(QUIC_TRANSPORT_CONNECTING);
return true;
}
bool QuicTransportChannel::CreateQuicSession() {
if (!ssl_role_ || !remote_fingerprint_) {
return false;
}
net::Perspective perspective = (*ssl_role_ == rtc::SSL_CLIENT)
? net::Perspective::IS_CLIENT
: net::Perspective::IS_SERVER;
bool owns_writer = false;
std::unique_ptr<net::QuicConnection> connection(new net::QuicConnection(
kConnectionId, kConnectionIpEndpoint, &helper_, this, owns_writer,
perspective, net::QuicSupportedVersions()));
quic_.reset(new QuicSession(std::move(connection), config_));
quic_->SignalHandshakeComplete.connect(
this, &QuicTransportChannel::OnHandshakeComplete);
quic_->SignalConnectionClosed.connect(
this, &QuicTransportChannel::OnConnectionClosed);
quic_->SignalIncomingStream.connect(this,
&QuicTransportChannel::OnIncomingStream);
return true;
}
bool QuicTransportChannel::StartQuicHandshake() {
if (*ssl_role_ == rtc::SSL_CLIENT) {
// Unique identifier for remote peer.
net::QuicServerId server_id(remote_fingerprint_->value, kQuicServerPort);
// Perform authentication of remote peer; owned by QuicCryptoClientConfig.
// TODO(mikescarlett): Actually verify proof.
net::ProofVerifier* proof_verifier = new InsecureProofVerifier();
quic_crypto_client_config_.reset(
new net::QuicCryptoClientConfig(proof_verifier));
net::QuicCryptoClientStream* crypto_stream =
new net::QuicCryptoClientStream(server_id, quic_.get(),
new net::ProofVerifyContext(),
quic_crypto_client_config_.get(), this);
quic_->StartClientHandshake(crypto_stream);
LOG_J(LS_INFO, this) << "QuicTransportChannel: Started client handshake.";
} else {
RTC_DCHECK_EQ(*ssl_role_, rtc::SSL_SERVER);
// Provide credentials to remote peer; owned by QuicCryptoServerConfig.
// TODO(mikescarlett): Actually provide credentials.
net::ProofSource* proof_source = new DummyProofSource();
// Input keying material to HKDF, per http://tools.ietf.org/html/rfc5869.
// This is pseudorandom so that HKDF-Extract outputs a pseudorandom key,
// since QuicCryptoServerConfig does not use a salt value.
std::string source_address_token_secret;
if (!rtc::CreateRandomString(kInputKeyingMaterialLength,
&source_address_token_secret)) {
LOG_J(LS_ERROR, this)
<< "Error generating input keying material for HKDF.";
return false;
}
quic_crypto_server_config_.reset(new net::QuicCryptoServerConfig(
source_address_token_secret, helper_.GetRandomGenerator(),
proof_source));
// Provide server with serialized config string to prove ownership.
net::QuicCryptoServerConfig::ConfigOptions options;
quic_crypto_server_config_->AddDefaultConfig(helper_.GetRandomGenerator(),
helper_.GetClock(), options);
quic_compressed_certs_cache_.reset(new net::QuicCompressedCertsCache(
net::QuicCompressedCertsCache::kQuicCompressedCertsCacheSize));
// TODO(mikescarlett): Add support for stateless rejects.
bool use_stateless_rejects_if_peer_supported = false;
net::QuicCryptoServerStream* crypto_stream =
new net::QuicCryptoServerStream(quic_crypto_server_config_.get(),
quic_compressed_certs_cache_.get(),
use_stateless_rejects_if_peer_supported,
quic_.get());
quic_->StartServerHandshake(crypto_stream);
LOG_J(LS_INFO, this) << "QuicTransportChannel: Started server handshake.";
}
return true;
}
bool QuicTransportChannel::HandleQuicPacket(const char* data, size_t size) {
RTC_DCHECK(rtc::Thread::Current() == network_thread_);
return quic_->OnReadPacket(data, size);
}
net::WriteResult QuicTransportChannel::WritePacket(
const char* buffer,
size_t buf_len,
const net::IPAddress& self_address,
const net::IPEndPoint& peer_address,
net::PerPacketOptions* options) {
// QUIC should never call this if IsWriteBlocked, but just in case...
if (IsWriteBlocked()) {
return net::WriteResult(net::WRITE_STATUS_BLOCKED, EWOULDBLOCK);
}
// TODO(mikescarlett): Figure out how to tell QUIC "I dropped your packet, but
// don't block" without the QUIC connection tearing itself down.
int sent = channel_->SendPacket(buffer, buf_len, rtc::PacketOptions());
int bytes_written = sent > 0 ? sent : 0;
return net::WriteResult(net::WRITE_STATUS_OK, bytes_written);
}
// TODO(mikescarlett): Implement check for whether |channel_| is currently
// write blocked so that |quic_| does not try to write packet. This is
// necessary because |channel_| can be writable yet write blocked and
// channel_->GetError() is not flushed when there is no error.
bool QuicTransportChannel::IsWriteBlocked() const {
return !channel_->writable();
}
void QuicTransportChannel::OnHandshakeComplete() {
set_quic_state(QUIC_TRANSPORT_CONNECTED);
set_writable(true);
// OnReceivingState might have been called before the QUIC channel was
// connected, in which case the QUIC channel is now receiving.
if (channel_->receiving()) {
set_receiving(true);
}
}
void QuicTransportChannel::OnConnectionClosed(net::QuicErrorCode error,
bool from_peer) {
LOG_J(LS_INFO, this) << "Connection closed by "
<< (from_peer ? "other" : "this") << " peer "
<< "with QUIC error " << error;
// TODO(mikescarlett): Allow the QUIC session to be reset when the connection
// does not close due to failure.
set_quic_state(QUIC_TRANSPORT_CLOSED);
set_writable(false);
SignalClosed();
}
void QuicTransportChannel::OnProofValid(
const net::QuicCryptoClientConfig::CachedState& cached) {
LOG_J(LS_INFO, this) << "Cached proof marked valid";
}
void QuicTransportChannel::OnProofVerifyDetailsAvailable(
const net::ProofVerifyDetails& verify_details) {
LOG_J(LS_INFO, this) << "Proof verify details available from"
<< " QuicCryptoClientStream";
}
bool QuicTransportChannel::HasDataToWrite() const {
return quic_ && quic_->HasDataToWrite();
}
void QuicTransportChannel::OnCanWrite() {
RTC_DCHECK(quic_ != nullptr);
quic_->connection()->OnCanWrite();
}
void QuicTransportChannel::set_quic_state(QuicTransportState state) {
LOG_J(LS_VERBOSE, this) << "set_quic_state from:" << quic_state_ << " to "
<< state;
quic_state_ = state;
}
ReliableQuicStream* QuicTransportChannel::CreateQuicStream() {
if (quic_) {
net::SpdyPriority priority = 0; // Priority of the QUIC stream
return quic_->CreateOutgoingDynamicStream(priority);
}
return nullptr;
}
void QuicTransportChannel::OnIncomingStream(ReliableQuicStream* stream) {
SignalIncomingStream(stream);
}
} // namespace cricket

View File

@ -1,308 +0,0 @@
/*
* 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.
*/
#ifndef P2P_QUIC_QUICTRANSPORTCHANNEL_H_
#define P2P_QUIC_QUICTRANSPORTCHANNEL_H_
#include <memory>
#include <string>
#include <vector>
#include "net/quic/quic_crypto_client_stream.h"
#include "net/quic/quic_packet_writer.h"
#include "api/optional.h"
#include "p2p/base/transportchannelimpl.h"
#include "p2p/quic/quicconnectionhelper.h"
#include "p2p/quic/quicsession.h"
#include "rtc_base/constructormagic.h"
namespace cricket {
enum QuicTransportState {
// Haven't started QUIC handshake.
QUIC_TRANSPORT_NEW = 0,
// Started QUIC handshake.
QUIC_TRANSPORT_CONNECTING,
// Negotiated, and has an encrypted connection.
QUIC_TRANSPORT_CONNECTED,
// QUIC connection closed due to handshake failure or explicit shutdown.
QUIC_TRANSPORT_CLOSED,
};
// QuicTransportChannel uses the QUIC protocol to establish encryption with
// another peer, wrapping an existing TransportChannelImpl instance
// (e.g a P2PTransportChannel) responsible for connecting peers.
// Once the wrapped transport channel is connected, QuicTransportChannel
// negotiates the crypto handshake and establishes SRTP keying material.
//
// How it works:
//
// QuicTransportChannel {
// QuicSession* quic_;
// TransportChannelImpl* channel_;
// }
//
// - Data written to SendPacket() is passed directly to |channel_| if it is
// an SRTP packet with the PF_SRTP_BYPASS flag.
//
// - |quic_| passes outgoing packets to WritePacket(), which transfers them
// to |channel_| to be sent across the network.
//
// - Data which comes into QuicTransportChannel::OnReadPacket is checked to
// see if it is QUIC, and if it is, passed to |quic_|. SRTP packets are
// signaled upwards as bypass packets.
//
// - When the QUIC handshake is completed, quic_state() returns
// QUIC_TRANSPORT_CONNECTED and SRTP keying material can be exported.
//
// - CreateQuicStream() creates an outgoing QUIC stream. Once the local peer
// sends data from this stream, the remote peer emits SignalIncomingStream
// with a QUIC stream of the same id to handle received data.
//
// TODO(mikescarlett): Implement secure QUIC handshake and 0-RTT handshakes.
class QuicTransportChannel : public TransportChannelImpl,
public net::QuicPacketWriter,
public net::QuicCryptoClientStream::ProofHandler {
public:
// |channel| - the TransportChannelImpl we are wrapping.
explicit QuicTransportChannel(TransportChannelImpl* channel);
~QuicTransportChannel() override;
// TransportChannel overrides.
// TODO(mikescarlett): Implement certificate authentication.
bool SetLocalCertificate(
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) override;
rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate() const override;
// TODO(mikescarlett): Implement fingerprint authentication.
bool SetRemoteFingerprint(const std::string& digest_alg,
const uint8_t* digest,
size_t digest_len) override;
// TODO(mikescarlett): Remove this DTLS-specific method when TransportChannel
// does not require defining it.
bool IsDtlsActive() const override { return true; }
// Sends a RTP packet if the PF_SRTP_BYPASS flag is set.
int SendPacket(const char* data,
size_t size,
const rtc::PacketOptions& options,
int flags) override;
// Sets up the ciphers to use for SRTP.
// TODO(mikescarlett): Use SRTP ciphers for negotiation.
bool SetSrtpCryptoSuites(const std::vector<int>& ciphers) override {
return true;
}
// Determines which SRTP cipher was negotiated.
// TODO(mikescarlett): Implement QUIC cipher negotiation. This currently
// returns SRTP_AES128_CM_SHA1_80.
bool GetSrtpCryptoSuite(int* cipher) override;
bool SetSslRole(rtc::SSLRole role) override;
bool GetSslRole(rtc::SSLRole* role) const override;
// Determines which SSL cipher was negotiated.
// TODO(mikescarlett): Implement QUIC cipher negotiation.
bool GetSslCipherSuite(int* cipher) override { return false; }
// Once QUIC is established (i.e., |quic_state_| is QUIC_TRANSPORT_CONNECTED),
// this extracts the keys negotiated during the QUIC handshake, for use
// in external encryption such as for extracting SRTP keys.
bool ExportKeyingMaterial(const std::string& label,
const uint8_t* context,
size_t context_len,
bool use_context,
uint8_t* result,
size_t result_len) override;
// TODO(mikescarlett): Remove this method once TransportChannel does not
// require defining it.
std::unique_ptr<rtc::SSLCertificate> GetRemoteSSLCertificate()
const override {
return nullptr;
}
// TransportChannelImpl overrides that we forward to the wrapped transport.
void SetIceRole(IceRole role) override { channel_->SetIceRole(role); }
IceRole GetIceRole() const override { return channel_->GetIceRole(); }
int SetOption(rtc::Socket::Option opt, int value) override {
return channel_->SetOption(opt, value);
}
bool GetOption(rtc::Socket::Option opt, int* value) override {
return channel_->GetOption(opt, value);
}
int GetError() override { return channel_->GetError(); }
bool GetStats(ConnectionInfos* infos) override {
return channel_->GetStats(infos);
}
const std::string SessionId() const override { return channel_->SessionId(); }
TransportChannelState GetState() const override {
return channel_->GetState();
}
void SetIceTiebreaker(uint64_t tiebreaker) override {
channel_->SetIceTiebreaker(tiebreaker);
}
void SetIceParameters(const IceParameters& ice_params) override {
channel_->SetIceParameters(ice_params);
}
void SetRemoteIceParameters(const IceParameters& ice_params) override {
channel_->SetRemoteIceParameters(ice_params);
}
void SetRemoteIceMode(IceMode mode) override {
channel_->SetRemoteIceMode(mode);
}
void MaybeStartGathering() override { channel_->MaybeStartGathering(); }
IceGatheringState gathering_state() const override {
return channel_->gathering_state();
}
void AddRemoteCandidate(const Candidate& candidate) override {
channel_->AddRemoteCandidate(candidate);
}
void RemoveRemoteCandidate(const Candidate& candidate) override {
channel_->RemoveRemoteCandidate(candidate);
}
void SetIceConfig(const IceConfig& config) override {
channel_->SetIceConfig(config);
}
// QuicPacketWriter overrides.
// Called from net::QuicConnection when |quic_| has packets to write.
net::WriteResult WritePacket(const char* buffer,
size_t buf_len,
const net::IPAddress& self_address,
const net::IPEndPoint& peer_address,
net::PerPacketOptions* options) override;
// Whether QuicTransportChannel buffers data when unable to write. If this is
// set to false, then net::QuicConnection buffers unsent packets.
bool IsWriteBlockedDataBuffered() const override { return false; }
// Whether QuicTransportChannel is write blocked. If this returns true,
// outgoing QUIC packets are queued by net::QuicConnection until
// QuicTransportChannel::OnCanWrite() is called.
bool IsWriteBlocked() const override;
// Maximum size of the QUIC packet which can be written.
net::QuicByteCount GetMaxPacketSize(
const net::IPEndPoint& peer_address) const override {
return net::kMaxPacketSize;
}
// This method is not used -- call set_writable(bool writable) instead.
// TODO(miekscarlett): Remove this method once QuicPacketWriter does not
// require defining it.
void SetWritable() override {}
// QuicCryptoClientStream::ProofHandler overrides.
// Called by client crypto handshake when cached proof is marked valid.
void OnProofValid(
const net::QuicCryptoClientConfig::CachedState& cached) override;
// Called by the client crypto handshake when proof verification details
// become available, either because proof verification is complete, or when
// cached details are used.
void OnProofVerifyDetailsAvailable(
const net::ProofVerifyDetails& verify_details) override;
void SetMetricsObserver(webrtc::MetricsObserverInterface* observer) override {
channel_->SetMetricsObserver(observer);
}
// Returns true if |quic_| has queued data which wasn't written due
// to |channel_| being write blocked.
bool HasDataToWrite() const;
// Writes queued data for |quic_| when |channel_| is no longer write blocked.
void OnCanWrite();
// Connectivity state of QuicTransportChannel.
QuicTransportState quic_state() const { return quic_state_; }
// Creates a new QUIC stream that can send data.
ReliableQuicStream* CreateQuicStream();
TransportChannelImpl* ice_transport_channel() { return channel_.get(); }
// Emitted when |quic_| creates a QUIC stream to receive data from the remote
// peer, when the stream did not exist previously.
sigslot::signal1<ReliableQuicStream*> SignalIncomingStream;
// Emitted when the QuicTransportChannel state becomes QUIC_TRANSPORT_CLOSED.
sigslot::signal0<> SignalClosed;
private:
// Fingerprint of remote peer.
struct RemoteFingerprint {
std::string value;
std::string algorithm;
};
// Callbacks for |channel_|.
void OnReadableState(TransportChannel* channel);
void OnWritableState(TransportChannel* channel);
void OnReadPacket(TransportChannel* channel,
const char* data,
size_t size,
const rtc::PacketTime& packet_time,
int flags);
void OnSentPacket(TransportChannel* channel,
const rtc::SentPacket& sent_packet);
void OnReadyToSend(TransportChannel* channel);
void OnReceivingState(TransportChannel* channel);
void OnGatheringState(TransportChannelImpl* channel);
void OnCandidateGathered(TransportChannelImpl* channel, const Candidate& c);
void OnRoleConflict(TransportChannelImpl* channel);
void OnRouteChange(TransportChannel* channel, const Candidate& candidate);
void OnSelectedCandidatePairChanged(
TransportChannel* channel,
CandidatePairInterface* selected_candidate_pair,
int last_sent_packet_id,
bool ready_to_send);
void OnChannelStateChanged(TransportChannelImpl* channel);
// Callbacks for |quic_|.
// Called when |quic_| has established the crypto handshake.
void OnHandshakeComplete();
// Called when |quic_| has closed the connection.
void OnConnectionClosed(net::QuicErrorCode error, bool from_peer);
// Called when |quic_| has created a new QUIC stream for incoming data.
void OnIncomingStream(ReliableQuicStream* stream);
// Called by OnReadPacket() when a QUIC packet is received.
bool HandleQuicPacket(const char* data, size_t size);
// Sets up the QUIC handshake.
bool MaybeStartQuic();
// Creates the QUIC connection and |quic_|.
bool CreateQuicSession();
// Creates the crypto stream and initializes the handshake.
bool StartQuicHandshake();
// Sets the QuicTransportChannel connectivity state.
void set_quic_state(QuicTransportState state);
// Everything should occur on this thread.
rtc::Thread* network_thread_;
// Underlying channel which is responsible for connecting with the remote peer
// and sending/receiving packets across the network.
std::unique_ptr<TransportChannelImpl> channel_;
// Connectivity state of QuicTransportChannel.
QuicTransportState quic_state_ = QUIC_TRANSPORT_NEW;
// QUIC session which establishes the crypto handshake and converts data
// to/from QUIC packets.
std::unique_ptr<QuicSession> quic_;
// Non-crypto config for |quic_|.
net::QuicConfig config_;
// Helper for net::QuicConnection that provides timing and
// random number generation.
QuicConnectionHelper helper_;
// This peer's role in the QUIC crypto handshake. SSL_CLIENT implies this peer
// initiates the handshake, while SSL_SERVER implies the remote peer initiates
// the handshake. This must be set before we start QUIC.
rtc::Optional<rtc::SSLRole> ssl_role_;
// Config for QUIC crypto client stream, used when |ssl_role_| is SSL_CLIENT.
std::unique_ptr<net::QuicCryptoClientConfig> quic_crypto_client_config_;
// Config for QUIC crypto server stream, used when |ssl_role_| is SSL_SERVER.
std::unique_ptr<net::QuicCryptoServerConfig> quic_crypto_server_config_;
// Used by QUIC crypto server stream to track most recently compressed certs.
std::unique_ptr<net::QuicCompressedCertsCache> quic_compressed_certs_cache_;
// This peer's certificate.
rtc::scoped_refptr<rtc::RTCCertificate> local_certificate_;
// Fingerprint of the remote peer. This must be set before we start QUIC.
rtc::Optional<RemoteFingerprint> remote_fingerprint_;
RTC_DISALLOW_COPY_AND_ASSIGN(QuicTransportChannel);
};
} // namespace cricket
#endif // P2P_QUIC_QUICTRANSPORTCHANNEL_H_

View File

@ -1,550 +0,0 @@
/*
* 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 "p2p/quic/quictransportchannel.h"
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "p2p/base/faketransportcontroller.h"
#include "rtc_base/gunit.h"
#include "rtc_base/sslidentity.h"
using cricket::ConnectionRole;
using cricket::IceRole;
using cricket::QuicTransportChannel;
using cricket::ReliableQuicStream;
using cricket::TransportChannel;
using cricket::TransportDescription;
// Timeout in milliseconds for asynchronous operations in unit tests.
static const int kTimeoutMs = 1000;
// Export keying material parameters.
static const char kExporterLabel[] = "label";
static const uint8_t kExporterContext[] = "context";
static const size_t kExporterContextLength = sizeof(kExporterContext);
static const size_t kOutputKeyLength = 20;
// Packet size for SRTP.
static const size_t kPacketSize = 100;
// Indicates ICE channel has no write error.
static const int kNoWriteError = 0;
// ICE parameters.
static const char kIceUfrag[] = "TESTICEUFRAG0001";
static const char kIcePwd[] = "TESTICEPWD00000000000001";
// QUIC packet parameters.
static const net::IPAddress kIpAddress(0, 0, 0, 0);
static const net::IPEndPoint kIpEndpoint(kIpAddress, 0);
// Detects incoming RTP packets.
static bool IsRtpLeadByte(uint8_t b) {
return (b & 0xC0) == 0x80;
}
// Maps SSL role to ICE connection role. The peer with a client role is assumed
// to be the one who initiates the connection.
static ConnectionRole SslRoleToConnectionRole(rtc::SSLRole ssl_role) {
return (ssl_role == rtc::SSL_CLIENT) ? cricket::CONNECTIONROLE_ACTIVE
: cricket::CONNECTIONROLE_PASSIVE;
}
// Allows cricket::FakeTransportChannel to simulate write blocked
// and write error states.
// TODO(mikescarlett): Add this functionality to cricket::FakeTransportChannel.
class FailableTransportChannel : public cricket::FakeTransportChannel {
public:
FailableTransportChannel(const std::string& name, int component)
: cricket::FakeTransportChannel(name, component), error_(kNoWriteError) {}
int GetError() override { return error_; }
void SetError(int error) { error_ = error; }
int SendPacket(const char* data,
size_t len,
const rtc::PacketOptions& options,
int flags) override {
if (error_ == kNoWriteError) {
return cricket::FakeTransportChannel::SendPacket(data, len, options,
flags);
}
return -1;
}
private:
int error_;
};
// Peer who establishes a handshake using a QuicTransportChannel, which wraps
// a FailableTransportChannel to simulate network connectivity and ICE
// negotiation.
class QuicTestPeer : public sigslot::has_slots<> {
public:
explicit QuicTestPeer(const std::string& name)
: name_(name),
bytes_sent_(0),
ice_channel_(new FailableTransportChannel(name_, 0)),
quic_channel_(ice_channel_),
incoming_stream_count_(0) {
quic_channel_.SignalReadPacket.connect(
this, &QuicTestPeer::OnTransportChannelReadPacket);
quic_channel_.SignalIncomingStream.connect(this,
&QuicTestPeer::OnIncomingStream);
quic_channel_.SignalClosed.connect(this, &QuicTestPeer::OnClosed);
ice_channel_->SetAsync(true);
rtc::scoped_refptr<rtc::RTCCertificate> local_cert =
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate(name_, rtc::KT_DEFAULT)));
quic_channel_.SetLocalCertificate(local_cert);
local_fingerprint_.reset(CreateFingerprint(local_cert.get()));
}
// Connects |ice_channel_| to that of the other peer.
void Connect(QuicTestPeer* other_peer) {
ice_channel_->SetDestination(other_peer->ice_channel_);
}
// Disconnects |ice_channel_|.
void Disconnect() { ice_channel_->SetDestination(nullptr); }
// Generates ICE credentials and passes them to |quic_channel_|.
void SetIceParameters(IceRole local_ice_role,
ConnectionRole local_connection_role,
ConnectionRole remote_connection_role,
rtc::SSLFingerprint* remote_fingerprint) {
quic_channel_.SetIceRole(local_ice_role);
quic_channel_.SetIceTiebreaker(
(local_ice_role == cricket::ICEROLE_CONTROLLING) ? 1 : 2);
TransportDescription local_desc(
std::vector<std::string>(), kIceUfrag, kIcePwd, cricket::ICEMODE_FULL,
local_connection_role, local_fingerprint_.get());
TransportDescription remote_desc(
std::vector<std::string>(), kIceUfrag, kIcePwd, cricket::ICEMODE_FULL,
remote_connection_role, remote_fingerprint);
quic_channel_.SetIceParameters(local_desc.GetIceParameters());
quic_channel_.SetRemoteIceParameters(remote_desc.GetIceParameters());
}
// Creates fingerprint from certificate.
rtc::SSLFingerprint* CreateFingerprint(rtc::RTCCertificate* cert) {
std::string digest_algorithm;
bool get_digest_algorithm =
cert->ssl_certificate().GetSignatureDigestAlgorithm(&digest_algorithm);
if (!get_digest_algorithm || digest_algorithm.empty()) {
return nullptr;
}
std::unique_ptr<rtc::SSLFingerprint> fingerprint(
rtc::SSLFingerprint::Create(digest_algorithm, cert->identity()));
if (digest_algorithm != rtc::DIGEST_SHA_256) {
return nullptr;
}
return fingerprint.release();
}
// Sends SRTP packet to the other peer via |quic_channel_|.
int SendSrtpPacket() {
char packet[kPacketSize];
packet[0] = 0x80; // Make the packet header look like RTP.
int rv = quic_channel_.SendPacket(
&packet[0], kPacketSize, rtc::PacketOptions(), cricket::PF_SRTP_BYPASS);
bytes_sent_ += rv;
return rv;
}
// Sends a non-SRTP packet with the PF_SRTP_BYPASS flag via |quic_channel_|.
int SendInvalidSrtpPacket() {
char packet[kPacketSize];
// Fill the packet with 0 to form an invalid SRTP packet.
memset(packet, 0, kPacketSize);
return quic_channel_.SendPacket(
&packet[0], kPacketSize, rtc::PacketOptions(), cricket::PF_SRTP_BYPASS);
}
// Sends an RTP packet to the other peer via |quic_channel_|, without the SRTP
// bypass flag.
int SendRtpPacket() {
char packet[kPacketSize];
packet[0] = 0x80; // Make the packet header look like RTP.
return quic_channel_.SendPacket(&packet[0], kPacketSize,
rtc::PacketOptions(), 0);
}
void ClearBytesSent() { bytes_sent_ = 0; }
void ClearBytesReceived() { bytes_received_ = 0; }
void SetWriteError(int error) { ice_channel_->SetError(error); }
size_t bytes_received() const { return bytes_received_; }
size_t bytes_sent() const { return bytes_sent_; }
FailableTransportChannel* ice_channel() { return ice_channel_; }
QuicTransportChannel* quic_channel() { return &quic_channel_; }
std::unique_ptr<rtc::SSLFingerprint>& local_fingerprint() {
return local_fingerprint_;
}
ReliableQuicStream* incoming_quic_stream() { return incoming_quic_stream_; }
size_t incoming_stream_count() const { return incoming_stream_count_; }
bool signal_closed_emitted() const { return signal_closed_emitted_; }
private:
// QuicTransportChannel callbacks.
void OnTransportChannelReadPacket(TransportChannel* channel,
const char* data,
size_t size,
const rtc::PacketTime& packet_time,
int flags) {
bytes_received_ += size;
// Only SRTP packets should have the bypass flag set.
int expected_flags = IsRtpLeadByte(data[0]) ? cricket::PF_SRTP_BYPASS : 0;
ASSERT_EQ(expected_flags, flags);
}
void OnIncomingStream(ReliableQuicStream* stream) {
incoming_quic_stream_ = stream;
++incoming_stream_count_;
}
void OnClosed() { signal_closed_emitted_ = true; }
std::string name_; // Channel name.
size_t bytes_sent_; // Bytes sent by QUIC channel.
size_t bytes_received_; // Bytes received by QUIC channel.
FailableTransportChannel* ice_channel_; // Simulates an ICE channel.
QuicTransportChannel quic_channel_; // QUIC channel to test.
std::unique_ptr<rtc::SSLFingerprint> local_fingerprint_;
ReliableQuicStream* incoming_quic_stream_ = nullptr;
size_t incoming_stream_count_;
bool signal_closed_emitted_ = false;
};
class QuicTransportChannelTest : public testing::Test {
public:
QuicTransportChannelTest() : peer1_("P1"), peer2_("P2") {}
// Performs negotiation before QUIC handshake, then connects the fake
// transport channels of each peer. As a side effect, the QUIC channels
// start sending handshake messages. |peer1_| has a client role and |peer2_|
// has server role in the QUIC handshake.
void Connect() {
SetIceAndCryptoParameters(rtc::SSL_CLIENT, rtc::SSL_SERVER);
peer1_.Connect(&peer2_);
}
// Disconnects the fake transport channels.
void Disconnect() {
peer1_.Disconnect();
peer2_.Disconnect();
}
// Sets up ICE parameters and exchanges fingerprints before QUIC handshake.
void SetIceAndCryptoParameters(rtc::SSLRole peer1_ssl_role,
rtc::SSLRole peer2_ssl_role) {
peer1_.quic_channel()->SetSslRole(peer1_ssl_role);
peer2_.quic_channel()->SetSslRole(peer2_ssl_role);
std::unique_ptr<rtc::SSLFingerprint>& peer1_fingerprint =
peer1_.local_fingerprint();
std::unique_ptr<rtc::SSLFingerprint>& peer2_fingerprint =
peer2_.local_fingerprint();
peer1_.quic_channel()->SetRemoteFingerprint(
peer2_fingerprint->algorithm,
reinterpret_cast<const uint8_t*>(peer2_fingerprint->digest.data()),
peer2_fingerprint->digest.size());
peer2_.quic_channel()->SetRemoteFingerprint(
peer1_fingerprint->algorithm,
reinterpret_cast<const uint8_t*>(peer1_fingerprint->digest.data()),
peer1_fingerprint->digest.size());
ConnectionRole peer1_connection_role =
SslRoleToConnectionRole(peer1_ssl_role);
ConnectionRole peer2_connection_role =
SslRoleToConnectionRole(peer2_ssl_role);
peer1_.SetIceParameters(cricket::ICEROLE_CONTROLLED, peer1_connection_role,
peer2_connection_role, peer2_fingerprint.get());
peer2_.SetIceParameters(cricket::ICEROLE_CONTROLLING, peer2_connection_role,
peer1_connection_role, peer1_fingerprint.get());
}
// Checks if QUIC handshake is done.
bool quic_connected() {
return peer1_.quic_channel()->quic_state() ==
cricket::QUIC_TRANSPORT_CONNECTED &&
peer2_.quic_channel()->quic_state() ==
cricket::QUIC_TRANSPORT_CONNECTED;
}
// Checks if QUIC channels are writable.
bool quic_writable() {
return peer1_.quic_channel()->writable() &&
peer2_.quic_channel()->writable();
}
protected:
// QUIC peer with a client role, who initiates the QUIC handshake.
QuicTestPeer peer1_;
// QUIC peer with a server role, who responds to the client peer.
QuicTestPeer peer2_;
};
// Test that the QUIC channel passes ICE parameters to the underlying ICE
// channel.
TEST_F(QuicTransportChannelTest, ChannelSetupIce) {
SetIceAndCryptoParameters(rtc::SSL_CLIENT, rtc::SSL_SERVER);
FailableTransportChannel* channel1 = peer1_.ice_channel();
FailableTransportChannel* channel2 = peer2_.ice_channel();
EXPECT_EQ(cricket::ICEROLE_CONTROLLED, channel1->GetIceRole());
EXPECT_EQ(2u, channel1->IceTiebreaker());
EXPECT_EQ(kIceUfrag, channel1->ice_ufrag());
EXPECT_EQ(kIcePwd, channel1->ice_pwd());
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel2->GetIceRole());
EXPECT_EQ(1u, channel2->IceTiebreaker());
}
// Test that export keying material generates identical keys for both peers
// after the QUIC handshake.
TEST_F(QuicTransportChannelTest, ExportKeyingMaterial) {
Connect();
ASSERT_TRUE_WAIT(quic_connected(), kTimeoutMs);
uint8_t key1[kOutputKeyLength];
uint8_t key2[kOutputKeyLength];
bool from_success = peer1_.quic_channel()->ExportKeyingMaterial(
kExporterLabel, kExporterContext, kExporterContextLength, true, key1,
kOutputKeyLength);
ASSERT_TRUE(from_success);
bool to_success = peer2_.quic_channel()->ExportKeyingMaterial(
kExporterLabel, kExporterContext, kExporterContextLength, true, key2,
kOutputKeyLength);
ASSERT_TRUE(to_success);
EXPECT_EQ(0, memcmp(key1, key2, sizeof(key1)));
}
// Test that the QUIC channel is not writable before the QUIC handshake.
TEST_F(QuicTransportChannelTest, NotWritableBeforeHandshake) {
Connect();
EXPECT_FALSE(quic_writable());
Disconnect();
EXPECT_FALSE(quic_writable());
Connect();
EXPECT_FALSE(quic_writable());
}
// Test that once handshake begins, QUIC is not writable until its completion.
TEST_F(QuicTransportChannelTest, QuicHandshake) {
Connect();
EXPECT_FALSE(quic_writable());
ASSERT_TRUE_WAIT(quic_connected(), kTimeoutMs);
EXPECT_TRUE(quic_writable());
}
// Test that Non-SRTP data is not sent using SendPacket(), regardless of QUIC
// channel state.
TEST_F(QuicTransportChannelTest, TransferNonSrtp) {
// Send data before ICE channel is connected.
peer1_.ClearBytesSent();
peer2_.ClearBytesReceived();
ASSERT_EQ(-1, peer1_.SendRtpPacket());
EXPECT_EQ(0u, peer1_.bytes_sent());
// Send data after ICE channel is connected, before QUIC handshake.
Connect();
peer1_.ClearBytesSent();
peer2_.ClearBytesReceived();
ASSERT_EQ(-1, peer1_.SendRtpPacket());
EXPECT_EQ(0u, peer1_.bytes_sent());
// Send data after QUIC handshake.
ASSERT_TRUE_WAIT(quic_connected(), kTimeoutMs);
peer1_.ClearBytesSent();
peer2_.ClearBytesReceived();
ASSERT_EQ(-1, peer1_.SendRtpPacket());
EXPECT_EQ(0u, peer1_.bytes_sent());
}
// Test that SRTP data is always be sent, regardless of QUIC channel state, when
// the ICE channel is connected.
TEST_F(QuicTransportChannelTest, TransferSrtp) {
// Send data after ICE channel is connected, before QUIC handshake.
Connect();
peer1_.ClearBytesSent();
peer2_.ClearBytesReceived();
ASSERT_EQ(kPacketSize, static_cast<size_t>(peer1_.SendSrtpPacket()));
EXPECT_EQ_WAIT(kPacketSize, peer2_.bytes_received(), kTimeoutMs);
EXPECT_EQ(kPacketSize, peer1_.bytes_sent());
ASSERT_TRUE_WAIT(quic_connected(), kTimeoutMs);
// Send data after QUIC handshake.
peer1_.ClearBytesSent();
peer2_.ClearBytesReceived();
ASSERT_EQ(kPacketSize, static_cast<size_t>(peer1_.SendSrtpPacket()));
EXPECT_EQ_WAIT(kPacketSize, peer2_.bytes_received(), kTimeoutMs);
EXPECT_EQ(kPacketSize, peer1_.bytes_sent());
}
// Test that invalid SRTP (non-SRTP data with
// PF_SRTP_BYPASS flag) fails to send with return value -1.
TEST_F(QuicTransportChannelTest, TransferInvalidSrtp) {
peer1_.ClearBytesSent();
peer2_.ClearBytesReceived();
EXPECT_EQ(-1, peer1_.SendInvalidSrtpPacket());
EXPECT_EQ(0u, peer2_.bytes_received());
Connect();
peer1_.ClearBytesSent();
peer2_.ClearBytesReceived();
EXPECT_EQ(-1, peer1_.SendInvalidSrtpPacket());
EXPECT_EQ(0u, peer2_.bytes_received());
}
// Test that QuicTransportChannel::WritePacket blocks when the ICE
// channel is not writable, and otherwise succeeds.
TEST_F(QuicTransportChannelTest, QuicWritePacket) {
peer1_.ice_channel()->SetDestination(peer2_.ice_channel());
std::string packet = "FAKEQUICPACKET";
// QUIC should be write blocked when the ICE channel is not writable.
peer1_.ice_channel()->SetWritable(false);
EXPECT_TRUE(peer1_.quic_channel()->IsWriteBlocked());
net::WriteResult write_blocked_result = peer1_.quic_channel()->WritePacket(
packet.data(), packet.size(), kIpAddress, kIpEndpoint, nullptr);
EXPECT_EQ(net::WRITE_STATUS_BLOCKED, write_blocked_result.status);
EXPECT_EQ(EWOULDBLOCK, write_blocked_result.error_code);
// QUIC should ignore errors when the ICE channel is writable.
peer1_.ice_channel()->SetWritable(true);
EXPECT_FALSE(peer1_.quic_channel()->IsWriteBlocked());
peer1_.SetWriteError(EWOULDBLOCK);
net::WriteResult ignore_error_result = peer1_.quic_channel()->WritePacket(
packet.data(), packet.size(), kIpAddress, kIpEndpoint, nullptr);
EXPECT_EQ(net::WRITE_STATUS_OK, ignore_error_result.status);
EXPECT_EQ(0, ignore_error_result.bytes_written);
peer1_.SetWriteError(kNoWriteError);
net::WriteResult no_error_result = peer1_.quic_channel()->WritePacket(
packet.data(), packet.size(), kIpAddress, kIpEndpoint, nullptr);
EXPECT_EQ(net::WRITE_STATUS_OK, no_error_result.status);
EXPECT_EQ(static_cast<int>(packet.size()), no_error_result.bytes_written);
}
// Test that SSL roles can be reversed before QUIC handshake.
TEST_F(QuicTransportChannelTest, QuicRoleReversalBeforeQuic) {
EXPECT_TRUE(peer1_.quic_channel()->SetSslRole(rtc::SSL_SERVER));
EXPECT_TRUE(peer1_.quic_channel()->SetSslRole(rtc::SSL_CLIENT));
EXPECT_TRUE(peer1_.quic_channel()->SetSslRole(rtc::SSL_SERVER));
}
// Test that SSL roles cannot be reversed after the QUIC handshake. SetSslRole
// returns true if the current SSL role equals the proposed SSL role.
TEST_F(QuicTransportChannelTest, QuicRoleReversalAfterQuic) {
Connect();
ASSERT_TRUE_WAIT(quic_connected(), kTimeoutMs);
EXPECT_FALSE(peer1_.quic_channel()->SetSslRole(rtc::SSL_SERVER));
EXPECT_TRUE(peer1_.quic_channel()->SetSslRole(rtc::SSL_CLIENT));
EXPECT_FALSE(peer2_.quic_channel()->SetSslRole(rtc::SSL_CLIENT));
EXPECT_TRUE(peer2_.quic_channel()->SetSslRole(rtc::SSL_SERVER));
}
// Set the SSL role, then test that GetSslRole returns the same value.
TEST_F(QuicTransportChannelTest, SetGetSslRole) {
ASSERT_TRUE(peer1_.quic_channel()->SetSslRole(rtc::SSL_SERVER));
std::unique_ptr<rtc::SSLRole> role(new rtc::SSLRole());
ASSERT_TRUE(peer1_.quic_channel()->GetSslRole(role.get()));
EXPECT_EQ(rtc::SSL_SERVER, *role);
}
// Test that after the QUIC handshake is complete, the QUIC handshake remains
// confirmed even if the ICE channel reconnects.
TEST_F(QuicTransportChannelTest, HandshakeConfirmedAfterReconnect) {
Connect();
ASSERT_TRUE_WAIT(quic_connected(), kTimeoutMs);
Disconnect();
EXPECT_TRUE(quic_connected());
Connect();
EXPECT_TRUE(quic_connected());
}
// Test that if the ICE channel becomes receiving after the QUIC channel is
// connected, then the QUIC channel becomes receiving.
TEST_F(QuicTransportChannelTest, IceReceivingAfterConnected) {
Connect();
ASSERT_TRUE_WAIT(quic_connected(), kTimeoutMs);
ASSERT_FALSE(peer1_.ice_channel()->receiving());
EXPECT_FALSE(peer1_.quic_channel()->receiving());
peer1_.ice_channel()->SetReceiving(true);
EXPECT_TRUE(peer1_.quic_channel()->receiving());
}
// Test that if the ICE channel becomes receiving before the QUIC channel is
// connected, then the QUIC channel becomes receiving.
TEST_F(QuicTransportChannelTest, IceReceivingBeforeConnected) {
Connect();
peer1_.ice_channel()->SetReceiving(true);
ASSERT_TRUE(peer1_.ice_channel()->receiving());
ASSERT_TRUE_WAIT(quic_connected(), kTimeoutMs);
EXPECT_TRUE(peer1_.quic_channel()->receiving());
}
// Test that when peer 1 creates an outgoing stream, peer 2 creates an incoming
// QUIC stream with the same ID and fires OnIncomingStream.
TEST_F(QuicTransportChannelTest, CreateOutgoingAndIncomingQuicStream) {
Connect();
EXPECT_EQ(nullptr, peer1_.quic_channel()->CreateQuicStream());
ASSERT_TRUE_WAIT(quic_connected(), kTimeoutMs);
ReliableQuicStream* stream = peer1_.quic_channel()->CreateQuicStream();
ASSERT_NE(nullptr, stream);
stream->Write("Hi", 2);
EXPECT_TRUE_WAIT(peer2_.incoming_quic_stream() != nullptr, kTimeoutMs);
EXPECT_EQ(stream->id(), peer2_.incoming_quic_stream()->id());
}
// Test that if the QuicTransportChannel is unwritable, then all outgoing QUIC
// streams can send data once the QuicTransprotChannel becomes writable again.
TEST_F(QuicTransportChannelTest, OutgoingQuicStreamSendsDataAfterReconnect) {
Connect();
ASSERT_TRUE_WAIT(quic_connected(), kTimeoutMs);
ReliableQuicStream* stream1 = peer1_.quic_channel()->CreateQuicStream();
ASSERT_NE(nullptr, stream1);
ReliableQuicStream* stream2 = peer1_.quic_channel()->CreateQuicStream();
ASSERT_NE(nullptr, stream2);
peer1_.ice_channel()->SetWritable(false);
stream1->Write("First", 5);
EXPECT_EQ(5u, stream1->queued_data_bytes());
stream2->Write("Second", 6);
EXPECT_EQ(6u, stream2->queued_data_bytes());
EXPECT_EQ(0u, peer2_.incoming_stream_count());
peer1_.ice_channel()->SetWritable(true);
EXPECT_EQ_WAIT(0u, stream1->queued_data_bytes(), kTimeoutMs);
EXPECT_EQ_WAIT(0u, stream2->queued_data_bytes(), kTimeoutMs);
EXPECT_EQ_WAIT(2u, peer2_.incoming_stream_count(), kTimeoutMs);
}
// Test that SignalClosed is emitted when the QuicConnection closes.
TEST_F(QuicTransportChannelTest, SignalClosedEmitted) {
Connect();
ASSERT_TRUE_WAIT(quic_connected(), kTimeoutMs);
ASSERT_FALSE(peer1_.signal_closed_emitted());
ReliableQuicStream* stream = peer1_.quic_channel()->CreateQuicStream();
ASSERT_NE(nullptr, stream);
stream->CloseConnectionWithDetails(net::QuicErrorCode::QUIC_NO_ERROR,
"Closing QUIC for testing");
EXPECT_TRUE(peer1_.signal_closed_emitted());
EXPECT_TRUE_WAIT(peer2_.signal_closed_emitted(), kTimeoutMs);
}

View File

@ -1,64 +0,0 @@
/*
* 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 "p2p/quic/reliablequicstream.h"
#include <string>
#include "net/quic/quic_session.h"
#include "rtc_base/checks.h"
namespace cricket {
ReliableQuicStream::ReliableQuicStream(net::QuicStreamId id,
net::QuicSession* session)
: net::ReliableQuicStream(id, session) {
RTC_DCHECK_NE(net::kCryptoStreamId, id);
}
ReliableQuicStream::~ReliableQuicStream() {}
void ReliableQuicStream::OnDataAvailable() {
struct iovec iov;
while (sequencer()->GetReadableRegions(&iov, 1) == 1) {
SignalDataReceived(id(), reinterpret_cast<const char*>(iov.iov_base),
iov.iov_len);
sequencer()->MarkConsumed(iov.iov_len);
}
}
void ReliableQuicStream::OnClose() {
net::ReliableQuicStream::OnClose();
SignalClosed(id(), connection_error());
}
rtc::StreamResult ReliableQuicStream::Write(const char* data,
size_t len,
bool fin) {
// Writes the data, or buffers it.
WriteOrBufferData(base::StringPiece(data, len), fin, nullptr);
if (HasBufferedData()) {
return rtc::StreamResult(rtc::SR_BLOCK);
}
return rtc::StreamResult(rtc::SR_SUCCESS);
}
void ReliableQuicStream::Close() {
net::ReliableQuicStream::session()->CloseStream(id());
}
void ReliableQuicStream::OnCanWrite() {
uint64_t prev_queued_bytes = queued_data_bytes();
net::ReliableQuicStream::OnCanWrite();
uint64_t queued_bytes_written = prev_queued_bytes - queued_data_bytes();
SignalQueuedBytesWritten(id(), queued_bytes_written);
}
} // namespace cricket

View File

@ -1,56 +0,0 @@
/*
* 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.
*/
#ifndef P2P_QUIC_RELIABLEQUICSTREAM_H_
#define P2P_QUIC_RELIABLEQUICSTREAM_H_
#include "net/quic/reliable_quic_stream.h"
#include "rtc_base/constructormagic.h"
#include "rtc_base/sigslot.h"
#include "rtc_base/stream.h"
namespace cricket {
// Streams created by QuicSession.
class ReliableQuicStream : public net::ReliableQuicStream,
public sigslot::has_slots<> {
public:
ReliableQuicStream(net::QuicStreamId id, net::QuicSession* session);
~ReliableQuicStream() override;
// ReliableQuicStream overrides.
void OnDataAvailable() override;
void OnClose() override;
void OnCanWrite() override;
// Process decrypted data into encrypted QUIC packets, which get sent to the
// QuicPacketWriter. rtc::SR_BLOCK is returned if the operation blocks instead
// of writing, in which case the data is queued until OnCanWrite() is called.
// If |fin| == true, then this stream closes after sending data.
rtc::StreamResult Write(const char* data, size_t len, bool fin = false);
// Removes this stream from the QuicSession's stream map.
void Close();
// Called when decrypted data is ready to be read.
sigslot::signal3<net::QuicStreamId, const char*, size_t> SignalDataReceived;
// Called when the stream is closed.
sigslot::signal2<net::QuicStreamId, int> SignalClosed;
// Emits the number of queued bytes that were written by OnCanWrite(), after
// the stream was previously write blocked.
sigslot::signal2<net::QuicStreamId, uint64_t> SignalQueuedBytesWritten;
private:
RTC_DISALLOW_COPY_AND_ASSIGN(ReliableQuicStream);
};
} // namespace cricket
#endif // P2P_QUIC_RELIABLEQUICSTREAM_H_

View File

@ -1,257 +0,0 @@
/*
* 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 "p2p/quic/reliablequicstream.h"
#include <memory>
#include <string>
#include "net/base/ip_address_number.h"
#include "net/quic/quic_connection.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_session.h"
#include "p2p/quic/quicconnectionhelper.h"
#include "rtc_base/buffer.h"
#include "rtc_base/gunit.h"
#include "rtc_base/sigslot.h"
#include "rtc_base/stream.h"
using cricket::QuicConnectionHelper;
using cricket::ReliableQuicStream;
using net::IPAddress;
using net::IPEndPoint;
using net::PerPacketOptions;
using net::Perspective;
using net::QuicAckListenerInterface;
using net::QuicConfig;
using net::QuicConnection;
using net::QuicConsumedData;
using net::QuicCryptoStream;
using net::QuicErrorCode;
using net::QuicIOVector;
using net::QuicPacketWriter;
using net::QuicRstStreamErrorCode;
using net::QuicSession;
using net::QuicStreamId;
using net::QuicStreamOffset;
using net::SpdyPriority;
using rtc::SR_SUCCESS;
using rtc::SR_BLOCK;
// Arbitrary number for a stream's write blocked priority.
static const SpdyPriority kDefaultPriority = 3;
static const net::QuicStreamId kStreamId = 5;
// QuicSession that does not create streams and writes data from
// ReliableQuicStream to a string.
class MockQuicSession : public QuicSession {
public:
MockQuicSession(QuicConnection* connection,
const QuicConfig& config,
std::string* write_buffer)
: QuicSession(connection, config), write_buffer_(write_buffer) {}
// Writes outgoing data from ReliableQuicStream to a string.
QuicConsumedData WritevData(
QuicStreamId id,
QuicIOVector iovector,
QuicStreamOffset offset,
bool fin,
QuicAckListenerInterface* ack_notifier_delegate) override {
if (!writable_) {
return QuicConsumedData(0, false);
}
const char* data = reinterpret_cast<const char*>(iovector.iov->iov_base);
size_t len = iovector.total_length;
write_buffer_->append(data, len);
return QuicConsumedData(len, false);
}
net::ReliableQuicStream* CreateIncomingDynamicStream(
QuicStreamId id) override {
return new ReliableQuicStream(kStreamId, this);
}
net::ReliableQuicStream* CreateOutgoingDynamicStream(
SpdyPriority priority) override {
return nullptr;
}
QuicCryptoStream* GetCryptoStream() override { return nullptr; }
// Called by ReliableQuicStream when they want to close stream.
void SendRstStream(QuicStreamId id,
QuicRstStreamErrorCode error,
QuicStreamOffset bytes_written) override {}
// Sets whether data is written to buffer, or else if this is write blocked.
void set_writable(bool writable) { writable_ = writable; }
// Tracks whether the stream is write blocked and its priority.
void register_write_blocked_stream(QuicStreamId stream_id,
SpdyPriority priority) {
write_blocked_streams()->RegisterStream(stream_id, priority);
}
private:
// Stores written data from ReliableQuicStream.
std::string* write_buffer_;
// Whether data is written to write_buffer_.
bool writable_ = true;
};
// Packet writer that does nothing. This is required for QuicConnection but
// isn't used for writing data.
class DummyPacketWriter : public QuicPacketWriter {
public:
DummyPacketWriter() {}
// QuicPacketWriter overrides.
net::WriteResult WritePacket(const char* buffer,
size_t buf_len,
const IPAddress& self_address,
const IPEndPoint& peer_address,
PerPacketOptions* options) override {
return net::WriteResult(net::WRITE_STATUS_ERROR, 0);
}
bool IsWriteBlockedDataBuffered() const override { return false; }
bool IsWriteBlocked() const override { return false; };
void SetWritable() override {}
net::QuicByteCount GetMaxPacketSize(
const net::IPEndPoint& peer_address) const override {
return 0;
}
};
class ReliableQuicStreamTest : public ::testing::Test,
public sigslot::has_slots<> {
public:
ReliableQuicStreamTest() {}
void CreateReliableQuicStream() {
// Arbitrary values for QuicConnection.
QuicConnectionHelper* quic_helper =
new QuicConnectionHelper(rtc::Thread::Current());
Perspective perspective = Perspective::IS_SERVER;
net::IPAddress ip(0, 0, 0, 0);
bool owns_writer = true;
QuicConnection* connection = new QuicConnection(
0, IPEndPoint(ip, 0), quic_helper, new DummyPacketWriter(), owns_writer,
perspective, net::QuicSupportedVersions());
session_.reset(
new MockQuicSession(connection, QuicConfig(), &write_buffer_));
stream_.reset(new ReliableQuicStream(kStreamId, session_.get()));
stream_->SignalDataReceived.connect(
this, &ReliableQuicStreamTest::OnDataReceived);
stream_->SignalClosed.connect(this, &ReliableQuicStreamTest::OnClosed);
stream_->SignalQueuedBytesWritten.connect(
this, &ReliableQuicStreamTest::OnQueuedBytesWritten);
session_->register_write_blocked_stream(stream_->id(), kDefaultPriority);
}
void OnDataReceived(QuicStreamId id, const char* data, size_t length) {
ASSERT_EQ(id, stream_->id());
read_buffer_.append(data, length);
}
void OnClosed(QuicStreamId id, int err) { closed_ = true; }
void OnQueuedBytesWritten(QuicStreamId id, uint64_t queued_bytes_written) {
queued_bytes_written_ = queued_bytes_written;
}
protected:
std::unique_ptr<ReliableQuicStream> stream_;
std::unique_ptr<MockQuicSession> session_;
// Data written by the ReliableQuicStream.
std::string write_buffer_;
// Data read by the ReliableQuicStream.
std::string read_buffer_;
// Whether the ReliableQuicStream is closed.
bool closed_ = false;
// Bytes written by OnCanWrite().
uint64_t queued_bytes_written_;
};
// Write an entire string.
TEST_F(ReliableQuicStreamTest, WriteDataWhole) {
CreateReliableQuicStream();
EXPECT_EQ(SR_SUCCESS, stream_->Write("Foo bar", 7));
EXPECT_EQ("Foo bar", write_buffer_);
}
// Write part of a string.
TEST_F(ReliableQuicStreamTest, WriteDataPartial) {
CreateReliableQuicStream();
EXPECT_EQ(SR_SUCCESS, stream_->Write("Hello, World!", 8));
EXPECT_EQ("Hello, W", write_buffer_);
}
// Test that strings are buffered correctly.
TEST_F(ReliableQuicStreamTest, BufferData) {
CreateReliableQuicStream();
session_->set_writable(false);
EXPECT_EQ(SR_BLOCK, stream_->Write("Foo bar", 7));
EXPECT_EQ(0ul, write_buffer_.size());
EXPECT_TRUE(stream_->HasBufferedData());
session_->set_writable(true);
stream_->OnCanWrite();
EXPECT_EQ(7ul, queued_bytes_written_);
EXPECT_FALSE(stream_->HasBufferedData());
EXPECT_EQ("Foo bar", write_buffer_);
EXPECT_EQ(SR_SUCCESS, stream_->Write("xyzzy", 5));
EXPECT_EQ("Foo barxyzzy", write_buffer_);
}
// Read an entire string.
TEST_F(ReliableQuicStreamTest, ReadDataWhole) {
CreateReliableQuicStream();
net::QuicStreamFrame frame(kStreamId, false, 0, "Hello, World!");
stream_->OnStreamFrame(frame);
EXPECT_EQ("Hello, World!", read_buffer_);
}
// Read part of a string.
TEST_F(ReliableQuicStreamTest, ReadDataPartial) {
CreateReliableQuicStream();
net::QuicStreamFrame frame(kStreamId, false, 0, "Hello, World!");
frame.frame_length = 5;
stream_->OnStreamFrame(frame);
EXPECT_EQ("Hello", read_buffer_);
}
// Test that closing the stream results in a callback.
TEST_F(ReliableQuicStreamTest, CloseStream) {
CreateReliableQuicStream();
EXPECT_FALSE(closed_);
stream_->OnClose();
EXPECT_TRUE(closed_);
}

View File

@ -232,19 +232,6 @@ rtc_source_set("libjingle_peerconnection") {
":peerconnection", ":peerconnection",
"../api:libjingle_peerconnection_api", "../api:libjingle_peerconnection_api",
] ]
if (rtc_use_quic) {
sources += [
"quicdatachannel.cc",
"quicdatachannel.h",
"quicdatatransport.cc",
"quicdatatransport.h",
]
deps += [ "//third_party/libquic" ]
public_deps = [
"//third_party/libquic",
]
}
} }
if (rtc_include_tests) { if (rtc_include_tests) {
@ -440,16 +427,6 @@ if (rtc_include_tests) {
] ]
} }
if (rtc_use_quic) {
public_deps = [
"//third_party/libquic",
]
sources += [
"quicdatachannel_unittest.cc",
"quicdatatransport_unittest.cc",
]
}
deps = [] deps = []
if (is_android) { if (is_android) {
deps += [ ":android_black_magic" ] deps += [ ":android_black_magic" ]

View File

@ -270,7 +270,6 @@ bool PeerConnectionInterface::RTCConfiguration::operator==(
ContinualGatheringPolicy continual_gathering_policy; ContinualGatheringPolicy continual_gathering_policy;
bool prioritize_most_likely_ice_candidate_pairs; bool prioritize_most_likely_ice_candidate_pairs;
struct cricket::MediaConfig media_config; struct cricket::MediaConfig media_config;
bool enable_quic;
bool prune_turn_ports; bool prune_turn_ports;
bool presume_writable_when_fully_relayed; bool presume_writable_when_fully_relayed;
bool enable_ice_renomination; bool enable_ice_renomination;
@ -302,7 +301,6 @@ bool PeerConnectionInterface::RTCConfiguration::operator==(
disable_ipv6_on_wifi == o.disable_ipv6_on_wifi && disable_ipv6_on_wifi == o.disable_ipv6_on_wifi &&
max_ipv6_networks == o.max_ipv6_networks && max_ipv6_networks == o.max_ipv6_networks &&
enable_rtp_data_channel == o.enable_rtp_data_channel && enable_rtp_data_channel == o.enable_rtp_data_channel &&
enable_quic == o.enable_quic &&
screencast_min_bitrate == o.screencast_min_bitrate && screencast_min_bitrate == o.screencast_min_bitrate &&
combined_audio_video_bwe == o.combined_audio_video_bwe && combined_audio_video_bwe == o.combined_audio_video_bwe &&
enable_dtls_srtp == o.enable_dtls_srtp && enable_dtls_srtp == o.enable_dtls_srtp &&
@ -781,22 +779,6 @@ PeerConnection::CreateDataChannel(
const std::string& label, const std::string& label,
const DataChannelInit* config) { const DataChannelInit* config) {
TRACE_EVENT0("webrtc", "PeerConnection::CreateDataChannel"); TRACE_EVENT0("webrtc", "PeerConnection::CreateDataChannel");
#ifdef HAVE_QUIC
if (session_->data_channel_type() == cricket::DCT_QUIC) {
// TODO(zhihuang): Handle case when config is NULL.
if (!config) {
LOG(LS_ERROR) << "Missing config for QUIC data channel.";
return nullptr;
}
// TODO(zhihuang): Allow unreliable or ordered QUIC data channels.
if (!config->reliable || config->ordered) {
LOG(LS_ERROR) << "QUIC data channel does not implement unreliable or "
"ordered delivery.";
return nullptr;
}
return session_->quic_data_transport()->CreateDataChannel(label, config);
}
#endif // HAVE_QUIC
bool first_datachannel = !HasDataChannels(); bool first_datachannel = !HasDataChannels();
@ -2356,13 +2338,7 @@ rtc::scoped_refptr<DataChannel> PeerConnection::InternalCreateDataChannel(
} }
bool PeerConnection::HasDataChannels() const { bool PeerConnection::HasDataChannels() const {
#ifdef HAVE_QUIC
return !rtp_data_channels_.empty() || !sctp_data_channels_.empty() ||
(session_->quic_data_transport() &&
session_->quic_data_transport()->HasDataChannels());
#else
return !rtp_data_channels_.empty() || !sctp_data_channels_.empty(); return !rtp_data_channels_.empty() || !sctp_data_channels_.empty();
#endif // HAVE_QUIC
} }
void PeerConnection::AllocateSctpSids(rtc::SSLRole role) { void PeerConnection::AllocateSctpSids(rtc::SSLRole role) {

View File

@ -495,85 +495,7 @@ TEST_F(PeerConnectionEndToEndTest,
EXPECT_EQ(1U, dc_1_observer->received_message_count()); EXPECT_EQ(1U, dc_1_observer->received_message_count());
EXPECT_EQ(1U, dc_2_observer->received_message_count()); EXPECT_EQ(1U, dc_2_observer->received_message_count());
} }
#endif // HAVE_SCTP
#ifdef HAVE_QUIC
// Test that QUIC data channels can be used and that messages go to the correct
// remote data channel when both peers want to use QUIC. It is assumed that the
// application has externally negotiated the data channel parameters.
TEST_F(PeerConnectionEndToEndTest, MessageTransferBetweenQuicDataChannels) {
config_.enable_quic = true;
CreatePcs();
webrtc::DataChannelInit init_1;
init_1.id = 0;
init_1.ordered = false;
init_1.reliable = true;
webrtc::DataChannelInit init_2;
init_2.id = 1;
init_2.ordered = false;
init_2.reliable = true;
rtc::scoped_refptr<DataChannelInterface> caller_dc_1(
caller_->CreateDataChannel("data", init_1));
ASSERT_NE(nullptr, caller_dc_1);
rtc::scoped_refptr<DataChannelInterface> caller_dc_2(
caller_->CreateDataChannel("data", init_2));
ASSERT_NE(nullptr, caller_dc_2);
rtc::scoped_refptr<DataChannelInterface> callee_dc_1(
callee_->CreateDataChannel("data", init_1));
ASSERT_NE(nullptr, callee_dc_1);
rtc::scoped_refptr<DataChannelInterface> callee_dc_2(
callee_->CreateDataChannel("data", init_2));
ASSERT_NE(nullptr, callee_dc_2);
Negotiate();
WaitForConnection();
EXPECT_TRUE_WAIT(caller_dc_1->state() == webrtc::DataChannelInterface::kOpen,
kMaxWait);
EXPECT_TRUE_WAIT(callee_dc_1->state() == webrtc::DataChannelInterface::kOpen,
kMaxWait);
EXPECT_TRUE_WAIT(caller_dc_2->state() == webrtc::DataChannelInterface::kOpen,
kMaxWait);
EXPECT_TRUE_WAIT(callee_dc_2->state() == webrtc::DataChannelInterface::kOpen,
kMaxWait);
std::unique_ptr<webrtc::MockDataChannelObserver> dc_1_observer(
new webrtc::MockDataChannelObserver(callee_dc_1.get()));
std::unique_ptr<webrtc::MockDataChannelObserver> dc_2_observer(
new webrtc::MockDataChannelObserver(callee_dc_2.get()));
const std::string message_1 = "hello 1";
const std::string message_2 = "hello 2";
// Send data from caller to callee.
caller_dc_1->Send(webrtc::DataBuffer(message_1));
EXPECT_EQ_WAIT(message_1, dc_1_observer->last_message(), kMaxWait);
caller_dc_2->Send(webrtc::DataBuffer(message_2));
EXPECT_EQ_WAIT(message_2, dc_2_observer->last_message(), kMaxWait);
EXPECT_EQ(1U, dc_1_observer->received_message_count());
EXPECT_EQ(1U, dc_2_observer->received_message_count());
// Send data from callee to caller.
dc_1_observer.reset(new webrtc::MockDataChannelObserver(caller_dc_1.get()));
dc_2_observer.reset(new webrtc::MockDataChannelObserver(caller_dc_2.get()));
callee_dc_1->Send(webrtc::DataBuffer(message_1));
EXPECT_EQ_WAIT(message_1, dc_1_observer->last_message(), kMaxWait);
callee_dc_2->Send(webrtc::DataBuffer(message_2));
EXPECT_EQ_WAIT(message_2, dc_2_observer->last_message(), kMaxWait);
EXPECT_EQ(1U, dc_1_observer->received_message_count());
EXPECT_EQ(1U, dc_2_observer->received_message_count());
}
#endif // HAVE_QUIC
#ifdef HAVE_SCTP
// Verifies that a DataChannel added from an OPEN message functions after // Verifies that a DataChannel added from an OPEN message functions after
// a channel has been previously closed (webrtc issue 3778). // a channel has been previously closed (webrtc issue 3778).
// This previously failed because the new channel re-uses the ID of the closed // This previously failed because the new channel re-uses the ID of the closed

View File

@ -1,396 +0,0 @@
/*
* 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 "pc/quicdatachannel.h"
#include "p2p/quic/quictransportchannel.h"
#include "p2p/quic/reliablequicstream.h"
#include "rtc_base/bind.h"
#include "rtc_base/bytebuffer.h"
#include "rtc_base/copyonwritebuffer.h"
#include "rtc_base/logging.h"
namespace webrtc {
void WriteQuicDataChannelMessageHeader(int data_channel_id,
uint64_t message_id,
rtc::CopyOnWriteBuffer* header) {
RTC_DCHECK(header);
// 64-bit varints require at most 10 bytes (7*10 == 70), and 32-bit varints
// require at most 5 bytes (7*5 == 35).
size_t max_length = 15;
rtc::ByteBufferWriter byte_buffer(nullptr, max_length,
rtc::ByteBuffer::ByteOrder::ORDER_HOST);
byte_buffer.WriteUVarint(data_channel_id);
byte_buffer.WriteUVarint(message_id);
header->SetData(byte_buffer.Data(), byte_buffer.Length());
}
bool ParseQuicDataMessageHeader(const char* data,
size_t len,
int* data_channel_id,
uint64_t* message_id,
size_t* bytes_read) {
RTC_DCHECK(data_channel_id);
RTC_DCHECK(message_id);
RTC_DCHECK(bytes_read);
rtc::ByteBufferReader byte_buffer(data, len, rtc::ByteBuffer::ORDER_HOST);
uint64_t dcid;
if (!byte_buffer.ReadUVarint(&dcid)) {
LOG(LS_ERROR) << "Could not read the data channel ID";
return false;
}
*data_channel_id = dcid;
if (!byte_buffer.ReadUVarint(message_id)) {
LOG(LS_ERROR) << "Could not read message ID for data channel "
<< *data_channel_id;
return false;
}
size_t remaining_bytes = byte_buffer.Length();
*bytes_read = len - remaining_bytes;
return true;
}
QuicDataChannel::QuicDataChannel(rtc::Thread* signaling_thread,
rtc::Thread* worker_thread,
rtc::Thread* network_thread,
const std::string& label,
const DataChannelInit& config)
: signaling_thread_(signaling_thread),
worker_thread_(worker_thread),
network_thread_(network_thread),
id_(config.id),
state_(kConnecting),
buffered_amount_(0),
next_message_id_(0),
label_(label),
protocol_(config.protocol) {}
QuicDataChannel::~QuicDataChannel() {}
void QuicDataChannel::RegisterObserver(DataChannelObserver* observer) {
RTC_DCHECK(signaling_thread_->IsCurrent());
observer_ = observer;
}
void QuicDataChannel::UnregisterObserver() {
RTC_DCHECK(signaling_thread_->IsCurrent());
observer_ = nullptr;
}
bool QuicDataChannel::Send(const DataBuffer& buffer) {
RTC_DCHECK(signaling_thread_->IsCurrent());
if (state_ != kOpen) {
LOG(LS_ERROR) << "QUIC data channel " << id_
<< " is not open so cannot send.";
return false;
}
return network_thread_->Invoke<bool>(
RTC_FROM_HERE, rtc::Bind(&QuicDataChannel::Send_n, this, buffer));
}
bool QuicDataChannel::Send_n(const DataBuffer& buffer) {
RTC_DCHECK(network_thread_->IsCurrent());
// Encode and send the header containing the data channel ID and message ID.
rtc::CopyOnWriteBuffer header;
WriteQuicDataChannelMessageHeader(id_, ++next_message_id_, &header);
RTC_DCHECK(quic_transport_channel_);
cricket::ReliableQuicStream* stream =
quic_transport_channel_->CreateQuicStream();
RTC_DCHECK(stream);
// Send the header with a FIN if the message is empty.
bool header_fin = (buffer.size() == 0);
rtc::StreamResult header_result =
stream->Write(header.data<char>(), header.size(), header_fin);
if (header_result == rtc::SR_BLOCK) {
// The header is write blocked but we should try sending the message. Since
// the ReliableQuicStream queues data in order, if the header is write
// blocked then the message will be write blocked. Otherwise if the message
// is sent then the header is sent.
LOG(LS_INFO) << "Stream " << stream->id()
<< " header is write blocked for QUIC data channel " << id_;
} else if (header_result != rtc::SR_SUCCESS) {
LOG(LS_ERROR) << "Stream " << stream->id()
<< " failed to write header for QUIC data channel " << id_
<< ". Unexpected error " << header_result;
return false;
}
// If the message is not empty, then send the message with a FIN.
bool message_fin = true;
rtc::StreamResult message_result =
header_fin ? header_result : stream->Write(buffer.data.data<char>(),
buffer.size(), message_fin);
if (message_result == rtc::SR_SUCCESS) {
// The message is sent and we don't need this QUIC stream.
LOG(LS_INFO) << "Stream " << stream->id()
<< " successfully wrote message for QUIC data channel " << id_;
stream->Close();
return true;
}
// TODO(mikescarlett): Register the ReliableQuicStream's priority to the
// QuicWriteBlockedList so that the QUIC session doesn't drop messages when
// the QUIC transport channel becomes unwritable.
if (message_result == rtc::SR_BLOCK) {
// The QUIC stream is write blocked, so the message is queued by the QUIC
// session. If this is due to the QUIC not being writable, it will be sent
// once QUIC becomes writable again. Otherwise it may be due to exceeding
// the QUIC flow control limit, in which case the remote peer's QUIC session
// will tell the QUIC stream to send more data.
LOG(LS_INFO) << "Stream " << stream->id()
<< " message is write blocked for QUIC data channel " << id_;
SetBufferedAmount_w(buffered_amount_ + stream->queued_data_bytes());
stream->SignalQueuedBytesWritten.connect(
this, &QuicDataChannel::OnQueuedBytesWritten);
write_blocked_quic_streams_[stream->id()] = stream;
// The QUIC stream will be removed from |write_blocked_quic_streams_| once
// it closes.
stream->SignalClosed.connect(this,
&QuicDataChannel::OnWriteBlockedStreamClosed);
return true;
}
LOG(LS_ERROR) << "Stream " << stream->id()
<< " failed to write message for QUIC data channel " << id_
<< ". Unexpected error: " << message_result;
return false;
}
void QuicDataChannel::OnQueuedBytesWritten(net::QuicStreamId stream_id,
uint64_t queued_bytes_written) {
RTC_DCHECK(worker_thread_->IsCurrent());
SetBufferedAmount_w(buffered_amount_ - queued_bytes_written);
const auto& kv = write_blocked_quic_streams_.find(stream_id);
if (kv == write_blocked_quic_streams_.end()) {
RTC_NOTREACHED();
return;
}
cricket::ReliableQuicStream* stream = kv->second;
// True if the QUIC stream is done sending data.
if (stream->fin_sent()) {
LOG(LS_INFO) << "Stream " << stream->id()
<< " successfully wrote data for QUIC data channel " << id_;
stream->Close();
}
}
void QuicDataChannel::SetBufferedAmount_w(uint64_t buffered_amount) {
RTC_DCHECK(worker_thread_->IsCurrent());
buffered_amount_ = buffered_amount;
invoker_.AsyncInvoke<void>(
RTC_FROM_HERE, signaling_thread_,
rtc::Bind(&QuicDataChannel::OnBufferedAmountChange_s, this,
buffered_amount));
}
void QuicDataChannel::Close() {
RTC_DCHECK(signaling_thread_->IsCurrent());
if (state_ == kClosed || state_ == kClosing) {
return;
}
LOG(LS_INFO) << "Closing QUIC data channel.";
SetState_s(kClosing);
worker_thread_->Invoke<void>(RTC_FROM_HERE,
rtc::Bind(&QuicDataChannel::Close_w, this));
SetState_s(kClosed);
}
void QuicDataChannel::Close_w() {
RTC_DCHECK(worker_thread_->IsCurrent());
for (auto& kv : incoming_quic_messages_) {
Message& message = kv.second;
cricket::ReliableQuicStream* stream = message.stream;
stream->Close();
}
for (auto& kv : write_blocked_quic_streams_) {
cricket::ReliableQuicStream* stream = kv.second;
stream->Close();
}
}
bool QuicDataChannel::SetTransportChannel(
cricket::QuicTransportChannel* channel) {
RTC_DCHECK(signaling_thread_->IsCurrent());
if (!channel) {
LOG(LS_ERROR) << "|channel| is NULL. Cannot set transport channel.";
return false;
}
if (quic_transport_channel_) {
if (channel == quic_transport_channel_) {
LOG(LS_WARNING) << "Ignoring duplicate transport channel.";
return true;
}
LOG(LS_ERROR) << "|channel| does not match existing transport channel.";
return false;
}
quic_transport_channel_ = channel;
LOG(LS_INFO) << "Setting QuicTransportChannel for QUIC data channel " << id_;
DataState data_channel_state = worker_thread_->Invoke<DataState>(
RTC_FROM_HERE, rtc::Bind(&QuicDataChannel::SetTransportChannel_w, this));
SetState_s(data_channel_state);
return true;
}
DataChannelInterface::DataState QuicDataChannel::SetTransportChannel_w() {
RTC_DCHECK(worker_thread_->IsCurrent());
quic_transport_channel_->SignalReadyToSend.connect(
this, &QuicDataChannel::OnReadyToSend);
quic_transport_channel_->SignalClosed.connect(
this, &QuicDataChannel::OnConnectionClosed);
if (quic_transport_channel_->writable()) {
return kOpen;
}
return kConnecting;
}
void QuicDataChannel::OnIncomingMessage(Message&& message) {
RTC_DCHECK(network_thread_->IsCurrent());
RTC_DCHECK(message.stream);
if (!observer_) {
LOG(LS_WARNING) << "QUIC data channel " << id_
<< " received a message but has no observer.";
message.stream->Close();
return;
}
// A FIN is received if the message fits into a single QUIC stream frame and
// the remote peer is done sending.
if (message.stream->fin_received()) {
LOG(LS_INFO) << "Stream " << message.stream->id()
<< " has finished receiving data for QUIC data channel "
<< id_;
DataBuffer final_message(message.buffer, false);
invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_,
rtc::Bind(&QuicDataChannel::OnMessage_s, this,
std::move(final_message)));
message.stream->Close();
return;
}
// Otherwise the message is divided across multiple QUIC stream frames, so
// queue the data. OnDataReceived() will be called each time the remaining
// QUIC stream frames arrive.
LOG(LS_INFO) << "QUIC data channel " << id_
<< " is queuing incoming data for stream "
<< message.stream->id();
incoming_quic_messages_[message.stream->id()] = std::move(message);
message.stream->SignalDataReceived.connect(this,
&QuicDataChannel::OnDataReceived);
// The QUIC stream will be removed from |incoming_quic_messages_| once it
// closes.
message.stream->SignalClosed.connect(
this, &QuicDataChannel::OnIncomingQueuedStreamClosed);
}
void QuicDataChannel::OnDataReceived(net::QuicStreamId stream_id,
const char* data,
size_t len) {
RTC_DCHECK(network_thread_->IsCurrent());
RTC_DCHECK(data);
const auto& kv = incoming_quic_messages_.find(stream_id);
if (kv == incoming_quic_messages_.end()) {
RTC_NOTREACHED();
return;
}
Message& message = kv->second;
cricket::ReliableQuicStream* stream = message.stream;
rtc::CopyOnWriteBuffer& received_data = message.buffer;
// If the QUIC stream has not received a FIN, then the remote peer is not
// finished sending data.
if (!stream->fin_received()) {
received_data.AppendData(data, len);
return;
}
// Otherwise we are done receiving and can provide the data channel observer
// with the message.
LOG(LS_INFO) << "Stream " << stream_id
<< " has finished receiving data for QUIC data channel " << id_;
received_data.AppendData(data, len);
DataBuffer final_message(std::move(received_data), false);
invoker_.AsyncInvoke<void>(
RTC_FROM_HERE, signaling_thread_,
rtc::Bind(&QuicDataChannel::OnMessage_s, this, std::move(final_message)));
// Once the stream is closed, OnDataReceived will not fire for the stream.
stream->Close();
}
void QuicDataChannel::OnReadyToSend(cricket::TransportChannel* channel) {
RTC_DCHECK(network_thread_->IsCurrent());
RTC_DCHECK(channel == quic_transport_channel_);
LOG(LS_INFO) << "QuicTransportChannel is ready to send";
invoker_.AsyncInvoke<void>(
RTC_FROM_HERE, signaling_thread_,
rtc::Bind(&QuicDataChannel::SetState_s, this, kOpen));
}
void QuicDataChannel::OnWriteBlockedStreamClosed(net::QuicStreamId stream_id,
int error) {
RTC_DCHECK(worker_thread_->IsCurrent());
LOG(LS_VERBOSE) << "Write blocked stream " << stream_id << " is closed.";
write_blocked_quic_streams_.erase(stream_id);
}
void QuicDataChannel::OnIncomingQueuedStreamClosed(net::QuicStreamId stream_id,
int error) {
RTC_DCHECK(network_thread_->IsCurrent());
LOG(LS_VERBOSE) << "Incoming queued stream " << stream_id << " is closed.";
incoming_quic_messages_.erase(stream_id);
}
void QuicDataChannel::OnConnectionClosed() {
RTC_DCHECK(worker_thread_->IsCurrent());
invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_,
rtc::Bind(&QuicDataChannel::Close, this));
}
void QuicDataChannel::OnMessage_s(const DataBuffer& received_data) {
RTC_DCHECK(signaling_thread_->IsCurrent());
if (observer_) {
observer_->OnMessage(received_data);
}
}
void QuicDataChannel::SetState_s(DataState state) {
RTC_DCHECK(signaling_thread_->IsCurrent());
if (state_ == state || state_ == kClosed) {
return;
}
if (state_ == kClosing && state != kClosed) {
return;
}
LOG(LS_INFO) << "Setting state to " << state << " for QUIC data channel "
<< id_;
state_ = state;
if (observer_) {
observer_->OnStateChange();
}
}
void QuicDataChannel::OnBufferedAmountChange_s(uint64_t buffered_amount) {
RTC_DCHECK(signaling_thread_->IsCurrent());
if (observer_) {
observer_->OnBufferedAmountChange(buffered_amount);
}
}
size_t QuicDataChannel::GetNumWriteBlockedStreams() const {
return write_blocked_quic_streams_.size();
}
size_t QuicDataChannel::GetNumIncomingStreams() const {
return incoming_quic_messages_.size();
}
} // namespace webrtc

View File

@ -1,220 +0,0 @@
/*
* 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.
*/
#ifndef PC_QUICDATACHANNEL_H_
#define PC_QUICDATACHANNEL_H_
#include <string>
#include <unordered_map>
#include <unordered_set>
#include "api/datachannelinterface.h"
#include "rtc_base/asyncinvoker.h"
#include "rtc_base/sigslot.h"
#include "rtc_base/thread.h"
namespace cricket {
class QuicTransportChannel;
class ReliableQuicStream;
class TransportChannel;
} // namepsace cricket
namespace net {
// TODO(mikescarlett): Make this uint64_t once QUIC uses 64-bit ids.
typedef uint32_t QuicStreamId;
} // namespace net
namespace rtc {
class CopyOnWriteBuffer;
} // namespace rtc
namespace webrtc {
// Encodes a QUIC message header with the data channel ID and message ID, then
// stores the result in |header|.
void WriteQuicDataChannelMessageHeader(int data_channel_id,
uint64_t message_id,
rtc::CopyOnWriteBuffer* header);
// Decodes the data channel ID and message ID from the initial data received by
// an incoming QUIC stream. The data channel ID is output to |data_channel_id|,
// the message ID is output to |message_id|, and the number of bytes read is
// output to |bytes_read|. Returns false if either ID cannot be read.
bool ParseQuicDataMessageHeader(const char* data,
size_t len,
int* data_channel_id,
uint64_t* message_id,
size_t* bytes_read);
// QuicDataChannel is an implementation of DataChannelInterface based on the
// QUIC protocol. It uses a QuicTransportChannel to establish encryption and
// transfer data, and a QuicDataTransport to receive incoming messages at
// the correct data channel. Currently this class implements unordered, reliable
// delivery and does not send an "OPEN" message.
//
// Each time a message is sent:
//
// - The QuicDataChannel prepends it with the data channel id and message id.
// The QuicTransportChannel creates a ReliableQuicStream, then the
// ReliableQuicStream sends the message with a FIN.
//
// - The remote QuicSession creates a ReliableQuicStream to receive the data.
// The remote QuicDataTransport dispatches the ReliableQuicStream to the
// QuicDataChannel with the same id as this data channel.
//
// - The remote QuicDataChannel queues data from the ReliableQuicStream. Once
// it receives a QUIC stream frame with a FIN, it provides the message to the
// DataChannelObserver.
//
// TODO(mikescarlett): Implement ordered delivery, unreliable delivery, and
// an OPEN message similar to the one for SCTP.
class QuicDataChannel : public rtc::RefCountedObject<DataChannelInterface>,
public sigslot::has_slots<> {
public:
// Message stores buffered data from the incoming QUIC stream. The QUIC stream
// is provided so that remaining data can be received from the remote peer.
struct Message {
uint64_t id;
rtc::CopyOnWriteBuffer buffer;
cricket::ReliableQuicStream* stream;
};
QuicDataChannel(rtc::Thread* signaling_thread,
rtc::Thread* worker_thread,
rtc::Thread* network_thread,
const std::string& label,
const DataChannelInit& config);
~QuicDataChannel() override;
// DataChannelInterface overrides.
std::string label() const override { return label_; }
bool reliable() const override { return true; }
bool ordered() const override { return false; }
uint16_t maxRetransmitTime() const override { return -1; }
uint16_t maxRetransmits() const override { return -1; }
bool negotiated() const override { return false; }
int id() const override { return id_; }
DataState state() const override { return state_; }
uint64_t buffered_amount() const override { return buffered_amount_; }
std::string protocol() const override { return protocol_; }
void RegisterObserver(DataChannelObserver* observer) override;
void UnregisterObserver() override;
void Close() override;
bool Send(const DataBuffer& buffer) override;
// Called from QuicDataTransport to set the QUIC transport channel that the
// QuicDataChannel sends messages with. Returns false if a different QUIC
// transport channel is already set or |channel| is NULL.
//
// The QUIC transport channel is not set in the constructor to allow creating
// the QuicDataChannel before the PeerConnection has a QUIC transport channel,
// such as before the session description is not set.
bool SetTransportChannel(cricket::QuicTransportChannel* channel);
// Called from QuicDataTransport when an incoming ReliableQuicStream is
// receiving a message received for this data channel. Once this function is
// called, |message| is owned by the QuicDataChannel and should not be
// accessed by the QuicDataTransport.
void OnIncomingMessage(Message&& message);
// Methods for testing.
// Gets the number of outgoing QUIC streams with write blocked data that are
// currently open for this data channel and are not finished writing a
// message. This is equivalent to the size of |write_blocked_quic_streams_|.
size_t GetNumWriteBlockedStreams() const;
// Gets the number of incoming QUIC streams with buffered data that are
// currently open for this data channel and are not finished receiving a
// message. This is equivalent to the size of |incoming_quic_messages_|.
size_t GetNumIncomingStreams() const;
private:
// Callbacks from ReliableQuicStream.
// Called when an incoming QUIC stream in |incoming_quic_messages_| has
// received a QUIC stream frame.
void OnDataReceived(net::QuicStreamId stream_id,
const char* data,
size_t len);
// Called when a write blocked QUIC stream that has been added to
// |write_blocked_quic_streams_| is closed.
void OnWriteBlockedStreamClosed(net::QuicStreamId stream_id, int error);
// Called when an incoming QUIC stream that has been added to
// |incoming_quic_messages_| is closed.
void OnIncomingQueuedStreamClosed(net::QuicStreamId stream_id, int error);
// Called when a write blocked QUIC stream in |write_blocked_quic_streams_|
// has written previously queued data.
void OnQueuedBytesWritten(net::QuicStreamId stream_id,
uint64_t queued_bytes_written);
// Callbacks from |quic_transport_channel_|.
void OnReadyToSend(cricket::TransportChannel* channel);
void OnConnectionClosed();
// Network thread methods.
// Sends the data buffer to the remote peer using an outgoing QUIC stream.
// Returns true if the data buffer can be successfully sent, or if it is
// queued to be sent later.
bool Send_n(const DataBuffer& buffer);
// Worker thread methods.
// Connects the |quic_transport_channel_| signals to this QuicDataChannel,
// then returns the new QuicDataChannel state.
DataState SetTransportChannel_w();
// Closes the QUIC streams associated with this QuicDataChannel.
void Close_w();
// Sets |buffered_amount_|.
void SetBufferedAmount_w(uint64_t buffered_amount);
// Signaling thread methods.
// Triggers QuicDataChannelObserver::OnMessage when a message from the remote
// peer is ready to be read.
void OnMessage_s(const DataBuffer& received_data);
// Triggers QuicDataChannel::OnStateChange if the state change is valid.
// Otherwise does nothing if |state| == |state_| or |state| != kClosed when
// the data channel is closing.
void SetState_s(DataState state);
// Triggers QuicDataChannelObserver::OnBufferedAmountChange when the total
// buffered data changes for a QUIC stream.
void OnBufferedAmountChange_s(uint64_t buffered_amount);
// QUIC transport channel which owns the QUIC session. It is used to create
// a QUIC stream for sending outgoing messages.
cricket::QuicTransportChannel* quic_transport_channel_ = nullptr;
// Signaling thread for DataChannelInterface methods.
rtc::Thread* const signaling_thread_;
// Worker thread for |quic_transport_channel_| callbacks.
rtc::Thread* const worker_thread_;
// Network thread for sending data and |quic_transport_channel_| callbacks.
rtc::Thread* const network_thread_;
rtc::AsyncInvoker invoker_;
// Map of QUIC stream ID => ReliableQuicStream* for write blocked QUIC
// streams.
std::unordered_map<net::QuicStreamId, cricket::ReliableQuicStream*>
write_blocked_quic_streams_;
// Map of QUIC stream ID => Message for each incoming QUIC stream.
std::unordered_map<net::QuicStreamId, Message> incoming_quic_messages_;
// Handles received data from the remote peer and data channel state changes.
DataChannelObserver* observer_ = nullptr;
// QuicDataChannel ID.
int id_;
// Connectivity state of the QuicDataChannel.
DataState state_;
// Total bytes that are buffered among the QUIC streams.
uint64_t buffered_amount_;
// Counter for number of sent messages that is used for message IDs.
uint64_t next_message_id_;
// Variables for application use.
const std::string& label_;
const std::string& protocol_;
};
} // namespace webrtc
#endif // PC_QUICDATACHANNEL_H_

View File

@ -1,658 +0,0 @@
/*
* 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 "pc/quicdatachannel.h"
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
#include "p2p/base/faketransportcontroller.h"
#include "p2p/quic/quictransportchannel.h"
#include "p2p/quic/reliablequicstream.h"
#include "rtc_base/bind.h"
#include "rtc_base/gunit.h"
#include "rtc_base/scoped_ref_ptr.h"
using cricket::FakeTransportChannel;
using cricket::QuicTransportChannel;
using cricket::ReliableQuicStream;
using webrtc::DataBuffer;
using webrtc::DataChannelObserver;
using webrtc::DataChannelInit;
using webrtc::QuicDataChannel;
namespace {
// Timeout for asynchronous operations.
static const int kTimeoutMs = 1000; // milliseconds
// Small messages that can be sent within a single QUIC packet.
static const std::string kSmallMessage1 = "Hello, world!";
static const std::string kSmallMessage2 = "WebRTC";
static const std::string kSmallMessage3 = "1";
static const std::string kSmallMessage4 = "abcdefghijklmnopqrstuvwxyz";
static const DataBuffer kSmallBuffer1(kSmallMessage1);
static const DataBuffer kSmallBuffer2(kSmallMessage2);
static const DataBuffer kSmallBuffer3(kSmallMessage3);
static const DataBuffer kSmallBuffer4(kSmallMessage4);
// Large messages (> 1350 bytes) that exceed the max size of a QUIC packet.
// These are < 16 KB so they don't exceed the QUIC stream flow control limit.
static const std::string kLargeMessage1 = std::string("a", 2000);
static const std::string kLargeMessage2 = std::string("a", 4000);
static const std::string kLargeMessage3 = std::string("a", 8000);
static const std::string kLargeMessage4 = std::string("a", 12000);
static const DataBuffer kLargeBuffer1(kLargeMessage1);
static const DataBuffer kLargeBuffer2(kLargeMessage2);
static const DataBuffer kLargeBuffer3(kLargeMessage3);
static const DataBuffer kLargeBuffer4(kLargeMessage4);
// Oversized message (> 16 KB) that violates the QUIC stream flow control limit.
static const std::string kOversizedMessage = std::string("a", 20000);
static const DataBuffer kOversizedBuffer(kOversizedMessage);
// Creates a fingerprint from a certificate.
static 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();
}
// FakeObserver receives messages from the QuicDataChannel.
class FakeObserver : public DataChannelObserver {
public:
FakeObserver()
: on_state_change_count_(0), on_buffered_amount_change_count_(0) {}
// DataChannelObserver overrides.
void OnStateChange() override { ++on_state_change_count_; }
void OnBufferedAmountChange(uint64_t previous_amount) override {
++on_buffered_amount_change_count_;
}
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(); }
size_t on_state_change_count() const { return on_state_change_count_; }
size_t on_buffered_amount_change_count() const {
return on_buffered_amount_change_count_;
}
private:
std::vector<std::string> messages_;
size_t on_state_change_count_;
size_t on_buffered_amount_change_count_;
};
// FakeQuicDataTransport simulates QuicDataTransport by dispatching QUIC
// stream messages to data channels and encoding/decoding messages.
class FakeQuicDataTransport : public sigslot::has_slots<> {
public:
FakeQuicDataTransport() {}
void ConnectToTransportChannel(QuicTransportChannel* quic_transport_channel) {
quic_transport_channel->SignalIncomingStream.connect(
this, &FakeQuicDataTransport::OnIncomingStream);
}
rtc::scoped_refptr<QuicDataChannel> CreateDataChannel(
int id,
const std::string& label,
const std::string& protocol) {
DataChannelInit config;
config.id = id;
config.protocol = protocol;
rtc::scoped_refptr<QuicDataChannel> data_channel(
new QuicDataChannel(rtc::Thread::Current(), rtc::Thread::Current(),
rtc::Thread::Current(), label, config));
data_channel_by_id_[id] = data_channel;
return data_channel;
}
private:
void OnIncomingStream(cricket::ReliableQuicStream* stream) {
incoming_stream_ = stream;
incoming_stream_->SignalDataReceived.connect(
this, &FakeQuicDataTransport::OnDataReceived);
}
void OnDataReceived(net::QuicStreamId id, const char* data, size_t len) {
ASSERT_EQ(incoming_stream_->id(), id);
incoming_stream_->SignalDataReceived.disconnect(this);
// Retrieve the data channel ID and message ID.
int data_channel_id;
uint64_t message_id;
size_t bytes_read;
ASSERT_TRUE(webrtc::ParseQuicDataMessageHeader(data, len, &data_channel_id,
&message_id, &bytes_read));
data += bytes_read;
len -= bytes_read;
// Dispatch the message to the matching QuicDataChannel.
const auto& kv = data_channel_by_id_.find(data_channel_id);
ASSERT_NE(kv, data_channel_by_id_.end());
QuicDataChannel* data_channel = kv->second;
QuicDataChannel::Message message;
message.id = message_id;
message.buffer = rtc::CopyOnWriteBuffer(data, len);
message.stream = incoming_stream_;
data_channel->OnIncomingMessage(std::move(message));
incoming_stream_ = nullptr;
}
// Map of data channel ID => QuicDataChannel.
std::map<int, rtc::scoped_refptr<QuicDataChannel>> data_channel_by_id_;
// Last incoming QUIC stream which has arrived.
cricket::ReliableQuicStream* incoming_stream_ = nullptr;
};
// A peer who creates a QuicDataChannel to transfer data, and simulates network
// connectivity with a fake ICE channel wrapped by the QUIC transport channel.
class QuicDataChannelPeer {
public:
QuicDataChannelPeer()
: ice_transport_channel_(new FakeTransportChannel("data", 0)),
quic_transport_channel_(ice_transport_channel_) {
ice_transport_channel_->SetAsync(true);
fake_quic_data_transport_.ConnectToTransportChannel(
&quic_transport_channel_);
}
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_transport_channel_.SetLocalCertificate(local_cert);
local_fingerprint_.reset(CreateFingerprint(local_cert.get()));
}
rtc::scoped_refptr<QuicDataChannel> CreateDataChannelWithTransportChannel(
int id,
const std::string& label,
const std::string& protocol) {
rtc::scoped_refptr<QuicDataChannel> data_channel =
fake_quic_data_transport_.CreateDataChannel(id, label, protocol);
data_channel->SetTransportChannel(&quic_transport_channel_);
return data_channel;
}
rtc::scoped_refptr<QuicDataChannel> CreateDataChannelWithoutTransportChannel(
int id,
const std::string& label,
const std::string& protocol) {
return fake_quic_data_transport_.CreateDataChannel(id, label, protocol);
}
// Connects |ice_transport_channel_| to that of the other peer.
void Connect(QuicDataChannelPeer* 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_transport_channel_;
}
FakeTransportChannel* ice_transport_channel() {
return ice_transport_channel_;
}
private:
FakeTransportChannel* ice_transport_channel_;
QuicTransportChannel quic_transport_channel_;
std::unique_ptr<rtc::SSLFingerprint> local_fingerprint_;
FakeQuicDataTransport fake_quic_data_transport_;
};
class QuicDataChannelTest : public testing::Test {
public:
QuicDataChannelTest() {}
// Connect the QuicTransportChannels and complete the crypto handshake.
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:
QuicDataChannelPeer peer1_;
QuicDataChannelPeer peer2_;
};
// Tests that a QuicDataChannel transitions from connecting to open when
// the QuicTransportChannel becomes writable for the first time.
TEST_F(QuicDataChannelTest, DataChannelOpensWhenTransportChannelConnects) {
rtc::scoped_refptr<QuicDataChannel> data_channel =
peer1_.CreateDataChannelWithTransportChannel(4, "label", "protocol");
EXPECT_EQ(webrtc::DataChannelInterface::kConnecting, data_channel->state());
ConnectTransportChannels();
EXPECT_EQ_WAIT(webrtc::DataChannelInterface::kOpen, data_channel->state(),
kTimeoutMs);
}
// Tests that a QuicDataChannel transitions from connecting to open when
// SetTransportChannel is called with a QuicTransportChannel that is already
// writable.
TEST_F(QuicDataChannelTest, DataChannelOpensWhenTransportChannelWritable) {
rtc::scoped_refptr<QuicDataChannel> data_channel =
peer1_.CreateDataChannelWithoutTransportChannel(4, "label", "protocol");
ConnectTransportChannels();
EXPECT_EQ(webrtc::DataChannelInterface::kConnecting, data_channel->state());
data_channel->SetTransportChannel(peer1_.quic_transport_channel());
EXPECT_EQ(webrtc::DataChannelInterface::kOpen, data_channel->state());
}
// Tests that the QuicDataChannel transfers messages small enough to fit into a
// single QUIC stream frame.
TEST_F(QuicDataChannelTest, TransferSmallMessage) {
ConnectTransportChannels();
int data_channel_id = 2;
std::string label = "label";
std::string protocol = "protocol";
rtc::scoped_refptr<QuicDataChannel> peer1_data_channel =
peer1_.CreateDataChannelWithTransportChannel(data_channel_id, label,
protocol);
ASSERT_TRUE(peer1_data_channel->state() ==
webrtc::DataChannelInterface::kOpen);
rtc::scoped_refptr<QuicDataChannel> peer2_data_channel =
peer2_.CreateDataChannelWithTransportChannel(data_channel_id, label,
protocol);
ASSERT_TRUE(peer2_data_channel->state() ==
webrtc::DataChannelInterface::kOpen);
FakeObserver peer1_observer;
peer1_data_channel->RegisterObserver(&peer1_observer);
FakeObserver peer2_observer;
peer2_data_channel->RegisterObserver(&peer2_observer);
// peer1 -> peer2
EXPECT_TRUE(peer1_data_channel->Send(kSmallBuffer1));
ASSERT_EQ_WAIT(1, peer2_observer.messages_received(), kTimeoutMs);
EXPECT_EQ(kSmallMessage1, peer2_observer.messages()[0]);
// peer2 -> peer1
EXPECT_TRUE(peer2_data_channel->Send(kSmallBuffer2));
ASSERT_EQ_WAIT(1, peer1_observer.messages_received(), kTimeoutMs);
EXPECT_EQ(kSmallMessage2, peer1_observer.messages()[0]);
// peer2 -> peer1
EXPECT_TRUE(peer2_data_channel->Send(kSmallBuffer3));
ASSERT_EQ_WAIT(2, peer1_observer.messages_received(), kTimeoutMs);
EXPECT_EQ(kSmallMessage3, peer1_observer.messages()[1]);
// peer1 -> peer2
EXPECT_TRUE(peer1_data_channel->Send(kSmallBuffer4));
ASSERT_EQ_WAIT(2, peer2_observer.messages_received(), kTimeoutMs);
EXPECT_EQ(kSmallMessage4, peer2_observer.messages()[1]);
}
// Tests that QuicDataChannel transfers messages large enough to fit into
// multiple QUIC stream frames, which don't violate the QUIC flow control limit.
// These require buffering by the QuicDataChannel.
TEST_F(QuicDataChannelTest, TransferLargeMessage) {
ConnectTransportChannels();
int data_channel_id = 347;
std::string label = "label";
std::string protocol = "protocol";
rtc::scoped_refptr<QuicDataChannel> peer1_data_channel =
peer1_.CreateDataChannelWithTransportChannel(data_channel_id, label,
protocol);
ASSERT_TRUE(peer1_data_channel->state() ==
webrtc::DataChannelInterface::kOpen);
rtc::scoped_refptr<QuicDataChannel> peer2_data_channel =
peer2_.CreateDataChannelWithTransportChannel(data_channel_id, label,
protocol);
ASSERT_TRUE(peer2_data_channel->state() ==
webrtc::DataChannelInterface::kOpen);
FakeObserver peer1_observer;
peer1_data_channel->RegisterObserver(&peer1_observer);
FakeObserver peer2_observer;
peer2_data_channel->RegisterObserver(&peer2_observer);
// peer1 -> peer2
EXPECT_TRUE(peer1_data_channel->Send(kLargeBuffer1));
ASSERT_TRUE_WAIT(peer2_observer.messages_received() == 1, kTimeoutMs);
EXPECT_EQ(kLargeMessage1, peer2_observer.messages()[0]);
// peer2 -> peer1
EXPECT_TRUE(peer2_data_channel->Send(kLargeBuffer2));
ASSERT_EQ_WAIT(1, peer1_observer.messages_received(), kTimeoutMs);
EXPECT_EQ(kLargeMessage2, peer1_observer.messages()[0]);
// peer2 -> peer1
EXPECT_TRUE(peer2_data_channel->Send(kLargeBuffer3));
ASSERT_EQ_WAIT(2, peer1_observer.messages_received(), kTimeoutMs);
EXPECT_EQ(kLargeMessage3, peer1_observer.messages()[1]);
// peer1 -> peer2
EXPECT_TRUE(peer1_data_channel->Send(kLargeBuffer4));
ASSERT_EQ_WAIT(2, peer2_observer.messages_received(), kTimeoutMs);
EXPECT_EQ(kLargeMessage4, peer2_observer.messages()[1]);
}
// Tests that when a message size exceeds the flow control limit (> 16KB), the
// QuicDataChannel can queue the data and send it after receiving window update
// frames from the remote peer.
TEST_F(QuicDataChannelTest, TransferOversizedMessage) {
ConnectTransportChannels();
int data_channel_id = 189;
std::string label = "label";
std::string protocol = "protocol";
rtc::scoped_refptr<QuicDataChannel> peer1_data_channel =
peer1_.CreateDataChannelWithTransportChannel(data_channel_id, label,
protocol);
rtc::scoped_refptr<QuicDataChannel> peer2_data_channel =
peer2_.CreateDataChannelWithTransportChannel(data_channel_id, label,
protocol);
ASSERT_TRUE(peer2_data_channel->state() ==
webrtc::DataChannelInterface::kOpen);
FakeObserver peer1_observer;
peer1_data_channel->RegisterObserver(&peer1_observer);
FakeObserver peer2_observer;
peer2_data_channel->RegisterObserver(&peer2_observer);
EXPECT_TRUE(peer1_data_channel->Send(kOversizedBuffer));
EXPECT_EQ(1, peer1_data_channel->GetNumWriteBlockedStreams());
EXPECT_EQ_WAIT(1, peer2_data_channel->GetNumIncomingStreams(), kTimeoutMs);
ASSERT_EQ_WAIT(1, peer2_observer.messages_received(), kTimeoutMs);
EXPECT_EQ(kOversizedMessage, peer2_observer.messages()[0]);
EXPECT_EQ(0, peer1_data_channel->GetNumWriteBlockedStreams());
EXPECT_EQ(0, peer2_data_channel->GetNumIncomingStreams());
}
// Tests that empty messages can be sent.
TEST_F(QuicDataChannelTest, TransferEmptyMessage) {
ConnectTransportChannels();
int data_channel_id = 69;
std::string label = "label";
std::string protocol = "protocol";
rtc::scoped_refptr<QuicDataChannel> peer1_data_channel =
peer1_.CreateDataChannelWithTransportChannel(data_channel_id, label,
protocol);
rtc::scoped_refptr<QuicDataChannel> peer2_data_channel =
peer2_.CreateDataChannelWithTransportChannel(data_channel_id, label,
protocol);
ASSERT_TRUE(peer2_data_channel->state() ==
webrtc::DataChannelInterface::kOpen);
FakeObserver peer1_observer;
peer1_data_channel->RegisterObserver(&peer1_observer);
FakeObserver peer2_observer;
peer2_data_channel->RegisterObserver(&peer2_observer);
EXPECT_TRUE(peer1_data_channel->Send(DataBuffer("")));
ASSERT_EQ_WAIT(1, peer2_observer.messages_received(), kTimeoutMs);
EXPECT_EQ("", peer2_observer.messages()[0]);
}
// Tests that when the QuicDataChannel is open and sends a message while the
// QuicTransportChannel is unwritable, it gets buffered then received once the
// QuicTransportChannel becomes writable again.
TEST_F(QuicDataChannelTest, MessagesReceivedWhenTransportChannelReconnects) {
ConnectTransportChannels();
int data_channel_id = 401;
std::string label = "label";
std::string protocol = "protocol";
rtc::scoped_refptr<QuicDataChannel> peer1_data_channel =
peer1_.CreateDataChannelWithTransportChannel(data_channel_id, label,
protocol);
ASSERT_TRUE(peer1_data_channel->state() ==
webrtc::DataChannelInterface::kOpen);
rtc::scoped_refptr<QuicDataChannel> peer2_data_channel =
peer2_.CreateDataChannelWithTransportChannel(data_channel_id, label,
protocol);
ASSERT_TRUE(peer2_data_channel->state() ==
webrtc::DataChannelInterface::kOpen);
FakeObserver peer1_observer;
peer1_data_channel->RegisterObserver(&peer1_observer);
FakeObserver peer2_observer;
peer2_data_channel->RegisterObserver(&peer2_observer);
// writable => unwritable
peer1_.ice_transport_channel()->SetWritable(false);
ASSERT_FALSE(peer1_.quic_transport_channel()->writable());
// Verify that sent data is buffered.
EXPECT_TRUE(peer1_data_channel->Send(kSmallBuffer1));
EXPECT_EQ(1, peer1_data_channel->GetNumWriteBlockedStreams());
EXPECT_TRUE(peer1_data_channel->Send(kSmallBuffer2));
EXPECT_EQ(2, peer1_data_channel->GetNumWriteBlockedStreams());
EXPECT_TRUE(peer1_data_channel->Send(kSmallBuffer3));
EXPECT_EQ(3, peer1_data_channel->GetNumWriteBlockedStreams());
EXPECT_TRUE(peer1_data_channel->Send(kSmallBuffer4));
EXPECT_EQ(4, peer1_data_channel->GetNumWriteBlockedStreams());
// unwritable => writable
peer1_.ice_transport_channel()->SetWritable(true);
ASSERT_TRUE(peer1_.quic_transport_channel()->writable());
ASSERT_EQ_WAIT(4, peer2_observer.messages_received(), kTimeoutMs);
EXPECT_EQ(0, peer1_data_channel->GetNumWriteBlockedStreams());
EXPECT_EQ(0, peer2_data_channel->GetNumIncomingStreams());
}
// Tests that the QuicDataChannel does not send before it is open.
TEST_F(QuicDataChannelTest, TransferMessageBeforeChannelOpens) {
rtc::scoped_refptr<QuicDataChannel> data_channel =
peer1_.CreateDataChannelWithTransportChannel(6, "label", "protocol");
ASSERT_TRUE(data_channel->state() ==
webrtc::DataChannelInterface::kConnecting);
EXPECT_FALSE(data_channel->Send(kSmallBuffer1));
}
// Tests that the QuicDataChannel does not send after it is closed.
TEST_F(QuicDataChannelTest, TransferDataAfterChannelClosed) {
rtc::scoped_refptr<QuicDataChannel> data_channel =
peer1_.CreateDataChannelWithTransportChannel(42, "label", "protocol");
data_channel->Close();
ASSERT_EQ_WAIT(webrtc::DataChannelInterface::kClosed, data_channel->state(),
kTimeoutMs);
EXPECT_FALSE(data_channel->Send(kSmallBuffer1));
}
// Tests that QuicDataChannel state changes fire OnStateChanged() for the
// observer, with the correct data channel states, when the data channel
// transitions from kConnecting => kOpen => kClosing => kClosed.
TEST_F(QuicDataChannelTest, OnStateChangedFired) {
rtc::scoped_refptr<QuicDataChannel> data_channel =
peer1_.CreateDataChannelWithTransportChannel(7, "label", "protocol");
FakeObserver observer;
data_channel->RegisterObserver(&observer);
EXPECT_EQ(webrtc::DataChannelInterface::kConnecting, data_channel->state());
EXPECT_EQ(0, observer.on_state_change_count());
ConnectTransportChannels();
EXPECT_EQ_WAIT(webrtc::DataChannelInterface::kOpen, data_channel->state(),
kTimeoutMs);
EXPECT_EQ(1, observer.on_state_change_count());
data_channel->Close();
EXPECT_EQ_WAIT(webrtc::DataChannelInterface::kClosed, data_channel->state(),
kTimeoutMs);
// 2 state changes due to kClosing and kClosed.
EXPECT_EQ(3, observer.on_state_change_count());
}
// Tests that a QuicTransportChannel can be closed without being opened when it
// is connected to a transprot chanenl.
TEST_F(QuicDataChannelTest, NeverOpenedWithTransportChannel) {
rtc::scoped_refptr<QuicDataChannel> data_channel =
peer1_.CreateDataChannelWithTransportChannel(7, "label", "protocol");
EXPECT_EQ(webrtc::DataChannelInterface::kConnecting, data_channel->state());
data_channel->Close();
EXPECT_EQ_WAIT(webrtc::DataChannelInterface::kClosed, data_channel->state(),
kTimeoutMs);
}
// Tests that a QuicTransportChannel can be closed without being opened or
// connected to a transport channel.
TEST_F(QuicDataChannelTest, NeverOpenedWithoutTransportChannel) {
rtc::scoped_refptr<QuicDataChannel> data_channel =
peer1_.CreateDataChannelWithoutTransportChannel(7, "label", "protocol");
EXPECT_EQ(webrtc::DataChannelInterface::kConnecting, data_channel->state());
data_channel->Close();
EXPECT_EQ_WAIT(webrtc::DataChannelInterface::kClosed, data_channel->state(),
kTimeoutMs);
}
// Tests that the QuicDataChannel is closed when the QUIC connection closes.
TEST_F(QuicDataChannelTest, ClosedOnTransportError) {
ConnectTransportChannels();
rtc::scoped_refptr<QuicDataChannel> data_channel =
peer1_.CreateDataChannelWithTransportChannel(1, "label", "protocol");
EXPECT_EQ(webrtc::DataChannelInterface::kOpen, data_channel->state());
ReliableQuicStream* stream =
peer1_.quic_transport_channel()->CreateQuicStream();
ASSERT_NE(nullptr, stream);
stream->CloseConnectionWithDetails(net::QuicErrorCode::QUIC_NO_ERROR,
"Closing QUIC for testing");
EXPECT_EQ_WAIT(webrtc::DataChannelInterface::kClosed, data_channel->state(),
kTimeoutMs);
}
// Tests that an already closed QuicDataChannel does not fire onStateChange and
// remains closed.
TEST_F(QuicDataChannelTest, DoesNotChangeStateWhenClosed) {
rtc::scoped_refptr<QuicDataChannel> data_channel =
peer1_.CreateDataChannelWithTransportChannel(4, "label", "protocol");
FakeObserver observer;
data_channel->RegisterObserver(&observer);
data_channel->Close();
EXPECT_EQ_WAIT(webrtc::DataChannelInterface::kClosed, data_channel->state(),
kTimeoutMs);
// OnStateChange called for kClosing and kClosed.
EXPECT_EQ(2, observer.on_state_change_count());
// Call Close() again to verify that the state cannot be kClosing.
data_channel->Close();
EXPECT_EQ(webrtc::DataChannelInterface::kClosed, data_channel->state());
EXPECT_EQ(2, observer.on_state_change_count());
ConnectTransportChannels();
EXPECT_EQ(webrtc::DataChannelInterface::kClosed, data_channel->state());
EXPECT_EQ(2, observer.on_state_change_count());
// writable => unwritable
peer1_.ice_transport_channel()->SetWritable(false);
ASSERT_FALSE(peer1_.quic_transport_channel()->writable());
EXPECT_EQ(webrtc::DataChannelInterface::kClosed, data_channel->state());
EXPECT_EQ(2, observer.on_state_change_count());
// unwritable => writable
peer1_.ice_transport_channel()->SetWritable(true);
ASSERT_TRUE(peer1_.quic_transport_channel()->writable());
EXPECT_EQ(webrtc::DataChannelInterface::kClosed, data_channel->state());
EXPECT_EQ(2, observer.on_state_change_count());
}
// Tests that when the QuicDataChannel is open and the QuicTransportChannel
// transitions between writable and unwritable, it does not fire onStateChange
// and remains open.
TEST_F(QuicDataChannelTest, DoesNotChangeStateWhenTransportChannelReconnects) {
ConnectTransportChannels();
rtc::scoped_refptr<QuicDataChannel> data_channel =
peer1_.CreateDataChannelWithTransportChannel(4, "label", "protocol");
FakeObserver observer;
data_channel->RegisterObserver(&observer);
EXPECT_EQ(webrtc::DataChannelInterface::kOpen, data_channel->state());
EXPECT_EQ(0, observer.on_state_change_count());
// writable => unwritable
peer1_.ice_transport_channel()->SetWritable(false);
ASSERT_FALSE(peer1_.quic_transport_channel()->writable());
EXPECT_EQ(webrtc::DataChannelInterface::kOpen, data_channel->state());
EXPECT_EQ(0, observer.on_state_change_count());
// unwritable => writable
peer1_.ice_transport_channel()->SetWritable(true);
ASSERT_TRUE(peer1_.quic_transport_channel()->writable());
EXPECT_EQ(webrtc::DataChannelInterface::kOpen, data_channel->state());
EXPECT_EQ(0, observer.on_state_change_count());
}
// Tests that SetTransportChannel returns false when setting a NULL transport
// channel or a transport channel that is not equivalent to the one already set.
TEST_F(QuicDataChannelTest, SetTransportChannelReturnValue) {
rtc::scoped_refptr<QuicDataChannel> data_channel =
peer1_.CreateDataChannelWithTransportChannel(4, "label", "protocol");
EXPECT_FALSE(data_channel->SetTransportChannel(nullptr));
QuicTransportChannel* transport_channel = peer1_.quic_transport_channel();
EXPECT_TRUE(data_channel->SetTransportChannel(transport_channel));
EXPECT_TRUE(data_channel->SetTransportChannel(transport_channel));
QuicTransportChannel* other_transport_channel =
peer2_.quic_transport_channel();
EXPECT_FALSE(data_channel->SetTransportChannel(other_transport_channel));
}
// Tests that the QUIC message header is encoded with the correct number of
// bytes and is properly decoded.
TEST_F(QuicDataChannelTest, EncodeParseQuicDataMessageHeader) {
int data_channel_id1 = 127; // 1 byte
uint64_t message_id1 = 0; // 1 byte
rtc::CopyOnWriteBuffer header1;
webrtc::WriteQuicDataChannelMessageHeader(data_channel_id1, message_id1,
&header1);
EXPECT_EQ(2u, header1.size());
int decoded_data_channel_id1;
uint64_t decoded_message_id1;
size_t bytes_read1;
ASSERT_TRUE(webrtc::ParseQuicDataMessageHeader(
header1.data<char>(), header1.size(), &decoded_data_channel_id1,
&decoded_message_id1, &bytes_read1));
EXPECT_EQ(data_channel_id1, decoded_data_channel_id1);
EXPECT_EQ(message_id1, decoded_message_id1);
EXPECT_EQ(2u, bytes_read1);
int data_channel_id2 = 4178; // 2 bytes
uint64_t message_id2 = 1324921792003; // 6 bytes
rtc::CopyOnWriteBuffer header2;
webrtc::WriteQuicDataChannelMessageHeader(data_channel_id2, message_id2,
&header2);
EXPECT_EQ(8u, header2.size());
int decoded_data_channel_id2;
uint64_t decoded_message_id2;
size_t bytes_read2;
ASSERT_TRUE(webrtc::ParseQuicDataMessageHeader(
header2.data<char>(), header2.size(), &decoded_data_channel_id2,
&decoded_message_id2, &bytes_read2));
EXPECT_EQ(data_channel_id2, decoded_data_channel_id2);
EXPECT_EQ(message_id2, decoded_message_id2);
EXPECT_EQ(8u, bytes_read2);
}
} // namespace

View File

@ -1,197 +0,0 @@
/*
* 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 "pc/quicdatatransport.h"
#include "p2p/quic/quictransportchannel.h"
#include "p2p/quic/reliablequicstream.h"
#include "rtc_base/bind.h"
#include "rtc_base/logging.h"
namespace webrtc {
QuicDataTransport::QuicDataTransport(
rtc::Thread* signaling_thread,
rtc::Thread* worker_thread,
rtc::Thread* network_thread,
cricket::TransportController* transport_controller)
: signaling_thread_(signaling_thread),
worker_thread_(worker_thread),
network_thread_(network_thread),
transport_controller_(transport_controller) {
RTC_DCHECK(signaling_thread_);
RTC_DCHECK(worker_thread_);
RTC_DCHECK(network_thread_);
}
QuicDataTransport::~QuicDataTransport() {
DestroyTransportChannel(quic_transport_channel_);
LOG(LS_INFO) << "Destroyed the QUIC data transport.";
}
bool QuicDataTransport::SetTransport(const std::string& transport_name) {
if (transport_name_ == transport_name) {
// Nothing to do if transport name isn't changing
return true;
}
cricket::QuicTransportChannel* transport_channel =
CreateTransportChannel(transport_name);
if (!SetTransportChannel(transport_channel)) {
DestroyTransportChannel(transport_channel);
return false;
}
transport_name_ = transport_name;
return true;
}
bool QuicDataTransport::SetTransportChannel(
cricket::QuicTransportChannel* channel) {
if (!channel) {
LOG(LS_ERROR) << "|channel| is NULL. Cannot set transport channel.";
return false;
}
if (quic_transport_channel_) {
if (channel == quic_transport_channel_) {
LOG(LS_WARNING) << "Ignoring duplicate transport channel.";
return true;
}
LOG(LS_ERROR) << "|channel| does not match existing transport channel.";
return false;
}
LOG(LS_INFO) << "Setting QuicTransportChannel for QuicDataTransport";
quic_transport_channel_ = channel;
quic_transport_channel_->SignalIncomingStream.connect(
this, &QuicDataTransport::OnIncomingStream);
bool success = true;
for (const auto& kv : data_channel_by_id_) {
rtc::scoped_refptr<QuicDataChannel> data_channel = kv.second;
if (!data_channel->SetTransportChannel(quic_transport_channel_)) {
LOG(LS_ERROR)
<< "Cannot set QUIC transport channel for QUIC data channel "
<< kv.first;
success = false;
}
}
return success;
}
rtc::scoped_refptr<DataChannelInterface> QuicDataTransport::CreateDataChannel(
const std::string& label,
const DataChannelInit* config) {
if (config == nullptr) {
return nullptr;
}
if (data_channel_by_id_.find(config->id) != data_channel_by_id_.end()) {
LOG(LS_ERROR) << "QUIC data channel already exists with id " << config->id;
return nullptr;
}
rtc::scoped_refptr<QuicDataChannel> data_channel(new QuicDataChannel(
signaling_thread_, worker_thread_, network_thread_, label, *config));
if (quic_transport_channel_) {
if (!data_channel->SetTransportChannel(quic_transport_channel_)) {
LOG(LS_ERROR)
<< "Cannot set QUIC transport channel for QUIC data channel "
<< config->id;
}
}
data_channel_by_id_[data_channel->id()] = data_channel;
return data_channel;
}
void QuicDataTransport::DestroyDataChannel(int id) {
data_channel_by_id_.erase(id);
}
bool QuicDataTransport::HasDataChannel(int id) const {
return data_channel_by_id_.find(id) != data_channel_by_id_.end();
}
bool QuicDataTransport::HasDataChannels() const {
return !data_channel_by_id_.empty();
}
// Called when a QUIC stream is created for incoming data.
void QuicDataTransport::OnIncomingStream(cricket::ReliableQuicStream* stream) {
RTC_DCHECK(stream != nullptr);
quic_stream_by_id_[stream->id()] = stream;
stream->SignalDataReceived.connect(this, &QuicDataTransport::OnDataReceived);
}
// Called when the first QUIC stream frame is received for incoming data.
void QuicDataTransport::OnDataReceived(net::QuicStreamId id,
const char* data,
size_t len) {
const auto& quic_stream_kv = quic_stream_by_id_.find(id);
if (quic_stream_kv == quic_stream_by_id_.end()) {
RTC_NOTREACHED();
return;
}
cricket::ReliableQuicStream* stream = quic_stream_kv->second;
stream->SignalDataReceived.disconnect(this);
quic_stream_by_id_.erase(id);
// Read the data channel ID and message ID.
int data_channel_id;
uint64_t message_id;
size_t bytes_read;
if (!ParseQuicDataMessageHeader(data, len, &data_channel_id, &message_id,
&bytes_read)) {
LOG(LS_ERROR) << "Could not read QUIC message header from QUIC stream "
<< id;
return;
}
data += bytes_read;
len -= bytes_read;
// Retrieve the data channel which will handle the message.
const auto& data_channel_kv = data_channel_by_id_.find(data_channel_id);
if (data_channel_kv == data_channel_by_id_.end()) {
// TODO(mikescarlett): Implement OPEN message to create a new
// QuicDataChannel when messages are received for a nonexistent ID.
LOG(LS_ERROR) << "Data was received for QUIC data channel "
<< data_channel_id
<< " but it is not registered to the QuicDataTransport.";
return;
}
QuicDataChannel* data_channel = data_channel_kv->second;
QuicDataChannel::Message message;
message.id = message_id;
message.buffer = rtc::CopyOnWriteBuffer(data, len);
message.stream = stream;
data_channel->OnIncomingMessage(std::move(message));
}
cricket::QuicTransportChannel* QuicDataTransport::CreateTransportChannel(
const std::string& transport_name) {
DCHECK(transport_controller_->quic());
cricket::TransportChannel* transport_channel =
network_thread_->Invoke<cricket::TransportChannel*>(
RTC_FROM_HERE,
rtc::Bind(&cricket::TransportController::CreateTransportChannel_n,
transport_controller_, transport_name,
cricket::ICE_CANDIDATE_COMPONENT_DEFAULT));
return static_cast<cricket::QuicTransportChannel*>(transport_channel);
}
void QuicDataTransport::DestroyTransportChannel(
cricket::TransportChannel* transport_channel) {
if (transport_channel) {
network_thread_->Invoke<void>(
RTC_FROM_HERE,
rtc::Bind(&cricket::TransportController::DestroyTransportChannel_n,
transport_controller_, transport_channel->transport_name(),
cricket::ICE_CANDIDATE_COMPONENT_DEFAULT));
}
}
} // namespace webrtc

View File

@ -1,115 +0,0 @@
/*
* 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.
*/
#ifndef PC_QUICDATATRANSPORT_H_
#define PC_QUICDATATRANSPORT_H_
#include <string>
#include <unordered_map>
#include "api/datachannelinterface.h"
#include "p2p/base/transportcontroller.h"
#include "pc/quicdatachannel.h"
#include "rtc_base/scoped_ref_ptr.h"
#include "rtc_base/sigslot.h"
#include "rtc_base/thread.h"
namespace cricket {
class QuicTransportChannel;
class ReliableQuicStream;
} // namepsace cricket
namespace webrtc {
// QuicDataTransport creates QuicDataChannels for the PeerConnection. It also
// handles QUIC stream demuxing by distributing incoming QUIC streams from the
// QuicTransportChannel among the QuicDataChannels that it has created.
//
// QuicDataTransport reads the data channel ID from the incoming QUIC stream,
// then looks it up in a map of ID => QuicDataChannel. If the data channel
// exists, it sends the QUIC stream to the QuicDataChannel.
class QuicDataTransport : public sigslot::has_slots<> {
public:
QuicDataTransport(rtc::Thread* signaling_thread,
rtc::Thread* worker_thread,
rtc::Thread* network_thread,
cricket::TransportController* transport_controller);
~QuicDataTransport() override;
// The QuicDataTransport acts like a BaseChannel with these functions.
bool SetTransport(const std::string& transport_name);
const std::string& transport_name() const { return transport_name_; }
const std::string& content_name() const { return content_name_; }
void set_content_name(const std::string& content_name) {
content_name_ = content_name;
}
// Creates a QuicDataChannel that uses this QuicDataTransport.
rtc::scoped_refptr<DataChannelInterface> CreateDataChannel(
const std::string& label,
const DataChannelInit* config);
// Removes a QuicDataChannel with the given ID from the QuicDataTransport's
// data channel map.
void DestroyDataChannel(int id);
// True if the QuicDataTransport has a data channel with the given ID.
bool HasDataChannel(int id) const;
// True if the QuicDataTransport has data channels.
bool HasDataChannels() const;
cricket::QuicTransportChannel* quic_transport_channel() {
return quic_transport_channel_;
}
private:
// Sets the QUIC transport channel for the QuicDataChannels and the
// QuicDataTransport. Returns false if a different QUIC transport channel is
// already set, the QUIC transport channel cannot be set for any of the
// QuicDataChannels, or |channel| is NULL.
bool SetTransportChannel(cricket::QuicTransportChannel* channel);
// Called from the QuicTransportChannel when a ReliableQuicStream is created
// to receive incoming data.
void OnIncomingStream(cricket::ReliableQuicStream* stream);
// Called from the ReliableQuicStream when the first QUIC stream frame is
// received for incoming data. The QuicDataTransport reads the data channel ID
// and message ID from the incoming data, then dispatches the
// ReliableQuicStream to the QuicDataChannel with the same data channel ID.
void OnDataReceived(net::QuicStreamId stream_id,
const char* data,
size_t len);
cricket::QuicTransportChannel* CreateTransportChannel(
const std::string& transport_name);
void DestroyTransportChannel(cricket::TransportChannel* transport_channel);
// Map of data channel ID => QUIC data channel values.
std::unordered_map<int, rtc::scoped_refptr<QuicDataChannel>>
data_channel_by_id_;
// Map of QUIC stream ID => ReliableQuicStream* values.
std::unordered_map<net::QuicStreamId, cricket::ReliableQuicStream*>
quic_stream_by_id_;
// QuicTransportChannel for sending/receiving data.
cricket::QuicTransportChannel* quic_transport_channel_ = nullptr;
// Threads for the QUIC data channel.
rtc::Thread* const signaling_thread_;
rtc::Thread* const worker_thread_;
rtc::Thread* const network_thread_;
cricket::TransportController* transport_controller_;
std::string content_name_;
std::string transport_name_;
};
} // namespace webrtc
#endif // PC_QUICDATATRANSPORT_H_

View File

@ -1,350 +0,0 @@
/*
* 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 "pc/quicdatatransport.h"
#include <memory>
#include <set>
#include <string>
#include <unordered_map>
#include <vector>
#include "p2p/base/faketransportcontroller.h"
#include "p2p/quic/quictransportchannel.h"
#include "p2p/quic/reliablequicstream.h"
#include "pc/quicdatachannel.h"
#include "rtc_base/bytebuffer.h"
#include "rtc_base/gunit.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

View File

@ -248,9 +248,6 @@ DtlsTransportInternal* TransportController::CreateDtlsTransport_n(
// Create DTLS channel wrapping ICE channel, and configure it. // Create DTLS channel wrapping ICE channel, and configure it.
IceTransportInternal* ice = IceTransportInternal* ice =
CreateIceTransportChannel_n(transport_name, component); CreateIceTransportChannel_n(transport_name, component);
// TODO(deadbeef): To support QUIC, would need to create a
// QuicTransportChannel here. What is "dtls" in this file would then become
// "dtls or quic".
DtlsTransportInternal* dtls = DtlsTransportInternal* dtls =
CreateDtlsTransportChannel_n(transport_name, component, ice); CreateDtlsTransportChannel_n(transport_name, component, ice);
dtls->ice_transport()->SetMetricsObserver(metrics_observer_); dtls->ice_transport()->SetMetricsObserver(metrics_observer_);

View File

@ -127,9 +127,6 @@ class TransportController : public sigslot::has_slots<>,
virtual void DestroyDtlsTransport_n(const std::string& transport_name, virtual void DestroyDtlsTransport_n(const std::string& transport_name,
int component); int component);
void use_quic() { quic_ = true; }
bool quic() const { return quic_; }
// TODO(deadbeef): Remove all for_testing methods! // TODO(deadbeef): Remove all for_testing methods!
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate_for_testing() const rtc::scoped_refptr<rtc::RTCCertificate>& certificate_for_testing()
const { const {
@ -267,8 +264,6 @@ class TransportController : public sigslot::has_slots<>,
rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_12; rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_12;
rtc::scoped_refptr<rtc::RTCCertificate> certificate_; rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
rtc::AsyncInvoker invoker_; rtc::AsyncInvoker invoker_;
// True if QUIC is used instead of DTLS.
bool quic_ = false;
webrtc::MetricsObserverInterface* metrics_observer_ = nullptr; webrtc::MetricsObserverInterface* metrics_observer_ = nullptr;

View File

@ -38,10 +38,6 @@
#include "rtc_base/stringencode.h" #include "rtc_base/stringencode.h"
#include "rtc_base/stringutils.h" #include "rtc_base/stringutils.h"
#ifdef HAVE_QUIC
#include "p2p/quic/quictransportchannel.h"
#endif // HAVE_QUIC
using cricket::ContentInfo; using cricket::ContentInfo;
using cricket::ContentInfos; using cricket::ContentInfos;
using cricket::MediaContentDescription; using cricket::MediaContentDescription;
@ -536,11 +532,6 @@ WebRtcSession::~WebRtcSession() {
network_thread_->Invoke<void>( network_thread_->Invoke<void>(
RTC_FROM_HERE, rtc::Bind(&WebRtcSession::DestroySctpTransport_n, this)); RTC_FROM_HERE, rtc::Bind(&WebRtcSession::DestroySctpTransport_n, this));
} }
#ifdef HAVE_QUIC
if (quic_data_transport_) {
quic_data_transport_.reset();
}
#endif
LOG(LS_INFO) << "Session: " << id() << " is destroyed."; LOG(LS_INFO) << "Session: " << id() << " is destroyed.";
} }
@ -580,21 +571,7 @@ bool WebRtcSession::Initialize(
// PeerConnectionFactoryInterface::Options. // PeerConnectionFactoryInterface::Options.
if (rtc_configuration.enable_rtp_data_channel) { if (rtc_configuration.enable_rtp_data_channel) {
data_channel_type_ = cricket::DCT_RTP; data_channel_type_ = cricket::DCT_RTP;
} } else {
#ifdef HAVE_QUIC
else if (rtc_configuration.enable_quic) {
// Use QUIC instead of DTLS when |enable_quic| is true.
data_channel_type_ = cricket::DCT_QUIC;
transport_controller_->use_quic();
if (dtls_enabled_) {
LOG(LS_INFO) << "Using QUIC instead of DTLS";
}
quic_data_transport_.reset(
new QuicDataTransport(signaling_thread(), worker_thread(),
network_thread(), transport_controller_.get()));
}
#endif // HAVE_QUIC
else {
// DTLS has to be enabled to use SCTP. // DTLS has to be enabled to use SCTP.
if (!options.disable_sctp_data_channels && dtls_enabled_) { if (!options.disable_sctp_data_channels && dtls_enabled_) {
data_channel_type_ = cricket::DCT_SCTP; data_channel_type_ = cricket::DCT_SCTP;
@ -1107,14 +1084,6 @@ bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) {
} }
const std::string& transport_name = *first_content_name; const std::string& transport_name = *first_content_name;
#ifdef HAVE_QUIC
if (quic_data_transport_ &&
bundle.HasContentName(quic_data_transport_->content_name()) &&
quic_data_transport_->transport_name() != transport_name) {
LOG(LS_ERROR) << "Unable to BUNDLE " << quic_data_transport_->content_name()
<< " on " << transport_name << "with QUIC.";
}
#endif
auto maybe_set_transport = [this, bundle, auto maybe_set_transport = [this, bundle,
transport_name](cricket::BaseChannel* ch) { transport_name](cricket::BaseChannel* ch) {
if (!ch || !bundle.HasContentName(ch->content_name())) { if (!ch || !bundle.HasContentName(ch->content_name())) {
@ -1723,12 +1692,6 @@ void WebRtcSession::RemoveUnusedChannels(const SessionDescription* desc) {
RTC_FROM_HERE, RTC_FROM_HERE,
rtc::Bind(&WebRtcSession::DestroySctpTransport_n, this)); rtc::Bind(&WebRtcSession::DestroySctpTransport_n, this));
} }
#ifdef HAVE_QUIC
// Clean up the existing QuicDataTransport and its QuicTransportChannels.
if (quic_data_transport_) {
quic_data_transport_.reset();
}
#endif
} }
} }
@ -1897,13 +1860,6 @@ bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content,
const std::string* bundle_transport) { const std::string* bundle_transport) {
const std::string transport_name = const std::string transport_name =
bundle_transport ? *bundle_transport : content->name; bundle_transport ? *bundle_transport : content->name;
#ifdef HAVE_QUIC
if (data_channel_type_ == cricket::DCT_QUIC) {
RTC_DCHECK(transport_controller_->quic());
quic_data_transport_->SetTransports(transport_name);
return true;
}
#endif // HAVE_QUIC
bool sctp = (data_channel_type_ == cricket::DCT_SCTP); bool sctp = (data_channel_type_ == cricket::DCT_SCTP);
if (sctp) { if (sctp) {
if (!sctp_factory_) { if (!sctp_factory_) {
@ -2409,12 +2365,6 @@ const std::string WebRtcSession::GetTransportName(
const std::string& content_name) { const std::string& content_name) {
cricket::BaseChannel* channel = GetChannel(content_name); cricket::BaseChannel* channel = GetChannel(content_name);
if (!channel) { if (!channel) {
#ifdef HAVE_QUIC
if (data_channel_type_ == cricket::DCT_QUIC && quic_data_transport_ &&
content_name == quic_data_transport_->transport_name()) {
return quic_data_transport_->transport_name();
}
#endif
if (sctp_transport_) { if (sctp_transport_) {
RTC_DCHECK(sctp_content_name_); RTC_DCHECK(sctp_content_name_);
RTC_DCHECK(sctp_transport_name_); RTC_DCHECK(sctp_transport_name_);

View File

@ -29,10 +29,6 @@
#include "rtc_base/sslidentity.h" #include "rtc_base/sslidentity.h"
#include "rtc_base/thread.h" #include "rtc_base/thread.h"
#ifdef HAVE_QUIC
#include "pc/quicdatatransport.h"
#endif // HAVE_QUIC
namespace cricket { namespace cricket {
class ChannelManager; class ChannelManager;
@ -43,10 +39,6 @@ class StatsReport;
class VideoChannel; class VideoChannel;
class VoiceChannel; class VoiceChannel;
#ifdef HAVE_QUIC
class QuicTransportChannel;
#endif // HAVE_QUIC
} // namespace cricket } // namespace cricket
namespace webrtc { namespace webrtc {
@ -375,11 +367,6 @@ class WebRtcSession :
// std::string represents the data channel label. // std::string represents the data channel label.
sigslot::signal2<const std::string&, const InternalDataChannelInit&> sigslot::signal2<const std::string&, const InternalDataChannelInit&>
SignalDataChannelOpenMessage; SignalDataChannelOpenMessage;
#ifdef HAVE_QUIC
QuicDataTransport* quic_data_transport() {
return quic_data_transport_.get();
}
#endif // HAVE_QUIC
private: private:
// Indicates the type of SessionDescription in a call to SetLocalDescription // Indicates the type of SessionDescription in a call to SetLocalDescription
@ -643,8 +630,6 @@ class WebRtcSession :
// not set or false, SCTP is allowed (DCT_SCTP); // not set or false, SCTP is allowed (DCT_SCTP);
// 2. If constraint kEnableRtpDataChannels is true, RTP is allowed (DCT_RTP); // 2. If constraint kEnableRtpDataChannels is true, RTP is allowed (DCT_RTP);
// 3. If both 1&2 are false, data channel is not allowed (DCT_NONE). // 3. If both 1&2 are false, data channel is not allowed (DCT_NONE).
// The data channel type could be DCT_QUIC if the QUIC data channel is
// enabled.
cricket::DataChannelType data_channel_type_; cricket::DataChannelType data_channel_type_;
// List of content names for which the remote side triggered an ICE restart. // List of content names for which the remote side triggered an ICE restart.
std::set<std::string> pending_ice_restarts_; std::set<std::string> pending_ice_restarts_;
@ -665,10 +650,6 @@ class WebRtcSession :
bool received_first_video_packet_ = false; bool received_first_video_packet_ = false;
bool received_first_audio_packet_ = false; bool received_first_audio_packet_ = false;
#ifdef HAVE_QUIC
std::unique_ptr<QuicDataTransport> quic_data_transport_;
#endif // HAVE_QUIC
RTC_DISALLOW_COPY_AND_ASSIGN(WebRtcSession); RTC_DISALLOW_COPY_AND_ASSIGN(WebRtcSession);
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -143,9 +143,6 @@ declare_args() {
# http://www.openh264.org, https://www.ffmpeg.org/ # http://www.openh264.org, https://www.ffmpeg.org/
rtc_use_h264 = proprietary_codecs && !is_android && !is_ios rtc_use_h264 = proprietary_codecs && !is_android && !is_ios
# Determines whether QUIC code will be built.
rtc_use_quic = false
# By default, use normal platform audio support or dummy audio, but don't # By default, use normal platform audio support or dummy audio, but don't
# use file-based audio playout and record. # use file-based audio playout and record.
rtc_use_dummy_audio_file_devices = false rtc_use_dummy_audio_file_devices = false