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:

committed by
Commit Bot

parent
8319e7f8ab
commit
d02541e276
@ -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
|
||||||
|
@ -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",
|
||||||
|
@ -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
|
||||||
|
@ -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_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
97
pc/dtlstransport_unittest.cc
Normal file
97
pc/dtlstransport_unittest.cc
Normal 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
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user