diff --git a/api/BUILD.gn b/api/BUILD.gn index 32f4c8e079..9e2bc9171e 100644 --- a/api/BUILD.gn +++ b/api/BUILD.gn @@ -113,6 +113,8 @@ rtc_static_library("libjingle_peerconnection_api") { "rtp_sender_interface.h", "rtp_transceiver_interface.cc", "rtp_transceiver_interface.h", + "sctp_transport_interface.cc", + "sctp_transport_interface.h", "set_remote_description_observer_interface.h", "stats_types.cc", "stats_types.h", diff --git a/api/DEPS b/api/DEPS index f5a0201b18..db8fddaee6 100644 --- a/api/DEPS +++ b/api/DEPS @@ -166,6 +166,10 @@ specific_include_rules = { "+rtc_base/ref_count.h", ], + "sctp_transport_interface\.h": [ + "+rtc_base/ref_count.h", + ], + "set_remote_description_observer_interface\.h": [ "+rtc_base/ref_count.h", ], diff --git a/api/peer_connection_interface.cc b/api/peer_connection_interface.cc index 944b28e31b..244ee739d9 100644 --- a/api/peer_connection_interface.cc +++ b/api/peer_connection_interface.cc @@ -10,6 +10,7 @@ #include "api/peer_connection_interface.h" #include "api/dtls_transport_interface.h" +#include "api/sctp_transport_interface.h" namespace webrtc { @@ -187,6 +188,12 @@ PeerConnectionInterface::LookupDtlsTransportByMid(const std::string& mid) { return nullptr; } +rtc::scoped_refptr +PeerConnectionInterface::GetSctpTransport() const { + RTC_NOTREACHED(); + return nullptr; +} + PeerConnectionInterface::BitrateParameters::BitrateParameters() = default; PeerConnectionInterface::BitrateParameters::~BitrateParameters() = default; diff --git a/api/peer_connection_interface.h b/api/peer_connection_interface.h index 541a6b4e2e..be4dcb9c25 100644 --- a/api/peer_connection_interface.h +++ b/api/peer_connection_interface.h @@ -122,6 +122,7 @@ class AudioDeviceModule; class AudioMixer; class AudioProcessing; class DtlsTransportInterface; +class SctpTransportInterface; class VideoDecoderFactory; class VideoEncoderFactory; @@ -1038,6 +1039,10 @@ class PeerConnectionInterface : public rtc::RefCountInterface { virtual rtc::scoped_refptr LookupDtlsTransportByMid( const std::string& mid); + // Returns the SCTP transport, if any. + // TODO(hta): Remove default implementation after updating Chrome. + virtual rtc::scoped_refptr GetSctpTransport() const; + // Returns the current SignalingState. virtual SignalingState signaling_state() = 0; diff --git a/api/sctp_transport_interface.cc b/api/sctp_transport_interface.cc new file mode 100644 index 0000000000..c6c1fbe964 --- /dev/null +++ b/api/sctp_transport_interface.cc @@ -0,0 +1,32 @@ +/* + * Copyright 2019 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 "api/sctp_transport_interface.h" + +namespace webrtc { + +SctpTransportInformation::SctpTransportInformation(SctpTransportState state) + : state_(state) {} + +SctpTransportInformation::SctpTransportInformation( + SctpTransportState state, + rtc::scoped_refptr dtls_transport, + absl::optional max_message_size, + absl::optional max_channels) + : state_(state), + dtls_transport_(std::move(dtls_transport)), + max_message_size_(max_message_size), + max_channels_(max_channels) {} + +SctpTransportInformation::~SctpTransportInformation() {} + +} // namespace webrtc diff --git a/api/sctp_transport_interface.h b/api/sctp_transport_interface.h new file mode 100644 index 0000000000..3698fc2e56 --- /dev/null +++ b/api/sctp_transport_interface.h @@ -0,0 +1,90 @@ +/* + * Copyright 2019 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 API_SCTP_TRANSPORT_INTERFACE_H_ +#define API_SCTP_TRANSPORT_INTERFACE_H_ + +#include "absl/types/optional.h" +#include "api/dtls_transport_interface.h" +#include "api/rtc_error.h" +#include "api/scoped_refptr.h" +#include "rtc_base/ref_count.h" + +namespace webrtc { + +// States of a SCTP transport, corresponding to the JS API specification. +// http://w3c.github.io/webrtc-pc/#dom-rtcsctptransportstate +enum class SctpTransportState { + kNew, // Has not started negotiating yet. Non-standard state. + kConnecting, // In the process of negotiating an association. + kConnected, // Completed negotiation of an association. + kClosed, // Closed by local or remote party. + kNumValues +}; + +// This object gives snapshot information about the changeable state of a +// SctpTransport. +// It reflects the readonly attributes of the object in the specification. +// http://w3c.github.io/webrtc-pc/#rtcsctptransport-interface +class SctpTransportInformation { + public: + explicit SctpTransportInformation(SctpTransportState state); + SctpTransportInformation( + SctpTransportState state, + rtc::scoped_refptr dtls_transport, + absl::optional max_message_size, + absl::optional max_channels); + ~SctpTransportInformation(); + // The DTLS transport that supports this SCTP transport. + rtc::scoped_refptr dtls_transport() const { + return dtls_transport_; + } + SctpTransportState state() const { return state_; } + absl::optional MaxMessageSize() const { return max_message_size_; } + absl::optional MaxChannels() const { return max_channels_; } + + private: + SctpTransportState state_; + rtc::scoped_refptr dtls_transport_; + absl::optional max_message_size_; + absl::optional max_channels_; +}; + +class SctpTransportObserverInterface { + public: + // This callback carries information about the state of the transport. + // The argument is a pass-by-value snapshot of the state. + // The callback will be called on the network thread. + virtual void OnStateChange(SctpTransportInformation info) = 0; + + protected: + virtual ~SctpTransportObserverInterface() = default; +}; + +// A SCTP transport, as represented to the outside world. +// This object is created on the network thread, and can only be +// accessed on that thread, except for functions explicitly marked otherwise. +// References can be held by other threads, and destruction can therefore +// be initiated by other threads. +class SctpTransportInterface : public rtc::RefCountInterface { + public: + // This function can be called from other threads. + virtual rtc::scoped_refptr dtls_transport() const = 0; + // Returns information on the state of the SctpTransport. + // This function can be called from other threads. + virtual SctpTransportInformation Information() const = 0; + // Observer management. + virtual void RegisterObserver(SctpTransportObserverInterface* observer) = 0; + virtual void UnregisterObserver() = 0; +}; + +} // namespace webrtc + +#endif // API_SCTP_TRANSPORT_INTERFACE_H_ diff --git a/media/sctp/sctp_transport.cc b/media/sctp/sctp_transport.cc index b6560180a4..8bcec41a28 100644 --- a/media/sctp/sctp_transport.cc +++ b/media/sctp/sctp_transport.cc @@ -383,9 +383,8 @@ SctpTransport::SctpTransport(rtc::Thread* network_thread, rtc::PacketTransportInternal* transport) : network_thread_(network_thread), transport_(transport), - was_ever_writable_(transport->writable()) { + was_ever_writable_(transport ? transport->writable() : false) { RTC_DCHECK(network_thread_); - RTC_DCHECK(transport_); RTC_DCHECK_RUN_ON(network_thread_); ConnectTransportSignals(); } diff --git a/media/sctp/sctp_transport.h b/media/sctp/sctp_transport.h index f7ddd8767c..dccecd86fb 100644 --- a/media/sctp/sctp_transport.h +++ b/media/sctp/sctp_transport.h @@ -36,10 +36,10 @@ struct sctp_stream_reset_event; struct socket; namespace cricket { -// Holds data to be passed on to a channel. +// Holds data to be passed on to a transport. struct SctpInboundPacket; -// From channel calls, data flows like this: +// From transport calls, data flows like this: // [network thread (although it can in princple be another thread)] // 1. SctpTransport::SendData(data) // 2. usrsctp_sendv(data) @@ -59,16 +59,15 @@ struct SctpInboundPacket; // 12. SctpTransport::SignalDataReceived(data) // [from the same thread, methods registered/connected to // SctpTransport are called with the recieved data] -// TODO(zhihuang): Rename "channel" to "transport" on network-level. class SctpTransport : public SctpTransportInternal, public sigslot::has_slots<> { public: // |network_thread| is where packets will be processed and callbacks from // this transport will be posted, and is the only thread on which public // methods can be called. - // |channel| is required (must not be null). + // |transport| is not required (can be null). SctpTransport(rtc::Thread* network_thread, - rtc::PacketTransportInternal* channel); + rtc::PacketTransportInternal* transport); ~SctpTransport() override; // SctpTransportInternal overrides (see sctptransportinternal.h for comments). @@ -108,7 +107,7 @@ class SctpTransport : public SctpTransportInternal, // Sets the "ready to send" flag and fires signal if needed. void SetReadyToSendData(); - // Callbacks from DTLS channel. + // Callbacks from DTLS transport. void OnWritableState(rtc::PacketTransportInternal* transport); virtual void OnPacketRead(rtc::PacketTransportInternal* transport, const char* data, @@ -135,12 +134,12 @@ class SctpTransport : public SctpTransportInternal, void OnStreamResetEvent(const struct sctp_stream_reset_event* evt); - // Responsible for marshalling incoming data to the channels listeners, and + // Responsible for marshalling incoming data to the transports listeners, and // outgoing data to the network interface. rtc::Thread* network_thread_; // Helps pass inbound/outbound packets asynchronously to the network thread. rtc::AsyncInvoker invoker_; - // Underlying DTLS channel. + // Underlying DTLS transport. rtc::PacketTransportInternal* transport_ = nullptr; // Track the data received from usrsctp between callbacks until the EOR bit diff --git a/pc/BUILD.gn b/pc/BUILD.gn index 9540ad62d4..b20f064672 100644 --- a/pc/BUILD.gn +++ b/pc/BUILD.gn @@ -55,6 +55,8 @@ rtc_static_library("rtc_pc_base") { "rtp_transport.cc", "rtp_transport.h", "rtp_transport_internal.h", + "sctp_transport.cc", + "sctp_transport.h", "session_description.cc", "session_description.h", "simulcast_description.cc", @@ -258,6 +260,7 @@ if (rtc_include_tests) { "media_session_unittest.cc", "rtcp_mux_filter_unittest.cc", "rtp_transport_unittest.cc", + "sctp_transport_unittest.cc", "session_description_unittest.cc", "srtp_filter_unittest.cc", "srtp_session_unittest.cc", diff --git a/pc/jsep_transport_controller.cc b/pc/jsep_transport_controller.cc index c1b8e4ed88..97f6bdde46 100644 --- a/pc/jsep_transport_controller.cc +++ b/pc/jsep_transport_controller.cc @@ -818,8 +818,8 @@ bool JsepTransportController::SetTransportForMid( mid_to_transport_[mid] = jsep_transport; return config_.transport_observer->OnTransportChanged( - mid, jsep_transport->rtp_transport(), - jsep_transport->rtp_dtls_transport(), jsep_transport->media_transport()); + mid, jsep_transport->rtp_transport(), jsep_transport->RtpDtlsTransport(), + jsep_transport->media_transport()); } void JsepTransportController::RemoveTransportForMid(const std::string& mid) { diff --git a/pc/jsep_transport_controller.h b/pc/jsep_transport_controller.h index 1363907377..c98ff25cc3 100644 --- a/pc/jsep_transport_controller.h +++ b/pc/jsep_transport_controller.h @@ -58,7 +58,7 @@ class JsepTransportController : public sigslot::has_slots<> { virtual bool OnTransportChanged( const std::string& mid, RtpTransportInternal* rtp_transport, - cricket::DtlsTransportInternal* dtls_transport, + rtc::scoped_refptr dtls_transport, MediaTransportInterface* media_transport) = 0; }; diff --git a/pc/jsep_transport_controller_unittest.cc b/pc/jsep_transport_controller_unittest.cc index 590f57dab2..4375080d68 100644 --- a/pc/jsep_transport_controller_unittest.cc +++ b/pc/jsep_transport_controller_unittest.cc @@ -305,10 +305,14 @@ class JsepTransportControllerTest : public JsepTransportController::Observer, // JsepTransportController::Observer overrides. bool OnTransportChanged(const std::string& mid, RtpTransportInternal* rtp_transport, - cricket::DtlsTransportInternal* dtls_transport, + rtc::scoped_refptr dtls_transport, MediaTransportInterface* media_transport) override { changed_rtp_transport_by_mid_[mid] = rtp_transport; - changed_dtls_transport_by_mid_[mid] = dtls_transport; + if (dtls_transport) { + changed_dtls_transport_by_mid_[mid] = dtls_transport->internal(); + } else { + changed_dtls_transport_by_mid_[mid] = nullptr; + } changed_media_transport_by_mid_[mid] = media_transport; return true; } diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc index d3b9258f38..44617dd822 100644 --- a/pc/peer_connection.cc +++ b/pc/peer_connection.cc @@ -41,6 +41,7 @@ #include "pc/rtp_media_utils.h" #include "pc/rtp_receiver.h" #include "pc/rtp_sender.h" +#include "pc/sctp_transport.h" #include "pc/sctp_utils.h" #include "pc/sdp_utils.h" #include "pc/stream_collection.h" @@ -3658,6 +3659,11 @@ PeerConnection::LookupDtlsTransportByMidInternal(const std::string& mid) { return transport_controller_->LookupDtlsTransportByMid(mid); } +rtc::scoped_refptr PeerConnection::GetSctpTransport() + const { + return sctp_transport_; +} + const SessionDescriptionInterface* PeerConnection::local_description() const { return pending_local_description_ ? pending_local_description_.get() : current_local_description_.get(); @@ -5497,7 +5503,7 @@ bool PeerConnection::PushdownSctpParameters_n(cricket::ContentSource source) { RTC_DCHECK(remote_description()); // Apply the SCTP port (which is hidden inside a DataCodec structure...) // When we support "max-message-size", that would also be pushed down here. - return sctp_transport_->Start( + return cricket_sctp_transport()->Start( GetSctpPort(local_description()->description()), GetSctpPort(remote_description()->description())); } @@ -5631,7 +5637,7 @@ bool PeerConnection::SendData(const cricket::SendDataParams& params, : network_thread()->Invoke( RTC_FROM_HERE, Bind(&cricket::SctpTransportInternal::SendData, - sctp_transport_.get(), params, payload, result)); + cricket_sctp_transport(), params, payload, result)); } bool PeerConnection::ConnectDataChannel(DataChannel* webrtc_data_channel) { @@ -5705,7 +5711,7 @@ void PeerConnection::AddSctpDataStream(int sid) { } network_thread()->Invoke( RTC_FROM_HERE, rtc::Bind(&cricket::SctpTransportInternal::OpenStream, - sctp_transport_.get(), sid)); + cricket_sctp_transport(), sid)); } void PeerConnection::RemoveSctpDataStream(int sid) { @@ -5720,7 +5726,7 @@ void PeerConnection::RemoveSctpDataStream(int sid) { } network_thread()->Invoke( RTC_FROM_HERE, rtc::Bind(&cricket::SctpTransportInternal::ResetStream, - sctp_transport_.get(), sid)); + cricket_sctp_transport(), sid)); } bool PeerConnection::ReadyToSendData() const { @@ -6243,32 +6249,38 @@ Call::Stats PeerConnection::GetCallStats() { bool PeerConnection::CreateSctpTransport_n(const std::string& mid) { RTC_DCHECK(network_thread()->IsCurrent()); RTC_DCHECK(sctp_factory_); + rtc::scoped_refptr webrtc_dtls_transport = + transport_controller_->LookupDtlsTransportByMid(mid); cricket::DtlsTransportInternal* dtls_transport = - transport_controller_->GetDtlsTransport(mid); + webrtc_dtls_transport->internal(); RTC_DCHECK(dtls_transport); - sctp_transport_ = sctp_factory_->CreateSctpTransport(dtls_transport); - RTC_DCHECK(sctp_transport_); + std::unique_ptr cricket_sctp_transport = + sctp_factory_->CreateSctpTransport(dtls_transport); + RTC_DCHECK(cricket_sctp_transport); sctp_invoker_.reset(new rtc::AsyncInvoker()); - sctp_transport_->SignalReadyToSendData.connect( + cricket_sctp_transport->SignalReadyToSendData.connect( this, &PeerConnection::OnSctpTransportReadyToSendData_n); - sctp_transport_->SignalDataReceived.connect( + cricket_sctp_transport->SignalDataReceived.connect( this, &PeerConnection::OnSctpTransportDataReceived_n); // TODO(deadbeef): All we do here is AsyncInvoke to fire the signal on // another thread. Would be nice if there was a helper class similar to // sigslot::repeater that did this for us, eliminating a bunch of boilerplate // code. - sctp_transport_->SignalClosingProcedureStartedRemotely.connect( + cricket_sctp_transport->SignalClosingProcedureStartedRemotely.connect( this, &PeerConnection::OnSctpClosingProcedureStartedRemotely_n); - sctp_transport_->SignalClosingProcedureComplete.connect( + cricket_sctp_transport->SignalClosingProcedureComplete.connect( this, &PeerConnection::OnSctpClosingProcedureComplete_n); sctp_mid_ = mid; - sctp_transport_->SetDtlsTransport(dtls_transport); + sctp_transport_ = new rtc::RefCountedObject( + std::move(cricket_sctp_transport)); + sctp_transport_->SetDtlsTransport(std::move(webrtc_dtls_transport)); return true; } void PeerConnection::DestroySctpTransport_n() { RTC_DCHECK(network_thread()->IsCurrent()); - sctp_transport_.reset(nullptr); + sctp_transport_->Clear(); + sctp_transport_ = nullptr; sctp_mid_.reset(); sctp_invoker_.reset(nullptr); sctp_ready_to_send_data_ = false; @@ -6949,7 +6961,7 @@ void PeerConnection::DestroyChannelInterface( bool PeerConnection::OnTransportChanged( const std::string& mid, RtpTransportInternal* rtp_transport, - cricket::DtlsTransportInternal* dtls_transport, + rtc::scoped_refptr dtls_transport, MediaTransportInterface* media_transport) { RTC_DCHECK_RUNS_SERIALIZED(&use_media_transport_race_checker_); bool ret = true; diff --git a/pc/peer_connection.h b/pc/peer_connection.h index 376dcc0c4e..20b66ac70a 100644 --- a/pc/peer_connection.h +++ b/pc/peer_connection.h @@ -26,6 +26,7 @@ #include "pc/peer_connection_internal.h" #include "pc/rtc_stats_collector.h" #include "pc/rtp_transceiver.h" +#include "pc/sctp_transport.h" #include "pc/stats_collector.h" #include "pc/stream_collection.h" #include "pc/webrtc_session_description_factory.h" @@ -201,6 +202,8 @@ class PeerConnection : public PeerConnectionInternal, rtc::scoped_refptr LookupDtlsTransportByMidInternal( const std::string& mid); + rtc::scoped_refptr GetSctpTransport() const override; + RTC_DEPRECATED bool StartRtcEventLog(rtc::PlatformFile file, int64_t max_size_bytes) override; bool StartRtcEventLog(std::unique_ptr output, @@ -1015,7 +1018,7 @@ class PeerConnection : public PeerConnectionInternal, // rejected). bool OnTransportChanged(const std::string& mid, RtpTransportInternal* rtp_transport, - cricket::DtlsTransportInternal* dtls_transport, + rtc::scoped_refptr dtls_transport, MediaTransportInterface* media_transport) override; // Returns the observer. Will crash on CHECK if the observer is removed. @@ -1149,7 +1152,10 @@ class PeerConnection : public PeerConnectionInternal, // when using SCTP. cricket::RtpDataChannel* rtp_data_channel_ = nullptr; - std::unique_ptr sctp_transport_; + cricket::SctpTransportInternal* cricket_sctp_transport() { + return sctp_transport_->internal(); + } + rtc::scoped_refptr sctp_transport_; // |sctp_mid_| is the content name (MID) in SDP. absl::optional sctp_mid_; // Value cached on signaling thread. Only updated when SctpReadyToSendData diff --git a/pc/sctp_transport.cc b/pc/sctp_transport.cc new file mode 100644 index 0000000000..99270e2eec --- /dev/null +++ b/pc/sctp_transport.cc @@ -0,0 +1,119 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "pc/sctp_transport.h" + +#include + +namespace webrtc { + +SctpTransport::SctpTransport( + std::unique_ptr internal) + : owner_thread_(rtc::Thread::Current()), + info_(SctpTransportState::kNew), + internal_sctp_transport_(std::move(internal)) { + RTC_DCHECK(internal_sctp_transport_.get()); + internal_sctp_transport_->SignalReadyToSendData.connect( + this, &SctpTransport::OnInternalReadyToSendData); + // TODO(https://bugs.webrtc.org/10360): Add handlers for transport closing. + + if (dtls_transport_) { + UpdateInformation(SctpTransportState::kConnecting); + } else { + UpdateInformation(SctpTransportState::kNew); + } +} + +SctpTransport::~SctpTransport() { + // We depend on the network thread to call Clear() before dropping + // its last reference to this object. + RTC_DCHECK(owner_thread_->IsCurrent() || !internal_sctp_transport_); +} + +SctpTransportInformation SctpTransport::Information() const { + rtc::CritScope scope(&lock_); + return info_; +} + +void SctpTransport::RegisterObserver(SctpTransportObserverInterface* observer) { + RTC_DCHECK_RUN_ON(owner_thread_); + RTC_DCHECK(observer); + RTC_DCHECK(!observer_); + observer_ = observer; +} + +void SctpTransport::UnregisterObserver() { + RTC_DCHECK_RUN_ON(owner_thread_); + observer_ = nullptr; +} + +rtc::scoped_refptr SctpTransport::dtls_transport() + const { + RTC_DCHECK_RUN_ON(owner_thread_); + return dtls_transport_; +} + +// Internal functions +void SctpTransport::Clear() { + RTC_DCHECK_RUN_ON(owner_thread_); + RTC_DCHECK(internal()); + { + rtc::CritScope scope(&lock_); + // Note that we delete internal_sctp_transport_, but + // only drop the reference to dtls_transport_. + dtls_transport_ = nullptr; + internal_sctp_transport_ = nullptr; + } + UpdateInformation(SctpTransportState::kClosed); +} + +void SctpTransport::SetDtlsTransport( + rtc::scoped_refptr transport) { + RTC_DCHECK_RUN_ON(owner_thread_); + rtc::CritScope scope(&lock_); + dtls_transport_ = transport; + if (internal_sctp_transport_) { + if (transport) { + internal_sctp_transport_->SetDtlsTransport(transport->internal()); + if (info_.state() == SctpTransportState::kNew) { + UpdateInformation(SctpTransportState::kConnecting); + } + } else { + internal_sctp_transport_->SetDtlsTransport(nullptr); + } + } +} + +void SctpTransport::UpdateInformation(SctpTransportState state) { + RTC_DCHECK_RUN_ON(owner_thread_); + bool must_send_update; + SctpTransportInformation info_copy(SctpTransportState::kNew); + { + rtc::CritScope scope(&lock_); + must_send_update = (state != info_.state()); + // TODO(https://bugs.webrtc.org/10358): Update max message size and + // max channels from internal SCTP transport when available. + info_ = SctpTransportInformation( + state, dtls_transport_, info_.MaxMessageSize(), info_.MaxChannels()); + if (observer_ && must_send_update) { + info_copy = info_; + } + } + // We call the observer without holding the lock. + if (observer_ && must_send_update) { + observer_->OnStateChange(info_copy); + } +} + +void SctpTransport::OnInternalReadyToSendData() { + UpdateInformation(SctpTransportState::kConnected); +} + +} // namespace webrtc diff --git a/pc/sctp_transport.h b/pc/sctp_transport.h new file mode 100644 index 0000000000..f1e50926c2 --- /dev/null +++ b/pc/sctp_transport.h @@ -0,0 +1,75 @@ +/* + * Copyright 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef PC_SCTP_TRANSPORT_H_ +#define PC_SCTP_TRANSPORT_H_ + +#include + +#include "api/scoped_refptr.h" +#include "api/sctp_transport_interface.h" +#include "media/sctp/sctp_transport.h" +#include "pc/dtls_transport.h" + +namespace webrtc { + +// This implementation wraps a cricket::SctpTransport, and takes +// ownership of it. +// This object must be constructed and updated on the networking thread, +// the same thread as the one the cricket::SctpTransportInternal object +// lives on. +class SctpTransport : public SctpTransportInterface, + public sigslot::has_slots<> { + public: + explicit SctpTransport( + std::unique_ptr internal); + + rtc::scoped_refptr dtls_transport() const override; + SctpTransportInformation Information() const override; + void RegisterObserver(SctpTransportObserverInterface* observer) override; + void UnregisterObserver() override; + + void Clear(); + void SetDtlsTransport(rtc::scoped_refptr); + + cricket::SctpTransportInternal* internal() { + rtc::CritScope scope(&lock_); + return internal_sctp_transport_.get(); + } + + const cricket::SctpTransportInternal* internal() const { + rtc::CritScope scope(&lock_); + return internal_sctp_transport_.get(); + } + + protected: + ~SctpTransport() override; + + private: + void UpdateInformation(SctpTransportState state); + void OnInternalReadyToSendData(); + void OnInternalClosingProcedureStartedRemotely(int sid); + void OnInternalClosingProcedureComplete(int sid); + + const rtc::Thread* owner_thread_; + rtc::CriticalSection lock_; + // Variables accessible off-thread, guarded by lock_ + SctpTransportInformation info_ RTC_GUARDED_BY(lock_); + std::unique_ptr internal_sctp_transport_ + RTC_GUARDED_BY(lock_); + // Variables only accessed on-thread + SctpTransportObserverInterface* observer_ RTC_GUARDED_BY(owner_thread_) = + nullptr; + rtc::scoped_refptr dtls_transport_ + RTC_GUARDED_BY(owner_thread_); +}; + +} // namespace webrtc +#endif // PC_SCTP_TRANSPORT_H_ diff --git a/pc/sctp_transport_unittest.cc b/pc/sctp_transport_unittest.cc new file mode 100644 index 0000000000..e2bdf449b9 --- /dev/null +++ b/pc/sctp_transport_unittest.cc @@ -0,0 +1,149 @@ +/* + * Copyright 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "pc/sctp_transport.h" + +#include +#include + +#include "absl/memory/memory.h" +#include "p2p/base/fake_dtls_transport.h" +#include "pc/dtls_transport.h" +#include "rtc_base/gunit.h" +#include "test/gmock.h" +#include "test/gtest.h" + +constexpr int kDefaultTimeout = 1000; // milliseconds + +using cricket::FakeDtlsTransport; +using ::testing::ElementsAre; + +namespace webrtc { + +namespace { + +class FakeCricketSctpTransport : public cricket::SctpTransportInternal { + public: + void SetDtlsTransport(rtc::PacketTransportInternal* transport) override {} + bool Start(int local_port, int remote_port) override { return true; } + bool OpenStream(int sid) override { return true; } + bool ResetStream(int sid) override { return true; } + bool SendData(const cricket::SendDataParams& params, + const rtc::CopyOnWriteBuffer& payload, + cricket::SendDataResult* result = nullptr) override { + return true; + } + bool ReadyToSendData() override { return true; } + void set_debug_name_for_testing(const char* debug_name) override {} + // Methods exposed for testing + void SendSignalReadyToSendData() { SignalReadyToSendData(); } + + void SendSignalClosingProcedureStartedRemotely() { + SignalClosingProcedureStartedRemotely(1); + } + + void SendSignalClosingProcedureComplete() { + SignalClosingProcedureComplete(1); + } +}; + +} // namespace + +class TestSctpTransportObserver : public SctpTransportObserverInterface { + public: + void OnStateChange(SctpTransportInformation info) override { + states_.push_back(info.state()); + } + + SctpTransportState State() { + if (states_.size() > 0) { + return states_[states_.size() - 1]; + } else { + return SctpTransportState::kNew; + } + } + + const std::vector& States() { return states_; } + + private: + std::vector states_; +}; + +class SctpTransportTest : public testing::Test { + public: + SctpTransport* transport() { return transport_.get(); } + SctpTransportObserverInterface* observer() { return &observer_; } + + void CreateTransport() { + auto cricket_sctp_transport = + absl::WrapUnique(new FakeCricketSctpTransport()); + transport_ = new rtc::RefCountedObject( + std::move(cricket_sctp_transport)); + } + + void AddDtlsTransport() { + std::unique_ptr cricket_transport = + absl::make_unique( + "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP); + dtls_transport_ = + new rtc::RefCountedObject(std::move(cricket_transport)); + transport_->SetDtlsTransport(dtls_transport_); + } + + void CompleteSctpHandshake() { + CricketSctpTransport()->SendSignalReadyToSendData(); + } + + FakeCricketSctpTransport* CricketSctpTransport() { + return static_cast(transport_->internal()); + } + + rtc::scoped_refptr transport_; + rtc::scoped_refptr dtls_transport_; + TestSctpTransportObserver observer_; +}; + +TEST(SctpTransportSimpleTest, CreateClearDelete) { + std::unique_ptr fake_cricket_sctp_transport = + absl::WrapUnique(new FakeCricketSctpTransport()); + rtc::scoped_refptr sctp_transport = + new rtc::RefCountedObject( + std::move(fake_cricket_sctp_transport)); + ASSERT_TRUE(sctp_transport->internal()); + ASSERT_EQ(SctpTransportState::kNew, sctp_transport->Information().state()); + sctp_transport->Clear(); + ASSERT_FALSE(sctp_transport->internal()); + ASSERT_EQ(SctpTransportState::kClosed, sctp_transport->Information().state()); +} + +TEST_F(SctpTransportTest, EventsObservedWhenConnecting) { + CreateTransport(); + transport()->RegisterObserver(observer()); + AddDtlsTransport(); + CompleteSctpHandshake(); + ASSERT_EQ_WAIT(SctpTransportState::kConnected, observer_.State(), + kDefaultTimeout); + EXPECT_THAT(observer_.States(), ElementsAre(SctpTransportState::kConnecting, + SctpTransportState::kConnected)); +} + +TEST_F(SctpTransportTest, CloseWhenClearing) { + CreateTransport(); + transport()->RegisterObserver(observer()); + AddDtlsTransport(); + CompleteSctpHandshake(); + ASSERT_EQ_WAIT(SctpTransportState::kConnected, observer_.State(), + kDefaultTimeout); + transport()->Clear(); + ASSERT_EQ_WAIT(SctpTransportState::kClosed, observer_.State(), + kDefaultTimeout); +} + +} // namespace webrtc