Add an observer API for DTLSTransport events.

This wires up the "state change" event and defines an observer
class that can be used by clients.

Bug: chromium:907849
Change-Id: I3cba2dc051a56280fb958f139f29cbb0022a39c6
Reviewed-on: https://webrtc-review.googlesource.com/c/114884
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26120}
This commit is contained in:
Harald Alvestrand
2019-01-03 12:43:28 +01:00
committed by Commit Bot
parent 8319e7f8ab
commit d02541e276
6 changed files with 238 additions and 10 deletions

View File

@ -11,18 +11,57 @@
#ifndef API_DTLSTRANSPORTINTERFACE_H_ #ifndef API_DTLSTRANSPORTINTERFACE_H_
#define API_DTLSTRANSPORTINTERFACE_H_ #define API_DTLSTRANSPORTINTERFACE_H_
#include "api/rtcerror.h"
#include "rtc_base/refcount.h" #include "rtc_base/refcount.h"
namespace webrtc { namespace webrtc {
// States of a DTLS transport, corresponding to the JS API specification.
// http://w3c.github.io/webrtc-pc/#dom-rtcdtlstransportstate
enum class DtlsTransportState {
kNew, // Has not started negotiating yet.
kConnecting, // In the process of negotiating a secure connection.
kConnected, // Completed negotiation and verified fingerprints.
kClosed, // Intentionally closed.
kFailed // Failure due to an error or failing to verify a remote fingerprint.
};
// This object gives snapshot information about the changeable state of a
// DTLSTransport.
class DtlsTransportInformation {
public:
explicit DtlsTransportInformation(DtlsTransportState state) : state_(state) {}
DtlsTransportState state() const { return state_; }
// TODO(hta): Add remote certificate access
private:
DtlsTransportState state_;
};
class DtlsTransportObserverInterface {
public:
// This callback carries information about the state of the transport.
// The argument is a pass-by-value snapshot of the state.
virtual void OnStateChange(DtlsTransportInformation info) = 0;
// This callback is called when an error occurs, causing the transport
// to go to the kFailed state.
virtual void OnError(RTCError error) = 0;
protected:
virtual ~DtlsTransportObserverInterface() = default;
};
// A DTLS transport, as represented to the outside world. // A DTLS transport, as represented to the outside world.
// Its role is to report state changes and errors, and make sure information // This object is created on the signaling thread, and can only be
// about remote certificates is available. // accessed on that thread.
// References can be held by other threads, and destruction can therefore
// be initiated by other threads.
class DtlsTransportInterface : public rtc::RefCountInterface { class DtlsTransportInterface : public rtc::RefCountInterface {
public: public:
// TODO(hta): Need a notifier interface to transmit state changes and // These functions can only be called from the signalling thread.
// error events. The generic NotifierInterface of mediasteraminterface.h virtual DtlsTransportInformation Information() = 0;
// may be suitable, or may be copyable. // Observer management.
virtual void RegisterObserver(DtlsTransportObserverInterface* observer) = 0;
virtual void UnregisterObserver() = 0;
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -249,6 +249,7 @@ if (rtc_include_tests) {
"channel_unittest.cc", "channel_unittest.cc",
"channelmanager_unittest.cc", "channelmanager_unittest.cc",
"dtlssrtptransport_unittest.cc", "dtlssrtptransport_unittest.cc",
"dtlstransport_unittest.cc",
"jseptransport_unittest.cc", "jseptransport_unittest.cc",
"jseptransportcontroller_unittest.cc", "jseptransportcontroller_unittest.cc",
"mediasession_unittest.cc", "mediasession_unittest.cc",

View File

@ -14,10 +14,81 @@
namespace webrtc { namespace webrtc {
namespace {
DtlsTransportState TranslateState(cricket::DtlsTransportState internal_state) {
switch (internal_state) {
case cricket::DTLS_TRANSPORT_NEW:
return DtlsTransportState::kNew;
break;
case cricket::DTLS_TRANSPORT_CONNECTING:
return DtlsTransportState::kConnecting;
break;
case cricket::DTLS_TRANSPORT_CONNECTED:
return DtlsTransportState::kConnected;
break;
case cricket::DTLS_TRANSPORT_CLOSED:
return DtlsTransportState::kClosed;
break;
case cricket::DTLS_TRANSPORT_FAILED:
return DtlsTransportState::kFailed;
break;
}
}
} // namespace
// Implementation of DtlsTransportInterface
DtlsTransport::DtlsTransport( DtlsTransport::DtlsTransport(
std::unique_ptr<cricket::DtlsTransportInternal> internal) std::unique_ptr<cricket::DtlsTransportInternal> internal)
: internal_dtls_transport_(std::move(internal)) { : signaling_thread_(rtc::Thread::Current()),
internal_dtls_transport_(std::move(internal)) {
RTC_DCHECK(internal_dtls_transport_.get()); RTC_DCHECK(internal_dtls_transport_.get());
internal_dtls_transport_->SignalDtlsState.connect(
this, &DtlsTransport::OnInternalDtlsState);
}
DtlsTransport::~DtlsTransport() {
// We depend on the signaling thread to call Clear() before dropping
// its last reference to this object.
RTC_DCHECK(signaling_thread_->IsCurrent() || !internal_dtls_transport_);
}
DtlsTransportInformation DtlsTransport::Information() {
RTC_DCHECK(signaling_thread_->IsCurrent());
if (internal()) {
return DtlsTransportInformation(TranslateState(internal()->dtls_state()));
} else {
return DtlsTransportInformation(DtlsTransportState::kClosed);
}
}
void DtlsTransport::RegisterObserver(DtlsTransportObserverInterface* observer) {
RTC_DCHECK(signaling_thread_->IsCurrent());
RTC_DCHECK(observer);
observer_ = observer;
}
void DtlsTransport::UnregisterObserver() {
RTC_DCHECK(signaling_thread_->IsCurrent());
observer_ = nullptr;
}
// Internal functions
void DtlsTransport::Clear() {
RTC_DCHECK(signaling_thread_->IsCurrent());
internal_dtls_transport_.reset();
}
void DtlsTransport::OnInternalDtlsState(
cricket::DtlsTransportInternal* transport,
cricket::DtlsTransportState state) {
RTC_DCHECK(signaling_thread_->IsCurrent());
RTC_DCHECK(transport == internal());
RTC_DCHECK(state == internal()->dtls_state());
if (observer_) {
observer_->OnStateChange(Information());
}
} }
} // namespace webrtc } // namespace webrtc

View File

@ -15,21 +15,41 @@
#include "api/dtlstransportinterface.h" #include "api/dtlstransportinterface.h"
#include "p2p/base/dtlstransport.h" #include "p2p/base/dtlstransport.h"
#include "rtc_base/asyncinvoker.h"
namespace webrtc { namespace webrtc {
// This implementation wraps a cricket::DtlsTransport, and takes // This implementation wraps a cricket::DtlsTransport, and takes
// ownership of it. // ownership of it.
class DtlsTransport : public DtlsTransportInterface { class DtlsTransport : public DtlsTransportInterface,
public sigslot::has_slots<> {
public: public:
// This object must be constructed on the signaling thread.
explicit DtlsTransport( explicit DtlsTransport(
std::unique_ptr<cricket::DtlsTransportInternal> internal); std::unique_ptr<cricket::DtlsTransportInternal> internal);
DtlsTransportInformation Information() override;
void RegisterObserver(DtlsTransportObserverInterface* observer) override;
void UnregisterObserver() override;
void Clear();
cricket::DtlsTransportInternal* internal() { cricket::DtlsTransportInternal* internal() {
return internal_dtls_transport_.get(); return internal_dtls_transport_.get();
} }
void clear() { internal_dtls_transport_.reset(); }
const cricket::DtlsTransportInternal* internal() const {
return internal_dtls_transport_.get();
}
protected:
~DtlsTransport();
private: private:
void OnInternalDtlsState(cricket::DtlsTransportInternal* transport,
cricket::DtlsTransportState state);
DtlsTransportObserverInterface* observer_ = nullptr;
rtc::Thread* signaling_thread_;
std::unique_ptr<cricket::DtlsTransportInternal> internal_dtls_transport_; std::unique_ptr<cricket::DtlsTransportInternal> internal_dtls_transport_;
}; };

View File

@ -0,0 +1,97 @@
/*
* 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/dtlstransport.h"
#include <utility>
#include <vector>
#include "absl/memory/memory.h"
#include "p2p/base/fakedtlstransport.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 {
class TestDtlsTransportObserver : public DtlsTransportObserverInterface {
public:
void OnStateChange(DtlsTransportInformation info) override {
state_change_called_ = true;
states_.push_back(info.state());
}
void OnError(RTCError error) override {}
bool state_change_called_ = false;
std::vector<DtlsTransportState> states_;
};
class DtlsTransportTest : public testing::Test {
public:
DtlsTransport* transport() { return transport_.get(); }
DtlsTransportObserverInterface* observer() { return &observer_; }
void CreateTransport() {
auto cricket_transport = absl::make_unique<FakeDtlsTransport>(
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
transport_ =
new rtc::RefCountedObject<DtlsTransport>(std::move(cricket_transport));
}
void CompleteDtlsHandshake() {
auto fake_dtls1 = static_cast<FakeDtlsTransport*>(transport_->internal());
auto fake_dtls2 = absl::make_unique<FakeDtlsTransport>(
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
auto cert1 = rtc::RTCCertificate::Create(absl::WrapUnique(
rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT)));
fake_dtls1->SetLocalCertificate(cert1);
auto cert2 = rtc::RTCCertificate::Create(absl::WrapUnique(
rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT)));
fake_dtls2->SetLocalCertificate(cert2);
fake_dtls1->SetDestination(fake_dtls2.get());
}
rtc::scoped_refptr<DtlsTransport> transport_;
TestDtlsTransportObserver observer_;
};
TEST_F(DtlsTransportTest, CreateClearDelete) {
auto cricket_transport = absl::make_unique<FakeDtlsTransport>(
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
rtc::scoped_refptr<DtlsTransport> webrtc_transport =
new rtc::RefCountedObject<DtlsTransport>(std::move(cricket_transport));
ASSERT_TRUE(webrtc_transport->internal());
ASSERT_EQ(DtlsTransportState::kNew, webrtc_transport->Information().state());
webrtc_transport->Clear();
ASSERT_FALSE(webrtc_transport->internal());
ASSERT_EQ(DtlsTransportState::kClosed,
webrtc_transport->Information().state());
}
TEST_F(DtlsTransportTest, EventsObservedWhenConnecting) {
CreateTransport();
transport()->RegisterObserver(observer());
CompleteDtlsHandshake();
ASSERT_TRUE_WAIT(observer_.state_change_called_, kDefaultTimeout);
EXPECT_THAT(
observer_.states_,
ElementsAre( // FakeDtlsTransport doesn't signal the "connecting" state.
// TODO(hta): fix FakeDtlsTransport or file bug on it.
// DtlsTransportState::kConnecting,
DtlsTransportState::kConnected));
}
} // namespace webrtc

View File

@ -141,9 +141,9 @@ JsepTransport::~JsepTransport() {
} }
// Clear all DtlsTransports. There may be pointers to these from // Clear all DtlsTransports. There may be pointers to these from
// other places, so we can't assume they'll be deleted by the destructor. // other places, so we can't assume they'll be deleted by the destructor.
rtp_dtls_transport_->clear(); rtp_dtls_transport_->Clear();
if (rtcp_dtls_transport_) { if (rtcp_dtls_transport_) {
rtcp_dtls_transport_->clear(); rtcp_dtls_transport_->Clear();
} }
} }