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',