| Index: remoting/test/fake_socket_factory.cc
|
| diff --git a/remoting/test/fake_socket_factory.cc b/remoting/test/fake_socket_factory.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c9b927d3afe79cd9bdb054c10edb970536a77b4a
|
| --- /dev/null
|
| +++ b/remoting/test/fake_socket_factory.cc
|
| @@ -0,0 +1,328 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +// MSVC++ requires this to be set before any other includes to get M_PI.
|
| +#define _USE_MATH_DEFINES
|
| +
|
| +#include "remoting/test/fake_socket_factory.h"
|
| +
|
| +#include <math.h>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/callback.h"
|
| +#include "base/location.h"
|
| +#include "base/rand_util.h"
|
| +#include "base/single_thread_task_runner.h"
|
| +#include "base/thread_task_runner_handle.h"
|
| +#include "net/base/io_buffer.h"
|
| +#include "remoting/test/leaky_bucket.h"
|
| +#include "third_party/webrtc/base/asyncpacketsocket.h"
|
| +
|
| +namespace remoting {
|
| +
|
| +namespace {
|
| +
|
| +const int kPortRangeStart = 1024;
|
| +const int kPortRangeEnd = 65535;
|
| +
|
| +double GetNormalRandom(double average, double stddev) {
|
| + // Based on Box-Muller transform, see
|
| + // http://en.wikipedia.org/wiki/Box_Muller_transform .
|
| + return average +
|
| + stddev * sqrt(-2.0 * log(1.0 - base::RandDouble())) *
|
| + cos(base::RandDouble() * 2.0 * M_PI);
|
| +}
|
| +
|
| +class FakeUdpSocket : public rtc::AsyncPacketSocket {
|
| + public:
|
| + FakeUdpSocket(FakePacketSocketFactory* factory,
|
| + scoped_refptr<FakeNetworkDispatcher> dispatcher,
|
| + const rtc::SocketAddress& local_address);
|
| + virtual ~FakeUdpSocket();
|
| +
|
| + void ReceivePacket(const rtc::SocketAddress& from,
|
| + const rtc::SocketAddress& to,
|
| + const scoped_refptr<net::IOBuffer>& data,
|
| + int data_size);
|
| +
|
| + // rtc::AsyncPacketSocket interface.
|
| + virtual rtc::SocketAddress GetLocalAddress() const OVERRIDE;
|
| + virtual rtc::SocketAddress GetRemoteAddress() const OVERRIDE;
|
| + virtual int Send(const void* data, size_t data_size,
|
| + const rtc::PacketOptions& options) OVERRIDE;
|
| + virtual int SendTo(const void* data, size_t data_size,
|
| + const rtc::SocketAddress& address,
|
| + const rtc::PacketOptions& options) OVERRIDE;
|
| + virtual int Close() OVERRIDE;
|
| + virtual State GetState() const OVERRIDE;
|
| + virtual int GetOption(rtc::Socket::Option option, int* value) OVERRIDE;
|
| + virtual int SetOption(rtc::Socket::Option option, int value) OVERRIDE;
|
| + virtual int GetError() const OVERRIDE;
|
| + virtual void SetError(int error) OVERRIDE;
|
| +
|
| + private:
|
| + FakePacketSocketFactory* factory_;
|
| + scoped_refptr<FakeNetworkDispatcher> dispatcher_;
|
| + rtc::SocketAddress local_address_;
|
| + State state_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(FakeUdpSocket);
|
| +};
|
| +
|
| +FakeUdpSocket::FakeUdpSocket(FakePacketSocketFactory* factory,
|
| + scoped_refptr<FakeNetworkDispatcher> dispatcher,
|
| + const rtc::SocketAddress& local_address)
|
| + : factory_(factory),
|
| + dispatcher_(dispatcher),
|
| + local_address_(local_address),
|
| + state_(STATE_BOUND) {
|
| +}
|
| +
|
| +FakeUdpSocket::~FakeUdpSocket() {
|
| + factory_->OnSocketDestroyed(local_address_.port());
|
| +}
|
| +
|
| +void FakeUdpSocket::ReceivePacket(const rtc::SocketAddress& from,
|
| + const rtc::SocketAddress& to,
|
| + const scoped_refptr<net::IOBuffer>& data,
|
| + int data_size) {
|
| + SignalReadPacket(
|
| + this, data->data(), data_size, from, rtc::CreatePacketTime(0));
|
| +}
|
| +
|
| +rtc::SocketAddress FakeUdpSocket::GetLocalAddress() const {
|
| + return local_address_;
|
| +}
|
| +
|
| +rtc::SocketAddress FakeUdpSocket::GetRemoteAddress() const {
|
| + NOTREACHED();
|
| + return rtc::SocketAddress();
|
| +}
|
| +
|
| +int FakeUdpSocket::Send(const void* data, size_t data_size,
|
| + const rtc::PacketOptions& options) {
|
| + NOTREACHED();
|
| + return EINVAL;
|
| +}
|
| +
|
| +int FakeUdpSocket::SendTo(const void* data, size_t data_size,
|
| + const rtc::SocketAddress& address,
|
| + const rtc::PacketOptions& options) {
|
| + scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(data_size);
|
| + memcpy(buffer->data(), data, data_size);
|
| + dispatcher_->DeliverPacket(local_address_, address, buffer, data_size);
|
| + return data_size;
|
| +}
|
| +
|
| +int FakeUdpSocket::Close() {
|
| + state_ = STATE_CLOSED;
|
| + return 0;
|
| +}
|
| +
|
| +rtc::AsyncPacketSocket::State FakeUdpSocket::GetState() const {
|
| + return state_;
|
| +}
|
| +
|
| +int FakeUdpSocket::GetOption(rtc::Socket::Option option, int* value) {
|
| + NOTIMPLEMENTED();
|
| + return -1;
|
| +}
|
| +
|
| +int FakeUdpSocket::SetOption(rtc::Socket::Option option, int value) {
|
| + NOTIMPLEMENTED();
|
| + return -1;
|
| +}
|
| +
|
| +int FakeUdpSocket::GetError() const {
|
| + return 0;
|
| +}
|
| +
|
| +void FakeUdpSocket::SetError(int error) {
|
| + NOTREACHED();
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +FakePacketSocketFactory::PendingPacket::PendingPacket()
|
| + : data_size(0) {
|
| +}
|
| +
|
| +FakePacketSocketFactory::PendingPacket::PendingPacket(
|
| + const rtc::SocketAddress& from,
|
| + const rtc::SocketAddress& to,
|
| + const scoped_refptr<net::IOBuffer>& data,
|
| + int data_size)
|
| + : from(from), to(to), data(data), data_size(data_size) {
|
| +}
|
| +
|
| +FakePacketSocketFactory::PendingPacket::~PendingPacket() {
|
| +}
|
| +
|
| +FakePacketSocketFactory::FakePacketSocketFactory(
|
| + FakeNetworkDispatcher* dispatcher)
|
| + : task_runner_(base::ThreadTaskRunnerHandle::Get()),
|
| + dispatcher_(dispatcher),
|
| + address_(dispatcher_->AllocateAddress()),
|
| + out_of_order_rate_(0.0),
|
| + next_port_(kPortRangeStart),
|
| + weak_factory_(this) {
|
| + dispatcher_->AddNode(this);
|
| +}
|
| +
|
| +FakePacketSocketFactory::~FakePacketSocketFactory() {
|
| + CHECK(udp_sockets_.empty());
|
| + dispatcher_->RemoveNode(this);
|
| +}
|
| +
|
| +void FakePacketSocketFactory::OnSocketDestroyed(int port) {
|
| + DCHECK(task_runner_->BelongsToCurrentThread());
|
| + udp_sockets_.erase(port);
|
| +}
|
| +
|
| +void FakePacketSocketFactory::SetBandwidth(int bandwidth, int max_buffer) {
|
| + DCHECK(task_runner_->BelongsToCurrentThread());
|
| + if (bandwidth <= 0) {
|
| + leaky_bucket_.reset();
|
| + } else {
|
| + leaky_bucket_.reset(new LeakyBucket(max_buffer, bandwidth));
|
| + }
|
| +}
|
| +
|
| +void FakePacketSocketFactory::SetLatency(base::TimeDelta average,
|
| + base::TimeDelta stddev) {
|
| + DCHECK(task_runner_->BelongsToCurrentThread());
|
| + latency_average_ = average;
|
| + latency_stddev_ = stddev;
|
| +}
|
| +
|
| +rtc::AsyncPacketSocket* FakePacketSocketFactory::CreateUdpSocket(
|
| + const rtc::SocketAddress& local_address,
|
| + int min_port, int max_port) {
|
| + DCHECK(task_runner_->BelongsToCurrentThread());
|
| +
|
| + int port = -1;
|
| + if (min_port > 0 && max_port > 0) {
|
| + for (int i = min_port; i <= max_port; ++i) {
|
| + if (udp_sockets_.find(i) == udp_sockets_.end()) {
|
| + port = i;
|
| + break;
|
| + }
|
| + }
|
| + if (port < 0)
|
| + return NULL;
|
| + } else {
|
| + do {
|
| + port = next_port_;
|
| + next_port_ =
|
| + (next_port_ >= kPortRangeEnd) ? kPortRangeStart : (next_port_ + 1);
|
| + } while (udp_sockets_.find(port) != udp_sockets_.end());
|
| + }
|
| +
|
| + CHECK(local_address.ipaddr() == address_);
|
| +
|
| + FakeUdpSocket* result =
|
| + new FakeUdpSocket(this, dispatcher_,
|
| + rtc::SocketAddress(local_address.ipaddr(), port));
|
| +
|
| + udp_sockets_[port] =
|
| + base::Bind(&FakeUdpSocket::ReceivePacket, base::Unretained(result));
|
| +
|
| + return result;
|
| +}
|
| +
|
| +rtc::AsyncPacketSocket* FakePacketSocketFactory::CreateServerTcpSocket(
|
| + const rtc::SocketAddress& local_address,
|
| + int min_port, int max_port,
|
| + int opts) {
|
| + return NULL;
|
| +}
|
| +
|
| +rtc::AsyncPacketSocket* FakePacketSocketFactory::CreateClientTcpSocket(
|
| + const rtc::SocketAddress& local_address,
|
| + const rtc::SocketAddress& remote_address,
|
| + const rtc::ProxyInfo& proxy_info,
|
| + const std::string& user_agent,
|
| + int opts) {
|
| + return NULL;
|
| +}
|
| +
|
| +rtc::AsyncResolverInterface*
|
| +FakePacketSocketFactory::CreateAsyncResolver() {
|
| + return NULL;
|
| +}
|
| +
|
| +const scoped_refptr<base::SingleThreadTaskRunner>&
|
| +FakePacketSocketFactory::GetThread() const {
|
| + return task_runner_;
|
| +}
|
| +
|
| +const rtc::IPAddress& FakePacketSocketFactory::GetAddress() const {
|
| + return address_;
|
| +}
|
| +
|
| +void FakePacketSocketFactory::ReceivePacket(
|
| + const rtc::SocketAddress& from,
|
| + const rtc::SocketAddress& to,
|
| + const scoped_refptr<net::IOBuffer>& data,
|
| + int data_size) {
|
| + DCHECK(task_runner_->BelongsToCurrentThread());
|
| + DCHECK(to.ipaddr() == address_);
|
| +
|
| + base::TimeDelta delay;
|
| +
|
| + if (leaky_bucket_) {
|
| + delay = leaky_bucket_->AddPacket(data_size);
|
| + if (delay.is_max()) {
|
| + // Drop the packet.
|
| + return;
|
| + }
|
| + }
|
| +
|
| + if (latency_average_ > base::TimeDelta()) {
|
| + delay += base::TimeDelta::FromMillisecondsD(
|
| + GetNormalRandom(latency_average_.InMillisecondsF(),
|
| + latency_stddev_.InMillisecondsF()));
|
| + }
|
| + if (delay < base::TimeDelta())
|
| + delay = base::TimeDelta();
|
| +
|
| + // Put the packet to the |pending_packets_| and post a task for
|
| + // DoReceivePackets(). Note that the DoReceivePackets() task posted here may
|
| + // deliver a different packet, not the one added to the queue here. This
|
| + // would happen if another task gets posted with a shorted delay or when
|
| + // |out_of_order_rate_| is greater than 0. It's implemented this way to
|
| + // decouple latency variability from out-of-order delivery.
|
| + PendingPacket packet(from, to, data, data_size);
|
| + pending_packets_.push_back(packet);
|
| + task_runner_->PostDelayedTask(
|
| + FROM_HERE,
|
| + base::Bind(&FakePacketSocketFactory::DoReceivePacket,
|
| + weak_factory_.GetWeakPtr()),
|
| + delay);
|
| +}
|
| +
|
| +void FakePacketSocketFactory::DoReceivePacket() {
|
| + DCHECK(task_runner_->BelongsToCurrentThread());
|
| +
|
| + PendingPacket packet;
|
| + if (pending_packets_.size() > 1 && base::RandDouble() < out_of_order_rate_) {
|
| + std::list<PendingPacket>::iterator it = pending_packets_.begin();
|
| + ++it;
|
| + packet = *it;
|
| + pending_packets_.erase(it);
|
| + } else {
|
| + packet = pending_packets_.front();
|
| + pending_packets_.pop_front();
|
| + }
|
| +
|
| + UdpSocketsMap::iterator iter = udp_sockets_.find(packet.to.port());
|
| + if (iter == udp_sockets_.end()) {
|
| + // Invalid port number.
|
| + return;
|
| + }
|
| +
|
| + iter->second.Run(packet.from, packet.to, packet.data, packet.data_size);
|
| +}
|
| +
|
| +} // namespace remoting
|
|
|