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:
parent
a0b66c7566
commit
c4faa9c4e1
@ -436,11 +436,6 @@ class PeerConnectionInterface : public rtc::RefCountInterface {
|
||||
|
||||
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
|
||||
// network interface. UDP is preferred over TCP and IPv6 over IPv4. This
|
||||
// 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.
|
||||
rtc::Optional<int> ice_check_min_interval;
|
||||
|
||||
|
||||
// ICE Periodic Regathering
|
||||
// If set, WebRTC will periodically create and propose candidates without
|
||||
// starting a new ICE generation. The regathering happens continuously with
|
||||
|
||||
@ -170,7 +170,7 @@ class CompositeMediaEngine : public MediaEngineInterface {
|
||||
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 {
|
||||
public:
|
||||
|
||||
31
p2p/BUILD.gn
31
p2p/BUILD.gn
@ -116,28 +116,6 @@ rtc_static_library("rtc_p2p") {
|
||||
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) {
|
||||
@ -197,15 +175,6 @@ if (rtc_include_tests) {
|
||||
"base/udptransport_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 = [
|
||||
":p2p_test_utils",
|
||||
":rtc_p2p",
|
||||
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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());
|
||||
}
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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()));
|
||||
}
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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));
|
||||
}
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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);
|
||||
}
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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_);
|
||||
}
|
||||
23
pc/BUILD.gn
23
pc/BUILD.gn
@ -232,19 +232,6 @@ rtc_source_set("libjingle_peerconnection") {
|
||||
":peerconnection",
|
||||
"../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) {
|
||||
@ -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 = []
|
||||
if (is_android) {
|
||||
deps += [ ":android_black_magic" ]
|
||||
|
||||
@ -270,7 +270,6 @@ bool PeerConnectionInterface::RTCConfiguration::operator==(
|
||||
ContinualGatheringPolicy continual_gathering_policy;
|
||||
bool prioritize_most_likely_ice_candidate_pairs;
|
||||
struct cricket::MediaConfig media_config;
|
||||
bool enable_quic;
|
||||
bool prune_turn_ports;
|
||||
bool presume_writable_when_fully_relayed;
|
||||
bool enable_ice_renomination;
|
||||
@ -302,7 +301,6 @@ bool PeerConnectionInterface::RTCConfiguration::operator==(
|
||||
disable_ipv6_on_wifi == o.disable_ipv6_on_wifi &&
|
||||
max_ipv6_networks == o.max_ipv6_networks &&
|
||||
enable_rtp_data_channel == o.enable_rtp_data_channel &&
|
||||
enable_quic == o.enable_quic &&
|
||||
screencast_min_bitrate == o.screencast_min_bitrate &&
|
||||
combined_audio_video_bwe == o.combined_audio_video_bwe &&
|
||||
enable_dtls_srtp == o.enable_dtls_srtp &&
|
||||
@ -781,22 +779,6 @@ PeerConnection::CreateDataChannel(
|
||||
const std::string& label,
|
||||
const DataChannelInit* config) {
|
||||
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();
|
||||
|
||||
@ -2356,13 +2338,7 @@ rtc::scoped_refptr<DataChannel> PeerConnection::InternalCreateDataChannel(
|
||||
}
|
||||
|
||||
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();
|
||||
#endif // HAVE_QUIC
|
||||
}
|
||||
|
||||
void PeerConnection::AllocateSctpSids(rtc::SSLRole role) {
|
||||
|
||||
@ -495,85 +495,7 @@ TEST_F(PeerConnectionEndToEndTest,
|
||||
EXPECT_EQ(1U, dc_1_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
|
||||
// a channel has been previously closed (webrtc issue 3778).
|
||||
// This previously failed because the new channel re-uses the ID of the closed
|
||||
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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
|
||||
@ -248,9 +248,6 @@ DtlsTransportInternal* TransportController::CreateDtlsTransport_n(
|
||||
// Create DTLS channel wrapping ICE channel, and configure it.
|
||||
IceTransportInternal* ice =
|
||||
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 =
|
||||
CreateDtlsTransportChannel_n(transport_name, component, ice);
|
||||
dtls->ice_transport()->SetMetricsObserver(metrics_observer_);
|
||||
|
||||
@ -127,9 +127,6 @@ class TransportController : public sigslot::has_slots<>,
|
||||
virtual void DestroyDtlsTransport_n(const std::string& transport_name,
|
||||
int component);
|
||||
|
||||
void use_quic() { quic_ = true; }
|
||||
bool quic() const { return quic_; }
|
||||
|
||||
// TODO(deadbeef): Remove all for_testing methods!
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate_for_testing()
|
||||
const {
|
||||
@ -267,8 +264,6 @@ class TransportController : public sigslot::has_slots<>,
|
||||
rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_12;
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
|
||||
rtc::AsyncInvoker invoker_;
|
||||
// True if QUIC is used instead of DTLS.
|
||||
bool quic_ = false;
|
||||
|
||||
webrtc::MetricsObserverInterface* metrics_observer_ = nullptr;
|
||||
|
||||
|
||||
@ -38,10 +38,6 @@
|
||||
#include "rtc_base/stringencode.h"
|
||||
#include "rtc_base/stringutils.h"
|
||||
|
||||
#ifdef HAVE_QUIC
|
||||
#include "p2p/quic/quictransportchannel.h"
|
||||
#endif // HAVE_QUIC
|
||||
|
||||
using cricket::ContentInfo;
|
||||
using cricket::ContentInfos;
|
||||
using cricket::MediaContentDescription;
|
||||
@ -536,11 +532,6 @@ WebRtcSession::~WebRtcSession() {
|
||||
network_thread_->Invoke<void>(
|
||||
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.";
|
||||
}
|
||||
@ -580,21 +571,7 @@ bool WebRtcSession::Initialize(
|
||||
// PeerConnectionFactoryInterface::Options.
|
||||
if (rtc_configuration.enable_rtp_data_channel) {
|
||||
data_channel_type_ = cricket::DCT_RTP;
|
||||
}
|
||||
#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 {
|
||||
} else {
|
||||
// DTLS has to be enabled to use SCTP.
|
||||
if (!options.disable_sctp_data_channels && dtls_enabled_) {
|
||||
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;
|
||||
|
||||
#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,
|
||||
transport_name](cricket::BaseChannel* ch) {
|
||||
if (!ch || !bundle.HasContentName(ch->content_name())) {
|
||||
@ -1723,12 +1692,6 @@ void WebRtcSession::RemoveUnusedChannels(const SessionDescription* desc) {
|
||||
RTC_FROM_HERE,
|
||||
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 transport_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);
|
||||
if (sctp) {
|
||||
if (!sctp_factory_) {
|
||||
@ -2409,12 +2365,6 @@ const std::string WebRtcSession::GetTransportName(
|
||||
const std::string& content_name) {
|
||||
cricket::BaseChannel* channel = GetChannel(content_name);
|
||||
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_) {
|
||||
RTC_DCHECK(sctp_content_name_);
|
||||
RTC_DCHECK(sctp_transport_name_);
|
||||
|
||||
@ -29,10 +29,6 @@
|
||||
#include "rtc_base/sslidentity.h"
|
||||
#include "rtc_base/thread.h"
|
||||
|
||||
#ifdef HAVE_QUIC
|
||||
#include "pc/quicdatatransport.h"
|
||||
#endif // HAVE_QUIC
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class ChannelManager;
|
||||
@ -43,10 +39,6 @@ class StatsReport;
|
||||
class VideoChannel;
|
||||
class VoiceChannel;
|
||||
|
||||
#ifdef HAVE_QUIC
|
||||
class QuicTransportChannel;
|
||||
#endif // HAVE_QUIC
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
namespace webrtc {
|
||||
@ -375,11 +367,6 @@ class WebRtcSession :
|
||||
// std::string represents the data channel label.
|
||||
sigslot::signal2<const std::string&, const InternalDataChannelInit&>
|
||||
SignalDataChannelOpenMessage;
|
||||
#ifdef HAVE_QUIC
|
||||
QuicDataTransport* quic_data_transport() {
|
||||
return quic_data_transport_.get();
|
||||
}
|
||||
#endif // HAVE_QUIC
|
||||
|
||||
private:
|
||||
// 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);
|
||||
// 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).
|
||||
// The data channel type could be DCT_QUIC if the QUIC data channel is
|
||||
// enabled.
|
||||
cricket::DataChannelType data_channel_type_;
|
||||
// List of content names for which the remote side triggered an ICE restart.
|
||||
std::set<std::string> pending_ice_restarts_;
|
||||
@ -665,10 +650,6 @@ class WebRtcSession :
|
||||
bool received_first_video_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);
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
@ -143,9 +143,6 @@ declare_args() {
|
||||
# http://www.openh264.org, https://www.ffmpeg.org/
|
||||
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
|
||||
# use file-based audio playout and record.
|
||||
rtc_use_dummy_audio_file_devices = false
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user