diff options
Diffstat (limited to 'third_party/libwebrtc/test/network/fake_network_socket_server.cc')
-rw-r--r-- | third_party/libwebrtc/test/network/fake_network_socket_server.cc | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/third_party/libwebrtc/test/network/fake_network_socket_server.cc b/third_party/libwebrtc/test/network/fake_network_socket_server.cc new file mode 100644 index 0000000000..c94c4e372a --- /dev/null +++ b/third_party/libwebrtc/test/network/fake_network_socket_server.cc @@ -0,0 +1,322 @@ +/* + * Copyright (c) 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 "test/network/fake_network_socket_server.h" + +#include <algorithm> +#include <string> +#include <utility> +#include <vector> + +#include "absl/algorithm/container.h" +#include "api/scoped_refptr.h" +#include "api/task_queue/pending_task_safety_flag.h" +#include "rtc_base/event.h" +#include "rtc_base/logging.h" +#include "rtc_base/thread.h" + +namespace webrtc { +namespace test { +namespace { +std::string ToString(const rtc::SocketAddress& addr) { + return addr.HostAsURIString() + ":" + std::to_string(addr.port()); +} + +} // namespace + +// Represents a socket, which will operate with emulated network. +class FakeNetworkSocket : public rtc::Socket, + public EmulatedNetworkReceiverInterface { + public: + explicit FakeNetworkSocket(FakeNetworkSocketServer* scoket_manager, + rtc::Thread* thread); + ~FakeNetworkSocket() override; + + // Will be invoked by EmulatedEndpoint to deliver packets into this socket. + void OnPacketReceived(EmulatedIpPacket packet) override; + + // rtc::Socket methods: + rtc::SocketAddress GetLocalAddress() const override; + rtc::SocketAddress GetRemoteAddress() const override; + int Bind(const rtc::SocketAddress& addr) override; + int Connect(const rtc::SocketAddress& addr) override; + int Close() override; + int Send(const void* pv, size_t cb) override; + int SendTo(const void* pv, + size_t cb, + const rtc::SocketAddress& addr) override; + int Recv(void* pv, size_t cb, int64_t* timestamp) override; + int RecvFrom(void* pv, + size_t cb, + rtc::SocketAddress* paddr, + int64_t* timestamp) override; + int Listen(int backlog) override; + rtc::Socket* Accept(rtc::SocketAddress* paddr) override; + int GetError() const override; + void SetError(int error) override; + ConnState GetState() const override; + int GetOption(Option opt, int* value) override; + int SetOption(Option opt, int value) override; + + private: + FakeNetworkSocketServer* const socket_server_; + rtc::Thread* const thread_; + EmulatedEndpointImpl* endpoint_ RTC_GUARDED_BY(&thread_); + rtc::SocketAddress local_addr_ RTC_GUARDED_BY(&thread_); + rtc::SocketAddress remote_addr_ RTC_GUARDED_BY(&thread_); + ConnState state_ RTC_GUARDED_BY(&thread_); + int error_ RTC_GUARDED_BY(&thread_); + std::map<Option, int> options_map_ RTC_GUARDED_BY(&thread_); + + absl::optional<EmulatedIpPacket> pending_ RTC_GUARDED_BY(thread_); + rtc::scoped_refptr<PendingTaskSafetyFlag> alive_; +}; + +FakeNetworkSocket::FakeNetworkSocket(FakeNetworkSocketServer* socket_server, + rtc::Thread* thread) + : socket_server_(socket_server), + thread_(thread), + state_(CS_CLOSED), + error_(0), + alive_(PendingTaskSafetyFlag::Create()) {} + +FakeNetworkSocket::~FakeNetworkSocket() { + // Abandon all pending packets. + alive_->SetNotAlive(); + + Close(); + socket_server_->Unregister(this); +} + +void FakeNetworkSocket::OnPacketReceived(EmulatedIpPacket packet) { + auto task = [this, packet = std::move(packet)]() mutable { + RTC_DCHECK_RUN_ON(thread_); + if (!endpoint_->Enabled()) + return; + RTC_DCHECK(!pending_); + pending_ = std::move(packet); + // Note that we expect that this will trigger exactly one call to RecvFrom() + // where pending_packet will be read and reset. This call is done without + // any thread switch (see AsyncUDPSocket::OnReadEvent) so it's safe to + // assume that SignalReadEvent() will block until the packet has been read. + SignalReadEvent(this); + RTC_DCHECK(!pending_); + }; + thread_->PostTask(SafeTask(alive_, std::move(task))); + socket_server_->WakeUp(); +} + + +rtc::SocketAddress FakeNetworkSocket::GetLocalAddress() const { + RTC_DCHECK_RUN_ON(thread_); + return local_addr_; +} + +rtc::SocketAddress FakeNetworkSocket::GetRemoteAddress() const { + RTC_DCHECK_RUN_ON(thread_); + return remote_addr_; +} + +int FakeNetworkSocket::Bind(const rtc::SocketAddress& addr) { + RTC_DCHECK_RUN_ON(thread_); + RTC_CHECK(local_addr_.IsNil()) + << "Socket already bound to address: " << ToString(local_addr_); + local_addr_ = addr; + endpoint_ = socket_server_->GetEndpointNode(local_addr_.ipaddr()); + if (!endpoint_) { + local_addr_.Clear(); + RTC_LOG(LS_INFO) << "No endpoint for address: " << ToString(addr); + error_ = EADDRNOTAVAIL; + return 2; + } + absl::optional<uint16_t> port = + endpoint_->BindReceiver(local_addr_.port(), this); + if (!port) { + local_addr_.Clear(); + RTC_LOG(LS_INFO) << "Cannot bind to in-use address: " << ToString(addr); + error_ = EADDRINUSE; + return 1; + } + local_addr_.SetPort(port.value()); + return 0; +} + +int FakeNetworkSocket::Connect(const rtc::SocketAddress& addr) { + RTC_DCHECK_RUN_ON(thread_); + RTC_CHECK(remote_addr_.IsNil()) + << "Socket already connected to address: " << ToString(remote_addr_); + RTC_CHECK(!local_addr_.IsNil()) + << "Socket have to be bind to some local address"; + remote_addr_ = addr; + state_ = CS_CONNECTED; + return 0; +} + +int FakeNetworkSocket::Send(const void* pv, size_t cb) { + RTC_DCHECK_RUN_ON(thread_); + RTC_CHECK(state_ == CS_CONNECTED) << "Socket cannot send: not connected"; + return SendTo(pv, cb, remote_addr_); +} + +int FakeNetworkSocket::SendTo(const void* pv, + size_t cb, + const rtc::SocketAddress& addr) { + RTC_DCHECK_RUN_ON(thread_); + RTC_CHECK(!local_addr_.IsNil()) + << "Socket have to be bind to some local address"; + if (!endpoint_->Enabled()) { + error_ = ENETDOWN; + return -1; + } + rtc::CopyOnWriteBuffer packet(static_cast<const uint8_t*>(pv), cb); + endpoint_->SendPacket(local_addr_, addr, packet); + return cb; +} + +int FakeNetworkSocket::Recv(void* pv, size_t cb, int64_t* timestamp) { + rtc::SocketAddress paddr; + return RecvFrom(pv, cb, &paddr, timestamp); +} + +// Reads 1 packet from internal queue. Reads up to `cb` bytes into `pv` +// and returns the length of received packet. +int FakeNetworkSocket::RecvFrom(void* pv, + size_t cb, + rtc::SocketAddress* paddr, + int64_t* timestamp) { + RTC_DCHECK_RUN_ON(thread_); + + if (timestamp) { + *timestamp = -1; + } + RTC_CHECK(pending_); + + *paddr = pending_->from; + size_t data_read = std::min(cb, pending_->size()); + memcpy(pv, pending_->cdata(), data_read); + *timestamp = pending_->arrival_time.us(); + + // According to RECV(2) Linux Man page + // real socket will discard data, that won't fit into provided buffer, + // but we won't to skip such error, so we will assert here. + RTC_CHECK(data_read == pending_->size()) + << "Too small buffer is provided for socket read. " + "Received data size: " + << pending_->size() << "; Provided buffer size: " << cb; + + pending_.reset(); + + // According to RECV(2) Linux Man page + // real socket will return message length, not data read. In our case it is + // actually the same value. + return static_cast<int>(data_read); +} + +int FakeNetworkSocket::Listen(int backlog) { + RTC_CHECK(false) << "Listen() isn't valid for SOCK_DGRAM"; +} + +rtc::Socket* FakeNetworkSocket::Accept(rtc::SocketAddress* /*paddr*/) { + RTC_CHECK(false) << "Accept() isn't valid for SOCK_DGRAM"; +} + +int FakeNetworkSocket::Close() { + RTC_DCHECK_RUN_ON(thread_); + state_ = CS_CLOSED; + if (!local_addr_.IsNil()) { + endpoint_->UnbindReceiver(local_addr_.port()); + } + local_addr_.Clear(); + remote_addr_.Clear(); + return 0; +} + +int FakeNetworkSocket::GetError() const { + RTC_DCHECK_RUN_ON(thread_); + return error_; +} + +void FakeNetworkSocket::SetError(int error) { + RTC_DCHECK_RUN_ON(thread_); + RTC_CHECK(error == 0); + error_ = error; +} + +rtc::Socket::ConnState FakeNetworkSocket::GetState() const { + RTC_DCHECK_RUN_ON(thread_); + return state_; +} + +int FakeNetworkSocket::GetOption(Option opt, int* value) { + RTC_DCHECK_RUN_ON(thread_); + auto it = options_map_.find(opt); + if (it == options_map_.end()) { + return -1; + } + *value = it->second; + return 0; +} + +int FakeNetworkSocket::SetOption(Option opt, int value) { + RTC_DCHECK_RUN_ON(thread_); + options_map_[opt] = value; + return 0; +} + +FakeNetworkSocketServer::FakeNetworkSocketServer( + EndpointsContainer* endpoints_container) + : endpoints_container_(endpoints_container), + wakeup_(/*manual_reset=*/false, /*initially_signaled=*/false) {} +FakeNetworkSocketServer::~FakeNetworkSocketServer() = default; + +EmulatedEndpointImpl* FakeNetworkSocketServer::GetEndpointNode( + const rtc::IPAddress& ip) { + return endpoints_container_->LookupByLocalAddress(ip); +} + +void FakeNetworkSocketServer::Unregister(FakeNetworkSocket* socket) { + MutexLock lock(&lock_); + sockets_.erase(absl::c_find(sockets_, socket)); +} + +rtc::Socket* FakeNetworkSocketServer::CreateSocket(int family, int type) { + RTC_DCHECK(family == AF_INET || family == AF_INET6); + // We support only UDP sockets for now. + RTC_DCHECK(type == SOCK_DGRAM) << "Only UDP sockets are supported"; + RTC_DCHECK(thread_) << "must be attached to thread before creating sockets"; + FakeNetworkSocket* out = new FakeNetworkSocket(this, thread_); + { + MutexLock lock(&lock_); + sockets_.push_back(out); + } + return out; +} + +void FakeNetworkSocketServer::SetMessageQueue(rtc::Thread* thread) { + thread_ = thread; +} + +// Always returns true (if return false, it won't be invoked again...) +bool FakeNetworkSocketServer::Wait(webrtc::TimeDelta max_wait_duration, + bool process_io) { + RTC_DCHECK(thread_ == rtc::Thread::Current()); + if (!max_wait_duration.IsZero()) + wakeup_.Wait(max_wait_duration); + + return true; +} + +void FakeNetworkSocketServer::WakeUp() { + wakeup_.Set(); +} + + +} // namespace test +} // namespace webrtc |