From 47ee2f3b9f33e8938948c482c921d4e13a3acd83 Mon Sep 17 00:00:00 2001 From: deadbeef Date: Tue, 22 Sep 2015 15:08:23 -0700 Subject: [PATCH] TransportController refactoring. Getting rid of TransportProxy, and in its place adding a TransportController class which will facilitate access to and manage the lifetimes of Transports. These Transports will now be accessed solely from the worker thread, simplifying their implementation. This refactoring also pulls Transport-related code out of BaseSession. Which means that BaseChannels will now rely on the TransportController interface to create channels, rather than BaseSession. Review URL: https://codereview.webrtc.org/1350523003 Cr-Commit-Position: refs/heads/master@{#10022} --- .../objctests/RTCPeerConnectionSyncObserver.m | 10 +- .../webrtc/objctests/RTCPeerConnectionTest.mm | 5 +- talk/app/webrtc/peerconnection.cc | 9 + talk/app/webrtc/statscollector.cc | 17 +- talk/app/webrtc/statscollector_unittest.cc | 172 ++-- talk/app/webrtc/webrtcsession.cc | 463 ++++++----- talk/app/webrtc/webrtcsession.h | 58 +- talk/app/webrtc/webrtcsession_unittest.cc | 313 +++++--- .../webrtc/webrtcsessiondescriptionfactory.cc | 20 +- .../webrtc/webrtcsessiondescriptionfactory.h | 13 +- .../webrtc/webrtcvoiceengine_unittest.cc | 2 +- talk/session/media/channel.cc | 214 +++-- talk/session/media/channel.h | 69 +- talk/session/media/channel_unittest.cc | 165 ++-- talk/session/media/channelmanager.cc | 57 +- talk/session/media/channelmanager.h | 23 +- talk/session/media/channelmanager_unittest.cc | 73 +- webrtc/base/fakenetwork.h | 33 +- webrtc/p2p/base/dtlstransport.h | 51 +- webrtc/p2p/base/dtlstransportchannel.cc | 35 +- webrtc/p2p/base/dtlstransportchannel.h | 53 +- .../p2p/base/dtlstransportchannel_unittest.cc | 94 ++- ...akesession.h => faketransportcontroller.h} | 411 +++++----- webrtc/p2p/base/p2ptransport.cc | 12 +- webrtc/p2p/base/p2ptransport.h | 6 +- webrtc/p2p/base/p2ptransportchannel.cc | 128 ++- webrtc/p2p/base/p2ptransportchannel.h | 45 +- .../p2p/base/p2ptransportchannel_unittest.cc | 133 ++-- webrtc/p2p/base/portallocator.cc | 7 +- webrtc/p2p/base/portallocator.h | 18 +- webrtc/p2p/base/session.cc | 639 +-------------- webrtc/p2p/base/session.h | 299 +------ webrtc/p2p/base/session_unittest.cc | 100 --- webrtc/p2p/base/transport.cc | 736 ++++++------------ webrtc/p2p/base/transport.h | 248 +++--- webrtc/p2p/base/transport_unittest.cc | 25 +- webrtc/p2p/base/transportchannel.cc | 2 +- webrtc/p2p/base/transportchannel.h | 16 +- webrtc/p2p/base/transportchannelimpl.h | 26 +- webrtc/p2p/base/transportchannelproxy.cc | 276 ------- webrtc/p2p/base/transportchannelproxy.h | 96 --- webrtc/p2p/base/transportcontroller.cc | 575 ++++++++++++++ webrtc/p2p/base/transportcontroller.h | 196 +++++ .../p2p/base/transportcontroller_unittest.cc | 679 ++++++++++++++++ .../p2p/base/transportdescriptionfactory.cc | 1 - webrtc/p2p/p2p.gyp | 4 +- webrtc/p2p/p2p_tests.gypi | 4 +- 47 files changed, 3394 insertions(+), 3237 deletions(-) rename webrtc/p2p/base/{fakesession.h => faketransportcontroller.h} (53%) delete mode 100644 webrtc/p2p/base/session_unittest.cc delete mode 100644 webrtc/p2p/base/transportchannelproxy.cc delete mode 100644 webrtc/p2p/base/transportchannelproxy.h create mode 100644 webrtc/p2p/base/transportcontroller.cc create mode 100644 webrtc/p2p/base/transportcontroller.h create mode 100644 webrtc/p2p/base/transportcontroller_unittest.cc diff --git a/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.m b/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.m index 5070b789b8..daebc9523d 100644 --- a/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.m +++ b/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.m @@ -196,8 +196,11 @@ if (newState == RTCICEGatheringGathering) { return; } + NSAssert([_expectedICEGatheringChanges count] > 0, + @"Unexpected ICE gathering state change"); int expectedState = [self popFirstElementAsInt:_expectedICEGatheringChanges]; - NSAssert(expectedState == (int)newState, @"Empty expectation array"); + NSAssert(expectedState == (int)newState, + @"ICE gathering state should match expectation"); } - (void)peerConnection:(RTCPeerConnection*)peerConnection @@ -205,8 +208,11 @@ // See TODO(fischman) in RTCPeerConnectionTest.mm about Completed. if (newState == RTCICEConnectionCompleted) return; + NSAssert([_expectedICEConnectionChanges count] > 0, + @"Unexpected ICE connection state change"); int expectedState = [self popFirstElementAsInt:_expectedICEConnectionChanges]; - NSAssert(expectedState == (int)newState, @"Empty expectation array"); + NSAssert(expectedState == (int)newState, + @"ICE connection state should match expectation"); } - (void)peerConnection:(RTCPeerConnection*)peerConnection diff --git a/talk/app/webrtc/objctests/RTCPeerConnectionTest.mm b/talk/app/webrtc/objctests/RTCPeerConnectionTest.mm index 050c9f4c95..f0703f5ad8 100644 --- a/talk/app/webrtc/objctests/RTCPeerConnectionTest.mm +++ b/talk/app/webrtc/objctests/RTCPeerConnectionTest.mm @@ -182,7 +182,10 @@ EXPECT_GT([answerSDP.description length], 0); [offeringExpectations expectICECandidates:2]; - [answeringExpectations expectICECandidates:2]; + // It's possible to only have 1 ICE candidate for the answerer, since we use + // BUNDLE and rtcp-mux by default, and don't provide any ICE servers in this + // test. + [answeringExpectations expectICECandidates:1]; sdpObserver = [[RTCSessionDescriptionSyncObserver alloc] init]; [answeringExpectations expectSignalingChange:RTCSignalingStable]; diff --git a/talk/app/webrtc/peerconnection.cc b/talk/app/webrtc/peerconnection.cc index 41df847cd6..121565593d 100644 --- a/talk/app/webrtc/peerconnection.cc +++ b/talk/app/webrtc/peerconnection.cc @@ -616,6 +616,10 @@ void PeerConnection::SetLocalDescription( } SetSessionDescriptionMsg* msg = new SetSessionDescriptionMsg(observer); signaling_thread()->Post(this, MSG_SET_SESSIONDESCRIPTION_SUCCESS, msg); + // MaybeStartGathering needs to be called after posting + // MSG_SET_SESSIONDESCRIPTION_SUCCESS, so that we don't signal any candidates + // before signaling that SetLocalDescription completed. + session_->MaybeStartGathering(); } void PeerConnection::SetRemoteDescription( @@ -868,6 +872,11 @@ void PeerConnection::OnRemoveLocalStream(MediaStreamInterface* stream) { void PeerConnection::OnIceConnectionChange( PeerConnectionInterface::IceConnectionState new_state) { ASSERT(signaling_thread()->IsCurrent()); + // After transitioning to "closed", ignore any additional states from + // WebRtcSession (such as "disconnected"). + if (ice_connection_state_ == kIceConnectionClosed) { + return; + } ice_connection_state_ = new_state; observer_->OnIceConnectionChange(ice_connection_state_); } diff --git a/talk/app/webrtc/statscollector.cc b/talk/app/webrtc/statscollector.cc index 76ac76d7d2..021234709d 100644 --- a/talk/app/webrtc/statscollector.cc +++ b/talk/app/webrtc/statscollector.cc @@ -697,24 +697,18 @@ void StatsCollector::ExtractSessionInfo() { // expose them in stats reports. All channels in a transport share the // same local and remote certificates. // - // Note that Transport::GetCertificate and Transport::GetRemoteCertificate - // invoke method calls on the worker thread and block this thread, but - // messages are still processed on this thread, which may blow way the - // existing transports. So we cannot reuse |transport| after these calls. StatsReport::Id local_cert_report_id, remote_cert_report_id; - - cricket::Transport* transport = - session_->GetTransport(transport_iter.second.content_name); rtc::scoped_refptr certificate; - if (transport && transport->GetCertificate(&certificate)) { + if (session_->GetLocalCertificate(transport_iter.second.transport_name, + &certificate)) { StatsReport* r = AddCertificateReports(&(certificate->ssl_certificate())); if (r) local_cert_report_id = r->id(); } - transport = session_->GetTransport(transport_iter.second.content_name); rtc::scoped_ptr cert; - if (transport && transport->GetRemoteSSLCertificate(cert.accept())) { + if (session_->GetRemoteSSLCertificate(transport_iter.second.transport_name, + cert.accept())) { StatsReport* r = AddCertificateReports(cert.get()); if (r) remote_cert_report_id = r->id(); @@ -722,7 +716,7 @@ void StatsCollector::ExtractSessionInfo() { for (const auto& channel_iter : transport_iter.second.channel_stats) { StatsReport::Id id(StatsReport::NewComponentId( - transport_iter.second.content_name, channel_iter.component)); + transport_iter.second.transport_name, channel_iter.component)); StatsReport* channel_report = reports_.ReplaceOrAddNew(id); channel_report->set_timestamp(stats_gathering_started_); channel_report->AddInt(StatsReport::kStatsValueNameComponent, @@ -939,7 +933,6 @@ void StatsCollector::UpdateTrackReports() { StatsReport* report = entry.second; report->set_timestamp(stats_gathering_started_); } - } void StatsCollector::ClearUpdateStatsCacheForTest() { diff --git a/talk/app/webrtc/statscollector_unittest.cc b/talk/app/webrtc/statscollector_unittest.cc index 9b037c4149..0a9d947841 100644 --- a/talk/app/webrtc/statscollector_unittest.cc +++ b/talk/app/webrtc/statscollector_unittest.cc @@ -27,6 +27,8 @@ #include +#include + #include "talk/app/webrtc/statscollector.h" #include "talk/app/webrtc/mediastream.h" @@ -45,7 +47,7 @@ #include "webrtc/base/fakesslidentity.h" #include "webrtc/base/gunit.h" #include "webrtc/base/network.h" -#include "webrtc/p2p/base/fakesession.h" +#include "webrtc/p2p/base/faketransportcontroller.h" using rtc::scoped_ptr; using testing::_; @@ -89,7 +91,12 @@ class MockWebRtcSession : public webrtc::WebRtcSession { MOCK_METHOD2(GetLocalTrackIdBySsrc, bool(uint32, std::string*)); MOCK_METHOD2(GetRemoteTrackIdBySsrc, bool(uint32, std::string*)); MOCK_METHOD1(GetTransportStats, bool(cricket::SessionStats*)); - MOCK_METHOD1(GetTransport, cricket::Transport*(const std::string&)); + MOCK_METHOD2(GetLocalCertificate, + bool(const std::string& transport_name, + rtc::scoped_refptr* certificate)); + MOCK_METHOD2(GetRemoteSSLCertificate, + bool(const std::string& transport_name, + rtc::SSLCertificate** cert)); }; class MockVideoMediaChannel : public cricket::FakeVideoMediaChannel { @@ -500,7 +507,7 @@ class StatsCollectorTest : public testing::Test { cricket::TransportStats transport_stats; cricket::TransportChannelStats channel_stats; channel_stats.component = 1; - transport_stats.content_name = kTransportName; + transport_stats.transport_name = kTransportName; transport_stats.channel_stats.push_back(channel_stats); session_stats_.transport_stats[kTransportName] = transport_stats; @@ -647,36 +654,27 @@ class StatsCollectorTest : public testing::Test { channel_stats.ssl_cipher = "the-ssl-cipher"; cricket::TransportStats transport_stats; - transport_stats.content_name = "audio"; + transport_stats.transport_name = "audio"; transport_stats.channel_stats.push_back(channel_stats); cricket::SessionStats session_stats; - session_stats.transport_stats[transport_stats.content_name] = + session_stats.transport_stats[transport_stats.transport_name] = transport_stats; - // Fake certificates to report. + // Fake certificate to report rtc::scoped_refptr local_certificate( rtc::RTCCertificate::Create(rtc::scoped_ptr( - new rtc::FakeSSLIdentity(local_cert)).Pass())); - rtc::scoped_ptr remote_cert_copy( - remote_cert.GetReference()); - - // Fake transport object. - rtc::scoped_ptr transport( - new cricket::FakeTransport( - session_.signaling_thread(), - session_.worker_thread(), - transport_stats.content_name)); - transport->SetCertificate(local_certificate); - cricket::FakeTransportChannel* channel = - static_cast( - transport->CreateChannel(channel_stats.component)); - EXPECT_FALSE(channel == NULL); - channel->SetRemoteSSLCertificate(remote_cert_copy.get()); + new rtc::FakeSSLIdentity(local_cert)) + .Pass())); // Configure MockWebRtcSession - EXPECT_CALL(session_, GetTransport(transport_stats.content_name)) - .WillRepeatedly(Return(transport.get())); + EXPECT_CALL(session_, + GetLocalCertificate(transport_stats.transport_name, _)) + .WillOnce(DoAll(SetArgPointee<1>(local_certificate), Return(true))); + EXPECT_CALL(session_, + GetRemoteSSLCertificate(transport_stats.transport_name, _)) + .WillOnce( + DoAll(SetArgPointee<1>(remote_cert.GetReference()), Return(true))); EXPECT_CALL(session_, GetTransportStats(_)) .WillOnce(DoAll(SetArgPointee<0>(session_stats), Return(true))); @@ -790,14 +788,17 @@ TEST_F(StatsCollectorTest, ExtractDataInfo) { TEST_F(StatsCollectorTest, BytesCounterHandles64Bits) { StatsCollectorForTest stats(&session_); + EXPECT_CALL(session_, GetLocalCertificate(_, _)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _)) + .WillRepeatedly(Return(false)); + const char kVideoChannelName[] = "video"; InitSessionStats(kVideoChannelName); EXPECT_CALL(session_, GetTransportStats(_)) .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_), Return(true))); - EXPECT_CALL(session_, GetTransport(_)) - .WillRepeatedly(Return(static_cast(NULL))); MockVideoMediaChannel* media_channel = new MockVideoMediaChannel(); cricket::VideoChannel video_channel(rtc::Thread::Current(), @@ -833,14 +834,17 @@ TEST_F(StatsCollectorTest, BytesCounterHandles64Bits) { TEST_F(StatsCollectorTest, BandwidthEstimationInfoIsReported) { StatsCollectorForTest stats(&session_); + EXPECT_CALL(session_, GetLocalCertificate(_, _)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _)) + .WillRepeatedly(Return(false)); + const char kVideoChannelName[] = "video"; InitSessionStats(kVideoChannelName); EXPECT_CALL(session_, GetTransportStats(_)) .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_), Return(true))); - EXPECT_CALL(session_, GetTransport(_)) - .WillRepeatedly(Return(static_cast(NULL))); MockVideoMediaChannel* media_channel = new MockVideoMediaChannel(); cricket::VideoChannel video_channel(rtc::Thread::Current(), @@ -946,13 +950,16 @@ TEST_F(StatsCollectorTest, TrackObjectExistsWithoutUpdateStats) { TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) { StatsCollectorForTest stats(&session_); + EXPECT_CALL(session_, GetLocalCertificate(_, _)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _)) + .WillRepeatedly(Return(false)); + const char kVideoChannelName[] = "video"; InitSessionStats(kVideoChannelName); EXPECT_CALL(session_, GetTransportStats(_)) .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_), Return(true))); - EXPECT_CALL(session_, GetTransport(_)) - .WillRepeatedly(Return(static_cast(NULL))); MockVideoMediaChannel* media_channel = new MockVideoMediaChannel(); cricket::VideoChannel video_channel(rtc::Thread::Current(), @@ -1011,11 +1018,13 @@ TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) { TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) { StatsCollectorForTest stats(&session_); - // Ignore unused callback (logspam). - EXPECT_CALL(session_, GetTransport(_)) - .WillRepeatedly(Return(static_cast(NULL))); + EXPECT_CALL(session_, GetLocalCertificate(_, _)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _)) + .WillRepeatedly(Return(false)); + MockVideoMediaChannel* media_channel = new MockVideoMediaChannel(); - // The content_name known by the video channel. + // The transport_name known by the video channel. const std::string kVcName("vcname"); cricket::VideoChannel video_channel(rtc::Thread::Current(), media_channel, NULL, kVcName, false); @@ -1073,7 +1082,7 @@ TEST_F(StatsCollectorTest, RemoteSsrcInfoIsAbsent) { StatsCollectorForTest stats(&session_); MockVideoMediaChannel* media_channel = new MockVideoMediaChannel(); - // The content_name known by the video channel. + // The transport_name known by the video channel. const std::string kVcName("vcname"); cricket::VideoChannel video_channel(rtc::Thread::Current(), media_channel, NULL, kVcName, false); @@ -1096,11 +1105,13 @@ TEST_F(StatsCollectorTest, RemoteSsrcInfoIsAbsent) { TEST_F(StatsCollectorTest, RemoteSsrcInfoIsPresent) { StatsCollectorForTest stats(&session_); - // Ignore unused callback (logspam). - EXPECT_CALL(session_, GetTransport(_)) - .WillRepeatedly(Return(static_cast(NULL))); + EXPECT_CALL(session_, GetLocalCertificate(_, _)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _)) + .WillRepeatedly(Return(false)); + MockVideoMediaChannel* media_channel = new MockVideoMediaChannel(); - // The content_name known by the video channel. + // The transport_name known by the video channel. const std::string kVcName("vcname"); cricket::VideoChannel video_channel(rtc::Thread::Current(), media_channel, NULL, kVcName, false); @@ -1145,13 +1156,16 @@ TEST_F(StatsCollectorTest, RemoteSsrcInfoIsPresent) { TEST_F(StatsCollectorTest, ReportsFromRemoteTrack) { StatsCollectorForTest stats(&session_); + EXPECT_CALL(session_, GetLocalCertificate(_, _)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _)) + .WillRepeatedly(Return(false)); + const char kVideoChannelName[] = "video"; InitSessionStats(kVideoChannelName); EXPECT_CALL(session_, GetTransportStats(_)) .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_), Return(true))); - EXPECT_CALL(session_, GetTransport(_)) - .WillRepeatedly(Return(static_cast(NULL))); MockVideoMediaChannel* media_channel = new MockVideoMediaChannel(); cricket::VideoChannel video_channel(rtc::Thread::Current(), @@ -1330,6 +1344,11 @@ TEST_F(StatsCollectorTest, ChainlessCertificateReportsCreated) { TEST_F(StatsCollectorTest, NoTransport) { StatsCollectorForTest stats(&session_); + EXPECT_CALL(session_, GetLocalCertificate(_, _)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _)) + .WillRepeatedly(Return(false)); + StatsReports reports; // returned values. // Fake stats to process. @@ -1337,16 +1356,14 @@ TEST_F(StatsCollectorTest, NoTransport) { channel_stats.component = 1; cricket::TransportStats transport_stats; - transport_stats.content_name = "audio"; + transport_stats.transport_name = "audio"; transport_stats.channel_stats.push_back(channel_stats); cricket::SessionStats session_stats; - session_stats.transport_stats[transport_stats.content_name] = + session_stats.transport_stats[transport_stats.transport_name] = transport_stats; // Configure MockWebRtcSession - EXPECT_CALL(session_, GetTransport(transport_stats.content_name)) - .WillRepeatedly(ReturnNull()); EXPECT_CALL(session_, GetTransportStats(_)) .WillOnce(DoAll(SetArgPointee<0>(session_stats), Return(true))); @@ -1389,6 +1406,11 @@ TEST_F(StatsCollectorTest, NoTransport) { TEST_F(StatsCollectorTest, NoCertificates) { StatsCollectorForTest stats(&session_); + EXPECT_CALL(session_, GetLocalCertificate(_, _)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _)) + .WillRepeatedly(Return(false)); + StatsReports reports; // returned values. // Fake stats to process. @@ -1396,23 +1418,18 @@ TEST_F(StatsCollectorTest, NoCertificates) { channel_stats.component = 1; cricket::TransportStats transport_stats; - transport_stats.content_name = "audio"; + transport_stats.transport_name = "audio"; transport_stats.channel_stats.push_back(channel_stats); cricket::SessionStats session_stats; - session_stats.transport_stats[transport_stats.content_name] = + session_stats.transport_stats[transport_stats.transport_name] = transport_stats; // Fake transport object. rtc::scoped_ptr transport( - new cricket::FakeTransport( - session_.signaling_thread(), - session_.worker_thread(), - transport_stats.content_name)); + new cricket::FakeTransport(transport_stats.transport_name)); // Configure MockWebRtcSession - EXPECT_CALL(session_, GetTransport(transport_stats.content_name)) - .WillRepeatedly(Return(transport.get())); EXPECT_CALL(session_, GetTransportStats(_)) .WillOnce(DoAll(SetArgPointee<0>(session_stats), Return(true))); @@ -1458,12 +1475,13 @@ TEST_F(StatsCollectorTest, UnsupportedDigestIgnored) { TEST_F(StatsCollectorTest, GetStatsFromLocalAudioTrack) { StatsCollectorForTest stats(&session_); - // Ignore unused callback (logspam). - EXPECT_CALL(session_, GetTransport(_)) - .WillRepeatedly(Return(static_cast(NULL))); + EXPECT_CALL(session_, GetLocalCertificate(_, _)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _)) + .WillRepeatedly(Return(false)); MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel(); - // The content_name known by the voice channel. + // The transport_name known by the voice channel. const std::string kVcName("vcname"); cricket::VoiceChannel voice_channel(rtc::Thread::Current(), media_engine_, media_channel, NULL, kVcName, false); @@ -1492,11 +1510,13 @@ TEST_F(StatsCollectorTest, GetStatsFromLocalAudioTrack) { TEST_F(StatsCollectorTest, GetStatsFromRemoteStream) { StatsCollectorForTest stats(&session_); - // Ignore unused callback (logspam). - EXPECT_CALL(session_, GetTransport(_)) - .WillRepeatedly(Return(static_cast(NULL))); + EXPECT_CALL(session_, GetLocalCertificate(_, _)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _)) + .WillRepeatedly(Return(false)); + MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel(); - // The content_name known by the voice channel. + // The transport_name known by the voice channel. const std::string kVcName("vcname"); cricket::VoiceChannel voice_channel(rtc::Thread::Current(), media_engine_, media_channel, NULL, kVcName, false); @@ -1519,11 +1539,13 @@ TEST_F(StatsCollectorTest, GetStatsFromRemoteStream) { TEST_F(StatsCollectorTest, GetStatsAfterRemoveAudioStream) { StatsCollectorForTest stats(&session_); - // Ignore unused callback (logspam). - EXPECT_CALL(session_, GetTransport(_)) - .WillRepeatedly(Return(static_cast(NULL))); + EXPECT_CALL(session_, GetLocalCertificate(_, _)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _)) + .WillRepeatedly(Return(false)); + MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel(); - // The content_name known by the voice channel. + // The transport_name known by the voice channel. const std::string kVcName("vcname"); cricket::VoiceChannel voice_channel(rtc::Thread::Current(), media_engine_, media_channel, NULL, kVcName, false); @@ -1578,11 +1600,13 @@ TEST_F(StatsCollectorTest, GetStatsAfterRemoveAudioStream) { TEST_F(StatsCollectorTest, LocalAndRemoteTracksWithSameSsrc) { StatsCollectorForTest stats(&session_); - // Ignore unused callback (logspam). - EXPECT_CALL(session_, GetTransport(_)) - .WillRepeatedly(Return(static_cast(NULL))); + EXPECT_CALL(session_, GetLocalCertificate(_, _)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _)) + .WillRepeatedly(Return(false)); + MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel(); - // The content_name known by the voice channel. + // The transport_name known by the voice channel. const std::string kVcName("vcname"); cricket::VoiceChannel voice_channel(rtc::Thread::Current(), media_engine_, media_channel, NULL, kVcName, false); @@ -1663,11 +1687,13 @@ TEST_F(StatsCollectorTest, LocalAndRemoteTracksWithSameSsrc) { TEST_F(StatsCollectorTest, TwoLocalTracksWithSameSsrc) { StatsCollectorForTest stats(&session_); - // Ignore unused callback (logspam). - EXPECT_CALL(session_, GetTransport(_)) - .WillRepeatedly(Return(static_cast(NULL))); + EXPECT_CALL(session_, GetLocalCertificate(_, _)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _)) + .WillRepeatedly(Return(false)); + MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel(); - // The content_name known by the voice channel. + // The transport_name known by the voice channel. const std::string kVcName("vcname"); cricket::VoiceChannel voice_channel(rtc::Thread::Current(), media_engine_, media_channel, NULL, kVcName, false); diff --git a/talk/app/webrtc/webrtcsession.cc b/talk/app/webrtc/webrtcsession.cc index 0c0e44d0e0..bd1cd1a0ac 100644 --- a/talk/app/webrtc/webrtcsession.cc +++ b/talk/app/webrtc/webrtcsession.cc @@ -31,6 +31,7 @@ #include #include +#include #include "talk/app/webrtc/jsepicecandidate.h" #include "talk/app/webrtc/jsepsessiondescription.h" @@ -86,6 +87,7 @@ const char kDtlsSetupFailureRtp[] = "Couldn't set up DTLS-SRTP on RTP channel."; const char kDtlsSetupFailureRtcp[] = "Couldn't set up DTLS-SRTP on RTCP channel."; +const char kEnableBundleFailed[] = "Failed to enable BUNDLE."; const int kMaxUnsignalledRecvStreams = 20; IceCandidatePairType GetIceCandidatePairCounter( @@ -543,7 +545,6 @@ WebRtcSession::WebRtcSession( worker_thread, port_allocator, rtc::ToString(rtc::CreateRandomId64() & LLONG_MAX), - cricket::NS_JINGLE_RTP, false), // RFC 3264: The numeric value of the session id and version in the // o line MUST be representable with a "64 bit signed integer". @@ -558,6 +559,14 @@ WebRtcSession::WebRtcSession( data_channel_type_(cricket::DCT_NONE), ice_restart_latch_(new IceRestartAnswerLatch), metrics_observer_(NULL) { + transport_controller()->SignalConnectionState.connect( + this, &WebRtcSession::OnTransportControllerConnectionState); + transport_controller()->SignalReceiving.connect( + this, &WebRtcSession::OnTransportControllerReceiving); + transport_controller()->SignalGatheringState.connect( + this, &WebRtcSession::OnTransportControllerGatheringState); + transport_controller()->SignalCandidatesGathered.connect( + this, &WebRtcSession::OnTransportControllerCandidatesGathered); } WebRtcSession::~WebRtcSession() { @@ -583,12 +592,12 @@ WebRtcSession::~WebRtcSession() { bool WebRtcSession::Initialize( const PeerConnectionFactoryInterface::Options& options, - const MediaConstraintsInterface* constraints, + const MediaConstraintsInterface* constraints, rtc::scoped_ptr dtls_identity_store, const PeerConnectionInterface::RTCConfiguration& rtc_configuration) { bundle_policy_ = rtc_configuration.bundle_policy; rtcp_mux_policy_ = rtc_configuration.rtcp_mux_policy; - SetSslMaxProtocolVersion(options.ssl_max_version); + transport_controller()->SetSslMaxProtocolVersion(options.ssl_max_version); // Obtain a certificate from RTCConfiguration if any were provided (optional). rtc::scoped_refptr certificate; @@ -613,10 +622,8 @@ bool WebRtcSession::Initialize( // Enable DTLS by default if we have an identity store or a certificate. dtls_enabled_ = (dtls_identity_store || certificate); // |constraints| can override the default |dtls_enabled_| value. - if (FindConstraint( - constraints, - MediaConstraintsInterface::kEnableDtlsSrtp, - &value, nullptr)) { + if (FindConstraint(constraints, MediaConstraintsInterface::kEnableDtlsSrtp, + &value, nullptr)) { dtls_enabled_ = value; } } @@ -736,35 +743,21 @@ bool WebRtcSession::Initialize( if (!dtls_enabled_) { // Construct with DTLS disabled. webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory( - signaling_thread(), - channel_manager_, - mediastream_signaling_, - this, - id(), - data_channel_type_)); + signaling_thread(), channel_manager_, mediastream_signaling_, this, + id(), data_channel_type_)); } else { // Construct with DTLS enabled. if (!certificate) { // Use the |dtls_identity_store| to generate a certificate. RTC_DCHECK(dtls_identity_store); webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory( - signaling_thread(), - channel_manager_, - mediastream_signaling_, - dtls_identity_store.Pass(), - this, - id(), - data_channel_type_)); + signaling_thread(), channel_manager_, mediastream_signaling_, + dtls_identity_store.Pass(), this, id(), data_channel_type_)); } else { // Use the already generated certificate. webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory( - signaling_thread(), - channel_manager_, - mediastream_signaling_, - certificate, - this, - id(), - data_channel_type_)); + signaling_thread(), channel_manager_, mediastream_signaling_, + certificate, this, id(), data_channel_type_)); } } @@ -791,26 +784,12 @@ bool WebRtcSession::Initialize( void WebRtcSession::Terminate() { SetState(STATE_RECEIVEDTERMINATE); - RemoveUnusedChannelsAndTransports(NULL); + RemoveUnusedChannels(NULL); ASSERT(!voice_channel_); ASSERT(!video_channel_); ASSERT(!data_channel_); } -bool WebRtcSession::StartCandidatesAllocation() { - // SpeculativelyConnectTransportChannels, will call ConnectChannels method - // from TransportProxy to start gathering ice candidates. - SpeculativelyConnectAllTransportChannels(); - if (!saved_candidates_.empty()) { - // If there are saved candidates which arrived before local description is - // set, copy those to remote description. - CopySavedCandidates(remote_desc_.get()); - } - // Push remote candidates present in remote description to transport channels. - UseCandidatesInSessionDescription(remote_desc_.get()); - return true; -} - void WebRtcSession::SetSdesPolicy(cricket::SecurePolicy secure_policy) { webrtc_session_desc_factory_->SetSdesPolicy(secure_policy); } @@ -826,17 +805,7 @@ bool WebRtcSession::GetSslRole(rtc::SSLRole* role) { return false; } - // TODO(mallinath) - Return role of each transport, as role may differ from - // one another. - // In current implementaion we just return the role of first transport in the - // transport map. - for (cricket::TransportMap::const_iterator iter = transport_proxies().begin(); - iter != transport_proxies().end(); ++iter) { - if (iter->second->impl()) { - return iter->second->impl()->GetSslRole(role); - } - } - return false; + return transport_controller()->GetSslRole(role); } void WebRtcSession::CreateOffer( @@ -852,6 +821,8 @@ void WebRtcSession::CreateAnswer(CreateSessionDescriptionObserver* observer, bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc, std::string* err_desc) { + ASSERT(signaling_thread()->IsCurrent()); + // Takes the ownership of |desc| regardless of the result. rtc::scoped_ptr desc_temp(desc); @@ -884,16 +855,24 @@ bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc, return BadLocalSdp(desc->type(), kCreateChannelFailed, err_desc); } - // Remove channel and transport proxies, if MediaContentDescription is - // rejected. - RemoveUnusedChannelsAndTransports(local_desc_->description()); + // Remove unused channels if MediaContentDescription is rejected. + RemoveUnusedChannels(local_desc_->description()); if (!UpdateSessionState(action, cricket::CS_LOCAL, err_desc)) { return false; } - // Kick starting the ice candidates allocation. - StartCandidatesAllocation(); + if (remote_description()) { + // Now that we have a local description, we can push down remote candidates + // that we stored, and those from the remote description. + if (!saved_candidates_.empty()) { + // If there are saved candidates which arrived before the local + // description was set, copy those to the remote description. + CopySavedCandidates(remote_desc_.get()); + } + // Push remote candidates in remote description to transport channels. + UseCandidatesInSessionDescription(remote_desc_.get()); + } // Update state and SSRC of local MediaStreams and DataChannels based on the // local session description. @@ -911,6 +890,8 @@ bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc, bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc, std::string* err_desc) { + ASSERT(signaling_thread()->IsCurrent()); + // Takes the ownership of |desc| regardless of the result. rtc::scoped_ptr desc_temp(desc); @@ -927,9 +908,8 @@ bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc, return BadRemoteSdp(desc->type(), kCreateChannelFailed, err_desc); } - // Remove channel and transport proxies, if MediaContentDescription is - // rejected. - RemoveUnusedChannelsAndTransports(desc->description()); + // Remove unused channels if MediaContentDescription is rejected. + RemoveUnusedChannels(desc->description()); // NOTE: Candidates allocation will be initiated only when SetLocalDescription // is called. @@ -988,6 +968,8 @@ bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc, bool WebRtcSession::UpdateSessionState( Action action, cricket::ContentSource source, std::string* err_desc) { + ASSERT(signaling_thread()->IsCurrent()); + // If there's already a pending error then no state transition should happen. // But all call-sites should be verifying this before calling us! ASSERT(error() == cricket::BaseSession::ERROR_NONE); @@ -1021,7 +1003,21 @@ bool WebRtcSession::UpdateSessionState( if (!PushdownTransportDescription(source, cricket::CA_ANSWER, &td_err)) { return BadAnswerSdp(source, MakeTdErrorString(td_err), err_desc); } - MaybeEnableMuxingSupport(); + const cricket::ContentGroup* local_bundle = + BaseSession::local_description()->GetGroupByName( + cricket::GROUP_TYPE_BUNDLE); + const cricket::ContentGroup* remote_bundle = + BaseSession::remote_description()->GetGroupByName( + cricket::GROUP_TYPE_BUNDLE); + if (local_bundle && remote_bundle) { + // The answerer decides the transport to bundle on + const cricket::ContentGroup* answer_bundle = + (source == cricket::CS_LOCAL ? local_bundle : remote_bundle); + if (!EnableBundle(*answer_bundle)) { + LOG(LS_WARNING) << "Failed to enable BUNDLE."; + return BadAnswerSdp(source, kEnableBundleFailed, err_desc); + } + } EnableChannels(); SetState(source == cricket::CS_LOCAL ? STATE_SENTACCEPT : STATE_RECEIVEDACCEPT); @@ -1070,32 +1066,101 @@ WebRtcSession::Action WebRtcSession::GetAction(const std::string& type) { bool WebRtcSession::GetTransportStats(cricket::SessionStats* stats) { ASSERT(signaling_thread()->IsCurrent()); + return (GetChannelTransportStats(voice_channel(), stats) && + GetChannelTransportStats(video_channel(), stats) && + GetChannelTransportStats(data_channel(), stats)); +} - const auto get_transport_stats = [stats](const std::string& content_name, - cricket::Transport* transport) { - const std::string& transport_id = transport->content_name(); - stats->proxy_to_transport[content_name] = transport_id; - if (stats->transport_stats.find(transport_id) - != stats->transport_stats.end()) { - // Transport stats already done for this transport. +bool WebRtcSession::GetChannelTransportStats(cricket::BaseChannel* ch, + cricket::SessionStats* stats) { + ASSERT(signaling_thread()->IsCurrent()); + if (!ch) { + // Not using this channel. + return true; + } + + const std::string& content_name = ch->content_name(); + const std::string& transport_name = ch->transport_name(); + stats->proxy_to_transport[content_name] = transport_name; + if (stats->transport_stats.find(transport_name) != + stats->transport_stats.end()) { + // Transport stats already done for this transport. + return true; + } + + cricket::TransportStats tstats; + if (!transport_controller()->GetStats(transport_name, &tstats)) { + return false; + } + + stats->transport_stats[transport_name] = tstats; + return true; +} + +bool WebRtcSession::GetLocalCertificate( + const std::string& transport_name, + rtc::scoped_refptr* certificate) { + ASSERT(signaling_thread()->IsCurrent()); + return transport_controller()->GetLocalCertificate(transport_name, + certificate); +} + +bool WebRtcSession::GetRemoteSSLCertificate(const std::string& transport_name, + rtc::SSLCertificate** cert) { + ASSERT(signaling_thread()->IsCurrent()); + return transport_controller()->GetRemoteSSLCertificate(transport_name, cert); +} + +cricket::BaseChannel* WebRtcSession::GetChannel( + const std::string& content_name) { + if (voice_channel() && voice_channel()->content_name() == content_name) { + return voice_channel(); + } + if (video_channel() && video_channel()->content_name() == content_name) { + return video_channel(); + } + if (data_channel() && data_channel()->content_name() == content_name) { + return data_channel(); + } + return nullptr; +} + +bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) { + const std::string* first_content_name = bundle.FirstContentName(); + if (!first_content_name) { + LOG(LS_WARNING) << "Tried to BUNDLE with no contents."; + return false; + } + const std::string& transport_name = *first_content_name; + cricket::BaseChannel* first_channel = GetChannel(transport_name); + + auto maybe_set_transport = [this, bundle, transport_name, + first_channel](cricket::BaseChannel* ch) { + if (!ch || !bundle.HasContentName(ch->content_name())) { return true; } - cricket::TransportStats tstats; - if (!transport->GetStats(&tstats)) { - return false; + if (ch->transport_name() == transport_name) { + LOG(LS_INFO) << "BUNDLE already enabled for " << ch->content_name() + << " on " << transport_name << "."; + return true; } - stats->transport_stats[transport_id] = tstats; + if (!ch->SetTransport(transport_name)) { + LOG(LS_WARNING) << "Failed to enable BUNDLE for " << ch->content_name(); + return false; + } + LOG(LS_INFO) << "Enabled BUNDLE for " << ch->content_name() << " on " + << transport_name << "."; return true; }; - for (const auto& kv : transport_proxies()) { - cricket::Transport* transport = kv.second->impl(); - if (transport && !get_transport_stats(kv.first, transport)) { - return false; - } + if (!maybe_set_transport(voice_channel()) || + !maybe_set_transport(video_channel()) || + !maybe_set_transport(data_channel())) { + return false; } + return true; } @@ -1401,13 +1466,18 @@ void WebRtcSession::ResetIceRestartLatch() { void WebRtcSession::OnCertificateReady( const rtc::scoped_refptr& certificate) { - SetCertificate(certificate); + transport_controller()->SetLocalCertificate(certificate); } bool WebRtcSession::waiting_for_certificate_for_testing() const { return webrtc_session_desc_factory_->waiting_for_certificate_for_testing(); } +const rtc::scoped_refptr& +WebRtcSession::certificate_for_testing() { + return transport_controller()->certificate_for_testing(); +} + void WebRtcSession::SetIceConnectionState( PeerConnectionInterface::IceConnectionState state) { if (ice_connection_state_ == state) { @@ -1418,6 +1488,8 @@ void WebRtcSession::SetIceConnectionState( // WebRtcSession does not implement "kIceConnectionClosed" (that is handled // within PeerConnection). This switch statement should compile away when // ASSERTs are disabled. + LOG(LS_INFO) << "Changing IceConnectionState " << ice_connection_state_ + << " => " << state; switch (ice_connection_state_) { case PeerConnectionInterface::kIceConnectionNew: ASSERT(state == PeerConnectionInterface::kIceConnectionChecking); @@ -1458,70 +1530,52 @@ void WebRtcSession::SetIceConnectionState( } } -void WebRtcSession::OnTransportRequestSignaling( - cricket::Transport* transport) { - ASSERT(signaling_thread()->IsCurrent()); - transport->OnSignalingReady(); - if (ice_observer_) { - ice_observer_->OnIceGatheringChange( - PeerConnectionInterface::kIceGatheringGathering); - } -} - -void WebRtcSession::OnTransportConnecting(cricket::Transport* transport) { - ASSERT(signaling_thread()->IsCurrent()); - // start monitoring for the write state of the transport. - OnTransportWritable(transport); -} - -void WebRtcSession::OnTransportWritable(cricket::Transport* transport) { - ASSERT(signaling_thread()->IsCurrent()); - if (transport->all_channels_writable()) { - SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected); - } else if (transport->HasChannels()) { - // If the current state is Connected or Completed, then there were writable - // channels but now there are not, so the next state must be Disconnected. - if (ice_connection_state_ == - PeerConnectionInterface::kIceConnectionConnected || - ice_connection_state_ == - PeerConnectionInterface::kIceConnectionCompleted) { - SetIceConnectionState( - PeerConnectionInterface::kIceConnectionDisconnected); - } - } -} - -void WebRtcSession::OnTransportCompleted(cricket::Transport* transport) { - ASSERT(signaling_thread()->IsCurrent()); - PeerConnectionInterface::IceConnectionState old_state = ice_connection_state_; - SetIceConnectionState(PeerConnectionInterface::kIceConnectionCompleted); - // Only report once when Ice connection is completed. - if (old_state != PeerConnectionInterface::kIceConnectionCompleted) { - cricket::TransportStats stats; - if (metrics_observer_ && transport->GetStats(&stats)) { - ReportBestConnectionState(stats); - ReportNegotiatedCiphers(stats); - } - } -} - -void WebRtcSession::OnTransportFailed(cricket::Transport* transport) { - ASSERT(signaling_thread()->IsCurrent()); - SetIceConnectionState(PeerConnectionInterface::kIceConnectionFailed); -} - -void WebRtcSession::OnTransportReceiving(cricket::Transport* transport) { - ASSERT(signaling_thread()->IsCurrent()); - // The ice connection is considered receiving if at least one transport is - // receiving on any channels. - bool receiving = false; - for (const auto& kv : transport_proxies()) { - cricket::Transport* transport = kv.second->impl(); - if (transport && transport->any_channel_receiving()) { - receiving = true; +void WebRtcSession::OnTransportControllerConnectionState( + cricket::IceConnectionState state) { + switch (state) { + case cricket::kIceConnectionConnecting: + // If the current state is Connected or Completed, then there were + // writable channels but now there are not, so the next state must + // be Disconnected. + // kIceConnectionConnecting is currently used as the default, + // un-connected state by the TransportController, so its only use is + // detecting disconnections. + if (ice_connection_state_ == + PeerConnectionInterface::kIceConnectionConnected || + ice_connection_state_ == + PeerConnectionInterface::kIceConnectionCompleted) { + SetIceConnectionState( + PeerConnectionInterface::kIceConnectionDisconnected); + } break; - } + case cricket::kIceConnectionFailed: + SetIceConnectionState(PeerConnectionInterface::kIceConnectionFailed); + break; + case cricket::kIceConnectionConnected: + LOG(LS_INFO) << "Changing to ICE connected state because " + << "all transports are writable."; + SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected); + break; + case cricket::kIceConnectionCompleted: + LOG(LS_INFO) << "Changing to ICE completed state because " + << "all transports are complete."; + if (ice_connection_state_ != + PeerConnectionInterface::kIceConnectionConnected) { + // If jumping directly from "checking" to "connected", + // signal "connected" first. + SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected); + } + SetIceConnectionState(PeerConnectionInterface::kIceConnectionCompleted); + if (metrics_observer_) { + ReportTransportStats(); + } + break; + default: + ASSERT(false); } +} + +void WebRtcSession::OnTransportControllerReceiving(bool receiving) { SetIceConnectionReceiving(receiving); } @@ -1535,18 +1589,27 @@ void WebRtcSession::SetIceConnectionReceiving(bool receiving) { } } -void WebRtcSession::OnTransportProxyCandidatesReady( - cricket::TransportProxy* proxy, const cricket::Candidates& candidates) { +void WebRtcSession::OnTransportControllerCandidatesGathered( + const std::string& transport_name, + const cricket::Candidates& candidates) { ASSERT(signaling_thread()->IsCurrent()); - ProcessNewLocalCandidate(proxy->content_name(), candidates); -} + int sdp_mline_index; + if (!GetLocalCandidateMediaIndex(transport_name, &sdp_mline_index)) { + LOG(LS_ERROR) << "OnTransportControllerCandidatesGathered: content name " + << transport_name << " not found"; + return; + } -void WebRtcSession::OnCandidatesAllocationDone() { - ASSERT(signaling_thread()->IsCurrent()); - if (ice_observer_) { - ice_observer_->OnIceGatheringChange( - PeerConnectionInterface::kIceGatheringComplete); - ice_observer_->OnIceComplete(); + for (cricket::Candidates::const_iterator citer = candidates.begin(); + citer != candidates.end(); ++citer) { + // Use transport_name as the candidate media id. + JsepIceCandidate candidate(transport_name, sdp_mline_index, *citer); + if (ice_observer_) { + ice_observer_->OnIceCandidate(&candidate); + } + if (local_desc_) { + local_desc_->AddCandidate(&candidate); + } } } @@ -1562,29 +1625,6 @@ void WebRtcSession::EnableChannels() { data_channel_->Enable(true); } -void WebRtcSession::ProcessNewLocalCandidate( - const std::string& content_name, - const cricket::Candidates& candidates) { - int sdp_mline_index; - if (!GetLocalCandidateMediaIndex(content_name, &sdp_mline_index)) { - LOG(LS_ERROR) << "ProcessNewLocalCandidate: content name " - << content_name << " not found"; - return; - } - - for (cricket::Candidates::const_iterator citer = candidates.begin(); - citer != candidates.end(); ++citer) { - // Use content_name as the candidate media id. - JsepIceCandidate candidate(content_name, sdp_mline_index, *citer); - if (ice_observer_) { - ice_observer_->OnIceCandidate(&candidate); - } - if (local_desc_) { - local_desc_->AddCandidate(&candidate); - } - } -} - // Returns the media index for a local ice candidate given the content name. bool WebRtcSession::GetLocalCandidateMediaIndex(const std::string& content_name, int* sdp_mline_index) { @@ -1649,7 +1689,8 @@ bool WebRtcSession::UseCandidate( candidates.push_back(candidate->candidate()); // Invoking BaseSession method to handle remote candidates. std::string error; - if (OnRemoteCandidates(content.name, candidates, &error)) { + if (transport_controller()->AddRemoteCandidates(content.name, candidates, + &error)) { // Candidates successfully submitted for checking. if (ice_connection_state_ == PeerConnectionInterface::kIceConnectionNew || ice_connection_state_ == @@ -1673,8 +1714,7 @@ bool WebRtcSession::UseCandidate( return true; } -void WebRtcSession::RemoveUnusedChannelsAndTransports( - const SessionDescription* desc) { +void WebRtcSession::RemoveUnusedChannels(const SessionDescription* desc) { // Destroy video_channel_ first since it may have a pointer to the // voice_channel_. const cricket::ContentInfo* video_info = @@ -1684,7 +1724,6 @@ void WebRtcSession::RemoveUnusedChannelsAndTransports( SignalVideoChannelDestroyed(); const std::string content_name = video_channel_->content_name(); channel_manager_->DestroyVideoChannel(video_channel_.release()); - DestroyTransportProxy(content_name); } const cricket::ContentInfo* voice_info = @@ -1694,7 +1733,6 @@ void WebRtcSession::RemoveUnusedChannelsAndTransports( SignalVoiceChannelDestroyed(); const std::string content_name = voice_channel_->content_name(); channel_manager_->DestroyVoiceChannel(voice_channel_.release()); - DestroyTransportProxy(content_name); } const cricket::ContentInfo* data_info = @@ -1704,7 +1742,6 @@ void WebRtcSession::RemoveUnusedChannelsAndTransports( SignalDataChannelDestroyed(); const std::string content_name = data_channel_->content_name(); channel_manager_->DestroyDataChannel(data_channel_.release()); - DestroyTransportProxy(content_name); } } @@ -1749,7 +1786,7 @@ bool WebRtcSession::CreateChannels(const SessionDescription* desc) { } } - // Enable bundle before when kMaxBundle policy is in effect. + // Enable BUNDLE immediately when kBundlePolicyMaxBundle is in effect. if (bundle_policy_ == PeerConnectionInterface::kBundlePolicyMaxBundle) { const cricket::ContentGroup* bundle_group = desc->GetGroupByName( cricket::GROUP_TYPE_BUNDLE); @@ -1757,7 +1794,7 @@ bool WebRtcSession::CreateChannels(const SessionDescription* desc) { LOG(LS_WARNING) << "max-bundle specified without BUNDLE specified"; return false; } - if (!BaseSession::BundleContentGroup(bundle_group)) { + if (!EnableBundle(*bundle_group)) { LOG(LS_WARNING) << "max-bundle failed to enable bundling."; return false; } @@ -1768,7 +1805,8 @@ bool WebRtcSession::CreateChannels(const SessionDescription* desc) { bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content) { voice_channel_.reset(channel_manager_->CreateVoiceChannel( - media_controller_.get(), this, content->name, true, audio_options_)); + media_controller_.get(), transport_controller(), content->name, true, + audio_options_)); if (!voice_channel_) { return false; } @@ -1780,7 +1818,8 @@ bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content) { bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) { video_channel_.reset(channel_manager_->CreateVideoChannel( - media_controller_.get(), this, content->name, true, video_options_)); + media_controller_.get(), transport_controller(), content->name, true, + video_options_)); if (!video_channel_) { return false; } @@ -1793,7 +1832,7 @@ bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) { bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content) { bool sctp = (data_channel_type_ == cricket::DCT_SCTP); data_channel_.reset(channel_manager_->CreateDataChannel( - this, content->name, !sctp, data_channel_type_)); + transport_controller(), content->name, !sctp, data_channel_type_)); if (!data_channel_) { return false; } @@ -1974,7 +2013,6 @@ bool WebRtcSession::ReadyToUseRemoteCandidate( const SessionDescriptionInterface* remote_desc, bool* valid) { *valid = true;; - cricket::TransportProxy* transport_proxy = NULL; const SessionDescriptionInterface* current_remote_desc = remote_desc ? remote_desc : remote_description(); @@ -1996,12 +2034,53 @@ bool WebRtcSession::ReadyToUseRemoteCandidate( cricket::ContentInfo content = current_remote_desc->description()->contents()[mediacontent_index]; - transport_proxy = GetTransportProxy(content.name); + cricket::BaseChannel* channel = GetChannel(content.name); + if (!channel) { + return false; + } - return transport_proxy && transport_proxy->local_description_set() && - transport_proxy->remote_description_set(); + return transport_controller()->ReadyForRemoteCandidates( + channel->transport_name()); } +void WebRtcSession::OnTransportControllerGatheringState( + cricket::IceGatheringState state) { + ASSERT(signaling_thread()->IsCurrent()); + if (state == cricket::kIceGatheringGathering) { + if (ice_observer_) { + ice_observer_->OnIceGatheringChange( + PeerConnectionInterface::kIceGatheringGathering); + } + } else if (state == cricket::kIceGatheringComplete) { + if (ice_observer_) { + ice_observer_->OnIceGatheringChange( + PeerConnectionInterface::kIceGatheringComplete); + ice_observer_->OnIceComplete(); + } + } +} + +void WebRtcSession::ReportTransportStats() { + // Use a set so we don't report the same stats twice if two channels share + // a transport. + std::set transport_names; + if (voice_channel()) { + transport_names.insert(voice_channel()->transport_name()); + } + if (video_channel()) { + transport_names.insert(video_channel()->transport_name()); + } + if (data_channel()) { + transport_names.insert(data_channel()->transport_name()); + } + for (const auto& name : transport_names) { + cricket::TransportStats stats; + if (transport_controller()->GetStats(name, &stats)) { + ReportBestConnectionState(stats); + ReportNegotiatedCiphers(stats); + } + } +} // Walk through the ConnectionInfos to gather best connection usage // for IPv4 and IPv6. void WebRtcSession::ReportBestConnectionState( @@ -2069,13 +2148,13 @@ void WebRtcSession::ReportNegotiatedCiphers( PeerConnectionMetricsName srtp_name; PeerConnectionMetricsName ssl_name; - if (stats.content_name == cricket::CN_AUDIO) { + if (stats.transport_name == cricket::CN_AUDIO) { srtp_name = kAudioSrtpCipher; ssl_name = kAudioSslCipher; - } else if (stats.content_name == cricket::CN_VIDEO) { + } else if (stats.transport_name == cricket::CN_VIDEO) { srtp_name = kVideoSrtpCipher; ssl_name = kVideoSslCipher; - } else if (stats.content_name == cricket::CN_DATA) { + } else if (stats.transport_name == cricket::CN_DATA) { srtp_name = kDataSrtpCipher; ssl_name = kDataSslCipher; } else { diff --git a/talk/app/webrtc/webrtcsession.h b/talk/app/webrtc/webrtcsession.h index 582d6d0bd5..1ad9a69e59 100644 --- a/talk/app/webrtc/webrtcsession.h +++ b/talk/app/webrtc/webrtcsession.h @@ -29,6 +29,7 @@ #define TALK_APP_WEBRTC_WEBRTCSESSION_H_ #include +#include #include "talk/app/webrtc/datachannel.h" #include "talk/app/webrtc/dtmfsender.h" @@ -49,7 +50,6 @@ class BaseChannel; class ChannelManager; class DataChannel; class StatsReport; -class Transport; class VideoCapturer; class VideoChannel; class VoiceChannel; @@ -77,6 +77,8 @@ extern const char kSessionError[]; extern const char kSessionErrorDesc[]; extern const char kDtlsSetupFailureRtp[]; extern const char kDtlsSetupFailureRtcp[]; +extern const char kEnableBundleFailed[]; + // Maximum number of received video streams that will be processed by webrtc // even if they are not signalled beforehand. extern const int kMaxUnsignalledRecvStreams; @@ -235,6 +237,19 @@ class WebRtcSession : public cricket::BaseSession, // This avoids exposing the internal structures used to track them. virtual bool GetTransportStats(cricket::SessionStats* stats); + // Get stats for a specific channel + bool GetChannelTransportStats(cricket::BaseChannel* ch, + cricket::SessionStats* stats); + + // virtual so it can be mocked in unit tests + virtual bool GetLocalCertificate( + const std::string& transport_name, + rtc::scoped_refptr* certificate); + + // Caller owns returned certificate + virtual bool GetRemoteSSLCertificate(const std::string& transport_name, + rtc::SSLCertificate** cert); + // Implements DataChannelFactory. rtc::scoped_refptr CreateDataChannel( const std::string& label, @@ -254,6 +269,7 @@ class WebRtcSession : public cricket::BaseSession, // For unit test. bool waiting_for_certificate_for_testing() const; + const rtc::scoped_refptr& certificate_for_testing(); void set_metrics_observer( webrtc::MetricsObserverInterface* metrics_observer) { @@ -269,9 +285,6 @@ class WebRtcSession : public cricket::BaseSession, kAnswer, }; - // Invokes ConnectChannels() on transport proxies, which initiates ice - // candidates allocation. - bool StartCandidatesAllocation(); bool UpdateSessionState(Action action, cricket::ContentSource source, std::string* err_desc); static Action GetAction(const std::string& type); @@ -281,25 +294,13 @@ class WebRtcSession : public cricket::BaseSession, cricket::ContentSource source, std::string* error_desc); - - // Transport related callbacks, override from cricket::BaseSession. - virtual void OnTransportRequestSignaling(cricket::Transport* transport); - virtual void OnTransportConnecting(cricket::Transport* transport); - virtual void OnTransportWritable(cricket::Transport* transport); - virtual void OnTransportCompleted(cricket::Transport* transport); - virtual void OnTransportFailed(cricket::Transport* transport); - virtual void OnTransportProxyCandidatesReady( - cricket::TransportProxy* proxy, - const cricket::Candidates& candidates); - virtual void OnCandidatesAllocationDone(); - void OnTransportReceiving(cricket::Transport* transport) override; + cricket::BaseChannel* GetChannel(const std::string& content_name); + // Cause all the BaseChannels in the bundle group to have the same + // transport channel. + bool EnableBundle(const cricket::ContentGroup& bundle); // Enables media channels to allow sending of media. void EnableChannels(); - // Creates a JsepIceCandidate and adds it to the local session description - // and notify observers. Called when a new local candidate have been found. - void ProcessNewLocalCandidate(const std::string& content_name, - const cricket::Candidates& candidates); // Returns the media index for a local ice candidate given the content name. // Returns false if the local session description does not have a media // content called |content_name|. @@ -312,8 +313,7 @@ class WebRtcSession : public cricket::BaseSession, bool UseCandidate(const IceCandidateInterface* candidate); // Deletes the corresponding channel of contents that don't exist in |desc|. // |desc| can be null. This means that all channels are deleted. - void RemoveUnusedChannelsAndTransports( - const cricket::SessionDescription* desc); + void RemoveUnusedChannels(const cricket::SessionDescription* desc); // Allocates media channels based on the |desc|. If |desc| doesn't have // the BUNDLE option, this method will disable BUNDLE in PortAllocator. @@ -362,10 +362,20 @@ class WebRtcSession : public cricket::BaseSession, const SessionDescriptionInterface* remote_desc, bool* valid); + void OnTransportControllerConnectionState(cricket::IceConnectionState state); + void OnTransportControllerReceiving(bool receiving); + void OnTransportControllerGatheringState(cricket::IceGatheringState state); + void OnTransportControllerCandidatesGathered( + const std::string& transport_name, + const cricket::Candidates& candidates); + std::string GetSessionErrorMsg(); - // Invoked when OnTransportCompleted is signaled to gather the usage - // of IPv4/IPv6 as best connection. + // Invoked when TransportController connection completion is signaled. + // Reports stats for all transports in use. + void ReportTransportStats(); + + // Gather the usage of IPv4/IPv6 as best connection. void ReportBestConnectionState(const cricket::TransportStats& stats); void ReportNegotiatedCiphers(const cricket::TransportStats& stats); diff --git a/talk/app/webrtc/webrtcsession_unittest.cc b/talk/app/webrtc/webrtcsession_unittest.cc index 8bd97e59ce..6dc0fc71cd 100644 --- a/talk/app/webrtc/webrtcsession_unittest.cc +++ b/talk/app/webrtc/webrtcsession_unittest.cc @@ -25,6 +25,8 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include + #include "talk/app/webrtc/audiotrack.h" #include "talk/app/webrtc/fakemetricsobserver.h" #include "talk/app/webrtc/jsepicecandidate.h" @@ -163,8 +165,8 @@ static void InjectAfter(const std::string& line, const std::string& newlines, std::string* message) { const std::string tmp = line + newlines; - rtc::replace_substrs(line.c_str(), line.length(), - tmp.c_str(), tmp.length(), message); + rtc::replace_substrs(line.c_str(), line.length(), tmp.c_str(), tmp.length(), + message); } class MockIceObserver : public webrtc::IceObserver { @@ -244,12 +246,52 @@ class WebRtcSessionForTest : public webrtc::WebRtcSession { } virtual ~WebRtcSessionForTest() {} - using cricket::BaseSession::GetTransportProxy; + // Note that these methods are only safe to use if the signaling thread + // is the same as the worker thread + cricket::TransportChannel* voice_rtp_transport_channel() { + return rtp_transport_channel(voice_channel()); + } + + cricket::TransportChannel* voice_rtcp_transport_channel() { + return rtcp_transport_channel(voice_channel()); + } + + cricket::TransportChannel* video_rtp_transport_channel() { + return rtp_transport_channel(video_channel()); + } + + cricket::TransportChannel* video_rtcp_transport_channel() { + return rtcp_transport_channel(video_channel()); + } + + cricket::TransportChannel* data_rtp_transport_channel() { + return rtp_transport_channel(data_channel()); + } + + cricket::TransportChannel* data_rtcp_transport_channel() { + return rtcp_transport_channel(data_channel()); + } + using webrtc::WebRtcSession::SetAudioPlayout; using webrtc::WebRtcSession::SetAudioSend; using webrtc::WebRtcSession::SetCaptureDevice; using webrtc::WebRtcSession::SetVideoPlayout; using webrtc::WebRtcSession::SetVideoSend; + + private: + cricket::TransportChannel* rtp_transport_channel(cricket::BaseChannel* ch) { + if (!ch) { + return nullptr; + } + return ch->transport_channel(); + } + + cricket::TransportChannel* rtcp_transport_channel(cricket::BaseChannel* ch) { + if (!ch) { + return nullptr; + } + return ch->rtcp_transport_channel(); + } }; class WebRtcSessionCreateSDPObserverForTest @@ -375,9 +417,9 @@ class WebRtcSessionTest EXPECT_EQ(PeerConnectionInterface::kIceGatheringNew, observer_.ice_gathering_state_); - EXPECT_TRUE(session_->Initialize( - options_, constraints_.get(), dtls_identity_store.Pass(), - rtc_configuration)); + EXPECT_TRUE(session_->Initialize(options_, constraints_.get(), + dtls_identity_store.Pass(), + rtc_configuration)); session_->set_metrics_observer(metrics_observer_); } @@ -490,13 +532,6 @@ class WebRtcSessionTest session_->video_channel() != NULL); } - void CheckTransportChannels() const { - EXPECT_TRUE(session_->GetChannel(cricket::CN_AUDIO, 1) != NULL); - EXPECT_TRUE(session_->GetChannel(cricket::CN_AUDIO, 2) != NULL); - EXPECT_TRUE(session_->GetChannel(cricket::CN_VIDEO, 1) != NULL); - EXPECT_TRUE(session_->GetChannel(cricket::CN_VIDEO, 2) != NULL); - } - void VerifyCryptoParams(const cricket::SessionDescription* sdp) { ASSERT_TRUE(session_.get() != NULL); const cricket::ContentInfo* content = cricket::GetFirstAudioContent(sdp); @@ -722,6 +757,7 @@ class WebRtcSessionTest } void SetLocalDescriptionWithoutError(SessionDescriptionInterface* desc) { EXPECT_TRUE(session_->SetLocalDescription(desc, NULL)); + session_->MaybeStartGathering(); } void SetLocalDescriptionExpectState(SessionDescriptionInterface* desc, BaseSession::State expected_state) { @@ -968,15 +1004,10 @@ class WebRtcSessionTest SetRemoteDescriptionWithoutError(new_answer); EXPECT_TRUE_WAIT(observer_.oncandidatesready_, kIceCandidatesTimeout); EXPECT_EQ(expected_candidate_num, observer_.mline_0_candidates_.size()); - EXPECT_EQ(expected_candidate_num, observer_.mline_1_candidates_.size()); - for (size_t i = 0; i < observer_.mline_0_candidates_.size(); ++i) { - cricket::Candidate c0 = observer_.mline_0_candidates_[i]; - cricket::Candidate c1 = observer_.mline_1_candidates_[i]; - if (bundle) { - EXPECT_TRUE(c0.IsEquivalent(c1)); - } else { - EXPECT_FALSE(c0.IsEquivalent(c1)); - } + if (bundle) { + EXPECT_EQ(0, observer_.mline_1_candidates_.size()); + } else { + EXPECT_EQ(expected_candidate_num, observer_.mline_1_candidates_.size()); } } // Tests that we can only send DTMF when the dtmf codec is supported. @@ -1001,7 +1032,7 @@ class WebRtcSessionTest // initial ICE convergences. class LoopbackNetworkConfiguration { - public: + public: LoopbackNetworkConfiguration() : test_ipv6_network_(false), test_extra_ipv4_network_(false), @@ -1150,11 +1181,8 @@ class WebRtcSessionTest // Clearing the rules, session should move back to completed state. loopback_network_manager.ClearRules(fss_.get()); - // Session is automatically calling OnSignalingReady after creation of - // new portallocator session which will allocate new set of candidates. LOG(LS_INFO) << "Firewall Rules cleared"; - EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted, observer_.ice_connection_state_, kIceCandidatesTimeout); @@ -1707,15 +1735,14 @@ TEST_P(WebRtcSessionTest, TestSetLocalNonDtlsAnswerWhenDtlsOn) { // a DTLS fingerprint when DTLS is required. TEST_P(WebRtcSessionTest, TestSetRemoteNonDtlsAnswerWhenDtlsOn) { MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp); - // Enable both SDES and DTLS, so that offer won't be outright rejected as a - // result of using the "UDP/TLS/RTP/SAVPF" profile. InitWithDtls(GetParam()); - session_->SetSdesPolicy(cricket::SEC_ENABLED); SessionDescriptionInterface* offer = CreateOffer(); cricket::MediaSessionOptions options; options.recv_video = true; + rtc::scoped_ptr temp_offer( + CreateRemoteOffer(options, cricket::SEC_ENABLED)); JsepSessionDescription* answer = - CreateRemoteAnswer(offer, options, cricket::SEC_ENABLED); + CreateRemoteAnswer(temp_offer.get(), options, cricket::SEC_ENABLED); // SetRemoteDescription and SetLocalDescription will take the ownership of // the offer and answer. @@ -2017,7 +2044,7 @@ TEST_F(WebRtcSessionTest, TestLocalCandidatesAddedToSessionDescription) { EXPECT_LT(0u, candidates->count()); candidates = local_desc->candidates(1); ASSERT_TRUE(candidates != NULL); - EXPECT_LT(0u, candidates->count()); + EXPECT_EQ(0u, candidates->count()); // Update the session descriptions. mediastream_signaling_.SendAudioVideoStream1(); @@ -2029,7 +2056,7 @@ TEST_F(WebRtcSessionTest, TestLocalCandidatesAddedToSessionDescription) { EXPECT_LT(0u, candidates->count()); candidates = local_desc->candidates(1); ASSERT_TRUE(candidates != NULL); - EXPECT_LT(0u, candidates->count()); + EXPECT_EQ(0u, candidates->count()); } // Test that we can set a remote session description with remote candidates. @@ -2073,23 +2100,17 @@ TEST_F(WebRtcSessionTest, TestSetLocalAndRemoteDescriptionWithCandidates) { // Wait until at least one local candidate has been collected. EXPECT_TRUE_WAIT(0u < observer_.mline_0_candidates_.size(), kIceCandidatesTimeout); - EXPECT_TRUE_WAIT(0u < observer_.mline_1_candidates_.size(), - kIceCandidatesTimeout); rtc::scoped_ptr local_offer(CreateOffer()); ASSERT_TRUE(local_offer->candidates(kMediaContentIndex0) != NULL); EXPECT_LT(0u, local_offer->candidates(kMediaContentIndex0)->count()); - ASSERT_TRUE(local_offer->candidates(kMediaContentIndex1) != NULL); - EXPECT_LT(0u, local_offer->candidates(kMediaContentIndex1)->count()); SessionDescriptionInterface* remote_offer(CreateRemoteOffer()); SetRemoteDescriptionWithoutError(remote_offer); SessionDescriptionInterface* answer = CreateAnswer(NULL); ASSERT_TRUE(answer->candidates(kMediaContentIndex0) != NULL); EXPECT_LT(0u, answer->candidates(kMediaContentIndex0)->count()); - ASSERT_TRUE(answer->candidates(kMediaContentIndex1) != NULL); - EXPECT_LT(0u, answer->candidates(kMediaContentIndex1)->count()); SetLocalDescriptionWithoutError(answer); } @@ -2131,8 +2152,14 @@ TEST_F(WebRtcSessionTest, TestChannelCreationsWithContentNames) { CreateAnswer(NULL); SetLocalDescriptionWithoutError(answer); - EXPECT_TRUE(session_->GetTransportProxy("audio_content_name") != NULL); - EXPECT_TRUE(session_->GetTransportProxy("video_content_name") != NULL); + cricket::TransportChannel* voice_transport_channel = + session_->voice_rtp_transport_channel(); + EXPECT_TRUE(voice_transport_channel != NULL); + EXPECT_EQ(voice_transport_channel->transport_name(), "audio_content_name"); + cricket::TransportChannel* video_transport_channel = + session_->video_rtp_transport_channel(); + EXPECT_TRUE(video_transport_channel != NULL); + EXPECT_EQ(video_transport_channel->transport_name(), "video_content_name"); EXPECT_TRUE((video_channel_ = media_engine_->GetVideoChannel(0)) != NULL); EXPECT_TRUE((voice_channel_ = media_engine_->GetVoiceChannel(0)) != NULL); } @@ -2692,20 +2719,23 @@ TEST_F(WebRtcSessionTest, TestIgnoreCandidatesForUnusedTransportWhenBundling) { SessionDescriptionInterface* answer = CreateAnswer(NULL); SetLocalDescriptionWithoutError(answer); - EXPECT_EQ(session_->GetTransportProxy("audio")->impl(), - session_->GetTransportProxy("video")->impl()); + EXPECT_EQ(session_->voice_rtp_transport_channel(), + session_->video_rtp_transport_channel()); - cricket::Transport* t = session_->GetTransport("audio"); + cricket::BaseChannel* voice_channel = session_->voice_channel(); + ASSERT(voice_channel != NULL); // Checks if one of the transport channels contains a connection using a given // port. - auto connection_with_remote_port = [t](int port) { - cricket::TransportStats stats; - t->GetStats(&stats); - for (auto& chan_stat : stats.channel_stats) { - for (auto& conn_info : chan_stat.connection_infos) { - if (conn_info.remote_candidate.address().port() == port) { - return true; + auto connection_with_remote_port = [this, voice_channel](int port) { + cricket::SessionStats stats; + session_->GetChannelTransportStats(voice_channel, &stats); + for (auto& kv : stats.transport_stats) { + for (auto& chan_stat : kv.second.channel_stats) { + for (auto& conn_info : chan_stat.connection_infos) { + if (conn_info.remote_candidate.address().port() == port) { + return true; + } } } } @@ -2758,7 +2788,7 @@ TEST_F(WebRtcSessionTest, TestIgnoreCandidatesForUnusedTransportWhenBundling) { EXPECT_FALSE(connection_with_remote_port(6000)); } -// kBundlePolicyBalanced bundle policy and answer contains BUNDLE. +// kBundlePolicyBalanced BUNDLE policy and answer contains BUNDLE. TEST_F(WebRtcSessionTest, TestBalancedBundleInAnswer) { InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyBalanced); mediastream_signaling_.SendAudioVideoStream1(); @@ -2769,19 +2799,19 @@ TEST_F(WebRtcSessionTest, TestBalancedBundleInAnswer) { SessionDescriptionInterface* offer = CreateOffer(options); SetLocalDescriptionWithoutError(offer); - EXPECT_NE(session_->GetTransportProxy("audio")->impl(), - session_->GetTransportProxy("video")->impl()); + EXPECT_NE(session_->voice_rtp_transport_channel(), + session_->video_rtp_transport_channel()); mediastream_signaling_.SendAudioVideoStream2(); SessionDescriptionInterface* answer = CreateRemoteAnswer(session_->local_description()); SetRemoteDescriptionWithoutError(answer); - EXPECT_EQ(session_->GetTransportProxy("audio")->impl(), - session_->GetTransportProxy("video")->impl()); + EXPECT_EQ(session_->voice_rtp_transport_channel(), + session_->video_rtp_transport_channel()); } -// kBundlePolicyBalanced bundle policy but no BUNDLE in the answer. +// kBundlePolicyBalanced BUNDLE policy but no BUNDLE in the answer. TEST_F(WebRtcSessionTest, TestBalancedNoBundleInAnswer) { InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyBalanced); mediastream_signaling_.SendAudioVideoStream1(); @@ -2792,8 +2822,8 @@ TEST_F(WebRtcSessionTest, TestBalancedNoBundleInAnswer) { SessionDescriptionInterface* offer = CreateOffer(options); SetLocalDescriptionWithoutError(offer); - EXPECT_NE(session_->GetTransportProxy("audio")->impl(), - session_->GetTransportProxy("video")->impl()); + EXPECT_NE(session_->voice_rtp_transport_channel(), + session_->video_rtp_transport_channel()); mediastream_signaling_.SendAudioVideoStream2(); @@ -2807,8 +2837,8 @@ TEST_F(WebRtcSessionTest, TestBalancedNoBundleInAnswer) { modified_answer->Initialize(answer_copy, "1", "1"); SetRemoteDescriptionWithoutError(modified_answer); // - EXPECT_NE(session_->GetTransportProxy("audio")->impl(), - session_->GetTransportProxy("video")->impl()); + EXPECT_NE(session_->voice_rtp_transport_channel(), + session_->video_rtp_transport_channel()); } // kBundlePolicyMaxBundle policy with BUNDLE in the answer. @@ -2822,16 +2852,49 @@ TEST_F(WebRtcSessionTest, TestMaxBundleBundleInAnswer) { SessionDescriptionInterface* offer = CreateOffer(options); SetLocalDescriptionWithoutError(offer); - EXPECT_EQ(session_->GetTransportProxy("audio")->impl(), - session_->GetTransportProxy("video")->impl()); + EXPECT_EQ(session_->voice_rtp_transport_channel(), + session_->video_rtp_transport_channel()); mediastream_signaling_.SendAudioVideoStream2(); SessionDescriptionInterface* answer = CreateRemoteAnswer(session_->local_description()); SetRemoteDescriptionWithoutError(answer); - EXPECT_EQ(session_->GetTransportProxy("audio")->impl(), - session_->GetTransportProxy("video")->impl()); + EXPECT_EQ(session_->voice_rtp_transport_channel(), + session_->video_rtp_transport_channel()); +} + +// kBundlePolicyMaxBundle policy with BUNDLE in the answer, but no +// audio content in the answer. +TEST_F(WebRtcSessionTest, TestMaxBundleRejectAudio) { + InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyMaxBundle); + mediastream_signaling_.SendAudioVideoStream1(); + + PeerConnectionInterface::RTCOfferAnswerOptions options; + options.use_rtp_mux = true; + + SessionDescriptionInterface* offer = CreateOffer(options); + SetLocalDescriptionWithoutError(offer); + + EXPECT_EQ(session_->voice_rtp_transport_channel(), + session_->video_rtp_transport_channel()); + + mediastream_signaling_.SendAudioVideoStream2(); + cricket::MediaSessionOptions recv_options; + recv_options.recv_audio = false; + recv_options.recv_video = true; + SessionDescriptionInterface* answer = + CreateRemoteAnswer(session_->local_description(), recv_options); + SetRemoteDescriptionWithoutError(answer); + + EXPECT_TRUE(NULL == session_->voice_channel()); + EXPECT_TRUE(NULL != session_->video_rtp_transport_channel()); + + session_->Terminate(); + EXPECT_TRUE(NULL == session_->voice_rtp_transport_channel()); + EXPECT_TRUE(NULL == session_->voice_rtcp_transport_channel()); + EXPECT_TRUE(NULL == session_->video_rtp_transport_channel()); + EXPECT_TRUE(NULL == session_->video_rtcp_transport_channel()); } // kBundlePolicyMaxBundle policy but no BUNDLE in the answer. @@ -2845,8 +2908,8 @@ TEST_F(WebRtcSessionTest, TestMaxBundleNoBundleInAnswer) { SessionDescriptionInterface* offer = CreateOffer(options); SetLocalDescriptionWithoutError(offer); - EXPECT_EQ(session_->GetTransportProxy("audio")->impl(), - session_->GetTransportProxy("video")->impl()); + EXPECT_EQ(session_->voice_rtp_transport_channel(), + session_->video_rtp_transport_channel()); mediastream_signaling_.SendAudioVideoStream2(); @@ -2860,8 +2923,45 @@ TEST_F(WebRtcSessionTest, TestMaxBundleNoBundleInAnswer) { modified_answer->Initialize(answer_copy, "1", "1"); SetRemoteDescriptionWithoutError(modified_answer); - EXPECT_EQ(session_->GetTransportProxy("audio")->impl(), - session_->GetTransportProxy("video")->impl()); + EXPECT_EQ(session_->voice_rtp_transport_channel(), + session_->video_rtp_transport_channel()); +} + +// kBundlePolicyMaxBundle policy with BUNDLE in the remote offer. +TEST_F(WebRtcSessionTest, TestMaxBundleBundleInRemoteOffer) { + InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyMaxBundle); + mediastream_signaling_.SendAudioVideoStream1(); + + SessionDescriptionInterface* offer = CreateRemoteOffer(); + SetRemoteDescriptionWithoutError(offer); + + EXPECT_EQ(session_->voice_rtp_transport_channel(), + session_->video_rtp_transport_channel()); + + mediastream_signaling_.SendAudioVideoStream2(); + SessionDescriptionInterface* answer = CreateAnswer(nullptr); + SetLocalDescriptionWithoutError(answer); + + EXPECT_EQ(session_->voice_rtp_transport_channel(), + session_->video_rtp_transport_channel()); +} + +// kBundlePolicyMaxBundle policy but no BUNDLE in the remote offer. +TEST_F(WebRtcSessionTest, TestMaxBundleNoBundleInRemoteOffer) { + InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyMaxBundle); + mediastream_signaling_.SendAudioVideoStream1(); + + // Remove BUNDLE from the offer. + rtc::scoped_ptr offer(CreateRemoteOffer()); + cricket::SessionDescription* offer_copy = offer->description()->Copy(); + offer_copy->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE); + JsepSessionDescription* modified_offer = + new JsepSessionDescription(JsepSessionDescription::kOffer); + modified_offer->Initialize(offer_copy, "1", "1"); + + // Expect an error when applying the remote description + SetRemoteDescriptionExpectError(JsepSessionDescription::kOffer, + kCreateChannelFailed, modified_offer); } // kBundlePolicyMaxCompat bundle policy and answer contains BUNDLE. @@ -2875,8 +2975,8 @@ TEST_F(WebRtcSessionTest, TestMaxCompatBundleInAnswer) { SessionDescriptionInterface* offer = CreateOffer(options); SetLocalDescriptionWithoutError(offer); - EXPECT_NE(session_->GetTransportProxy("audio")->impl(), - session_->GetTransportProxy("video")->impl()); + EXPECT_NE(session_->voice_rtp_transport_channel(), + session_->video_rtp_transport_channel()); mediastream_signaling_.SendAudioVideoStream2(); SessionDescriptionInterface* answer = @@ -2885,11 +2985,11 @@ TEST_F(WebRtcSessionTest, TestMaxCompatBundleInAnswer) { // This should lead to an audio-only call but isn't implemented // correctly yet. - EXPECT_EQ(session_->GetTransportProxy("audio")->impl(), - session_->GetTransportProxy("video")->impl()); + EXPECT_EQ(session_->voice_rtp_transport_channel(), + session_->video_rtp_transport_channel()); } -// kBundlePolicyMaxCompat bundle policy but no BUNDLE in the answer. +// kBundlePolicyMaxCompat BUNDLE policy but no BUNDLE in the answer. TEST_F(WebRtcSessionTest, TestMaxCompatNoBundleInAnswer) { InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyMaxCompat); mediastream_signaling_.SendAudioVideoStream1(); @@ -2899,8 +2999,8 @@ TEST_F(WebRtcSessionTest, TestMaxCompatNoBundleInAnswer) { SessionDescriptionInterface* offer = CreateOffer(options); SetLocalDescriptionWithoutError(offer); - EXPECT_NE(session_->GetTransportProxy("audio")->impl(), - session_->GetTransportProxy("video")->impl()); + EXPECT_NE(session_->voice_rtp_transport_channel(), + session_->video_rtp_transport_channel()); mediastream_signaling_.SendAudioVideoStream2(); @@ -2914,8 +3014,8 @@ TEST_F(WebRtcSessionTest, TestMaxCompatNoBundleInAnswer) { modified_answer->Initialize(answer_copy, "1", "1"); SetRemoteDescriptionWithoutError(modified_answer); // - EXPECT_NE(session_->GetTransportProxy("audio")->impl(), - session_->GetTransportProxy("video")->impl()); + EXPECT_NE(session_->voice_rtp_transport_channel(), + session_->video_rtp_transport_channel()); } // kBundlePolicyMaxbundle and then we call SetRemoteDescription first. @@ -2929,8 +3029,8 @@ TEST_F(WebRtcSessionTest, TestMaxBundleWithSetRemoteDescriptionFirst) { SessionDescriptionInterface* offer = CreateOffer(options); SetRemoteDescriptionWithoutError(offer); - EXPECT_EQ(session_->GetTransportProxy("audio")->impl(), - session_->GetTransportProxy("video")->impl()); + EXPECT_EQ(session_->voice_rtp_transport_channel(), + session_->video_rtp_transport_channel()); } TEST_F(WebRtcSessionTest, TestRequireRtcpMux) { @@ -2941,16 +3041,16 @@ TEST_F(WebRtcSessionTest, TestRequireRtcpMux) { SessionDescriptionInterface* offer = CreateOffer(options); SetLocalDescriptionWithoutError(offer); - EXPECT_FALSE(session_->GetTransportProxy("audio")->impl()->HasChannel(2)); - EXPECT_FALSE(session_->GetTransportProxy("video")->impl()->HasChannel(2)); + EXPECT_TRUE(session_->voice_rtcp_transport_channel() == NULL); + EXPECT_TRUE(session_->video_rtcp_transport_channel() == NULL); mediastream_signaling_.SendAudioVideoStream2(); SessionDescriptionInterface* answer = CreateRemoteAnswer(session_->local_description()); SetRemoteDescriptionWithoutError(answer); - EXPECT_FALSE(session_->GetTransportProxy("audio")->impl()->HasChannel(2)); - EXPECT_FALSE(session_->GetTransportProxy("video")->impl()->HasChannel(2)); + EXPECT_TRUE(session_->voice_rtcp_transport_channel() == NULL); + EXPECT_TRUE(session_->video_rtcp_transport_channel() == NULL); } TEST_F(WebRtcSessionTest, TestNegotiateRtcpMux) { @@ -2961,16 +3061,16 @@ TEST_F(WebRtcSessionTest, TestNegotiateRtcpMux) { SessionDescriptionInterface* offer = CreateOffer(options); SetLocalDescriptionWithoutError(offer); - EXPECT_TRUE(session_->GetTransportProxy("audio")->impl()->HasChannel(2)); - EXPECT_TRUE(session_->GetTransportProxy("video")->impl()->HasChannel(2)); + EXPECT_TRUE(session_->voice_rtcp_transport_channel() != NULL); + EXPECT_TRUE(session_->video_rtcp_transport_channel() != NULL); mediastream_signaling_.SendAudioVideoStream2(); SessionDescriptionInterface* answer = CreateRemoteAnswer(session_->local_description()); SetRemoteDescriptionWithoutError(answer); - EXPECT_FALSE(session_->GetTransportProxy("audio")->impl()->HasChannel(2)); - EXPECT_FALSE(session_->GetTransportProxy("video")->impl()->HasChannel(2)); + EXPECT_TRUE(session_->voice_rtcp_transport_channel() == NULL); + EXPECT_TRUE(session_->video_rtcp_transport_channel() == NULL); } // This test verifies that SetLocalDescription and SetRemoteDescription fails @@ -2991,11 +3091,11 @@ TEST_F(WebRtcSessionTest, TestDisabledRtcpMuxWithBundleEnabled) { rtc::replace_substrs(rtcp_mux.c_str(), rtcp_mux.length(), xrtcp_mux.c_str(), xrtcp_mux.length(), &offer_str); - JsepSessionDescription *local_offer = + JsepSessionDescription* local_offer = new JsepSessionDescription(JsepSessionDescription::kOffer); EXPECT_TRUE((local_offer)->Initialize(offer_str, NULL)); SetLocalDescriptionOfferExpectError(kBundleWithoutRtcpMux, local_offer); - JsepSessionDescription *remote_offer = + JsepSessionDescription* remote_offer = new JsepSessionDescription(JsepSessionDescription::kOffer); EXPECT_TRUE((remote_offer)->Initialize(offer_str, NULL)); SetRemoteDescriptionOfferExpectError(kBundleWithoutRtcpMux, remote_offer); @@ -3258,8 +3358,8 @@ TEST_F(WebRtcSessionTest, TestIceStartAfterSetLocalDescriptionOnly) { candidate1); EXPECT_TRUE(offer->AddCandidate(&ice_candidate1)); SetRemoteDescriptionWithoutError(offer); - ASSERT_TRUE(session_->GetTransportProxy("audio") != NULL); - ASSERT_TRUE(session_->GetTransportProxy("video") != NULL); + ASSERT_TRUE(session_->voice_rtp_transport_channel() != NULL); + ASSERT_TRUE(session_->video_rtp_transport_channel() != NULL); // Pump for 1 second and verify that no candidates are generated. rtc::Thread::Current()->ProcessMessages(1000); @@ -3268,8 +3368,6 @@ TEST_F(WebRtcSessionTest, TestIceStartAfterSetLocalDescriptionOnly) { SessionDescriptionInterface* answer = CreateAnswer(NULL); SetLocalDescriptionWithoutError(answer); - EXPECT_TRUE(session_->GetTransportProxy("audio")->negotiated()); - EXPECT_TRUE(session_->GetTransportProxy("video")->negotiated()); EXPECT_TRUE_WAIT(observer_.oncandidatesready_, kIceCandidatesTimeout); } @@ -3304,7 +3402,7 @@ TEST_F(WebRtcSessionTest, TestCryptoAfterSetLocalDescriptionWithDisabled) { // will be set as per MediaSessionDescriptionFactory. std::string offer_str; offer->ToString(&offer_str); - SessionDescriptionInterface *jsep_offer_str = + SessionDescriptionInterface* jsep_offer_str = CreateSessionDescription(JsepSessionDescription::kOffer, offer_str, NULL); SetLocalDescriptionWithoutError(jsep_offer_str); EXPECT_FALSE(session_->voice_channel()->secure_required()); @@ -3657,8 +3755,8 @@ TEST_F(WebRtcSessionTest, TestCreateOfferAfterIdentityRequestReturnFailure) { TEST_P(WebRtcSessionTest, TestMultipleCreateOfferBeforeIdentityRequestReturnSuccess) { MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp); - VerifyMultipleAsyncCreateDescription( - GetParam(), CreateSessionDescriptionRequest::kOffer); + VerifyMultipleAsyncCreateDescription(GetParam(), + CreateSessionDescriptionRequest::kOffer); } // Verifies that CreateOffer fails when Multiple CreateOffer calls are made @@ -3881,31 +3979,31 @@ TEST_F(WebRtcSessionTest, TestSetSocketOptionBeforeBundle) { rtc::Socket::Option::OPT_RCVBUF, 8000); int option_val; - EXPECT_TRUE(session_->video_channel()->transport_channel()->GetOption( + EXPECT_TRUE(session_->video_rtp_transport_channel()->GetOption( rtc::Socket::Option::OPT_SNDBUF, &option_val)); EXPECT_EQ(4000, option_val); - EXPECT_FALSE(session_->voice_channel()->transport_channel()->GetOption( + EXPECT_FALSE(session_->voice_rtp_transport_channel()->GetOption( rtc::Socket::Option::OPT_SNDBUF, &option_val)); - EXPECT_TRUE(session_->voice_channel()->transport_channel()->GetOption( + EXPECT_TRUE(session_->voice_rtp_transport_channel()->GetOption( rtc::Socket::Option::OPT_RCVBUF, &option_val)); EXPECT_EQ(8000, option_val); - EXPECT_FALSE(session_->video_channel()->transport_channel()->GetOption( + EXPECT_FALSE(session_->video_rtp_transport_channel()->GetOption( rtc::Socket::Option::OPT_RCVBUF, &option_val)); - EXPECT_NE(session_->voice_channel()->transport_channel(), - session_->video_channel()->transport_channel()); + EXPECT_NE(session_->voice_rtp_transport_channel(), + session_->video_rtp_transport_channel()); mediastream_signaling_.SendAudioVideoStream2(); SessionDescriptionInterface* answer = CreateRemoteAnswer(session_->local_description()); SetRemoteDescriptionWithoutError(answer); - EXPECT_TRUE(session_->voice_channel()->transport_channel()->GetOption( + EXPECT_TRUE(session_->voice_rtp_transport_channel()->GetOption( rtc::Socket::Option::OPT_SNDBUF, &option_val)); EXPECT_EQ(4000, option_val); - EXPECT_TRUE(session_->voice_channel()->transport_channel()->GetOption( + EXPECT_TRUE(session_->voice_rtp_transport_channel()->GetOption( rtc::Socket::Option::OPT_RCVBUF, &option_val)); EXPECT_EQ(8000, option_val); } @@ -3941,6 +4039,7 @@ TEST_F(WebRtcSessionTest, CreateOffersAndShutdown) { // currently fails because upon disconnection and reconnection OnIceComplete is // called more than once without returning to IceGatheringGathering. -INSTANTIATE_TEST_CASE_P( - WebRtcSessionTests, WebRtcSessionTest, - testing::Values(ALREADY_GENERATED, DTLS_IDENTITY_STORE)); +INSTANTIATE_TEST_CASE_P(WebRtcSessionTests, + WebRtcSessionTest, + testing::Values(ALREADY_GENERATED, + DTLS_IDENTITY_STORE)); diff --git a/talk/app/webrtc/webrtcsessiondescriptionfactory.cc b/talk/app/webrtc/webrtcsessiondescriptionfactory.cc index caa53df4ad..f6414d314e 100644 --- a/talk/app/webrtc/webrtcsessiondescriptionfactory.cc +++ b/talk/app/webrtc/webrtcsessiondescriptionfactory.cc @@ -165,9 +165,15 @@ WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory( WebRtcSession* session, const std::string& session_id, cricket::DataChannelType dct) - : WebRtcSessionDescriptionFactory( - signaling_thread, channel_manager, mediastream_signaling, nullptr, - nullptr, session, session_id, dct, false) { + : WebRtcSessionDescriptionFactory(signaling_thread, + channel_manager, + mediastream_signaling, + nullptr, + nullptr, + session, + session_id, + dct, + false) { LOG(LS_VERBOSE) << "DTLS-SRTP disabled."; } @@ -226,9 +232,9 @@ WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory( // We already have a certificate but we wait to do SetIdentity; if we do // it in the constructor then the caller has not had a chance to connect to // SignalIdentityReady. - signaling_thread_->Post(this, MSG_USE_CONSTRUCTOR_CERTIFICATE, - new rtc::ScopedRefMessageData( - certificate)); + signaling_thread_->Post( + this, MSG_USE_CONSTRUCTOR_CERTIFICATE, + new rtc::ScopedRefMessageData(certificate)); } WebRtcSessionDescriptionFactory::~WebRtcSessionDescriptionFactory() { @@ -254,8 +260,6 @@ WebRtcSessionDescriptionFactory::~WebRtcSessionDescriptionFactory() { delete msg.pdata; } } - - transport_desc_factory_.set_certificate(nullptr); } void WebRtcSessionDescriptionFactory::CreateOffer( diff --git a/talk/app/webrtc/webrtcsessiondescriptionfactory.h b/talk/app/webrtc/webrtcsessiondescriptionfactory.h index b42a551ef2..52b8da5d01 100644 --- a/talk/app/webrtc/webrtcsessiondescriptionfactory.h +++ b/talk/app/webrtc/webrtcsessiondescriptionfactory.h @@ -90,13 +90,12 @@ class WebRtcSessionDescriptionFactory : public rtc::MessageHandler, public sigslot::has_slots<> { public: // Construct with DTLS disabled. - WebRtcSessionDescriptionFactory( - rtc::Thread* signaling_thread, - cricket::ChannelManager* channel_manager, - MediaStreamSignaling* mediastream_signaling, - WebRtcSession* session, - const std::string& session_id, - cricket::DataChannelType dct); + WebRtcSessionDescriptionFactory(rtc::Thread* signaling_thread, + cricket::ChannelManager* channel_manager, + MediaStreamSignaling* mediastream_signaling, + WebRtcSession* session, + const std::string& session_id, + cricket::DataChannelType dct); // Construct with DTLS enabled using the specified |dtls_identity_store| to // generate a certificate. diff --git a/talk/media/webrtc/webrtcvoiceengine_unittest.cc b/talk/media/webrtc/webrtcvoiceengine_unittest.cc index d37374e972..ea9c40e0b5 100644 --- a/talk/media/webrtc/webrtcvoiceengine_unittest.cc +++ b/talk/media/webrtc/webrtcvoiceengine_unittest.cc @@ -36,7 +36,7 @@ #include "talk/media/webrtc/fakewebrtccall.h" #include "talk/media/webrtc/fakewebrtcvoiceengine.h" #include "talk/media/webrtc/webrtcvoiceengine.h" -#include "webrtc/p2p/base/fakesession.h" +#include "webrtc/p2p/base/faketransportcontroller.h" #include "talk/session/media/channel.h" // Tests for the WebRtcVoiceEngine/VoiceChannel code. diff --git a/talk/session/media/channel.cc b/talk/session/media/channel.cc index 3c5b9ddc44..1405c55bf7 100644 --- a/talk/session/media/channel.cc +++ b/talk/session/media/channel.cc @@ -170,15 +170,17 @@ void RtpSendParametersFromMediaDescription( } BaseChannel::BaseChannel(rtc::Thread* thread, - MediaChannel* media_channel, BaseSession* session, - const std::string& content_name, bool rtcp) + MediaChannel* media_channel, + TransportController* transport_controller, + const std::string& content_name, + bool rtcp) : worker_thread_(thread), - session_(session), + transport_controller_(transport_controller), media_channel_(media_channel), content_name_(content_name), - rtcp_(rtcp), - transport_channel_(NULL), - rtcp_transport_channel_(NULL), + rtcp_transport_enabled_(rtcp), + transport_channel_(nullptr), + rtcp_transport_channel_(nullptr), enabled_(false), writable_(false), rtp_ready_to_send_(false), @@ -204,20 +206,31 @@ BaseChannel::~BaseChannel() { // the media channel may try to send on the dead transport channel. NULLing // is not an effective strategy since the sends will come on another thread. delete media_channel_; - set_transport_channel(nullptr); - set_rtcp_transport_channel(nullptr); + // Note that we don't just call set_transport_channel(nullptr) because that + // would call a pure virtual method which we can't do from a destructor. + if (transport_channel_) { + DisconnectFromTransportChannel(transport_channel_); + transport_controller_->DestroyTransportChannel_w( + transport_name_, cricket::ICE_CANDIDATE_COMPONENT_RTP); + } + if (rtcp_transport_channel_) { + DisconnectFromTransportChannel(rtcp_transport_channel_); + transport_controller_->DestroyTransportChannel_w( + transport_name_, cricket::ICE_CANDIDATE_COMPONENT_RTCP); + } LOG(LS_INFO) << "Destroyed channel"; } bool BaseChannel::Init() { - if (!SetTransportChannels(session(), rtcp())) { + if (!SetTransport(content_name())) { return false; } if (!SetDtlsSrtpCiphers(transport_channel(), false)) { return false; } - if (rtcp() && !SetDtlsSrtpCiphers(rtcp_transport_channel(), true)) { + if (rtcp_transport_enabled() && + !SetDtlsSrtpCiphers(rtcp_transport_channel(), true)) { return false; } @@ -231,29 +244,35 @@ void BaseChannel::Deinit() { media_channel_->SetInterface(NULL); } -bool BaseChannel::SetTransportChannels(BaseSession* session, bool rtcp) { - return worker_thread_->Invoke(Bind( - &BaseChannel::SetTransportChannels_w, this, session, rtcp)); +bool BaseChannel::SetTransport(const std::string& transport_name) { + return worker_thread_->Invoke( + Bind(&BaseChannel::SetTransport_w, this, transport_name)); } -bool BaseChannel::SetTransportChannels_w(BaseSession* session, bool rtcp) { +bool BaseChannel::SetTransport_w(const std::string& transport_name) { ASSERT(worker_thread_ == rtc::Thread::Current()); - set_transport_channel(session->CreateChannel( - content_name(), cricket::ICE_CANDIDATE_COMPONENT_RTP)); + if (transport_name == transport_name_) { + // Nothing to do if transport name isn't changing + return true; + } + + set_transport_channel(transport_controller_->CreateTransportChannel_w( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP)); if (!transport_channel()) { return false; } - if (rtcp) { - set_rtcp_transport_channel(session->CreateChannel( - content_name(), cricket::ICE_CANDIDATE_COMPONENT_RTCP)); + if (rtcp_transport_enabled()) { + LOG(LS_INFO) << "Create RTCP TransportChannel for " << content_name() + << " on " << transport_name << " transport "; + set_rtcp_transport_channel(transport_controller_->CreateTransportChannel_w( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP)); if (!rtcp_transport_channel()) { return false; } - } else { - set_rtcp_transport_channel(nullptr); } + transport_name_ = transport_name; return true; } @@ -261,42 +280,62 @@ void BaseChannel::set_transport_channel(TransportChannel* new_tc) { ASSERT(worker_thread_ == rtc::Thread::Current()); TransportChannel* old_tc = transport_channel_; - - if (old_tc == new_tc) { + if (!old_tc && !new_tc) { + // Nothing to do return; } + ASSERT(old_tc != new_tc); + if (old_tc) { DisconnectFromTransportChannel(old_tc); - session()->DestroyChannel( - content_name(), cricket::ICE_CANDIDATE_COMPONENT_RTP); + transport_controller_->DestroyTransportChannel_w( + transport_name_, cricket::ICE_CANDIDATE_COMPONENT_RTP); } transport_channel_ = new_tc; if (new_tc) { ConnectToTransportChannel(new_tc); + for (const auto& pair : socket_options_) { + new_tc->SetOption(pair.first, pair.second); + } } + + // Update aggregate writable/ready-to-send state between RTP and RTCP upon + // setting new channel + UpdateWritableState_w(); + SetReadyToSend(false, new_tc && new_tc->writable()); } void BaseChannel::set_rtcp_transport_channel(TransportChannel* new_tc) { ASSERT(worker_thread_ == rtc::Thread::Current()); TransportChannel* old_tc = rtcp_transport_channel_; - - if (old_tc == new_tc) { + if (!old_tc && !new_tc) { + // Nothing to do return; } + ASSERT(old_tc != new_tc); + if (old_tc) { DisconnectFromTransportChannel(old_tc); - session()->DestroyChannel( - content_name(), cricket::ICE_CANDIDATE_COMPONENT_RTCP); + transport_controller_->DestroyTransportChannel_w( + transport_name_, cricket::ICE_CANDIDATE_COMPONENT_RTCP); } rtcp_transport_channel_ = new_tc; if (new_tc) { ConnectToTransportChannel(new_tc); + for (const auto& pair : rtcp_socket_options_) { + new_tc->SetOption(pair.first, pair.second); + } } + + // Update aggregate writable/ready-to-send state between RTP and RTCP upon + // setting new channel + UpdateWritableState_w(); + SetReadyToSend(true, new_tc && new_tc->writable()); } void BaseChannel::ConnectToTransportChannel(TransportChannel* tc) { @@ -407,9 +446,13 @@ int BaseChannel::SetOption(SocketType type, rtc::Socket::Option opt, switch (type) { case ST_RTP: channel = transport_channel_; + socket_options_.push_back( + std::pair(opt, value)); break; case ST_RTCP: channel = rtcp_transport_channel_; + rtcp_socket_options_.push_back( + std::pair(opt, value)); break; } return channel ? channel->SetOption(opt, value) : -1; @@ -417,12 +460,7 @@ int BaseChannel::SetOption(SocketType type, rtc::Socket::Option opt, void BaseChannel::OnWritableState(TransportChannel* channel) { ASSERT(channel == transport_channel_ || channel == rtcp_transport_channel_); - if (transport_channel_->writable() - && (!rtcp_transport_channel_ || rtcp_transport_channel_->writable())) { - ChannelWritable_w(); - } else { - ChannelNotWritable_w(); - } + UpdateWritableState_w(); } void BaseChannel::OnChannelRead(TransportChannel* channel, @@ -440,26 +478,25 @@ void BaseChannel::OnChannelRead(TransportChannel* channel, } void BaseChannel::OnReadyToSend(TransportChannel* channel) { - SetReadyToSend(channel, true); + ASSERT(channel == transport_channel_ || channel == rtcp_transport_channel_); + SetReadyToSend(channel == rtcp_transport_channel_, true); } -void BaseChannel::SetReadyToSend(TransportChannel* channel, bool ready) { - ASSERT(channel == transport_channel_ || channel == rtcp_transport_channel_); - if (channel == transport_channel_) { +void BaseChannel::SetReadyToSend(bool rtcp, bool ready) { + if (rtcp) { + rtcp_ready_to_send_ = ready; + } else { rtp_ready_to_send_ = ready; } - if (channel == rtcp_transport_channel_) { - rtcp_ready_to_send_ = ready; - } - if (!ready) { - // Notify the MediaChannel when either rtp or rtcp channel can't send. - media_channel_->OnReadyToSend(false); - } else if (rtp_ready_to_send_ && - // In the case of rtcp mux |rtcp_transport_channel_| will be null. - (rtcp_ready_to_send_ || !rtcp_transport_channel_)) { + if (rtp_ready_to_send_ && + // In the case of rtcp mux |rtcp_transport_channel_| will be null. + (rtcp_ready_to_send_ || !rtcp_transport_channel_)) { // Notify the MediaChannel when both rtp and rtcp channel can send. media_channel_->OnReadyToSend(true); + } else { + // Notify the MediaChannel when either rtp or rtcp channel can't send. + media_channel_->OnReadyToSend(false); } } @@ -581,7 +618,7 @@ bool BaseChannel::SendPacket(bool rtcp, rtc::Buffer* packet, if (ret != static_cast(packet->size())) { if (channel->GetError() == EWOULDBLOCK) { LOG(LS_WARNING) << "Got EWOULDBLOCK from socket."; - SetReadyToSend(channel, false); + SetReadyToSend(rtcp, false); } return false; } @@ -715,14 +752,21 @@ void BaseChannel::DisableMedia_w() { ChangeState(); } +void BaseChannel::UpdateWritableState_w() { + if (transport_channel_ && transport_channel_->writable() && + (!rtcp_transport_channel_ || rtcp_transport_channel_->writable())) { + ChannelWritable_w(); + } else { + ChannelNotWritable_w(); + } +} + void BaseChannel::ChannelWritable_w() { ASSERT(worker_thread_ == rtc::Thread::Current()); if (writable_) return; - LOG(LS_INFO) << "Channel socket writable (" - << transport_channel_->content_name() << ", " - << transport_channel_->component() << ")" + LOG(LS_INFO) << "Channel writable (" << content_name_ << ")" << (was_ever_writable_ ? "" : " for the first time"); std::vector infos; @@ -739,13 +783,13 @@ void BaseChannel::ChannelWritable_w() { // If we're doing DTLS-SRTP, now is the time. if (!was_ever_writable_ && ShouldSetupDtlsSrtp()) { if (!SetupDtlsSrtp(false)) { - SignalDtlsSetupFailure(this, false); + SignalDtlsSetupFailure_w(false); return; } if (rtcp_transport_channel_) { if (!SetupDtlsSrtp(true)) { - SignalDtlsSetupFailure(this, true); + SignalDtlsSetupFailure_w(true); return; } } @@ -788,8 +832,8 @@ bool BaseChannel::ShouldSetupDtlsSrtp() const { bool BaseChannel::SetupDtlsSrtp(bool rtcp_channel) { bool ret = false; - TransportChannel *channel = rtcp_channel ? - rtcp_transport_channel_ : transport_channel_; + TransportChannel* channel = + rtcp_channel ? rtcp_transport_channel_ : transport_channel_; // No DTLS if (!channel->IsDtlsActive()) @@ -884,9 +928,7 @@ void BaseChannel::ChannelNotWritable_w() { if (!writable_) return; - LOG(LS_INFO) << "Channel socket not writable (" - << transport_channel_->content_name() << ", " - << transport_channel_->component() << ")"; + LOG(LS_INFO) << "Channel not writable (" << content_name_ << ")"; writable_ = false; ChangeState(); } @@ -985,7 +1027,8 @@ void BaseChannel::ActivateRtcpMux() { void BaseChannel::ActivateRtcpMux_w() { if (!rtcp_mux_filter_.IsActive()) { rtcp_mux_filter_.SetActive(); - set_rtcp_transport_channel(NULL); + set_rtcp_transport_channel(nullptr); + rtcp_transport_enabled_ = false; } } @@ -1004,7 +1047,11 @@ bool BaseChannel::SetRtcpMux_w(bool enable, ContentAction action, ret = rtcp_mux_filter_.SetAnswer(enable, src); if (ret && rtcp_mux_filter_.IsActive()) { // We activated RTCP mux, close down the RTCP transport. - set_rtcp_transport_channel(NULL); + LOG(LS_INFO) << "Enabling rtcp-mux for " << content_name() + << " by destroying RTCP transport channel for " + << transport_name(); + set_rtcp_transport_channel(nullptr); + rtcp_transport_enabled_ = false; } break; case CA_UPDATE: @@ -1231,14 +1278,16 @@ void BaseChannel::FlushRtcpMessages() { VoiceChannel::VoiceChannel(rtc::Thread* thread, MediaEngineInterface* media_engine, VoiceMediaChannel* media_channel, - BaseSession* session, + TransportController* transport_controller, const std::string& content_name, bool rtcp) - : BaseChannel(thread, media_channel, session, content_name, + : BaseChannel(thread, + media_channel, + transport_controller, + content_name, rtcp), media_engine_(media_engine), - received_media_(false) { -} + received_media_(false) {} VoiceChannel::~VoiceChannel() { StopAudioMonitor(); @@ -1264,11 +1313,12 @@ bool VoiceChannel::SetRemoteRenderer(uint32 ssrc, AudioRenderer* renderer) { media_channel(), ssrc, renderer)); } -bool VoiceChannel::SetAudioSend(uint32 ssrc, bool mute, +bool VoiceChannel::SetAudioSend(uint32 ssrc, + bool mute, const AudioOptions* options, AudioRenderer* renderer) { - return InvokeOnWorker(Bind(&VoiceMediaChannel::SetAudioSend, - media_channel(), ssrc, mute, options, renderer)); + return InvokeOnWorker(Bind(&VoiceMediaChannel::SetAudioSend, media_channel(), + ssrc, mute, options, renderer)); } // TODO(juberti): Handle early media the right way. We should get an explicit @@ -1583,14 +1633,16 @@ void VoiceChannel::GetSrtpCiphers(std::vector* ciphers) const { VideoChannel::VideoChannel(rtc::Thread* thread, VideoMediaChannel* media_channel, - BaseSession* session, + TransportController* transport_controller, const std::string& content_name, bool rtcp) - : BaseChannel(thread, media_channel, session, content_name, + : BaseChannel(thread, + media_channel, + transport_controller, + content_name, rtcp), renderer_(NULL), - previous_we_(rtc::WE_CLOSE) { -} + previous_we_(rtc::WE_CLOSE) {} bool VideoChannel::Init() { if (!BaseChannel::Init()) { @@ -1683,10 +1735,11 @@ bool VideoChannel::RequestIntraFrame() { return true; } -bool VideoChannel::SetVideoSend(uint32 ssrc, bool mute, +bool VideoChannel::SetVideoSend(uint32 ssrc, + bool mute, const VideoOptions* options) { - return InvokeOnWorker(Bind(&VideoMediaChannel::SetVideoSend, - media_channel(), ssrc, mute, options)); + return InvokeOnWorker(Bind(&VideoMediaChannel::SetVideoSend, media_channel(), + ssrc, mute, options)); } void VideoChannel::ChangeState() { @@ -2021,13 +2074,16 @@ void VideoChannel::GetSrtpCiphers(std::vector* ciphers) const { DataChannel::DataChannel(rtc::Thread* thread, DataMediaChannel* media_channel, - BaseSession* session, + TransportController* transport_controller, const std::string& content_name, bool rtcp) - : BaseChannel(thread, media_channel, session, content_name, rtcp), + : BaseChannel(thread, + media_channel, + transport_controller, + content_name, + rtcp), data_channel_type_(cricket::DCT_NONE), - ready_to_send_data_(false) { -} + ready_to_send_data_(false) {} DataChannel::~DataChannel() { StopMediaMonitor(); diff --git a/talk/session/media/channel.h b/talk/session/media/channel.h index ec35f4cdb8..0a7ce9d1a9 100644 --- a/talk/session/media/channel.h +++ b/talk/session/media/channel.h @@ -30,12 +30,15 @@ #include #include +#include +#include +#include #include "talk/media/base/mediachannel.h" #include "talk/media/base/mediaengine.h" #include "talk/media/base/streamparams.h" #include "talk/media/base/videocapturer.h" -#include "webrtc/p2p/base/session.h" +#include "webrtc/p2p/base/transportcontroller.h" #include "webrtc/p2p/client/socketmonitor.h" #include "talk/session/media/audiomonitor.h" #include "talk/session/media/bundlefilter.h" @@ -74,8 +77,11 @@ class BaseChannel public MediaChannel::NetworkInterface, public ConnectionStatsGetter { public: - BaseChannel(rtc::Thread* thread, MediaChannel* channel, BaseSession* session, - const std::string& content_name, bool rtcp); + BaseChannel(rtc::Thread* thread, + MediaChannel* channel, + TransportController* transport_controller, + const std::string& content_name, + bool rtcp); virtual ~BaseChannel(); bool Init(); // Deinit may be called multiple times and is simply ignored if it's alreay @@ -83,8 +89,8 @@ class BaseChannel void Deinit(); rtc::Thread* worker_thread() const { return worker_thread_; } - BaseSession* session() const { return session_; } - const std::string& content_name() { return content_name_; } + const std::string& content_name() const { return content_name_; } + const std::string& transport_name() const { return transport_name_; } TransportChannel* transport_channel() const { return transport_channel_; } @@ -109,6 +115,7 @@ class BaseChannel // description doesn't support RTCP mux, setting the remote // description will fail. void ActivateRtcpMux(); + bool SetTransport(const std::string& transport_name); bool PushdownLocalDescription(const SessionDescription* local_desc, ContentAction action, std::string* error_desc); @@ -135,7 +142,7 @@ class BaseChannel void StartConnectionMonitor(int cms); void StopConnectionMonitor(); // For ConnectionStatsGetter, used by ConnectionMonitor - virtual bool GetConnectionStats(ConnectionInfos* infos) override; + bool GetConnectionStats(ConnectionInfos* infos) override; void set_srtp_signal_silent_time(uint32 silent_time) { srtp_filter_.set_signal_silent_time(silent_time); @@ -158,19 +165,16 @@ class BaseChannel sigslot::signal1 SignalFirstPacketReceived; // Made public for easier testing. - void SetReadyToSend(TransportChannel* channel, bool ready); + void SetReadyToSend(bool rtcp, bool ready); // Only public for unit tests. Otherwise, consider protected. virtual int SetOption(SocketType type, rtc::Socket::Option o, int val); protected: virtual MediaChannel* media_channel() const { return media_channel_; } - // Sets the transport_channel_ and rtcp_transport_channel_. If - // |rtcp| is false, set rtcp_transport_channel_ is set to NULL. Get - // the transport channels from |session|. - // TODO(pthatcher): Pass in a Transport instead of a BaseSession. - bool SetTransportChannels(BaseSession* session, bool rtcp); - bool SetTransportChannels_w(BaseSession* session, bool rtcp); + // Sets the |transport_channel_| (and |rtcp_transport_channel_|, if |rtcp_| is + // true). Gets the transport channels from |transport_controller_|. + bool SetTransport_w(const std::string& transport_name); void set_transport_channel(TransportChannel* transport); void set_rtcp_transport_channel(TransportChannel* transport); bool was_ever_writable() const { return was_ever_writable_; } @@ -185,9 +189,11 @@ class BaseChannel } bool IsReadyToReceive() const; bool IsReadyToSend() const; - rtc::Thread* signaling_thread() { return session_->signaling_thread(); } + rtc::Thread* signaling_thread() { + return transport_controller_->signaling_thread(); + } SrtpFilter* srtp_filter() { return &srtp_filter_; } - bool rtcp() const { return rtcp_; } + bool rtcp_transport_enabled() const { return rtcp_transport_enabled_; } void ConnectToTransportChannel(TransportChannel* tc); void DisconnectFromTransportChannel(TransportChannel* tc); @@ -217,12 +223,9 @@ class BaseChannel void HandlePacket(bool rtcp, rtc::Buffer* packet, const rtc::PacketTime& packet_time); - // Apply the new local/remote session description. - void OnNewLocalDescription(BaseSession* session, ContentAction action); - void OnNewRemoteDescription(BaseSession* session, ContentAction action); - void EnableMedia_w(); void DisableMedia_w(); + void UpdateWritableState_w(); void ChannelWritable_w(); void ChannelNotWritable_w(); bool AddRecvStream_w(const StreamParams& sp); @@ -293,15 +296,18 @@ class BaseChannel private: rtc::Thread* worker_thread_; - BaseSession* session_; + TransportController* transport_controller_; MediaChannel* media_channel_; std::vector local_streams_; std::vector remote_streams_; const std::string content_name_; - bool rtcp_; + std::string transport_name_; + bool rtcp_transport_enabled_; TransportChannel* transport_channel_; + std::vector > socket_options_; TransportChannel* rtcp_transport_channel_; + std::vector > rtcp_socket_options_; SrtpFilter srtp_filter_; RtcpMuxFilter rtcp_mux_filter_; BundleFilter bundle_filter_; @@ -323,16 +329,21 @@ class BaseChannel // and input/output level monitoring. class VoiceChannel : public BaseChannel { public: - VoiceChannel(rtc::Thread* thread, MediaEngineInterface* media_engine, - VoiceMediaChannel* channel, BaseSession* session, - const std::string& content_name, bool rtcp); + VoiceChannel(rtc::Thread* thread, + MediaEngineInterface* media_engine, + VoiceMediaChannel* channel, + TransportController* transport_controller, + const std::string& content_name, + bool rtcp); ~VoiceChannel(); bool Init(); bool SetRemoteRenderer(uint32 ssrc, AudioRenderer* renderer); // Configure sending media on the stream with SSRC |ssrc| // If there is only one sending stream SSRC 0 can be used. - bool SetAudioSend(uint32 ssrc, bool mute, const AudioOptions* options, + bool SetAudioSend(uint32 ssrc, + bool mute, + const AudioOptions* options, AudioRenderer* renderer); // downcasts a MediaChannel @@ -429,8 +440,10 @@ class VoiceChannel : public BaseChannel { // VideoChannel is a specialization for video. class VideoChannel : public BaseChannel { public: - VideoChannel(rtc::Thread* thread, VideoMediaChannel* channel, - BaseSession* session, const std::string& content_name, + VideoChannel(rtc::Thread* thread, + VideoMediaChannel* channel, + TransportController* transport_controller, + const std::string& content_name, bool rtcp); ~VideoChannel(); bool Init(); @@ -529,7 +542,7 @@ class DataChannel : public BaseChannel { public: DataChannel(rtc::Thread* thread, DataMediaChannel* media_channel, - BaseSession* session, + TransportController* transport_controller, const std::string& content_name, bool rtcp); ~DataChannel(); diff --git a/talk/session/media/channel_unittest.cc b/talk/session/media/channel_unittest.cc index 22b1aef407..d6e259d31e 100644 --- a/talk/session/media/channel_unittest.cc +++ b/talk/session/media/channel_unittest.cc @@ -33,7 +33,7 @@ #include "talk/media/base/rtpdump.h" #include "talk/media/base/screencastid.h" #include "talk/media/base/testutils.h" -#include "webrtc/p2p/base/fakesession.h" +#include "webrtc/p2p/base/faketransportcontroller.h" #include "talk/session/media/channel.h" #include "webrtc/base/fileutils.h" #include "webrtc/base/gunit.h" @@ -73,12 +73,12 @@ static const uint32 kSsrc3 = 0x3333; static const int kAudioPts[] = {0, 8}; static const int kVideoPts[] = {97, 99}; -template +template class Traits { public: typedef ChannelT Channel; @@ -98,25 +98,21 @@ class VoiceTraits : public Traits { -}; + cricket::AudioOptions> {}; class VideoTraits : public Traits { -}; + cricket::VideoOptions> {}; class DataTraits : public Traits { -}; - + cricket::DataOptions> {}; rtc::StreamInterface* Open(const std::string& path) { return rtc::Filesystem::OpenFile( @@ -130,10 +126,12 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { enum Flags { RTCP = 0x1, RTCP_MUX = 0x2, SECURE = 0x4, SSRC_MUX = 0x8, DTLS = 0x10 }; - ChannelTest(const uint8* rtp_data, int rtp_len, - const uint8* rtcp_data, int rtcp_len) - : session1_(true), - session2_(false), + ChannelTest(const uint8* rtp_data, + int rtp_len, + const uint8* rtcp_data, + int rtcp_len) + : transport_controller1_(cricket::ICEROLE_CONTROLLING), + transport_controller2_(cricket::ICEROLE_CONTROLLED), media_channel1_(NULL), media_channel2_(NULL), rtp_packet_(reinterpret_cast(rtp_data), rtp_len), @@ -141,8 +139,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { media_info_callbacks1_(), media_info_callbacks2_(), ssrc_(0), - error_(T::MediaChannel::ERROR_NONE) { - } + error_(T::MediaChannel::ERROR_NONE) {} void CreateChannels(int flags1, int flags2) { CreateChannels(new typename T::MediaChannel(NULL, typename T::Options()), @@ -154,9 +151,11 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { int flags1, int flags2, rtc::Thread* thread) { media_channel1_ = ch1; media_channel2_ = ch2; - channel1_.reset(CreateChannel(thread, &media_engine_, ch1, &session1_, + channel1_.reset(CreateChannel(thread, &media_engine_, ch1, + &transport_controller1_, (flags1 & RTCP) != 0)); - channel2_.reset(CreateChannel(thread, &media_engine_, ch2, &session2_, + channel2_.reset(CreateChannel(thread, &media_engine_, ch2, + &transport_controller2_, (flags2 & RTCP) != 0)); channel1_->SignalMediaMonitor.connect( this, &ChannelTest::OnMediaMonitor); @@ -179,15 +178,17 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { if (flags1 & DTLS) { // Confirmed to work with KT_RSA and KT_ECDSA. - session1_.set_ssl_rtccertificate(rtc::RTCCertificate::Create( - rtc::scoped_ptr(rtc::SSLIdentity::Generate( - "session1", rtc::KT_DEFAULT)).Pass())); + transport_controller1_.SetLocalCertificate(rtc::RTCCertificate::Create( + rtc::scoped_ptr( + rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT)) + .Pass())); } if (flags2 & DTLS) { // Confirmed to work with KT_RSA and KT_ECDSA. - session2_.set_ssl_rtccertificate(rtc::RTCCertificate::Create( - rtc::scoped_ptr(rtc::SSLIdentity::Generate( - "session2", rtc::KT_DEFAULT)).Pass())); + transport_controller2_.SetLocalCertificate(rtc::RTCCertificate::Create( + rtc::scoped_ptr( + rtc::SSLIdentity::Generate("session2", rtc::KT_DEFAULT)) + .Pass())); } // Add stream information (SSRC) to the local content but not to the remote @@ -204,13 +205,14 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { AddLegacyStreamInContent(kSsrc2, flags2, &remote_media_content2_); } } - typename T::Channel* CreateChannel(rtc::Thread* thread, - cricket::MediaEngineInterface* engine, - typename T::MediaChannel* ch, - cricket::BaseSession* session, - bool rtcp) { + typename T::Channel* CreateChannel( + rtc::Thread* thread, + cricket::MediaEngineInterface* engine, + typename T::MediaChannel* ch, + cricket::TransportController* transport_controller, + bool rtcp) { typename T::Channel* channel = new typename T::Channel( - thread, engine, ch, session, cricket::CN_AUDIO, rtcp); + thread, engine, ch, transport_controller, cricket::CN_AUDIO, rtcp); if (!channel->Init()) { delete channel; channel = NULL; @@ -226,7 +228,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { result = channel2_->SetRemoteContent(&remote_media_content1_, CA_OFFER, NULL); if (result) { - session1_.Connect(&session2_); + transport_controller1_.Connect(&transport_controller2_); result = channel2_->SetLocalContent(&local_media_content2_, CA_ANSWER, NULL); @@ -259,7 +261,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { channel2_->Enable(true); result = channel1_->SetRemoteContent(&remote_media_content2_, CA_PRANSWER, NULL); - session1_.Connect(&session2_); + transport_controller1_.Connect(&transport_controller2_); } return result; } @@ -286,11 +288,12 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { return channel1_->RemoveRecvStream(id); } + // Calling "_w" method here is ok since we only use one thread for this test cricket::FakeTransport* GetTransport1() { - return session1_.GetTransport(channel1_->content_name()); + return transport_controller1_.GetTransport_w(channel1_->content_name()); } cricket::FakeTransport* GetTransport2() { - return session2_.GetTransport(channel2_->content_name()); + return transport_controller2_.GetTransport_w(channel2_->content_name()); } bool SendRtp1() { @@ -769,7 +772,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { EXPECT_TRUE(channel2_->SetRemoteContent(&content1, CA_OFFER, NULL)); EXPECT_EQ(1u, media_channel2_->recv_streams().size()); - session1_.Connect(&session2_); + transport_controller1_.Connect(&transport_controller2_); // Channel 2 do not send anything. typename T::Content content2; @@ -832,7 +835,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { CA_ANSWER, NULL)); EXPECT_FALSE(media_channel2_->playout()); EXPECT_FALSE(media_channel2_->sending()); - session1_.Connect(&session2_); + transport_controller1_.Connect(&transport_controller2_); EXPECT_TRUE(media_channel1_->playout()); EXPECT_FALSE(media_channel1_->sending()); EXPECT_FALSE(media_channel2_->playout()); @@ -868,7 +871,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { EXPECT_TRUE(channel2_->SetRemoteContent(&content1, CA_OFFER, NULL)); EXPECT_TRUE(channel2_->SetLocalContent(&content2, CA_PRANSWER, NULL)); EXPECT_TRUE(channel1_->SetRemoteContent(&content2, CA_PRANSWER, NULL)); - session1_.Connect(&session2_); + transport_controller1_.Connect(&transport_controller2_); EXPECT_TRUE(media_channel1_->playout()); EXPECT_FALSE(media_channel1_->sending()); // remote InActive @@ -938,6 +941,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { CreateChannels(0, 0); EXPECT_TRUE(SendInitiate()); EXPECT_TRUE(SendAccept()); + ASSERT_TRUE(GetTransport1()); + ASSERT_TRUE(GetTransport2()); EXPECT_EQ(1U, GetTransport1()->channels().size()); EXPECT_EQ(1U, GetTransport2()->channels().size()); EXPECT_TRUE(SendRtp1()); @@ -953,6 +958,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { CreateChannels(0, 0); EXPECT_TRUE(SendInitiate()); EXPECT_TRUE(SendAccept()); + ASSERT_TRUE(GetTransport1()); + ASSERT_TRUE(GetTransport2()); EXPECT_EQ(1U, GetTransport1()->channels().size()); EXPECT_EQ(1U, GetTransport2()->channels().size()); EXPECT_FALSE(SendRtcp1()); @@ -966,6 +973,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { CreateChannels(0, RTCP); EXPECT_TRUE(SendInitiate()); EXPECT_TRUE(SendAccept()); + ASSERT_TRUE(GetTransport1()); + ASSERT_TRUE(GetTransport2()); EXPECT_EQ(1U, GetTransport1()->channels().size()); EXPECT_EQ(2U, GetTransport2()->channels().size()); EXPECT_FALSE(SendRtcp1()); @@ -979,6 +988,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { CreateChannels(RTCP, 0); EXPECT_TRUE(SendInitiate()); EXPECT_TRUE(SendAccept()); + ASSERT_TRUE(GetTransport1()); + ASSERT_TRUE(GetTransport2()); EXPECT_EQ(2U, GetTransport1()->channels().size()); EXPECT_EQ(1U, GetTransport2()->channels().size()); EXPECT_FALSE(SendRtcp1()); @@ -992,6 +1003,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { CreateChannels(RTCP, RTCP); EXPECT_TRUE(SendInitiate()); EXPECT_TRUE(SendAccept()); + ASSERT_TRUE(GetTransport1()); + ASSERT_TRUE(GetTransport2()); EXPECT_EQ(2U, GetTransport1()->channels().size()); EXPECT_EQ(2U, GetTransport2()->channels().size()); EXPECT_TRUE(SendRtcp1()); @@ -1007,6 +1020,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { CreateChannels(RTCP | RTCP_MUX, RTCP); EXPECT_TRUE(SendInitiate()); EXPECT_TRUE(SendAccept()); + ASSERT_TRUE(GetTransport1()); + ASSERT_TRUE(GetTransport2()); EXPECT_EQ(2U, GetTransport1()->channels().size()); EXPECT_EQ(2U, GetTransport2()->channels().size()); EXPECT_TRUE(SendRtcp1()); @@ -1021,6 +1036,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { void SendRtcpMuxToRtcpMux() { CreateChannels(RTCP | RTCP_MUX, RTCP | RTCP_MUX); EXPECT_TRUE(SendInitiate()); + ASSERT_TRUE(GetTransport1()); + ASSERT_TRUE(GetTransport2()); EXPECT_EQ(2U, GetTransport1()->channels().size()); EXPECT_EQ(1U, GetTransport2()->channels().size()); EXPECT_TRUE(SendAccept()); @@ -1045,6 +1062,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { CreateChannels(RTCP | RTCP_MUX, RTCP | RTCP_MUX); channel1_->ActivateRtcpMux(); EXPECT_TRUE(SendInitiate()); + ASSERT_TRUE(GetTransport1()); + ASSERT_TRUE(GetTransport2()); EXPECT_EQ(1U, GetTransport1()->channels().size()); EXPECT_EQ(1U, GetTransport2()->channels().size()); EXPECT_TRUE(SendAccept()); @@ -1068,6 +1087,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { CreateChannels(RTCP | RTCP_MUX, RTCP | RTCP_MUX); channel2_->ActivateRtcpMux(); EXPECT_TRUE(SendInitiate()); + ASSERT_TRUE(GetTransport1()); + ASSERT_TRUE(GetTransport2()); EXPECT_EQ(2U, GetTransport1()->channels().size()); EXPECT_EQ(1U, GetTransport2()->channels().size()); EXPECT_TRUE(SendAccept()); @@ -1093,6 +1114,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { channel1_->ActivateRtcpMux(); channel2_->ActivateRtcpMux(); EXPECT_TRUE(SendInitiate()); + ASSERT_TRUE(GetTransport1()); + ASSERT_TRUE(GetTransport2()); EXPECT_EQ(1U, GetTransport1()->channels().size()); EXPECT_EQ(1U, GetTransport2()->channels().size()); EXPECT_TRUE(SendAccept()); @@ -1117,6 +1140,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { CreateChannels(RTCP | RTCP_MUX, RTCP); channel1_->ActivateRtcpMux(); EXPECT_TRUE(SendInitiate()); + ASSERT_TRUE(GetTransport1()); + ASSERT_TRUE(GetTransport2()); EXPECT_EQ(1U, GetTransport1()->channels().size()); EXPECT_EQ(2U, GetTransport2()->channels().size()); EXPECT_FALSE(SendAccept()); @@ -1126,6 +1151,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { void SendEarlyRtcpMuxToRtcp() { CreateChannels(RTCP | RTCP_MUX, RTCP); EXPECT_TRUE(SendInitiate()); + ASSERT_TRUE(GetTransport1()); + ASSERT_TRUE(GetTransport2()); EXPECT_EQ(2U, GetTransport1()->channels().size()); EXPECT_EQ(2U, GetTransport2()->channels().size()); @@ -1156,6 +1183,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { void SendEarlyRtcpMuxToRtcpMux() { CreateChannels(RTCP | RTCP_MUX, RTCP | RTCP_MUX); EXPECT_TRUE(SendInitiate()); + ASSERT_TRUE(GetTransport1()); + ASSERT_TRUE(GetTransport2()); EXPECT_EQ(2U, GetTransport1()->channels().size()); EXPECT_EQ(1U, GetTransport2()->channels().size()); @@ -1246,6 +1275,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { EXPECT_TRUE(SendProvisionalAnswer()); EXPECT_TRUE(channel1_->secure()); EXPECT_TRUE(channel2_->secure()); + ASSERT_TRUE(GetTransport1()); + ASSERT_TRUE(GetTransport2()); EXPECT_EQ(2U, GetTransport1()->channels().size()); EXPECT_EQ(2U, GetTransport2()->channels().size()); EXPECT_TRUE(SendCustomRtcp1(kSsrc1)); @@ -1329,6 +1360,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { CreateChannels(0, 0); EXPECT_TRUE(SendInitiate()); EXPECT_TRUE(SendAccept()); + ASSERT_TRUE(GetTransport1()); + ASSERT_TRUE(GetTransport2()); EXPECT_EQ(1U, GetTransport1()->channels().size()); EXPECT_EQ(1U, GetTransport2()->channels().size()); EXPECT_TRUE(SendRtp1()); @@ -1393,6 +1426,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { } CreateChannels(flags, flags); EXPECT_TRUE(SendInitiate()); + ASSERT_TRUE(GetTransport1()); + ASSERT_TRUE(GetTransport2()); EXPECT_EQ(2U, GetTransport1()->channels().size()); EXPECT_EQ(expected_channels, GetTransport2()->channels().size()); EXPECT_TRUE(SendAccept()); @@ -1581,6 +1616,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { CreateChannels(RTCP, RTCP); EXPECT_TRUE(SendInitiate()); EXPECT_TRUE(SendAccept()); + ASSERT_TRUE(GetTransport1()); + ASSERT_TRUE(GetTransport2()); EXPECT_EQ(2U, GetTransport1()->channels().size()); EXPECT_EQ(2U, GetTransport2()->channels().size()); @@ -1669,15 +1706,15 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { EXPECT_TRUE(media_channel1_->ready_to_send()); // rtp channel becomes not ready to send will be propagated to mediachannel - channel1_->SetReadyToSend(rtp, false); + channel1_->SetReadyToSend(false, false); EXPECT_FALSE(media_channel1_->ready_to_send()); - channel1_->SetReadyToSend(rtp, true); + channel1_->SetReadyToSend(false, true); EXPECT_TRUE(media_channel1_->ready_to_send()); // rtcp channel becomes not ready to send will be propagated to mediachannel - channel1_->SetReadyToSend(rtcp, false); + channel1_->SetReadyToSend(true, false); EXPECT_FALSE(media_channel1_->ready_to_send()); - channel1_->SetReadyToSend(rtcp, true); + channel1_->SetReadyToSend(true, true); EXPECT_TRUE(media_channel1_->ready_to_send()); } @@ -1696,13 +1733,13 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { // should trigger the MediaChannel's OnReadyToSend. rtp->SignalReadyToSend(rtp); EXPECT_TRUE(media_channel1_->ready_to_send()); - channel1_->SetReadyToSend(rtp, false); + channel1_->SetReadyToSend(false, false); EXPECT_FALSE(media_channel1_->ready_to_send()); } protected: - cricket::FakeSession session1_; - cricket::FakeSession session2_; + cricket::FakeTransportController transport_controller1_; + cricket::FakeTransportController transport_controller2_; cricket::FakeMediaEngine media_engine_; // The media channels are owned by the voice channel objects below. typename T::MediaChannel* media_channel1_; @@ -1763,18 +1800,21 @@ class VoiceChannelTest : public ChannelTest { public: typedef ChannelTest Base; - VoiceChannelTest() : Base(kPcmuFrame, sizeof(kPcmuFrame), - kRtcpReport, sizeof(kRtcpReport)) {} + VoiceChannelTest() + : Base(kPcmuFrame, sizeof(kPcmuFrame), kRtcpReport, sizeof(kRtcpReport)) { + } }; // override to add NULL parameter -template<> +template <> cricket::VideoChannel* ChannelTest::CreateChannel( - rtc::Thread* thread, cricket::MediaEngineInterface* engine, - cricket::FakeVideoMediaChannel* ch, cricket::BaseSession* session, + rtc::Thread* thread, + cricket::MediaEngineInterface* engine, + cricket::FakeVideoMediaChannel* ch, + cricket::TransportController* transport_controller, bool rtcp) { cricket::VideoChannel* channel = new cricket::VideoChannel( - thread, ch, session, cricket::CN_VIDEO, rtcp); + thread, ch, transport_controller, cricket::CN_VIDEO, rtcp); if (!channel->Init()) { delete channel; channel = NULL; @@ -1827,8 +1867,11 @@ class VideoChannelTest : public ChannelTest { public: typedef ChannelTest Base; - VideoChannelTest() : Base(kH264Packet, sizeof(kH264Packet), - kRtcpReport, sizeof(kRtcpReport)) {} + VideoChannelTest() + : Base(kH264Packet, + sizeof(kH264Packet), + kRtcpReport, + sizeof(kRtcpReport)) {} }; @@ -2519,13 +2562,15 @@ class DataChannelTest }; // Override to avoid engine channel parameter. -template<> +template <> cricket::DataChannel* ChannelTest::CreateChannel( - rtc::Thread* thread, cricket::MediaEngineInterface* engine, - cricket::FakeDataMediaChannel* ch, cricket::BaseSession* session, + rtc::Thread* thread, + cricket::MediaEngineInterface* engine, + cricket::FakeDataMediaChannel* ch, + cricket::TransportController* transport_controller, bool rtcp) { cricket::DataChannel* channel = new cricket::DataChannel( - thread, ch, session, cricket::CN_DATA, rtcp); + thread, ch, transport_controller, cricket::CN_DATA, rtcp); if (!channel->Init()) { delete channel; channel = NULL; diff --git a/talk/session/media/channelmanager.cc b/talk/session/media/channelmanager.cc index 8667e597a0..e0ee95037e 100644 --- a/talk/session/media/channelmanager.cc +++ b/talk/session/media/channelmanager.cc @@ -318,23 +318,18 @@ void ChannelManager::Terminate_w() { VoiceChannel* ChannelManager::CreateVoiceChannel( webrtc::MediaControllerInterface* media_controller, - BaseSession* session, + TransportController* transport_controller, const std::string& content_name, bool rtcp, const AudioOptions& options) { return worker_thread_->Invoke( - Bind(&ChannelManager::CreateVoiceChannel_w, - this, - media_controller, - session, - content_name, - rtcp, - options)); + Bind(&ChannelManager::CreateVoiceChannel_w, this, media_controller, + transport_controller, content_name, rtcp, options)); } VoiceChannel* ChannelManager::CreateVoiceChannel_w( webrtc::MediaControllerInterface* media_controller, - BaseSession* session, + TransportController* transport_controller, const std::string& content_name, bool rtcp, const AudioOptions& options) { @@ -346,9 +341,9 @@ VoiceChannel* ChannelManager::CreateVoiceChannel_w( if (!media_channel) return nullptr; - VoiceChannel* voice_channel = new VoiceChannel( - worker_thread_, media_engine_.get(), media_channel, - session, content_name, rtcp); + VoiceChannel* voice_channel = + new VoiceChannel(worker_thread_, media_engine_.get(), media_channel, + transport_controller, content_name, rtcp); if (!voice_channel->Init()) { delete voice_channel; return nullptr; @@ -379,23 +374,18 @@ void ChannelManager::DestroyVoiceChannel_w(VoiceChannel* voice_channel) { VideoChannel* ChannelManager::CreateVideoChannel( webrtc::MediaControllerInterface* media_controller, - BaseSession* session, + TransportController* transport_controller, const std::string& content_name, bool rtcp, const VideoOptions& options) { return worker_thread_->Invoke( - Bind(&ChannelManager::CreateVideoChannel_w, - this, - media_controller, - session, - content_name, - rtcp, - options)); + Bind(&ChannelManager::CreateVideoChannel_w, this, media_controller, + transport_controller, content_name, rtcp, options)); } VideoChannel* ChannelManager::CreateVideoChannel_w( webrtc::MediaControllerInterface* media_controller, - BaseSession* session, + TransportController* transport_controller, const std::string& content_name, bool rtcp, const VideoOptions& options) { @@ -404,12 +394,12 @@ VideoChannel* ChannelManager::CreateVideoChannel_w( ASSERT(nullptr != media_controller); VideoMediaChannel* media_channel = media_engine_->CreateVideoChannel(media_controller->call_w(), options); - if (media_channel == NULL) + if (media_channel == NULL) { return NULL; + } VideoChannel* video_channel = new VideoChannel( - worker_thread_, media_channel, - session, content_name, rtcp); + worker_thread_, media_channel, transport_controller, content_name, rtcp); if (!video_channel->Init()) { delete video_channel; return NULL; @@ -440,16 +430,20 @@ void ChannelManager::DestroyVideoChannel_w(VideoChannel* video_channel) { } DataChannel* ChannelManager::CreateDataChannel( - BaseSession* session, const std::string& content_name, - bool rtcp, DataChannelType channel_type) { + TransportController* transport_controller, + const std::string& content_name, + bool rtcp, + DataChannelType channel_type) { return worker_thread_->Invoke( - Bind(&ChannelManager::CreateDataChannel_w, this, session, content_name, - rtcp, channel_type)); + Bind(&ChannelManager::CreateDataChannel_w, this, transport_controller, + content_name, rtcp, channel_type)); } DataChannel* ChannelManager::CreateDataChannel_w( - BaseSession* session, const std::string& content_name, - bool rtcp, DataChannelType data_channel_type) { + TransportController* transport_controller, + const std::string& content_name, + bool rtcp, + DataChannelType data_channel_type) { // This is ok to alloc from a thread other than the worker thread. ASSERT(initialized_); DataMediaChannel* media_channel = data_media_engine_->CreateChannel( @@ -461,8 +455,7 @@ DataChannel* ChannelManager::CreateDataChannel_w( } DataChannel* data_channel = new DataChannel( - worker_thread_, media_channel, - session, content_name, rtcp); + worker_thread_, media_channel, transport_controller, content_name, rtcp); if (!data_channel->Init()) { LOG(LS_WARNING) << "Failed to init data channel."; delete data_channel; diff --git a/talk/session/media/channelmanager.h b/talk/session/media/channelmanager.h index 68b3c9efa7..c307dcd5f2 100644 --- a/talk/session/media/channelmanager.h +++ b/talk/session/media/channelmanager.h @@ -105,11 +105,10 @@ class ChannelManager : public rtc::MessageHandler, void Terminate(); // The operations below all occur on the worker thread. - // Creates a voice channel, to be associated with the specified session. VoiceChannel* CreateVoiceChannel( webrtc::MediaControllerInterface* media_controller, - BaseSession* session, + TransportController* transport_controller, const std::string& content_name, bool rtcp, const AudioOptions& options); @@ -119,15 +118,16 @@ class ChannelManager : public rtc::MessageHandler, // associated with the specified session. VideoChannel* CreateVideoChannel( webrtc::MediaControllerInterface* media_controller, - BaseSession* session, + TransportController* transport_controller, const std::string& content_name, bool rtcp, const VideoOptions& options); // Destroys a video channel created with the Create API. void DestroyVideoChannel(VideoChannel* video_channel); - DataChannel* CreateDataChannel( - BaseSession* session, const std::string& content_name, - bool rtcp, DataChannelType data_channel_type); + DataChannel* CreateDataChannel(TransportController* transport_controller, + const std::string& content_name, + bool rtcp, + DataChannelType data_channel_type); // Destroys a data channel created with the Create API. void DestroyDataChannel(DataChannel* data_channel); @@ -249,21 +249,22 @@ class ChannelManager : public rtc::MessageHandler, void Terminate_w(); VoiceChannel* CreateVoiceChannel_w( webrtc::MediaControllerInterface* media_controller, - BaseSession* session, + TransportController* transport_controller, const std::string& content_name, bool rtcp, const AudioOptions& options); void DestroyVoiceChannel_w(VoiceChannel* voice_channel); VideoChannel* CreateVideoChannel_w( webrtc::MediaControllerInterface* media_controller, - BaseSession* session, + TransportController* transport_controller, const std::string& content_name, bool rtcp, const VideoOptions& options); void DestroyVideoChannel_w(VideoChannel* video_channel); - DataChannel* CreateDataChannel_w( - BaseSession* session, const std::string& content_name, - bool rtcp, DataChannelType data_channel_type); + DataChannel* CreateDataChannel_w(TransportController* transport_controller, + const std::string& content_name, + bool rtcp, + DataChannelType data_channel_type); void DestroyDataChannel_w(DataChannel* data_channel); bool SetAudioOptions_w(const AudioOptions& options, int delay_offset, const Device* in_dev, const Device* out_dev); diff --git a/talk/session/media/channelmanager_unittest.cc b/talk/session/media/channelmanager_unittest.cc index 71493c8ff8..c699ee1a5f 100644 --- a/talk/session/media/channelmanager_unittest.cc +++ b/talk/session/media/channelmanager_unittest.cc @@ -32,11 +32,11 @@ #include "talk/media/base/testutils.h" #include "talk/media/devices/fakedevicemanager.h" #include "talk/media/webrtc/fakewebrtccall.h" -#include "webrtc/p2p/base/fakesession.h" #include "talk/session/media/channelmanager.h" #include "webrtc/base/gunit.h" #include "webrtc/base/logging.h" #include "webrtc/base/thread.h" +#include "webrtc/p2p/base/faketransportcontroller.h" namespace cricket { @@ -58,14 +58,20 @@ class FakeMediaController : public webrtc::MediaControllerInterface { } ~FakeMediaController() override {} webrtc::Call* call_w() override { return call_; } + private: webrtc::Call* call_; }; class ChannelManagerTest : public testing::Test { protected: - ChannelManagerTest() : fake_call_(webrtc::Call::Config()), - fake_mc_(&fake_call_), fme_(NULL), fdm_(NULL), fcm_(NULL), cm_(NULL) {} + ChannelManagerTest() + : fake_call_(webrtc::Call::Config()), + fake_mc_(&fake_call_), + fme_(NULL), + fdm_(NULL), + fcm_(NULL), + cm_(NULL) {} virtual void SetUp() { fme_ = new cricket::FakeMediaEngine(); @@ -76,7 +82,8 @@ class ChannelManagerTest : public testing::Test { fcm_ = new cricket::FakeCaptureManager(); cm_ = new cricket::ChannelManager( fme_, fdme_, fdm_, fcm_, rtc::Thread::Current()); - session_ = new cricket::FakeSession(true); + transport_controller_ = + new cricket::FakeTransportController(ICEROLE_CONTROLLING); std::vector in_device_list, out_device_list, vid_device_list; in_device_list.push_back("audio-in1"); @@ -91,7 +98,7 @@ class ChannelManagerTest : public testing::Test { } virtual void TearDown() { - delete session_; + delete transport_controller_; delete cm_; cm_ = NULL; fdm_ = NULL; @@ -108,7 +115,7 @@ class ChannelManagerTest : public testing::Test { cricket::FakeDeviceManager* fdm_; cricket::FakeCaptureManager* fcm_; cricket::ChannelManager* cm_; - cricket::FakeSession* session_; + cricket::FakeTransportController* transport_controller_; }; // Test that we startup/shutdown properly. @@ -139,15 +146,16 @@ TEST_F(ChannelManagerTest, StartupShutdownOnThread) { // Test that we can create and destroy a voice and video channel. TEST_F(ChannelManagerTest, CreateDestroyChannels) { EXPECT_TRUE(cm_->Init()); - cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel( - &fake_mc_, session_, cricket::CN_AUDIO, false, AudioOptions()); + cricket::VoiceChannel* voice_channel = + cm_->CreateVoiceChannel(&fake_mc_, transport_controller_, + cricket::CN_AUDIO, false, AudioOptions()); EXPECT_TRUE(voice_channel != nullptr); - cricket::VideoChannel* video_channel = cm_->CreateVideoChannel( - &fake_mc_, session_, cricket::CN_VIDEO, false, VideoOptions()); + cricket::VideoChannel* video_channel = + cm_->CreateVideoChannel(&fake_mc_, transport_controller_, + cricket::CN_VIDEO, false, VideoOptions()); EXPECT_TRUE(video_channel != nullptr); - cricket::DataChannel* data_channel = - cm_->CreateDataChannel(session_, cricket::CN_DATA, - false, cricket::DCT_RTP); + cricket::DataChannel* data_channel = cm_->CreateDataChannel( + transport_controller_, cricket::CN_DATA, false, cricket::DCT_RTP); EXPECT_TRUE(data_channel != nullptr); cm_->DestroyVideoChannel(video_channel); cm_->DestroyVoiceChannel(voice_channel); @@ -160,17 +168,19 @@ TEST_F(ChannelManagerTest, CreateDestroyChannelsOnThread) { worker_.Start(); EXPECT_TRUE(cm_->set_worker_thread(&worker_)); EXPECT_TRUE(cm_->Init()); - delete session_; - session_ = new cricket::FakeSession(&worker_, true); - cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel( - &fake_mc_, session_, cricket::CN_AUDIO, false, AudioOptions()); + delete transport_controller_; + transport_controller_ = + new cricket::FakeTransportController(&worker_, ICEROLE_CONTROLLING); + cricket::VoiceChannel* voice_channel = + cm_->CreateVoiceChannel(&fake_mc_, transport_controller_, + cricket::CN_AUDIO, false, AudioOptions()); EXPECT_TRUE(voice_channel != nullptr); - cricket::VideoChannel* video_channel = cm_->CreateVideoChannel( - &fake_mc_, session_, cricket::CN_VIDEO, false, VideoOptions()); + cricket::VideoChannel* video_channel = + cm_->CreateVideoChannel(&fake_mc_, transport_controller_, + cricket::CN_VIDEO, false, VideoOptions()); EXPECT_TRUE(video_channel != nullptr); - cricket::DataChannel* data_channel = - cm_->CreateDataChannel(session_, cricket::CN_DATA, - false, cricket::DCT_RTP); + cricket::DataChannel* data_channel = cm_->CreateDataChannel( + transport_controller_, cricket::CN_DATA, false, cricket::DCT_RTP); EXPECT_TRUE(data_channel != nullptr); cm_->DestroyVideoChannel(video_channel); cm_->DestroyVoiceChannel(voice_channel); @@ -182,21 +192,22 @@ TEST_F(ChannelManagerTest, CreateDestroyChannelsOnThread) { // to create a cricket::TransportChannel TEST_F(ChannelManagerTest, NoTransportChannelTest) { EXPECT_TRUE(cm_->Init()); - session_->set_fail_channel_creation(true); + transport_controller_->set_fail_channel_creation(true); // The test is useless unless the session does not fail creating // cricket::TransportChannel. - ASSERT_TRUE(session_->CreateChannel( + ASSERT_TRUE(transport_controller_->CreateTransportChannel_w( "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP) == nullptr); - cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel( - &fake_mc_, session_, cricket::CN_AUDIO, false, AudioOptions()); + cricket::VoiceChannel* voice_channel = + cm_->CreateVoiceChannel(&fake_mc_, transport_controller_, + cricket::CN_AUDIO, false, AudioOptions()); EXPECT_TRUE(voice_channel == nullptr); - cricket::VideoChannel* video_channel = cm_->CreateVideoChannel( - &fake_mc_, session_, cricket::CN_VIDEO, false, VideoOptions()); + cricket::VideoChannel* video_channel = + cm_->CreateVideoChannel(&fake_mc_, transport_controller_, + cricket::CN_VIDEO, false, VideoOptions()); EXPECT_TRUE(video_channel == nullptr); - cricket::DataChannel* data_channel = - cm_->CreateDataChannel(session_, cricket::CN_DATA, - false, cricket::DCT_RTP); + cricket::DataChannel* data_channel = cm_->CreateDataChannel( + transport_controller_, cricket::CN_DATA, false, cricket::DCT_RTP); EXPECT_TRUE(data_channel == nullptr); cm_->Terminate(); } diff --git a/webrtc/base/fakenetwork.h b/webrtc/base/fakenetwork.h index 4b6bb68a96..065d08d4c1 100644 --- a/webrtc/base/fakenetwork.h +++ b/webrtc/base/fakenetwork.h @@ -29,12 +29,7 @@ const int kFakeIPv6NetworkPrefixLength = 64; class FakeNetworkManager : public NetworkManagerBase, public MessageHandler { public: - FakeNetworkManager() - : thread_(Thread::Current()), - next_index_(0), - started_(false), - sent_first_update_(false) { - } + FakeNetworkManager() : thread_(Thread::Current()) {} typedef std::vector IfaceList; @@ -58,20 +53,18 @@ class FakeNetworkManager : public NetworkManagerBase, } virtual void StartUpdating() { - if (started_) { - if (sent_first_update_) + ++start_count_; + if (start_count_ == 1) { + sent_first_update_ = false; + thread_->Post(this); + } else { + if (sent_first_update_) { SignalNetworksChanged(); - return; + } } - - started_ = true; - sent_first_update_ = false; - thread_->Post(this); } - virtual void StopUpdating() { - started_ = false; - } + virtual void StopUpdating() { --start_count_; } // MessageHandler interface. virtual void OnMessage(Message* msg) { @@ -82,7 +75,7 @@ class FakeNetworkManager : public NetworkManagerBase, private: void DoUpdateNetworks() { - if (!started_) + if (start_count_ == 0) return; std::vector networks; for (IfaceList::iterator it = ifaces_.begin(); @@ -111,9 +104,9 @@ class FakeNetworkManager : public NetworkManagerBase, Thread* thread_; IfaceList ifaces_; - int next_index_; - bool started_; - bool sent_first_update_; + int next_index_ = 0; + int start_count_ = 0; + bool sent_first_update_ = false; }; } // namespace rtc diff --git a/webrtc/p2p/base/dtlstransport.h b/webrtc/p2p/base/dtlstransport.h index 9559c1e6d2..c448eb16fc 100644 --- a/webrtc/p2p/base/dtlstransport.h +++ b/webrtc/p2p/base/dtlstransport.h @@ -11,7 +11,6 @@ #ifndef WEBRTC_P2P_BASE_DTLSTRANSPORT_H_ #define WEBRTC_P2P_BASE_DTLSTRANSPORT_H_ -#include "webrtc/base/checks.h" #include "webrtc/p2p/base/dtlstransportchannel.h" #include "webrtc/p2p/base/transport.h" @@ -23,33 +22,31 @@ namespace cricket { class PortAllocator; -// Base should be a descendant of cricket::Transport -// TODO(hbos): Add appropriate RTC_DCHECK thread checks to all methods. +// Base should be a descendant of cricket::Transport and have a constructor +// that takes a transport name and PortAllocator. +// +// Everything in this class should be called on the worker thread. template class DtlsTransport : public Base { public: - DtlsTransport(rtc::Thread* signaling_thread, - rtc::Thread* worker_thread, - const std::string& content_name, + DtlsTransport(const std::string& name, PortAllocator* allocator, const rtc::scoped_refptr& certificate) - : Base(signaling_thread, worker_thread, content_name, allocator), + : Base(name, allocator), certificate_(certificate), secure_role_(rtc::SSL_CLIENT), - ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_10) { - } + ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_10) {} ~DtlsTransport() { Base::DestroyAllChannels(); } - void SetCertificate_w( + + void SetLocalCertificate( const rtc::scoped_refptr& certificate) override { - RTC_DCHECK(Base::worker_thread()->IsCurrent()); certificate_ = certificate; } - bool GetCertificate_w( + bool GetLocalCertificate( rtc::scoped_refptr* certificate) override { - RTC_DCHECK(Base::worker_thread()->IsCurrent()); if (!certificate_) return false; @@ -57,15 +54,13 @@ class DtlsTransport : public Base { return true; } - bool SetSslMaxProtocolVersion_w(rtc::SSLProtocolVersion version) override { - RTC_DCHECK(Base::worker_thread()->IsCurrent()); + bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) override { ssl_max_version_ = version; return true; } - bool ApplyLocalTransportDescription_w(TransportChannelImpl* channel, - std::string* error_desc) override { - RTC_DCHECK(Base::worker_thread()->IsCurrent()); + bool ApplyLocalTransportDescription(TransportChannelImpl* channel, + std::string* error_desc) override { rtc::SSLFingerprint* local_fp = Base::local_description()->identity_fingerprint.get(); @@ -98,12 +93,11 @@ class DtlsTransport : public Base { } // Apply the description in the base class. - return Base::ApplyLocalTransportDescription_w(channel, error_desc); + return Base::ApplyLocalTransportDescription(channel, error_desc); } - bool NegotiateTransportDescription_w(ContentAction local_role, - std::string* error_desc) override { - RTC_DCHECK(Base::worker_thread()->IsCurrent()); + bool NegotiateTransportDescription(ContentAction local_role, + std::string* error_desc) override { if (!Base::local_description() || !Base::remote_description()) { const std::string msg = "Local and Remote description must be set before " "transport descriptions are negotiated"; @@ -200,7 +194,7 @@ class DtlsTransport : public Base { } // Now run the negotiation for the base class. - return Base::NegotiateTransportDescription_w(local_role, error_desc); + return Base::NegotiateTransportDescription(local_role, error_desc); } DtlsTransportChannelWrapper* CreateTransportChannel(int component) override { @@ -219,18 +213,15 @@ class DtlsTransport : public Base { Base::DestroyTransportChannel(base_channel); } - bool GetSslRole_w(rtc::SSLRole* ssl_role) const override { - RTC_DCHECK(Base::worker_thread()->IsCurrent()); + bool GetSslRole(rtc::SSLRole* ssl_role) const override { ASSERT(ssl_role != NULL); *ssl_role = secure_role_; return true; } private: - bool ApplyNegotiatedTransportDescription_w( - TransportChannelImpl* channel, - std::string* error_desc) override { - RTC_DCHECK(Base::worker_thread()->IsCurrent()); + bool ApplyNegotiatedTransportDescription(TransportChannelImpl* channel, + std::string* error_desc) override { // Set ssl role. Role must be set before fingerprint is applied, which // initiates DTLS setup. if (!channel->SetSslRole(secure_role_)) { @@ -245,7 +236,7 @@ class DtlsTransport : public Base { return BadTransportDescription("Failed to apply remote fingerprint.", error_desc); } - return Base::ApplyNegotiatedTransportDescription_w(channel, error_desc); + return Base::ApplyNegotiatedTransportDescription(channel, error_desc); } rtc::scoped_refptr certificate_; diff --git a/webrtc/p2p/base/dtlstransportchannel.cc b/webrtc/p2p/base/dtlstransportchannel.cc index dcebdee53e..b434d0860e 100644 --- a/webrtc/p2p/base/dtlstransportchannel.cc +++ b/webrtc/p2p/base/dtlstransportchannel.cc @@ -87,9 +87,9 @@ bool StreamInterfaceChannel::OnPacketReceived(const char* data, size_t size) { } DtlsTransportChannelWrapper::DtlsTransportChannelWrapper( - Transport* transport, - TransportChannelImpl* channel) - : TransportChannelImpl(channel->content_name(), channel->component()), + Transport* transport, + TransportChannelImpl* channel) + : TransportChannelImpl(channel->transport_name(), channel->component()), transport_(transport), worker_thread_(rtc::Thread::Current()), channel_(channel), @@ -103,12 +103,10 @@ DtlsTransportChannelWrapper::DtlsTransportChannelWrapper( &DtlsTransportChannelWrapper::OnReadPacket); channel_->SignalReadyToSend.connect(this, &DtlsTransportChannelWrapper::OnReadyToSend); - channel_->SignalRequestSignaling.connect(this, - &DtlsTransportChannelWrapper::OnRequestSignaling); - channel_->SignalCandidateReady.connect(this, - &DtlsTransportChannelWrapper::OnCandidateReady); - channel_->SignalCandidatesAllocationDone.connect(this, - &DtlsTransportChannelWrapper::OnCandidatesAllocationDone); + channel_->SignalGatheringState.connect( + this, &DtlsTransportChannelWrapper::OnGatheringState); + channel_->SignalCandidateGathered.connect( + this, &DtlsTransportChannelWrapper::OnCandidateGathered); channel_->SignalRoleConflict.connect(this, &DtlsTransportChannelWrapper::OnRoleConflict); channel_->SignalRouteChange.connect(this, @@ -211,7 +209,7 @@ bool DtlsTransportChannelWrapper::SetRemoteFingerprint( return true; } - // Allow SetRemoteFingerprint with a NULL digest even if SetLocalIdentity + // Allow SetRemoteFingerprint with a NULL digest even if SetLocalCertificate // hasn't been called. if (dtls_state_ > STATE_OFFERED || (dtls_state_ == STATE_NONE && !digest_alg.empty())) { @@ -591,22 +589,17 @@ bool DtlsTransportChannelWrapper::HandleDtlsPacket(const char* data, return downward_->OnPacketReceived(data, size); } -void DtlsTransportChannelWrapper::OnRequestSignaling( +void DtlsTransportChannelWrapper::OnGatheringState( TransportChannelImpl* channel) { ASSERT(channel == channel_); - SignalRequestSignaling(this); + SignalGatheringState(this); } -void DtlsTransportChannelWrapper::OnCandidateReady( - TransportChannelImpl* channel, const Candidate& c) { +void DtlsTransportChannelWrapper::OnCandidateGathered( + TransportChannelImpl* channel, + const Candidate& c) { ASSERT(channel == channel_); - SignalCandidateReady(this, c); -} - -void DtlsTransportChannelWrapper::OnCandidatesAllocationDone( - TransportChannelImpl* channel) { - ASSERT(channel == channel_); - SignalCandidatesAllocationDone(this); + SignalCandidateGathered(this, c); } void DtlsTransportChannelWrapper::OnRoleConflict( diff --git a/webrtc/p2p/base/dtlstransportchannel.h b/webrtc/p2p/base/dtlstransportchannel.h index ddedcbcaa8..ea6a579dfb 100644 --- a/webrtc/p2p/base/dtlstransportchannel.h +++ b/webrtc/p2p/base/dtlstransportchannel.h @@ -27,7 +27,7 @@ namespace cricket { // the bottom and a StreamInterface on the top. class StreamInterfaceChannel : public rtc::StreamInterface { public: - StreamInterfaceChannel(TransportChannel* channel); + explicit StreamInterfaceChannel(TransportChannel* channel); // Push in a packet; this gets pulled out from Read(). bool OnPacketReceived(const char* data, size_t size); @@ -35,10 +35,14 @@ class StreamInterfaceChannel : public rtc::StreamInterface { // Implementations of StreamInterface rtc::StreamState GetState() const override { return state_; } void Close() override { state_ = rtc::SS_CLOSED; } - rtc::StreamResult Read(void* buffer, size_t buffer_len, - size_t* read, int* error) override; - rtc::StreamResult Write(const void* data, size_t data_len, - size_t* written, int* error) override; + rtc::StreamResult Read(void* buffer, + size_t buffer_len, + size_t* read, + int* error) override; + rtc::StreamResult Write(const void* data, + size_t data_len, + size_t* written, + int* error) override; private: TransportChannel* channel_; // owned by DtlsTransportChannelWrapper @@ -93,12 +97,8 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl { TransportChannelImpl* channel); ~DtlsTransportChannelWrapper() override; - void SetIceRole(IceRole role) override { - channel_->SetIceRole(role); - } - IceRole GetIceRole() const override { - return channel_->GetIceRole(); - } + void SetIceRole(IceRole role) override { channel_->SetIceRole(role); } + IceRole GetIceRole() const override { return channel_->GetIceRole(); } bool SetLocalCertificate( const rtc::scoped_refptr& certificate) override; rtc::scoped_refptr GetLocalCertificate() const override; @@ -109,7 +109,8 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl { bool IsDtlsActive() const override { return dtls_state_ != STATE_NONE; } // Called to send a packet (via DTLS, if turned on). - int SendPacket(const char* data, size_t size, + int SendPacket(const char* data, + size_t size, const rtc::PacketOptions& options, int flags) override; @@ -120,15 +121,11 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl { bool GetOption(rtc::Socket::Option opt, int* value) override { return channel_->GetOption(opt, value); } - int GetError() override { - return channel_->GetError(); - } + int GetError() override { return channel_->GetError(); } bool GetStats(ConnectionInfos* infos) override { return channel_->GetStats(infos); } - const std::string SessionId() const override { - return channel_->SessionId(); - } + const std::string SessionId() const override { return channel_->SessionId(); } virtual bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version); @@ -168,9 +165,7 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl { } // TransportChannelImpl calls. - Transport* GetTransport() override { - return transport_; - } + Transport* GetTransport() override { return transport_; } TransportChannelState GetState() const override { return channel_->GetState(); @@ -192,11 +187,14 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl { void Connect() override; - void OnSignalingReady() override { - channel_->OnSignalingReady(); + void MaybeStartGathering() override { channel_->MaybeStartGathering(); } + + IceGatheringState gathering_state() const override { + return channel_->gathering_state(); } - void OnCandidate(const Candidate& candidate) override { - channel_->OnCandidate(candidate); + + void AddRemoteCandidate(const Candidate& candidate) override { + channel_->AddRemoteCandidate(candidate); } void SetReceivingTimeout(int receiving_timeout_ms) override { @@ -217,9 +215,8 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl { bool SetupDtls(); bool MaybeStartDtls(); bool HandleDtlsPacket(const char* data, size_t size); - void OnRequestSignaling(TransportChannelImpl* channel); - void OnCandidateReady(TransportChannelImpl* channel, const Candidate& c); - void OnCandidatesAllocationDone(TransportChannelImpl* channel); + void OnGatheringState(TransportChannelImpl* channel); + void OnCandidateGathered(TransportChannelImpl* channel, const Candidate& c); void OnRoleConflict(TransportChannelImpl* channel); void OnRouteChange(TransportChannel* channel, const Candidate& candidate); void OnConnectionRemoved(TransportChannelImpl* channel); diff --git a/webrtc/p2p/base/dtlstransportchannel_unittest.cc b/webrtc/p2p/base/dtlstransportchannel_unittest.cc index 10640f99e3..95696e222c 100644 --- a/webrtc/p2p/base/dtlstransportchannel_unittest.cc +++ b/webrtc/p2p/base/dtlstransportchannel_unittest.cc @@ -11,7 +11,7 @@ #include #include "webrtc/p2p/base/dtlstransport.h" -#include "webrtc/p2p/base/fakesession.h" +#include "webrtc/p2p/base/faketransportcontroller.h" #include "webrtc/base/common.h" #include "webrtc/base/dscp.h" #include "webrtc/base/gunit.h" @@ -21,7 +21,6 @@ #include "webrtc/base/sslidentity.h" #include "webrtc/base/sslstreamadapter.h" #include "webrtc/base/stringutils.h" -#include "webrtc/base/thread.h" #define MAYBE_SKIP_TEST(feature) \ if (!(rtc::SSLStreamAdapter::feature())) { \ @@ -45,19 +44,14 @@ enum Flags { NF_REOFFER = 0x1, NF_EXPECT_FAILURE = 0x2 }; class DtlsTestClient : public sigslot::has_slots<> { public: - DtlsTestClient(const std::string& name, - rtc::Thread* signaling_thread, - rtc::Thread* worker_thread) : - name_(name), - signaling_thread_(signaling_thread), - worker_thread_(worker_thread), - packet_size_(0), - use_dtls_srtp_(false), - ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_10), - negotiated_dtls_(false), - received_dtls_client_hello_(false), - received_dtls_server_hello_(false) { - } + DtlsTestClient(const std::string& name) + : name_(name), + packet_size_(0), + use_dtls_srtp_(false), + ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_10), + negotiated_dtls_(false), + received_dtls_client_hello_(false), + received_dtls_server_hello_(false) {} void CreateCertificate(rtc::KeyType key_type) { certificate_ = rtc::RTCCertificate::Create( rtc::scoped_ptr( @@ -71,13 +65,12 @@ class DtlsTestClient : public sigslot::has_slots<> { use_dtls_srtp_ = true; } void SetupMaxProtocolVersion(rtc::SSLProtocolVersion version) { - ASSERT(transport_.get() == NULL); + ASSERT(!transport_); ssl_max_version_ = version; } void SetupChannels(int count, cricket::IceRole role) { transport_.reset(new cricket::DtlsTransport( - signaling_thread_, worker_thread_, "dtls content name", nullptr, - certificate_)); + "dtls content name", nullptr, certificate_)); transport_->SetAsync(true); transport_->SetIceRole(role); transport_->SetIceTiebreaker( @@ -118,8 +111,8 @@ class DtlsTestClient : public sigslot::has_slots<> { void Negotiate(DtlsTestClient* peer, cricket::ContentAction action, ConnectionRole local_role, ConnectionRole remote_role, int flags) { - Negotiate(certificate_, certificate_ ? peer->certificate_ : nullptr, - action, local_role, remote_role, flags); + Negotiate(certificate_, certificate_ ? peer->certificate_ : nullptr, action, + local_role, remote_role, flags); } // Allow any DTLS configuration to be specified (including invalid ones). @@ -163,18 +156,18 @@ class DtlsTestClient : public sigslot::has_slots<> { } cricket::TransportDescription local_desc( - std::vector(), kIceUfrag1, kIcePwd1, - cricket::ICEMODE_FULL, local_role, - // If remote if the offerer and has no DTLS support, answer will be + std::vector(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL, + local_role, + // If remote if the offerer and has no DTLS support, answer will be // without any fingerprint. - (action == cricket::CA_ANSWER && !remote_cert) ? - NULL : local_fingerprint.get(), + (action == cricket::CA_ANSWER && !remote_cert) + ? nullptr + : local_fingerprint.get(), cricket::Candidates()); cricket::TransportDescription remote_desc( - std::vector(), kIceUfrag1, kIcePwd1, - cricket::ICEMODE_FULL, remote_role, remote_fingerprint.get(), - cricket::Candidates()); + std::vector(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL, + remote_role, remote_fingerprint.get(), cricket::Candidates()); bool expect_success = (flags & NF_EXPECT_FAILURE) ? false : true; // If |expect_success| is false, expect SRTD or SLTD to fail when @@ -199,7 +192,9 @@ class DtlsTestClient : public sigslot::has_slots<> { return true; } - bool writable() const { return transport_->writable(); } + bool all_channels_writable() const { + return transport_->all_channels_writable(); + } void CheckRole(rtc::SSLRole role) { if (role == rtc::SSL_CLIENT) { @@ -337,8 +332,8 @@ class DtlsTestClient : public sigslot::has_slots<> { ASSERT_TRUE(VerifyPacket(data, size, &packet_num)); received_.insert(packet_num); // Only DTLS-SRTP packets should have the bypass flag set. - int expected_flags = (certificate_ && IsRtpLeadByte(data[0])) ? - cricket::PF_SRTP_BYPASS : 0; + int expected_flags = + (certificate_ && IsRtpLeadByte(data[0])) ? cricket::PF_SRTP_BYPASS : 0; ASSERT_EQ(expected_flags, flags); } @@ -372,8 +367,6 @@ class DtlsTestClient : public sigslot::has_slots<> { private: std::string name_; - rtc::Thread* signaling_thread_; - rtc::Thread* worker_thread_; rtc::scoped_refptr certificate_; rtc::scoped_ptr transport_; std::vector channels_; @@ -389,16 +382,13 @@ class DtlsTestClient : public sigslot::has_slots<> { class DtlsTransportChannelTest : public testing::Test { public: - DtlsTransportChannelTest() : - client1_("P1", rtc::Thread::Current(), - rtc::Thread::Current()), - client2_("P2", rtc::Thread::Current(), - rtc::Thread::Current()), - channel_ct_(1), - use_dtls_(false), - use_dtls_srtp_(false), - ssl_expected_version_(rtc::SSL_PROTOCOL_DTLS_10) { - } + DtlsTransportChannelTest() + : client1_("P1"), + client2_("P2"), + channel_ct_(1), + use_dtls_(false), + use_dtls_srtp_(false), + ssl_expected_version_(rtc::SSL_PROTOCOL_DTLS_10) {} void SetChannelCount(size_t channel_ct) { channel_ct_ = static_cast(channel_ct); @@ -440,8 +430,10 @@ class DtlsTransportChannelTest : public testing::Test { if (!rv) return false; - EXPECT_TRUE_WAIT(client1_.writable() && client2_.writable(), 10000); - if (!client1_.writable() || !client2_.writable()) + EXPECT_TRUE_WAIT( + client1_.all_channels_writable() && client2_.all_channels_writable(), + 10000); + if (!client1_.all_channels_writable() || !client2_.all_channels_writable()) return false; // Check that we used the right roles. @@ -818,7 +810,9 @@ TEST_F(DtlsTransportChannelTest, TestRenegotiateBeforeConnect) { cricket::CONNECTIONROLE_ACTIVE, NF_REOFFER); bool rv = client1_.Connect(&client2_); EXPECT_TRUE(rv); - EXPECT_TRUE_WAIT(client1_.writable() && client2_.writable(), 10000); + EXPECT_TRUE_WAIT( + client1_.all_channels_writable() && client2_.all_channels_writable(), + 10000); TestTransfer(0, 1000, 100, true); TestTransfer(1, 1000, 100, true); @@ -837,8 +831,8 @@ TEST_F(DtlsTransportChannelTest, TestCertificatesBeforeConnect) { // After negotiation, each side has a distinct local certificate, but still no // remote certificate, because connection has not yet occurred. - ASSERT_TRUE(client1_.transport()->GetCertificate(&certificate1)); - ASSERT_TRUE(client2_.transport()->GetCertificate(&certificate2)); + ASSERT_TRUE(client1_.transport()->GetLocalCertificate(&certificate1)); + ASSERT_TRUE(client2_.transport()->GetLocalCertificate(&certificate2)); ASSERT_NE(certificate1->ssl_certificate().ToPEMString(), certificate2->ssl_certificate().ToPEMString()); ASSERT_FALSE( @@ -861,8 +855,8 @@ TEST_F(DtlsTransportChannelTest, TestCertificatesAfterConnect) { rtc::scoped_ptr remote_cert2; // After connection, each side has a distinct local certificate. - ASSERT_TRUE(client1_.transport()->GetCertificate(&certificate1)); - ASSERT_TRUE(client2_.transport()->GetCertificate(&certificate2)); + ASSERT_TRUE(client1_.transport()->GetLocalCertificate(&certificate1)); + ASSERT_TRUE(client2_.transport()->GetLocalCertificate(&certificate2)); ASSERT_NE(certificate1->ssl_certificate().ToPEMString(), certificate2->ssl_certificate().ToPEMString()); diff --git a/webrtc/p2p/base/fakesession.h b/webrtc/p2p/base/faketransportcontroller.h similarity index 53% rename from webrtc/p2p/base/fakesession.h rename to webrtc/p2p/base/faketransportcontroller.h index bd3c089025..e356e814b7 100644 --- a/webrtc/p2p/base/fakesession.h +++ b/webrtc/p2p/base/faketransportcontroller.h @@ -8,30 +8,31 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_P2P_BASE_FAKESESSION_H_ -#define WEBRTC_P2P_BASE_FAKESESSION_H_ +#ifndef WEBRTC_P2P_BASE_FAKETRANSPORTCONTROLLER_H_ +#define WEBRTC_P2P_BASE_FAKETRANSPORTCONTROLLER_H_ #include #include #include -#include "webrtc/p2p/base/session.h" #include "webrtc/p2p/base/transport.h" #include "webrtc/p2p/base/transportchannel.h" +#include "webrtc/p2p/base/transportcontroller.h" #include "webrtc/p2p/base/transportchannelimpl.h" +#include "webrtc/base/bind.h" #include "webrtc/base/buffer.h" #include "webrtc/base/fakesslidentity.h" #include "webrtc/base/messagequeue.h" #include "webrtc/base/sigslot.h" #include "webrtc/base/sslfingerprint.h" +#include "webrtc/base/thread.h" namespace cricket { class FakeTransport; struct PacketMessageData : public rtc::MessageData { - PacketMessageData(const char* data, size_t len) : packet(data, len) { - } + PacketMessageData(const char* data, size_t len) : packet(data, len) {} rtc::Buffer packet; }; @@ -43,24 +44,12 @@ class FakeTransportChannel : public TransportChannelImpl, public rtc::MessageHandler { public: explicit FakeTransportChannel(Transport* transport, - const std::string& content_name, + const std::string& name, int component) - : TransportChannelImpl(content_name, component), + : TransportChannelImpl(name, component), transport_(transport), - dest_(nullptr), - state_(STATE_INIT), - async_(false), - do_dtls_(false), - role_(ICEROLE_UNKNOWN), - tiebreaker_(0), - remote_ice_mode_(ICEMODE_FULL), - dtls_fingerprint_("", nullptr, 0), - ssl_role_(rtc::SSL_CLIENT), - connection_count_(0) { - } - ~FakeTransportChannel() { - Reset(); - } + dtls_fingerprint_("", nullptr, 0) {} + ~FakeTransportChannel() { Reset(); } uint64 IceTiebreaker() const { return tiebreaker_; } IceMode remote_ice_mode() const { return remote_ice_mode_; } @@ -72,24 +61,23 @@ class FakeTransportChannel : public TransportChannelImpl, return dtls_fingerprint_; } - void SetAsync(bool async) { - async_ = async; - } + // If async, will send packets by "Post"-ing to message queue instead of + // synchronously "Send"-ing. + void SetAsync(bool async) { async_ = async; } - Transport* GetTransport() override { - return transport_; - } + Transport* GetTransport() override { return transport_; } TransportChannelState GetState() const override { if (connection_count_ == 0) { - return TransportChannelState::STATE_FAILED; + return had_connection_ ? TransportChannelState::STATE_FAILED + : TransportChannelState::STATE_INIT; } if (connection_count_ == 1) { return TransportChannelState::STATE_COMPLETED; } - return TransportChannelState::STATE_FAILED; + return TransportChannelState::STATE_CONNECTING; } void SetIceRole(IceRole role) override { role_ = role; } @@ -109,7 +97,8 @@ class FakeTransportChannel : public TransportChannelImpl, } void SetRemoteIceMode(IceMode mode) override { remote_ice_mode_ = mode; } - bool SetRemoteFingerprint(const std::string& alg, const uint8* digest, + bool SetRemoteFingerprint(const std::string& alg, + const uint8* digest, size_t digest_len) override { dtls_fingerprint_ = rtc::SSLFingerprint(alg, digest, digest_len); return true; @@ -128,27 +117,37 @@ class FakeTransportChannel : public TransportChannelImpl, state_ = STATE_CONNECTING; } } - virtual void Reset() { + + void MaybeStartGathering() override { + if (gathering_state_ == kIceGatheringNew) { + gathering_state_ = kIceGatheringGathering; + SignalGatheringState(this); + } + } + + IceGatheringState gathering_state() const override { + return gathering_state_; + } + + void Reset() { if (state_ != STATE_INIT) { state_ = STATE_INIT; if (dest_) { dest_->state_ = STATE_INIT; - dest_->dest_ = NULL; - dest_ = NULL; + dest_->dest_ = nullptr; + dest_ = nullptr; } } } - void SetWritable(bool writable) { - set_writable(writable); - } + void SetWritable(bool writable) { set_writable(writable); } void SetDestination(FakeTransportChannel* dest) { if (state_ == STATE_CONNECTING && dest) { // This simulates the delivery of candidates. dest_ = dest; dest_->dest_ = this; - if (certificate_ && dest_->certificate_) { + if (local_cert_ && dest_->local_cert_) { do_dtls_ = true; dest_->do_dtls_ = true; NegotiateSrtpCiphers(); @@ -159,7 +158,7 @@ class FakeTransportChannel : public TransportChannelImpl, dest_->set_writable(true); } else if (state_ == STATE_CONNECTED && !dest) { // Simulates loss of connectivity, by asymmetrically forgetting dest_. - dest_ = NULL; + dest_ = nullptr; state_ = STATE_CONNECTING; set_writable(false); } @@ -168,18 +167,31 @@ class FakeTransportChannel : public TransportChannelImpl, void SetConnectionCount(size_t connection_count) { size_t old_connection_count = connection_count_; connection_count_ = connection_count; + if (connection_count) + had_connection_ = true; if (connection_count_ < old_connection_count) SignalConnectionRemoved(this); } - void SetReceiving(bool receiving) { - set_receiving(receiving); + void SetCandidatesGatheringComplete() { + if (gathering_state_ != kIceGatheringComplete) { + gathering_state_ = kIceGatheringComplete; + SignalGatheringState(this); + } } - void SetReceivingTimeout(int timeout) override {} + void SetReceiving(bool receiving) { set_receiving(receiving); } - int SendPacket(const char* data, size_t len, - const rtc::PacketOptions& options, int flags) override { + void SetReceivingTimeout(int timeout) override { + receiving_timeout_ = timeout; + } + + int receiving_timeout() const { return receiving_timeout_; } + + int SendPacket(const char* data, + size_t len, + const rtc::PacketOptions& options, + int flags) override { if (state_ != STATE_CONNECTED) { return -1; } @@ -196,32 +208,25 @@ class FakeTransportChannel : public TransportChannelImpl, } return static_cast(len); } - int SetOption(rtc::Socket::Option opt, int value) override { - return true; - } - bool GetOption(rtc::Socket::Option opt, int* value) override { - return true; - } - int GetError() override { - return 0; - } + int SetOption(rtc::Socket::Option opt, int value) override { return true; } + bool GetOption(rtc::Socket::Option opt, int* value) override { return true; } + int GetError() override { return 0; } - void OnSignalingReady() override { - } - void OnCandidate(const Candidate& candidate) override { + void AddRemoteCandidate(const Candidate& candidate) override { + remote_candidates_.push_back(candidate); } + const Candidates& remote_candidates() const { return remote_candidates_; } void OnMessage(rtc::Message* msg) override { - PacketMessageData* data = static_cast( - msg->pdata); + PacketMessageData* data = static_cast(msg->pdata); dest_->SignalReadPacket(dest_, data->packet.data(), data->packet.size(), rtc::CreatePacketTime(0), 0); delete data; } bool SetLocalCertificate( - const rtc::scoped_refptr& certificate) override { - certificate_ = certificate; + const rtc::scoped_refptr& certificate) { + local_cert_ = certificate; return true; } @@ -229,9 +234,7 @@ class FakeTransportChannel : public TransportChannelImpl, remote_cert_ = cert; } - bool IsDtlsActive() const override { - return do_dtls_; - } + bool IsDtlsActive() const override { return do_dtls_; } bool SetSrtpCiphers(const std::vector& ciphers) override { srtp_ciphers_ = ciphers; @@ -246,13 +249,10 @@ class FakeTransportChannel : public TransportChannelImpl, return false; } - bool GetSslCipher(std::string* cipher) override { - return false; - } + bool GetSslCipher(std::string* cipher) override { return false; } - rtc::scoped_refptr - GetLocalCertificate() const override { - return certificate_; + rtc::scoped_refptr GetLocalCertificate() const { + return local_cert_; } bool GetRemoteSSLCertificate(rtc::SSLCertificate** cert) const override { @@ -277,12 +277,12 @@ class FakeTransportChannel : public TransportChannelImpl, return false; } - virtual void NegotiateSrtpCiphers() { + void NegotiateSrtpCiphers() { for (std::vector::const_iterator it1 = srtp_ciphers_.begin(); - it1 != srtp_ciphers_.end(); ++it1) { + it1 != srtp_ciphers_.end(); ++it1) { for (std::vector::const_iterator it2 = - dest_->srtp_ciphers_.begin(); - it2 != dest_->srtp_ciphers_.end(); ++it2) { + dest_->srtp_ciphers_.begin(); + it2 != dest_->srtp_ciphers_.end(); ++it2) { if (*it1 == *it2) { chosen_srtp_cipher_ = *it1; dest_->chosen_srtp_cipher_ = *it2; @@ -299,27 +299,39 @@ class FakeTransportChannel : public TransportChannelImpl, return true; } + void set_ssl_max_protocol_version(rtc::SSLProtocolVersion version) { + ssl_max_version_ = version; + } + rtc::SSLProtocolVersion ssl_max_protocol_version() const { + return ssl_max_version_; + } + private: enum State { STATE_INIT, STATE_CONNECTING, STATE_CONNECTED }; Transport* transport_; - FakeTransportChannel* dest_; - State state_; - bool async_; - rtc::scoped_refptr certificate_; - rtc::FakeSSLCertificate* remote_cert_; - bool do_dtls_; + FakeTransportChannel* dest_ = nullptr; + State state_ = STATE_INIT; + bool async_ = false; + Candidates remote_candidates_; + rtc::scoped_refptr local_cert_; + rtc::FakeSSLCertificate* remote_cert_ = nullptr; + bool do_dtls_ = false; std::vector srtp_ciphers_; std::string chosen_srtp_cipher_; - IceRole role_; - uint64 tiebreaker_; + int receiving_timeout_ = -1; + IceRole role_ = ICEROLE_UNKNOWN; + uint64 tiebreaker_ = 0; std::string ice_ufrag_; std::string ice_pwd_; std::string remote_ice_ufrag_; std::string remote_ice_pwd_; - IceMode remote_ice_mode_; + IceMode remote_ice_mode_ = ICEMODE_FULL; + rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_10; rtc::SSLFingerprint dtls_fingerprint_; - rtc::SSLRole ssl_role_; - size_t connection_count_; + rtc::SSLRole ssl_role_ = rtc::SSL_CLIENT; + size_t connection_count_ = 0; + IceGatheringState gathering_state_ = kIceGatheringNew; + bool had_connection_ = false; }; // Fake transport class, which can be passed to anything that needs a Transport. @@ -328,67 +340,40 @@ class FakeTransportChannel : public TransportChannelImpl, class FakeTransport : public Transport { public: typedef std::map ChannelMap; - FakeTransport(rtc::Thread* signaling_thread, - rtc::Thread* worker_thread, - const std::string& content_name, - PortAllocator* alllocator = nullptr) - : Transport(signaling_thread, worker_thread, - content_name, nullptr), - dest_(nullptr), - async_(false) { - } - ~FakeTransport() { - DestroyAllChannels(); - } + + explicit FakeTransport(const std::string& name) : Transport(name, nullptr) {} + + // Note that we only have a constructor with the allocator parameter so it can + // be wrapped by a DtlsTransport. + FakeTransport(const std::string& name, PortAllocator* allocator) + : Transport(name, nullptr) {} + + ~FakeTransport() { DestroyAllChannels(); } const ChannelMap& channels() const { return channels_; } + // If async, will send packets by "Post"-ing to message queue instead of + // synchronously "Send"-ing. void SetAsync(bool async) { async_ = async; } void SetDestination(FakeTransport* dest) { dest_ = dest; - for (ChannelMap::iterator it = channels_.begin(); it != channels_.end(); - ++it) { - it->second->SetLocalCertificate(certificate_); - SetChannelDestination(it->first, it->second); + for (const auto& kv : channels_) { + kv.second->SetLocalCertificate(certificate_); + SetChannelDestination(kv.first, kv.second); } } void SetWritable(bool writable) { - for (ChannelMap::iterator it = channels_.begin(); it != channels_.end(); - ++it) { - it->second->SetWritable(writable); + for (const auto& kv : channels_) { + kv.second->SetWritable(writable); } } - void set_certificate( - const rtc::scoped_refptr& certificate) { - certificate_ = certificate; - } - - using Transport::local_description; - using Transport::remote_description; - - protected: - TransportChannelImpl* CreateTransportChannel(int component) override { - if (channels_.find(component) != channels_.end()) { - return NULL; - } - FakeTransportChannel* channel = - new FakeTransportChannel(this, content_name(), component); - channel->SetAsync(async_); - SetChannelDestination(component, channel); - channels_[component] = channel; - return channel; - } - void DestroyTransportChannel(TransportChannelImpl* channel) override { - channels_.erase(channel->component()); - delete channel; - } - void SetCertificate_w( + void SetLocalCertificate( const rtc::scoped_refptr& certificate) override { certificate_ = certificate; } - bool GetCertificate_w( + bool GetLocalCertificate( rtc::scoped_refptr* certificate) override { if (!certificate_) return false; @@ -397,103 +382,149 @@ class FakeTransport : public Transport { return true; } + bool GetSslRole(rtc::SSLRole* role) const override { + if (channels_.empty()) { + return false; + } + return channels_.begin()->second->GetSslRole(role); + } + + bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) override { + ssl_max_version_ = version; + for (const auto& kv : channels_) { + kv.second->set_ssl_max_protocol_version(ssl_max_version_); + } + return true; + } + rtc::SSLProtocolVersion ssl_max_protocol_version() const { + return ssl_max_version_; + } + + using Transport::local_description; + using Transport::remote_description; + + protected: + TransportChannelImpl* CreateTransportChannel(int component) override { + if (channels_.find(component) != channels_.end()) { + return nullptr; + } + FakeTransportChannel* channel = + new FakeTransportChannel(this, name(), component); + channel->set_ssl_max_protocol_version(ssl_max_version_); + channel->SetAsync(async_); + SetChannelDestination(component, channel); + channels_[component] = channel; + return channel; + } + + void DestroyTransportChannel(TransportChannelImpl* channel) override { + channels_.erase(channel->component()); + delete channel; + } + private: FakeTransportChannel* GetFakeChannel(int component) { - ChannelMap::iterator it = channels_.find(component); - return (it != channels_.end()) ? it->second : NULL; + auto it = channels_.find(component); + return (it != channels_.end()) ? it->second : nullptr; } - void SetChannelDestination(int component, - FakeTransportChannel* channel) { - FakeTransportChannel* dest_channel = NULL; + + void SetChannelDestination(int component, FakeTransportChannel* channel) { + FakeTransportChannel* dest_channel = nullptr; if (dest_) { dest_channel = dest_->GetFakeChannel(component); - if (dest_channel) + if (dest_channel) { dest_channel->SetLocalCertificate(dest_->certificate_); + } } channel->SetDestination(dest_channel); } // Note, this is distinct from the Channel map owned by Transport. // This map just tracks the FakeTransportChannels created by this class. + // It's mainly needed so that we can access a FakeTransportChannel directly, + // even if wrapped by a DtlsTransportChannelWrapper. ChannelMap channels_; - FakeTransport* dest_; - bool async_; + FakeTransport* dest_ = nullptr; + bool async_ = false; rtc::scoped_refptr certificate_; + rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_10; }; -// Fake session class, which can be passed into a BaseChannel object for -// test purposes. Can be connected to other FakeSessions via Connect(). -class FakeSession : public BaseSession { +// Fake TransportController class, which can be passed into a BaseChannel object +// for test purposes. Can be connected to other FakeTransportControllers via +// Connect(). +// +// This fake is unusual in that for the most part, it's implemented with the +// real TransportController code, but with fake TransportChannels underneath. +class FakeTransportController : public TransportController { public: - explicit FakeSession() - : BaseSession(rtc::Thread::Current(), - rtc::Thread::Current(), - NULL, "", "", true), - fail_create_channel_(false) { - } - explicit FakeSession(bool initiator) - : BaseSession(rtc::Thread::Current(), - rtc::Thread::Current(), - NULL, "", "", initiator), - fail_create_channel_(false) { - } - FakeSession(rtc::Thread* worker_thread, bool initiator) - : BaseSession(rtc::Thread::Current(), - worker_thread, - NULL, "", "", initiator), - fail_create_channel_(false) { + FakeTransportController() + : TransportController(rtc::Thread::Current(), + rtc::Thread::Current(), + nullptr), + fail_create_channel_(false) {} + + explicit FakeTransportController(IceRole role) + : TransportController(rtc::Thread::Current(), + rtc::Thread::Current(), + nullptr), + fail_create_channel_(false) { + SetIceRole(role); } - FakeTransport* GetTransport(const std::string& content_name) { + explicit FakeTransportController(rtc::Thread* worker_thread) + : TransportController(rtc::Thread::Current(), worker_thread, nullptr), + fail_create_channel_(false) {} + + FakeTransportController(rtc::Thread* worker_thread, IceRole role) + : TransportController(rtc::Thread::Current(), worker_thread, nullptr), + fail_create_channel_(false) { + SetIceRole(role); + } + + FakeTransport* GetTransport_w(const std::string& transport_name) { return static_cast( - BaseSession::GetTransport(content_name)); + TransportController::GetTransport_w(transport_name)); } - void Connect(FakeSession* dest) { - // Simulate the exchange of candidates. - CompleteNegotiation(); - dest->CompleteNegotiation(); - for (TransportMap::const_iterator it = transport_proxies().begin(); - it != transport_proxies().end(); ++it) { - static_cast(it->second->impl())->SetDestination( - dest->GetTransport(it->first)); - } + void Connect(FakeTransportController* dest) { + worker_thread()->Invoke( + rtc::Bind(&FakeTransportController::Connect_w, this, dest)); } - TransportChannel* CreateChannel(const std::string& content_name, - int component) override { + TransportChannel* CreateTransportChannel_w(const std::string& transport_name, + int component) override { if (fail_create_channel_) { - return NULL; + return nullptr; } - return BaseSession::CreateChannel(content_name, component); + return TransportController::CreateTransportChannel_w(transport_name, + component); } void set_fail_channel_creation(bool fail_channel_creation) { fail_create_channel_ = fail_channel_creation; } - // TODO: Hoist this into Session when we re-work the Session code. - void set_ssl_rtccertificate( - const rtc::scoped_refptr& certificate) { - for (TransportMap::const_iterator it = transport_proxies().begin(); - it != transport_proxies().end(); ++it) { - // We know that we have a FakeTransport* + protected: + Transport* CreateTransport_w(const std::string& transport_name) override { + return new FakeTransport(transport_name); + } - static_cast(it->second->impl())->set_certificate - (certificate); + void Connect_w(FakeTransportController* dest) { + // Simulate the exchange of candidates. + ConnectChannels_w(); + dest->ConnectChannels_w(); + for (auto& kv : transports()) { + FakeTransport* transport = static_cast(kv.second); + transport->SetDestination(dest->GetTransport_w(kv.first)); } } - protected: - Transport* CreateTransport(const std::string& content_name) override { - return new FakeTransport(signaling_thread(), worker_thread(), content_name); - } - - void CompleteNegotiation() { - for (TransportMap::const_iterator it = transport_proxies().begin(); - it != transport_proxies().end(); ++it) { - it->second->CompleteNegotiation(); - it->second->ConnectChannels(); + void ConnectChannels_w() { + for (auto& kv : transports()) { + FakeTransport* transport = static_cast(kv.second); + transport->ConnectChannels(); + transport->MaybeStartGathering(); } } @@ -503,4 +534,4 @@ class FakeSession : public BaseSession { } // namespace cricket -#endif // WEBRTC_P2P_BASE_FAKESESSION_H_ +#endif // WEBRTC_P2P_BASE_FAKETRANSPORTCONTROLLER_H_ diff --git a/webrtc/p2p/base/p2ptransport.cc b/webrtc/p2p/base/p2ptransport.cc index b919fde31a..abc4c14504 100644 --- a/webrtc/p2p/base/p2ptransport.cc +++ b/webrtc/p2p/base/p2ptransport.cc @@ -20,21 +20,15 @@ namespace cricket { -P2PTransport::P2PTransport(rtc::Thread* signaling_thread, - rtc::Thread* worker_thread, - const std::string& content_name, - PortAllocator* allocator) - : Transport(signaling_thread, worker_thread, - content_name, allocator) { -} +P2PTransport::P2PTransport(const std::string& name, PortAllocator* allocator) + : Transport(name, allocator) {} P2PTransport::~P2PTransport() { DestroyAllChannels(); } TransportChannelImpl* P2PTransport::CreateTransportChannel(int component) { - return new P2PTransportChannel(content_name(), component, this, - port_allocator()); + return new P2PTransportChannel(name(), component, this, port_allocator()); } void P2PTransport::DestroyTransportChannel(TransportChannelImpl* channel) { diff --git a/webrtc/p2p/base/p2ptransport.h b/webrtc/p2p/base/p2ptransport.h index 2e27bd8a3b..0f965b4cdc 100644 --- a/webrtc/p2p/base/p2ptransport.h +++ b/webrtc/p2p/base/p2ptransport.h @@ -16,12 +16,10 @@ namespace cricket { +// Everything in this class should be called on the worker thread. class P2PTransport : public Transport { public: - P2PTransport(rtc::Thread* signaling_thread, - rtc::Thread* worker_thread, - const std::string& content_name, - PortAllocator* allocator); + P2PTransport(const std::string& name, PortAllocator* allocator); virtual ~P2PTransport(); protected: diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc index ee9906053c..e7f5c941c4 100644 --- a/webrtc/p2p/base/p2ptransportchannel.cc +++ b/webrtc/p2p/base/p2ptransportchannel.cc @@ -11,6 +11,7 @@ #include "webrtc/p2p/base/p2ptransportchannel.h" #include +#include #include "webrtc/p2p/base/common.h" #include "webrtc/p2p/base/relayport.h" // For RELAY_PORT_TYPE. #include "webrtc/p2p/base/stunport.h" // For STUN_PORT_TYPE. @@ -169,28 +170,27 @@ bool ShouldSwitch(cricket::Connection* a_conn, cricket::Connection* b_conn) { namespace cricket { -P2PTransportChannel::P2PTransportChannel(const std::string& content_name, +P2PTransportChannel::P2PTransportChannel(const std::string& transport_name, int component, P2PTransport* transport, - PortAllocator *allocator) : - TransportChannelImpl(content_name, component), - transport_(transport), - allocator_(allocator), - worker_thread_(rtc::Thread::Current()), - incoming_only_(false), - waiting_for_signaling_(false), - error_(0), - best_connection_(NULL), - pending_best_connection_(NULL), - sort_dirty_(false), - was_writable_(false), - remote_ice_mode_(ICEMODE_FULL), - ice_role_(ICEROLE_UNKNOWN), - tiebreaker_(0), - remote_candidate_generation_(0), - check_receiving_delay_(MIN_CHECK_RECEIVING_DELAY * 5), - receiving_timeout_(MIN_CHECK_RECEIVING_DELAY * 50) { -} + PortAllocator* allocator) + : TransportChannelImpl(transport_name, component), + transport_(transport), + allocator_(allocator), + worker_thread_(rtc::Thread::Current()), + incoming_only_(false), + error_(0), + best_connection_(NULL), + pending_best_connection_(NULL), + sort_dirty_(false), + was_writable_(false), + remote_ice_mode_(ICEMODE_FULL), + ice_role_(ICEROLE_UNKNOWN), + tiebreaker_(0), + remote_candidate_generation_(0), + gathering_state_(kIceGatheringNew), + check_receiving_delay_(MIN_CHECK_RECEIVING_DELAY * 5), + receiving_timeout_(MIN_CHECK_RECEIVING_DELAY * 50) {} P2PTransportChannel::~P2PTransportChannel() { ASSERT(worker_thread_ == rtc::Thread::Current()); @@ -231,6 +231,7 @@ void P2PTransportChannel::AddConnection(Connection* connection) { connection->SignalDestroyed.connect( this, &P2PTransportChannel::OnConnectionDestroyed); connection->SignalNominated.connect(this, &P2PTransportChannel::OnNominated); + had_connection_ = true; } void P2PTransportChannel::SetIceRole(IceRole ice_role) { @@ -265,8 +266,9 @@ void P2PTransportChannel::SetIceTiebreaker(uint64 tiebreaker) { TransportChannelState P2PTransportChannel::GetState() const { std::set networks; - if (connections_.size() == 0) { - return TransportChannelState::STATE_FAILED; + if (connections_.empty()) { + return had_connection_ ? TransportChannelState::STATE_FAILED + : TransportChannelState::STATE_INIT; } for (uint32 i = 0; i < connections_.size(); ++i) { @@ -288,21 +290,10 @@ TransportChannelState P2PTransportChannel::GetState() const { void P2PTransportChannel::SetIceCredentials(const std::string& ice_ufrag, const std::string& ice_pwd) { ASSERT(worker_thread_ == rtc::Thread::Current()); - bool ice_restart = false; - if (!ice_ufrag_.empty() && !ice_pwd_.empty()) { - // Restart candidate allocation if there is any change in either - // ice ufrag or password. - ice_restart = - IceCredentialsChanged(ice_ufrag_, ice_pwd_, ice_ufrag, ice_pwd); - } - ice_ufrag_ = ice_ufrag; ice_pwd_ = ice_pwd; - - if (ice_restart) { - // Restart candidate gathering. - Allocate(); - } + // Note: Candidate gathering will restart when MaybeStartGathering is next + // called. } void P2PTransportChannel::SetRemoteIceCredentials(const std::string& ice_ufrag, @@ -359,9 +350,6 @@ void P2PTransportChannel::Connect() { return; } - // Kick off an allocator session - Allocate(); - // Start pinging as the ports come in. thread()->Post(this, MSG_PING); @@ -369,6 +357,22 @@ void P2PTransportChannel::Connect() { check_receiving_delay_, this, MSG_CHECK_RECEIVING); } +void P2PTransportChannel::MaybeStartGathering() { + // Start gathering if we never started before, or if an ICE restart occurred. + if (allocator_sessions_.empty() || + IceCredentialsChanged(allocator_sessions_.back()->ice_ufrag(), + allocator_sessions_.back()->ice_pwd(), ice_ufrag_, + ice_pwd_)) { + if (gathering_state_ != kIceGatheringGathering) { + gathering_state_ = kIceGatheringGathering; + SignalGatheringState(this); + } + // Time for a new allocator + AddAllocatorSession(allocator_->CreateSession( + SessionId(), transport_name(), component(), ice_ufrag_, ice_pwd_)); + } +} + // A new port is available, attempt to make connections for it void P2PTransportChannel::OnPortReady(PortAllocatorSession *session, PortInterface* port) { @@ -413,17 +417,21 @@ void P2PTransportChannel::OnPortReady(PortAllocatorSession *session, // A new candidate is available, let listeners know void P2PTransportChannel::OnCandidatesReady( - PortAllocatorSession *session, const std::vector& candidates) { + PortAllocatorSession* session, + const std::vector& candidates) { ASSERT(worker_thread_ == rtc::Thread::Current()); for (size_t i = 0; i < candidates.size(); ++i) { - SignalCandidateReady(this, candidates[i]); + SignalCandidateGathered(this, candidates[i]); } } void P2PTransportChannel::OnCandidatesAllocationDone( PortAllocatorSession* session) { ASSERT(worker_thread_ == rtc::Thread::Current()); - SignalCandidatesAllocationDone(this); + gathering_state_ = kIceGatheringComplete; + LOG(LS_INFO) << "P2PTransportChannel: " << transport_name() << ", component " + << component() << " gathering complete"; + SignalGatheringState(this); } // Handle stun packets @@ -494,8 +502,7 @@ void P2PTransportChannel::OnUnknownAddress( LOG(LS_WARNING) << "P2PTransportChannel::OnUnknownAddress - " << "No STUN_ATTR_PRIORITY found in the " << "stun request message"; - port->SendBindingErrorResponse(stun_msg, address, - STUN_ERROR_BAD_REQUEST, + port->SendBindingErrorResponse(stun_msg, address, STUN_ERROR_BAD_REQUEST, STUN_ERROR_REASON_BAD_REQUEST); return; } @@ -545,8 +552,7 @@ void P2PTransportChannel::OnUnknownAddress( remote_candidate, cricket::PortInterface::ORIGIN_THIS_PORT); if (!connection) { ASSERT(false); - port->SendBindingErrorResponse(stun_msg, address, - STUN_ERROR_SERVER_ERROR, + port->SendBindingErrorResponse(stun_msg, address, STUN_ERROR_SERVER_ERROR, STUN_ERROR_REASON_SERVER_ERROR); return; } @@ -575,16 +581,6 @@ void P2PTransportChannel::OnRoleConflict(PortInterface* port) { // from Transport. } -// When the signalling channel is ready, we can really kick off the allocator -void P2PTransportChannel::OnSignalingReady() { - ASSERT(worker_thread_ == rtc::Thread::Current()); - if (waiting_for_signaling_) { - waiting_for_signaling_ = false; - AddAllocatorSession(allocator_->CreateSession( - SessionId(), content_name(), component(), ice_ufrag_, ice_pwd_)); - } -} - void P2PTransportChannel::OnNominated(Connection* conn) { ASSERT(worker_thread_ == rtc::Thread::Current()); ASSERT(ice_role_ == ICEROLE_CONTROLLED); @@ -606,7 +602,7 @@ void P2PTransportChannel::OnNominated(Connection* conn) { } } -void P2PTransportChannel::OnCandidate(const Candidate& candidate) { +void P2PTransportChannel::AddRemoteCandidate(const Candidate& candidate) { ASSERT(worker_thread_ == rtc::Thread::Current()); uint32 generation = candidate.generation(); @@ -847,7 +843,7 @@ bool P2PTransportChannel::GetStats(ConnectionInfos *infos) { std::vector::const_iterator it; for (it = connections_.begin(); it != connections_.end(); ++it) { - Connection *connection = *it; + Connection* connection = *it; ConnectionInfo info; info.best_connection = (best_connection_ == connection); info.receiving = connection->receiving(); @@ -881,14 +877,6 @@ rtc::DiffServCodePoint P2PTransportChannel::DefaultDscpValue() const { return static_cast (it->second); } -// Begin allocate (or immediately re-allocate, if MSG_ALLOCATE pending) -void P2PTransportChannel::Allocate() { - // Time for a new allocator, lets make sure we have a signalling channel - // to communicate candidates through first. - waiting_for_signaling_ = true; - SignalRequestSignaling(this); -} - // Monitor connection states. void P2PTransportChannel::UpdateConnectionStates() { uint32 now = rtc::Time(); @@ -1228,8 +1216,7 @@ Connection* P2PTransportChannel::FindNextPingableConnection() { void P2PTransportChannel::PingConnection(Connection* conn) { bool use_candidate = false; if (remote_ice_mode_ == ICEMODE_FULL && ice_role_ == ICEROLE_CONTROLLING) { - use_candidate = (conn == best_connection_) || - (best_connection_ == NULL) || + use_candidate = (conn == best_connection_) || (best_connection_ == NULL) || (!best_connection_->writable()) || (conn->priority() > best_connection_->priority()); } else if (remote_ice_mode_ == ICEMODE_LITE && conn == best_connection_) { @@ -1311,9 +1298,10 @@ void P2PTransportChannel::OnPortDestroyed(PortInterface* port) { } // We data is available, let listeners know -void P2PTransportChannel::OnReadPacket( - Connection *connection, const char *data, size_t len, - const rtc::PacketTime& packet_time) { +void P2PTransportChannel::OnReadPacket(Connection* connection, + const char* data, + size_t len, + const rtc::PacketTime& packet_time) { ASSERT(worker_thread_ == rtc::Thread::Current()); // Do not deliver, if packet doesn't belong to the correct transport channel. diff --git a/webrtc/p2p/base/p2ptransportchannel.h b/webrtc/p2p/base/p2ptransportchannel.h index a8f16666c9..92969c8b7e 100644 --- a/webrtc/p2p/base/p2ptransportchannel.h +++ b/webrtc/p2p/base/p2ptransportchannel.h @@ -51,11 +51,11 @@ class RemoteCandidate : public Candidate { class P2PTransportChannel : public TransportChannelImpl, public rtc::MessageHandler { public: - P2PTransportChannel(const std::string& content_name, + P2PTransportChannel(const std::string& transport_name, int component, P2PTransport* transport, - PortAllocator *allocator); - ~P2PTransportChannel() override; + PortAllocator* allocator); + virtual ~P2PTransportChannel(); // From TransportChannelImpl: Transport* GetTransport() override { return transport_; } @@ -69,15 +69,20 @@ class P2PTransportChannel : public TransportChannelImpl, const std::string& ice_pwd) override; void SetRemoteIceMode(IceMode mode) override; void Connect() override; - void OnSignalingReady() override; - void OnCandidate(const Candidate& candidate) override; + void MaybeStartGathering() override; + IceGatheringState gathering_state() const override { + return gathering_state_; + } + void AddRemoteCandidate(const Candidate& candidate) override; // Sets the receiving timeout in milliseconds. // This also sets the check_receiving_delay proportionally. void SetReceivingTimeout(int receiving_timeout_ms) override; // From TransportChannel: - int SendPacket(const char *data, size_t len, - const rtc::PacketOptions& options, int flags) override; + int SendPacket(const char* data, + size_t len, + const rtc::PacketOptions& options, + int flags) override; int SetOption(rtc::Socket::Option opt, int value) override; bool GetOption(rtc::Socket::Option opt, int* value) override; int GetError() override { return error_; } @@ -96,13 +101,9 @@ class P2PTransportChannel : public TransportChannelImpl, bool IsDtlsActive() const override { return false; } // Default implementation. - bool GetSslRole(rtc::SSLRole* role) const override { - return false; - } + bool GetSslRole(rtc::SSLRole* role) const override { return false; } - bool SetSslRole(rtc::SSLRole role) override { - return false; - } + bool SetSslRole(rtc::SSLRole role) override { return false; } // Set up the ciphers to use for DTLS-SRTP. bool SetSrtpCiphers(const std::vector& ciphers) override { @@ -110,14 +111,10 @@ class P2PTransportChannel : public TransportChannelImpl, } // Find out which DTLS-SRTP cipher was negotiated. - bool GetSrtpCipher(std::string* cipher) override { - return false; - } + bool GetSrtpCipher(std::string* cipher) override { return false; } // Find out which DTLS cipher was negotiated. - bool GetSslCipher(std::string* cipher) override { - return false; - } + bool GetSslCipher(std::string* cipher) override { return false; } // Returns null because the channel is not encrypted by default. rtc::scoped_refptr GetLocalCertificate() const override { @@ -165,7 +162,6 @@ class P2PTransportChannel : public TransportChannelImpl, return allocator_sessions_.back(); } - void Allocate(); void UpdateConnectionStates(); void RequestSort(); void SortConnections(); @@ -213,7 +209,7 @@ class P2PTransportChannel : public TransportChannelImpl, void OnNominated(Connection* conn); - void OnMessage(rtc::Message *pmsg) override; + void OnMessage(rtc::Message* pmsg) override; void OnSort(); void OnPing(); @@ -223,10 +219,9 @@ class P2PTransportChannel : public TransportChannelImpl, Connection* best_nominated_connection() const; P2PTransport* transport_; - PortAllocator *allocator_; - rtc::Thread *worker_thread_; + PortAllocator* allocator_; + rtc::Thread* worker_thread_; bool incoming_only_; - bool waiting_for_signaling_; int error_; std::vector allocator_sessions_; std::vector ports_; @@ -238,6 +233,7 @@ class P2PTransportChannel : public TransportChannelImpl, std::vector remote_candidates_; bool sort_dirty_; // indicates whether another sort is needed right now bool was_writable_; + bool had_connection_ = false; // if connections_ has ever been nonempty typedef std::map OptionMap; OptionMap options_; std::string ice_ufrag_; @@ -248,6 +244,7 @@ class P2PTransportChannel : public TransportChannelImpl, IceRole ice_role_; uint64 tiebreaker_; uint32 remote_candidate_generation_; + IceGatheringState gathering_state_; int check_receiving_delay_; int receiving_timeout_; diff --git a/webrtc/p2p/base/p2ptransportchannel_unittest.cc b/webrtc/p2p/base/p2ptransportchannel_unittest.cc index d0277f4814..485449fa88 100644 --- a/webrtc/p2p/base/p2ptransportchannel_unittest.cc +++ b/webrtc/p2p/base/p2ptransportchannel_unittest.cc @@ -297,10 +297,8 @@ class P2PTransportChannelTestBase : public testing::Test, const std::string& remote_ice_pwd) { cricket::P2PTransportChannel* channel = new cricket::P2PTransportChannel( "test content name", component, NULL, GetAllocator(endpoint)); - channel->SignalRequestSignaling.connect( - this, &P2PTransportChannelTestBase::OnChannelRequestSignaling); - channel->SignalCandidateReady.connect(this, - &P2PTransportChannelTestBase::OnCandidate); + channel->SignalCandidateGathered.connect( + this, &P2PTransportChannelTestBase::OnCandidate); channel->SignalReadPacket.connect( this, &P2PTransportChannelTestBase::OnReadPacket); channel->SignalRoleConflict.connect( @@ -314,6 +312,7 @@ class P2PTransportChannelTestBase : public testing::Test, channel->SetIceRole(GetEndpoint(endpoint)->ice_role()); channel->SetIceTiebreaker(GetEndpoint(endpoint)->GetIceTiebreaker()); channel->Connect(); + channel->MaybeStartGathering(); return channel; } void DestroyChannels() { @@ -389,10 +388,9 @@ class P2PTransportChannelTestBase : public testing::Test, } bool IsLocalToPrflxOrTheReverse(const Result& expected) { - return ((expected.local_type == "local" && - expected.remote_type == "prflx") || - (expected.local_type == "prflx" && - expected.remote_type == "local")); + return ( + (expected.local_type == "local" && expected.remote_type == "prflx") || + (expected.local_type == "prflx" && expected.remote_type == "local")); } // Return true if the approprite parts of the expected Result, based @@ -512,8 +510,8 @@ class P2PTransportChannelTestBase : public testing::Test, ep2_ch1()->best_connection()) { int32 converge_start = rtc::Time(), converge_time; int converge_wait = 2000; - EXPECT_TRUE_WAIT_MARGIN(CheckCandidate1(expected), - converge_wait, converge_wait); + EXPECT_TRUE_WAIT_MARGIN(CheckCandidate1(expected), converge_wait, + converge_wait); // Also do EXPECT_EQ on each part so that failures are more verbose. ExpectCandidate1(expected); @@ -562,7 +560,7 @@ class P2PTransportChannelTestBase : public testing::Test, } // This test waits for the transport to become receiving and writable on both - // end points. Once they are, the end points set new local ice credentials to + // end points. Once they are, the end points set new local ice credentials and // restart the ice gathering. Finally it waits for the transport to select a // new connection using the newly generated ice candidates. // Before calling this function the end points must be configured. @@ -582,8 +580,10 @@ class P2PTransportChannelTestBase : public testing::Test, ep1_ch1()->SetIceCredentials(kIceUfrag[2], kIcePwd[2]); ep1_ch1()->SetRemoteIceCredentials(kIceUfrag[3], kIcePwd[3]); + ep1_ch1()->MaybeStartGathering(); ep2_ch1()->SetIceCredentials(kIceUfrag[3], kIcePwd[3]); ep2_ch1()->SetRemoteIceCredentials(kIceUfrag[2], kIcePwd[2]); + ep2_ch1()->MaybeStartGathering(); EXPECT_TRUE_WAIT_MARGIN(LocalCandidate(ep1_ch1())->generation() != old_local_candidate1->generation(), @@ -626,9 +626,6 @@ class P2PTransportChannelTestBase : public testing::Test, TestSendRecv(1); } - void OnChannelRequestSignaling(cricket::TransportChannelImpl* channel) { - channel->OnSignalingReady(); - } // We pass the candidates directly to the other side. void OnCandidate(cricket::TransportChannelImpl* ch, const cricket::Candidate& c) { @@ -669,7 +666,7 @@ class P2PTransportChannelTestBase : public testing::Test, } LOG(LS_INFO) << "Candidate(" << data->channel->component() << "->" << rch->component() << "): " << c.ToString(); - rch->OnCandidate(c); + rch->AddRemoteCandidate(c); break; } } @@ -804,8 +801,10 @@ class P2PTransportChannelTest : public P2PTransportChannelTestBase { static const Result* kMatrixSharedUfrag[NUM_CONFIGS][NUM_CONFIGS]; static const Result* kMatrixSharedSocketAsGice[NUM_CONFIGS][NUM_CONFIGS]; static const Result* kMatrixSharedSocketAsIce[NUM_CONFIGS][NUM_CONFIGS]; - void ConfigureEndpoints(Config config1, Config config2, - int allocator_flags1, int allocator_flags2) { + void ConfigureEndpoints(Config config1, + Config config2, + int allocator_flags1, + int allocator_flags2) { ServerAddresses stun_servers; stun_servers.insert(kStunAddr); GetEndpoint(0)->allocator_.reset( @@ -821,8 +820,8 @@ class P2PTransportChannelTest : public P2PTransportChannelTestBase { cricket::RelayServerConfig relay_server(cricket::RELAY_TURN); relay_server.credentials = kRelayCredentials; - relay_server.ports.push_back(cricket::ProtocolAddress( - kTurnUdpIntAddr, cricket::PROTO_UDP, false)); + relay_server.ports.push_back( + cricket::ProtocolAddress(kTurnUdpIntAddr, cricket::PROTO_UDP, false)); GetEndpoint(0)->allocator_->AddRelay(relay_server); GetEndpoint(1)->allocator_->AddRelay(relay_server); @@ -1026,15 +1025,14 @@ const P2PTransportChannelTest::Result* // The actual tests that exercise all the various configurations. // Test names are of the form P2PTransportChannelTest_TestOPENToNAT_FULL_CONE -#define P2P_TEST_DECLARATION(x, y, z) \ - TEST_F(P2PTransportChannelTest, z##Test##x##To##y) { \ - ConfigureEndpoints(x, y, \ - PORTALLOCATOR_ENABLE_SHARED_SOCKET, \ - PORTALLOCATOR_ENABLE_SHARED_SOCKET); \ - if (kMatrixSharedSocketAsIce[x][y] != NULL) \ - Test(*kMatrixSharedSocketAsIce[x][y]); \ - else \ - LOG(LS_WARNING) << "Not yet implemented"; \ +#define P2P_TEST_DECLARATION(x, y, z) \ + TEST_F(P2PTransportChannelTest, z##Test##x##To##y) { \ + ConfigureEndpoints(x, y, PORTALLOCATOR_ENABLE_SHARED_SOCKET, \ + PORTALLOCATOR_ENABLE_SHARED_SOCKET); \ + if (kMatrixSharedSocketAsIce[x][y] != NULL) \ + Test(*kMatrixSharedSocketAsIce[x][y]); \ + else \ + LOG(LS_WARNING) << "Not yet implemented"; \ } #define P2P_TEST(x, y) \ @@ -1089,8 +1087,7 @@ P2P_TEST_SET(PROXY_SOCKS) // Test that we restart candidate allocation when local ufrag&pwd changed. // Standard Ice protocol is used. TEST_F(P2PTransportChannelTest, HandleUfragPwdChange) { - ConfigureEndpoints(OPEN, OPEN, - kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); CreateChannels(1); TestHandleIceUfragPasswordChanged(); @@ -1099,8 +1096,7 @@ TEST_F(P2PTransportChannelTest, HandleUfragPwdChange) { // Test the operation of GetStats. TEST_F(P2PTransportChannelTest, GetStats) { - ConfigureEndpoints(OPEN, OPEN, - kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); CreateChannels(1); EXPECT_TRUE_WAIT_MARGIN(ep1_ch1()->receiving() && ep1_ch1()->writable() && @@ -1126,8 +1122,7 @@ TEST_F(P2PTransportChannelTest, GetStats) { // Test that we properly create a connection on a STUN ping from unknown address // when the signaling is slow. TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignaling) { - ConfigureEndpoints(OPEN, OPEN, - kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); // Emulate no remote credentials coming in. set_clear_remote_candidates_ufrag_pwd(false); @@ -1171,8 +1166,7 @@ TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignaling) { // Test that we properly create a connection on a STUN ping from unknown address // when the signaling is slow and the end points are behind NAT. TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignalingWithNAT) { - ConfigureEndpoints(OPEN, NAT_SYMMETRIC, - kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, NAT_SYMMETRIC, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); // Emulate no remote credentials coming in. set_clear_remote_candidates_ufrag_pwd(false); @@ -1214,8 +1208,7 @@ TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignalingWithNAT) { // Test that if remote candidates don't have ufrag and pwd, we still work. TEST_F(P2PTransportChannelTest, RemoteCandidatesWithoutUfragPwd) { set_clear_remote_candidates_ufrag_pwd(true); - ConfigureEndpoints(OPEN, OPEN, - kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); CreateChannels(1); const cricket::Connection* best_connection = NULL; @@ -1230,8 +1223,7 @@ TEST_F(P2PTransportChannelTest, RemoteCandidatesWithoutUfragPwd) { // Test that a host behind NAT cannot be reached when incoming_only // is set to true. TEST_F(P2PTransportChannelTest, IncomingOnlyBlocked) { - ConfigureEndpoints(NAT_FULL_CONE, OPEN, - kDefaultPortAllocatorFlags, + ConfigureEndpoints(NAT_FULL_CONE, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); SetAllocatorFlags(0, kOnlyLocalPorts); @@ -1252,8 +1244,7 @@ TEST_F(P2PTransportChannelTest, IncomingOnlyBlocked) { // Test that a peer behind NAT can connect to a peer that has // incoming_only flag set. TEST_F(P2PTransportChannelTest, IncomingOnlyOpen) { - ConfigureEndpoints(OPEN, NAT_FULL_CONE, - kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, NAT_FULL_CONE, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); SetAllocatorFlags(0, kOnlyLocalPorts); @@ -1414,11 +1405,10 @@ TEST_F(P2PTransportChannelTest, TestIPv6Connections) { // Testing forceful TURN connections. TEST_F(P2PTransportChannelTest, TestForceTurn) { - ConfigureEndpoints(NAT_PORT_RESTRICTED, NAT_SYMMETRIC, - kDefaultPortAllocatorFlags | - cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET, - kDefaultPortAllocatorFlags | - cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET); + ConfigureEndpoints( + NAT_PORT_RESTRICTED, NAT_SYMMETRIC, + kDefaultPortAllocatorFlags | cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET, + kDefaultPortAllocatorFlags | cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET); set_force_relay(true); SetAllocationStepDelay(0, kMinimumStepDelay); @@ -1473,8 +1463,7 @@ class P2PTransportChannelSameNatTest : public P2PTransportChannelTestBase { TEST_F(P2PTransportChannelSameNatTest, TestConesBehindSameCone) { ConfigureEndpoints(NAT_FULL_CONE, NAT_FULL_CONE, NAT_FULL_CONE); Test(P2PTransportChannelTestBase::Result( - "prflx", "udp", "stun", "udp", - "stun", "udp", "prflx", "udp", 1000)); + "prflx", "udp", "stun", "udp", "stun", "udp", "prflx", "udp", 1000)); } // Test what happens when we have multiple available pathways. @@ -1560,11 +1549,12 @@ TEST_F(P2PTransportChannelMultihomedTest, TestDrain) { // Remove the public interface, add the alternate interface, and allocate - // a new generation of candidates for the new interface (via Connect()). + // a new generation of candidates for the new interface (via + // MaybeStartGathering()). LOG(LS_INFO) << "Draining..."; AddAddress(1, kAlternateAddrs[1]); RemoveAddress(1, kPublicAddrs[1]); - ep2_ch1()->Connect(); + ep2_ch1()->MaybeStartGathering(); // We should switch over to use the alternate address after // an exchange of pings. @@ -1591,17 +1581,11 @@ class P2PTransportChannelPingTest : public testing::Test, protected: void PrepareChannel(cricket::P2PTransportChannel* ch) { - ch->SignalRequestSignaling.connect( - this, &P2PTransportChannelPingTest::OnChannelRequestSignaling); ch->SetIceRole(cricket::ICEROLE_CONTROLLING); ch->SetIceCredentials(kIceUfrag[0], kIcePwd[0]); ch->SetRemoteIceCredentials(kIceUfrag[1], kIcePwd[1]); } - void OnChannelRequestSignaling(cricket::TransportChannelImpl* channel) { - channel->OnSignalingReady(); - } - cricket::Candidate CreateCandidate(const std::string& ip, int port, int priority) { @@ -1648,8 +1632,9 @@ TEST_F(P2PTransportChannelPingTest, TestTriggeredChecks) { cricket::P2PTransportChannel ch("trigger checks", 1, nullptr, &pa); PrepareChannel(&ch); ch.Connect(); - ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1)); - ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 2)); + ch.MaybeStartGathering(); + ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 1)); + ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 2)); cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1); cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2); @@ -1672,8 +1657,9 @@ TEST_F(P2PTransportChannelPingTest, TestNoTriggeredChecksWhenWritable) { cricket::P2PTransportChannel ch("trigger checks", 1, nullptr, &pa); PrepareChannel(&ch); ch.Connect(); - ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1)); - ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 2)); + ch.MaybeStartGathering(); + ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 1)); + ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 2)); cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1); cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2); @@ -1696,16 +1682,17 @@ TEST_F(P2PTransportChannelPingTest, ConnectionResurrection) { cricket::P2PTransportChannel ch("connection resurrection", 1, nullptr, &pa); PrepareChannel(&ch); ch.Connect(); + ch.MaybeStartGathering(); // Create conn1 and keep track of original candidate priority. - ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1)); + ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 1)); cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1); ASSERT_TRUE(conn1 != nullptr); uint32 remote_priority = conn1->remote_candidate().priority(); // Create a higher priority candidate and make the connection // receiving/writable. This will prune conn1. - ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 2)); + ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 2)); cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2); ASSERT_TRUE(conn2 != nullptr); conn2->ReceivedPing(); @@ -1752,7 +1739,8 @@ TEST_F(P2PTransportChannelPingTest, TestReceivingStateChange) { EXPECT_EQ(500, ch.receiving_timeout()); EXPECT_EQ(50, ch.check_receiving_delay()); ch.Connect(); - ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1)); + ch.MaybeStartGathering(); + ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 1)); cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1); ASSERT_TRUE(conn1 != nullptr); @@ -1773,14 +1761,15 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBeforeNomination) { PrepareChannel(&ch); ch.SetIceRole(cricket::ICEROLE_CONTROLLED); ch.Connect(); - ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1)); + ch.MaybeStartGathering(); + ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 1)); cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1); ASSERT_TRUE(conn1 != nullptr); EXPECT_EQ(conn1, ch.best_connection()); // When a higher priority candidate comes in, the new connection is chosen // as the best connection. - ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 10)); + ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 10)); cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2); ASSERT_TRUE(conn1 != nullptr); EXPECT_EQ(conn2, ch.best_connection()); @@ -1788,7 +1777,7 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBeforeNomination) { // If a stun request with use-candidate attribute arrives, the receiving // connection will be set as the best connection, even though // its priority is lower. - ch.OnCandidate(CreateCandidate("3.3.3.3", 3, 1)); + ch.AddRemoteCandidate(CreateCandidate("3.3.3.3", 3, 1)); cricket::Connection* conn3 = WaitForConnectionTo(&ch, "3.3.3.3", 3); ASSERT_TRUE(conn3 != nullptr); // Because it has a lower priority, the best connection is still conn2. @@ -1803,7 +1792,7 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBeforeNomination) { // Even if another higher priority candidate arrives, // it will not be set as the best connection because the best connection // is nominated by the controlling side. - ch.OnCandidate(CreateCandidate("4.4.4.4", 4, 100)); + ch.AddRemoteCandidate(CreateCandidate("4.4.4.4", 4, 100)); cricket::Connection* conn4 = WaitForConnectionTo(&ch, "4.4.4.4", 4); ASSERT_TRUE(conn4 != nullptr); EXPECT_EQ(conn3, ch.best_connection()); @@ -1828,6 +1817,7 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionFromUnknownAddress) { PrepareChannel(&ch); ch.SetIceRole(cricket::ICEROLE_CONTROLLED); ch.Connect(); + ch.MaybeStartGathering(); // A minimal STUN message with prflx priority. cricket::IceMessage request; request.SetType(cricket::STUN_BINDING_REQUEST); @@ -1846,7 +1836,7 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionFromUnknownAddress) { EXPECT_EQ(conn1, ch.best_connection()); // Another connection is nominated via use_candidate. - ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 1)); + ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 1)); cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2); ASSERT_TRUE(conn2 != nullptr); // Because it has a lower priority, the best connection is still conn1. @@ -1892,7 +1882,8 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBasedOnMediaReceived) { PrepareChannel(&ch); ch.SetIceRole(cricket::ICEROLE_CONTROLLED); ch.Connect(); - ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 10)); + ch.MaybeStartGathering(); + ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 10)); cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1); ASSERT_TRUE(conn1 != nullptr); EXPECT_EQ(conn1, ch.best_connection()); @@ -1900,7 +1891,7 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBasedOnMediaReceived) { // If a data packet is received on conn2, the best connection should // switch to conn2 because the controlled side must mirror the media path // chosen by the controlling side. - ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 1)); + ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 1)); cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2); ASSERT_TRUE(conn2 != nullptr); conn2->ReceivedPing(); // Start receiving. diff --git a/webrtc/p2p/base/portallocator.cc b/webrtc/p2p/base/portallocator.cc index 76455b506e..b97ad554d6 100644 --- a/webrtc/p2p/base/portallocator.cc +++ b/webrtc/p2p/base/portallocator.cc @@ -8,6 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include "webrtc/base/checks.h" #include "webrtc/p2p/base/portallocator.h" namespace cricket { @@ -21,8 +22,10 @@ PortAllocatorSession::PortAllocatorSession(const std::string& content_name, component_(component), flags_(flags), generation_(0), - username_(ice_ufrag), - password_(ice_pwd) { + ice_ufrag_(ice_ufrag), + ice_pwd_(ice_pwd) { + RTC_DCHECK(!ice_ufrag.empty()); + RTC_DCHECK(!ice_pwd.empty()); } PortAllocatorSession* PortAllocator::CreateSession( diff --git a/webrtc/p2p/base/portallocator.h b/webrtc/p2p/base/portallocator.h index b754376d89..6e3efa8c41 100644 --- a/webrtc/p2p/base/portallocator.h +++ b/webrtc/p2p/base/portallocator.h @@ -74,11 +74,10 @@ enum { class PortAllocatorSession : public sigslot::has_slots<> { public: // Content name passed in mostly for logging and debugging. - // TODO(mallinath) - Change username and password to ice_ufrag and ice_pwd. PortAllocatorSession(const std::string& content_name, int component, - const std::string& username, - const std::string& password, + const std::string& ice_ufrag, + const std::string& ice_pwd, uint32 flags); // Subclasses should clean up any ports created. @@ -103,9 +102,14 @@ class PortAllocatorSession : public sigslot::has_slots<> { virtual void set_generation(uint32 generation) { generation_ = generation; } sigslot::signal1 SignalDestroyed; + const std::string& ice_ufrag() const { return ice_ufrag_; } + const std::string& ice_pwd() const { return ice_pwd_; } + protected: - const std::string& username() const { return username_; } - const std::string& password() const { return password_; } + // TODO(deadbeef): Get rid of these when everyone switches to ice_ufrag and + // ice_pwd. + const std::string& username() const { return ice_ufrag_; } + const std::string& password() const { return ice_pwd_; } std::string content_name_; int component_; @@ -113,8 +117,8 @@ class PortAllocatorSession : public sigslot::has_slots<> { private: uint32 flags_; uint32 generation_; - std::string username_; - std::string password_; + std::string ice_ufrag_; + std::string ice_pwd_; }; class PortAllocator : public sigslot::has_slots<> { diff --git a/webrtc/p2p/base/session.cc b/webrtc/p2p/base/session.cc index 23680b9be8..83edb63ea5 100644 --- a/webrtc/p2p/base/session.cc +++ b/webrtc/p2p/base/session.cc @@ -10,11 +10,6 @@ #include "webrtc/p2p/base/session.h" -#include "webrtc/p2p/base/dtlstransport.h" -#include "webrtc/p2p/base/p2ptransport.h" -#include "webrtc/p2p/base/transport.h" -#include "webrtc/p2p/base/transportchannelproxy.h" -#include "webrtc/p2p/base/transportinfo.h" #include "webrtc/base/bind.h" #include "webrtc/base/common.h" #include "webrtc/base/helpers.h" @@ -22,266 +17,15 @@ #include "webrtc/base/scoped_ptr.h" #include "webrtc/base/stringencode.h" #include "webrtc/base/sslstreamadapter.h" - +#include "webrtc/p2p/base/transport.h" +#include "webrtc/p2p/base/transportinfo.h" +#include "webrtc/p2p/base/transportcontroller.h" #include "webrtc/p2p/base/constants.h" namespace cricket { using rtc::Bind; -TransportProxy::~TransportProxy() { - for (ChannelMap::iterator iter = channels_.begin(); - iter != channels_.end(); ++iter) { - iter->second->SignalDestroyed(iter->second); - delete iter->second; - } -} - -TransportChannel* TransportProxy::GetChannel(int component) { - ASSERT(rtc::Thread::Current() == worker_thread_); - return GetChannelProxy(component); -} - -TransportChannel* TransportProxy::CreateChannel(int component) { - ASSERT(rtc::Thread::Current() == worker_thread_); - ASSERT(GetChannel(component) == NULL); - ASSERT(!transport_->get()->HasChannel(component)); - - // We always create a proxy in case we need to change out the transport later. - TransportChannelProxy* channel_proxy = - new TransportChannelProxy(content_name(), component); - channels_[component] = channel_proxy; - - // If we're already negotiated, create an impl and hook it up to the proxy - // channel. If we're connecting, create an impl but don't hook it up yet. - if (negotiated_) { - CreateChannelImpl_w(component); - SetChannelImplFromTransport_w(channel_proxy, component); - } else if (connecting_) { - CreateChannelImpl_w(component); - } - return channel_proxy; -} - -bool TransportProxy::HasChannel(int component) { - return transport_->get()->HasChannel(component); -} - -void TransportProxy::DestroyChannel(int component) { - ASSERT(rtc::Thread::Current() == worker_thread_); - TransportChannelProxy* channel_proxy = GetChannelProxy(component); - if (channel_proxy) { - // If the state of TransportProxy is not NEGOTIATED then - // TransportChannelProxy and its impl are not connected. Both must - // be connected before deletion. - // - // However, if we haven't entered the connecting state then there - // is no implementation to hook up. - if (connecting_ && !negotiated_) { - SetChannelImplFromTransport_w(channel_proxy, component); - } - - channels_.erase(component); - channel_proxy->SignalDestroyed(channel_proxy); - delete channel_proxy; - } -} - -void TransportProxy::ConnectChannels() { - if (!connecting_) { - if (!negotiated_) { - for (auto& iter : channels_) { - CreateChannelImpl(iter.first); - } - } - connecting_ = true; - } - // TODO(juberti): Right now Transport::ConnectChannels doesn't work if we - // don't have any channels yet, so we need to allow this method to be called - // multiple times. Once we fix Transport, we can move this call inside the - // if (!connecting_) block. - transport_->get()->ConnectChannels(); -} - -void TransportProxy::CompleteNegotiation() { - if (!negotiated_) { - // Negotiating assumes connecting_ has happened and - // implementations exist. If not we need to create the - // implementations. - for (auto& iter : channels_) { - if (!connecting_) { - CreateChannelImpl(iter.first); - } - SetChannelImplFromTransport(iter.second, iter.first); - } - negotiated_ = true; - } -} - -void TransportProxy::AddSentCandidates(const Candidates& candidates) { - for (Candidates::const_iterator cand = candidates.begin(); - cand != candidates.end(); ++cand) { - sent_candidates_.push_back(*cand); - } -} - -void TransportProxy::AddUnsentCandidates(const Candidates& candidates) { - for (Candidates::const_iterator cand = candidates.begin(); - cand != candidates.end(); ++cand) { - unsent_candidates_.push_back(*cand); - } -} - -TransportChannelProxy* TransportProxy::GetChannelProxy(int component) const { - ChannelMap::const_iterator iter = channels_.find(component); - return (iter != channels_.end()) ? iter->second : NULL; -} - -void TransportProxy::CreateChannelImpl(int component) { - worker_thread_->Invoke(Bind( - &TransportProxy::CreateChannelImpl_w, this, component)); -} - -void TransportProxy::CreateChannelImpl_w(int component) { - ASSERT(rtc::Thread::Current() == worker_thread_); - transport_->get()->CreateChannel(component); -} - -void TransportProxy::SetChannelImplFromTransport(TransportChannelProxy* proxy, - int component) { - worker_thread_->Invoke(Bind( - &TransportProxy::SetChannelImplFromTransport_w, this, proxy, component)); -} - -void TransportProxy::SetChannelImplFromTransport_w(TransportChannelProxy* proxy, - int component) { - ASSERT(rtc::Thread::Current() == worker_thread_); - TransportChannelImpl* impl = transport_->get()->GetChannel(component); - ASSERT(impl != NULL); - ReplaceChannelImpl_w(proxy, impl); -} - -void TransportProxy::ReplaceChannelImpl(TransportChannelProxy* proxy, - TransportChannelImpl* impl) { - worker_thread_->Invoke(Bind( - &TransportProxy::ReplaceChannelImpl_w, this, proxy, impl)); -} - -void TransportProxy::ReplaceChannelImpl_w(TransportChannelProxy* proxy, - TransportChannelImpl* impl) { - ASSERT(rtc::Thread::Current() == worker_thread_); - ASSERT(proxy != NULL); - proxy->SetImplementation(impl); -} - -// This function muxes |this| onto |target| by repointing |this| at -// |target|'s transport and setting our TransportChannelProxies -// to point to |target|'s underlying implementations. -bool TransportProxy::SetupMux(TransportProxy* target) { - // Bail out if there's nothing to do. - if (transport_ == target->transport_) { - return true; - } - - // Run through all channels and remove any non-rtp transport channels before - // setting target transport channels. - for (ChannelMap::const_iterator iter = channels_.begin(); - iter != channels_.end(); ++iter) { - if (!target->transport_->get()->HasChannel(iter->first)) { - // Remove if channel doesn't exist in |transport_|. - ReplaceChannelImpl(iter->second, NULL); - } else { - // Replace the impl for all the TransportProxyChannels with the channels - // from |target|'s transport. Fail if there's not an exact match. - ReplaceChannelImpl( - iter->second, target->transport_->get()->CreateChannel(iter->first)); - } - } - - // Now replace our transport. Must happen afterwards because - // it deletes all impls as a side effect. - transport_ = target->transport_; - transport_->get()->SignalCandidatesReady.connect( - this, &TransportProxy::OnTransportCandidatesReady); - set_candidates_allocated(target->candidates_allocated()); - return true; -} - -void TransportProxy::SetIceRole(IceRole role) { - transport_->get()->SetIceRole(role); -} - -bool TransportProxy::SetLocalTransportDescription( - const TransportDescription& description, - ContentAction action, - std::string* error_desc) { - // If this is an answer, finalize the negotiation. - if (action == CA_ANSWER) { - CompleteNegotiation(); - } - bool result = transport_->get()->SetLocalTransportDescription(description, - action, - error_desc); - if (result) - local_description_set_ = true; - return result; -} - -bool TransportProxy::SetRemoteTransportDescription( - const TransportDescription& description, - ContentAction action, - std::string* error_desc) { - // If this is an answer, finalize the negotiation. - if (action == CA_ANSWER) { - CompleteNegotiation(); - } - bool result = transport_->get()->SetRemoteTransportDescription(description, - action, - error_desc); - if (result) - remote_description_set_ = true; - return result; -} - -void TransportProxy::OnSignalingReady() { - // If we're starting a new allocation sequence, reset our state. - set_candidates_allocated(false); - transport_->get()->OnSignalingReady(); -} - -bool TransportProxy::OnRemoteCandidates(const Candidates& candidates, - std::string* error) { - // Ensure the transport is negotiated before handling candidates. - // TODO(juberti): Remove this once everybody calls SetLocalTD. - CompleteNegotiation(); - - // Ignore candidates for if the proxy content_name doesn't match the content - // name of the actual transport. This stops video candidates from being sent - // down to the audio transport when BUNDLE is enabled. - if (content_name_ != transport_->get()->content_name()) { - return true; - } - - // Verify each candidate before passing down to transport layer. - for (Candidates::const_iterator cand = candidates.begin(); - cand != candidates.end(); ++cand) { - if (!transport_->get()->VerifyCandidate(*cand, error)) - return false; - if (!HasChannel(cand->component())) { - *error = "Candidate has unknown component: " + cand->ToString() + - " for content: " + content_name_; - return false; - } - } - transport_->get()->OnRemoteCandidates(candidates); - return true; -} - -void TransportProxy::SetCertificate( - const rtc::scoped_refptr& certificate) { - transport_->get()->SetCertificate(certificate); -} - std::string BaseSession::StateToString(State state) { switch (state) { case STATE_INIT: @@ -326,7 +70,6 @@ BaseSession::BaseSession(rtc::Thread* signaling_thread, rtc::Thread* worker_thread, PortAllocator* port_allocator, const std::string& sid, - const std::string& content_type, bool initiator) : state_(STATE_INIT), error_(ERROR_NONE), @@ -334,13 +77,11 @@ BaseSession::BaseSession(rtc::Thread* signaling_thread, worker_thread_(worker_thread), port_allocator_(port_allocator), sid_(sid), - content_type_(content_type), - initiator_(initiator), - ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_10), - ice_tiebreaker_(rtc::CreateRandomId64()), - role_switch_(false), - ice_receiving_timeout_(-1) { + transport_controller_(new TransportController(signaling_thread, + worker_thread, + port_allocator)) { ASSERT(signaling_thread->IsCurrent()); + set_initiator(initiator); } BaseSession::~BaseSession() { @@ -350,11 +91,6 @@ BaseSession::~BaseSession() { LogState(state_, STATE_DEINIT); state_ = STATE_DEINIT; SignalState(this, state_); - - for (TransportMap::iterator iter = transports_.begin(); - iter != transports_.end(); ++iter) { - delete iter->second; - } } const SessionDescription* BaseSession::local_description() const { @@ -384,37 +120,23 @@ void BaseSession::set_remote_description(SessionDescription* sdesc) { remote_description_.reset(sdesc); } +void BaseSession::set_initiator(bool initiator) { + initiator_ = initiator; + + IceRole ice_role = initiator ? ICEROLE_CONTROLLING : ICEROLE_CONTROLLED; + transport_controller_->SetIceRole(ice_role); +} + const SessionDescription* BaseSession::initiator_description() const { // TODO(tommi): Assert on thread correctness. return initiator_ ? local_description_.get() : remote_description_.get(); } -bool BaseSession::SetCertificate( - const rtc::scoped_refptr& certificate) { - if (certificate_) - return false; - if (!certificate) - return false; - certificate_ = certificate; - for (TransportMap::iterator iter = transports_.begin(); - iter != transports_.end(); ++iter) { - iter->second->SetCertificate(certificate_); - } - return true; -} - -bool BaseSession::SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) { - if (state_ != STATE_INIT) { - return false; - } - - ssl_max_version_ = version; - return true; -} - bool BaseSession::PushdownTransportDescription(ContentSource source, ContentAction action, std::string* error_desc) { + ASSERT(signaling_thread()->IsCurrent()); + if (source == CS_LOCAL) { return PushdownLocalTransportDescription(local_description(), action, @@ -428,23 +150,17 @@ bool BaseSession::PushdownTransportDescription(ContentSource source, bool BaseSession::PushdownLocalTransportDescription( const SessionDescription* sdesc, ContentAction action, - std::string* error_desc) { - // Update the Transports with the right information, and trigger them to - // start connecting. - for (TransportMap::iterator iter = transports_.begin(); - iter != transports_.end(); ++iter) { - // If no transport info was in this session description, ret == false - // and we just skip this one. - TransportDescription tdesc; - bool ret = GetTransportDescription( - sdesc, iter->second->content_name(), &tdesc); - if (ret) { - if (!iter->second->SetLocalTransportDescription(tdesc, action, - error_desc)) { - return false; - } + std::string* err) { + ASSERT(signaling_thread()->IsCurrent()); - iter->second->ConnectChannels(); + if (!sdesc) { + return false; + } + + for (const TransportInfo& tinfo : sdesc->transport_infos()) { + if (!transport_controller_->SetLocalTransportDescription( + tinfo.content_name, tinfo.description, action, err)) { + return false; } } @@ -454,134 +170,23 @@ bool BaseSession::PushdownLocalTransportDescription( bool BaseSession::PushdownRemoteTransportDescription( const SessionDescription* sdesc, ContentAction action, - std::string* error_desc) { - // Update the Transports with the right information. - for (TransportMap::iterator iter = transports_.begin(); - iter != transports_.end(); ++iter) { - TransportDescription tdesc; + std::string* err) { + ASSERT(signaling_thread()->IsCurrent()); - // If no transport info was in this session description, ret == false - // and we just skip this one. - bool ret = GetTransportDescription( - sdesc, iter->second->content_name(), &tdesc); - if (ret) { - if (!iter->second->SetRemoteTransportDescription(tdesc, action, - error_desc)) { - return false; - } + if (!sdesc) { + return false; + } + + for (const TransportInfo& tinfo : sdesc->transport_infos()) { + if (!transport_controller_->SetRemoteTransportDescription( + tinfo.content_name, tinfo.description, action, err)) { + return false; } } return true; } -void BaseSession::SetIceConnectionReceivingTimeout(int timeout_ms) { - ice_receiving_timeout_ = timeout_ms; - for (const auto& kv : transport_proxies()) { - Transport* transport = kv.second->impl(); - if (transport) { - transport->SetChannelReceivingTimeout(timeout_ms); - } - } -} - -TransportChannel* BaseSession::CreateChannel(const std::string& content_name, - int component) { - // We create the proxy "on demand" here because we need to support - // creating channels at any time, even before we send or receive - // initiate messages, which is before we create the transports. - TransportProxy* transproxy = GetOrCreateTransportProxy(content_name); - return transproxy->CreateChannel(component); -} - -TransportChannel* BaseSession::GetChannel(const std::string& content_name, - int component) { - TransportProxy* transproxy = GetTransportProxy(content_name); - if (transproxy == NULL) - return NULL; - - return transproxy->GetChannel(component); -} - -void BaseSession::DestroyChannel(const std::string& content_name, - int component) { - TransportProxy* transproxy = GetTransportProxy(content_name); - ASSERT(transproxy != NULL); - transproxy->DestroyChannel(component); -} - -TransportProxy* BaseSession::GetOrCreateTransportProxy( - const std::string& content_name) { - TransportProxy* transproxy = GetTransportProxy(content_name); - if (transproxy) - return transproxy; - - Transport* transport = CreateTransport(content_name); - transport->SetIceRole(initiator_ ? ICEROLE_CONTROLLING : ICEROLE_CONTROLLED); - transport->SetIceTiebreaker(ice_tiebreaker_); - transport->SetSslMaxProtocolVersion(ssl_max_version_); - // TODO: Connect all the Transport signals to TransportProxy - // then to the BaseSession. - transport->SignalConnecting.connect( - this, &BaseSession::OnTransportConnecting); - transport->SignalWritableState.connect( - this, &BaseSession::OnTransportWritable); - transport->SignalReceivingState.connect( - this, &BaseSession::OnTransportReceiving); - transport->SignalRequestSignaling.connect( - this, &BaseSession::OnTransportRequestSignaling); - transport->SignalRouteChange.connect( - this, &BaseSession::OnTransportRouteChange); - transport->SignalCandidatesAllocationDone.connect( - this, &BaseSession::OnTransportCandidatesAllocationDone); - transport->SignalRoleConflict.connect( - this, &BaseSession::OnRoleConflict); - transport->SignalCompleted.connect( - this, &BaseSession::OnTransportCompleted); - transport->SignalFailed.connect( - this, &BaseSession::OnTransportFailed); - - transproxy = new TransportProxy(worker_thread_, sid_, content_name, - new TransportWrapper(transport)); - transproxy->SignalCandidatesReady.connect( - this, &BaseSession::OnTransportProxyCandidatesReady); - if (certificate_) - transproxy->SetCertificate(certificate_); - transports_[content_name] = transproxy; - - return transproxy; -} - -Transport* BaseSession::GetTransport(const std::string& content_name) { - TransportProxy* transproxy = GetTransportProxy(content_name); - if (transproxy == NULL) - return NULL; - return transproxy->impl(); -} - -TransportProxy* BaseSession::GetTransportProxy( - const std::string& content_name) { - TransportMap::iterator iter = transports_.find(content_name); - return (iter != transports_.end()) ? iter->second : NULL; -} - -void BaseSession::DestroyTransportProxy( - const std::string& content_name) { - TransportMap::iterator iter = transports_.find(content_name); - if (iter != transports_.end()) { - delete iter->second; - transports_.erase(content_name); - } -} - -Transport* BaseSession::CreateTransport(const std::string& content_name) { - Transport* transport = new DtlsTransport( - signaling_thread(), worker_thread(), content_name, port_allocator(), - certificate_); - transport->SetChannelReceivingTimeout(ice_receiving_timeout_); - return transport; -} - void BaseSession::SetState(State state) { ASSERT(signaling_thread_->IsCurrent()); if (state != state_) { @@ -601,180 +206,18 @@ void BaseSession::SetError(Error error, const std::string& error_desc) { } } -void BaseSession::OnSignalingReady() { - ASSERT(signaling_thread()->IsCurrent()); - for (TransportMap::iterator iter = transports_.begin(); - iter != transports_.end(); ++iter) { - iter->second->OnSignalingReady(); - } +void BaseSession::SetIceConnectionReceivingTimeout(int timeout_ms) { + transport_controller_->SetIceConnectionReceivingTimeout(timeout_ms); } -// TODO(juberti): Since PushdownLocalTD now triggers the connection process to -// start, remove this method once everyone calls PushdownLocalTD. -void BaseSession::SpeculativelyConnectAllTransportChannels() { - // Put all transports into the connecting state. - for (TransportMap::iterator iter = transports_.begin(); - iter != transports_.end(); ++iter) { - iter->second->ConnectChannels(); - } -} - -bool BaseSession::OnRemoteCandidates(const std::string& content_name, - const Candidates& candidates, - std::string* error) { - // Give candidates to the appropriate transport, and tell that transport - // to start connecting, if it's not already doing so. - TransportProxy* transproxy = GetTransportProxy(content_name); - if (!transproxy) { - *error = "Unknown content name " + content_name; - return false; - } - if (!transproxy->OnRemoteCandidates(candidates, error)) { - return false; - } - // TODO(juberti): Remove this call once we can be sure that we always have - // a local transport description (which will trigger the connection). - transproxy->ConnectChannels(); - return true; -} - -bool BaseSession::MaybeEnableMuxingSupport() { - // We need both a local and remote description to decide if we should mux. - if ((state_ == STATE_SENTINITIATE || - state_ == STATE_RECEIVEDINITIATE) && - ((local_description_ == NULL) || - (remote_description_ == NULL))) { - return false; - } - - // In order to perform the multiplexing, we need all proxies to be in the - // negotiated state, i.e. to have implementations underneath. - // Ensure that this is the case, regardless of whether we are going to mux. - for (TransportMap::iterator iter = transports_.begin(); - iter != transports_.end(); ++iter) { - ASSERT(iter->second->negotiated()); - if (!iter->second->negotiated()) { - return false; - } - } - - // If both sides agree to BUNDLE, mux all the specified contents onto the - // transport belonging to the first content name in the BUNDLE group. - // If the contents are already muxed, this will be a no-op. - // TODO(juberti): Should this check that local and remote have configured - // BUNDLE the same way? - bool candidates_allocated = IsCandidateAllocationDone(); - const ContentGroup* local_bundle_group = - local_description_->GetGroupByName(GROUP_TYPE_BUNDLE); - const ContentGroup* remote_bundle_group = - remote_description_->GetGroupByName(GROUP_TYPE_BUNDLE); - if (local_bundle_group && remote_bundle_group) { - if (!BundleContentGroup(local_bundle_group)) { - LOG(LS_WARNING) << "Failed to set up BUNDLE"; - return false; - } - - // If we weren't done gathering before, we might be done now, as a result - // of enabling mux. - if (!candidates_allocated) { - MaybeCandidateAllocationDone(); - } - } else { - LOG(LS_INFO) << "BUNDLE group missing from remote or local description."; - } - return true; -} - -bool BaseSession::BundleContentGroup(const ContentGroup* bundle_group) { - const std::string* content_name = bundle_group->FirstContentName(); - if (!content_name) { - LOG(LS_INFO) << "No content names specified in BUNDLE group."; - return true; - } - - TransportProxy* selected_proxy = GetTransportProxy(*content_name); - if (!selected_proxy) { - LOG(LS_WARNING) << "No transport found for content \"" - << *content_name << "\"."; - return false; - } - - for (TransportMap::iterator iter = transports_.begin(); - iter != transports_.end(); ++iter) { - // If content is part of the mux group, then repoint its proxy at the - // transport object that we have chosen to mux onto. If the proxy - // is already pointing at the right object, it will be a no-op. - if (bundle_group->HasContentName(iter->first) && - !iter->second->SetupMux(selected_proxy)) { - LOG(LS_WARNING) << "Failed to bundle " << iter->first << " to " - << *content_name; - return false; - } - LOG(LS_INFO) << "Bundling " << iter->first << " to " << *content_name; - } - - return true; -} - -void BaseSession::OnTransportCandidatesAllocationDone(Transport* transport) { - // TODO(juberti): This is a clunky way of processing the done signal. Instead, - // TransportProxy should receive the done signal directly, set its allocated - // flag internally, and then reissue the done signal to Session. - // Overall we should make TransportProxy receive *all* the signals from - // Transport, since this removes the need to manually iterate over all - // the transports, as is needed to make sure signals are handled properly - // when BUNDLEing. - // TODO(juberti): Per b/7998978, devs and QA are hitting this assert in ways - // that make it prohibitively difficult to run dbg builds. Disabled for now. - //ASSERT(!IsCandidateAllocationDone()); - for (TransportMap::iterator iter = transports_.begin(); - iter != transports_.end(); ++iter) { - if (iter->second->impl() == transport) { - iter->second->set_candidates_allocated(true); - } - } - MaybeCandidateAllocationDone(); -} - -bool BaseSession::IsCandidateAllocationDone() const { - for (TransportMap::const_iterator iter = transports_.begin(); - iter != transports_.end(); ++iter) { - if (!iter->second->candidates_allocated()) { - LOG(LS_INFO) << "Candidate allocation not done for " - << iter->second->content_name(); - return false; - } - } - return true; -} - -void BaseSession::MaybeCandidateAllocationDone() { - if (IsCandidateAllocationDone()) { - LOG(LS_INFO) << "Candidate gathering is complete."; - OnCandidatesAllocationDone(); - } -} - -void BaseSession::OnRoleConflict() { - if (role_switch_) { - LOG(LS_WARNING) << "Repeat of role conflict signal from Transport."; - return; - } - - role_switch_ = true; - for (TransportMap::iterator iter = transports_.begin(); - iter != transports_.end(); ++iter) { - // Role will be reverse of initial role setting. - IceRole role = initiator_ ? ICEROLE_CONTROLLED : ICEROLE_CONTROLLING; - iter->second->SetIceRole(role); - } +void BaseSession::MaybeStartGathering() { + transport_controller_->MaybeStartGathering(); } void BaseSession::LogState(State old_state, State new_state) { LOG(LS_INFO) << "Session:" << id() << " Old state:" << StateToString(old_state) - << " New state:" << StateToString(new_state) - << " Type:" << content_type(); + << " New state:" << StateToString(new_state); } // static diff --git a/webrtc/p2p/base/session.h b/webrtc/p2p/base/session.h index 8d7aa21c22..d8721fd0f8 100644 --- a/webrtc/p2p/base/session.h +++ b/webrtc/p2p/base/session.h @@ -16,14 +16,14 @@ #include #include -#include "webrtc/p2p/base/candidate.h" -#include "webrtc/p2p/base/port.h" -#include "webrtc/p2p/base/transport.h" #include "webrtc/base/refcount.h" #include "webrtc/base/rtccertificate.h" #include "webrtc/base/scoped_ptr.h" #include "webrtc/base/scoped_ref_ptr.h" #include "webrtc/base/socketaddress.h" +#include "webrtc/p2p/base/candidate.h" +#include "webrtc/p2p/base/port.h" +#include "webrtc/p2p/base/transport.h" namespace cricket { @@ -31,136 +31,8 @@ class BaseSession; class P2PTransportChannel; class Transport; class TransportChannel; -class TransportChannelProxy; class TransportChannelImpl; - -typedef rtc::RefCountedObject > -TransportWrapper; - -// Bundles a Transport and ChannelMap together. ChannelMap is used to -// create transport channels before receiving or sending a session -// initiate, and for speculatively connecting channels. Previously, a -// session had one ChannelMap and transport. Now, with multiple -// transports per session, we need multiple ChannelMaps as well. - -typedef std::map ChannelMap; - -class TransportProxy : public sigslot::has_slots<> { - public: - TransportProxy( - rtc::Thread* worker_thread, - const std::string& sid, - const std::string& content_name, - TransportWrapper* transport) - : worker_thread_(worker_thread), - sid_(sid), - content_name_(content_name), - transport_(transport), - connecting_(false), - negotiated_(false), - sent_candidates_(false), - candidates_allocated_(false), - local_description_set_(false), - remote_description_set_(false) { - transport_->get()->SignalCandidatesReady.connect( - this, &TransportProxy::OnTransportCandidatesReady); - } - ~TransportProxy(); - - const std::string& content_name() const { return content_name_; } - // TODO(juberti): It's not good form to expose the object you're wrapping, - // since callers can mutate it. Can we make this return a const Transport*? - Transport* impl() const { return transport_->get(); } - - const std::string& type() const; - bool negotiated() const { return negotiated_; } - const Candidates& sent_candidates() const { return sent_candidates_; } - const Candidates& unsent_candidates() const { return unsent_candidates_; } - bool candidates_allocated() const { return candidates_allocated_; } - void set_candidates_allocated(bool allocated) { - candidates_allocated_ = allocated; - } - - TransportChannel* GetChannel(int component); - TransportChannel* CreateChannel(int component); - bool HasChannel(int component); - void DestroyChannel(int component); - - void AddSentCandidates(const Candidates& candidates); - void AddUnsentCandidates(const Candidates& candidates); - void ClearSentCandidates() { sent_candidates_.clear(); } - void ClearUnsentCandidates() { unsent_candidates_.clear(); } - - // Start the connection process for any channels, creating impls if needed. - void ConnectChannels(); - // Hook up impls to the proxy channels. Doesn't change connect state. - void CompleteNegotiation(); - - // Mux this proxy onto the specified proxy's transport. - bool SetupMux(TransportProxy* proxy); - - // Simple functions that thunk down to the same functions on Transport. - void SetIceRole(IceRole role); - void SetCertificate( - const rtc::scoped_refptr& certificate); - bool SetLocalTransportDescription(const TransportDescription& description, - ContentAction action, - std::string* error_desc); - bool SetRemoteTransportDescription(const TransportDescription& description, - ContentAction action, - std::string* error_desc); - void OnSignalingReady(); - bool OnRemoteCandidates(const Candidates& candidates, std::string* error); - - // Called when a transport signals that it has new candidates. - void OnTransportCandidatesReady(cricket::Transport* transport, - const Candidates& candidates) { - SignalCandidatesReady(this, candidates); - } - - bool local_description_set() const { - return local_description_set_; - } - bool remote_description_set() const { - return remote_description_set_; - } - - // Handles sending of ready candidates and receiving of remote candidates. - sigslot::signal2&> SignalCandidatesReady; - - private: - TransportChannelProxy* GetChannelProxy(int component) const; - - // Creates a new channel on the Transport which causes the reference - // count to increment. - void CreateChannelImpl(int component); - void CreateChannelImpl_w(int component); - - // Manipulators of transportchannelimpl in channel proxy. - void SetChannelImplFromTransport(TransportChannelProxy* proxy, int component); - void SetChannelImplFromTransport_w(TransportChannelProxy* proxy, - int component); - void ReplaceChannelImpl(TransportChannelProxy* proxy, - TransportChannelImpl* impl); - void ReplaceChannelImpl_w(TransportChannelProxy* proxy, - TransportChannelImpl* impl); - - rtc::Thread* const worker_thread_; - const std::string sid_; - const std::string content_name_; - rtc::scoped_refptr transport_; - bool connecting_; - bool negotiated_; - ChannelMap channels_; - Candidates sent_candidates_; - Candidates unsent_candidates_; - bool candidates_allocated_; - bool local_description_set_; - bool remote_description_set_; -}; - -typedef std::map TransportMap; +class TransportController; // Statistics for all the transports of this session. typedef std::map TransportStatsMap; @@ -224,7 +96,6 @@ class BaseSession : public sigslot::has_slots<>, rtc::Thread* worker_thread, PortAllocator* port_allocator, const std::string& sid, - const std::string& content_type, bool initiator); virtual ~BaseSession(); @@ -236,14 +107,6 @@ class BaseSession : public sigslot::has_slots<>, // The ID of this session. const std::string& id() const { return sid_; } - // TODO(juberti): This data is largely redundant, as it can now be obtained - // from local/remote_description(). Remove these functions and members. - // Returns the XML namespace identifying the type of this session. - const std::string& content_type() const { return content_type_; } - - // Indicates whether we initiated this session. - bool initiator() const { return initiator_; } - // Returns the application-level description given by our client. // If we are the recipient, this will be NULL until we send an accept. const SessionDescription* local_description() const; @@ -260,6 +123,9 @@ class BaseSession : public sigslot::has_slots<>, // Takes ownership of SessionDescription* void set_remote_description(SessionDescription* sdesc); + void set_initiator(bool initiator); + bool initiator() const { return initiator_; } + const SessionDescription* initiator_description() const; // Returns the current state of the session. See the enum above for details. @@ -280,151 +146,29 @@ class BaseSession : public sigslot::has_slots<>, // TODO(ronghuawu): remove the SetError method that doesn't take |error_desc|. virtual void SetError(Error error, const std::string& error_desc); - // Fired when the remote description is updated, with the updated - // contents. - sigslot::signal2 - SignalRemoteDescriptionUpdate; - - // Fired when SetState is called (regardless if there's a state change), which - // indicates the session description might have be updated. - sigslot::signal2 SignalNewLocalDescription; - - // Fired when SetState is called (regardless if there's a state change), which - // indicates the session description might have be updated. - sigslot::signal2 SignalNewRemoteDescription; - - // Returns the transport that has been negotiated or NULL if - // negotiation is still in progress. - virtual Transport* GetTransport(const std::string& content_name); - - // Creates a new channel with the given names. This method may be called - // immediately after creating the session. However, the actual - // implementation may not be fixed until transport negotiation completes. - // This will usually be called from the worker thread, but that - // shouldn't be an issue since the main thread will be blocked in - // Send when doing so. - virtual TransportChannel* CreateChannel(const std::string& content_name, - int component); - - // Returns the channel with the given names. - virtual TransportChannel* GetChannel(const std::string& content_name, - int component); - - // Destroys the channel with the given names. - // This will usually be called from the worker thread, but that - // shouldn't be an issue since the main thread will be blocked in - // Send when doing so. - virtual void DestroyChannel(const std::string& content_name, - int component); - - // Set the ice connection receiving timeout. void SetIceConnectionReceivingTimeout(int timeout_ms); - // For testing. - const rtc::scoped_refptr& - certificate_for_testing() const { - return certificate_; - } + // Start gathering candidates for any new transports, or transports doing an + // ICE restart. + void MaybeStartGathering(); protected: - // Specifies the identity to use in this session. - bool SetCertificate( - const rtc::scoped_refptr& certificate); - - bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version); - bool PushdownTransportDescription(ContentSource source, ContentAction action, std::string* error_desc); - void set_initiator(bool initiator) { initiator_ = initiator; } - - const TransportMap& transport_proxies() const { return transports_; } - // Get a TransportProxy by content_name or transport. NULL if not found. - TransportProxy* GetTransportProxy(const std::string& content_name); - void DestroyTransportProxy(const std::string& content_name); - // TransportProxy is owned by session. Return proxy just for convenience. - TransportProxy* GetOrCreateTransportProxy(const std::string& content_name); - // Creates the actual transport object. Overridable for testing. - virtual Transport* CreateTransport(const std::string& content_name); - - void OnSignalingReady(); - void SpeculativelyConnectAllTransportChannels(); - // Helper method to provide remote candidates to the transport. - bool OnRemoteCandidates(const std::string& content_name, - const Candidates& candidates, - std::string* error); - - // This method will mux transport channels by content_name. - // First content is used for muxing. - bool MaybeEnableMuxingSupport(); - - // Called when a transport requests signaling. - virtual void OnTransportRequestSignaling(Transport* transport) { - } - - // Called when the first channel of a transport begins connecting. We use - // this to start a timer, to make sure that the connection completes in a - // reasonable amount of time. - virtual void OnTransportConnecting(Transport* transport) { - } - - // Called when a transport changes its writable state. We track this to make - // sure that the transport becomes writable within a reasonable amount of - // time. If this does not occur, we signal an error. - virtual void OnTransportWritable(Transport* transport) { - } - virtual void OnTransportReadable(Transport* transport) { - } - - virtual void OnTransportReceiving(Transport* transport) { - } - - // Called when a transport has found its steady-state connections. - virtual void OnTransportCompleted(Transport* transport) { - } - - // Called when a transport has failed permanently. - virtual void OnTransportFailed(Transport* transport) { - } - - // Called when a transport signals that it has new candidates. - virtual void OnTransportProxyCandidatesReady(TransportProxy* proxy, - const Candidates& candidates) { - } - - virtual void OnTransportRouteChange( - Transport* transport, - int component, - const cricket::Candidate& remote_candidate) { - } - - virtual void OnTransportCandidatesAllocationDone(Transport* transport); - - // Called when all transport channels allocated required candidates. - // This method should be used as an indication of candidates gathering process - // is completed and application can now send local candidates list to remote. - virtual void OnCandidatesAllocationDone() { - } - - // Handles the ice role change callback from Transport. This must be - // propagated to all the transports. - virtual void OnRoleConflict(); // Handles messages posted to us. virtual void OnMessage(rtc::Message *pmsg); - protected: - bool IsCandidateAllocationDone() const; + TransportController* transport_controller() { + return transport_controller_.get(); + } + protected: State state_; Error error_; std::string error_desc_; - // This method will delete the Transport and TransportChannelImpls - // and replace those with the Transport object of the first - // MediaContent in bundle_group. - bool BundleContentGroup(const ContentGroup* bundle_group); - private: // Helper methods to push local and remote transport descriptions. bool PushdownLocalTransportDescription( @@ -434,8 +178,6 @@ class BaseSession : public sigslot::has_slots<>, const SessionDescription* sdesc, ContentAction action, std::string* error_desc); - void MaybeCandidateAllocationDone(); - // Log session state. void LogState(State old_state, State new_state); @@ -449,21 +191,10 @@ class BaseSession : public sigslot::has_slots<>, rtc::Thread* const worker_thread_; PortAllocator* const port_allocator_; const std::string sid_; - const std::string content_type_; bool initiator_; - rtc::scoped_refptr certificate_; - rtc::SSLProtocolVersion ssl_max_version_; + rtc::scoped_ptr transport_controller_; rtc::scoped_ptr local_description_; rtc::scoped_ptr remote_description_; - uint64 ice_tiebreaker_; - // This flag will be set to true after the first role switch. This flag - // will enable us to stop any role switch during the call. - bool role_switch_; - TransportMap transports_; - - // Timeout value in milliseconds for which no ICE connection receives - // any packets. - int ice_receiving_timeout_; }; } // namespace cricket diff --git a/webrtc/p2p/base/session_unittest.cc b/webrtc/p2p/base/session_unittest.cc deleted file mode 100644 index 3419cc3c46..0000000000 --- a/webrtc/p2p/base/session_unittest.cc +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2015 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/base/gunit.h" -#include "webrtc/base/helpers.h" -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/base/thread.h" -#include "webrtc/p2p/base/dtlstransportchannel.h" -#include "webrtc/p2p/base/p2ptransportchannel.h" -#include "webrtc/p2p/base/portallocator.h" -#include "webrtc/p2p/base/session.h" -#include "webrtc/p2p/base/transportchannelproxy.h" -#include "webrtc/p2p/client/fakeportallocator.h" - -using cricket::BaseSession; -using cricket::DtlsTransportChannelWrapper; -using cricket::FakePortAllocator; -using cricket::P2PTransportChannel; -using cricket::PortAllocator; -using cricket::TransportChannelProxy; -using cricket::TransportProxy; - -class BaseSessionForTest : public BaseSession { - public: - BaseSessionForTest(rtc::Thread* signaling_thread, - rtc::Thread* worker_thread, - PortAllocator* port_allocator, - const std::string& sid, - const std::string& content_type, - bool initiator) - : BaseSession(signaling_thread, - worker_thread, - port_allocator, - sid, - content_type, - initiator) {} - using BaseSession::GetOrCreateTransportProxy; -}; - -class BaseSessionTest : public testing::Test { - public: - BaseSessionTest() - : port_allocator_(new FakePortAllocator(rtc::Thread::Current(), nullptr)), - session_(new BaseSessionForTest(rtc::Thread::Current(), - rtc::Thread::Current(), - port_allocator_.get(), - "123", - cricket::NS_JINGLE_RTP, - false)) {} - P2PTransportChannel* CreateChannel(const std::string& content, - int component) { - TransportProxy* transport_proxy = - session_->GetOrCreateTransportProxy(content); - // This hacking is needed in order that the p2p transport channel - // will be created in the following. - transport_proxy->CompleteNegotiation(); - - TransportChannelProxy* channel_proxy = static_cast( - session_->CreateChannel(content, component)); - DtlsTransportChannelWrapper* dtls_channel = - static_cast(channel_proxy->impl()); - return static_cast(dtls_channel->channel()); - } - - rtc::scoped_ptr port_allocator_; - rtc::scoped_ptr session_; -}; - -TEST_F(BaseSessionTest, TestSetIceReceivingTimeout) { - P2PTransportChannel* channel1 = CreateChannel("audio", 1); - ASSERT_NE(channel1, nullptr); - // These are the default values. - EXPECT_EQ(2500, channel1->receiving_timeout()); - EXPECT_EQ(250, channel1->check_receiving_delay()); - // Set the timeout to a different value. - session_->SetIceConnectionReceivingTimeout(1000); - EXPECT_EQ(1000, channel1->receiving_timeout()); - EXPECT_EQ(100, channel1->check_receiving_delay()); - - // Even if a channel is created after setting the receiving timeout, - // the set timeout value is applied to the new channel. - P2PTransportChannel* channel2 = CreateChannel("video", 2); - ASSERT_NE(channel2, nullptr); - EXPECT_EQ(1000, channel2->receiving_timeout()); - EXPECT_EQ(100, channel2->check_receiving_delay()); - - // Test minimum checking delay. - session_->SetIceConnectionReceivingTimeout(200); - EXPECT_EQ(200, channel1->receiving_timeout()); - EXPECT_EQ(50, channel1->check_receiving_delay()); - EXPECT_EQ(200, channel2->receiving_timeout()); - EXPECT_EQ(50, channel2->check_receiving_delay()); -} diff --git a/webrtc/p2p/base/transport.cc b/webrtc/p2p/base/transport.cc index d626ad3d65..b7aba7540a 100644 --- a/webrtc/p2p/base/transport.cc +++ b/webrtc/p2p/base/transport.cc @@ -8,6 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include // for std::pair + #include "webrtc/p2p/base/transport.h" #include "webrtc/p2p/base/candidate.h" @@ -22,39 +24,6 @@ namespace cricket { using rtc::Bind; -enum { - MSG_ONSIGNALINGREADY = 1, - MSG_ONREMOTECANDIDATE, - MSG_WRITESTATE, - MSG_REQUESTSIGNALING, - MSG_CANDIDATEREADY, - MSG_ROUTECHANGE, - MSG_CONNECTING, - MSG_CANDIDATEALLOCATIONCOMPLETE, - MSG_ROLECONFLICT, - MSG_COMPLETED, - MSG_FAILED, - MSG_RECEIVINGSTATE, -}; - -struct ChannelParams : public rtc::MessageData { - ChannelParams() : channel(NULL), candidate(NULL) {} - explicit ChannelParams(int component) - : component(component), channel(NULL), candidate(NULL) {} - explicit ChannelParams(Candidate* candidate) - : channel(NULL), candidate(candidate) { - } - - ~ChannelParams() { - delete candidate; - } - - std::string name; - int component; - TransportChannelImpl* channel; - Candidate* candidate; -}; - static bool VerifyIceParams(const TransportDescription& desc) { // For legacy protocols. if (desc.ice_ufrag.empty() && desc.ice_pwd.empty()) @@ -96,58 +65,59 @@ static bool IceCredentialsChanged(const TransportDescription& old_desc, new_desc.ice_ufrag, new_desc.ice_pwd); } -Transport::Transport(rtc::Thread* signaling_thread, - rtc::Thread* worker_thread, - const std::string& content_name, - PortAllocator* allocator) - : signaling_thread_(signaling_thread), - worker_thread_(worker_thread), - content_name_(content_name), - allocator_(allocator), - destroyed_(false), - readable_(TRANSPORT_STATE_NONE), - writable_(TRANSPORT_STATE_NONE), - receiving_(TRANSPORT_STATE_NONE), - was_writable_(false), - connect_requested_(false), - ice_role_(ICEROLE_UNKNOWN), - tiebreaker_(0), - remote_ice_mode_(ICEMODE_FULL), - channel_receiving_timeout_(-1) { -} +Transport::Transport(const std::string& name, PortAllocator* allocator) + : name_(name), allocator_(allocator) {} Transport::~Transport() { - ASSERT(signaling_thread_->IsCurrent()); - ASSERT(destroyed_); + ASSERT(channels_destroyed_); +} + +bool Transport::AllChannelsCompleted() const { + // We aren't completed until at least one channel is complete, so if there + // are no channels, we aren't complete yet. + if (channels_.empty()) { + LOG(LS_INFO) << name() << " transport is not complete" + << " because it has no TransportChannels"; + return false; + } + + // A Transport's ICE process is completed if all of its channels are writable, + // have finished allocating candidates, and have pruned all but one of their + // connections. + for (const auto& iter : channels_) { + const TransportChannelImpl* channel = iter.second.get(); + bool complete = + channel->writable() && + channel->GetState() == TransportChannelState::STATE_COMPLETED && + channel->GetIceRole() == ICEROLE_CONTROLLING && + channel->gathering_state() == kIceGatheringComplete; + if (!complete) { + LOG(LS_INFO) << name() << " transport is not complete" + << " because a channel is still incomplete."; + return false; + } + } + + return true; +} + +bool Transport::AnyChannelFailed() const { + for (const auto& iter : channels_) { + if (iter.second->GetState() == TransportChannelState::STATE_FAILED) { + return true; + } + } + return false; } void Transport::SetIceRole(IceRole role) { - worker_thread_->Invoke(Bind(&Transport::SetIceRole_w, this, role)); -} - -void Transport::SetCertificate( - const rtc::scoped_refptr& certificate) { - worker_thread_->Invoke(Bind(&Transport::SetCertificate_w, this, - certificate)); -} - -bool Transport::GetCertificate( - rtc::scoped_refptr* certificate) { - // The identity is set on the worker thread, so for safety it must also be - // acquired on the worker thread. - return worker_thread_->Invoke( - Bind(&Transport::GetCertificate_w, this, certificate)); + ice_role_ = role; + for (auto& iter : channels_) { + iter.second->SetIceRole(ice_role_); + } } bool Transport::GetRemoteSSLCertificate(rtc::SSLCertificate** cert) { - // Channels can be deleted on the worker thread, so for safety the remote - // certificate is acquired on the worker thread. - return worker_thread_->Invoke( - Bind(&Transport::GetRemoteSSLCertificate_w, this, cert)); -} - -bool Transport::GetRemoteSSLCertificate_w(rtc::SSLCertificate** cert) { - ASSERT(worker_thread()->IsCurrent()); if (channels_.empty()) return false; @@ -156,12 +126,6 @@ bool Transport::GetRemoteSSLCertificate_w(rtc::SSLCertificate** cert) { } void Transport::SetChannelReceivingTimeout(int timeout_ms) { - worker_thread_->Invoke( - Bind(&Transport::SetChannelReceivingTimeout_w, this, timeout_ms)); -} - -void Transport::SetChannelReceivingTimeout_w(int timeout_ms) { - ASSERT(worker_thread()->IsCurrent()); channel_receiving_timeout_ = timeout_ms; for (const auto& kv : channels_) { kv.second->SetReceivingTimeout(timeout_ms); @@ -172,35 +136,73 @@ bool Transport::SetLocalTransportDescription( const TransportDescription& description, ContentAction action, std::string* error_desc) { - return worker_thread_->Invoke(Bind( - &Transport::SetLocalTransportDescription_w, this, - description, action, error_desc)); + bool ret = true; + + if (!VerifyIceParams(description)) { + return BadTransportDescription("Invalid ice-ufrag or ice-pwd length", + error_desc); + } + + if (local_description_ && + IceCredentialsChanged(*local_description_, description)) { + IceRole new_ice_role = + (action == CA_OFFER) ? ICEROLE_CONTROLLING : ICEROLE_CONTROLLED; + + // It must be called before ApplyLocalTransportDescription, which may + // trigger an ICE restart and depends on the new ICE role. + SetIceRole(new_ice_role); + } + + local_description_.reset(new TransportDescription(description)); + + for (auto& iter : channels_) { + ret &= ApplyLocalTransportDescription(iter.second.get(), error_desc); + } + if (!ret) { + return false; + } + + // If PRANSWER/ANSWER is set, we should decide transport protocol type. + if (action == CA_PRANSWER || action == CA_ANSWER) { + ret &= NegotiateTransportDescription(action, error_desc); + } + if (ret) { + local_description_set_ = true; + ConnectChannels(); + } + + return ret; } bool Transport::SetRemoteTransportDescription( const TransportDescription& description, ContentAction action, std::string* error_desc) { - return worker_thread_->Invoke(Bind( - &Transport::SetRemoteTransportDescription_w, this, - description, action, error_desc)); + bool ret = true; + + if (!VerifyIceParams(description)) { + return BadTransportDescription("Invalid ice-ufrag or ice-pwd length", + error_desc); + } + + remote_description_.reset(new TransportDescription(description)); + for (auto& iter : channels_) { + ret &= ApplyRemoteTransportDescription(iter.second.get(), error_desc); + } + + // If PRANSWER/ANSWER is set, we should decide transport protocol type. + if (action == CA_PRANSWER || action == CA_ANSWER) { + ret = NegotiateTransportDescription(CA_OFFER, error_desc); + } + if (ret) { + remote_description_set_ = true; + } + + return ret; } TransportChannelImpl* Transport::CreateChannel(int component) { - return worker_thread_->Invoke(Bind( - &Transport::CreateChannel_w, this, component)); -} - -TransportChannelImpl* Transport::CreateChannel_w(int component) { - ASSERT(worker_thread()->IsCurrent()); TransportChannelImpl* impl; - // TODO(tommi): We don't really need to grab the lock until the actual call - // to insert() below and presumably hold it throughout initialization of - // |impl| after the impl_exists check. Maybe we can factor that out to - // a separate function and not grab the lock in this function. - // Actually, we probably don't need to hold the lock while initializing - // |impl| since we can just do the insert when that's done. - rtc::CritScope cs(&crit_); // Create the entry if it does not exist. bool impl_exists = false; @@ -216,7 +218,7 @@ TransportChannelImpl* Transport::CreateChannel_w(int component) { // Increase the ref count. iterator->second.AddRef(); - destroyed_ = false; + channels_destroyed_ = false; if (impl_exists) { // If this is an existing channel, we should just return it without @@ -228,23 +230,21 @@ TransportChannelImpl* Transport::CreateChannel_w(int component) { impl->SetIceRole(ice_role_); impl->SetIceTiebreaker(tiebreaker_); impl->SetReceivingTimeout(channel_receiving_timeout_); - // TODO(ronghuawu): Change CreateChannel_w to be able to return error since - // below Apply**Description_w calls can fail. + // TODO(ronghuawu): Change CreateChannel to be able to return error since + // below Apply**Description calls can fail. if (local_description_) - ApplyLocalTransportDescription_w(impl, NULL); + ApplyLocalTransportDescription(impl, NULL); if (remote_description_) - ApplyRemoteTransportDescription_w(impl, NULL); + ApplyRemoteTransportDescription(impl, NULL); if (local_description_ && remote_description_) - ApplyNegotiatedTransportDescription_w(impl, NULL); + ApplyNegotiatedTransportDescription(impl, NULL); impl->SignalWritableState.connect(this, &Transport::OnChannelWritableState); impl->SignalReceivingState.connect(this, &Transport::OnChannelReceivingState); - impl->SignalRequestSignaling.connect( - this, &Transport::OnChannelRequestSignaling); - impl->SignalCandidateReady.connect(this, &Transport::OnChannelCandidateReady); + impl->SignalGatheringState.connect(this, &Transport::OnChannelGatheringState); + impl->SignalCandidateGathered.connect(this, + &Transport::OnChannelCandidateGathered); impl->SignalRouteChange.connect(this, &Transport::OnChannelRouteChange); - impl->SignalCandidatesAllocationDone.connect( - this, &Transport::OnChannelCandidatesAllocationDone); impl->SignalRoleConflict.connect(this, &Transport::OnRoleConflict); impl->SignalConnectionRemoved.connect( this, &Transport::OnChannelConnectionRemoved); @@ -254,36 +254,22 @@ TransportChannelImpl* Transport::CreateChannel_w(int component) { if (channels_.size() == 1) { // If this is the first channel, then indicate that we have started // connecting. - signaling_thread()->Post(this, MSG_CONNECTING, NULL); + SignalConnecting(this); } } return impl; } TransportChannelImpl* Transport::GetChannel(int component) { - // TODO(tommi,pthatcher): Since we're returning a pointer from the channels_ - // map, shouldn't we assume that we're on the worker thread? (The pointer - // will be used outside of the lock). - // And if we're on the worker thread, which is the only thread that modifies - // channels_, can we skip grabbing the lock? - rtc::CritScope cs(&crit_); ChannelMap::iterator iter = channels_.find(component); return (iter != channels_.end()) ? iter->second.get() : NULL; } bool Transport::HasChannels() { - rtc::CritScope cs(&crit_); return !channels_.empty(); } void Transport::DestroyChannel(int component) { - worker_thread_->Invoke(Bind( - &Transport::DestroyChannel_w, this, component)); -} - -void Transport::DestroyChannel_w(int component) { - ASSERT(worker_thread()->IsCurrent()); - ChannelMap::iterator iter = channels_.find(component); if (iter == channels_.end()) return; @@ -293,34 +279,30 @@ void Transport::DestroyChannel_w(int component) { iter->second.DecRef(); if (!iter->second.ref()) { impl = iter->second.get(); - rtc::CritScope cs(&crit_); channels_.erase(iter); } if (connect_requested_ && channels_.empty()) { // We're no longer attempting to connect. - signaling_thread()->Post(this, MSG_CONNECTING, NULL); + SignalConnecting(this); } if (impl) { - // Check in case the deleted channel was the only non-writable channel. - OnChannelWritableState(impl); DestroyTransportChannel(impl); + // Need to update aggregate state after destroying a channel, + // for example if it was the only one that wasn't yet writable. + UpdateWritableState(); + UpdateReceivingState(); + UpdateGatheringState(); + MaybeSignalCompleted(); } } void Transport::ConnectChannels() { - ASSERT(signaling_thread()->IsCurrent()); - worker_thread_->Invoke(Bind(&Transport::ConnectChannels_w, this)); -} - -void Transport::ConnectChannels_w() { - ASSERT(worker_thread()->IsCurrent()); if (connect_requested_ || channels_.empty()) return; connect_requested_ = true; - signaling_thread()->Post(this, MSG_CANDIDATEREADY, NULL); if (!local_description_) { // TOOD(mallinath) : TransportDescription(TD) shouldn't be generated here. @@ -329,38 +311,28 @@ void Transport::ConnectChannels_w() { // Session. // Session must generate local TD before remote candidates pushed when // initiate request initiated by the remote. - LOG(LS_INFO) << "Transport::ConnectChannels_w: No local description has " + LOG(LS_INFO) << "Transport::ConnectChannels: No local description has " << "been set. Will generate one."; - TransportDescription desc(std::vector(), - rtc::CreateRandomString(ICE_UFRAG_LENGTH), - rtc::CreateRandomString(ICE_PWD_LENGTH), - ICEMODE_FULL, CONNECTIONROLE_NONE, NULL, - Candidates()); - SetLocalTransportDescription_w(desc, CA_OFFER, NULL); + TransportDescription desc( + std::vector(), rtc::CreateRandomString(ICE_UFRAG_LENGTH), + rtc::CreateRandomString(ICE_PWD_LENGTH), ICEMODE_FULL, + CONNECTIONROLE_NONE, NULL, Candidates()); + SetLocalTransportDescription(desc, CA_OFFER, NULL); } - CallChannels_w(&TransportChannelImpl::Connect); - if (!channels_.empty()) { - signaling_thread()->Post(this, MSG_CONNECTING, NULL); + CallChannels(&TransportChannelImpl::Connect); + if (HasChannels()) { + SignalConnecting(this); } } -void Transport::OnConnecting_s() { - ASSERT(signaling_thread()->IsCurrent()); - SignalConnecting(this); +void Transport::MaybeStartGathering() { + if (connect_requested_) { + CallChannels(&TransportChannelImpl::MaybeStartGathering); + } } void Transport::DestroyAllChannels() { - ASSERT(signaling_thread()->IsCurrent()); - worker_thread_->Invoke(Bind(&Transport::DestroyAllChannels_w, this)); - worker_thread()->Clear(this); - signaling_thread()->Clear(this); - destroyed_ = true; -} - -void Transport::DestroyAllChannels_w() { - ASSERT(worker_thread()->IsCurrent()); - std::vector impls; for (auto& iter : channels_) { iter.second.DecRef(); @@ -368,27 +340,15 @@ void Transport::DestroyAllChannels_w() { impls.push_back(iter.second.get()); } - { - rtc::CritScope cs(&crit_); - channels_.clear(); + channels_.clear(); + + for (TransportChannelImpl* impl : impls) { + DestroyTransportChannel(impl); } - - for (size_t i = 0; i < impls.size(); ++i) - DestroyTransportChannel(impls[i]); + channels_destroyed_ = true; } -void Transport::OnSignalingReady() { - ASSERT(signaling_thread()->IsCurrent()); - if (destroyed_) return; - - worker_thread()->Post(this, MSG_ONSIGNALINGREADY, NULL); - - // Notify the subclass. - OnTransportSignalingReady(); -} - -void Transport::CallChannels_w(TransportChannelFunc func) { - ASSERT(worker_thread()->IsCurrent()); +void Transport::CallChannels(TransportChannelFunc func) { for (const auto& iter : channels_) { ((iter.second.get())->*func)(); } @@ -427,14 +387,7 @@ bool Transport::VerifyCandidate(const Candidate& cand, std::string* error) { bool Transport::GetStats(TransportStats* stats) { - ASSERT(signaling_thread()->IsCurrent()); - return worker_thread_->Invoke(Bind( - &Transport::GetStats_w, this, stats)); -} - -bool Transport::GetStats_w(TransportStats* stats) { - ASSERT(worker_thread()->IsCurrent()); - stats->content_name = content_name(); + stats->transport_name = name(); stats->channel_stats.clear(); for (auto iter : channels_) { ChannelMapEntry& entry = iter.second; @@ -450,82 +403,45 @@ bool Transport::GetStats_w(TransportStats* stats) { return true; } -bool Transport::GetSslRole(rtc::SSLRole* ssl_role) const { - return worker_thread_->Invoke(Bind( - &Transport::GetSslRole_w, this, ssl_role)); -} +bool Transport::AddRemoteCandidates(const std::vector& candidates, + std::string* error) { + ASSERT(!channels_destroyed_); + // Verify each candidate before passing down to transport layer. + for (const Candidate& cand : candidates) { + if (!VerifyCandidate(cand, error)) { + return false; + } + if (!HasChannel(cand.component())) { + *error = "Candidate has unknown component: " + cand.ToString() + + " for content: " + name(); + return false; + } + } -bool Transport::SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) { - return worker_thread_->Invoke(Bind( - &Transport::SetSslMaxProtocolVersion_w, this, version)); -} - -void Transport::OnRemoteCandidates(const std::vector& candidates) { for (std::vector::const_iterator iter = candidates.begin(); iter != candidates.end(); ++iter) { - OnRemoteCandidate(*iter); - } -} - -void Transport::OnRemoteCandidate(const Candidate& candidate) { - ASSERT(signaling_thread()->IsCurrent()); - if (destroyed_) return; - - if (!HasChannel(candidate.component())) { - LOG(LS_WARNING) << "Ignoring candidate for unknown component " - << candidate.component(); - return; - } - - ChannelParams* params = new ChannelParams(new Candidate(candidate)); - worker_thread()->Post(this, MSG_ONREMOTECANDIDATE, params); -} - -void Transport::OnRemoteCandidate_w(const Candidate& candidate) { - ASSERT(worker_thread()->IsCurrent()); - ChannelMap::iterator iter = channels_.find(candidate.component()); - // It's ok for a channel to go away while this message is in transit. - if (iter != channels_.end()) { - iter->second->OnCandidate(candidate); + TransportChannelImpl* channel = GetChannel(iter->component()); + if (channel != NULL) { + channel->AddRemoteCandidate(*iter); + } } + return true; } void Transport::OnChannelWritableState(TransportChannel* channel) { - ASSERT(worker_thread()->IsCurrent()); - signaling_thread()->Post(this, MSG_WRITESTATE, NULL); - - MaybeCompleted_w(); -} - -void Transport::OnChannelWritableState_s() { - ASSERT(signaling_thread()->IsCurrent()); - TransportState writable = GetTransportState_s(TRANSPORT_WRITABLE_STATE); - if (writable_ != writable) { - was_writable_ = (writable_ == TRANSPORT_STATE_ALL); - writable_ = writable; - SignalWritableState(this); - } + LOG(LS_INFO) << name() << " TransportChannel " << channel->component() + << " writability changed to " << channel->writable() + << ". Check if transport is complete."; + UpdateWritableState(); + MaybeSignalCompleted(); } void Transport::OnChannelReceivingState(TransportChannel* channel) { - ASSERT(worker_thread()->IsCurrent()); - signaling_thread()->Post(this, MSG_RECEIVINGSTATE); + UpdateReceivingState(); } -void Transport::OnChannelReceivingState_s() { - ASSERT(signaling_thread()->IsCurrent()); - TransportState receiving = GetTransportState_s(TRANSPORT_RECEIVING_STATE); - if (receiving_ != receiving) { - receiving_ = receiving; - SignalReceivingState(this); - } -} - -TransportState Transport::GetTransportState_s(TransportStateType state_type) { - ASSERT(signaling_thread()->IsCurrent()); - - rtc::CritScope cs(&crit_); +TransportState Transport::GetTransportState(TransportStateType state_type) { bool any = false; bool all = !channels_.empty(); for (const auto iter : channels_) { @@ -553,106 +469,44 @@ TransportState Transport::GetTransportState_s(TransportStateType state_type) { return TRANSPORT_STATE_NONE; } -void Transport::OnChannelRequestSignaling(TransportChannelImpl* channel) { - ASSERT(worker_thread()->IsCurrent()); - // Resetting ICE state for the channel. - ChannelMap::iterator iter = channels_.find(channel->component()); - if (iter != channels_.end()) - iter->second.set_candidates_allocated(false); - signaling_thread()->Post(this, MSG_REQUESTSIGNALING, nullptr); +void Transport::OnChannelGatheringState(TransportChannelImpl* channel) { + ASSERT(channels_.find(channel->component()) != channels_.end()); + UpdateGatheringState(); + if (gathering_state_ == kIceGatheringComplete) { + // If UpdateGatheringState brought us to kIceGatheringComplete, check if + // our connection state is also "Completed". Otherwise, there's no point in + // checking (since it would only produce log messages). + MaybeSignalCompleted(); + } } -void Transport::OnChannelRequestSignaling_s() { - ASSERT(signaling_thread()->IsCurrent()); - LOG(LS_INFO) << "Transport: " << content_name_ << ", allocating candidates"; - SignalRequestSignaling(this); -} - -void Transport::OnChannelCandidateReady(TransportChannelImpl* channel, - const Candidate& candidate) { +void Transport::OnChannelCandidateGathered(TransportChannelImpl* channel, + const Candidate& candidate) { // We should never signal peer-reflexive candidates. if (candidate.type() == PRFLX_PORT_TYPE) { ASSERT(false); return; } - ASSERT(worker_thread()->IsCurrent()); - rtc::CritScope cs(&crit_); - ready_candidates_.push_back(candidate); - - // We hold any messages until the client lets us connect. - if (connect_requested_) { - signaling_thread()->Post( - this, MSG_CANDIDATEREADY, NULL); - } -} - -void Transport::OnChannelCandidateReady_s() { - ASSERT(signaling_thread()->IsCurrent()); ASSERT(connect_requested_); - std::vector candidates; - { - rtc::CritScope cs(&crit_); - candidates.swap(ready_candidates_); - } - - // we do the deleting of Candidate* here to keep the new above and - // delete below close to each other - if (!candidates.empty()) { - SignalCandidatesReady(this, candidates); - } + candidates.push_back(candidate); + SignalCandidatesGathered(this, candidates); } void Transport::OnChannelRouteChange(TransportChannel* channel, const Candidate& remote_candidate) { - ASSERT(worker_thread()->IsCurrent()); - ChannelParams* params = new ChannelParams(new Candidate(remote_candidate)); - params->channel = static_cast(channel); - signaling_thread()->Post(this, MSG_ROUTECHANGE, params); -} - -void Transport::OnChannelRouteChange_s(const TransportChannel* channel, - const Candidate& remote_candidate) { - ASSERT(signaling_thread()->IsCurrent()); SignalRouteChange(this, remote_candidate.component(), remote_candidate); } -void Transport::OnChannelCandidatesAllocationDone( - TransportChannelImpl* channel) { - ASSERT(worker_thread()->IsCurrent()); - ChannelMap::iterator iter = channels_.find(channel->component()); - ASSERT(iter != channels_.end()); - LOG(LS_INFO) << "Transport: " << content_name_ << ", component " - << channel->component() << " allocation complete"; - - iter->second.set_candidates_allocated(true); - - // If all channels belonging to this Transport got signal, then - // forward this signal to upper layer. - // Can this signal arrive before all transport channels are created? - for (auto& iter : channels_) { - if (!iter.second.candidates_allocated()) - return; - } - signaling_thread_->Post(this, MSG_CANDIDATEALLOCATIONCOMPLETE); - - MaybeCompleted_w(); -} - -void Transport::OnChannelCandidatesAllocationDone_s() { - ASSERT(signaling_thread()->IsCurrent()); - LOG(LS_INFO) << "Transport: " << content_name_ << " allocation complete"; - SignalCandidatesAllocationDone(this); -} - void Transport::OnRoleConflict(TransportChannelImpl* channel) { - signaling_thread_->Post(this, MSG_ROLECONFLICT); + SignalRoleConflict(); } void Transport::OnChannelConnectionRemoved(TransportChannelImpl* channel) { - ASSERT(worker_thread()->IsCurrent()); - MaybeCompleted_w(); + LOG(LS_INFO) << name() << " TransportChannel " << channel->component() + << " connection removed. Check if transport is complete."; + MaybeSignalCompleted(); // Check if the state is now Failed. // Failed is only available in the Controlling ICE role. @@ -660,158 +514,97 @@ void Transport::OnChannelConnectionRemoved(TransportChannelImpl* channel) { return; } - ChannelMap::iterator iter = channels_.find(channel->component()); - ASSERT(iter != channels_.end()); - // Failed can only occur after candidate allocation has stopped. - if (!iter->second.candidates_allocated()) { + // Failed can only occur after candidate gathering has stopped. + if (channel->gathering_state() != kIceGatheringComplete) { return; } if (channel->GetState() == TransportChannelState::STATE_FAILED) { // A Transport has failed if any of its channels have no remaining // connections. - signaling_thread_->Post(this, MSG_FAILED); + SignalFailed(this); } } -void Transport::MaybeCompleted_w() { - ASSERT(worker_thread()->IsCurrent()); +void Transport::MaybeSignalCompleted() { + if (AllChannelsCompleted()) { + LOG(LS_INFO) << name() << " transport is complete" + << " because all the channels are complete."; + SignalCompleted(this); + } + // TODO(deadbeef): Should we do anything if we previously were completed, + // but now are not (if, for example, a new remote candidate is added)? +} - // When there is no channel created yet, calling this function could fire an - // IceConnectionCompleted event prematurely. - if (channels_.empty()) { - return; +void Transport::UpdateGatheringState() { + IceGatheringState new_state = kIceGatheringNew; + bool any_gathering = false; + bool all_complete = !channels_.empty(); + for (const auto& kv : channels_) { + any_gathering = + any_gathering || kv.second->gathering_state() != kIceGatheringNew; + all_complete = + all_complete && kv.second->gathering_state() == kIceGatheringComplete; + } + if (all_complete) { + new_state = kIceGatheringComplete; + } else if (any_gathering) { + new_state = kIceGatheringGathering; } - // A Transport's ICE process is completed if all of its channels are writable, - // have finished allocating candidates, and have pruned all but one of their - // connections. - for (const auto& iter : channels_) { - const TransportChannelImpl* channel = iter.second.get(); - if (!(channel->writable() && - channel->GetState() == TransportChannelState::STATE_COMPLETED && - channel->GetIceRole() == ICEROLE_CONTROLLING && - iter.second.candidates_allocated())) { - return; + if (gathering_state_ != new_state) { + gathering_state_ = new_state; + if (gathering_state_ == kIceGatheringGathering) { + LOG(LS_INFO) << "Transport: " << name_ << ", gathering candidates"; + } else if (gathering_state_ == kIceGatheringComplete) { + LOG(LS_INFO) << "Transport " << name() << " gathering complete."; } - } - - signaling_thread_->Post(this, MSG_COMPLETED); -} - -void Transport::SetIceRole_w(IceRole role) { - ASSERT(worker_thread()->IsCurrent()); - rtc::CritScope cs(&crit_); - ice_role_ = role; - for (auto& iter : channels_) { - iter.second->SetIceRole(ice_role_); + SignalGatheringState(this); } } -void Transport::SetRemoteIceMode_w(IceMode mode) { - ASSERT(worker_thread()->IsCurrent()); - remote_ice_mode_ = mode; - // Shouldn't channels be created after this method executed? - for (auto& iter : channels_) { - iter.second->SetRemoteIceMode(remote_ice_mode_); +void Transport::UpdateReceivingState() { + TransportState receiving = GetTransportState(TRANSPORT_RECEIVING_STATE); + if (receiving_ != receiving) { + receiving_ = receiving; + SignalReceivingState(this); } } -bool Transport::SetLocalTransportDescription_w( - const TransportDescription& desc, - ContentAction action, - std::string* error_desc) { - ASSERT(worker_thread()->IsCurrent()); - bool ret = true; - - if (!VerifyIceParams(desc)) { - return BadTransportDescription("Invalid ice-ufrag or ice-pwd length", - error_desc); +void Transport::UpdateWritableState() { + TransportState writable = GetTransportState(TRANSPORT_WRITABLE_STATE); + LOG(LS_INFO) << name() << " transport writable state changed? " << writable_ + << " => " << writable; + if (writable_ != writable) { + was_writable_ = (writable_ == TRANSPORT_STATE_ALL); + writable_ = writable; + SignalWritableState(this); } - - // TODO(tommi,pthatcher): I'm not sure why we need to grab this lock at this - // point. |local_description_| seems to always be modified on the worker - // thread, so we should be able to use it here without grabbing the lock. - // However, we _might_ need it before the call to reset() below? - // Raw access to |local_description_| is granted to derived transports outside - // of locking (see local_description() in the header file). - // The contract is that the derived implementations must be aware of when the - // description might change and do appropriate synchronization. - rtc::CritScope cs(&crit_); - if (local_description_ && IceCredentialsChanged(*local_description_, desc)) { - IceRole new_ice_role = (action == CA_OFFER) ? ICEROLE_CONTROLLING - : ICEROLE_CONTROLLED; - - // It must be called before ApplyLocalTransportDescription_w, which may - // trigger an ICE restart and depends on the new ICE role. - SetIceRole_w(new_ice_role); - } - - local_description_.reset(new TransportDescription(desc)); - - for (auto& iter : channels_) { - ret &= ApplyLocalTransportDescription_w(iter.second.get(), error_desc); - } - if (!ret) - return false; - - // If PRANSWER/ANSWER is set, we should decide transport protocol type. - if (action == CA_PRANSWER || action == CA_ANSWER) { - ret &= NegotiateTransportDescription_w(action, error_desc); - } - return ret; } -bool Transport::SetRemoteTransportDescription_w( - const TransportDescription& desc, - ContentAction action, - std::string* error_desc) { - bool ret = true; - - if (!VerifyIceParams(desc)) { - return BadTransportDescription("Invalid ice-ufrag or ice-pwd length", - error_desc); - } - - // TODO(tommi,pthatcher): See todo for local_description_ above. - rtc::CritScope cs(&crit_); - remote_description_.reset(new TransportDescription(desc)); - for (auto& iter : channels_) { - ret &= ApplyRemoteTransportDescription_w(iter.second.get(), error_desc); - } - - // If PRANSWER/ANSWER is set, we should decide transport protocol type. - if (action == CA_PRANSWER || action == CA_ANSWER) { - ret = NegotiateTransportDescription_w(CA_OFFER, error_desc); - } - return ret; -} - -bool Transport::ApplyLocalTransportDescription_w(TransportChannelImpl* ch, - std::string* error_desc) { - ASSERT(worker_thread()->IsCurrent()); +bool Transport::ApplyLocalTransportDescription(TransportChannelImpl* ch, + std::string* error_desc) { ch->SetIceCredentials(local_description_->ice_ufrag, local_description_->ice_pwd); return true; } -bool Transport::ApplyRemoteTransportDescription_w(TransportChannelImpl* ch, - std::string* error_desc) { +bool Transport::ApplyRemoteTransportDescription(TransportChannelImpl* ch, + std::string* error_desc) { ch->SetRemoteIceCredentials(remote_description_->ice_ufrag, remote_description_->ice_pwd); return true; } -bool Transport::ApplyNegotiatedTransportDescription_w( - TransportChannelImpl* channel, std::string* error_desc) { - ASSERT(worker_thread()->IsCurrent()); +bool Transport::ApplyNegotiatedTransportDescription( + TransportChannelImpl* channel, + std::string* error_desc) { channel->SetRemoteIceMode(remote_ice_mode_); return true; } -bool Transport::NegotiateTransportDescription_w(ContentAction local_role, - std::string* error_desc) { - ASSERT(worker_thread()->IsCurrent()); +bool Transport::NegotiateTransportDescription(ContentAction local_role, + std::string* error_desc) { // TODO(ekr@rtfm.com): This is ICE-specific stuff. Refactor into // P2PTransport. @@ -819,7 +612,7 @@ bool Transport::NegotiateTransportDescription_w(ContentAction local_role, // ice_lite, this local end point should take CONTROLLING role. if (ice_role_ == ICEROLE_CONTROLLED && remote_description_->ice_mode == ICEMODE_LITE) { - SetIceRole_w(ICEROLE_CONTROLLING); + SetIceRole(ICEROLE_CONTROLLING); } // Update remote ice_mode to all existing channels. @@ -831,57 +624,10 @@ bool Transport::NegotiateTransportDescription_w(ContentAction local_role, // creation, we have the negotiation state saved until a new // negotiation happens. for (auto& iter : channels_) { - if (!ApplyNegotiatedTransportDescription_w(iter.second.get(), error_desc)) + if (!ApplyNegotiatedTransportDescription(iter.second.get(), error_desc)) return false; } return true; } -void Transport::OnMessage(rtc::Message* msg) { - switch (msg->message_id) { - case MSG_ONSIGNALINGREADY: - CallChannels_w(&TransportChannelImpl::OnSignalingReady); - break; - case MSG_ONREMOTECANDIDATE: { - ChannelParams* params = static_cast(msg->pdata); - OnRemoteCandidate_w(*params->candidate); - delete params; - } - break; - case MSG_CONNECTING: - OnConnecting_s(); - break; - case MSG_WRITESTATE: - OnChannelWritableState_s(); - break; - case MSG_RECEIVINGSTATE: - OnChannelReceivingState_s(); - break; - case MSG_REQUESTSIGNALING: - OnChannelRequestSignaling_s(); - break; - case MSG_CANDIDATEREADY: - OnChannelCandidateReady_s(); - break; - case MSG_ROUTECHANGE: { - ChannelParams* params = static_cast(msg->pdata); - OnChannelRouteChange_s(params->channel, *params->candidate); - delete params; - } - break; - case MSG_CANDIDATEALLOCATIONCOMPLETE: - OnChannelCandidatesAllocationDone_s(); - break; - case MSG_ROLECONFLICT: - SignalRoleConflict(); - break; - case MSG_COMPLETED: - SignalCompleted(this); - break; - case MSG_FAILED: - SignalFailed(this); - break; - } -} - } // namespace cricket diff --git a/webrtc/p2p/base/transport.h b/webrtc/p2p/base/transport.h index 72a089501e..cd763ab3bc 100644 --- a/webrtc/p2p/base/transport.h +++ b/webrtc/p2p/base/transport.h @@ -15,15 +15,11 @@ // state changes (in order to update the manager's state), and forwards // requests to begin connecting or to reset to each of the channels. // -// On Threading: Transport performs work on both the signaling and worker -// threads. For subclasses, the rule is that all signaling related calls will -// be made on the signaling thread and all channel related calls (including -// signaling for a channel) will be made on the worker thread. When -// information needs to be sent between the two threads, this class should do -// the work (e.g., OnRemoteCandidate). +// On Threading: Transport performs work solely on the worker thread, and so +// its methods should only be called on the worker thread. // -// Note: Subclasses must call DestroyChannels() in their own constructors. -// It is not possible to do so here because the subclass constructor will +// Note: Subclasses must call DestroyChannels() in their own destructors. +// It is not possible to do so here because the subclass destructor will // already have run. #ifndef WEBRTC_P2P_BASE_TRANSPORT_H_ @@ -36,16 +32,11 @@ #include "webrtc/p2p/base/constants.h" #include "webrtc/p2p/base/sessiondescription.h" #include "webrtc/p2p/base/transportinfo.h" -#include "webrtc/base/criticalsection.h" #include "webrtc/base/messagequeue.h" #include "webrtc/base/rtccertificate.h" #include "webrtc/base/sigslot.h" #include "webrtc/base/sslstreamadapter.h" -namespace rtc { -class Thread; -} - namespace cricket { class PortAllocator; @@ -54,6 +45,26 @@ class TransportChannelImpl; typedef std::vector Candidates; +// TODO(deadbeef): Unify with PeerConnectionInterface::IceConnectionState +// once /talk/ and /webrtc/ are combined, and also switch to ENUM_NAME naming +// style. +enum IceConnectionState { + kIceConnectionConnecting = 0, + kIceConnectionFailed, + kIceConnectionConnected, // Writable, but still checking one or more + // connections + kIceConnectionCompleted, +}; + +// TODO(deadbeef): Unify with PeerConnectionInterface::IceConnectionState +// once /talk/ and /webrtc/ are combined, and also switch to ENUM_NAME naming +// style. +enum IceGatheringState { + kIceGatheringNew = 0, + kIceGatheringGathering, + kIceGatheringComplete, +}; + // For "writable" and "receiving", we need to differentiate between // none, all, and some. enum TransportState { @@ -124,7 +135,7 @@ typedef std::vector TransportChannelStatsList; // Information about the stats of a transport. struct TransportStats { - std::string content_name; + std::string transport_name; TransportChannelStatsList channel_stats; }; @@ -135,22 +146,13 @@ bool IceCredentialsChanged(const std::string& old_ufrag, const std::string& new_ufrag, const std::string& new_pwd); -class Transport : public rtc::MessageHandler, - public sigslot::has_slots<> { +class Transport : public sigslot::has_slots<> { public: - Transport(rtc::Thread* signaling_thread, - rtc::Thread* worker_thread, - const std::string& content_name, - PortAllocator* allocator); + Transport(const std::string& name, PortAllocator* allocator); virtual ~Transport(); - // Returns the signaling thread. The app talks to Transport on this thread. - rtc::Thread* signaling_thread() const { return signaling_thread_; } - // Returns the worker thread. The actual networking is done on this thread. - rtc::Thread* worker_thread() const { return worker_thread_; } - - // Returns the content_name of this transport. - const std::string& content_name() const { return content_name_; } + // Returns the name of this transport. + const std::string& name() const { return name_; } // Returns the port allocator object for this transport. PortAllocator* port_allocator() { return allocator_; } @@ -172,6 +174,14 @@ class Transport : public rtc::MessageHandler, return (receiving_ == TRANSPORT_STATE_SOME || receiving_ == TRANSPORT_STATE_ALL); } + bool ready_for_remote_candidates() const { + return local_description_set_ && remote_description_set_; + } + + bool AllChannelsCompleted() const; + bool AnyChannelFailed() const; + + IceGatheringState gathering_state() const { return gathering_state_; } sigslot::signal1 SignalWritableState; sigslot::signal1 SignalReceivingState; @@ -190,21 +200,23 @@ class Transport : public rtc::MessageHandler, void SetChannelReceivingTimeout(int timeout_ms); // Must be called before applying local session description. - void SetCertificate( - const rtc::scoped_refptr& certificate); + virtual void SetLocalCertificate( + const rtc::scoped_refptr& certificate) {} - // Get a copy of the local identity provided by SetIdentity. - bool GetCertificate(rtc::scoped_refptr* certificate); + // Get a copy of the local certificate provided by SetLocalCertificate. + virtual bool GetLocalCertificate( + rtc::scoped_refptr* certificate) { + return false; + } // Get a copy of the remote certificate in use by the specified channel. bool GetRemoteSSLCertificate(rtc::SSLCertificate** cert); // Create, destroy, and lookup the channels of this type by their components. TransportChannelImpl* CreateChannel(int component); - // Note: GetChannel may lead to race conditions, since the mutex is not held - // after the pointer is returned. + TransportChannelImpl* GetChannel(int component); - // Note: HasChannel does not lead to race conditions, unlike GetChannel. + bool HasChannel(int component) { return (NULL != GetChannel(component)); } @@ -212,7 +224,6 @@ class Transport : public rtc::MessageHandler, void DestroyChannel(int component); // Set the local TransportDescription to be used by TransportChannels. - // This should be called before ConnectChannels(). bool SetLocalTransportDescription(const TransportDescription& description, ContentAction action, std::string* error_desc); @@ -227,6 +238,11 @@ class Transport : public rtc::MessageHandler, void ConnectChannels(); sigslot::signal1 SignalConnecting; + // Tells channels to start gathering candidates if necessary. + // Should be called after ConnectChannels() has been called at least once, + // which will happen in SetLocalTransportDescription. + void MaybeStartGathering(); + // Resets all of the channels back to their initial state. They are no // longer connecting. void ResetChannels(); @@ -236,20 +252,15 @@ class Transport : public rtc::MessageHandler, bool GetStats(TransportStats* stats); - // Before any stanza is sent, the manager will request signaling. Once - // signaling is available, the client should call OnSignalingReady. Once - // this occurs, the transport (or its channels) can send any waiting stanzas. - // OnSignalingReady invokes OnTransportSignalingReady and then forwards this - // signal to each channel. - sigslot::signal1 SignalRequestSignaling; - void OnSignalingReady(); + sigslot::signal1 SignalGatheringState; // Handles sending of ready candidates and receiving of remote candidates. - sigslot::signal2&> SignalCandidatesReady; + sigslot::signal2&> + SignalCandidatesGathered; - sigslot::signal1 SignalCandidatesAllocationDone; - void OnRemoteCandidates(const std::vector& candidates); + // Called when one or more candidates are ready from the remote peer. + bool AddRemoteCandidates(const std::vector& candidates, + std::string* error); // If candidate is not acceptable, returns false and sets error. // Call this before calling OnRemoteCandidates. @@ -264,10 +275,12 @@ class Transport : public rtc::MessageHandler, // Forwards the signal from TransportChannel to BaseSession. sigslot::signal0<> SignalRoleConflict; - virtual bool GetSslRole(rtc::SSLRole* ssl_role) const; + virtual bool GetSslRole(rtc::SSLRole* ssl_role) const { return false; } // Must be called before channel is starting to connect. - virtual bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version); + virtual bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) { + return false; + } protected: // These are called by Create/DestroyChannel above in order to create or @@ -275,9 +288,6 @@ class Transport : public rtc::MessageHandler, virtual TransportChannelImpl* CreateTransportChannel(int component) = 0; virtual void DestroyTransportChannel(TransportChannelImpl* channel) = 0; - // Informs the subclass that we received the signaling ready message. - virtual void OnTransportSignalingReady() {} - // The current local transport description, for use by derived classes // when performing transport description negotiation. const TransportDescription* local_description() const { @@ -290,53 +300,37 @@ class Transport : public rtc::MessageHandler, return remote_description_.get(); } - virtual void SetCertificate_w( - const rtc::scoped_refptr& certificate) {} - - virtual bool GetCertificate_w( - rtc::scoped_refptr* certificate) { - return false; - } - // Pushes down the transport parameters from the local description, such // as the ICE ufrag and pwd. // Derived classes can override, but must call the base as well. - virtual bool ApplyLocalTransportDescription_w(TransportChannelImpl* channel, - std::string* error_desc); + virtual bool ApplyLocalTransportDescription(TransportChannelImpl* channel, + std::string* error_desc); // Pushes down remote ice credentials from the remote description to the // transport channel. - virtual bool ApplyRemoteTransportDescription_w(TransportChannelImpl* ch, - std::string* error_desc); + virtual bool ApplyRemoteTransportDescription(TransportChannelImpl* ch, + std::string* error_desc); // Negotiates the transport parameters based on the current local and remote // transport description, such as the ICE role to use, and whether DTLS // should be activated. // Derived classes can negotiate their specific parameters here, but must call // the base as well. - virtual bool NegotiateTransportDescription_w(ContentAction local_role, - std::string* error_desc); + virtual bool NegotiateTransportDescription(ContentAction local_role, + std::string* error_desc); // Pushes down the transport parameters obtained via negotiation. // Derived classes can set their specific parameters here, but must call the // base as well. - virtual bool ApplyNegotiatedTransportDescription_w( - TransportChannelImpl* channel, std::string* error_desc); - - virtual bool GetSslRole_w(rtc::SSLRole* ssl_role) const { - return false; - } - - virtual bool SetSslMaxProtocolVersion_w(rtc::SSLProtocolVersion version) { - return false; - } + virtual bool ApplyNegotiatedTransportDescription( + TransportChannelImpl* channel, + std::string* error_desc); private: struct ChannelMapEntry { - ChannelMapEntry() : impl_(NULL), candidates_allocated_(false), ref_(0) {} + ChannelMapEntry() : impl_(NULL), ref_(0) {} explicit ChannelMapEntry(TransportChannelImpl *impl) : impl_(impl), - candidates_allocated_(false), ref_(0) { } @@ -349,14 +343,9 @@ class Transport : public rtc::MessageHandler, TransportChannelImpl* get() const { return impl_; } TransportChannelImpl* operator->() const { return impl_; } - void set_candidates_allocated(bool status) { - candidates_allocated_ = status; - } - bool candidates_allocated() const { return candidates_allocated_; } - private: - TransportChannelImpl *impl_; - bool candidates_allocated_; + private: + TransportChannelImpl* impl_; int ref_; }; @@ -369,92 +358,55 @@ class Transport : public rtc::MessageHandler, // Called when the receiving state of a channel changes. void OnChannelReceivingState(TransportChannel* channel); - // Called when a channel requests signaling. - void OnChannelRequestSignaling(TransportChannelImpl* channel); + // Called when a channel starts finishes gathering candidates + void OnChannelGatheringState(TransportChannelImpl* channel); - // Called when a candidate is ready from remote peer. - void OnRemoteCandidate(const Candidate& candidate); // Called when a candidate is ready from channel. - void OnChannelCandidateReady(TransportChannelImpl* channel, - const Candidate& candidate); + void OnChannelCandidateGathered(TransportChannelImpl* channel, + const Candidate& candidate); void OnChannelRouteChange(TransportChannel* channel, const Candidate& remote_candidate); - void OnChannelCandidatesAllocationDone(TransportChannelImpl* channel); // Called when there is ICE role change. void OnRoleConflict(TransportChannelImpl* channel); // Called when the channel removes a connection. void OnChannelConnectionRemoved(TransportChannelImpl* channel); - // Dispatches messages to the appropriate handler (below). - void OnMessage(rtc::Message* msg); - - // These are versions of the above methods that are called only on a - // particular thread (s = signaling, w = worker). The above methods post or - // send a message to invoke this version. - TransportChannelImpl* CreateChannel_w(int component); - void DestroyChannel_w(int component); - void ConnectChannels_w(); - void ResetChannels_w(); - void DestroyAllChannels_w(); - void OnRemoteCandidate_w(const Candidate& candidate); - void OnChannelWritableState_s(); - void OnChannelReceivingState_s(); - void OnChannelRequestSignaling_s(); - void OnConnecting_s(); - void OnChannelRouteChange_s(const TransportChannel* channel, - const Candidate& remote_candidate); - void OnChannelCandidatesAllocationDone_s(); - // Helper function that invokes the given function on every channel. typedef void (TransportChannelImpl::* TransportChannelFunc)(); - void CallChannels_w(TransportChannelFunc func); + void CallChannels(TransportChannelFunc func); // Computes the AND and OR of the channel's read/write/receiving state // (argument picks the operation). - TransportState GetTransportState_s(TransportStateType type); - - void OnChannelCandidateReady_s(); - - void SetIceRole_w(IceRole role); - void SetRemoteIceMode_w(IceMode mode); - bool SetLocalTransportDescription_w(const TransportDescription& desc, - ContentAction action, - std::string* error_desc); - bool SetRemoteTransportDescription_w(const TransportDescription& desc, - ContentAction action, - std::string* error_desc); - bool GetStats_w(TransportStats* infos); - bool GetRemoteSSLCertificate_w(rtc::SSLCertificate** cert); - - void SetChannelReceivingTimeout_w(int timeout_ms); + TransportState GetTransportState(TransportStateType type); // Sends SignalCompleted if we are now in that state. - void MaybeCompleted_w(); + void MaybeSignalCompleted(); - rtc::Thread* const signaling_thread_; - rtc::Thread* const worker_thread_; - const std::string content_name_; + // Sends SignalGatheringState if gathering state changed + void UpdateGatheringState(); + + void UpdateWritableState(); + void UpdateReceivingState(); + + const std::string name_; PortAllocator* const allocator_; - bool destroyed_; - TransportState readable_; - TransportState writable_; - TransportState receiving_; - bool was_writable_; - bool connect_requested_; - IceRole ice_role_; - uint64 tiebreaker_; - IceMode remote_ice_mode_; - int channel_receiving_timeout_; + bool channels_destroyed_ = false; + TransportState readable_ = TRANSPORT_STATE_NONE; + TransportState writable_ = TRANSPORT_STATE_NONE; + TransportState receiving_ = TRANSPORT_STATE_NONE; + bool was_writable_ = false; + bool connect_requested_ = false; + IceRole ice_role_ = ICEROLE_UNKNOWN; + uint64 tiebreaker_ = 0; + IceMode remote_ice_mode_ = ICEMODE_FULL; + int channel_receiving_timeout_ = -1; rtc::scoped_ptr local_description_; rtc::scoped_ptr remote_description_; + bool local_description_set_ = false; + bool remote_description_set_ = false; + IceGatheringState gathering_state_ = kIceGatheringNew; - // TODO(tommi): Make sure we only use this on the worker thread. ChannelMap channels_; - // Buffers the ready_candidates so that SignalCanidatesReady can - // provide them in multiples. - std::vector ready_candidates_; - // Protects changes to channels and messages - rtc::CriticalSection crit_; RTC_DISALLOW_COPY_AND_ASSIGN(Transport); }; diff --git a/webrtc/p2p/base/transport_unittest.cc b/webrtc/p2p/base/transport_unittest.cc index 43b761a64c..9febfe33f7 100644 --- a/webrtc/p2p/base/transport_unittest.cc +++ b/webrtc/p2p/base/transport_unittest.cc @@ -11,8 +11,7 @@ #include "webrtc/base/fakesslidentity.h" #include "webrtc/base/gunit.h" #include "webrtc/base/network.h" -#include "webrtc/base/thread.h" -#include "webrtc/p2p/base/fakesession.h" +#include "webrtc/p2p/base/faketransportcontroller.h" #include "webrtc/p2p/base/p2ptransport.h" using cricket::Candidate; @@ -35,9 +34,7 @@ class TransportTest : public testing::Test, public sigslot::has_slots<> { public: TransportTest() - : thread_(rtc::Thread::Current()), - transport_(new FakeTransport( - thread_, thread_, "test content name", NULL)), + : transport_(new FakeTransport("test content name")), channel_(NULL), connecting_signalled_(false), completed_(false), @@ -73,7 +70,6 @@ class TransportTest : public testing::Test, failed_ = true; } - rtc::Thread* thread_; rtc::scoped_ptr transport_; FakeTransportChannel* channel_; bool connecting_signalled_; @@ -85,20 +81,7 @@ class TransportTest : public testing::Test, TEST_F(TransportTest, TestConnectChannelsDoesSignal) { EXPECT_TRUE(SetupChannel()); transport_->ConnectChannels(); - EXPECT_FALSE(connecting_signalled_); - - EXPECT_TRUE_WAIT(connecting_signalled_, 100); -} - -// Test that DestroyAllChannels kills any pending OnConnecting signals. -TEST_F(TransportTest, TestDestroyAllClearsPosts) { - EXPECT_TRUE(transport_->CreateChannel(1) != NULL); - - transport_->ConnectChannels(); - transport_->DestroyAllChannels(); - - thread_->ProcessMessages(0); - EXPECT_FALSE(connecting_signalled_); + EXPECT_TRUE(connecting_signalled_); } // This test verifies channels are created with proper ICE @@ -232,7 +215,7 @@ TEST_F(TransportTest, TestChannelCompletedAndFailed) { NULL)); channel_->SetConnectionCount(2); - channel_->SignalCandidatesAllocationDone(channel_); + channel_->SetCandidatesGatheringComplete(); channel_->SetWritable(true); EXPECT_TRUE_WAIT(transport_->all_channels_writable(), 100); // ICE is not yet completed because there is still more than one connection. diff --git a/webrtc/p2p/base/transportchannel.cc b/webrtc/p2p/base/transportchannel.cc index 5d5a7c98fe..97a41130c7 100644 --- a/webrtc/p2p/base/transportchannel.cc +++ b/webrtc/p2p/base/transportchannel.cc @@ -18,7 +18,7 @@ std::string TransportChannel::ToString() const { const char RECEIVING_ABBREV[2] = { '_', 'R' }; const char WRITABLE_ABBREV[2] = { '_', 'W' }; std::stringstream ss; - ss << "Channel[" << content_name_ << "|" << component_ << "|" + ss << "Channel[" << transport_name_ << "|" << component_ << "|" << RECEIVING_ABBREV[receiving_] << WRITABLE_ABBREV[writable_] << "]"; return ss.str(); } diff --git a/webrtc/p2p/base/transportchannel.h b/webrtc/p2p/base/transportchannel.h index f195a0edc2..5e7adfa9c7 100644 --- a/webrtc/p2p/base/transportchannel.h +++ b/webrtc/p2p/base/transportchannel.h @@ -37,14 +37,19 @@ enum PacketFlags { }; // Used to indicate channel's connection state. -enum TransportChannelState { STATE_CONNECTING, STATE_COMPLETED, STATE_FAILED }; +enum TransportChannelState { + STATE_INIT, + STATE_CONNECTING, // Will enter this state once a connection is created + STATE_COMPLETED, + STATE_FAILED +}; // A TransportChannel represents one logical stream of packets that are sent // between the two sides of a session. class TransportChannel : public sigslot::has_slots<> { public: - explicit TransportChannel(const std::string& content_name, int component) - : content_name_(content_name), + explicit TransportChannel(const std::string& transport_name, int component) + : transport_name_(transport_name), component_(component), writable_(false), receiving_(false) {} @@ -60,7 +65,7 @@ class TransportChannel : public sigslot::has_slots<> { // Returns the session id of this channel. virtual const std::string SessionId() const { return std::string(); } - const std::string& content_name() const { return content_name_; } + const std::string& transport_name() const { return transport_name_; } int component() const { return component_; } // Returns the states of this channel. Each time one of these states changes, @@ -146,10 +151,9 @@ class TransportChannel : public sigslot::has_slots<> { // Sets the receiving state, signaling if necessary. void set_receiving(bool receiving); - private: // Used mostly for debugging. - std::string content_name_; + std::string transport_name_; int component_; bool writable_; bool receiving_; diff --git a/webrtc/p2p/base/transportchannelimpl.h b/webrtc/p2p/base/transportchannelimpl.h index 3aca951a23..ddddf15a03 100644 --- a/webrtc/p2p/base/transportchannelimpl.h +++ b/webrtc/p2p/base/transportchannelimpl.h @@ -32,8 +32,9 @@ enum IceProtocolType { // client. class TransportChannelImpl : public TransportChannel { public: - explicit TransportChannelImpl(const std::string& content_name, int component) - : TransportChannel(content_name, component) {} + explicit TransportChannelImpl(const std::string& transport_name, + int component) + : TransportChannel(transport_name, component) {} // Returns the transport that created this channel. virtual Transport* GetTransport() = 0; @@ -63,11 +64,11 @@ class TransportChannelImpl : public TransportChannel { // Begins the process of attempting to make a connection to the other client. virtual void Connect() = 0; - // Allows an individual channel to request signaling and be notified when it - // is ready. This is useful if the individual named channels have need to - // send their own transport-info stanzas. - sigslot::signal1 SignalRequestSignaling; - virtual void OnSignalingReady() = 0; + // Start gathering candidates if not already started, or if an ICE restart + // occurred. + virtual void MaybeStartGathering() = 0; + + sigslot::signal1 SignalGatheringState; // Handles sending and receiving of candidates. The Transport // receives the candidates and may forward them to the relevant @@ -77,9 +78,11 @@ class TransportChannelImpl : public TransportChannel { // channel, they cannot return an error if the message is invalid. // It is assumed that the Transport will have checked validity // before forwarding. - sigslot::signal2 SignalCandidateReady; - virtual void OnCandidate(const Candidate& candidate) = 0; + sigslot::signal2 + SignalCandidateGathered; + virtual void AddRemoteCandidate(const Candidate& candidate) = 0; + + virtual IceGatheringState gathering_state() const = 0; // DTLS methods virtual bool SetLocalCertificate( @@ -92,9 +95,6 @@ class TransportChannelImpl : public TransportChannel { virtual bool SetSslRole(rtc::SSLRole role) = 0; - // TransportChannel is forwarding this signal from PortAllocatorSession. - sigslot::signal1 SignalCandidatesAllocationDone; - // Invoked when there is conflict in the ICE role between local and remote // agents. sigslot::signal1 SignalRoleConflict; diff --git a/webrtc/p2p/base/transportchannelproxy.cc b/webrtc/p2p/base/transportchannelproxy.cc deleted file mode 100644 index 74d1e1db76..0000000000 --- a/webrtc/p2p/base/transportchannelproxy.cc +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright 2004 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/p2p/base/transport.h" -#include "webrtc/p2p/base/transportchannelimpl.h" -#include "webrtc/p2p/base/transportchannelproxy.h" -#include "webrtc/base/common.h" -#include "webrtc/base/logging.h" -#include "webrtc/base/thread.h" - -namespace cricket { - -enum { - MSG_UPDATESTATE, -}; - -TransportChannelProxy::TransportChannelProxy(const std::string& content_name, - int component) - : TransportChannel(content_name, component), - impl_(NULL) { - worker_thread_ = rtc::Thread::Current(); -} - -TransportChannelProxy::~TransportChannelProxy() { - // Clearing any pending signal. - worker_thread_->Clear(this); - if (impl_) { - impl_->GetTransport()->DestroyChannel(impl_->component()); - } -} - -void TransportChannelProxy::SetImplementation(TransportChannelImpl* impl) { - ASSERT(rtc::Thread::Current() == worker_thread_); - - if (impl == impl_) { - // Ignore if the |impl| has already been set. - LOG(LS_WARNING) << "Ignored TransportChannelProxy::SetImplementation call " - << "with a same impl as the existing one."; - return; - } - - // Destroy any existing impl_. - if (impl_) { - impl_->GetTransport()->DestroyChannel(impl_->component()); - } - - // Adopt the supplied impl, and connect to its signals. - impl_ = impl; - - if (impl_) { - impl_->SignalWritableState.connect( - this, &TransportChannelProxy::OnWritableState); - impl_->SignalReceivingState.connect( - this, &TransportChannelProxy::OnReceivingState); - impl_->SignalReadPacket.connect( - this, &TransportChannelProxy::OnReadPacket); - impl_->SignalReadyToSend.connect( - this, &TransportChannelProxy::OnReadyToSend); - impl_->SignalRouteChange.connect( - this, &TransportChannelProxy::OnRouteChange); - for (const auto& pair : options_) { - impl_->SetOption(pair.first, pair.second); - } - - // Push down the SRTP ciphers, if any were set. - if (!pending_srtp_ciphers_.empty()) { - impl_->SetSrtpCiphers(pending_srtp_ciphers_); - } - } - - // Post ourselves a message to see if we need to fire state callbacks. - worker_thread_->Post(this, MSG_UPDATESTATE); -} - -int TransportChannelProxy::SendPacket(const char* data, size_t len, - const rtc::PacketOptions& options, - int flags) { - ASSERT(rtc::Thread::Current() == worker_thread_); - // Fail if we don't have an impl yet. - if (!impl_) { - return -1; - } - return impl_->SendPacket(data, len, options, flags); -} - -int TransportChannelProxy::SetOption(rtc::Socket::Option opt, int value) { - ASSERT(rtc::Thread::Current() == worker_thread_); - options_.push_back(OptionPair(opt, value)); - if (!impl_) { - return 0; - } - return impl_->SetOption(opt, value); -} - -bool TransportChannelProxy::GetOption(rtc::Socket::Option opt, int* value) { - ASSERT(rtc::Thread::Current() == worker_thread_); - if (impl_) { - return impl_->GetOption(opt, value); - } - - for (const auto& pair : options_) { - if (pair.first == opt) { - *value = pair.second; - return true; - } - } - return false; -} - -int TransportChannelProxy::GetError() { - ASSERT(rtc::Thread::Current() == worker_thread_); - if (!impl_) { - return 0; - } - return impl_->GetError(); -} - -TransportChannelState TransportChannelProxy::GetState() const { - ASSERT(rtc::Thread::Current() == worker_thread_); - if (!impl_) { - return TransportChannelState::STATE_CONNECTING; - } - return impl_->GetState(); -} - -bool TransportChannelProxy::GetStats(ConnectionInfos* infos) { - ASSERT(rtc::Thread::Current() == worker_thread_); - if (!impl_) { - return false; - } - return impl_->GetStats(infos); -} - -bool TransportChannelProxy::IsDtlsActive() const { - ASSERT(rtc::Thread::Current() == worker_thread_); - if (!impl_) { - return false; - } - return impl_->IsDtlsActive(); -} - -bool TransportChannelProxy::GetSslRole(rtc::SSLRole* role) const { - ASSERT(rtc::Thread::Current() == worker_thread_); - if (!impl_) { - return false; - } - return impl_->GetSslRole(role); -} - -bool TransportChannelProxy::SetSslRole(rtc::SSLRole role) { - ASSERT(rtc::Thread::Current() == worker_thread_); - if (!impl_) { - return false; - } - return impl_->SetSslRole(role); -} - -bool TransportChannelProxy::SetSrtpCiphers(const std::vector& - ciphers) { - ASSERT(rtc::Thread::Current() == worker_thread_); - pending_srtp_ciphers_ = ciphers; // Cache so we can send later, but always - // set so it stays consistent. - if (impl_) { - return impl_->SetSrtpCiphers(ciphers); - } - return true; -} - -bool TransportChannelProxy::GetSrtpCipher(std::string* cipher) { - ASSERT(rtc::Thread::Current() == worker_thread_); - if (!impl_) { - return false; - } - return impl_->GetSrtpCipher(cipher); -} - -bool TransportChannelProxy::GetSslCipher(std::string* cipher) { - ASSERT(rtc::Thread::Current() == worker_thread_); - if (!impl_) { - return false; - } - return impl_->GetSslCipher(cipher); -} - -rtc::scoped_refptr -TransportChannelProxy::GetLocalCertificate() const { - ASSERT(rtc::Thread::Current() == worker_thread_); - if (!impl_) { - return nullptr; - } - return impl_->GetLocalCertificate(); -} - -bool TransportChannelProxy::GetRemoteSSLCertificate( - rtc::SSLCertificate** cert) const { - ASSERT(rtc::Thread::Current() == worker_thread_); - if (!impl_) { - return false; - } - return impl_->GetRemoteSSLCertificate(cert); -} - -bool TransportChannelProxy::ExportKeyingMaterial(const std::string& label, - const uint8* context, - size_t context_len, - bool use_context, - uint8* result, - size_t result_len) { - ASSERT(rtc::Thread::Current() == worker_thread_); - if (!impl_) { - return false; - } - return impl_->ExportKeyingMaterial(label, context, context_len, use_context, - result, result_len); -} - -IceRole TransportChannelProxy::GetIceRole() const { - ASSERT(rtc::Thread::Current() == worker_thread_); - if (!impl_) { - return ICEROLE_UNKNOWN; - } - return impl_->GetIceRole(); -} - -void TransportChannelProxy::OnWritableState(TransportChannel* channel) { - ASSERT(rtc::Thread::Current() == worker_thread_); - ASSERT(channel == impl_); - set_writable(impl_->writable()); - // Note: SignalWritableState fired by set_writable. -} - -void TransportChannelProxy::OnReceivingState(TransportChannel* channel) { - ASSERT(rtc::Thread::Current() == worker_thread_); - ASSERT(channel == impl_); - set_receiving(impl_->receiving()); - // Note: SignalReceivingState fired by set_receiving. -} - -void TransportChannelProxy::OnReadPacket( - TransportChannel* channel, const char* data, size_t size, - const rtc::PacketTime& packet_time, int flags) { - ASSERT(rtc::Thread::Current() == worker_thread_); - ASSERT(channel == impl_); - SignalReadPacket(this, data, size, packet_time, flags); -} - -void TransportChannelProxy::OnReadyToSend(TransportChannel* channel) { - ASSERT(rtc::Thread::Current() == worker_thread_); - ASSERT(channel == impl_); - SignalReadyToSend(this); -} - -void TransportChannelProxy::OnRouteChange(TransportChannel* channel, - const Candidate& candidate) { - ASSERT(rtc::Thread::Current() == worker_thread_); - ASSERT(channel == impl_); - SignalRouteChange(this, candidate); -} - -void TransportChannelProxy::OnMessage(rtc::Message* msg) { - ASSERT(rtc::Thread::Current() == worker_thread_); - if (msg->message_id == MSG_UPDATESTATE) { - // If impl_ is already receiving or writable, push up those signals. - set_writable(impl_ ? impl_->writable() : false); - set_receiving(impl_ ? impl_->receiving() : false); - } -} - -} // namespace cricket diff --git a/webrtc/p2p/base/transportchannelproxy.h b/webrtc/p2p/base/transportchannelproxy.h deleted file mode 100644 index 80ee20aabb..0000000000 --- a/webrtc/p2p/base/transportchannelproxy.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2004 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 WEBRTC_P2P_BASE_TRANSPORTCHANNELPROXY_H_ -#define WEBRTC_P2P_BASE_TRANSPORTCHANNELPROXY_H_ - -#include -#include -#include - -#include "webrtc/p2p/base/transportchannel.h" -#include "webrtc/base/messagehandler.h" - -namespace rtc { -class Thread; -} - -namespace cricket { - -class TransportChannelImpl; - -// Proxies calls between the client and the transport channel implementation. -// This is needed because clients are allowed to create channels before the -// network negotiation is complete. Hence, we create a proxy up front, and -// when negotiation completes, connect the proxy to the implementaiton. -class TransportChannelProxy : public TransportChannel, - public rtc::MessageHandler { - public: - TransportChannelProxy(const std::string& content_name, - int component); - ~TransportChannelProxy() override; - - TransportChannelImpl* impl() { return impl_; } - - TransportChannelState GetState() const override; - - // Sets the implementation to which we will proxy. - void SetImplementation(TransportChannelImpl* impl); - - // Implementation of the TransportChannel interface. These simply forward to - // the implementation. - int SendPacket(const char* data, size_t len, - const rtc::PacketOptions& options, - int flags) override; - int SetOption(rtc::Socket::Option opt, int value) override; - bool GetOption(rtc::Socket::Option opt, int* value) override; - int GetError() override; - virtual IceRole GetIceRole() const; - bool GetStats(ConnectionInfos* infos) override; - bool IsDtlsActive() const override; - bool GetSslRole(rtc::SSLRole* role) const override; - virtual bool SetSslRole(rtc::SSLRole role); - bool SetSrtpCiphers(const std::vector& ciphers) override; - bool GetSrtpCipher(std::string* cipher) override; - bool GetSslCipher(std::string* cipher) override; - rtc::scoped_refptr GetLocalCertificate() const override; - bool GetRemoteSSLCertificate(rtc::SSLCertificate** cert) const override; - bool ExportKeyingMaterial(const std::string& label, - const uint8* context, - size_t context_len, - bool use_context, - uint8* result, - size_t result_len) override; - - private: - // Catch signals from the implementation channel. These just forward to the - // client (after updating our state to match). - void OnReceivingState(TransportChannel* channel); - void OnWritableState(TransportChannel* channel); - void OnReadPacket(TransportChannel* channel, const char* data, size_t size, - const rtc::PacketTime& packet_time, int flags); - void OnReadyToSend(TransportChannel* channel); - void OnRouteChange(TransportChannel* channel, const Candidate& candidate); - - void OnMessage(rtc::Message* message) override; - - typedef std::pair OptionPair; - typedef std::vector OptionList; - rtc::Thread* worker_thread_; - TransportChannelImpl* impl_; - OptionList options_; - std::vector pending_srtp_ciphers_; - - RTC_DISALLOW_COPY_AND_ASSIGN(TransportChannelProxy); -}; - -} // namespace cricket - -#endif // WEBRTC_P2P_BASE_TRANSPORTCHANNELPROXY_H_ diff --git a/webrtc/p2p/base/transportcontroller.cc b/webrtc/p2p/base/transportcontroller.cc new file mode 100644 index 0000000000..4d9e403d3b --- /dev/null +++ b/webrtc/p2p/base/transportcontroller.cc @@ -0,0 +1,575 @@ +/* + * Copyright 2015 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/p2p/base/transportcontroller.h" + +#include "webrtc/base/bind.h" +#include "webrtc/base/checks.h" +#include "webrtc/base/thread.h" +#include "webrtc/p2p/base/dtlstransport.h" +#include "webrtc/p2p/base/p2ptransport.h" + +namespace cricket { + +enum { + MSG_ICECONNECTIONSTATE, + MSG_RECEIVING, + MSG_ICEGATHERINGSTATE, + MSG_CANDIDATESGATHERED, +}; + +struct CandidatesData : public rtc::MessageData { + CandidatesData(const std::string& transport_name, + const Candidates& candidates) + : transport_name(transport_name), candidates(candidates) {} + + std::string transport_name; + Candidates candidates; +}; + +TransportController::TransportController(rtc::Thread* signaling_thread, + rtc::Thread* worker_thread, + PortAllocator* port_allocator) + : signaling_thread_(signaling_thread), + worker_thread_(worker_thread), + port_allocator_(port_allocator) {} + +TransportController::~TransportController() { + worker_thread_->Invoke( + rtc::Bind(&TransportController::DestroyAllTransports_w, this)); + signaling_thread_->Clear(this); +} + +bool TransportController::SetSslMaxProtocolVersion( + rtc::SSLProtocolVersion version) { + return worker_thread_->Invoke(rtc::Bind( + &TransportController::SetSslMaxProtocolVersion_w, this, version)); +} + +void TransportController::SetIceConnectionReceivingTimeout(int timeout_ms) { + worker_thread_->Invoke( + rtc::Bind(&TransportController::SetIceConnectionReceivingTimeout_w, this, + timeout_ms)); +} + +void TransportController::SetIceRole(IceRole ice_role) { + worker_thread_->Invoke( + rtc::Bind(&TransportController::SetIceRole_w, this, ice_role)); +} + +bool TransportController::GetSslRole(rtc::SSLRole* role) { + return worker_thread_->Invoke( + rtc::Bind(&TransportController::GetSslRole_w, this, role)); +} + +bool TransportController::SetLocalCertificate( + const rtc::scoped_refptr& certificate) { + return worker_thread_->Invoke(rtc::Bind( + &TransportController::SetLocalCertificate_w, this, certificate)); +} + +bool TransportController::GetLocalCertificate( + const std::string& transport_name, + rtc::scoped_refptr* certificate) { + return worker_thread_->Invoke( + rtc::Bind(&TransportController::GetLocalCertificate_w, this, + transport_name, certificate)); +} + +bool TransportController::GetRemoteSSLCertificate( + const std::string& transport_name, + rtc::SSLCertificate** cert) { + return worker_thread_->Invoke( + rtc::Bind(&TransportController::GetRemoteSSLCertificate_w, this, + transport_name, cert)); +} + +bool TransportController::SetLocalTransportDescription( + const std::string& transport_name, + const TransportDescription& tdesc, + ContentAction action, + std::string* err) { + return worker_thread_->Invoke( + rtc::Bind(&TransportController::SetLocalTransportDescription_w, this, + transport_name, tdesc, action, err)); +} + +bool TransportController::SetRemoteTransportDescription( + const std::string& transport_name, + const TransportDescription& tdesc, + ContentAction action, + std::string* err) { + return worker_thread_->Invoke( + rtc::Bind(&TransportController::SetRemoteTransportDescription_w, this, + transport_name, tdesc, action, err)); +} + +void TransportController::MaybeStartGathering() { + worker_thread_->Invoke( + rtc::Bind(&TransportController::MaybeStartGathering_w, this)); +} + +bool TransportController::AddRemoteCandidates(const std::string& transport_name, + const Candidates& candidates, + std::string* err) { + return worker_thread_->Invoke( + rtc::Bind(&TransportController::AddRemoteCandidates_w, this, + transport_name, candidates, err)); +} + +bool TransportController::ReadyForRemoteCandidates( + const std::string& transport_name) { + return worker_thread_->Invoke(rtc::Bind( + &TransportController::ReadyForRemoteCandidates_w, this, transport_name)); +} + +bool TransportController::GetStats(const std::string& transport_name, + TransportStats* stats) { + return worker_thread_->Invoke( + rtc::Bind(&TransportController::GetStats_w, this, transport_name, stats)); +} + +TransportChannel* TransportController::CreateTransportChannel_w( + const std::string& transport_name, + int component) { + RTC_DCHECK(worker_thread_->IsCurrent()); + + Transport* transport = GetOrCreateTransport_w(transport_name); + return transport->CreateChannel(component); +} + +void TransportController::DestroyTransportChannel_w( + const std::string& transport_name, + int component) { + RTC_DCHECK(worker_thread_->IsCurrent()); + + Transport* transport = GetTransport_w(transport_name); + if (!transport) { + ASSERT(false); + return; + } + transport->DestroyChannel(component); + + // Just as we create a Transport when its first channel is created, + // we delete it when its last channel is deleted. + if (!transport->HasChannels()) { + DestroyTransport_w(transport_name); + } +} + +const rtc::scoped_refptr& +TransportController::certificate_for_testing() { + return certificate_; +} + +Transport* TransportController::CreateTransport_w( + const std::string& transport_name) { + RTC_DCHECK(worker_thread_->IsCurrent()); + + Transport* transport = new DtlsTransport( + transport_name, port_allocator(), certificate_); + return transport; +} + +Transport* TransportController::GetTransport_w( + const std::string& transport_name) { + RTC_DCHECK(worker_thread_->IsCurrent()); + + auto iter = transports_.find(transport_name); + return (iter != transports_.end()) ? iter->second : nullptr; +} + +void TransportController::OnMessage(rtc::Message* pmsg) { + RTC_DCHECK(signaling_thread_->IsCurrent()); + + switch (pmsg->message_id) { + case MSG_ICECONNECTIONSTATE: { + rtc::TypedMessageData* data = + static_cast*>(pmsg->pdata); + SignalConnectionState(data->data()); + delete data; + break; + } + case MSG_RECEIVING: { + rtc::TypedMessageData* data = + static_cast*>(pmsg->pdata); + SignalReceiving(data->data()); + delete data; + break; + } + case MSG_ICEGATHERINGSTATE: { + rtc::TypedMessageData* data = + static_cast*>(pmsg->pdata); + SignalGatheringState(data->data()); + delete data; + break; + } + case MSG_CANDIDATESGATHERED: { + CandidatesData* data = static_cast(pmsg->pdata); + SignalCandidatesGathered(data->transport_name, data->candidates); + delete data; + break; + } + default: + ASSERT(false); + } +} + +Transport* TransportController::GetOrCreateTransport_w( + const std::string& transport_name) { + RTC_DCHECK(worker_thread_->IsCurrent()); + + Transport* transport = GetTransport_w(transport_name); + if (transport) { + return transport; + } + + transport = CreateTransport_w(transport_name); + // The stuff below happens outside of CreateTransport_w so that unit tests + // can override CreateTransport_w to return a different type of transport. + transport->SetSslMaxProtocolVersion(ssl_max_version_); + transport->SetChannelReceivingTimeout(ice_receiving_timeout_ms_); + transport->SetIceRole(ice_role_); + transport->SetIceTiebreaker(ice_tiebreaker_); + if (certificate_) { + transport->SetLocalCertificate(certificate_); + } + transport->SignalConnecting.connect( + this, &TransportController::OnTransportConnecting_w); + transport->SignalWritableState.connect( + this, &TransportController::OnTransportWritableState_w); + transport->SignalReceivingState.connect( + this, &TransportController::OnTransportReceivingState_w); + transport->SignalCompleted.connect( + this, &TransportController::OnTransportCompleted_w); + transport->SignalFailed.connect(this, + &TransportController::OnTransportFailed_w); + transport->SignalGatheringState.connect( + this, &TransportController::OnTransportGatheringState_w); + transport->SignalCandidatesGathered.connect( + this, &TransportController::OnTransportCandidatesGathered_w); + transport->SignalRoleConflict.connect( + this, &TransportController::OnTransportRoleConflict_w); + transports_[transport_name] = transport; + + return transport; +} + +void TransportController::DestroyTransport_w( + const std::string& transport_name) { + RTC_DCHECK(worker_thread_->IsCurrent()); + + auto iter = transports_.find(transport_name); + if (iter != transports_.end()) { + delete iter->second; + transports_.erase(transport_name); + } + // Destroying a transport may cause aggregate state to change. + UpdateAggregateStates_w(); +} + +void TransportController::DestroyAllTransports_w() { + RTC_DCHECK(worker_thread_->IsCurrent()); + + for (const auto& kv : transports_) { + delete kv.second; + } + transports_.clear(); +} + +bool TransportController::SetSslMaxProtocolVersion_w( + rtc::SSLProtocolVersion version) { + RTC_DCHECK(worker_thread_->IsCurrent()); + + // Max SSL version can only be set before transports are created. + if (!transports_.empty()) { + return false; + } + + ssl_max_version_ = version; + return true; +} + +void TransportController::SetIceConnectionReceivingTimeout_w(int timeout_ms) { + RTC_DCHECK(worker_thread_->IsCurrent()); + ice_receiving_timeout_ms_ = timeout_ms; + for (const auto& kv : transports_) { + kv.second->SetChannelReceivingTimeout(timeout_ms); + } +} + +void TransportController::SetIceRole_w(IceRole ice_role) { + RTC_DCHECK(worker_thread_->IsCurrent()); + ice_role_ = ice_role; + for (const auto& kv : transports_) { + kv.second->SetIceRole(ice_role_); + } +} + +bool TransportController::GetSslRole_w(rtc::SSLRole* role) { + RTC_DCHECK(worker_thread()->IsCurrent()); + + if (transports_.empty()) { + return false; + } + return transports_.begin()->second->GetSslRole(role); +} + +bool TransportController::SetLocalCertificate_w( + const rtc::scoped_refptr& certificate) { + RTC_DCHECK(worker_thread_->IsCurrent()); + + if (certificate_) { + return false; + } + if (!certificate) { + return false; + } + certificate_ = certificate; + + for (const auto& kv : transports_) { + kv.second->SetLocalCertificate(certificate_); + } + return true; +} + +bool TransportController::GetLocalCertificate_w( + const std::string& transport_name, + rtc::scoped_refptr* certificate) { + RTC_DCHECK(worker_thread_->IsCurrent()); + + Transport* t = GetTransport_w(transport_name); + if (!t) { + return false; + } + + return t->GetLocalCertificate(certificate); +} + +bool TransportController::GetRemoteSSLCertificate_w( + const std::string& transport_name, + rtc::SSLCertificate** cert) { + RTC_DCHECK(worker_thread_->IsCurrent()); + + Transport* t = GetTransport_w(transport_name); + if (!t) { + return false; + } + + return t->GetRemoteSSLCertificate(cert); +} + +bool TransportController::SetLocalTransportDescription_w( + const std::string& transport_name, + const TransportDescription& tdesc, + ContentAction action, + std::string* err) { + RTC_DCHECK(worker_thread()->IsCurrent()); + + Transport* transport = GetTransport_w(transport_name); + if (!transport) { + // If we didn't find a transport, that's not an error; + // it could have been deleted as a result of bundling. + // TODO(deadbeef): Make callers smarter so they won't attempt to set a + // description on a deleted transport. + return true; + } + + return transport->SetLocalTransportDescription(tdesc, action, err); +} + +bool TransportController::SetRemoteTransportDescription_w( + const std::string& transport_name, + const TransportDescription& tdesc, + ContentAction action, + std::string* err) { + RTC_DCHECK(worker_thread()->IsCurrent()); + + Transport* transport = GetTransport_w(transport_name); + if (!transport) { + // If we didn't find a transport, that's not an error; + // it could have been deleted as a result of bundling. + // TODO(deadbeef): Make callers smarter so they won't attempt to set a + // description on a deleted transport. + return true; + } + + return transport->SetRemoteTransportDescription(tdesc, action, err); +} + +void TransportController::MaybeStartGathering_w() { + for (const auto& kv : transports_) { + kv.second->MaybeStartGathering(); + } +} + +bool TransportController::AddRemoteCandidates_w( + const std::string& transport_name, + const Candidates& candidates, + std::string* err) { + RTC_DCHECK(worker_thread()->IsCurrent()); + + Transport* transport = GetTransport_w(transport_name); + if (!transport) { + // If we didn't find a transport, that's not an error; + // it could have been deleted as a result of bundling. + return true; + } + + return transport->AddRemoteCandidates(candidates, err); +} + +bool TransportController::ReadyForRemoteCandidates_w( + const std::string& transport_name) { + RTC_DCHECK(worker_thread()->IsCurrent()); + + Transport* transport = GetTransport_w(transport_name); + if (!transport) { + return false; + } + return transport->ready_for_remote_candidates(); +} + +bool TransportController::GetStats_w(const std::string& transport_name, + TransportStats* stats) { + RTC_DCHECK(worker_thread()->IsCurrent()); + + Transport* transport = GetTransport_w(transport_name); + if (!transport) { + return false; + } + return transport->GetStats(stats); +} + +void TransportController::OnTransportConnecting_w(Transport* transport) { + RTC_DCHECK(worker_thread_->IsCurrent()); + UpdateAggregateStates_w(); +} + +void TransportController::OnTransportWritableState_w(Transport* transport) { + RTC_DCHECK(worker_thread_->IsCurrent()); + UpdateAggregateStates_w(); +} + +void TransportController::OnTransportReceivingState_w(Transport* transport) { + RTC_DCHECK(worker_thread_->IsCurrent()); + UpdateAggregateStates_w(); +} + +void TransportController::OnTransportCompleted_w(Transport* transport) { + RTC_DCHECK(worker_thread_->IsCurrent()); + UpdateAggregateStates_w(); +} + +void TransportController::OnTransportFailed_w(Transport* transport) { + RTC_DCHECK(worker_thread_->IsCurrent()); + UpdateAggregateStates_w(); +} + +void TransportController::OnTransportGatheringState_w(Transport* transport) { + RTC_DCHECK(worker_thread_->IsCurrent()); + UpdateAggregateStates_w(); +} + +void TransportController::OnTransportCandidatesGathered_w( + Transport* transport, + const std::vector& candidates) { + RTC_DCHECK(worker_thread_->IsCurrent()); + CandidatesData* data = new CandidatesData(transport->name(), candidates); + signaling_thread_->Post(this, MSG_CANDIDATESGATHERED, data); +} + +void TransportController::OnTransportRoleConflict_w() { + RTC_DCHECK(worker_thread_->IsCurrent()); + + if (ice_role_switch_) { + LOG(LS_WARNING) << "Repeat of role conflict signal from Transport."; + return; + } + + ice_role_switch_ = true; + IceRole reversed_role = (ice_role_ == ICEROLE_CONTROLLING) + ? ICEROLE_CONTROLLED + : ICEROLE_CONTROLLING; + for (const auto& kv : transports_) { + kv.second->SetIceRole(reversed_role); + } +} + +void TransportController::UpdateAggregateStates_w() { + RTC_DCHECK(worker_thread_->IsCurrent()); + + IceConnectionState new_connection_state = kIceConnectionConnecting; + IceGatheringState new_gathering_state = kIceGatheringNew; + bool any_receiving = false; + bool any_failed = false; + bool all_connected = HasChannels_w(); + bool all_completed = HasChannels_w(); + bool any_gathering = false; + bool all_done_gathering = HasChannels_w(); + for (const auto& kv : transports_) { + // Ignore transports without channels since they're about to be deleted, + // and their state is meaningless. + if (!kv.second->HasChannels()) { + continue; + } + any_receiving = any_receiving || kv.second->any_channel_receiving(); + any_failed = any_failed || kv.second->AnyChannelFailed(); + all_connected = all_connected && kv.second->all_channels_writable(); + all_completed = all_completed && kv.second->AllChannelsCompleted(); + any_gathering = + any_gathering || kv.second->gathering_state() != kIceGatheringNew; + all_done_gathering = all_done_gathering && + kv.second->gathering_state() == kIceGatheringComplete; + } + + if (any_failed) { + new_connection_state = kIceConnectionFailed; + } else if (all_completed) { + new_connection_state = kIceConnectionCompleted; + } else if (all_connected) { + new_connection_state = kIceConnectionConnected; + } + if (connection_state_ != new_connection_state) { + connection_state_ = new_connection_state; + signaling_thread_->Post( + this, MSG_ICECONNECTIONSTATE, + new rtc::TypedMessageData(new_connection_state)); + } + + if (receiving_ != any_receiving) { + receiving_ = any_receiving; + signaling_thread_->Post(this, MSG_RECEIVING, + new rtc::TypedMessageData(any_receiving)); + } + + if (all_done_gathering) { + new_gathering_state = kIceGatheringComplete; + } else if (any_gathering) { + new_gathering_state = kIceGatheringGathering; + } + if (gathering_state_ != new_gathering_state) { + gathering_state_ = new_gathering_state; + signaling_thread_->Post( + this, MSG_ICEGATHERINGSTATE, + new rtc::TypedMessageData(new_gathering_state)); + } +} + +bool TransportController::HasChannels_w() { + for (const auto& kv : transports_) { + if (kv.second->HasChannels()) { + return true; + } + } + return false; +} + +} // namespace cricket diff --git a/webrtc/p2p/base/transportcontroller.h b/webrtc/p2p/base/transportcontroller.h new file mode 100644 index 0000000000..e31eb83aff --- /dev/null +++ b/webrtc/p2p/base/transportcontroller.h @@ -0,0 +1,196 @@ +/* + * Copyright 2015 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 WEBRTC_P2P_BASE_TRANSPORTCONTROLLER_H_ +#define WEBRTC_P2P_BASE_TRANSPORTCONTROLLER_H_ + +#include +#include +#include + +#include "webrtc/base/sigslot.h" +#include "webrtc/base/sslstreamadapter.h" +#include "webrtc/p2p/base/candidate.h" +#include "webrtc/p2p/base/transport.h" + +namespace rtc { +class Thread; +} + +namespace cricket { + +class TransportController : public sigslot::has_slots<>, + public rtc::MessageHandler { + public: + TransportController(rtc::Thread* signaling_thread, + rtc::Thread* worker_thread, + PortAllocator* port_allocator); + + virtual ~TransportController(); + + rtc::Thread* signaling_thread() const { return signaling_thread_; } + rtc::Thread* worker_thread() const { return worker_thread_; } + + PortAllocator* port_allocator() const { return port_allocator_; } + + // Can only be set before transports are created. + // TODO(deadbeef): Make this an argument to the constructor once BaseSession + // and WebRtcSession are combined + bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version); + + void SetIceConnectionReceivingTimeout(int timeout_ms); + void SetIceRole(IceRole ice_role); + + // TODO(deadbeef) - Return role of each transport, as role may differ from + // one another. + // In current implementaion we just return the role of the first transport + // alphabetically. + bool GetSslRole(rtc::SSLRole* role); + + // Specifies the identity to use in this session. + // Can only be called once. + bool SetLocalCertificate( + const rtc::scoped_refptr& certificate); + bool GetLocalCertificate( + const std::string& transport_name, + rtc::scoped_refptr* certificate); + // Caller owns returned certificate + bool GetRemoteSSLCertificate(const std::string& transport_name, + rtc::SSLCertificate** cert); + bool SetLocalTransportDescription(const std::string& transport_name, + const TransportDescription& tdesc, + ContentAction action, + std::string* err); + bool SetRemoteTransportDescription(const std::string& transport_name, + const TransportDescription& tdesc, + ContentAction action, + std::string* err); + // Start gathering candidates for any new transports, or transports doing an + // ICE restart. + void MaybeStartGathering(); + bool AddRemoteCandidates(const std::string& transport_name, + const Candidates& candidates, + std::string* err); + bool ReadyForRemoteCandidates(const std::string& transport_name); + bool GetStats(const std::string& transport_name, TransportStats* stats); + + virtual TransportChannel* CreateTransportChannel_w( + const std::string& transport_name, + int component); + virtual void DestroyTransportChannel_w(const std::string& transport_name, + int component); + + // All of these signals are fired on the signalling thread. + + // If any transport failed => failed, + // Else if all completed => completed, + // Else if all connected => connected, + // Else => connecting + sigslot::signal1 SignalConnectionState; + + // Receiving if any transport is receiving + sigslot::signal1 SignalReceiving; + + // If all transports done gathering => complete, + // Else if any are gathering => gathering, + // Else => new + sigslot::signal1 SignalGatheringState; + + // (transport_name, candidates) + sigslot::signal2 + SignalCandidatesGathered; + + // for unit test + const rtc::scoped_refptr& certificate_for_testing(); + + protected: + // Protected and virtual so we can override it in unit tests. + virtual Transport* CreateTransport_w(const std::string& transport_name); + + // For unit tests + const std::map& transports() { return transports_; } + Transport* GetTransport_w(const std::string& transport_name); + + private: + void OnMessage(rtc::Message* pmsg) override; + + Transport* GetOrCreateTransport_w(const std::string& transport_name); + void DestroyTransport_w(const std::string& transport_name); + void DestroyAllTransports_w(); + + bool SetSslMaxProtocolVersion_w(rtc::SSLProtocolVersion version); + void SetIceConnectionReceivingTimeout_w(int timeout_ms); + void SetIceRole_w(IceRole ice_role); + bool GetSslRole_w(rtc::SSLRole* role); + bool SetLocalCertificate_w( + const rtc::scoped_refptr& certificate); + bool GetLocalCertificate_w( + const std::string& transport_name, + rtc::scoped_refptr* certificate); + bool GetRemoteSSLCertificate_w(const std::string& transport_name, + rtc::SSLCertificate** cert); + bool SetLocalTransportDescription_w(const std::string& transport_name, + const TransportDescription& tdesc, + ContentAction action, + std::string* err); + bool SetRemoteTransportDescription_w(const std::string& transport_name, + const TransportDescription& tdesc, + ContentAction action, + std::string* err); + void MaybeStartGathering_w(); + bool AddRemoteCandidates_w(const std::string& transport_name, + const Candidates& candidates, + std::string* err); + bool ReadyForRemoteCandidates_w(const std::string& transport_name); + bool GetStats_w(const std::string& transport_name, TransportStats* stats); + + // Handlers for signals from Transport. + void OnTransportConnecting_w(Transport* transport); + void OnTransportWritableState_w(Transport* transport); + void OnTransportReceivingState_w(Transport* transport); + void OnTransportCompleted_w(Transport* transport); + void OnTransportFailed_w(Transport* transport); + void OnTransportGatheringState_w(Transport* transport); + void OnTransportCandidatesGathered_w( + Transport* transport, + const std::vector& candidates); + void OnTransportRoleConflict_w(); + + void UpdateAggregateStates_w(); + bool HasChannels_w(); + + rtc::Thread* const signaling_thread_ = nullptr; + rtc::Thread* const worker_thread_ = nullptr; + typedef std::map TransportMap; + TransportMap transports_; + + PortAllocator* const port_allocator_ = nullptr; + rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_10; + + // Aggregate state for Transports + IceConnectionState connection_state_ = kIceConnectionConnecting; + bool receiving_ = false; + IceGatheringState gathering_state_ = kIceGatheringNew; + + // TODO(deadbeef): Move the fields below down to the transports themselves + + // Timeout value in milliseconds for which no ICE connection receives + // any packets + int ice_receiving_timeout_ms_ = -1; + IceRole ice_role_ = ICEROLE_CONTROLLING; + // Flag which will be set to true after the first role switch + bool ice_role_switch_ = false; + uint64 ice_tiebreaker_ = rtc::CreateRandomId64(); + rtc::scoped_refptr certificate_; +}; + +} // namespace cricket + +#endif // WEBRTC_P2P_BASE_TRANSPORTCONTROLLER_H_ diff --git a/webrtc/p2p/base/transportcontroller_unittest.cc b/webrtc/p2p/base/transportcontroller_unittest.cc new file mode 100644 index 0000000000..c78352591b --- /dev/null +++ b/webrtc/p2p/base/transportcontroller_unittest.cc @@ -0,0 +1,679 @@ +/* + * Copyright 2015 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 + +#include "webrtc/base/fakesslidentity.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/helpers.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/sslidentity.h" +#include "webrtc/base/thread.h" +#include "webrtc/p2p/base/dtlstransportchannel.h" +#include "webrtc/p2p/base/faketransportcontroller.h" +#include "webrtc/p2p/base/p2ptransportchannel.h" +#include "webrtc/p2p/base/portallocator.h" +#include "webrtc/p2p/base/transportcontroller.h" +#include "webrtc/p2p/client/fakeportallocator.h" + +static const int kTimeout = 100; +static const char kIceUfrag1[] = "TESTICEUFRAG0001"; +static const char kIcePwd1[] = "TESTICEPWD00000000000001"; +static const char kIceUfrag2[] = "TESTICEUFRAG0002"; +static const char kIcePwd2[] = "TESTICEPWD00000000000002"; + +using cricket::Candidate; +using cricket::Candidates; +using cricket::FakeTransportChannel; +using cricket::FakeTransportController; +using cricket::IceConnectionState; +using cricket::IceGatheringState; +using cricket::TransportChannel; +using cricket::TransportController; +using cricket::TransportDescription; +using cricket::TransportStats; + +// Only subclassing from FakeTransportController because currently that's the +// only way to have a TransportController with fake TransportChannels. +// +// TODO(deadbeef): Change this once the Transport/TransportChannel class +// heirarchy is cleaned up, and we can pass a "TransportChannelFactory" or +// something similar into TransportController. +typedef FakeTransportController TransportControllerForTest; + +class TransportControllerTest : public testing::Test, + public sigslot::has_slots<> { + public: + TransportControllerTest() + : transport_controller_(new TransportControllerForTest()), + signaling_thread_(rtc::Thread::Current()) { + ConnectTransportControllerSignals(); + } + + void CreateTransportControllerWithWorkerThread() { + if (!worker_thread_) { + worker_thread_.reset(new rtc::Thread()); + worker_thread_->Start(); + } + transport_controller_.reset( + new TransportControllerForTest(worker_thread_.get())); + ConnectTransportControllerSignals(); + } + + void ConnectTransportControllerSignals() { + transport_controller_->SignalConnectionState.connect( + this, &TransportControllerTest::OnConnectionState); + transport_controller_->SignalReceiving.connect( + this, &TransportControllerTest::OnReceiving); + transport_controller_->SignalGatheringState.connect( + this, &TransportControllerTest::OnGatheringState); + transport_controller_->SignalCandidatesGathered.connect( + this, &TransportControllerTest::OnCandidatesGathered); + } + + FakeTransportChannel* CreateChannel(const std::string& content, + int component) { + TransportChannel* channel = + transport_controller_->CreateTransportChannel_w(content, component); + return static_cast(channel); + } + + void DestroyChannel(const std::string& content, int component) { + transport_controller_->DestroyTransportChannel_w(content, component); + } + + Candidate CreateCandidate(int component) { + Candidate c; + c.set_address(rtc::SocketAddress("192.168.1.1", 8000)); + c.set_component(1); + c.set_protocol(cricket::UDP_PROTOCOL_NAME); + c.set_priority(1); + return c; + } + + // Used for thread hopping test. + void CreateChannelsAndCompleteConnectionOnWorkerThread() { + worker_thread_->Invoke(rtc::Bind( + &TransportControllerTest::CreateChannelsAndCompleteConnection_w, this)); + } + + void CreateChannelsAndCompleteConnection_w() { + transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING); + FakeTransportChannel* channel1 = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel1); + FakeTransportChannel* channel2 = CreateChannel("video", 1); + ASSERT_NE(nullptr, channel2); + + TransportDescription local_desc( + std::vector(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL, + cricket::CONNECTIONROLE_ACTPASS, nullptr, Candidates()); + std::string err; + transport_controller_->SetLocalTransportDescription( + "audio", local_desc, cricket::CA_OFFER, &err); + transport_controller_->SetLocalTransportDescription( + "video", local_desc, cricket::CA_OFFER, &err); + transport_controller_->MaybeStartGathering(); + channel1->SignalCandidateGathered(channel1, CreateCandidate(1)); + channel2->SignalCandidateGathered(channel2, CreateCandidate(1)); + channel1->SetCandidatesGatheringComplete(); + channel2->SetCandidatesGatheringComplete(); + channel1->SetConnectionCount(2); + channel2->SetConnectionCount(2); + channel1->SetReceiving(true); + channel2->SetReceiving(true); + channel1->SetWritable(true); + channel2->SetWritable(true); + channel1->SetConnectionCount(1); + channel2->SetConnectionCount(1); + } + + protected: + void OnConnectionState(IceConnectionState state) { + if (!signaling_thread_->IsCurrent()) { + signaled_on_non_signaling_thread_ = true; + } + connection_state_ = state; + ++connection_state_signal_count_; + } + + void OnReceiving(bool receiving) { + if (!signaling_thread_->IsCurrent()) { + signaled_on_non_signaling_thread_ = true; + } + receiving_ = receiving; + ++receiving_signal_count_; + } + + void OnGatheringState(IceGatheringState state) { + if (!signaling_thread_->IsCurrent()) { + signaled_on_non_signaling_thread_ = true; + } + gathering_state_ = state; + ++gathering_state_signal_count_; + } + + void OnCandidatesGathered(const std::string& transport_name, + const Candidates& candidates) { + if (!signaling_thread_->IsCurrent()) { + signaled_on_non_signaling_thread_ = true; + } + candidates_[transport_name].insert(candidates_[transport_name].end(), + candidates.begin(), candidates.end()); + ++candidates_signal_count_; + } + + rtc::scoped_ptr worker_thread_; // Not used for most tests. + rtc::scoped_ptr transport_controller_; + + // Information received from signals from transport controller. + IceConnectionState connection_state_ = cricket::kIceConnectionConnecting; + bool receiving_ = false; + IceGatheringState gathering_state_ = cricket::kIceGatheringNew; + // transport_name => candidates + std::map candidates_; + // Counts of each signal emitted. + int connection_state_signal_count_ = 0; + int receiving_signal_count_ = 0; + int gathering_state_signal_count_ = 0; + int candidates_signal_count_ = 0; + + // Used to make sure signals only come on signaling thread. + rtc::Thread* const signaling_thread_ = nullptr; + bool signaled_on_non_signaling_thread_ = false; +}; + +TEST_F(TransportControllerTest, TestSetIceReceivingTimeout) { + FakeTransportChannel* channel1 = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel1); + + transport_controller_->SetIceConnectionReceivingTimeout(1000); + EXPECT_EQ(1000, channel1->receiving_timeout()); + + // Test that value stored in controller is applied to new channels. + FakeTransportChannel* channel2 = CreateChannel("video", 1); + ASSERT_NE(nullptr, channel2); + EXPECT_EQ(1000, channel2->receiving_timeout()); +} + +TEST_F(TransportControllerTest, TestSetSslMaxProtocolVersion) { + EXPECT_TRUE(transport_controller_->SetSslMaxProtocolVersion( + rtc::SSL_PROTOCOL_DTLS_12)); + FakeTransportChannel* channel = CreateChannel("audio", 1); + + ASSERT_NE(nullptr, channel); + EXPECT_EQ(rtc::SSL_PROTOCOL_DTLS_12, channel->ssl_max_protocol_version()); + + // Setting max version after transport is created should fail. + EXPECT_FALSE(transport_controller_->SetSslMaxProtocolVersion( + rtc::SSL_PROTOCOL_DTLS_10)); +} + +TEST_F(TransportControllerTest, TestSetIceRole) { + FakeTransportChannel* channel1 = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel1); + + transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING); + EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel1->GetIceRole()); + transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLED); + EXPECT_EQ(cricket::ICEROLE_CONTROLLED, channel1->GetIceRole()); + + // Test that value stored in controller is applied to new channels. + FakeTransportChannel* channel2 = CreateChannel("video", 1); + ASSERT_NE(nullptr, channel2); + EXPECT_EQ(cricket::ICEROLE_CONTROLLED, channel2->GetIceRole()); +} + +// Test that when one channel encounters a role conflict, the ICE role is +// swapped on every channel. +TEST_F(TransportControllerTest, TestIceRoleConflict) { + FakeTransportChannel* channel1 = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel1); + FakeTransportChannel* channel2 = CreateChannel("video", 1); + ASSERT_NE(nullptr, channel2); + + transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING); + EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel1->GetIceRole()); + EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel2->GetIceRole()); + + channel1->SignalRoleConflict(channel1); + EXPECT_EQ(cricket::ICEROLE_CONTROLLED, channel1->GetIceRole()); + EXPECT_EQ(cricket::ICEROLE_CONTROLLED, channel2->GetIceRole()); +} + +TEST_F(TransportControllerTest, TestGetSslRole) { + FakeTransportChannel* channel = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel); + ASSERT_TRUE(channel->SetSslRole(rtc::SSL_CLIENT)); + rtc::SSLRole role; + EXPECT_TRUE(transport_controller_->GetSslRole(&role)); + EXPECT_EQ(rtc::SSL_CLIENT, role); +} + +TEST_F(TransportControllerTest, TestSetAndGetLocalCertificate) { + rtc::scoped_refptr certificate1 = + rtc::RTCCertificate::Create( + rtc::scoped_ptr( + rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT)) + .Pass()); + rtc::scoped_refptr certificate2 = + rtc::RTCCertificate::Create( + rtc::scoped_ptr( + rtc::SSLIdentity::Generate("session2", rtc::KT_DEFAULT)) + .Pass()); + rtc::scoped_refptr returned_certificate; + + FakeTransportChannel* channel1 = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel1); + + EXPECT_TRUE(transport_controller_->SetLocalCertificate(certificate1)); + EXPECT_TRUE(transport_controller_->GetLocalCertificate( + "audio", &returned_certificate)); + EXPECT_EQ(certificate1->identity()->certificate().ToPEMString(), + returned_certificate->identity()->certificate().ToPEMString()); + + // Should fail if called for a nonexistant transport. + EXPECT_FALSE(transport_controller_->GetLocalCertificate( + "video", &returned_certificate)); + + // Test that identity stored in controller is applied to new channels. + FakeTransportChannel* channel2 = CreateChannel("video", 1); + ASSERT_NE(nullptr, channel2); + EXPECT_TRUE(transport_controller_->GetLocalCertificate( + "video", &returned_certificate)); + EXPECT_EQ(certificate1->identity()->certificate().ToPEMString(), + returned_certificate->identity()->certificate().ToPEMString()); + + // Shouldn't be able to change the identity once set. + EXPECT_FALSE(transport_controller_->SetLocalCertificate(certificate2)); +} + +TEST_F(TransportControllerTest, TestGetRemoteSSLCertificate) { + rtc::FakeSSLCertificate fake_certificate("fake_data"); + rtc::scoped_ptr returned_certificate; + + FakeTransportChannel* channel = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel); + + channel->SetRemoteSSLCertificate(&fake_certificate); + EXPECT_TRUE(transport_controller_->GetRemoteSSLCertificate( + "audio", returned_certificate.accept())); + EXPECT_EQ(fake_certificate.ToPEMString(), + returned_certificate->ToPEMString()); + + // Should fail if called for a nonexistant transport. + EXPECT_FALSE(transport_controller_->GetRemoteSSLCertificate( + "video", returned_certificate.accept())); +} + +TEST_F(TransportControllerTest, TestSetLocalTransportDescription) { + FakeTransportChannel* channel = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel); + TransportDescription local_desc( + std::vector(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL, + cricket::CONNECTIONROLE_ACTPASS, nullptr, Candidates()); + std::string err; + EXPECT_TRUE(transport_controller_->SetLocalTransportDescription( + "audio", local_desc, cricket::CA_OFFER, &err)); + // Check that ICE ufrag and pwd were propagated to channel. + EXPECT_EQ(kIceUfrag1, channel->ice_ufrag()); + EXPECT_EQ(kIcePwd1, channel->ice_pwd()); + // After setting local description, we should be able to start gathering + // candidates. + transport_controller_->MaybeStartGathering(); + EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout); + EXPECT_EQ(1, gathering_state_signal_count_); +} + +TEST_F(TransportControllerTest, TestSetRemoteTransportDescription) { + FakeTransportChannel* channel = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel); + TransportDescription remote_desc( + std::vector(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL, + cricket::CONNECTIONROLE_ACTPASS, nullptr, Candidates()); + std::string err; + EXPECT_TRUE(transport_controller_->SetRemoteTransportDescription( + "audio", remote_desc, cricket::CA_OFFER, &err)); + // Check that ICE ufrag and pwd were propagated to channel. + EXPECT_EQ(kIceUfrag1, channel->remote_ice_ufrag()); + EXPECT_EQ(kIcePwd1, channel->remote_ice_pwd()); +} + +TEST_F(TransportControllerTest, TestAddRemoteCandidates) { + FakeTransportChannel* channel = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel); + Candidates candidates; + candidates.push_back(CreateCandidate(1)); + std::string err; + EXPECT_TRUE( + transport_controller_->AddRemoteCandidates("audio", candidates, &err)); + EXPECT_EQ(1U, channel->remote_candidates().size()); +} + +TEST_F(TransportControllerTest, TestReadyForRemoteCandidates) { + FakeTransportChannel* channel = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel); + // We expect to be ready for remote candidates only after local and remote + // descriptions are set. + EXPECT_FALSE(transport_controller_->ReadyForRemoteCandidates("audio")); + + std::string err; + TransportDescription remote_desc( + std::vector(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL, + cricket::CONNECTIONROLE_ACTPASS, nullptr, Candidates()); + EXPECT_TRUE(transport_controller_->SetRemoteTransportDescription( + "audio", remote_desc, cricket::CA_OFFER, &err)); + EXPECT_FALSE(transport_controller_->ReadyForRemoteCandidates("audio")); + + TransportDescription local_desc( + std::vector(), kIceUfrag2, kIcePwd2, cricket::ICEMODE_FULL, + cricket::CONNECTIONROLE_ACTPASS, nullptr, Candidates()); + EXPECT_TRUE(transport_controller_->SetLocalTransportDescription( + "audio", local_desc, cricket::CA_ANSWER, &err)); + EXPECT_TRUE(transport_controller_->ReadyForRemoteCandidates("audio")); +} + +TEST_F(TransportControllerTest, TestGetStats) { + FakeTransportChannel* channel1 = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel1); + FakeTransportChannel* channel2 = CreateChannel("audio", 2); + ASSERT_NE(nullptr, channel2); + FakeTransportChannel* channel3 = CreateChannel("video", 1); + ASSERT_NE(nullptr, channel3); + + TransportStats stats; + EXPECT_TRUE(transport_controller_->GetStats("audio", &stats)); + EXPECT_EQ("audio", stats.transport_name); + EXPECT_EQ(2U, stats.channel_stats.size()); +} + +// Test that transport gets destroyed when it has no more channels. +TEST_F(TransportControllerTest, TestCreateAndDestroyChannel) { + FakeTransportChannel* channel1 = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel1); + FakeTransportChannel* channel2 = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel2); + ASSERT_EQ(channel1, channel2); + FakeTransportChannel* channel3 = CreateChannel("audio", 2); + ASSERT_NE(nullptr, channel3); + + // Using GetStats to check if transport is destroyed from an outside class's + // perspective. + TransportStats stats; + EXPECT_TRUE(transport_controller_->GetStats("audio", &stats)); + DestroyChannel("audio", 2); + DestroyChannel("audio", 1); + EXPECT_TRUE(transport_controller_->GetStats("audio", &stats)); + DestroyChannel("audio", 1); + EXPECT_FALSE(transport_controller_->GetStats("audio", &stats)); +} + +TEST_F(TransportControllerTest, TestSignalConnectionStateFailed) { + // Need controlling ICE role to get in failed state. + transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING); + FakeTransportChannel* channel1 = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel1); + FakeTransportChannel* channel2 = CreateChannel("video", 1); + ASSERT_NE(nullptr, channel2); + + // Should signal "failed" if any channel failed; channel is considered failed + // if it previously had a connection but now has none, and gathering is + // complete. + channel1->SetCandidatesGatheringComplete(); + channel1->SetConnectionCount(1); + channel1->SetConnectionCount(0); + EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout); + EXPECT_EQ(1, connection_state_signal_count_); +} + +TEST_F(TransportControllerTest, TestSignalConnectionStateConnected) { + transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING); + FakeTransportChannel* channel1 = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel1); + FakeTransportChannel* channel2 = CreateChannel("video", 1); + ASSERT_NE(nullptr, channel2); + FakeTransportChannel* channel3 = CreateChannel("video", 2); + ASSERT_NE(nullptr, channel3); + + // First, have one channel connect, and another fail, to ensure that + // the first channel connecting didn't trigger a "connected" state signal. + // We should only get a signal when all are connected. + channel1->SetConnectionCount(2); + channel1->SetWritable(true); + channel3->SetCandidatesGatheringComplete(); + channel3->SetConnectionCount(1); + channel3->SetConnectionCount(0); + EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout); + // Signal count of 1 means that the only signal emitted was "failed". + EXPECT_EQ(1, connection_state_signal_count_); + + // Destroy the failed channel to return to "connecting" state. + DestroyChannel("video", 2); + EXPECT_EQ_WAIT(cricket::kIceConnectionConnecting, connection_state_, + kTimeout); + EXPECT_EQ(2, connection_state_signal_count_); + + // Make the remaining channel reach a connected state. + channel2->SetConnectionCount(2); + channel2->SetWritable(true); + EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout); + EXPECT_EQ(3, connection_state_signal_count_); +} + +TEST_F(TransportControllerTest, TestSignalConnectionStateComplete) { + transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING); + FakeTransportChannel* channel1 = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel1); + FakeTransportChannel* channel2 = CreateChannel("video", 1); + ASSERT_NE(nullptr, channel2); + FakeTransportChannel* channel3 = CreateChannel("video", 2); + ASSERT_NE(nullptr, channel3); + + // Similar to above test, but we're now reaching the completed state, which + // means only one connection per FakeTransportChannel. + channel1->SetCandidatesGatheringComplete(); + channel1->SetConnectionCount(1); + channel1->SetWritable(true); + channel3->SetCandidatesGatheringComplete(); + channel3->SetConnectionCount(1); + channel3->SetConnectionCount(0); + EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout); + // Signal count of 1 means that the only signal emitted was "failed". + EXPECT_EQ(1, connection_state_signal_count_); + + // Destroy the failed channel to return to "connecting" state. + DestroyChannel("video", 2); + EXPECT_EQ_WAIT(cricket::kIceConnectionConnecting, connection_state_, + kTimeout); + EXPECT_EQ(2, connection_state_signal_count_); + + // Make the remaining channel reach a connected state. + channel2->SetCandidatesGatheringComplete(); + channel2->SetConnectionCount(2); + channel2->SetWritable(true); + EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout); + EXPECT_EQ(3, connection_state_signal_count_); + + // Finally, transition to completed state. + channel2->SetConnectionCount(1); + EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout); + EXPECT_EQ(4, connection_state_signal_count_); +} + +// Make sure that if we're "connected" and remove a transport, we stay in the +// "connected" state. +TEST_F(TransportControllerTest, TestDestroyTransportAndStayConnected) { + FakeTransportChannel* channel1 = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel1); + FakeTransportChannel* channel2 = CreateChannel("video", 1); + ASSERT_NE(nullptr, channel2); + + channel1->SetCandidatesGatheringComplete(); + channel1->SetConnectionCount(2); + channel1->SetWritable(true); + channel2->SetCandidatesGatheringComplete(); + channel2->SetConnectionCount(2); + channel2->SetWritable(true); + EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout); + EXPECT_EQ(1, connection_state_signal_count_); + + // Destroy one channel, then "complete" the other one, so we reach + // a known state. + DestroyChannel("video", 1); + channel1->SetConnectionCount(1); + EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout); + // Signal count of 2 means the deletion didn't cause any unexpected signals + EXPECT_EQ(2, connection_state_signal_count_); +} + +// If we destroy the last/only transport, we should simply transition to +// "connecting". +TEST_F(TransportControllerTest, TestDestroyLastTransportWhileConnected) { + FakeTransportChannel* channel = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel); + + channel->SetCandidatesGatheringComplete(); + channel->SetConnectionCount(2); + channel->SetWritable(true); + EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout); + EXPECT_EQ(1, connection_state_signal_count_); + + DestroyChannel("audio", 1); + EXPECT_EQ_WAIT(cricket::kIceConnectionConnecting, connection_state_, + kTimeout); + // Signal count of 2 means the deletion didn't cause any unexpected signals + EXPECT_EQ(2, connection_state_signal_count_); +} + +TEST_F(TransportControllerTest, TestSignalReceiving) { + FakeTransportChannel* channel1 = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel1); + FakeTransportChannel* channel2 = CreateChannel("video", 1); + ASSERT_NE(nullptr, channel2); + + // Should signal receiving as soon as any channel is receiving. + channel1->SetReceiving(true); + EXPECT_TRUE_WAIT(receiving_, kTimeout); + EXPECT_EQ(1, receiving_signal_count_); + + channel2->SetReceiving(true); + channel1->SetReceiving(false); + channel2->SetReceiving(false); + EXPECT_TRUE_WAIT(!receiving_, kTimeout); + EXPECT_EQ(2, receiving_signal_count_); +} + +TEST_F(TransportControllerTest, TestSignalGatheringStateGathering) { + FakeTransportChannel* channel = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel); + channel->Connect(); + channel->MaybeStartGathering(); + // Should be in the gathering state as soon as any transport starts gathering. + EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout); + EXPECT_EQ(1, gathering_state_signal_count_); +} + +TEST_F(TransportControllerTest, TestSignalGatheringStateComplete) { + FakeTransportChannel* channel1 = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel1); + FakeTransportChannel* channel2 = CreateChannel("video", 1); + ASSERT_NE(nullptr, channel2); + FakeTransportChannel* channel3 = CreateChannel("data", 1); + ASSERT_NE(nullptr, channel3); + + channel3->Connect(); + channel3->MaybeStartGathering(); + EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout); + EXPECT_EQ(1, gathering_state_signal_count_); + + // Have one channel finish gathering, then destroy it, to make sure gathering + // completion wasn't signalled if only one transport finished gathering. + channel3->SetCandidatesGatheringComplete(); + DestroyChannel("data", 1); + EXPECT_EQ_WAIT(cricket::kIceGatheringNew, gathering_state_, kTimeout); + EXPECT_EQ(2, gathering_state_signal_count_); + + // Make remaining channels start and then finish gathering. + channel1->Connect(); + channel1->MaybeStartGathering(); + channel2->Connect(); + channel2->MaybeStartGathering(); + EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout); + EXPECT_EQ(3, gathering_state_signal_count_); + + channel1->SetCandidatesGatheringComplete(); + channel2->SetCandidatesGatheringComplete(); + EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout); + EXPECT_EQ(4, gathering_state_signal_count_); +} + +// Test that when the last transport that hasn't finished connecting and/or +// gathering is destroyed, the aggregate state jumps to "completed". This can +// happen if, for example, we have an audio and video transport, the audio +// transport completes, then we start bundling video on the audio transport. +TEST_F(TransportControllerTest, + TestSignalingWhenLastIncompleteTransportDestroyed) { + transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING); + FakeTransportChannel* channel1 = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel1); + FakeTransportChannel* channel2 = CreateChannel("video", 1); + ASSERT_NE(nullptr, channel2); + + channel1->SetCandidatesGatheringComplete(); + EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout); + EXPECT_EQ(1, gathering_state_signal_count_); + + channel1->SetConnectionCount(1); + channel1->SetWritable(true); + DestroyChannel("video", 1); + EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout); + EXPECT_EQ(1, connection_state_signal_count_); + EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout); + EXPECT_EQ(2, gathering_state_signal_count_); +} + +TEST_F(TransportControllerTest, TestSignalCandidatesGathered) { + FakeTransportChannel* channel = CreateChannel("audio", 1); + ASSERT_NE(nullptr, channel); + + // Transport won't signal candidates until it has a local description. + TransportDescription local_desc( + std::vector(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL, + cricket::CONNECTIONROLE_ACTPASS, nullptr, Candidates()); + std::string err; + EXPECT_TRUE(transport_controller_->SetLocalTransportDescription( + "audio", local_desc, cricket::CA_OFFER, &err)); + transport_controller_->MaybeStartGathering(); + + channel->SignalCandidateGathered(channel, CreateCandidate(1)); + EXPECT_EQ_WAIT(1, candidates_signal_count_, kTimeout); + EXPECT_EQ(1U, candidates_["audio"].size()); +} + +TEST_F(TransportControllerTest, TestSignalingOccursOnSignalingThread) { + CreateTransportControllerWithWorkerThread(); + CreateChannelsAndCompleteConnectionOnWorkerThread(); + + // connecting --> connected --> completed + EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout); + EXPECT_EQ(2, connection_state_signal_count_); + + EXPECT_TRUE_WAIT(receiving_, kTimeout); + EXPECT_EQ(1, receiving_signal_count_); + + // new --> gathering --> complete + EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout); + EXPECT_EQ(2, gathering_state_signal_count_); + + EXPECT_EQ_WAIT(1U, candidates_["audio"].size(), kTimeout); + EXPECT_EQ_WAIT(1U, candidates_["video"].size(), kTimeout); + EXPECT_EQ(2, candidates_signal_count_); + + EXPECT_TRUE(!signaled_on_non_signaling_thread_); +} diff --git a/webrtc/p2p/base/transportdescriptionfactory.cc b/webrtc/p2p/base/transportdescriptionfactory.cc index 4c701df0dd..1ddf55d4a1 100644 --- a/webrtc/p2p/base/transportdescriptionfactory.cc +++ b/webrtc/p2p/base/transportdescriptionfactory.cc @@ -14,7 +14,6 @@ #include "webrtc/base/helpers.h" #include "webrtc/base/logging.h" #include "webrtc/base/messagedigest.h" -#include "webrtc/base/scoped_ptr.h" #include "webrtc/base/sslfingerprint.h" namespace cricket { diff --git a/webrtc/p2p/p2p.gyp b/webrtc/p2p/p2p.gyp index 45c9e13d19..b5409f4df7 100644 --- a/webrtc/p2p/p2p.gyp +++ b/webrtc/p2p/p2p.gyp @@ -66,8 +66,8 @@ 'base/transportchannel.cc', 'base/transportchannel.h', 'base/transportchannelimpl.h', - 'base/transportchannelproxy.cc', - 'base/transportchannelproxy.h', + 'base/transportcontroller.cc', + 'base/transportcontroller.h', 'base/transportdescription.cc', 'base/transportdescription.h', 'base/transportdescriptionfactory.cc', diff --git a/webrtc/p2p/p2p_tests.gypi b/webrtc/p2p/p2p_tests.gypi index 8c2c2b35ab..ba7f553bba 100644 --- a/webrtc/p2p/p2p_tests.gypi +++ b/webrtc/p2p/p2p_tests.gypi @@ -15,13 +15,12 @@ 'direct_dependent_settings': { 'sources': [ 'base/dtlstransportchannel_unittest.cc', - 'base/fakesession.h', + 'base/faketransportcontroller.h', 'base/p2ptransportchannel_unittest.cc', 'base/port_unittest.cc', 'base/pseudotcp_unittest.cc', 'base/relayport_unittest.cc', 'base/relayserver_unittest.cc', - 'base/session_unittest.cc', 'base/stun_unittest.cc', 'base/stunport_unittest.cc', 'base/stunrequest_unittest.cc', @@ -30,6 +29,7 @@ 'base/teststunserver.h', 'base/testturnserver.h', 'base/transport_unittest.cc', + 'base/transportcontroller_unittest.cc', 'base/transportdescriptionfactory_unittest.cc', 'base/turnport_unittest.cc', 'client/fakeportallocator.h',