diff --git a/webrtc/api/BUILD.gn b/webrtc/api/BUILD.gn index 149fd51e0b..fa3c629524 100644 --- a/webrtc/api/BUILD.gn +++ b/webrtc/api/BUILD.gn @@ -75,6 +75,9 @@ rtc_static_library("libjingle_peerconnection") { "mediastreamtrack.h", "mediastreamtrackproxy.h", "notifier.h", + "ortcfactory.cc", + "ortcfactory.h", + "ortcfactoryinterface.h", "peerconnection.cc", "peerconnection.h", "peerconnectionfactory.cc", @@ -103,6 +106,7 @@ rtc_static_library("libjingle_peerconnection") { "streamcollection.h", "trackmediainfomap.cc", "trackmediainfomap.h", + "udptransportinterface.h", "videocapturertracksource.cc", "videocapturertracksource.h", "videosourceproxy.h", @@ -236,6 +240,7 @@ if (rtc_include_tests) { "localaudiosource_unittest.cc", "mediaconstraintsinterface_unittest.cc", "mediastream_unittest.cc", + "ortcfactory_unittest.cc", "peerconnection_unittest.cc", "peerconnectionendtoend_unittest.cc", "peerconnectionfactory_unittest.cc", diff --git a/webrtc/api/ortcfactory.cc b/webrtc/api/ortcfactory.cc new file mode 100644 index 0000000000..ba41a03ca5 --- /dev/null +++ b/webrtc/api/ortcfactory.cc @@ -0,0 +1,119 @@ +/* + * Copyright 2017 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/api/ortcfactory.h" + +#include +#include // For std::move. + +#include "webrtc/base/bind.h" +#include "webrtc/base/asyncpacketsocket.h" +#include "webrtc/p2p/base/basicpacketsocketfactory.h" +#include "webrtc/p2p/base/udptransport.h" + +namespace webrtc { + +// static +std::unique_ptr OrtcFactoryInterface::Create( + rtc::Thread* network_thread, + rtc::Thread* signaling_thread, + rtc::NetworkManager* network_manager, + rtc::PacketSocketFactory* socket_factory) { + // Hop to signaling thread if needed. + if (signaling_thread && !signaling_thread->IsCurrent()) { + return signaling_thread->Invoke>( + RTC_FROM_HERE, + rtc::Bind(&OrtcFactoryInterface::Create, network_thread, + signaling_thread, network_manager, socket_factory)); + } + OrtcFactory* new_factory = + new OrtcFactory(network_thread, signaling_thread, + network_manager, socket_factory); + // Return a proxy so that any calls on the returned object (including + // destructor) happen on the signaling thread. + return OrtcFactoryProxy::Create(new_factory->signaling_thread(), + new_factory->network_thread(), new_factory); +} + +OrtcFactory::OrtcFactory(rtc::Thread* network_thread, + rtc::Thread* signaling_thread, + rtc::NetworkManager* network_manager, + rtc::PacketSocketFactory* socket_factory) + : network_thread_(network_thread), + signaling_thread_(signaling_thread), + network_manager_(network_manager), + socket_factory_(socket_factory) { + if (!network_thread_) { + owned_network_thread_ = rtc::Thread::CreateWithSocketServer(); + owned_network_thread_->Start(); + network_thread_ = owned_network_thread_.get(); + } + + // The worker thread is created internally because it's an implementation + // detail, and consumers of the API don't need to really know about it. + owned_worker_thread_ = rtc::Thread::Create(); + owned_worker_thread_->Start(); + + if (signaling_thread_) { + RTC_DCHECK_RUN_ON(signaling_thread_); + } else { + signaling_thread_ = rtc::Thread::Current(); + if (!signaling_thread_) { + // If this thread isn't already wrapped by an rtc::Thread, create a + // wrapper and own it in this class. + signaling_thread_ = rtc::ThreadManager::Instance()->WrapCurrentThread(); + wraps_signaling_thread_ = true; + } + } + if (!network_manager_) { + owned_network_manager_.reset(new rtc::BasicNetworkManager()); + network_manager_ = owned_network_manager_.get(); + } + if (!socket_factory_) { + owned_socket_factory_.reset( + new rtc::BasicPacketSocketFactory(network_thread_)); + socket_factory_ = owned_socket_factory_.get(); + } +} + +OrtcFactory::~OrtcFactory() { + RTC_DCHECK_RUN_ON(signaling_thread_); + if (wraps_signaling_thread_) { + rtc::ThreadManager::Instance()->UnwrapCurrentThread(); + } +} + +std::unique_ptr OrtcFactory::CreateUdpTransport( + int family, + uint16_t min_port, + uint16_t max_port) { + if (!network_thread_->IsCurrent()) { + RTC_DCHECK_RUN_ON(signaling_thread_); + return network_thread_->Invoke>( + RTC_FROM_HERE, rtc::Bind(&OrtcFactory::CreateUdpTransport, this, family, + min_port, max_port)); + } + std::unique_ptr socket( + socket_factory_->CreateUdpSocket( + rtc::SocketAddress(rtc::GetAnyIP(family), 0), min_port, max_port)); + if (!socket) { + LOG(LS_WARNING) << "Local socket allocation failure."; + return nullptr; + } + LOG(LS_INFO) << "Created UDP socket with address " + << socket->GetLocalAddress().ToSensitiveString() << "."; + // Use proxy so that calls to the returned object are invoked on the network + // thread. + return UdpTransportProxy::Create( + signaling_thread_, network_thread_, + new cricket::UdpTransport(std::string(), std::move(socket))); +} + +} // namespace webrtc diff --git a/webrtc/api/ortcfactory.h b/webrtc/api/ortcfactory.h new file mode 100644 index 0000000000..dc92774ff4 --- /dev/null +++ b/webrtc/api/ortcfactory.h @@ -0,0 +1,64 @@ +/* + * Copyright 2017 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_API_ORTCFACTORY_H_ +#define WEBRTC_API_ORTCFACTORY_H_ + +#include + +#include "webrtc/api/ortcfactoryinterface.h" +#include "webrtc/base/constructormagic.h" + +namespace webrtc { + +// Implementation of OrtcFactoryInterface. +// +// See ortcfactoryinterface.h for documentation. +class OrtcFactory : public OrtcFactoryInterface { + public: + OrtcFactory(rtc::Thread* network_thread, + rtc::Thread* signaling_thread, + rtc::NetworkManager* network_manager, + rtc::PacketSocketFactory* socket_factory); + ~OrtcFactory() override; + std::unique_ptr + CreateUdpTransport(int family, uint16_t min_port, uint16_t max_port) override; + + rtc::Thread* network_thread() { return network_thread_; } + rtc::Thread* worker_thread() { return owned_worker_thread_.get(); } + rtc::Thread* signaling_thread() { return signaling_thread_; } + + private: + rtc::Thread* network_thread_; + rtc::Thread* signaling_thread_; + rtc::NetworkManager* network_manager_; + rtc::PacketSocketFactory* socket_factory_; + // If we created/own the objects above, these will be non-null and thus will + // be released automatically upon destruction. + std::unique_ptr owned_network_thread_; + std::unique_ptr owned_worker_thread_; + bool wraps_signaling_thread_ = false; + std::unique_ptr owned_network_manager_; + std::unique_ptr owned_socket_factory_; + RTC_DISALLOW_COPY_AND_ASSIGN(OrtcFactory); +}; + +BEGIN_OWNED_PROXY_MAP(OrtcFactory) + PROXY_SIGNALING_THREAD_DESTRUCTOR() + PROXY_METHOD3(std::unique_ptr, + CreateUdpTransport, + int, + uint16_t, + uint16_t) +END_PROXY_MAP() + +} // namespace webrtc + +#endif // WEBRTC_API_ORTCFACTORY_H_ diff --git a/webrtc/api/ortcfactory_unittest.cc b/webrtc/api/ortcfactory_unittest.cc new file mode 100644 index 0000000000..21ad6f8696 --- /dev/null +++ b/webrtc/api/ortcfactory_unittest.cc @@ -0,0 +1,122 @@ +/* + * Copyright 2017 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/api/ortcfactoryinterface.h" +#include "webrtc/base/fakenetwork.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/physicalsocketserver.h" +#include "webrtc/base/virtualsocketserver.h" +#include "webrtc/p2p/base/udptransport.h" + +namespace { + +const int kDefaultTimeout = 10000; // 10 seconds. +static const rtc::IPAddress kIPv4LocalHostAddress = + rtc::IPAddress(0x7F000001); // 127.0.0.1 + +class PacketReceiver : public sigslot::has_slots<> { + public: + explicit PacketReceiver(rtc::PacketTransportInterface* transport) { + transport->SignalReadPacket.connect(this, &PacketReceiver::OnReadPacket); + } + int packets_read() const { return packets_read_; } + + private: + void OnReadPacket(rtc::PacketTransportInterface*, + const char*, + size_t, + const rtc::PacketTime&, + int) { + ++packets_read_; + } + + int packets_read_ = 0; +}; + +} // namespace + +namespace webrtc { + +// Used to test that things work end-to-end when using the default +// implementations of threads/etc. provided by OrtcFactory, with the exception +// of using a virtual network. +// +// By default, the virtual network manager doesn't enumerate any networks, but +// sockets can still be created in this state. +class OrtcFactoryTest : public testing::Test { + public: + OrtcFactoryTest() + : virtual_socket_server_(&physical_socket_server_), + network_thread_(&virtual_socket_server_), + ortc_factory_(OrtcFactoryInterface::Create(&network_thread_, + nullptr, + &fake_network_manager_, + nullptr)) { + // Sockets are bound to the ANY address, so this is needed to tell the + // virtual network which address to use in this case. + virtual_socket_server_.SetDefaultRoute(kIPv4LocalHostAddress); + network_thread_.Start(); + } + + protected: + rtc::PhysicalSocketServer physical_socket_server_; + rtc::VirtualSocketServer virtual_socket_server_; + rtc::Thread network_thread_; + rtc::FakeNetworkManager fake_network_manager_; + std::unique_ptr ortc_factory_; +}; + +TEST_F(OrtcFactoryTest, EndToEndUdpTransport) { + std::unique_ptr transport1 = + ortc_factory_->CreateUdpTransport(AF_INET); + std::unique_ptr transport2 = + ortc_factory_->CreateUdpTransport(AF_INET); + ASSERT_NE(nullptr, transport1); + ASSERT_NE(nullptr, transport2); + // Sockets are bound to the ANY address, so we need to provide the IP address + // explicitly. + transport1->SetRemoteAddress( + rtc::SocketAddress(virtual_socket_server_.GetDefaultRoute(AF_INET), + transport2->GetLocalAddress().port())); + transport2->SetRemoteAddress( + rtc::SocketAddress(virtual_socket_server_.GetDefaultRoute(AF_INET), + transport1->GetLocalAddress().port())); + + // TODO(deadbeef): Once there's something (RTP senders/receivers) that can + // use UdpTransport end-to-end, use that for this end-to-end test instead of + // making assumptions about the implementation. + // + // For now, this assumes the returned object is a UdpTransportProxy that wraps + // a UdpTransport. + cricket::UdpTransport* internal_transport1 = + static_cast*>( + transport1.get()) + ->internal(); + cricket::UdpTransport* internal_transport2 = + static_cast*>( + transport2.get()) + ->internal(); + // Need to call internal "SendPacket" method on network thread. + network_thread_.Invoke( + RTC_FROM_HERE, [internal_transport1, internal_transport2]() { + PacketReceiver receiver1(internal_transport1); + PacketReceiver receiver2(internal_transport2); + internal_transport1->SendPacket("foo", sizeof("foo"), + rtc::PacketOptions(), 0); + internal_transport2->SendPacket("foo", sizeof("foo"), + rtc::PacketOptions(), 0); + EXPECT_EQ_WAIT(1, receiver1.packets_read(), kDefaultTimeout); + EXPECT_EQ_WAIT(1, receiver2.packets_read(), kDefaultTimeout); + }); +} + +} // namespace webrtc diff --git a/webrtc/api/ortcfactoryinterface.h b/webrtc/api/ortcfactoryinterface.h new file mode 100644 index 0000000000..8d46d6865e --- /dev/null +++ b/webrtc/api/ortcfactoryinterface.h @@ -0,0 +1,81 @@ +/* + * Copyright 2017 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_API_ORTCFACTORYINTERFACE_H_ +#define WEBRTC_API_ORTCFACTORYINTERFACE_H_ + +#include + +#include "webrtc/api/udptransportinterface.h" +#include "webrtc/base/network.h" +#include "webrtc/base/thread.h" +#include "webrtc/p2p/base/packetsocketfactory.h" + +namespace webrtc { + +// WARNING: This is experimental/under development, so use at your own risk; no +// guarantee about API stability is guaranteed here yet. +// +// This class is the ORTC analog of PeerConnectionFactory. It acts as a factory +// for ORTC objects that can be connected to each other. +// +// Some of these objects may not be represented by the ORTC specification, but +// follow the same general principles. +// +// On object lifetimes: The factory must not be destroyed before destroying the +// objects it created, and the objects passed into the factory must not be +// destroyed before destroying the factory. +class OrtcFactoryInterface { + public: + // |network_thread| is the thread on which packets are sent and received. + // If null, a new rtc::Thread with a default socket server is created. + // + // |signaling_thread| is used for callbacks to the consumer of the API. If + // null, the current thread will be used, which assumes that the API consumer + // is running a message loop on this thread (either using an existing + // rtc::Thread, or by calling rtc::Thread::Current()->ProcessMessages). + // + // |network_manager| is used to determine which network interfaces are + // available. This is used for ICE, for example. If null, a default + // implementation will be used. Only accessed on |network_thread|. + // + // |socket_factory| is used (on the network thread) for creating sockets. If + // it's null, a default implementation will be used, which assumes + // |network_thread| is a normal rtc::Thread. + // + // Note that the OrtcFactoryInterface does not take ownership of any of the + // objects + // passed in, and as previously stated, these objects can't be destroyed + // before the factory is. + static std::unique_ptr Create( + rtc::Thread* network_thread, + rtc::Thread* signaling_thread, + rtc::NetworkManager* network_manager, + rtc::PacketSocketFactory* socket_factory); + // Constructor for convenience which uses default implementations of + // everything (though does still require that the current thread runs a + // message loop; see above). + static std::unique_ptr Create() { + return Create(nullptr, nullptr, nullptr, nullptr); + } + + virtual ~OrtcFactoryInterface() {} + + virtual std::unique_ptr + CreateUdpTransport(int family, uint16_t min_port, uint16_t max_port) = 0; + // Method for convenience that has no port range restrictions. + std::unique_ptr CreateUdpTransport(int family) { + return CreateUdpTransport(family, 0, 0); + } +}; + +} // namespace webrtc + +#endif // WEBRTC_API_ORTCFACTORYINTERFACE_H_ diff --git a/webrtc/api/udptransportinterface.h b/webrtc/api/udptransportinterface.h new file mode 100644 index 0000000000..1a285311a4 --- /dev/null +++ b/webrtc/api/udptransportinterface.h @@ -0,0 +1,57 @@ +/* + * Copyright 2017 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_API_UDPTRANSPORTINTERFACE_H_ +#define WEBRTC_API_UDPTRANSPORTINTERFACE_H_ + +#include "webrtc/api/proxy.h" +#include "webrtc/base/socketaddress.h" + +namespace webrtc { + +// Interface for a raw UDP transport (not using ICE), meaning a combination of +// a local/remote IP address/port. +// +// An instance can be instantiated using OrtcFactory. +// +// Each instance reserves a UDP port, which will be freed when the +// UdpTransportInterface destructor is called. +// +// Calling SetRemoteAddress sets the destination of outgoing packets; without a +// destination, packets can't be sent, but they can be received. +class UdpTransportInterface { + public: + virtual ~UdpTransportInterface() {} + + // Get the address of the socket allocated for this transport. + virtual rtc::SocketAddress GetLocalAddress() const = 0; + + // Sets the address to which packets will be delivered. + // + // Calling with a "nil" (default-constructed) address is legal, and unsets + // any previously set destination. + // + // However, calling with an incomplete address (port or IP not set) will + // fail. + virtual bool SetRemoteAddress(const rtc::SocketAddress& dest) = 0; + // Simple getter. If never set, returns nil address. + virtual rtc::SocketAddress GetRemoteAddress() const = 0; +}; + +BEGIN_OWNED_PROXY_MAP(UdpTransport) + PROXY_WORKER_THREAD_DESTRUCTOR() + PROXY_WORKER_CONSTMETHOD0(rtc::SocketAddress, GetLocalAddress) + PROXY_WORKER_METHOD1(bool, SetRemoteAddress, const rtc::SocketAddress&) + PROXY_WORKER_CONSTMETHOD0(rtc::SocketAddress, GetRemoteAddress) +END_PROXY_MAP() + +} // namespace webrtc + +#endif // WEBRTC_API_UDPTRANSPORTINTERFACE_H_ diff --git a/webrtc/p2p/BUILD.gn b/webrtc/p2p/BUILD.gn index 649e9cfd21..bc2306bf24 100644 --- a/webrtc/p2p/BUILD.gn +++ b/webrtc/p2p/BUILD.gn @@ -74,8 +74,8 @@ rtc_static_library("rtc_p2p") { "base/turnport.cc", "base/turnport.h", "base/udpport.h", - "base/udptransportchannel.cc", - "base/udptransportchannel.h", + "base/udptransport.cc", + "base/udptransport.h", "client/basicportallocator.cc", "client/basicportallocator.h", "client/socketmonitor.cc", @@ -169,7 +169,7 @@ if (rtc_include_tests) { "base/transportdescriptionfactory_unittest.cc", "base/turnport_unittest.cc", "base/turnserver_unittest.cc", - "base/udptransportchannel_unittest.cc", + "base/udptransport_unittest.cc", "client/basicportallocator_unittest.cc", ] if (rtc_use_quic) { diff --git a/webrtc/p2p/base/udptransport.cc b/webrtc/p2p/base/udptransport.cc new file mode 100644 index 0000000000..976d5bb6ae --- /dev/null +++ b/webrtc/p2p/base/udptransport.cc @@ -0,0 +1,104 @@ +/* + * Copyright 2016 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/udptransport.h" + +#include +#include // For std::move. + +#include "webrtc/base/asyncudpsocket.h" +#include "webrtc/base/asyncpacketsocket.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/socketaddress.h" +#include "webrtc/base/thread.h" +#include "webrtc/base/thread_checker.h" + +namespace cricket { + +UdpTransport::UdpTransport(const std::string& transport_name, + std::unique_ptr socket) + : transport_name_(transport_name), socket_(std::move(socket)) { + RTC_DCHECK(socket_); + socket_->SignalReadPacket.connect(this, &UdpTransport::OnSocketReadPacket); + socket_->SignalSentPacket.connect(this, &UdpTransport::OnSocketSentPacket); +} + +UdpTransport::~UdpTransport() { + RTC_DCHECK_RUN_ON(&network_thread_checker_); +} + +rtc::SocketAddress UdpTransport::GetLocalAddress() const { + RTC_DCHECK_RUN_ON(&network_thread_checker_); + return socket_->GetLocalAddress(); +} + +bool UdpTransport::SetRemoteAddress(const rtc::SocketAddress& addr) { + RTC_DCHECK_RUN_ON(&network_thread_checker_); + if (!addr.IsComplete()) { + LOG(LS_WARNING) << "Remote address not complete."; + return false; + } + // TODO(johan): check for ipv4, other settings. + bool prev_destination_nil = remote_address_.IsNil(); + remote_address_ = addr; + // Going from "didn't have destination" to "have destination" or vice versa. + if (prev_destination_nil != remote_address_.IsNil()) { + SignalWritableState(this); + if (prev_destination_nil) { + SignalReadyToSend(this); + } + } + return true; +} + +rtc::SocketAddress UdpTransport::GetRemoteAddress() const { + RTC_DCHECK_RUN_ON(&network_thread_checker_); + return remote_address_; +} + +bool UdpTransport::writable() const { + RTC_DCHECK_RUN_ON(&network_thread_checker_); + return !remote_address_.IsNil(); +} + +int UdpTransport::SendPacket(const char* data, + size_t len, + const rtc::PacketOptions& options, + int flags) { + // No thread_checker in high frequency network function. + if (remote_address_.IsNil()) { + LOG(LS_WARNING) << "Remote address not set."; + send_error_ = ENOTCONN; + return -1; + } + int result = + socket_->SendTo((const void*)data, len, remote_address_, options); + if (result <= 0) { + LOG(LS_VERBOSE) << "SendPacket() " << result; + } + return result; +} + +void UdpTransport::OnSocketReadPacket(rtc::AsyncPacketSocket* socket, + const char* data, + size_t len, + const rtc::SocketAddress& remote_addr, + const rtc::PacketTime& packet_time) { + // No thread_checker in high frequency network function. + SignalReadPacket(this, data, len, packet_time, 0); +} + +void UdpTransport::OnSocketSentPacket(rtc::AsyncPacketSocket* socket, + const rtc::SentPacket& packet) { + RTC_DCHECK_EQ(socket_.get(), socket); + SignalSentPacket(this, packet); +} + +} // namespace cricket diff --git a/webrtc/p2p/base/udptransportchannel.h b/webrtc/p2p/base/udptransport.h similarity index 56% rename from webrtc/p2p/base/udptransportchannel.h rename to webrtc/p2p/base/udptransport.h index 2152fb7308..802d892dba 100644 --- a/webrtc/p2p/base/udptransportchannel.h +++ b/webrtc/p2p/base/udptransport.h @@ -8,33 +8,44 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_P2P_BASE_UDPTRANSPORTCHANNEL_H_ -#define WEBRTC_P2P_BASE_UDPTRANSPORTCHANNEL_H_ +#ifndef WEBRTC_P2P_BASE_UDPTRANSPORT_H_ +#define WEBRTC_P2P_BASE_UDPTRANSPORT_H_ #include #include +#include "webrtc/api/udptransportinterface.h" +#include "webrtc/base/asyncpacketsocket.h" // For PacketOptions. #include "webrtc/base/optional.h" #include "webrtc/base/thread_checker.h" #include "webrtc/p2p/base/packettransportinterface.h" namespace rtc { class AsyncPacketSocket; -class PhysicalSocketServer; +struct PacketTime; +struct SentPacket; class SocketAddress; -class SocketServer; -class Thread; } namespace cricket { -class UdpTransportChannel : public rtc::PacketTransportInterface { +// Implementation of UdpTransportInterface. +// Used by OrtcFactory. +class UdpTransport : public webrtc::UdpTransportInterface, + public rtc::PacketTransportInterface { public: - enum class State { INIT, CONNECTING, CONNECTED, FAILED }; - explicit UdpTransportChannel(const std::string& transport_name); - UdpTransportChannel(const std::string& transport_name, rtc::SocketServer* ss); - ~UdpTransportChannel(); + // |transport_name| is only used for identification/logging. + // |socket| must be non-null. + UdpTransport(const std::string& transport_name, + std::unique_ptr socket); + ~UdpTransport(); + // Overrides of UdpTransportInterface, used by the API consumer. + rtc::SocketAddress GetLocalAddress() const override; + bool SetRemoteAddress(const rtc::SocketAddress& addr) override; + rtc::SocketAddress GetRemoteAddress() const override; + + // Overrides of PacketTransportInterface, used by webrtc internally. const std::string debug_name() const override { return transport_name_; } bool receiving() const override { @@ -53,24 +64,6 @@ class UdpTransportChannel : public rtc::PacketTransportInterface { int GetError() override { return send_error_; } - State state() const { - RTC_DCHECK_RUN_ON(&network_thread_checker_); - return state_; - } - - // Start() makes UdpTransportChannel transition from state INIT to CONNECTING. - // It creates the local UDP socket and binds it to a port. - // Consider checking state() after calling Start(). - void Start(); - - void SetRemoteParameters(const rtc::SocketAddress& remote); - - // Returned optional does not have a value if in the INIT or FAILED state. - // Consider checking state() before calling local_parameters(). - rtc::Optional local_parameters() const { - return local_parameters_; - } - private: void OnSocketReadPacket(rtc::AsyncPacketSocket* socket, const char* data, @@ -79,18 +72,14 @@ class UdpTransportChannel : public rtc::PacketTransportInterface { const rtc::PacketTime& packet_time); void OnSocketSentPacket(rtc::AsyncPacketSocket* socket, const rtc::SentPacket& packet); - void SetState(State state); // Set State and Signal. bool IsLocalConsistent(); - void UpdateState(); std::string transport_name_; - rtc::SocketServer* socket_server_; - State state_ = State::INIT; int send_error_ = 0; std::unique_ptr socket_; - rtc::Optional local_parameters_; - rtc::Optional remote_parameters_; + // If not set, will be an "nil" address ("IsNil" returns true). + rtc::SocketAddress remote_address_; rtc::ThreadChecker network_thread_checker_; }; } // namespace cricket -#endif // WEBRTC_P2P_BASE_UDPTRANSPORTCHANNEL_H_ +#endif // WEBRTC_P2P_BASE_UDPTRANSPORT_H_ diff --git a/webrtc/p2p/base/udptransportchannel_unittest.cc b/webrtc/p2p/base/udptransport_unittest.cc similarity index 66% rename from webrtc/p2p/base/udptransportchannel_unittest.cc rename to webrtc/p2p/base/udptransport_unittest.cc index 1cedde6472..0e67beb87c 100644 --- a/webrtc/p2p/base/udptransportchannel_unittest.cc +++ b/webrtc/p2p/base/udptransport_unittest.cc @@ -22,8 +22,9 @@ #include "webrtc/base/socketaddress.h" #include "webrtc/base/socketserver.h" #include "webrtc/base/virtualsocketserver.h" +#include "webrtc/p2p/base/basicpacketsocketfactory.h" #include "webrtc/p2p/base/packettransportinterface.h" -#include "webrtc/p2p/base/udptransportchannel.h" +#include "webrtc/p2p/base/udptransport.h" namespace cricket { @@ -31,28 +32,39 @@ constexpr int kTimeoutMs = 10000; static const rtc::IPAddress kIPv4LocalHostAddress = rtc::IPAddress(0x7F000001); // 127.0.0.1 -class UdpTransportChannelTest : public testing::Test, - public sigslot::has_slots<> { +class UdpTransportTest : public testing::Test, public sigslot::has_slots<> { public: - UdpTransportChannelTest() + UdpTransportTest() : network_thread_(rtc::Thread::Current()), physical_socket_server_(new rtc::PhysicalSocketServer), virtual_socket_server_( new rtc::VirtualSocketServer(physical_socket_server_.get())), ss_scope_(virtual_socket_server_.get()), - ep1_("Name1"), - ep2_("name2") { - // Setup IP Address for outgoing packets from sockets bound to - // IPV4 INADDR_ANY ("0.0.0.0."). Virtual socket server sends these packets - // only if the default address is explicit set. With a physical socket, the - // actual network stack / operating system would set the IP address for - // outgoing packets. + ep1_("Name1", + std::unique_ptr( + socket_factory_.CreateUdpSocket( + rtc::SocketAddress(rtc::GetAnyIP(AF_INET), 0), + 0, + 0))), + ep2_("Name2", + std::unique_ptr( + socket_factory_.CreateUdpSocket( + rtc::SocketAddress(rtc::GetAnyIP(AF_INET), 0), + 0, + 0))) { + // Setup IP Address for outgoing packets from sockets bound to IPV4 + // INADDR_ANY ("0.0.0.0."), as used above when creating the virtual + // sockets. The virtual socket server sends these packets only if the + // default address is explicit set. With a physical socket, the actual + // network stack / operating system would set the IP address for outgoing + // packets. virtual_socket_server_->SetDefaultRoute(kIPv4LocalHostAddress); } struct Endpoint : public sigslot::has_slots<> { - explicit Endpoint(std::string tch_name) { - ch_.reset(new UdpTransportChannel(std::move(tch_name))); + explicit Endpoint(std::string tch_name, + std::unique_ptr socket) { + ch_.reset(new UdpTransport(std::move(tch_name), std::move(socket))); ch_->SignalReadPacket.connect(this, &Endpoint::OnReadPacket); ch_->SignalSentPacket.connect(this, &Endpoint::OnSentPacket); ch_->SignalReadyToSend.connect(this, &Endpoint::OnReadyToSend); @@ -98,16 +110,11 @@ class UdpTransportChannelTest : public testing::Test, } void GetLocalPort(uint16_t* local_port) { - rtc::Optional addr = ch_->local_parameters(); - if (!addr) { - *local_port = 0; - return; - } - *local_port = addr->port(); + *local_port = ch_->GetLocalAddress().port(); } std::list ch_packets_; - std::unique_ptr ch_; + std::unique_ptr ch_; uint32_t num_received_packets_ = 0; // Increases on SignalReadPacket. uint32_t num_sig_sent_packets_ = 0; // Increases on SignalSentPacket. uint32_t num_sig_writable_ = 0; // Increases on SignalWritable. @@ -118,6 +125,8 @@ class UdpTransportChannelTest : public testing::Test, std::unique_ptr physical_socket_server_; std::unique_ptr virtual_socket_server_; rtc::SocketServerScope ss_scope_; + // Uses current thread's socket server, which will be set by ss_scope_. + rtc::BasicPacketSocketFactory socket_factory_; Endpoint ep1_; Endpoint ep2_; @@ -137,43 +146,43 @@ class UdpTransportChannelTest : public testing::Test, } }; -TEST_F(UdpTransportChannelTest, SendRecvBasic) { - ep1_.ch_->Start(); - ep2_.ch_->Start(); +TEST_F(UdpTransportTest, AddressGetters) { + // Initially, remote address should be nil but local address shouldn't be. + EXPECT_FALSE(ep1_.ch_->GetLocalAddress().IsNil()); + EXPECT_TRUE(ep1_.ch_->GetRemoteAddress().IsNil()); + rtc::SocketAddress destination("127.0.0.1", 1337); + ASSERT_TRUE(ep1_.ch_->SetRemoteAddress(destination)); + EXPECT_EQ(destination, ep1_.ch_->GetRemoteAddress()); +} + +// Setting an invalid address should fail and have no effect. +TEST_F(UdpTransportTest, SettingIncompleteRemoteAddressFails) { + EXPECT_FALSE(ep1_.ch_->SetRemoteAddress(rtc::SocketAddress("127.0.0.1", 0))); + EXPECT_TRUE(ep1_.ch_->GetRemoteAddress().IsNil()); +} + +TEST_F(UdpTransportTest, SendRecvBasic) { uint16_t port; ep2_.GetLocalPort(&port); rtc::SocketAddress addr2 = rtc::SocketAddress("127.0.0.1", port); - ep1_.ch_->SetRemoteParameters(addr2); + EXPECT_TRUE(ep1_.ch_->SetRemoteAddress(addr2)); ep1_.GetLocalPort(&port); rtc::SocketAddress addr1 = rtc::SocketAddress("127.0.0.1", port); - ep2_.ch_->SetRemoteParameters(addr1); + EXPECT_TRUE(ep2_.ch_->SetRemoteAddress(addr1)); TestSendRecv(); } -TEST_F(UdpTransportChannelTest, DefaultLocalParameters) { - EXPECT_FALSE(ep1_.ch_->local_parameters()); -} - -TEST_F(UdpTransportChannelTest, StartTwice) { - ep1_.ch_->Start(); - EXPECT_EQ(UdpTransportChannel::State::CONNECTING, ep1_.ch_->state()); - ep1_.ch_->Start(); - EXPECT_EQ(UdpTransportChannel::State::CONNECTING, ep1_.ch_->state()); -} - -TEST_F(UdpTransportChannelTest, StatusAndSignals) { - EXPECT_EQ(UdpTransportChannel::State::INIT, ep1_.ch_->state()); - ep1_.ch_->Start(); - EXPECT_EQ(UdpTransportChannel::State::CONNECTING, ep1_.ch_->state()); +// Test the signals and state methods used internally by causing a UdpTransport +// to send a packet to itself. +TEST_F(UdpTransportTest, StatusAndSignals) { EXPECT_EQ(0u, ep1_.num_sig_writable_); EXPECT_EQ(0u, ep1_.num_sig_ready_to_send_); // Loopback EXPECT_TRUE(!ep1_.ch_->writable()); - rtc::Optional addr = ep1_.ch_->local_parameters(); - ASSERT_TRUE(addr); + rtc::SocketAddress addr = ep1_.ch_->GetLocalAddress(); // Keep port, but explicitly set IP. - addr->SetIP("127.0.0.1"); - ep1_.ch_->SetRemoteParameters(*addr); + addr.SetIP("127.0.0.1"); + ep1_.ch_->SetRemoteAddress(addr); EXPECT_TRUE(ep1_.ch_->writable()); EXPECT_EQ(1u, ep1_.num_sig_writable_); EXPECT_EQ(1u, ep1_.num_sig_ready_to_send_); @@ -182,4 +191,5 @@ TEST_F(UdpTransportChannelTest, StatusAndSignals) { EXPECT_EQ_WAIT(1u, ep1_.ch_packets_.size(), kTimeoutMs); EXPECT_EQ_WAIT(1u, ep1_.num_sig_sent_packets_, kTimeoutMs); } + } // namespace cricket diff --git a/webrtc/p2p/base/udptransportchannel.cc b/webrtc/p2p/base/udptransportchannel.cc deleted file mode 100644 index 7360217778..0000000000 --- a/webrtc/p2p/base/udptransportchannel.cc +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2016 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/udptransportchannel.h" - -#include - -#include "webrtc/base/asyncudpsocket.h" -#include "webrtc/base/asyncpacketsocket.h" -#include "webrtc/base/logging.h" -#include "webrtc/base/physicalsocketserver.h" -#include "webrtc/base/socketaddress.h" -#include "webrtc/base/thread.h" -#include "webrtc/base/thread_checker.h" -#include "webrtc/p2p/base/basicpacketsocketfactory.h" -#include "webrtc/p2p/base/packettransportinterface.h" - -namespace cricket { - -UdpTransportChannel::UdpTransportChannel(const std::string& transport_name) - : UdpTransportChannel(transport_name, - rtc::Thread::Current()->socketserver()) {} - -UdpTransportChannel::UdpTransportChannel(const std::string& transport_name, - rtc::SocketServer* socket_server) - : transport_name_(transport_name), socket_server_(socket_server) {} - -UdpTransportChannel::~UdpTransportChannel() { - RTC_DCHECK_RUN_ON(&network_thread_checker_); -} - -void UdpTransportChannel::OnSocketReadPacket( - rtc::AsyncPacketSocket* socket, - const char* data, - size_t len, - const rtc::SocketAddress& remote_addr, - const rtc::PacketTime& packet_time) { - // No thread_checker in high frequency network function. - SignalReadPacket(this, data, len, packet_time, 0); -} - -void UdpTransportChannel::OnSocketSentPacket(rtc::AsyncPacketSocket* socket, - const rtc::SentPacket& packet) { - RTC_DCHECK_EQ(socket_.get(), socket); - SignalSentPacket(this, packet); -} - -bool UdpTransportChannel::writable() const { - return state_ == State::CONNECTED; -} - -int UdpTransportChannel::SendPacket(const char* data, - size_t len, - const rtc::PacketOptions& options, - int flags) { - // No thread_checker in high frequency network function. - if (!remote_parameters_) { - LOG(LS_WARNING) << "Remote parameters not set."; - send_error_ = ENOTCONN; - return -1; - } - const rtc::SocketAddress& remote_addr_ = *remote_parameters_; - int result = socket_->SendTo((const void*)data, len, remote_addr_, options); - if (result <= 0) { - LOG(LS_VERBOSE) << "SendPacket() " << result; - } - return result; -} - -void UdpTransportChannel::Start() { - RTC_DCHECK_RUN_ON(&network_thread_checker_); - if (socket_) { - LOG(LS_WARNING) << "Local socket already allocated."; - return; - } - static constexpr uint16_t kMaxTries = 100; - static constexpr uint16_t kMinPortNumber = 2000; - // TODO(johan) provide configuration option for kMinPortNumber. - rtc::SocketAddress socket_addr("0.0.0.0", 0); - // TODO(johan): Replace BasicPacketSocketFactory by something that honors RFC - // 3550 Section 11 port number requirements like - // {port_{RTP} is even, port_{RTCP} := port{RTP} + 1}. - rtc::BasicPacketSocketFactory socket_factory(socket_server_); - socket_.reset(socket_factory.CreateUdpSocket(socket_addr, kMinPortNumber, - kMinPortNumber + kMaxTries)); - if (socket_) { - local_parameters_ = - rtc::Optional(socket_->GetLocalAddress()); - LOG(INFO) << "Created UDP socket with addr " << local_parameters_->ipaddr() - << " port " << local_parameters_->port() << "."; - socket_->SignalReadPacket.connect(this, - &UdpTransportChannel::OnSocketReadPacket); - socket_->SignalSentPacket.connect(this, - &UdpTransportChannel::OnSocketSentPacket); - } else { - LOG(INFO) << "Local socket allocation failure"; - } - UpdateState(); - return; -} - -void UdpTransportChannel::UpdateState() { - RTC_DCHECK_RUN_ON(&network_thread_checker_); - RTC_DCHECK(!(local_parameters_ && !socket_)); - RTC_DCHECK(!(!local_parameters_ && socket_)); - if (!local_parameters_) { - SetState(State::INIT); - } else if (!remote_parameters_) { - SetState(State::CONNECTING); - } else { - SetState(State::CONNECTED); - } -} - -void UdpTransportChannel::SetRemoteParameters(const rtc::SocketAddress& addr) { - RTC_DCHECK_RUN_ON(&network_thread_checker_); - if (!addr.IsComplete()) { - LOG(INFO) << "remote address not complete"; - return; - } - // TODO(johan) check for ipv4, other settings. - remote_parameters_ = rtc::Optional(addr); - UpdateState(); -} - -void UdpTransportChannel::SetState(State state) { - RTC_DCHECK_RUN_ON(&network_thread_checker_); - if (state_ == state) { - return; - } - state_ = state; - if (state == State::CONNECTED) { - SignalWritableState(this); - SignalReadyToSend(this); - } -} -} // namespace cricket