diff --git a/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.m b/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.m index daebc9523d..5070b789b8 100644 --- a/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.m +++ b/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.m @@ -196,11 +196,8 @@ if (newState == RTCICEGatheringGathering) { return; } - NSAssert([_expectedICEGatheringChanges count] > 0, - @"Unexpected ICE gathering state change"); int expectedState = [self popFirstElementAsInt:_expectedICEGatheringChanges]; - NSAssert(expectedState == (int)newState, - @"ICE gathering state should match expectation"); + NSAssert(expectedState == (int)newState, @"Empty expectation array"); } - (void)peerConnection:(RTCPeerConnection*)peerConnection @@ -208,11 +205,8 @@ // 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, - @"ICE connection state should match expectation"); + NSAssert(expectedState == (int)newState, @"Empty expectation array"); } - (void)peerConnection:(RTCPeerConnection*)peerConnection diff --git a/talk/app/webrtc/peerconnection.cc b/talk/app/webrtc/peerconnection.cc index 7e689490b9..41df847cd6 100644 --- a/talk/app/webrtc/peerconnection.cc +++ b/talk/app/webrtc/peerconnection.cc @@ -868,11 +868,6 @@ 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 3583f6151a..632744568e 100644 --- a/talk/app/webrtc/statscollector.cc +++ b/talk/app/webrtc/statscollector.cc @@ -697,18 +697,24 @@ 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 (session_->GetLocalCertificate(transport_iter.second.transport_name, - &certificate)) { + if (transport && transport->GetCertificate(&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 (session_->GetRemoteSSLCertificate(transport_iter.second.transport_name, - cert.accept())) { + if (transport && transport->GetRemoteSSLCertificate(cert.accept())) { StatsReport* r = AddCertificateReports(cert.get()); if (r) remote_cert_report_id = r->id(); @@ -716,7 +722,7 @@ void StatsCollector::ExtractSessionInfo() { for (const auto& channel_iter : transport_iter.second.channel_stats) { StatsReport::Id id(StatsReport::NewComponentId( - transport_iter.second.transport_name, channel_iter.component)); + transport_iter.second.content_name, channel_iter.component)); StatsReport* channel_report = reports_.ReplaceOrAddNew(id); channel_report->set_timestamp(stats_gathering_started_); channel_report->AddInt(StatsReport::kStatsValueNameComponent, @@ -933,6 +939,7 @@ 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 0a9d947841..9b037c4149 100644 --- a/talk/app/webrtc/statscollector_unittest.cc +++ b/talk/app/webrtc/statscollector_unittest.cc @@ -27,8 +27,6 @@ #include -#include - #include "talk/app/webrtc/statscollector.h" #include "talk/app/webrtc/mediastream.h" @@ -47,7 +45,7 @@ #include "webrtc/base/fakesslidentity.h" #include "webrtc/base/gunit.h" #include "webrtc/base/network.h" -#include "webrtc/p2p/base/faketransportcontroller.h" +#include "webrtc/p2p/base/fakesession.h" using rtc::scoped_ptr; using testing::_; @@ -91,12 +89,7 @@ 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_METHOD2(GetLocalCertificate, - bool(const std::string& transport_name, - rtc::scoped_refptr* certificate)); - MOCK_METHOD2(GetRemoteSSLCertificate, - bool(const std::string& transport_name, - rtc::SSLCertificate** cert)); + MOCK_METHOD1(GetTransport, cricket::Transport*(const std::string&)); }; class MockVideoMediaChannel : public cricket::FakeVideoMediaChannel { @@ -507,7 +500,7 @@ class StatsCollectorTest : public testing::Test { cricket::TransportStats transport_stats; cricket::TransportChannelStats channel_stats; channel_stats.component = 1; - transport_stats.transport_name = kTransportName; + transport_stats.content_name = kTransportName; transport_stats.channel_stats.push_back(channel_stats); session_stats_.transport_stats[kTransportName] = transport_stats; @@ -654,27 +647,36 @@ class StatsCollectorTest : public testing::Test { channel_stats.ssl_cipher = "the-ssl-cipher"; cricket::TransportStats transport_stats; - transport_stats.transport_name = "audio"; + transport_stats.content_name = "audio"; transport_stats.channel_stats.push_back(channel_stats); cricket::SessionStats session_stats; - session_stats.transport_stats[transport_stats.transport_name] = + session_stats.transport_stats[transport_stats.content_name] = transport_stats; - // Fake certificate to report + // Fake certificates to report. rtc::scoped_refptr local_certificate( rtc::RTCCertificate::Create(rtc::scoped_ptr( - new rtc::FakeSSLIdentity(local_cert)) - .Pass())); + 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()); // Configure MockWebRtcSession - 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_, GetTransport(transport_stats.content_name)) + .WillRepeatedly(Return(transport.get())); EXPECT_CALL(session_, GetTransportStats(_)) .WillOnce(DoAll(SetArgPointee<0>(session_stats), Return(true))); @@ -788,17 +790,14 @@ 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(), @@ -834,17 +833,14 @@ 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(), @@ -950,16 +946,13 @@ 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(), @@ -1018,13 +1011,11 @@ TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) { TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) { StatsCollectorForTest stats(&session_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _)) - .WillRepeatedly(Return(false)); - + // Ignore unused callback (logspam). + EXPECT_CALL(session_, GetTransport(_)) + .WillRepeatedly(Return(static_cast(NULL))); MockVideoMediaChannel* media_channel = new MockVideoMediaChannel(); - // The transport_name known by the video channel. + // The content_name known by the video channel. const std::string kVcName("vcname"); cricket::VideoChannel video_channel(rtc::Thread::Current(), media_channel, NULL, kVcName, false); @@ -1082,7 +1073,7 @@ TEST_F(StatsCollectorTest, RemoteSsrcInfoIsAbsent) { StatsCollectorForTest stats(&session_); MockVideoMediaChannel* media_channel = new MockVideoMediaChannel(); - // The transport_name known by the video channel. + // The content_name known by the video channel. const std::string kVcName("vcname"); cricket::VideoChannel video_channel(rtc::Thread::Current(), media_channel, NULL, kVcName, false); @@ -1105,13 +1096,11 @@ TEST_F(StatsCollectorTest, RemoteSsrcInfoIsAbsent) { TEST_F(StatsCollectorTest, RemoteSsrcInfoIsPresent) { StatsCollectorForTest stats(&session_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _)) - .WillRepeatedly(Return(false)); - + // Ignore unused callback (logspam). + EXPECT_CALL(session_, GetTransport(_)) + .WillRepeatedly(Return(static_cast(NULL))); MockVideoMediaChannel* media_channel = new MockVideoMediaChannel(); - // The transport_name known by the video channel. + // The content_name known by the video channel. const std::string kVcName("vcname"); cricket::VideoChannel video_channel(rtc::Thread::Current(), media_channel, NULL, kVcName, false); @@ -1156,16 +1145,13 @@ 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(), @@ -1344,11 +1330,6 @@ 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. @@ -1356,14 +1337,16 @@ TEST_F(StatsCollectorTest, NoTransport) { channel_stats.component = 1; cricket::TransportStats transport_stats; - transport_stats.transport_name = "audio"; + transport_stats.content_name = "audio"; transport_stats.channel_stats.push_back(channel_stats); cricket::SessionStats session_stats; - session_stats.transport_stats[transport_stats.transport_name] = + session_stats.transport_stats[transport_stats.content_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))); @@ -1406,11 +1389,6 @@ 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. @@ -1418,18 +1396,23 @@ TEST_F(StatsCollectorTest, NoCertificates) { channel_stats.component = 1; cricket::TransportStats transport_stats; - transport_stats.transport_name = "audio"; + transport_stats.content_name = "audio"; transport_stats.channel_stats.push_back(channel_stats); cricket::SessionStats session_stats; - session_stats.transport_stats[transport_stats.transport_name] = + session_stats.transport_stats[transport_stats.content_name] = transport_stats; // Fake transport object. rtc::scoped_ptr transport( - new cricket::FakeTransport(transport_stats.transport_name)); + new cricket::FakeTransport( + session_.signaling_thread(), + session_.worker_thread(), + transport_stats.content_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))); @@ -1475,13 +1458,12 @@ TEST_F(StatsCollectorTest, UnsupportedDigestIgnored) { TEST_F(StatsCollectorTest, GetStatsFromLocalAudioTrack) { StatsCollectorForTest stats(&session_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _)) - .WillRepeatedly(Return(false)); + // Ignore unused callback (logspam). + EXPECT_CALL(session_, GetTransport(_)) + .WillRepeatedly(Return(static_cast(NULL))); MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel(); - // The transport_name known by the voice channel. + // The content_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); @@ -1510,13 +1492,11 @@ TEST_F(StatsCollectorTest, GetStatsFromLocalAudioTrack) { TEST_F(StatsCollectorTest, GetStatsFromRemoteStream) { StatsCollectorForTest stats(&session_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _)) - .WillRepeatedly(Return(false)); - + // Ignore unused callback (logspam). + EXPECT_CALL(session_, GetTransport(_)) + .WillRepeatedly(Return(static_cast(NULL))); MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel(); - // The transport_name known by the voice channel. + // The content_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); @@ -1539,13 +1519,11 @@ TEST_F(StatsCollectorTest, GetStatsFromRemoteStream) { TEST_F(StatsCollectorTest, GetStatsAfterRemoveAudioStream) { StatsCollectorForTest stats(&session_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _)) - .WillRepeatedly(Return(false)); - + // Ignore unused callback (logspam). + EXPECT_CALL(session_, GetTransport(_)) + .WillRepeatedly(Return(static_cast(NULL))); MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel(); - // The transport_name known by the voice channel. + // The content_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); @@ -1600,13 +1578,11 @@ TEST_F(StatsCollectorTest, GetStatsAfterRemoveAudioStream) { TEST_F(StatsCollectorTest, LocalAndRemoteTracksWithSameSsrc) { StatsCollectorForTest stats(&session_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _)) - .WillRepeatedly(Return(false)); - + // Ignore unused callback (logspam). + EXPECT_CALL(session_, GetTransport(_)) + .WillRepeatedly(Return(static_cast(NULL))); MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel(); - // The transport_name known by the voice channel. + // The content_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); @@ -1687,13 +1663,11 @@ TEST_F(StatsCollectorTest, LocalAndRemoteTracksWithSameSsrc) { TEST_F(StatsCollectorTest, TwoLocalTracksWithSameSsrc) { StatsCollectorForTest stats(&session_); - EXPECT_CALL(session_, GetLocalCertificate(_, _)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _)) - .WillRepeatedly(Return(false)); - + // Ignore unused callback (logspam). + EXPECT_CALL(session_, GetTransport(_)) + .WillRepeatedly(Return(static_cast(NULL))); MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel(); - // The transport_name known by the voice channel. + // The content_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 bd1cd1a0ac..0c0e44d0e0 100644 --- a/talk/app/webrtc/webrtcsession.cc +++ b/talk/app/webrtc/webrtcsession.cc @@ -31,7 +31,6 @@ #include #include -#include #include "talk/app/webrtc/jsepicecandidate.h" #include "talk/app/webrtc/jsepsessiondescription.h" @@ -87,7 +86,6 @@ 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( @@ -545,6 +543,7 @@ 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". @@ -559,14 +558,6 @@ 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() { @@ -592,12 +583,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; - transport_controller()->SetSslMaxProtocolVersion(options.ssl_max_version); + SetSslMaxProtocolVersion(options.ssl_max_version); // Obtain a certificate from RTCConfiguration if any were provided (optional). rtc::scoped_refptr certificate; @@ -622,8 +613,10 @@ 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; } } @@ -743,21 +736,35 @@ 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_)); } } @@ -784,12 +791,26 @@ bool WebRtcSession::Initialize( void WebRtcSession::Terminate() { SetState(STATE_RECEIVEDTERMINATE); - RemoveUnusedChannels(NULL); + RemoveUnusedChannelsAndTransports(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); } @@ -805,7 +826,17 @@ bool WebRtcSession::GetSslRole(rtc::SSLRole* role) { return false; } - return transport_controller()->GetSslRole(role); + // 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; } void WebRtcSession::CreateOffer( @@ -821,8 +852,6 @@ 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); @@ -855,24 +884,16 @@ bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc, return BadLocalSdp(desc->type(), kCreateChannelFailed, err_desc); } - // Remove unused channels if MediaContentDescription is rejected. - RemoveUnusedChannels(local_desc_->description()); + // Remove channel and transport proxies, if MediaContentDescription is + // rejected. + RemoveUnusedChannelsAndTransports(local_desc_->description()); if (!UpdateSessionState(action, cricket::CS_LOCAL, err_desc)) { return false; } - 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()); - } + // Kick starting the ice candidates allocation. + StartCandidatesAllocation(); // Update state and SSRC of local MediaStreams and DataChannels based on the // local session description. @@ -890,8 +911,6 @@ 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); @@ -908,8 +927,9 @@ bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc, return BadRemoteSdp(desc->type(), kCreateChannelFailed, err_desc); } - // Remove unused channels if MediaContentDescription is rejected. - RemoveUnusedChannels(desc->description()); + // Remove channel and transport proxies, if MediaContentDescription is + // rejected. + RemoveUnusedChannelsAndTransports(desc->description()); // NOTE: Candidates allocation will be initiated only when SetLocalDescription // is called. @@ -968,8 +988,6 @@ 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); @@ -1003,21 +1021,7 @@ bool WebRtcSession::UpdateSessionState( if (!PushdownTransportDescription(source, cricket::CA_ANSWER, &td_err)) { return BadAnswerSdp(source, MakeTdErrorString(td_err), err_desc); } - 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); - } - } + MaybeEnableMuxingSupport(); EnableChannels(); SetState(source == cricket::CS_LOCAL ? STATE_SENTACCEPT : STATE_RECEIVEDACCEPT); @@ -1066,101 +1070,32 @@ 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)); -} -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())) { + 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. return true; } - if (ch->transport_name() == transport_name) { - LOG(LS_INFO) << "BUNDLE already enabled for " << ch->content_name() - << " on " << transport_name << "."; - return true; - } - - if (!ch->SetTransport(transport_name)) { - LOG(LS_WARNING) << "Failed to enable BUNDLE for " << ch->content_name(); + cricket::TransportStats tstats; + if (!transport->GetStats(&tstats)) { return false; } - LOG(LS_INFO) << "Enabled BUNDLE for " << ch->content_name() << " on " - << transport_name << "."; + + stats->transport_stats[transport_id] = tstats; return true; }; - if (!maybe_set_transport(voice_channel()) || - !maybe_set_transport(video_channel()) || - !maybe_set_transport(data_channel())) { - return false; + for (const auto& kv : transport_proxies()) { + cricket::Transport* transport = kv.second->impl(); + if (transport && !get_transport_stats(kv.first, transport)) { + return false; + } } - return true; } @@ -1466,18 +1401,13 @@ void WebRtcSession::ResetIceRestartLatch() { void WebRtcSession::OnCertificateReady( const rtc::scoped_refptr& certificate) { - transport_controller()->SetLocalCertificate(certificate); + SetCertificate(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) { @@ -1488,8 +1418,6 @@ 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); @@ -1530,52 +1458,70 @@ void WebRtcSession::SetIceConnectionState( } } -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::OnTransportRequestSignaling( + cricket::Transport* transport) { + ASSERT(signaling_thread()->IsCurrent()); + transport->OnSignalingReady(); + if (ice_observer_) { + ice_observer_->OnIceGatheringChange( + PeerConnectionInterface::kIceGatheringGathering); } } -void WebRtcSession::OnTransportControllerReceiving(bool receiving) { +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; + break; + } + } SetIceConnectionReceiving(receiving); } @@ -1589,27 +1535,18 @@ void WebRtcSession::SetIceConnectionReceiving(bool receiving) { } } -void WebRtcSession::OnTransportControllerCandidatesGathered( - const std::string& transport_name, - const cricket::Candidates& candidates) { +void WebRtcSession::OnTransportProxyCandidatesReady( + cricket::TransportProxy* proxy, const cricket::Candidates& candidates) { ASSERT(signaling_thread()->IsCurrent()); - int sdp_mline_index; - if (!GetLocalCandidateMediaIndex(transport_name, &sdp_mline_index)) { - LOG(LS_ERROR) << "OnTransportControllerCandidatesGathered: content name " - << transport_name << " not found"; - return; - } + ProcessNewLocalCandidate(proxy->content_name(), candidates); +} - 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); - } +void WebRtcSession::OnCandidatesAllocationDone() { + ASSERT(signaling_thread()->IsCurrent()); + if (ice_observer_) { + ice_observer_->OnIceGatheringChange( + PeerConnectionInterface::kIceGatheringComplete); + ice_observer_->OnIceComplete(); } } @@ -1625,6 +1562,29 @@ 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) { @@ -1689,8 +1649,7 @@ bool WebRtcSession::UseCandidate( candidates.push_back(candidate->candidate()); // Invoking BaseSession method to handle remote candidates. std::string error; - if (transport_controller()->AddRemoteCandidates(content.name, candidates, - &error)) { + if (OnRemoteCandidates(content.name, candidates, &error)) { // Candidates successfully submitted for checking. if (ice_connection_state_ == PeerConnectionInterface::kIceConnectionNew || ice_connection_state_ == @@ -1714,7 +1673,8 @@ bool WebRtcSession::UseCandidate( return true; } -void WebRtcSession::RemoveUnusedChannels(const SessionDescription* desc) { +void WebRtcSession::RemoveUnusedChannelsAndTransports( + const SessionDescription* desc) { // Destroy video_channel_ first since it may have a pointer to the // voice_channel_. const cricket::ContentInfo* video_info = @@ -1724,6 +1684,7 @@ void WebRtcSession::RemoveUnusedChannels(const SessionDescription* desc) { SignalVideoChannelDestroyed(); const std::string content_name = video_channel_->content_name(); channel_manager_->DestroyVideoChannel(video_channel_.release()); + DestroyTransportProxy(content_name); } const cricket::ContentInfo* voice_info = @@ -1733,6 +1694,7 @@ void WebRtcSession::RemoveUnusedChannels(const SessionDescription* desc) { SignalVoiceChannelDestroyed(); const std::string content_name = voice_channel_->content_name(); channel_manager_->DestroyVoiceChannel(voice_channel_.release()); + DestroyTransportProxy(content_name); } const cricket::ContentInfo* data_info = @@ -1742,6 +1704,7 @@ void WebRtcSession::RemoveUnusedChannels(const SessionDescription* desc) { SignalDataChannelDestroyed(); const std::string content_name = data_channel_->content_name(); channel_manager_->DestroyDataChannel(data_channel_.release()); + DestroyTransportProxy(content_name); } } @@ -1786,7 +1749,7 @@ bool WebRtcSession::CreateChannels(const SessionDescription* desc) { } } - // Enable BUNDLE immediately when kBundlePolicyMaxBundle is in effect. + // Enable bundle before when kMaxBundle policy is in effect. if (bundle_policy_ == PeerConnectionInterface::kBundlePolicyMaxBundle) { const cricket::ContentGroup* bundle_group = desc->GetGroupByName( cricket::GROUP_TYPE_BUNDLE); @@ -1794,7 +1757,7 @@ bool WebRtcSession::CreateChannels(const SessionDescription* desc) { LOG(LS_WARNING) << "max-bundle specified without BUNDLE specified"; return false; } - if (!EnableBundle(*bundle_group)) { + if (!BaseSession::BundleContentGroup(bundle_group)) { LOG(LS_WARNING) << "max-bundle failed to enable bundling."; return false; } @@ -1805,8 +1768,7 @@ bool WebRtcSession::CreateChannels(const SessionDescription* desc) { bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content) { voice_channel_.reset(channel_manager_->CreateVoiceChannel( - media_controller_.get(), transport_controller(), content->name, true, - audio_options_)); + media_controller_.get(), this, content->name, true, audio_options_)); if (!voice_channel_) { return false; } @@ -1818,8 +1780,7 @@ bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content) { bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) { video_channel_.reset(channel_manager_->CreateVideoChannel( - media_controller_.get(), transport_controller(), content->name, true, - video_options_)); + media_controller_.get(), this, content->name, true, video_options_)); if (!video_channel_) { return false; } @@ -1832,7 +1793,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( - transport_controller(), content->name, !sctp, data_channel_type_)); + this, content->name, !sctp, data_channel_type_)); if (!data_channel_) { return false; } @@ -2013,6 +1974,7 @@ 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(); @@ -2034,53 +1996,12 @@ bool WebRtcSession::ReadyToUseRemoteCandidate( cricket::ContentInfo content = current_remote_desc->description()->contents()[mediacontent_index]; - cricket::BaseChannel* channel = GetChannel(content.name); - if (!channel) { - return false; - } + transport_proxy = GetTransportProxy(content.name); - return transport_controller()->ReadyForRemoteCandidates( - channel->transport_name()); + return transport_proxy && transport_proxy->local_description_set() && + transport_proxy->remote_description_set(); } -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( @@ -2148,13 +2069,13 @@ void WebRtcSession::ReportNegotiatedCiphers( PeerConnectionMetricsName srtp_name; PeerConnectionMetricsName ssl_name; - if (stats.transport_name == cricket::CN_AUDIO) { + if (stats.content_name == cricket::CN_AUDIO) { srtp_name = kAudioSrtpCipher; ssl_name = kAudioSslCipher; - } else if (stats.transport_name == cricket::CN_VIDEO) { + } else if (stats.content_name == cricket::CN_VIDEO) { srtp_name = kVideoSrtpCipher; ssl_name = kVideoSslCipher; - } else if (stats.transport_name == cricket::CN_DATA) { + } else if (stats.content_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 1ad9a69e59..582d6d0bd5 100644 --- a/talk/app/webrtc/webrtcsession.h +++ b/talk/app/webrtc/webrtcsession.h @@ -29,7 +29,6 @@ #define TALK_APP_WEBRTC_WEBRTCSESSION_H_ #include -#include #include "talk/app/webrtc/datachannel.h" #include "talk/app/webrtc/dtmfsender.h" @@ -50,6 +49,7 @@ class BaseChannel; class ChannelManager; class DataChannel; class StatsReport; +class Transport; class VideoCapturer; class VideoChannel; class VoiceChannel; @@ -77,8 +77,6 @@ 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; @@ -237,19 +235,6 @@ 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, @@ -269,7 +254,6 @@ 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) { @@ -285,6 +269,9 @@ 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); @@ -294,13 +281,25 @@ class WebRtcSession : public cricket::BaseSession, cricket::ContentSource source, std::string* error_desc); - 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); + + // 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; // 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|. @@ -313,7 +312,8 @@ 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 RemoveUnusedChannels(const cricket::SessionDescription* desc); + void RemoveUnusedChannelsAndTransports( + 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,20 +362,10 @@ 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 TransportController connection completion is signaled. - // Reports stats for all transports in use. - void ReportTransportStats(); - - // Gather the usage of IPv4/IPv6 as best connection. + // Invoked when OnTransportCompleted is signaled to 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 66704a2225..8bd97e59ce 100644 --- a/talk/app/webrtc/webrtcsession_unittest.cc +++ b/talk/app/webrtc/webrtcsession_unittest.cc @@ -25,8 +25,6 @@ * 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" @@ -165,8 +163,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 { @@ -246,52 +244,12 @@ class WebRtcSessionForTest : public webrtc::WebRtcSession { } virtual ~WebRtcSessionForTest() {} - // 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 cricket::BaseSession::GetTransportProxy; 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 @@ -417,9 +375,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_); } @@ -532,6 +490,13 @@ 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); @@ -1003,10 +968,15 @@ class WebRtcSessionTest SetRemoteDescriptionWithoutError(new_answer); EXPECT_TRUE_WAIT(observer_.oncandidatesready_, kIceCandidatesTimeout); EXPECT_EQ(expected_candidate_num, observer_.mline_0_candidates_.size()); - if (bundle) { - EXPECT_EQ(0, observer_.mline_1_candidates_.size()); - } else { - EXPECT_EQ(expected_candidate_num, observer_.mline_1_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)); + } } } // Tests that we can only send DTMF when the dtmf codec is supported. @@ -1031,7 +1001,7 @@ class WebRtcSessionTest // initial ICE convergences. class LoopbackNetworkConfiguration { - public: + public: LoopbackNetworkConfiguration() : test_ipv6_network_(false), test_extra_ipv4_network_(false), @@ -1180,8 +1150,11 @@ 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); @@ -1734,14 +1707,15 @@ 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(temp_offer.get(), options, cricket::SEC_ENABLED); + CreateRemoteAnswer(offer, options, cricket::SEC_ENABLED); // SetRemoteDescription and SetLocalDescription will take the ownership of // the offer and answer. @@ -2043,7 +2017,7 @@ TEST_F(WebRtcSessionTest, TestLocalCandidatesAddedToSessionDescription) { EXPECT_LT(0u, candidates->count()); candidates = local_desc->candidates(1); ASSERT_TRUE(candidates != NULL); - EXPECT_EQ(0u, candidates->count()); + EXPECT_LT(0u, candidates->count()); // Update the session descriptions. mediastream_signaling_.SendAudioVideoStream1(); @@ -2055,7 +2029,7 @@ TEST_F(WebRtcSessionTest, TestLocalCandidatesAddedToSessionDescription) { EXPECT_LT(0u, candidates->count()); candidates = local_desc->candidates(1); ASSERT_TRUE(candidates != NULL); - EXPECT_EQ(0u, candidates->count()); + EXPECT_LT(0u, candidates->count()); } // Test that we can set a remote session description with remote candidates. @@ -2099,17 +2073,23 @@ 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); } @@ -2151,14 +2131,8 @@ TEST_F(WebRtcSessionTest, TestChannelCreationsWithContentNames) { CreateAnswer(NULL); SetLocalDescriptionWithoutError(answer); - 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(session_->GetTransportProxy("audio_content_name") != NULL); + EXPECT_TRUE(session_->GetTransportProxy("video_content_name") != NULL); EXPECT_TRUE((video_channel_ = media_engine_->GetVideoChannel(0)) != NULL); EXPECT_TRUE((voice_channel_ = media_engine_->GetVoiceChannel(0)) != NULL); } @@ -2718,23 +2692,20 @@ TEST_F(WebRtcSessionTest, TestIgnoreCandidatesForUnusedTransportWhenBundling) { SessionDescriptionInterface* answer = CreateAnswer(NULL); SetLocalDescriptionWithoutError(answer); - EXPECT_EQ(session_->voice_rtp_transport_channel(), - session_->video_rtp_transport_channel()); + EXPECT_EQ(session_->GetTransportProxy("audio")->impl(), + session_->GetTransportProxy("video")->impl()); - cricket::BaseChannel* voice_channel = session_->voice_channel(); - ASSERT(voice_channel != NULL); + cricket::Transport* t = session_->GetTransport("audio"); // Checks if one of the transport channels contains a connection using a given // port. - 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; - } + 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; } } } @@ -2787,7 +2758,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(); @@ -2798,19 +2769,19 @@ TEST_F(WebRtcSessionTest, TestBalancedBundleInAnswer) { SessionDescriptionInterface* offer = CreateOffer(options); SetLocalDescriptionWithoutError(offer); - EXPECT_NE(session_->voice_rtp_transport_channel(), - session_->video_rtp_transport_channel()); + EXPECT_NE(session_->GetTransportProxy("audio")->impl(), + session_->GetTransportProxy("video")->impl()); mediastream_signaling_.SendAudioVideoStream2(); SessionDescriptionInterface* answer = CreateRemoteAnswer(session_->local_description()); SetRemoteDescriptionWithoutError(answer); - EXPECT_EQ(session_->voice_rtp_transport_channel(), - session_->video_rtp_transport_channel()); + EXPECT_EQ(session_->GetTransportProxy("audio")->impl(), + session_->GetTransportProxy("video")->impl()); } -// 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(); @@ -2821,8 +2792,8 @@ TEST_F(WebRtcSessionTest, TestBalancedNoBundleInAnswer) { SessionDescriptionInterface* offer = CreateOffer(options); SetLocalDescriptionWithoutError(offer); - EXPECT_NE(session_->voice_rtp_transport_channel(), - session_->video_rtp_transport_channel()); + EXPECT_NE(session_->GetTransportProxy("audio")->impl(), + session_->GetTransportProxy("video")->impl()); mediastream_signaling_.SendAudioVideoStream2(); @@ -2836,8 +2807,8 @@ TEST_F(WebRtcSessionTest, TestBalancedNoBundleInAnswer) { modified_answer->Initialize(answer_copy, "1", "1"); SetRemoteDescriptionWithoutError(modified_answer); // - EXPECT_NE(session_->voice_rtp_transport_channel(), - session_->video_rtp_transport_channel()); + EXPECT_NE(session_->GetTransportProxy("audio")->impl(), + session_->GetTransportProxy("video")->impl()); } // kBundlePolicyMaxBundle policy with BUNDLE in the answer. @@ -2851,49 +2822,16 @@ TEST_F(WebRtcSessionTest, TestMaxBundleBundleInAnswer) { SessionDescriptionInterface* offer = CreateOffer(options); SetLocalDescriptionWithoutError(offer); - EXPECT_EQ(session_->voice_rtp_transport_channel(), - session_->video_rtp_transport_channel()); + EXPECT_EQ(session_->GetTransportProxy("audio")->impl(), + session_->GetTransportProxy("video")->impl()); mediastream_signaling_.SendAudioVideoStream2(); SessionDescriptionInterface* answer = CreateRemoteAnswer(session_->local_description()); SetRemoteDescriptionWithoutError(answer); - 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()); + EXPECT_EQ(session_->GetTransportProxy("audio")->impl(), + session_->GetTransportProxy("video")->impl()); } // kBundlePolicyMaxBundle policy but no BUNDLE in the answer. @@ -2907,8 +2845,8 @@ TEST_F(WebRtcSessionTest, TestMaxBundleNoBundleInAnswer) { SessionDescriptionInterface* offer = CreateOffer(options); SetLocalDescriptionWithoutError(offer); - EXPECT_EQ(session_->voice_rtp_transport_channel(), - session_->video_rtp_transport_channel()); + EXPECT_EQ(session_->GetTransportProxy("audio")->impl(), + session_->GetTransportProxy("video")->impl()); mediastream_signaling_.SendAudioVideoStream2(); @@ -2922,45 +2860,8 @@ TEST_F(WebRtcSessionTest, TestMaxBundleNoBundleInAnswer) { modified_answer->Initialize(answer_copy, "1", "1"); SetRemoteDescriptionWithoutError(modified_answer); - 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); + EXPECT_EQ(session_->GetTransportProxy("audio")->impl(), + session_->GetTransportProxy("video")->impl()); } // kBundlePolicyMaxCompat bundle policy and answer contains BUNDLE. @@ -2974,8 +2875,8 @@ TEST_F(WebRtcSessionTest, TestMaxCompatBundleInAnswer) { SessionDescriptionInterface* offer = CreateOffer(options); SetLocalDescriptionWithoutError(offer); - EXPECT_NE(session_->voice_rtp_transport_channel(), - session_->video_rtp_transport_channel()); + EXPECT_NE(session_->GetTransportProxy("audio")->impl(), + session_->GetTransportProxy("video")->impl()); mediastream_signaling_.SendAudioVideoStream2(); SessionDescriptionInterface* answer = @@ -2984,11 +2885,11 @@ TEST_F(WebRtcSessionTest, TestMaxCompatBundleInAnswer) { // This should lead to an audio-only call but isn't implemented // correctly yet. - EXPECT_EQ(session_->voice_rtp_transport_channel(), - session_->video_rtp_transport_channel()); + EXPECT_EQ(session_->GetTransportProxy("audio")->impl(), + session_->GetTransportProxy("video")->impl()); } -// 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(); @@ -2998,8 +2899,8 @@ TEST_F(WebRtcSessionTest, TestMaxCompatNoBundleInAnswer) { SessionDescriptionInterface* offer = CreateOffer(options); SetLocalDescriptionWithoutError(offer); - EXPECT_NE(session_->voice_rtp_transport_channel(), - session_->video_rtp_transport_channel()); + EXPECT_NE(session_->GetTransportProxy("audio")->impl(), + session_->GetTransportProxy("video")->impl()); mediastream_signaling_.SendAudioVideoStream2(); @@ -3013,8 +2914,8 @@ TEST_F(WebRtcSessionTest, TestMaxCompatNoBundleInAnswer) { modified_answer->Initialize(answer_copy, "1", "1"); SetRemoteDescriptionWithoutError(modified_answer); // - EXPECT_NE(session_->voice_rtp_transport_channel(), - session_->video_rtp_transport_channel()); + EXPECT_NE(session_->GetTransportProxy("audio")->impl(), + session_->GetTransportProxy("video")->impl()); } // kBundlePolicyMaxbundle and then we call SetRemoteDescription first. @@ -3028,8 +2929,8 @@ TEST_F(WebRtcSessionTest, TestMaxBundleWithSetRemoteDescriptionFirst) { SessionDescriptionInterface* offer = CreateOffer(options); SetRemoteDescriptionWithoutError(offer); - EXPECT_EQ(session_->voice_rtp_transport_channel(), - session_->video_rtp_transport_channel()); + EXPECT_EQ(session_->GetTransportProxy("audio")->impl(), + session_->GetTransportProxy("video")->impl()); } TEST_F(WebRtcSessionTest, TestRequireRtcpMux) { @@ -3040,16 +2941,16 @@ TEST_F(WebRtcSessionTest, TestRequireRtcpMux) { SessionDescriptionInterface* offer = CreateOffer(options); SetLocalDescriptionWithoutError(offer); - EXPECT_TRUE(session_->voice_rtcp_transport_channel() == NULL); - EXPECT_TRUE(session_->video_rtcp_transport_channel() == NULL); + EXPECT_FALSE(session_->GetTransportProxy("audio")->impl()->HasChannel(2)); + EXPECT_FALSE(session_->GetTransportProxy("video")->impl()->HasChannel(2)); mediastream_signaling_.SendAudioVideoStream2(); SessionDescriptionInterface* answer = CreateRemoteAnswer(session_->local_description()); SetRemoteDescriptionWithoutError(answer); - EXPECT_TRUE(session_->voice_rtcp_transport_channel() == NULL); - EXPECT_TRUE(session_->video_rtcp_transport_channel() == NULL); + EXPECT_FALSE(session_->GetTransportProxy("audio")->impl()->HasChannel(2)); + EXPECT_FALSE(session_->GetTransportProxy("video")->impl()->HasChannel(2)); } TEST_F(WebRtcSessionTest, TestNegotiateRtcpMux) { @@ -3060,16 +2961,16 @@ TEST_F(WebRtcSessionTest, TestNegotiateRtcpMux) { SessionDescriptionInterface* offer = CreateOffer(options); SetLocalDescriptionWithoutError(offer); - EXPECT_TRUE(session_->voice_rtcp_transport_channel() != NULL); - EXPECT_TRUE(session_->video_rtcp_transport_channel() != NULL); + EXPECT_TRUE(session_->GetTransportProxy("audio")->impl()->HasChannel(2)); + EXPECT_TRUE(session_->GetTransportProxy("video")->impl()->HasChannel(2)); mediastream_signaling_.SendAudioVideoStream2(); SessionDescriptionInterface* answer = CreateRemoteAnswer(session_->local_description()); SetRemoteDescriptionWithoutError(answer); - EXPECT_TRUE(session_->voice_rtcp_transport_channel() == NULL); - EXPECT_TRUE(session_->video_rtcp_transport_channel() == NULL); + EXPECT_FALSE(session_->GetTransportProxy("audio")->impl()->HasChannel(2)); + EXPECT_FALSE(session_->GetTransportProxy("video")->impl()->HasChannel(2)); } // This test verifies that SetLocalDescription and SetRemoteDescription fails @@ -3090,11 +2991,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); @@ -3357,8 +3258,8 @@ TEST_F(WebRtcSessionTest, TestIceStartAfterSetLocalDescriptionOnly) { candidate1); EXPECT_TRUE(offer->AddCandidate(&ice_candidate1)); SetRemoteDescriptionWithoutError(offer); - ASSERT_TRUE(session_->voice_rtp_transport_channel() != NULL); - ASSERT_TRUE(session_->video_rtp_transport_channel() != NULL); + ASSERT_TRUE(session_->GetTransportProxy("audio") != NULL); + ASSERT_TRUE(session_->GetTransportProxy("video") != NULL); // Pump for 1 second and verify that no candidates are generated. rtc::Thread::Current()->ProcessMessages(1000); @@ -3367,6 +3268,8 @@ 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); } @@ -3401,7 +3304,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()); @@ -3754,8 +3657,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 @@ -3978,31 +3881,31 @@ TEST_F(WebRtcSessionTest, TestSetSocketOptionBeforeBundle) { rtc::Socket::Option::OPT_RCVBUF, 8000); int option_val; - EXPECT_TRUE(session_->video_rtp_transport_channel()->GetOption( + EXPECT_TRUE(session_->video_channel()->transport_channel()->GetOption( rtc::Socket::Option::OPT_SNDBUF, &option_val)); EXPECT_EQ(4000, option_val); - EXPECT_FALSE(session_->voice_rtp_transport_channel()->GetOption( + EXPECT_FALSE(session_->voice_channel()->transport_channel()->GetOption( rtc::Socket::Option::OPT_SNDBUF, &option_val)); - EXPECT_TRUE(session_->voice_rtp_transport_channel()->GetOption( + EXPECT_TRUE(session_->voice_channel()->transport_channel()->GetOption( rtc::Socket::Option::OPT_RCVBUF, &option_val)); EXPECT_EQ(8000, option_val); - EXPECT_FALSE(session_->video_rtp_transport_channel()->GetOption( + EXPECT_FALSE(session_->video_channel()->transport_channel()->GetOption( rtc::Socket::Option::OPT_RCVBUF, &option_val)); - EXPECT_NE(session_->voice_rtp_transport_channel(), - session_->video_rtp_transport_channel()); + EXPECT_NE(session_->voice_channel()->transport_channel(), + session_->video_channel()->transport_channel()); mediastream_signaling_.SendAudioVideoStream2(); SessionDescriptionInterface* answer = CreateRemoteAnswer(session_->local_description()); SetRemoteDescriptionWithoutError(answer); - EXPECT_TRUE(session_->voice_rtp_transport_channel()->GetOption( + EXPECT_TRUE(session_->voice_channel()->transport_channel()->GetOption( rtc::Socket::Option::OPT_SNDBUF, &option_val)); EXPECT_EQ(4000, option_val); - EXPECT_TRUE(session_->voice_rtp_transport_channel()->GetOption( + EXPECT_TRUE(session_->voice_channel()->transport_channel()->GetOption( rtc::Socket::Option::OPT_RCVBUF, &option_val)); EXPECT_EQ(8000, option_val); } @@ -4038,7 +3941,6 @@ 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 f6414d314e..caa53df4ad 100644 --- a/talk/app/webrtc/webrtcsessiondescriptionfactory.cc +++ b/talk/app/webrtc/webrtcsessiondescriptionfactory.cc @@ -165,15 +165,9 @@ 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."; } @@ -232,9 +226,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() { @@ -260,6 +254,8 @@ 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 52b8da5d01..b42a551ef2 100644 --- a/talk/app/webrtc/webrtcsessiondescriptionfactory.h +++ b/talk/app/webrtc/webrtcsessiondescriptionfactory.h @@ -90,12 +90,13 @@ 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 1ecfcc0619..fc373db916 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/faketransportcontroller.h" +#include "webrtc/p2p/base/fakesession.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 7b5809855f..294681ef14 100644 --- a/talk/session/media/channel.cc +++ b/talk/session/media/channel.cc @@ -170,17 +170,15 @@ void RtpSendParametersFromMediaDescription( } BaseChannel::BaseChannel(rtc::Thread* thread, - MediaChannel* media_channel, - TransportController* transport_controller, - const std::string& content_name, - bool rtcp) + MediaChannel* media_channel, BaseSession* session, + const std::string& content_name, bool rtcp) : worker_thread_(thread), - transport_controller_(transport_controller), + session_(session), media_channel_(media_channel), content_name_(content_name), - rtcp_transport_enabled_(rtcp), - transport_channel_(nullptr), - rtcp_transport_channel_(nullptr), + rtcp_(rtcp), + transport_channel_(NULL), + rtcp_transport_channel_(NULL), enabled_(false), writable_(false), rtp_ready_to_send_(false), @@ -206,31 +204,20 @@ 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_; - // 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); - } + set_transport_channel(nullptr); + set_rtcp_transport_channel(nullptr); LOG(LS_INFO) << "Destroyed channel"; } bool BaseChannel::Init() { - if (!SetTransport(content_name())) { + if (!SetTransportChannels(session(), rtcp())) { return false; } if (!SetDtlsSrtpCiphers(transport_channel(), false)) { return false; } - if (rtcp_transport_enabled() && - !SetDtlsSrtpCiphers(rtcp_transport_channel(), true)) { + if (rtcp() && !SetDtlsSrtpCiphers(rtcp_transport_channel(), true)) { return false; } @@ -244,35 +231,29 @@ void BaseChannel::Deinit() { media_channel_->SetInterface(NULL); } -bool BaseChannel::SetTransport(const std::string& transport_name) { - return worker_thread_->Invoke( - Bind(&BaseChannel::SetTransport_w, this, transport_name)); +bool BaseChannel::SetTransportChannels(BaseSession* session, bool rtcp) { + return worker_thread_->Invoke(Bind( + &BaseChannel::SetTransportChannels_w, this, session, rtcp)); } -bool BaseChannel::SetTransport_w(const std::string& transport_name) { +bool BaseChannel::SetTransportChannels_w(BaseSession* session, bool rtcp) { ASSERT(worker_thread_ == rtc::Thread::Current()); - 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)); + set_transport_channel(session->CreateChannel( + content_name(), cricket::ICE_CANDIDATE_COMPONENT_RTP)); if (!transport_channel()) { return false; } - 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) { + set_rtcp_transport_channel(session->CreateChannel( + content_name(), cricket::ICE_CANDIDATE_COMPONENT_RTCP)); if (!rtcp_transport_channel()) { return false; } + } else { + set_rtcp_transport_channel(nullptr); } - transport_name_ = transport_name; return true; } @@ -280,62 +261,42 @@ void BaseChannel::set_transport_channel(TransportChannel* new_tc) { ASSERT(worker_thread_ == rtc::Thread::Current()); TransportChannel* old_tc = transport_channel_; - if (!old_tc && !new_tc) { - // Nothing to do + + if (old_tc == new_tc) { return; } - ASSERT(old_tc != new_tc); - if (old_tc) { DisconnectFromTransportChannel(old_tc); - transport_controller_->DestroyTransportChannel_w( - transport_name_, cricket::ICE_CANDIDATE_COMPONENT_RTP); + session()->DestroyChannel( + content_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) { - // Nothing to do + + if (old_tc == new_tc) { return; } - ASSERT(old_tc != new_tc); - if (old_tc) { DisconnectFromTransportChannel(old_tc); - transport_controller_->DestroyTransportChannel_w( - transport_name_, cricket::ICE_CANDIDATE_COMPONENT_RTCP); + session()->DestroyChannel( + content_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) { @@ -446,13 +407,9 @@ 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; @@ -460,7 +417,12 @@ int BaseChannel::SetOption(SocketType type, rtc::Socket::Option opt, void BaseChannel::OnWritableState(TransportChannel* channel) { ASSERT(channel == transport_channel_ || channel == rtcp_transport_channel_); - UpdateWritableState_w(); + if (transport_channel_->writable() + && (!rtcp_transport_channel_ || rtcp_transport_channel_->writable())) { + ChannelWritable_w(); + } else { + ChannelNotWritable_w(); + } } void BaseChannel::OnChannelRead(TransportChannel* channel, @@ -478,25 +440,26 @@ void BaseChannel::OnChannelRead(TransportChannel* channel, } void BaseChannel::OnReadyToSend(TransportChannel* channel) { - ASSERT(channel == transport_channel_ || channel == rtcp_transport_channel_); - SetReadyToSend(channel == rtcp_transport_channel_, true); + SetReadyToSend(channel, true); } -void BaseChannel::SetReadyToSend(bool rtcp, bool ready) { - if (rtcp) { - rtcp_ready_to_send_ = ready; - } else { +void BaseChannel::SetReadyToSend(TransportChannel* channel, bool ready) { + ASSERT(channel == transport_channel_ || channel == rtcp_transport_channel_); + if (channel == transport_channel_) { rtp_ready_to_send_ = ready; } + if (channel == rtcp_transport_channel_) { + rtcp_ready_to_send_ = ready; + } - 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 { + 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_)) { + // Notify the MediaChannel when both rtp and rtcp channel can send. + media_channel_->OnReadyToSend(true); } } @@ -618,7 +581,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(rtcp, false); + SetReadyToSend(channel, false); } return false; } @@ -752,21 +715,14 @@ 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 writable (" << content_name_ << ")" + LOG(LS_INFO) << "Channel socket writable (" + << transport_channel_->content_name() << ", " + << transport_channel_->component() << ")" << (was_ever_writable_ ? "" : " for the first time"); std::vector infos; @@ -783,13 +739,13 @@ void BaseChannel::ChannelWritable_w() { // If we're doing DTLS-SRTP, now is the time. if (!was_ever_writable_ && ShouldSetupDtlsSrtp()) { if (!SetupDtlsSrtp(false)) { - SignalDtlsSetupFailure_w(false); + SignalDtlsSetupFailure(this, false); return; } if (rtcp_transport_channel_) { if (!SetupDtlsSrtp(true)) { - SignalDtlsSetupFailure_w(true); + SignalDtlsSetupFailure(this, true); return; } } @@ -832,8 +788,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()) @@ -928,7 +884,9 @@ void BaseChannel::ChannelNotWritable_w() { if (!writable_) return; - LOG(LS_INFO) << "Channel not writable (" << content_name_ << ")"; + LOG(LS_INFO) << "Channel socket not writable (" + << transport_channel_->content_name() << ", " + << transport_channel_->component() << ")"; writable_ = false; ChangeState(); } @@ -1027,8 +985,7 @@ void BaseChannel::ActivateRtcpMux() { void BaseChannel::ActivateRtcpMux_w() { if (!rtcp_mux_filter_.IsActive()) { rtcp_mux_filter_.SetActive(); - set_rtcp_transport_channel(nullptr); - rtcp_transport_enabled_ = false; + set_rtcp_transport_channel(NULL); } } @@ -1047,11 +1004,7 @@ 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. - 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; + set_rtcp_transport_channel(NULL); } break; case CA_UPDATE: @@ -1278,16 +1231,14 @@ void BaseChannel::FlushRtcpMessages() { VoiceChannel::VoiceChannel(rtc::Thread* thread, MediaEngineInterface* media_engine, VoiceMediaChannel* media_channel, - TransportController* transport_controller, + BaseSession* session, const std::string& content_name, bool rtcp) - : BaseChannel(thread, - media_channel, - transport_controller, - content_name, + : BaseChannel(thread, media_channel, session, content_name, rtcp), media_engine_(media_engine), - received_media_(false) {} + received_media_(false) { +} VoiceChannel::~VoiceChannel() { StopAudioMonitor(); @@ -1313,12 +1264,11 @@ 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)); } bool VoiceChannel::SetRingbackTone(const void* buf, int len) { @@ -1657,16 +1607,14 @@ void VoiceChannel::GetSrtpCiphers(std::vector* ciphers) const { VideoChannel::VideoChannel(rtc::Thread* thread, VideoMediaChannel* media_channel, - TransportController* transport_controller, + BaseSession* session, const std::string& content_name, bool rtcp) - : BaseChannel(thread, - media_channel, - transport_controller, - content_name, + : BaseChannel(thread, media_channel, session, content_name, rtcp), renderer_(NULL), - previous_we_(rtc::WE_CLOSE) {} + previous_we_(rtc::WE_CLOSE) { +} bool VideoChannel::Init() { if (!BaseChannel::Init()) { @@ -1759,11 +1707,10 @@ 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() { @@ -2098,16 +2045,13 @@ void VideoChannel::GetSrtpCiphers(std::vector* ciphers) const { DataChannel::DataChannel(rtc::Thread* thread, DataMediaChannel* media_channel, - TransportController* transport_controller, + BaseSession* session, const std::string& content_name, bool rtcp) - : BaseChannel(thread, - media_channel, - transport_controller, - content_name, - rtcp), + : BaseChannel(thread, media_channel, session, 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 9cde0d1c70..bb430bf365 100644 --- a/talk/session/media/channel.h +++ b/talk/session/media/channel.h @@ -30,15 +30,12 @@ #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/transportcontroller.h" +#include "webrtc/p2p/base/session.h" #include "webrtc/p2p/client/socketmonitor.h" #include "talk/session/media/audiomonitor.h" #include "talk/session/media/bundlefilter.h" @@ -77,11 +74,8 @@ class BaseChannel public MediaChannel::NetworkInterface, public ConnectionStatsGetter { public: - BaseChannel(rtc::Thread* thread, - MediaChannel* channel, - TransportController* transport_controller, - const std::string& content_name, - bool rtcp); + BaseChannel(rtc::Thread* thread, MediaChannel* channel, BaseSession* session, + 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 @@ -89,8 +83,8 @@ class BaseChannel void Deinit(); rtc::Thread* worker_thread() const { return worker_thread_; } - const std::string& content_name() const { return content_name_; } - const std::string& transport_name() const { return transport_name_; } + BaseSession* session() const { return session_; } + const std::string& content_name() { return content_name_; } TransportChannel* transport_channel() const { return transport_channel_; } @@ -115,7 +109,6 @@ 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); @@ -142,7 +135,7 @@ class BaseChannel void StartConnectionMonitor(int cms); void StopConnectionMonitor(); // For ConnectionStatsGetter, used by ConnectionMonitor - bool GetConnectionStats(ConnectionInfos* infos) override; + virtual bool GetConnectionStats(ConnectionInfos* infos) override; void set_srtp_signal_silent_time(uint32 silent_time) { srtp_filter_.set_signal_silent_time(silent_time); @@ -165,16 +158,19 @@ class BaseChannel sigslot::signal1 SignalFirstPacketReceived; // Made public for easier testing. - void SetReadyToSend(bool rtcp, bool ready); + void SetReadyToSend(TransportChannel* channel, 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 - // true). Gets the transport channels from |transport_controller_|. - bool SetTransport_w(const std::string& transport_name); + // 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); void set_transport_channel(TransportChannel* transport); void set_rtcp_transport_channel(TransportChannel* transport); bool was_ever_writable() const { return was_ever_writable_; } @@ -189,11 +185,9 @@ class BaseChannel } bool IsReadyToReceive() const; bool IsReadyToSend() const; - rtc::Thread* signaling_thread() { - return transport_controller_->signaling_thread(); - } + rtc::Thread* signaling_thread() { return session_->signaling_thread(); } SrtpFilter* srtp_filter() { return &srtp_filter_; } - bool rtcp_transport_enabled() const { return rtcp_transport_enabled_; } + bool rtcp() const { return rtcp_; } void ConnectToTransportChannel(TransportChannel* tc); void DisconnectFromTransportChannel(TransportChannel* tc); @@ -223,9 +217,12 @@ 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); @@ -296,18 +293,15 @@ class BaseChannel private: rtc::Thread* worker_thread_; - TransportController* transport_controller_; + BaseSession* session_; MediaChannel* media_channel_; std::vector local_streams_; std::vector remote_streams_; const std::string content_name_; - std::string transport_name_; - bool rtcp_transport_enabled_; + bool rtcp_; 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_; @@ -329,21 +323,16 @@ class BaseChannel // and input/output level monitoring. class VoiceChannel : public BaseChannel { public: - VoiceChannel(rtc::Thread* thread, - MediaEngineInterface* media_engine, - VoiceMediaChannel* channel, - TransportController* transport_controller, - const std::string& content_name, - bool rtcp); + VoiceChannel(rtc::Thread* thread, MediaEngineInterface* media_engine, + VoiceMediaChannel* channel, BaseSession* session, + 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 @@ -444,10 +433,8 @@ class VoiceChannel : public BaseChannel { // VideoChannel is a specialization for video. class VideoChannel : public BaseChannel { public: - VideoChannel(rtc::Thread* thread, - VideoMediaChannel* channel, - TransportController* transport_controller, - const std::string& content_name, + VideoChannel(rtc::Thread* thread, VideoMediaChannel* channel, + BaseSession* session, const std::string& content_name, bool rtcp); ~VideoChannel(); bool Init(); @@ -546,7 +533,7 @@ class DataChannel : public BaseChannel { public: DataChannel(rtc::Thread* thread, DataMediaChannel* media_channel, - TransportController* transport_controller, + BaseSession* session, 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 499f9de890..9020cafcec 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/faketransportcontroller.h" +#include "webrtc/p2p/base/fakesession.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,21 +98,25 @@ 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( @@ -126,12 +130,10 @@ 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) - : transport_controller1_(cricket::ICEROLE_CONTROLLING), - transport_controller2_(cricket::ICEROLE_CONTROLLED), + ChannelTest(const uint8* rtp_data, int rtp_len, + const uint8* rtcp_data, int rtcp_len) + : session1_(true), + session2_(false), media_channel1_(NULL), media_channel2_(NULL), rtp_packet_(reinterpret_cast(rtp_data), rtp_len), @@ -139,7 +141,8 @@ 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()), @@ -151,11 +154,9 @@ 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, - &transport_controller1_, + channel1_.reset(CreateChannel(thread, &media_engine_, ch1, &session1_, (flags1 & RTCP) != 0)); - channel2_.reset(CreateChannel(thread, &media_engine_, ch2, - &transport_controller2_, + channel2_.reset(CreateChannel(thread, &media_engine_, ch2, &session2_, (flags2 & RTCP) != 0)); channel1_->SignalMediaMonitor.connect( this, &ChannelTest::OnMediaMonitor); @@ -178,17 +179,15 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { if (flags1 & DTLS) { // Confirmed to work with KT_RSA and KT_ECDSA. - transport_controller1_.SetLocalCertificate(rtc::RTCCertificate::Create( - rtc::scoped_ptr( - rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT)) - .Pass())); + session1_.set_ssl_rtccertificate(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. - transport_controller2_.SetLocalCertificate(rtc::RTCCertificate::Create( - rtc::scoped_ptr( - rtc::SSLIdentity::Generate("session2", rtc::KT_DEFAULT)) - .Pass())); + session2_.set_ssl_rtccertificate(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 @@ -205,14 +204,13 @@ 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::TransportController* transport_controller, - bool rtcp) { + typename T::Channel* CreateChannel(rtc::Thread* thread, + cricket::MediaEngineInterface* engine, + typename T::MediaChannel* ch, + cricket::BaseSession* session, + bool rtcp) { typename T::Channel* channel = new typename T::Channel( - thread, engine, ch, transport_controller, cricket::CN_AUDIO, rtcp); + thread, engine, ch, session, cricket::CN_AUDIO, rtcp); if (!channel->Init()) { delete channel; channel = NULL; @@ -228,7 +226,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { result = channel2_->SetRemoteContent(&remote_media_content1_, CA_OFFER, NULL); if (result) { - transport_controller1_.Connect(&transport_controller2_); + session1_.Connect(&session2_); result = channel2_->SetLocalContent(&local_media_content2_, CA_ANSWER, NULL); @@ -261,7 +259,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { channel2_->Enable(true); result = channel1_->SetRemoteContent(&remote_media_content2_, CA_PRANSWER, NULL); - transport_controller1_.Connect(&transport_controller2_); + session1_.Connect(&session2_); } return result; } @@ -288,12 +286,11 @@ 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 transport_controller1_.GetTransport_w(channel1_->content_name()); + return session1_.GetTransport(channel1_->content_name()); } cricket::FakeTransport* GetTransport2() { - return transport_controller2_.GetTransport_w(channel2_->content_name()); + return session2_.GetTransport(channel2_->content_name()); } bool SendRtp1() { @@ -772,7 +769,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()); - transport_controller1_.Connect(&transport_controller2_); + session1_.Connect(&session2_); // Channel 2 do not send anything. typename T::Content content2; @@ -835,7 +832,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { CA_ANSWER, NULL)); EXPECT_FALSE(media_channel2_->playout()); EXPECT_FALSE(media_channel2_->sending()); - transport_controller1_.Connect(&transport_controller2_); + session1_.Connect(&session2_); EXPECT_TRUE(media_channel1_->playout()); EXPECT_FALSE(media_channel1_->sending()); EXPECT_FALSE(media_channel2_->playout()); @@ -871,7 +868,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)); - transport_controller1_.Connect(&transport_controller2_); + session1_.Connect(&session2_); EXPECT_TRUE(media_channel1_->playout()); EXPECT_FALSE(media_channel1_->sending()); // remote InActive @@ -941,8 +938,6 @@ 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()); @@ -958,8 +953,6 @@ 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()); @@ -973,8 +966,6 @@ 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()); @@ -988,8 +979,6 @@ 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()); @@ -1003,8 +992,6 @@ 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()); @@ -1020,8 +1007,6 @@ 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()); @@ -1036,8 +1021,6 @@ 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()); @@ -1062,8 +1045,6 @@ 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()); @@ -1087,8 +1068,6 @@ 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()); @@ -1114,8 +1093,6 @@ 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()); @@ -1140,8 +1117,6 @@ 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()); @@ -1151,8 +1126,6 @@ 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()); @@ -1183,8 +1156,6 @@ 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()); @@ -1275,8 +1246,6 @@ 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)); @@ -1360,8 +1329,6 @@ 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()); @@ -1426,8 +1393,6 @@ 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()); @@ -1616,8 +1581,6 @@ 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()); @@ -1706,15 +1669,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(false, false); + channel1_->SetReadyToSend(rtp, false); EXPECT_FALSE(media_channel1_->ready_to_send()); - channel1_->SetReadyToSend(false, true); + channel1_->SetReadyToSend(rtp, true); EXPECT_TRUE(media_channel1_->ready_to_send()); // rtcp channel becomes not ready to send will be propagated to mediachannel - channel1_->SetReadyToSend(true, false); + channel1_->SetReadyToSend(rtcp, false); EXPECT_FALSE(media_channel1_->ready_to_send()); - channel1_->SetReadyToSend(true, true); + channel1_->SetReadyToSend(rtcp, true); EXPECT_TRUE(media_channel1_->ready_to_send()); } @@ -1733,13 +1696,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(false, false); + channel1_->SetReadyToSend(rtp, false); EXPECT_FALSE(media_channel1_->ready_to_send()); } protected: - cricket::FakeTransportController transport_controller1_; - cricket::FakeTransportController transport_controller2_; + cricket::FakeSession session1_; + cricket::FakeSession session2_; cricket::FakeMediaEngine media_engine_; // The media channels are owned by the voice channel objects below. typename T::MediaChannel* media_channel1_; @@ -1800,21 +1763,18 @@ 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::TransportController* transport_controller, + rtc::Thread* thread, cricket::MediaEngineInterface* engine, + cricket::FakeVideoMediaChannel* ch, cricket::BaseSession* session, bool rtcp) { cricket::VideoChannel* channel = new cricket::VideoChannel( - thread, ch, transport_controller, cricket::CN_VIDEO, rtcp); + thread, ch, session, cricket::CN_VIDEO, rtcp); if (!channel->Init()) { delete channel; channel = NULL; @@ -1867,11 +1827,8 @@ class VideoChannelTest : public ChannelTest { public: typedef ChannelTest Base; - VideoChannelTest() - : Base(kH264Packet, - sizeof(kH264Packet), - kRtcpReport, - sizeof(kRtcpReport)) {} + VideoChannelTest() : Base(kH264Packet, sizeof(kH264Packet), + kRtcpReport, sizeof(kRtcpReport)) {} }; @@ -2589,15 +2546,13 @@ class DataChannelTest }; // Override to avoid engine channel parameter. -template <> +template<> cricket::DataChannel* ChannelTest::CreateChannel( - rtc::Thread* thread, - cricket::MediaEngineInterface* engine, - cricket::FakeDataMediaChannel* ch, - cricket::TransportController* transport_controller, + rtc::Thread* thread, cricket::MediaEngineInterface* engine, + cricket::FakeDataMediaChannel* ch, cricket::BaseSession* session, bool rtcp) { cricket::DataChannel* channel = new cricket::DataChannel( - thread, ch, transport_controller, cricket::CN_DATA, rtcp); + thread, ch, session, 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 11fd41dd14..c109d60388 100644 --- a/talk/session/media/channelmanager.cc +++ b/talk/session/media/channelmanager.cc @@ -319,18 +319,23 @@ void ChannelManager::Terminate_w() { VoiceChannel* ChannelManager::CreateVoiceChannel( webrtc::MediaControllerInterface* media_controller, - TransportController* transport_controller, + BaseSession* session, const std::string& content_name, bool rtcp, const AudioOptions& options) { return worker_thread_->Invoke( - Bind(&ChannelManager::CreateVoiceChannel_w, this, media_controller, - transport_controller, content_name, rtcp, options)); + Bind(&ChannelManager::CreateVoiceChannel_w, + this, + media_controller, + session, + content_name, + rtcp, + options)); } VoiceChannel* ChannelManager::CreateVoiceChannel_w( webrtc::MediaControllerInterface* media_controller, - TransportController* transport_controller, + BaseSession* session, const std::string& content_name, bool rtcp, const AudioOptions& options) { @@ -342,9 +347,9 @@ VoiceChannel* ChannelManager::CreateVoiceChannel_w( if (!media_channel) return nullptr; - VoiceChannel* voice_channel = - new VoiceChannel(worker_thread_, media_engine_.get(), media_channel, - transport_controller, content_name, rtcp); + VoiceChannel* voice_channel = new VoiceChannel( + worker_thread_, media_engine_.get(), media_channel, + session, content_name, rtcp); if (!voice_channel->Init()) { delete voice_channel; return nullptr; @@ -375,18 +380,23 @@ void ChannelManager::DestroyVoiceChannel_w(VoiceChannel* voice_channel) { VideoChannel* ChannelManager::CreateVideoChannel( webrtc::MediaControllerInterface* media_controller, - TransportController* transport_controller, + BaseSession* session, const std::string& content_name, bool rtcp, const VideoOptions& options) { return worker_thread_->Invoke( - Bind(&ChannelManager::CreateVideoChannel_w, this, media_controller, - transport_controller, content_name, rtcp, options)); + Bind(&ChannelManager::CreateVideoChannel_w, + this, + media_controller, + session, + content_name, + rtcp, + options)); } VideoChannel* ChannelManager::CreateVideoChannel_w( webrtc::MediaControllerInterface* media_controller, - TransportController* transport_controller, + BaseSession* session, const std::string& content_name, bool rtcp, const VideoOptions& options) { @@ -395,12 +405,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, transport_controller, content_name, rtcp); + worker_thread_, media_channel, + session, content_name, rtcp); if (!video_channel->Init()) { delete video_channel; return NULL; @@ -431,20 +441,16 @@ void ChannelManager::DestroyVideoChannel_w(VideoChannel* video_channel) { } DataChannel* ChannelManager::CreateDataChannel( - TransportController* transport_controller, - const std::string& content_name, - bool rtcp, - DataChannelType channel_type) { + BaseSession* session, const std::string& content_name, + bool rtcp, DataChannelType channel_type) { return worker_thread_->Invoke( - Bind(&ChannelManager::CreateDataChannel_w, this, transport_controller, - content_name, rtcp, channel_type)); + Bind(&ChannelManager::CreateDataChannel_w, this, session, content_name, + rtcp, channel_type)); } DataChannel* ChannelManager::CreateDataChannel_w( - TransportController* transport_controller, - const std::string& content_name, - bool rtcp, - DataChannelType data_channel_type) { + BaseSession* session, 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( @@ -456,7 +462,8 @@ DataChannel* ChannelManager::CreateDataChannel_w( } DataChannel* data_channel = new DataChannel( - worker_thread_, media_channel, transport_controller, content_name, rtcp); + worker_thread_, media_channel, + session, 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 31fec0e214..3bfef84de2 100644 --- a/talk/session/media/channelmanager.h +++ b/talk/session/media/channelmanager.h @@ -105,10 +105,11 @@ 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, - TransportController* transport_controller, + BaseSession* session, const std::string& content_name, bool rtcp, const AudioOptions& options); @@ -118,16 +119,15 @@ class ChannelManager : public rtc::MessageHandler, // associated with the specified session. VideoChannel* CreateVideoChannel( webrtc::MediaControllerInterface* media_controller, - TransportController* transport_controller, + BaseSession* session, 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(TransportController* transport_controller, - const std::string& content_name, - bool rtcp, - DataChannelType data_channel_type); + DataChannel* CreateDataChannel( + BaseSession* session, 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); @@ -251,22 +251,21 @@ class ChannelManager : public rtc::MessageHandler, void Terminate_w(); VoiceChannel* CreateVoiceChannel_w( webrtc::MediaControllerInterface* media_controller, - TransportController* transport_controller, + BaseSession* session, const std::string& content_name, bool rtcp, const AudioOptions& options); void DestroyVoiceChannel_w(VoiceChannel* voice_channel); VideoChannel* CreateVideoChannel_w( webrtc::MediaControllerInterface* media_controller, - TransportController* transport_controller, + BaseSession* session, const std::string& content_name, bool rtcp, const VideoOptions& options); void DestroyVideoChannel_w(VideoChannel* video_channel); - DataChannel* CreateDataChannel_w(TransportController* transport_controller, - const std::string& content_name, - bool rtcp, - DataChannelType data_channel_type); + DataChannel* CreateDataChannel_w( + BaseSession* session, 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 c699ee1a5f..71493c8ff8 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,20 +58,14 @@ 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(); @@ -82,8 +76,7 @@ class ChannelManagerTest : public testing::Test { fcm_ = new cricket::FakeCaptureManager(); cm_ = new cricket::ChannelManager( fme_, fdme_, fdm_, fcm_, rtc::Thread::Current()); - transport_controller_ = - new cricket::FakeTransportController(ICEROLE_CONTROLLING); + session_ = new cricket::FakeSession(true); std::vector in_device_list, out_device_list, vid_device_list; in_device_list.push_back("audio-in1"); @@ -98,7 +91,7 @@ class ChannelManagerTest : public testing::Test { } virtual void TearDown() { - delete transport_controller_; + delete session_; delete cm_; cm_ = NULL; fdm_ = NULL; @@ -115,7 +108,7 @@ class ChannelManagerTest : public testing::Test { cricket::FakeDeviceManager* fdm_; cricket::FakeCaptureManager* fcm_; cricket::ChannelManager* cm_; - cricket::FakeTransportController* transport_controller_; + cricket::FakeSession* session_; }; // Test that we startup/shutdown properly. @@ -146,16 +139,15 @@ 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_, transport_controller_, - cricket::CN_AUDIO, false, AudioOptions()); + cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel( + &fake_mc_, session_, cricket::CN_AUDIO, false, AudioOptions()); EXPECT_TRUE(voice_channel != nullptr); - cricket::VideoChannel* video_channel = - cm_->CreateVideoChannel(&fake_mc_, transport_controller_, - cricket::CN_VIDEO, false, VideoOptions()); + cricket::VideoChannel* video_channel = cm_->CreateVideoChannel( + &fake_mc_, session_, cricket::CN_VIDEO, false, VideoOptions()); EXPECT_TRUE(video_channel != nullptr); - cricket::DataChannel* data_channel = cm_->CreateDataChannel( - transport_controller_, cricket::CN_DATA, false, cricket::DCT_RTP); + cricket::DataChannel* data_channel = + cm_->CreateDataChannel(session_, cricket::CN_DATA, + false, cricket::DCT_RTP); EXPECT_TRUE(data_channel != nullptr); cm_->DestroyVideoChannel(video_channel); cm_->DestroyVoiceChannel(voice_channel); @@ -168,19 +160,17 @@ TEST_F(ChannelManagerTest, CreateDestroyChannelsOnThread) { worker_.Start(); EXPECT_TRUE(cm_->set_worker_thread(&worker_)); EXPECT_TRUE(cm_->Init()); - 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()); + delete session_; + session_ = new cricket::FakeSession(&worker_, true); + cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel( + &fake_mc_, session_, cricket::CN_AUDIO, false, AudioOptions()); EXPECT_TRUE(voice_channel != nullptr); - cricket::VideoChannel* video_channel = - cm_->CreateVideoChannel(&fake_mc_, transport_controller_, - cricket::CN_VIDEO, false, VideoOptions()); + cricket::VideoChannel* video_channel = cm_->CreateVideoChannel( + &fake_mc_, session_, cricket::CN_VIDEO, false, VideoOptions()); EXPECT_TRUE(video_channel != nullptr); - cricket::DataChannel* data_channel = cm_->CreateDataChannel( - transport_controller_, cricket::CN_DATA, false, cricket::DCT_RTP); + cricket::DataChannel* data_channel = + cm_->CreateDataChannel(session_, cricket::CN_DATA, + false, cricket::DCT_RTP); EXPECT_TRUE(data_channel != nullptr); cm_->DestroyVideoChannel(video_channel); cm_->DestroyVoiceChannel(voice_channel); @@ -192,22 +182,21 @@ TEST_F(ChannelManagerTest, CreateDestroyChannelsOnThread) { // to create a cricket::TransportChannel TEST_F(ChannelManagerTest, NoTransportChannelTest) { EXPECT_TRUE(cm_->Init()); - transport_controller_->set_fail_channel_creation(true); + session_->set_fail_channel_creation(true); // The test is useless unless the session does not fail creating // cricket::TransportChannel. - ASSERT_TRUE(transport_controller_->CreateTransportChannel_w( + ASSERT_TRUE(session_->CreateChannel( "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP) == nullptr); - cricket::VoiceChannel* voice_channel = - cm_->CreateVoiceChannel(&fake_mc_, transport_controller_, - cricket::CN_AUDIO, false, AudioOptions()); + cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel( + &fake_mc_, session_, cricket::CN_AUDIO, false, AudioOptions()); EXPECT_TRUE(voice_channel == nullptr); - cricket::VideoChannel* video_channel = - cm_->CreateVideoChannel(&fake_mc_, transport_controller_, - cricket::CN_VIDEO, false, VideoOptions()); + cricket::VideoChannel* video_channel = cm_->CreateVideoChannel( + &fake_mc_, session_, cricket::CN_VIDEO, false, VideoOptions()); EXPECT_TRUE(video_channel == nullptr); - cricket::DataChannel* data_channel = cm_->CreateDataChannel( - transport_controller_, cricket::CN_DATA, false, cricket::DCT_RTP); + cricket::DataChannel* data_channel = + cm_->CreateDataChannel(session_, 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 065d08d4c1..4b6bb68a96 100644 --- a/webrtc/base/fakenetwork.h +++ b/webrtc/base/fakenetwork.h @@ -29,7 +29,12 @@ const int kFakeIPv6NetworkPrefixLength = 64; class FakeNetworkManager : public NetworkManagerBase, public MessageHandler { public: - FakeNetworkManager() : thread_(Thread::Current()) {} + FakeNetworkManager() + : thread_(Thread::Current()), + next_index_(0), + started_(false), + sent_first_update_(false) { + } typedef std::vector IfaceList; @@ -53,18 +58,20 @@ class FakeNetworkManager : public NetworkManagerBase, } virtual void StartUpdating() { - ++start_count_; - if (start_count_ == 1) { - sent_first_update_ = false; - thread_->Post(this); - } else { - if (sent_first_update_) { + if (started_) { + if (sent_first_update_) SignalNetworksChanged(); - } + return; } + + started_ = true; + sent_first_update_ = false; + thread_->Post(this); } - virtual void StopUpdating() { --start_count_; } + virtual void StopUpdating() { + started_ = false; + } // MessageHandler interface. virtual void OnMessage(Message* msg) { @@ -75,7 +82,7 @@ class FakeNetworkManager : public NetworkManagerBase, private: void DoUpdateNetworks() { - if (start_count_ == 0) + if (!started_) return; std::vector networks; for (IfaceList::iterator it = ifaces_.begin(); @@ -104,9 +111,9 @@ class FakeNetworkManager : public NetworkManagerBase, Thread* thread_; IfaceList ifaces_; - int next_index_ = 0; - int start_count_ = 0; - bool sent_first_update_ = false; + int next_index_; + bool started_; + bool sent_first_update_; }; } // namespace rtc diff --git a/webrtc/p2p/base/dtlstransport.h b/webrtc/p2p/base/dtlstransport.h index c448eb16fc..9559c1e6d2 100644 --- a/webrtc/p2p/base/dtlstransport.h +++ b/webrtc/p2p/base/dtlstransport.h @@ -11,6 +11,7 @@ #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" @@ -22,31 +23,33 @@ namespace cricket { class PortAllocator; -// 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. +// Base should be a descendant of cricket::Transport +// TODO(hbos): Add appropriate RTC_DCHECK thread checks to all methods. template class DtlsTransport : public Base { public: - DtlsTransport(const std::string& name, + DtlsTransport(rtc::Thread* signaling_thread, + rtc::Thread* worker_thread, + const std::string& content_name, PortAllocator* allocator, const rtc::scoped_refptr& certificate) - : Base(name, allocator), + : Base(signaling_thread, worker_thread, content_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 SetLocalCertificate( + void SetCertificate_w( const rtc::scoped_refptr& certificate) override { + RTC_DCHECK(Base::worker_thread()->IsCurrent()); certificate_ = certificate; } - bool GetLocalCertificate( + bool GetCertificate_w( rtc::scoped_refptr* certificate) override { + RTC_DCHECK(Base::worker_thread()->IsCurrent()); if (!certificate_) return false; @@ -54,13 +57,15 @@ class DtlsTransport : public Base { return true; } - bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) override { + bool SetSslMaxProtocolVersion_w(rtc::SSLProtocolVersion version) override { + RTC_DCHECK(Base::worker_thread()->IsCurrent()); ssl_max_version_ = version; return true; } - bool ApplyLocalTransportDescription(TransportChannelImpl* channel, - std::string* error_desc) override { + bool ApplyLocalTransportDescription_w(TransportChannelImpl* channel, + std::string* error_desc) override { + RTC_DCHECK(Base::worker_thread()->IsCurrent()); rtc::SSLFingerprint* local_fp = Base::local_description()->identity_fingerprint.get(); @@ -93,11 +98,12 @@ class DtlsTransport : public Base { } // Apply the description in the base class. - return Base::ApplyLocalTransportDescription(channel, error_desc); + return Base::ApplyLocalTransportDescription_w(channel, error_desc); } - bool NegotiateTransportDescription(ContentAction local_role, - std::string* error_desc) override { + bool NegotiateTransportDescription_w(ContentAction local_role, + std::string* error_desc) override { + RTC_DCHECK(Base::worker_thread()->IsCurrent()); if (!Base::local_description() || !Base::remote_description()) { const std::string msg = "Local and Remote description must be set before " "transport descriptions are negotiated"; @@ -194,7 +200,7 @@ class DtlsTransport : public Base { } // Now run the negotiation for the base class. - return Base::NegotiateTransportDescription(local_role, error_desc); + return Base::NegotiateTransportDescription_w(local_role, error_desc); } DtlsTransportChannelWrapper* CreateTransportChannel(int component) override { @@ -213,15 +219,18 @@ class DtlsTransport : public Base { Base::DestroyTransportChannel(base_channel); } - bool GetSslRole(rtc::SSLRole* ssl_role) const override { + bool GetSslRole_w(rtc::SSLRole* ssl_role) const override { + RTC_DCHECK(Base::worker_thread()->IsCurrent()); ASSERT(ssl_role != NULL); *ssl_role = secure_role_; return true; } private: - bool ApplyNegotiatedTransportDescription(TransportChannelImpl* channel, - std::string* error_desc) override { + bool ApplyNegotiatedTransportDescription_w( + TransportChannelImpl* channel, + std::string* error_desc) override { + RTC_DCHECK(Base::worker_thread()->IsCurrent()); // Set ssl role. Role must be set before fingerprint is applied, which // initiates DTLS setup. if (!channel->SetSslRole(secure_role_)) { @@ -236,7 +245,7 @@ class DtlsTransport : public Base { return BadTransportDescription("Failed to apply remote fingerprint.", error_desc); } - return Base::ApplyNegotiatedTransportDescription(channel, error_desc); + return Base::ApplyNegotiatedTransportDescription_w(channel, error_desc); } rtc::scoped_refptr certificate_; diff --git a/webrtc/p2p/base/dtlstransportchannel.cc b/webrtc/p2p/base/dtlstransportchannel.cc index d3651e089b..3474237269 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->transport_name(), channel->component()), + Transport* transport, + TransportChannelImpl* channel) + : TransportChannelImpl(channel->content_name(), channel->component()), transport_(transport), worker_thread_(rtc::Thread::Current()), channel_(channel), @@ -105,10 +105,12 @@ DtlsTransportChannelWrapper::DtlsTransportChannelWrapper( &DtlsTransportChannelWrapper::OnReadPacket); channel_->SignalReadyToSend.connect(this, &DtlsTransportChannelWrapper::OnReadyToSend); - channel_->SignalGatheringState.connect( - this, &DtlsTransportChannelWrapper::OnGatheringState); - channel_->SignalCandidateGathered.connect( - this, &DtlsTransportChannelWrapper::OnCandidateGathered); + channel_->SignalRequestSignaling.connect(this, + &DtlsTransportChannelWrapper::OnRequestSignaling); + channel_->SignalCandidateReady.connect(this, + &DtlsTransportChannelWrapper::OnCandidateReady); + channel_->SignalCandidatesAllocationDone.connect(this, + &DtlsTransportChannelWrapper::OnCandidatesAllocationDone); channel_->SignalRoleConflict.connect(this, &DtlsTransportChannelWrapper::OnRoleConflict); channel_->SignalRouteChange.connect(this, @@ -211,7 +213,7 @@ bool DtlsTransportChannelWrapper::SetRemoteFingerprint( return true; } - // Allow SetRemoteFingerprint with a NULL digest even if SetLocalCertificate + // Allow SetRemoteFingerprint with a NULL digest even if SetLocalIdentity // hasn't been called. if (dtls_state_ > STATE_OFFERED || (dtls_state_ == STATE_NONE && !digest_alg.empty())) { @@ -608,17 +610,22 @@ bool DtlsTransportChannelWrapper::HandleDtlsPacket(const char* data, return downward_->OnPacketReceived(data, size); } -void DtlsTransportChannelWrapper::OnGatheringState( +void DtlsTransportChannelWrapper::OnRequestSignaling( TransportChannelImpl* channel) { ASSERT(channel == channel_); - SignalGatheringState(this); + SignalRequestSignaling(this); } -void DtlsTransportChannelWrapper::OnCandidateGathered( - TransportChannelImpl* channel, - const Candidate& c) { +void DtlsTransportChannelWrapper::OnCandidateReady( + TransportChannelImpl* channel, const Candidate& c) { ASSERT(channel == channel_); - SignalCandidateGathered(this, c); + SignalCandidateReady(this, c); +} + +void DtlsTransportChannelWrapper::OnCandidatesAllocationDone( + TransportChannelImpl* channel) { + ASSERT(channel == channel_); + SignalCandidatesAllocationDone(this); } void DtlsTransportChannelWrapper::OnRoleConflict( diff --git a/webrtc/p2p/base/dtlstransportchannel.h b/webrtc/p2p/base/dtlstransportchannel.h index 273291df8b..ddedcbcaa8 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: - explicit StreamInterfaceChannel(TransportChannel* channel); + StreamInterfaceChannel(TransportChannel* channel); // Push in a packet; this gets pulled out from Read(). bool OnPacketReceived(const char* data, size_t size); @@ -35,14 +35,10 @@ 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 @@ -97,8 +93,12 @@ 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,8 +109,7 @@ 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; @@ -121,11 +120,15 @@ 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); @@ -165,7 +168,9 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl { } // TransportChannelImpl calls. - Transport* GetTransport() override { return transport_; } + Transport* GetTransport() override { + return transport_; + } TransportChannelState GetState() const override { return channel_->GetState(); @@ -187,12 +192,11 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl { void Connect() override; - IceGatheringState gathering_state() const override { - return channel_->gathering_state(); + void OnSignalingReady() override { + channel_->OnSignalingReady(); } - - void AddRemoteCandidate(const Candidate& candidate) override { - channel_->AddRemoteCandidate(candidate); + void OnCandidate(const Candidate& candidate) override { + channel_->OnCandidate(candidate); } void SetReceivingTimeout(int receiving_timeout_ms) override { @@ -213,8 +217,9 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl { bool SetupDtls(); bool MaybeStartDtls(); bool HandleDtlsPacket(const char* data, size_t size); - void OnGatheringState(TransportChannelImpl* channel); - void OnCandidateGathered(TransportChannelImpl* channel, const Candidate& c); + void OnRequestSignaling(TransportChannelImpl* channel); + void OnCandidateReady(TransportChannelImpl* channel, const Candidate& c); + void OnCandidatesAllocationDone(TransportChannelImpl* channel); 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 95696e222c..10640f99e3 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/faketransportcontroller.h" +#include "webrtc/p2p/base/fakesession.h" #include "webrtc/base/common.h" #include "webrtc/base/dscp.h" #include "webrtc/base/gunit.h" @@ -21,6 +21,7 @@ #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())) { \ @@ -44,14 +45,19 @@ enum Flags { NF_REOFFER = 0x1, NF_EXPECT_FAILURE = 0x2 }; class DtlsTestClient : public sigslot::has_slots<> { public: - 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) {} + 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) { + } void CreateCertificate(rtc::KeyType key_type) { certificate_ = rtc::RTCCertificate::Create( rtc::scoped_ptr( @@ -65,12 +71,13 @@ class DtlsTestClient : public sigslot::has_slots<> { use_dtls_srtp_ = true; } void SetupMaxProtocolVersion(rtc::SSLProtocolVersion version) { - ASSERT(!transport_); + ASSERT(transport_.get() == NULL); ssl_max_version_ = version; } void SetupChannels(int count, cricket::IceRole role) { transport_.reset(new cricket::DtlsTransport( - "dtls content name", nullptr, certificate_)); + signaling_thread_, worker_thread_, "dtls content name", nullptr, + certificate_)); transport_->SetAsync(true); transport_->SetIceRole(role); transport_->SetIceTiebreaker( @@ -111,8 +118,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). @@ -156,18 +163,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) - ? nullptr - : local_fingerprint.get(), + (action == cricket::CA_ANSWER && !remote_cert) ? + NULL : 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 @@ -192,9 +199,7 @@ class DtlsTestClient : public sigslot::has_slots<> { return true; } - bool all_channels_writable() const { - return transport_->all_channels_writable(); - } + bool writable() const { return transport_->writable(); } void CheckRole(rtc::SSLRole role) { if (role == rtc::SSL_CLIENT) { @@ -332,8 +337,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); } @@ -367,6 +372,8 @@ 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_; @@ -382,13 +389,16 @@ class DtlsTestClient : public sigslot::has_slots<> { class DtlsTransportChannelTest : public testing::Test { public: - DtlsTransportChannelTest() - : client1_("P1"), - client2_("P2"), - channel_ct_(1), - use_dtls_(false), - use_dtls_srtp_(false), - ssl_expected_version_(rtc::SSL_PROTOCOL_DTLS_10) {} + 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) { + } void SetChannelCount(size_t channel_ct) { channel_ct_ = static_cast(channel_ct); @@ -430,10 +440,8 @@ class DtlsTransportChannelTest : public testing::Test { if (!rv) return false; - EXPECT_TRUE_WAIT( - client1_.all_channels_writable() && client2_.all_channels_writable(), - 10000); - if (!client1_.all_channels_writable() || !client2_.all_channels_writable()) + EXPECT_TRUE_WAIT(client1_.writable() && client2_.writable(), 10000); + if (!client1_.writable() || !client2_.writable()) return false; // Check that we used the right roles. @@ -810,9 +818,7 @@ TEST_F(DtlsTransportChannelTest, TestRenegotiateBeforeConnect) { cricket::CONNECTIONROLE_ACTIVE, NF_REOFFER); bool rv = client1_.Connect(&client2_); EXPECT_TRUE(rv); - EXPECT_TRUE_WAIT( - client1_.all_channels_writable() && client2_.all_channels_writable(), - 10000); + EXPECT_TRUE_WAIT(client1_.writable() && client2_.writable(), 10000); TestTransfer(0, 1000, 100, true); TestTransfer(1, 1000, 100, true); @@ -831,8 +837,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()->GetLocalCertificate(&certificate1)); - ASSERT_TRUE(client2_.transport()->GetLocalCertificate(&certificate2)); + ASSERT_TRUE(client1_.transport()->GetCertificate(&certificate1)); + ASSERT_TRUE(client2_.transport()->GetCertificate(&certificate2)); ASSERT_NE(certificate1->ssl_certificate().ToPEMString(), certificate2->ssl_certificate().ToPEMString()); ASSERT_FALSE( @@ -855,8 +861,8 @@ TEST_F(DtlsTransportChannelTest, TestCertificatesAfterConnect) { rtc::scoped_ptr remote_cert2; // After connection, each side has a distinct local certificate. - ASSERT_TRUE(client1_.transport()->GetLocalCertificate(&certificate1)); - ASSERT_TRUE(client2_.transport()->GetLocalCertificate(&certificate2)); + ASSERT_TRUE(client1_.transport()->GetCertificate(&certificate1)); + ASSERT_TRUE(client2_.transport()->GetCertificate(&certificate2)); ASSERT_NE(certificate1->ssl_certificate().ToPEMString(), certificate2->ssl_certificate().ToPEMString()); diff --git a/webrtc/p2p/base/faketransportcontroller.h b/webrtc/p2p/base/fakesession.h similarity index 53% rename from webrtc/p2p/base/faketransportcontroller.h rename to webrtc/p2p/base/fakesession.h index 4fbee23592..bd3c089025 100644 --- a/webrtc/p2p/base/faketransportcontroller.h +++ b/webrtc/p2p/base/fakesession.h @@ -8,31 +8,30 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_P2P_BASE_FAKETRANSPORTCONTROLLER_H_ -#define WEBRTC_P2P_BASE_FAKETRANSPORTCONTROLLER_H_ +#ifndef WEBRTC_P2P_BASE_FAKESESSION_H_ +#define WEBRTC_P2P_BASE_FAKESESSION_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; }; @@ -44,12 +43,24 @@ class FakeTransportChannel : public TransportChannelImpl, public rtc::MessageHandler { public: explicit FakeTransportChannel(Transport* transport, - const std::string& name, + const std::string& content_name, int component) - : TransportChannelImpl(name, component), + : TransportChannelImpl(content_name, component), transport_(transport), - dtls_fingerprint_("", nullptr, 0) {} - ~FakeTransportChannel() { Reset(); } + 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(); + } uint64 IceTiebreaker() const { return tiebreaker_; } IceMode remote_ice_mode() const { return remote_ice_mode_; } @@ -61,23 +72,24 @@ class FakeTransportChannel : public TransportChannelImpl, return dtls_fingerprint_; } - // If async, will send packets by "Post"-ing to message queue instead of - // synchronously "Send"-ing. - void SetAsync(bool async) { async_ = async; } + 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 had_connection_ ? TransportChannelState::STATE_FAILED - : TransportChannelState::STATE_INIT; + return TransportChannelState::STATE_FAILED; } if (connection_count_ == 1) { return TransportChannelState::STATE_COMPLETED; } - return TransportChannelState::STATE_CONNECTING; + return TransportChannelState::STATE_FAILED; } void SetIceRole(IceRole role) override { role_ = role; } @@ -97,8 +109,7 @@ 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; @@ -115,37 +126,29 @@ class FakeTransportChannel : public TransportChannelImpl, void Connect() override { if (state_ == STATE_INIT) { state_ = STATE_CONNECTING; - // Connect is expected to start candidate gathering - if (gathering_state_ != kIceGatheringGathering) { - gathering_state_ = kIceGatheringGathering; - SignalGatheringState(this); - } } } - - IceGatheringState gathering_state() const override { - return gathering_state_; - } - - void Reset() { + virtual void Reset() { if (state_ != STATE_INIT) { state_ = STATE_INIT; if (dest_) { dest_->state_ = STATE_INIT; - dest_->dest_ = nullptr; - dest_ = nullptr; + dest_->dest_ = NULL; + dest_ = NULL; } } } - 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 (local_cert_ && dest_->local_cert_) { + if (certificate_ && dest_->certificate_) { do_dtls_ = true; dest_->do_dtls_ = true; NegotiateSrtpCiphers(); @@ -156,7 +159,7 @@ class FakeTransportChannel : public TransportChannelImpl, dest_->set_writable(true); } else if (state_ == STATE_CONNECTED && !dest) { // Simulates loss of connectivity, by asymmetrically forgetting dest_. - dest_ = nullptr; + dest_ = NULL; state_ = STATE_CONNECTING; set_writable(false); } @@ -165,31 +168,18 @@ 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 SetCandidatesGatheringComplete() { - if (gathering_state_ != kIceGatheringComplete) { - gathering_state_ = kIceGatheringComplete; - SignalGatheringState(this); - } + void SetReceiving(bool receiving) { + set_receiving(receiving); } - void SetReceiving(bool receiving) { set_receiving(receiving); } + void SetReceivingTimeout(int timeout) 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 { + int SendPacket(const char* data, size_t len, + const rtc::PacketOptions& options, int flags) override { if (state_ != STATE_CONNECTED) { return -1; } @@ -206,25 +196,32 @@ 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; } - - void AddRemoteCandidate(const Candidate& candidate) override { - remote_candidates_.push_back(candidate); + 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 { } - 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) { - local_cert_ = certificate; + const rtc::scoped_refptr& certificate) override { + certificate_ = certificate; return true; } @@ -232,7 +229,9 @@ 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; @@ -247,10 +246,13 @@ 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 { - return local_cert_; + rtc::scoped_refptr + GetLocalCertificate() const override { + return certificate_; } bool GetRemoteSSLCertificate(rtc::SSLCertificate** cert) const override { @@ -275,12 +277,12 @@ class FakeTransportChannel : public TransportChannelImpl, return false; } - void NegotiateSrtpCiphers() { + virtual 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; @@ -297,39 +299,27 @@ 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_ = 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; + FakeTransportChannel* dest_; + State state_; + bool async_; + rtc::scoped_refptr certificate_; + rtc::FakeSSLCertificate* remote_cert_; + bool do_dtls_; std::vector srtp_ciphers_; std::string chosen_srtp_cipher_; - int receiving_timeout_ = -1; - IceRole role_ = ICEROLE_UNKNOWN; - uint64 tiebreaker_ = 0; + IceRole role_; + uint64 tiebreaker_; std::string ice_ufrag_; std::string ice_pwd_; std::string remote_ice_ufrag_; std::string remote_ice_pwd_; - IceMode remote_ice_mode_ = ICEMODE_FULL; - rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_10; + IceMode remote_ice_mode_; rtc::SSLFingerprint dtls_fingerprint_; - rtc::SSLRole ssl_role_ = rtc::SSL_CLIENT; - size_t connection_count_ = 0; - IceGatheringState gathering_state_ = kIceGatheringNew; - bool had_connection_ = false; + rtc::SSLRole ssl_role_; + size_t connection_count_; }; // Fake transport class, which can be passed to anything that needs a Transport. @@ -338,65 +328,42 @@ class FakeTransportChannel : public TransportChannelImpl, class FakeTransport : public Transport { public: typedef std::map ChannelMap; - - 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(); } + 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(); + } 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 (const auto& kv : channels_) { - kv.second->SetLocalCertificate(certificate_); - SetChannelDestination(kv.first, kv.second); + for (ChannelMap::iterator it = channels_.begin(); it != channels_.end(); + ++it) { + it->second->SetLocalCertificate(certificate_); + SetChannelDestination(it->first, it->second); } } void SetWritable(bool writable) { - for (const auto& kv : channels_) { - kv.second->SetWritable(writable); + for (ChannelMap::iterator it = channels_.begin(); it != channels_.end(); + ++it) { + it->second->SetWritable(writable); } } - void SetLocalCertificate( - const rtc::scoped_refptr& certificate) override { + void set_certificate( + const rtc::scoped_refptr& certificate) { certificate_ = certificate; } - bool GetLocalCertificate( - rtc::scoped_refptr* certificate) override { - if (!certificate_) - return false; - - *certificate = certificate_; - 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; @@ -404,124 +371,129 @@ class FakeTransport : public Transport { protected: TransportChannelImpl* CreateTransportChannel(int component) override { if (channels_.find(component) != channels_.end()) { - return nullptr; + return NULL; } FakeTransportChannel* channel = - new FakeTransportChannel(this, name(), component); - channel->set_ssl_max_protocol_version(ssl_max_version_); + 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( + const rtc::scoped_refptr& certificate) override { + certificate_ = certificate; + } + bool GetCertificate_w( + rtc::scoped_refptr* certificate) override { + if (!certificate_) + return false; + + *certificate = certificate_; + return true; + } private: FakeTransportChannel* GetFakeChannel(int component) { - auto it = channels_.find(component); - return (it != channels_.end()) ? it->second : nullptr; + ChannelMap::iterator it = channels_.find(component); + return (it != channels_.end()) ? it->second : NULL; } - - void SetChannelDestination(int component, FakeTransportChannel* channel) { - FakeTransportChannel* dest_channel = nullptr; + void SetChannelDestination(int component, + FakeTransportChannel* channel) { + FakeTransportChannel* dest_channel = NULL; 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_ = nullptr; - bool async_ = false; + FakeTransport* dest_; + bool async_; rtc::scoped_refptr certificate_; - rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_10; }; -// 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 { +// 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 { public: - 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); + 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) { } - 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) { + FakeTransport* GetTransport(const std::string& content_name) { return static_cast( - TransportController::GetTransport_w(transport_name)); + BaseSession::GetTransport(content_name)); } - void Connect(FakeTransportController* dest) { - worker_thread()->Invoke( - rtc::Bind(&FakeTransportController::Connect_w, this, dest)); - } - - TransportChannel* CreateTransportChannel_w(const std::string& transport_name, - int component) override { - if (fail_create_channel_) { - return nullptr; + 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)); } - return TransportController::CreateTransportChannel_w(transport_name, - component); + } + + TransportChannel* CreateChannel(const std::string& content_name, + int component) override { + if (fail_create_channel_) { + return NULL; + } + return BaseSession::CreateChannel(content_name, component); } void set_fail_channel_creation(bool fail_channel_creation) { fail_create_channel_ = fail_channel_creation; } - protected: - Transport* CreateTransport_w(const std::string& transport_name) override { - return new FakeTransport(transport_name); - } + // 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* - 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)); + static_cast(it->second->impl())->set_certificate + (certificate); } } - void ConnectChannels_w() { - for (auto& kv : transports()) { - FakeTransport* transport = static_cast(kv.second); - transport->ConnectChannels(); + 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(); } } @@ -531,4 +503,4 @@ class FakeTransportController : public TransportController { } // namespace cricket -#endif // WEBRTC_P2P_BASE_FAKETRANSPORTCONTROLLER_H_ +#endif // WEBRTC_P2P_BASE_FAKESESSION_H_ diff --git a/webrtc/p2p/base/p2ptransport.cc b/webrtc/p2p/base/p2ptransport.cc index 6c8d657e3c..b919fde31a 100644 --- a/webrtc/p2p/base/p2ptransport.cc +++ b/webrtc/p2p/base/p2ptransport.cc @@ -20,8 +20,12 @@ namespace cricket { -P2PTransport::P2PTransport(const std::string& name, PortAllocator* allocator) - : Transport(name, allocator) { +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() { @@ -29,7 +33,8 @@ P2PTransport::~P2PTransport() { } TransportChannelImpl* P2PTransport::CreateTransportChannel(int component) { - return new P2PTransportChannel(name(), component, this, port_allocator()); + return new P2PTransportChannel(content_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 0f965b4cdc..2e27bd8a3b 100644 --- a/webrtc/p2p/base/p2ptransport.h +++ b/webrtc/p2p/base/p2ptransport.h @@ -16,10 +16,12 @@ namespace cricket { -// Everything in this class should be called on the worker thread. class P2PTransport : public Transport { public: - P2PTransport(const std::string& name, PortAllocator* allocator); + P2PTransport(rtc::Thread* signaling_thread, + rtc::Thread* worker_thread, + const std::string& content_name, + PortAllocator* allocator); virtual ~P2PTransport(); protected: diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc index bcd9933e64..094a8dcc8f 100644 --- a/webrtc/p2p/base/p2ptransportchannel.cc +++ b/webrtc/p2p/base/p2ptransportchannel.cc @@ -11,7 +11,6 @@ #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. @@ -170,27 +169,27 @@ bool ShouldSwitch(cricket::Connection* a_conn, cricket::Connection* b_conn) { namespace cricket { -P2PTransportChannel::P2PTransportChannel(const std::string& transport_name, +P2PTransportChannel::P2PTransportChannel(const std::string& content_name, int component, P2PTransport* transport, - 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) { + 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) { } P2PTransportChannel::~P2PTransportChannel() { @@ -231,7 +230,6 @@ 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) { @@ -266,9 +264,8 @@ void P2PTransportChannel::SetIceTiebreaker(uint64 tiebreaker) { TransportChannelState P2PTransportChannel::GetState() const { std::set networks; - if (connections_.empty()) { - return had_connection_ ? TransportChannelState::STATE_FAILED - : TransportChannelState::STATE_INIT; + if (connections_.size() == 0) { + return TransportChannelState::STATE_FAILED; } for (uint32 i = 0; i < connections_.size(); ++i) { @@ -303,7 +300,7 @@ void P2PTransportChannel::SetIceCredentials(const std::string& ice_ufrag, if (ice_restart) { // Restart candidate gathering. - StartGatheringCandidates(); + Allocate(); } } @@ -358,7 +355,7 @@ void P2PTransportChannel::Connect() { } // Kick off an allocator session - StartGatheringCandidates(); + Allocate(); // Start pinging as the ports come in. thread()->Post(this, MSG_PING); @@ -411,21 +408,17 @@ 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) { - SignalCandidateGathered(this, candidates[i]); + SignalCandidateReady(this, candidates[i]); } } void P2PTransportChannel::OnCandidatesAllocationDone( PortAllocatorSession* session) { ASSERT(worker_thread_ == rtc::Thread::Current()); - gathering_state_ = kIceGatheringComplete; - LOG(LS_INFO) << "P2PTransportChannel: " << transport_name() << ", component " - << component() << " gathering complete"; - SignalGatheringState(this); + SignalCandidatesAllocationDone(this); } // Handle stun packets @@ -496,7 +489,8 @@ 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; } @@ -546,7 +540,8 @@ 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,6 +570,16 @@ 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); @@ -596,7 +601,7 @@ void P2PTransportChannel::OnNominated(Connection* conn) { } } -void P2PTransportChannel::AddRemoteCandidate(const Candidate& candidate) { +void P2PTransportChannel::OnCandidate(const Candidate& candidate) { ASSERT(worker_thread_ == rtc::Thread::Current()); uint32 generation = candidate.generation(); @@ -845,7 +850,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.readable = @@ -880,14 +885,12 @@ rtc::DiffServCodePoint P2PTransportChannel::DefaultDscpValue() const { return static_cast (it->second); } -void P2PTransportChannel::StartGatheringCandidates() { - // Time for a new allocator - if (gathering_state_ != kIceGatheringGathering) { - gathering_state_ = kIceGatheringGathering; - SignalGatheringState(this); - } - AddAllocatorSession(allocator_->CreateSession( - SessionId(), transport_name(), component(), ice_ufrag_, ice_pwd_)); +// 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. @@ -1249,7 +1252,8 @@ 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_) { @@ -1331,10 +1335,9 @@ 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 f07a81cb0b..a00c17e237 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& transport_name, + P2PTransportChannel(const std::string& content_name, int component, P2PTransport* transport, - PortAllocator* allocator); - virtual ~P2PTransportChannel(); + PortAllocator *allocator); + ~P2PTransportChannel() override; // From TransportChannelImpl: Transport* GetTransport() override { return transport_; } @@ -69,19 +69,15 @@ class P2PTransportChannel : public TransportChannelImpl, const std::string& ice_pwd) override; void SetRemoteIceMode(IceMode mode) override; void Connect() override; - IceGatheringState gathering_state() const override { - return gathering_state_; - } - void AddRemoteCandidate(const Candidate& candidate) override; + void OnSignalingReady() override; + void OnCandidate(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_; } @@ -100,9 +96,13 @@ 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,10 +110,14 @@ 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 { @@ -161,7 +165,7 @@ class P2PTransportChannel : public TransportChannelImpl, return allocator_sessions_.back(); } - void StartGatheringCandidates(); + void Allocate(); void UpdateConnectionStates(); void RequestSort(); void SortConnections(); @@ -208,7 +212,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(); @@ -218,9 +222,10 @@ 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_; @@ -232,7 +237,6 @@ 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_; @@ -243,7 +247,6 @@ 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 25a5d9abbc..3bb3be5ae8 100644 --- a/webrtc/p2p/base/p2ptransportchannel_unittest.cc +++ b/webrtc/p2p/base/p2ptransportchannel_unittest.cc @@ -297,8 +297,10 @@ 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->SignalCandidateGathered.connect( - this, &P2PTransportChannelTestBase::OnCandidate); + channel->SignalRequestSignaling.connect( + this, &P2PTransportChannelTestBase::OnChannelRequestSignaling); + channel->SignalCandidateReady.connect(this, + &P2PTransportChannelTestBase::OnCandidate); channel->SignalReadPacket.connect( this, &P2PTransportChannelTestBase::OnReadPacket); channel->SignalRoleConflict.connect( @@ -387,9 +389,10 @@ 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 @@ -509,8 +512,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); @@ -623,6 +626,9 @@ 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) { @@ -663,7 +669,7 @@ class P2PTransportChannelTestBase : public testing::Test, } LOG(LS_INFO) << "Candidate(" << data->channel->component() << "->" << rch->component() << "): " << c.ToString(); - rch->AddRemoteCandidate(c); + rch->OnCandidate(c); break; } } @@ -798,10 +804,8 @@ 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( @@ -817,8 +821,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); @@ -1022,14 +1026,15 @@ 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) \ @@ -1084,7 +1089,8 @@ 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(); @@ -1093,7 +1099,8 @@ 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()->readable() && ep1_ch1()->writable() && @@ -1119,7 +1126,8 @@ 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); @@ -1163,7 +1171,8 @@ 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); @@ -1205,7 +1214,8 @@ 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; @@ -1220,7 +1230,8 @@ 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); @@ -1241,7 +1252,8 @@ 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); @@ -1402,10 +1414,11 @@ 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); @@ -1413,8 +1426,10 @@ TEST_F(P2PTransportChannelTest, TestForceTurn) { CreateChannels(1); - EXPECT_TRUE_WAIT(ep1_ch1()->readable() && ep1_ch1()->writable() && - ep2_ch1()->readable() && ep2_ch1()->writable(), + EXPECT_TRUE_WAIT(ep1_ch1()->readable() && + ep1_ch1()->writable() && + ep2_ch1()->readable() && + ep2_ch1()->writable(), 2000); EXPECT_TRUE(ep1_ch1()->best_connection() && @@ -1460,7 +1475,8 @@ 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. @@ -1577,11 +1593,17 @@ 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) { @@ -1628,8 +1650,8 @@ TEST_F(P2PTransportChannelPingTest, TestTriggeredChecks) { cricket::P2PTransportChannel ch("trigger checks", 1, nullptr, &pa); PrepareChannel(&ch); ch.Connect(); - ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 1)); - ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 2)); + ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1)); + ch.OnCandidate(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); @@ -1652,8 +1674,8 @@ TEST_F(P2PTransportChannelPingTest, TestNoTriggeredChecksWhenWritable) { cricket::P2PTransportChannel ch("trigger checks", 1, nullptr, &pa); PrepareChannel(&ch); ch.Connect(); - ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 1)); - ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 2)); + ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1)); + ch.OnCandidate(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); @@ -1678,14 +1700,14 @@ TEST_F(P2PTransportChannelPingTest, ConnectionResurrection) { ch.Connect(); // Create conn1 and keep track of original candidate priority. - ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 1)); + ch.OnCandidate(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 // readable/writable. This will prune conn1. - ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 2)); + ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 2)); cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2); ASSERT_TRUE(conn2 != nullptr); conn2->ReceivedPing(); @@ -1732,7 +1754,7 @@ TEST_F(P2PTransportChannelPingTest, TestReceivingStateChange) { EXPECT_EQ(500, ch.receiving_timeout()); EXPECT_EQ(50, ch.check_receiving_delay()); ch.Connect(); - ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 1)); + ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1)); cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1); ASSERT_TRUE(conn1 != nullptr); @@ -1753,14 +1775,14 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBeforeNomination) { PrepareChannel(&ch); ch.SetIceRole(cricket::ICEROLE_CONTROLLED); ch.Connect(); - ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 1)); + ch.OnCandidate(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.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 10)); + ch.OnCandidate(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()); @@ -1768,7 +1790,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.AddRemoteCandidate(CreateCandidate("3.3.3.3", 3, 1)); + ch.OnCandidate(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. @@ -1783,7 +1805,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.AddRemoteCandidate(CreateCandidate("4.4.4.4", 4, 100)); + ch.OnCandidate(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()); @@ -1826,7 +1848,7 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionFromUnknownAddress) { EXPECT_EQ(conn1, ch.best_connection()); // Another connection is nominated via use_candidate. - ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 1)); + ch.OnCandidate(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. @@ -1872,7 +1894,7 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBasedOnMediaReceived) { PrepareChannel(&ch); ch.SetIceRole(cricket::ICEROLE_CONTROLLED); ch.Connect(); - ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 10)); + ch.OnCandidate(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()); @@ -1880,7 +1902,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.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 1)); + ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 1)); cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2); ASSERT_TRUE(conn2 != nullptr); conn2->ReceivedPing(); // Become readable. diff --git a/webrtc/p2p/base/session.cc b/webrtc/p2p/base/session.cc index b80a23368b..23680b9be8 100644 --- a/webrtc/p2p/base/session.cc +++ b/webrtc/p2p/base/session.cc @@ -10,6 +10,11 @@ #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" @@ -17,15 +22,266 @@ #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: @@ -70,6 +326,7 @@ 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), @@ -77,11 +334,13 @@ BaseSession::BaseSession(rtc::Thread* signaling_thread, worker_thread_(worker_thread), port_allocator_(port_allocator), sid_(sid), - transport_controller_(new TransportController(signaling_thread, - worker_thread, - port_allocator)) { + 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) { ASSERT(signaling_thread->IsCurrent()); - set_initiator(initiator); } BaseSession::~BaseSession() { @@ -91,6 +350,11 @@ 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 { @@ -120,23 +384,37 @@ 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, @@ -150,17 +428,23 @@ bool BaseSession::PushdownTransportDescription(ContentSource source, bool BaseSession::PushdownLocalTransportDescription( const SessionDescription* sdesc, ContentAction action, - std::string* err) { - ASSERT(signaling_thread()->IsCurrent()); + 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; + } - if (!sdesc) { - return false; - } - - for (const TransportInfo& tinfo : sdesc->transport_infos()) { - if (!transport_controller_->SetLocalTransportDescription( - tinfo.content_name, tinfo.description, action, err)) { - return false; + iter->second->ConnectChannels(); } } @@ -170,23 +454,134 @@ bool BaseSession::PushdownLocalTransportDescription( bool BaseSession::PushdownRemoteTransportDescription( const SessionDescription* sdesc, ContentAction action, - std::string* err) { - ASSERT(signaling_thread()->IsCurrent()); + std::string* error_desc) { + // Update the Transports with the right information. + for (TransportMap::iterator iter = transports_.begin(); + iter != transports_.end(); ++iter) { + TransportDescription tdesc; - if (!sdesc) { - return false; - } - - for (const TransportInfo& tinfo : sdesc->transport_infos()) { - if (!transport_controller_->SetRemoteTransportDescription( - tinfo.content_name, tinfo.description, action, err)) { - return false; + // 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; + } } } 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_) { @@ -206,14 +601,180 @@ void BaseSession::SetError(Error error, const std::string& error_desc) { } } -void BaseSession::SetIceConnectionReceivingTimeout(int timeout_ms) { - transport_controller_->SetIceConnectionReceivingTimeout(timeout_ms); +void BaseSession::OnSignalingReady() { + ASSERT(signaling_thread()->IsCurrent()); + for (TransportMap::iterator iter = transports_.begin(); + iter != transports_.end(); ++iter) { + iter->second->OnSignalingReady(); + } +} + +// 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::LogState(State old_state, State new_state) { LOG(LS_INFO) << "Session:" << id() << " Old state:" << StateToString(old_state) - << " New state:" << StateToString(new_state); + << " New state:" << StateToString(new_state) + << " Type:" << content_type(); } // static diff --git a/webrtc/p2p/base/session.h b/webrtc/p2p/base/session.h index 7fcf29b9f2..8d7aa21c22 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,8 +31,136 @@ class BaseSession; class P2PTransportChannel; class Transport; class TransportChannel; +class TransportChannelProxy; class TransportChannelImpl; -class TransportController; + +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; // Statistics for all the transports of this session. typedef std::map TransportStatsMap; @@ -96,6 +224,7 @@ 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(); @@ -107,6 +236,14 @@ 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; @@ -123,9 +260,6 @@ 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. @@ -146,25 +280,151 @@ 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_; + } + 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); - TransportController* transport_controller() { - return transport_controller_.get(); - } - protected: + bool IsCandidateAllocationDone() const; + 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( @@ -174,6 +434,8 @@ 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); @@ -187,10 +449,21 @@ 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_ptr transport_controller_; + rtc::scoped_refptr certificate_; + rtc::SSLProtocolVersion ssl_max_version_; 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 new file mode 100644 index 0000000000..3419cc3c46 --- /dev/null +++ b/webrtc/p2p/base/session_unittest.cc @@ -0,0 +1,100 @@ +/* + * 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 e3e6ca0c08..3322e3898a 100644 --- a/webrtc/p2p/base/transport.cc +++ b/webrtc/p2p/base/transport.cc @@ -8,8 +8,6 @@ * 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" @@ -24,6 +22,40 @@ namespace cricket { using rtc::Bind; +enum { + MSG_ONSIGNALINGREADY = 1, + MSG_ONREMOTECANDIDATE, + MSG_READSTATE, + 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()) @@ -65,59 +97,58 @@ static bool IceCredentialsChanged(const TransportDescription& old_desc, new_desc.ice_ufrag, new_desc.ice_pwd); } -Transport::Transport(const std::string& name, PortAllocator* allocator) - : name_(name), allocator_(allocator) {} +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() { - 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; + ASSERT(signaling_thread_->IsCurrent()); + ASSERT(destroyed_); } void Transport::SetIceRole(IceRole role) { - ice_role_ = role; - for (auto& iter : channels_) { - iter.second->SetIceRole(ice_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)); } 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; @@ -126,6 +157,12 @@ bool Transport::GetRemoteSSLCertificate(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); @@ -136,74 +173,35 @@ bool Transport::SetLocalTransportDescription( const TransportDescription& description, ContentAction action, std::string* 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; - // This kicks off candidate gathering. - ConnectChannels(); - } - - return ret; + return worker_thread_->Invoke(Bind( + &Transport::SetLocalTransportDescription_w, this, + description, action, error_desc)); } bool Transport::SetRemoteTransportDescription( const TransportDescription& description, ContentAction action, std::string* 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; + return worker_thread_->Invoke(Bind( + &Transport::SetRemoteTransportDescription_w, this, + description, action, error_desc)); } 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; @@ -219,7 +217,7 @@ TransportChannelImpl* Transport::CreateChannel(int component) { // Increase the ref count. iterator->second.AddRef(); - channels_destroyed_ = false; + destroyed_ = false; if (impl_exists) { // If this is an existing channel, we should just return it without @@ -231,22 +229,24 @@ TransportChannelImpl* Transport::CreateChannel(int component) { impl->SetIceRole(ice_role_); impl->SetIceTiebreaker(tiebreaker_); impl->SetReceivingTimeout(channel_receiving_timeout_); - // TODO(ronghuawu): Change CreateChannel to be able to return error since - // below Apply**Description calls can fail. + // TODO(ronghuawu): Change CreateChannel_w to be able to return error since + // below Apply**Description_w calls can fail. if (local_description_) - ApplyLocalTransportDescription(impl, NULL); + ApplyLocalTransportDescription_w(impl, NULL); if (remote_description_) - ApplyRemoteTransportDescription(impl, NULL); + ApplyRemoteTransportDescription_w(impl, NULL); if (local_description_ && remote_description_) - ApplyNegotiatedTransportDescription(impl, NULL); + ApplyNegotiatedTransportDescription_w(impl, NULL); impl->SignalReadableState.connect(this, &Transport::OnChannelReadableState); impl->SignalWritableState.connect(this, &Transport::OnChannelWritableState); impl->SignalReceivingState.connect(this, &Transport::OnChannelReceivingState); - impl->SignalGatheringState.connect(this, &Transport::OnChannelGatheringState); - impl->SignalCandidateGathered.connect(this, - &Transport::OnChannelCandidateGathered); + impl->SignalRequestSignaling.connect( + this, &Transport::OnChannelRequestSignaling); + impl->SignalCandidateReady.connect(this, &Transport::OnChannelCandidateReady); impl->SignalRouteChange.connect(this, &Transport::OnChannelRouteChange); + impl->SignalCandidatesAllocationDone.connect( + this, &Transport::OnChannelCandidatesAllocationDone); impl->SignalRoleConflict.connect(this, &Transport::OnRoleConflict); impl->SignalConnectionRemoved.connect( this, &Transport::OnChannelConnectionRemoved); @@ -256,22 +256,36 @@ TransportChannelImpl* Transport::CreateChannel(int component) { if (channels_.size() == 1) { // If this is the first channel, then indicate that we have started // connecting. - SignalConnecting(this); + signaling_thread()->Post(this, MSG_CONNECTING, NULL); } } 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; @@ -281,36 +295,34 @@ void Transport::DestroyChannel(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. - SignalConnecting(this); + signaling_thread()->Post(this, MSG_CONNECTING, NULL); } 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. - UpdateReadableState(); - 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; - - if (!ready_candidates_.empty()) { - SignalCandidatesGathered(this, ready_candidates_); - ready_candidates_.clear(); - } + signaling_thread()->Post(this, MSG_CANDIDATEREADY, NULL); if (!local_description_) { // TOOD(mallinath) : TransportDescription(TD) shouldn't be generated here. @@ -319,22 +331,38 @@ void Transport::ConnectChannels() { // Session. // Session must generate local TD before remote candidates pushed when // initiate request initiated by the remote. - LOG(LS_INFO) << "Transport::ConnectChannels: No local description has " + LOG(LS_INFO) << "Transport::ConnectChannels_w: 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(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_w(desc, CA_OFFER, NULL); } - CallChannels(&TransportChannelImpl::Connect); - if (HasChannels()) { - SignalConnecting(this); + CallChannels_w(&TransportChannelImpl::Connect); + if (!channels_.empty()) { + signaling_thread()->Post(this, MSG_CONNECTING, NULL); } } +void Transport::OnConnecting_s() { + ASSERT(signaling_thread()->IsCurrent()); + SignalConnecting(this); +} + 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(); @@ -342,15 +370,27 @@ void Transport::DestroyAllChannels() { impls.push_back(iter.second.get()); } - channels_.clear(); - - for (TransportChannelImpl* impl : impls) { - DestroyTransportChannel(impl); + { + rtc::CritScope cs(&crit_); + channels_.clear(); } - channels_destroyed_ = true; + + for (size_t i = 0; i < impls.size(); ++i) + DestroyTransportChannel(impls[i]); } -void Transport::CallChannels(TransportChannelFunc func) { +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()); for (const auto& iter : channels_) { ((iter.second.get())->*func)(); } @@ -389,7 +429,14 @@ bool Transport::VerifyCandidate(const Candidate& cand, std::string* error) { bool Transport::GetStats(TransportStats* stats) { - stats->transport_name = name(); + 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->channel_stats.clear(); for (auto iter : channels_) { ChannelMapEntry& entry = iter.second; @@ -405,49 +452,96 @@ bool Transport::GetStats(TransportStats* stats) { return true; } -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::GetSslRole(rtc::SSLRole* ssl_role) const { + return worker_thread_->Invoke(Bind( + &Transport::GetSslRole_w, this, ssl_role)); +} +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) { - TransportChannelImpl* channel = GetChannel(iter->component()); - if (channel != NULL) { - channel->AddRemoteCandidate(*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); } - return true; } void Transport::OnChannelReadableState(TransportChannel* channel) { - UpdateReadableState(); + ASSERT(worker_thread()->IsCurrent()); + signaling_thread()->Post(this, MSG_READSTATE, NULL); +} + +void Transport::OnChannelReadableState_s() { + ASSERT(signaling_thread()->IsCurrent()); + TransportState readable = GetTransportState_s(TRANSPORT_READABLE_STATE); + if (readable_ != readable) { + readable_ = readable; + SignalReadableState(this); + } } void Transport::OnChannelWritableState(TransportChannel* channel) { - LOG(LS_INFO) << name() << " TransportChannel " << channel->component() - << " writability changed to " << channel->writable() - << ". Check if transport is complete."; - UpdateWritableState(); - MaybeSignalCompleted(); + 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); + } } void Transport::OnChannelReceivingState(TransportChannel* channel) { - UpdateReceivingState(); + ASSERT(worker_thread()->IsCurrent()); + signaling_thread()->Post(this, MSG_RECEIVINGSTATE); } -TransportState Transport::GetTransportState(TransportStateType state_type) { +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_); bool any = false; bool all = !channels_.empty(); for (const auto iter : channels_) { @@ -478,48 +572,106 @@ TransportState Transport::GetTransportState(TransportStateType state_type) { return TRANSPORT_STATE_NONE; } -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(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::OnChannelCandidateGathered(TransportChannelImpl* channel, - const Candidate& candidate) { +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) { // 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_) { - std::vector candidates; - candidates.push_back(candidate); - SignalCandidatesGathered(this, candidates); - } else { - // We hold any candidates until the client lets us connect. - ready_candidates_.push_back(candidate); + 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); } } 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) { - SignalRoleConflict(); + signaling_thread_->Post(this, MSG_ROLECONFLICT); } void Transport::OnChannelConnectionRemoved(TransportChannelImpl* channel) { - LOG(LS_INFO) << name() << " TransportChannel " << channel->component() - << " connection removed. Check if transport is complete."; - MaybeSignalCompleted(); + ASSERT(worker_thread()->IsCurrent()); + MaybeCompleted_w(); // Check if the state is now Failed. // Failed is only available in the Controlling ICE role. @@ -527,105 +679,158 @@ void Transport::OnChannelConnectionRemoved(TransportChannelImpl* channel) { return; } - // Failed can only occur after candidate gathering has stopped. - if (channel->gathering_state() != kIceGatheringComplete) { + 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()) { return; } if (channel->GetState() == TransportChannelState::STATE_FAILED) { // A Transport has failed if any of its channels have no remaining // connections. - SignalFailed(this); + signaling_thread_->Post(this, MSG_FAILED); } } -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)? -} +void Transport::MaybeCompleted_w() { + ASSERT(worker_thread()->IsCurrent()); -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; + // When there is no channel created yet, calling this function could fire an + // IceConnectionCompleted event prematurely. + if (channels_.empty()) { + 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."; + // 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; } - SignalGatheringState(this); + } + + 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_); } } -void Transport::UpdateReceivingState() { - TransportState receiving = GetTransportState(TRANSPORT_RECEIVING_STATE); - if (receiving_ != receiving) { - receiving_ = receiving; - SignalReceivingState(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::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); +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); } + + // 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; } -void Transport::UpdateReadableState() { - TransportState readable = GetTransportState(TRANSPORT_READABLE_STATE); - if (readable_ != readable) { - readable_ = readable; - SignalReadableState(this); +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(TransportChannelImpl* ch, - std::string* error_desc) { +bool Transport::ApplyLocalTransportDescription_w(TransportChannelImpl* ch, + std::string* error_desc) { + ASSERT(worker_thread()->IsCurrent()); ch->SetIceCredentials(local_description_->ice_ufrag, local_description_->ice_pwd); return true; } -bool Transport::ApplyRemoteTransportDescription(TransportChannelImpl* ch, - std::string* error_desc) { +bool Transport::ApplyRemoteTransportDescription_w(TransportChannelImpl* ch, + std::string* error_desc) { ch->SetRemoteIceCredentials(remote_description_->ice_ufrag, remote_description_->ice_pwd); return true; } -bool Transport::ApplyNegotiatedTransportDescription( - TransportChannelImpl* channel, - std::string* error_desc) { +bool Transport::ApplyNegotiatedTransportDescription_w( + TransportChannelImpl* channel, std::string* error_desc) { + ASSERT(worker_thread()->IsCurrent()); channel->SetRemoteIceMode(remote_ice_mode_); return true; } -bool Transport::NegotiateTransportDescription(ContentAction local_role, - std::string* error_desc) { +bool Transport::NegotiateTransportDescription_w(ContentAction local_role, + std::string* error_desc) { + ASSERT(worker_thread()->IsCurrent()); // TODO(ekr@rtfm.com): This is ICE-specific stuff. Refactor into // P2PTransport. @@ -633,7 +838,7 @@ bool Transport::NegotiateTransportDescription(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(ICEROLE_CONTROLLING); + SetIceRole_w(ICEROLE_CONTROLLING); } // Update remote ice_mode to all existing channels. @@ -645,10 +850,60 @@ bool Transport::NegotiateTransportDescription(ContentAction local_role, // creation, we have the negotiation state saved until a new // negotiation happens. for (auto& iter : channels_) { - if (!ApplyNegotiatedTransportDescription(iter.second.get(), error_desc)) + if (!ApplyNegotiatedTransportDescription_w(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_READSTATE: + OnChannelReadableState_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 b401c260c3..5539655953 100644 --- a/webrtc/p2p/base/transport.h +++ b/webrtc/p2p/base/transport.h @@ -15,11 +15,15 @@ // 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 solely on the worker thread, and so -// its methods should only be called on the worker thread. +// 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). // -// Note: Subclasses must call DestroyChannels() in their own destructors. -// It is not possible to do so here because the subclass destructor will +// Note: Subclasses must call DestroyChannels() in their own constructors. +// It is not possible to do so here because the subclass constructor will // already have run. #ifndef WEBRTC_P2P_BASE_TRANSPORT_H_ @@ -32,11 +36,16 @@ #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; @@ -45,26 +54,6 @@ 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", "readable", and "receiving", we need to differentiate between // none, all, and some. enum TransportState { @@ -136,7 +125,7 @@ typedef std::vector TransportChannelStatsList; // Information about the stats of a transport. struct TransportStats { - std::string transport_name; + std::string content_name; TransportChannelStatsList channel_stats; }; @@ -147,13 +136,22 @@ bool IceCredentialsChanged(const std::string& old_ufrag, const std::string& new_ufrag, const std::string& new_pwd); -class Transport : public sigslot::has_slots<> { +class Transport : public rtc::MessageHandler, + public sigslot::has_slots<> { public: - Transport(const std::string& name, PortAllocator* allocator); + Transport(rtc::Thread* signaling_thread, + rtc::Thread* worker_thread, + const std::string& content_name, + PortAllocator* allocator); virtual ~Transport(); - // Returns the name of this transport. - const std::string& name() const { return name_; } + // 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 port allocator object for this transport. PortAllocator* port_allocator() { return allocator_; } @@ -184,14 +182,6 @@ class Transport : public sigslot::has_slots<> { 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 SignalReadableState; sigslot::signal1 SignalWritableState; @@ -211,23 +201,21 @@ class Transport : public sigslot::has_slots<> { void SetChannelReceivingTimeout(int timeout_ms); // Must be called before applying local session description. - virtual void SetLocalCertificate( - const rtc::scoped_refptr& certificate) {} + void SetCertificate( + const 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 local identity provided by SetIdentity. + bool GetCertificate(rtc::scoped_refptr* certificate); // 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)); } @@ -235,7 +223,7 @@ class Transport : public sigslot::has_slots<> { void DestroyChannel(int component); // Set the local TransportDescription to be used by TransportChannels. - // This calls ConnectChannels internally, kicking off candidate gathering. + // This should be called before ConnectChannels(). bool SetLocalTransportDescription(const TransportDescription& description, ContentAction action, std::string* error_desc); @@ -259,15 +247,20 @@ class Transport : public sigslot::has_slots<> { bool GetStats(TransportStats* stats); - sigslot::signal1 SignalGatheringState; + // 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(); // Handles sending of ready candidates and receiving of remote candidates. - sigslot::signal2&> - SignalCandidatesGathered; + sigslot::signal2&> SignalCandidatesReady; - // Called when one or more candidates are ready from the remote peer. - bool AddRemoteCandidates(const std::vector& candidates, - std::string* error); + sigslot::signal1 SignalCandidatesAllocationDone; + void OnRemoteCandidates(const std::vector& candidates); // If candidate is not acceptable, returns false and sets error. // Call this before calling OnRemoteCandidates. @@ -282,12 +275,10 @@ class Transport : public sigslot::has_slots<> { // Forwards the signal from TransportChannel to BaseSession. sigslot::signal0<> SignalRoleConflict; - virtual bool GetSslRole(rtc::SSLRole* ssl_role) const { return false; } + virtual bool GetSslRole(rtc::SSLRole* ssl_role) const; // Must be called before channel is starting to connect. - virtual bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) { - return false; - } + virtual bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version); protected: // These are called by Create/DestroyChannel above in order to create or @@ -295,6 +286,9 @@ class Transport : public sigslot::has_slots<> { 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 { @@ -307,37 +301,53 @@ class Transport : public sigslot::has_slots<> { 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(TransportChannelImpl* channel, - std::string* error_desc); + virtual bool ApplyLocalTransportDescription_w(TransportChannelImpl* channel, + std::string* error_desc); // Pushes down remote ice credentials from the remote description to the // transport channel. - virtual bool ApplyRemoteTransportDescription(TransportChannelImpl* ch, - std::string* error_desc); + virtual bool ApplyRemoteTransportDescription_w(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(ContentAction local_role, - std::string* error_desc); + virtual bool NegotiateTransportDescription_w(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( - TransportChannelImpl* channel, - std::string* error_desc); + 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; + } private: struct ChannelMapEntry { - ChannelMapEntry() : impl_(NULL), ref_(0) {} + ChannelMapEntry() : impl_(NULL), candidates_allocated_(false), ref_(0) {} explicit ChannelMapEntry(TransportChannelImpl *impl) : impl_(impl), + candidates_allocated_(false), ref_(0) { } @@ -350,9 +360,14 @@ class Transport : public sigslot::has_slots<> { 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_; + private: + TransportChannelImpl *impl_; + bool candidates_allocated_; int ref_; }; @@ -366,59 +381,93 @@ class Transport : public sigslot::has_slots<> { // Called when the receiving state of a channel changes. void OnChannelReceivingState(TransportChannel* channel); - // Called when a channel starts finishes gathering candidates - void OnChannelGatheringState(TransportChannelImpl* channel); + // Called when a channel requests signaling. + void OnChannelRequestSignaling(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 OnChannelCandidateGathered(TransportChannelImpl* channel, - const Candidate& candidate); + void OnChannelCandidateReady(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 OnChannelReadableState_s(); + 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(TransportChannelFunc func); + void CallChannels_w(TransportChannelFunc func); // Computes the AND and OR of the channel's read/write/receiving state // (argument picks the operation). - TransportState GetTransportState(TransportStateType type); + 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); // Sends SignalCompleted if we are now in that state. - void MaybeSignalCompleted(); + void MaybeCompleted_w(); - // Sends SignalGatheringState if gathering state changed - void UpdateGatheringState(); - - void UpdateReadableState(); - void UpdateWritableState(); - void UpdateReceivingState(); - - const std::string name_; + rtc::Thread* const signaling_thread_; + rtc::Thread* const worker_thread_; + const std::string content_name_; PortAllocator* const allocator_; - 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; + 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_; 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 9febfe33f7..43b761a64c 100644 --- a/webrtc/p2p/base/transport_unittest.cc +++ b/webrtc/p2p/base/transport_unittest.cc @@ -11,7 +11,8 @@ #include "webrtc/base/fakesslidentity.h" #include "webrtc/base/gunit.h" #include "webrtc/base/network.h" -#include "webrtc/p2p/base/faketransportcontroller.h" +#include "webrtc/base/thread.h" +#include "webrtc/p2p/base/fakesession.h" #include "webrtc/p2p/base/p2ptransport.h" using cricket::Candidate; @@ -34,7 +35,9 @@ class TransportTest : public testing::Test, public sigslot::has_slots<> { public: TransportTest() - : transport_(new FakeTransport("test content name")), + : thread_(rtc::Thread::Current()), + transport_(new FakeTransport( + thread_, thread_, "test content name", NULL)), channel_(NULL), connecting_signalled_(false), completed_(false), @@ -70,6 +73,7 @@ class TransportTest : public testing::Test, failed_ = true; } + rtc::Thread* thread_; rtc::scoped_ptr transport_; FakeTransportChannel* channel_; bool connecting_signalled_; @@ -81,7 +85,20 @@ class TransportTest : public testing::Test, TEST_F(TransportTest, TestConnectChannelsDoesSignal) { EXPECT_TRUE(SetupChannel()); transport_->ConnectChannels(); - EXPECT_TRUE(connecting_signalled_); + 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_); } // This test verifies channels are created with proper ICE @@ -215,7 +232,7 @@ TEST_F(TransportTest, TestChannelCompletedAndFailed) { NULL)); channel_->SetConnectionCount(2); - channel_->SetCandidatesGatheringComplete(); + channel_->SignalCandidatesAllocationDone(channel_); 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 9ab76413ca..5fb0eb472c 100644 --- a/webrtc/p2p/base/transportchannel.cc +++ b/webrtc/p2p/base/transportchannel.cc @@ -18,8 +18,9 @@ std::string TransportChannel::ToString() const { const char READABLE_ABBREV[2] = { '_', 'R' }; const char WRITABLE_ABBREV[2] = { '_', 'W' }; std::stringstream ss; - ss << "Channel[" << transport_name_ << "|" << component_ << "|" - << READABLE_ABBREV[readable_] << WRITABLE_ABBREV[writable_] << "]"; + ss << "Channel[" << content_name_ + << "|" << component_ + << "|" << READABLE_ABBREV[readable_] << WRITABLE_ABBREV[writable_] << "]"; return ss.str(); } diff --git a/webrtc/p2p/base/transportchannel.h b/webrtc/p2p/base/transportchannel.h index 5b13e68992..f492e4e000 100644 --- a/webrtc/p2p/base/transportchannel.h +++ b/webrtc/p2p/base/transportchannel.h @@ -37,23 +37,16 @@ enum PacketFlags { }; // Used to indicate channel's connection state. -enum TransportChannelState { - STATE_INIT, - STATE_CONNECTING, // Will enter this state once a connection is created - STATE_COMPLETED, - STATE_FAILED -}; +enum TransportChannelState { STATE_CONNECTING, 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& transport_name, int component) - : transport_name_(transport_name), + explicit TransportChannel(const std::string& content_name, int component) + : content_name_(content_name), component_(component), - readable_(false), - writable_(false), - receiving_(false) {} + readable_(false), writable_(false), receiving_(false) {} virtual ~TransportChannel() {} // TODO(guoweis) - Make this pure virtual once all subclasses of @@ -66,7 +59,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& transport_name() const { return transport_name_; } + const std::string& content_name() const { return content_name_; } int component() const { return component_; } // Returns the readable and states of this channel. Each time one of these @@ -155,9 +148,10 @@ 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 transport_name_; + std::string content_name_; int component_; bool readable_; bool writable_; diff --git a/webrtc/p2p/base/transportchannelimpl.h b/webrtc/p2p/base/transportchannelimpl.h index d9c267fe73..3aca951a23 100644 --- a/webrtc/p2p/base/transportchannelimpl.h +++ b/webrtc/p2p/base/transportchannelimpl.h @@ -32,9 +32,8 @@ enum IceProtocolType { // client. class TransportChannelImpl : public TransportChannel { public: - explicit TransportChannelImpl(const std::string& transport_name, - int component) - : TransportChannel(transport_name, component) {} + explicit TransportChannelImpl(const std::string& content_name, int component) + : TransportChannel(content_name, component) {} // Returns the transport that created this channel. virtual Transport* GetTransport() = 0; @@ -62,10 +61,13 @@ class TransportChannelImpl : public TransportChannel { virtual void SetReceivingTimeout(int timeout_ms) = 0; // Begins the process of attempting to make a connection to the other client. - // This includes starting to gather candidates. virtual void Connect() = 0; - sigslot::signal1 SignalGatheringState; + // 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; // Handles sending and receiving of candidates. The Transport // receives the candidates and may forward them to the relevant @@ -75,11 +77,9 @@ 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 - SignalCandidateGathered; - virtual void AddRemoteCandidate(const Candidate& candidate) = 0; - - virtual IceGatheringState gathering_state() const = 0; + sigslot::signal2 SignalCandidateReady; + virtual void OnCandidate(const Candidate& candidate) = 0; // DTLS methods virtual bool SetLocalCertificate( @@ -92,6 +92,9 @@ 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 new file mode 100644 index 0000000000..f7946dd96f --- /dev/null +++ b/webrtc/p2p/base/transportchannelproxy.cc @@ -0,0 +1,276 @@ +/* + * 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_->SignalReadableState.connect( + this, &TransportChannelProxy::OnReadableState); + impl_->SignalWritableState.connect( + this, &TransportChannelProxy::OnWritableState); + 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::OnReadableState(TransportChannel* channel) { + ASSERT(rtc::Thread::Current() == worker_thread_); + ASSERT(channel == impl_); + set_readable(impl_->readable()); + // Note: SignalReadableState fired by set_readable. +} + +void TransportChannelProxy::OnWritableState(TransportChannel* channel) { + ASSERT(rtc::Thread::Current() == worker_thread_); + ASSERT(channel == impl_); + set_writable(impl_->writable()); + // Note: SignalWritableState fired by set_readable. +} + +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 readable or writable, push up those signals. + set_readable(impl_ ? impl_->readable() : false); + set_writable(impl_ ? impl_->writable() : false); + } +} + +} // namespace cricket diff --git a/webrtc/p2p/base/transportchannelproxy.h b/webrtc/p2p/base/transportchannelproxy.h new file mode 100644 index 0000000000..f10f5076eb --- /dev/null +++ b/webrtc/p2p/base/transportchannelproxy.h @@ -0,0 +1,96 @@ +/* + * 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 OnReadableState(TransportChannel* channel); + void OnWritableState(TransportChannel* channel); + void OnReadPacket(TransportChannel* channel, const char* data, size_t size, + const rtc::PacketTime& packet_time, int flags); + void 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 deleted file mode 100644 index f78882d84b..0000000000 --- a/webrtc/p2p/base/transportcontroller.cc +++ /dev/null @@ -1,564 +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/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)); -} - -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); -} - -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 deleted file mode 100644 index 44eb9d4d26..0000000000 --- a/webrtc/p2p/base/transportcontroller.h +++ /dev/null @@ -1,192 +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. - */ - -#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); - 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); - 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 deleted file mode 100644 index dd89353c3e..0000000000 --- a/webrtc/p2p/base/transportcontroller_unittest.cc +++ /dev/null @@ -1,675 +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 - -#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); - 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()); - // We also expect that the channel started gathering as a result of the - // description being set. - 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* channel1 = CreateChannel("audio", 1); - ASSERT_NE(nullptr, channel1); - FakeTransportChannel* channel2 = CreateChannel("video", 1); - ASSERT_NE(nullptr, channel2); - // Connect starts candidate gathering. - channel1->Connect(); - // 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(); - 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(); - channel2->Connect(); - 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)); - - 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 1ddf55d4a1..4c701df0dd 100644 --- a/webrtc/p2p/base/transportdescriptionfactory.cc +++ b/webrtc/p2p/base/transportdescriptionfactory.cc @@ -14,6 +14,7 @@ #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 b5409f4df7..45c9e13d19 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/transportcontroller.cc', - 'base/transportcontroller.h', + 'base/transportchannelproxy.cc', + 'base/transportchannelproxy.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 ba7f553bba..8c2c2b35ab 100644 --- a/webrtc/p2p/p2p_tests.gypi +++ b/webrtc/p2p/p2p_tests.gypi @@ -15,12 +15,13 @@ 'direct_dependent_settings': { 'sources': [ 'base/dtlstransportchannel_unittest.cc', - 'base/faketransportcontroller.h', + 'base/fakesession.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', @@ -29,7 +30,6 @@ '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',