mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 13:50:40 +01:00

As documented in webrtc:11908 this cleanup is fairly invasive and when a part of a frequently executed code path, can be quite costly in terms of performance overhead. This is currently the case with synchronous calls between threads (Thread) as well with our proxy api classes. With this CL, all code in WebRTC should now either be using MessageHandlerAutoCleanup or calling MessageHandler(false) explicitly. Next steps will be to update external code to either depend on the AutoCleanup variant, or call MessageHandler(false). Changing the proxy classes to use TaskQueue set of concepts instead of MessageHandler. This avoids the perf overhead related to the cleanup above as well as incompatibility with the thread policy checks in Thread that some current external users of the proxies would otherwise run into (if we were to use Thread::Send() for synchronous call). Following this we'll move the cleanup step into the AutoCleanup class and an RTC_DCHECK that all calls to the MessageHandler are setting the flag to false, before eventually removing the flag and make MessageHandler pure virtual. Bug: webrtc:11908 Change-Id: Idf4ff9bcc8438cb8c583777e282005e0bc511c8f Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/183442 Reviewed-by: Artem Titov <titovartem@webrtc.org> Commit-Queue: Tommi <tommi@webrtc.org> Cr-Commit-Position: refs/heads/master@{#32049}
1114 lines
39 KiB
C++
1114 lines
39 KiB
C++
/*
|
|
* Copyright 2007 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 "rtc_base/socket.h"
|
|
|
|
#include <errno.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include <memory>
|
|
|
|
#include "absl/memory/memory.h"
|
|
#include "rtc_base/arraysize.h"
|
|
#include "rtc_base/async_packet_socket.h"
|
|
#include "rtc_base/async_socket.h"
|
|
#include "rtc_base/async_udp_socket.h"
|
|
#include "rtc_base/buffer.h"
|
|
#include "rtc_base/gunit.h"
|
|
#include "rtc_base/location.h"
|
|
#include "rtc_base/logging.h"
|
|
#include "rtc_base/message_handler.h"
|
|
#include "rtc_base/net_helpers.h"
|
|
#include "rtc_base/socket_address.h"
|
|
#include "rtc_base/socket_server.h"
|
|
#include "rtc_base/socket_unittest.h"
|
|
#include "rtc_base/test_client.h"
|
|
#include "rtc_base/test_utils.h"
|
|
#include "rtc_base/third_party/sigslot/sigslot.h"
|
|
#include "rtc_base/thread.h"
|
|
#include "rtc_base/time_utils.h"
|
|
|
|
namespace rtc {
|
|
|
|
using webrtc::testing::SSE_CLOSE;
|
|
using webrtc::testing::SSE_ERROR;
|
|
using webrtc::testing::SSE_OPEN;
|
|
using webrtc::testing::SSE_READ;
|
|
using webrtc::testing::SSE_WRITE;
|
|
using webrtc::testing::StreamSink;
|
|
|
|
#define MAYBE_SKIP_IPV6 \
|
|
if (!HasIPv6Enabled()) { \
|
|
RTC_LOG(LS_INFO) << "No IPv6... skipping"; \
|
|
return; \
|
|
}
|
|
|
|
// Data size to be used in TcpInternal tests.
|
|
static const size_t kTcpInternalDataSize = 1024 * 1024; // bytes
|
|
|
|
void SocketTest::SetUp() {
|
|
ss_ = Thread::Current()->socketserver();
|
|
}
|
|
|
|
void SocketTest::TestConnectIPv4() {
|
|
ConnectInternal(kIPv4Loopback);
|
|
}
|
|
|
|
void SocketTest::TestConnectIPv6() {
|
|
MAYBE_SKIP_IPV6;
|
|
ConnectInternal(kIPv6Loopback);
|
|
}
|
|
|
|
void SocketTest::TestConnectWithDnsLookupIPv4() {
|
|
ConnectWithDnsLookupInternal(kIPv4Loopback, "localhost");
|
|
}
|
|
|
|
void SocketTest::TestConnectWithDnsLookupIPv6() {
|
|
// TODO: Enable this when DNS resolution supports IPv6.
|
|
RTC_LOG(LS_INFO) << "Skipping IPv6 DNS test";
|
|
// ConnectWithDnsLookupInternal(kIPv6Loopback, "localhost6");
|
|
}
|
|
|
|
void SocketTest::TestConnectFailIPv4() {
|
|
ConnectFailInternal(kIPv4Loopback);
|
|
}
|
|
|
|
void SocketTest::TestConnectFailIPv6() {
|
|
MAYBE_SKIP_IPV6;
|
|
ConnectFailInternal(kIPv6Loopback);
|
|
}
|
|
|
|
void SocketTest::TestConnectWithDnsLookupFailIPv4() {
|
|
ConnectWithDnsLookupFailInternal(kIPv4Loopback);
|
|
}
|
|
|
|
void SocketTest::TestConnectWithDnsLookupFailIPv6() {
|
|
MAYBE_SKIP_IPV6;
|
|
ConnectWithDnsLookupFailInternal(kIPv6Loopback);
|
|
}
|
|
|
|
void SocketTest::TestConnectWithClosedSocketIPv4() {
|
|
ConnectWithClosedSocketInternal(kIPv4Loopback);
|
|
}
|
|
|
|
void SocketTest::TestConnectWithClosedSocketIPv6() {
|
|
MAYBE_SKIP_IPV6;
|
|
ConnectWithClosedSocketInternal(kIPv6Loopback);
|
|
}
|
|
|
|
void SocketTest::TestConnectWhileNotClosedIPv4() {
|
|
ConnectWhileNotClosedInternal(kIPv4Loopback);
|
|
}
|
|
|
|
void SocketTest::TestConnectWhileNotClosedIPv6() {
|
|
MAYBE_SKIP_IPV6;
|
|
ConnectWhileNotClosedInternal(kIPv6Loopback);
|
|
}
|
|
|
|
void SocketTest::TestServerCloseDuringConnectIPv4() {
|
|
ServerCloseDuringConnectInternal(kIPv4Loopback);
|
|
}
|
|
|
|
void SocketTest::TestServerCloseDuringConnectIPv6() {
|
|
MAYBE_SKIP_IPV6;
|
|
ServerCloseDuringConnectInternal(kIPv6Loopback);
|
|
}
|
|
|
|
void SocketTest::TestClientCloseDuringConnectIPv4() {
|
|
ClientCloseDuringConnectInternal(kIPv4Loopback);
|
|
}
|
|
|
|
void SocketTest::TestClientCloseDuringConnectIPv6() {
|
|
MAYBE_SKIP_IPV6;
|
|
ClientCloseDuringConnectInternal(kIPv6Loopback);
|
|
}
|
|
|
|
void SocketTest::TestServerCloseIPv4() {
|
|
ServerCloseInternal(kIPv4Loopback);
|
|
}
|
|
|
|
void SocketTest::TestServerCloseIPv6() {
|
|
MAYBE_SKIP_IPV6;
|
|
ServerCloseInternal(kIPv6Loopback);
|
|
}
|
|
|
|
void SocketTest::TestCloseInClosedCallbackIPv4() {
|
|
CloseInClosedCallbackInternal(kIPv4Loopback);
|
|
}
|
|
|
|
void SocketTest::TestCloseInClosedCallbackIPv6() {
|
|
MAYBE_SKIP_IPV6;
|
|
CloseInClosedCallbackInternal(kIPv6Loopback);
|
|
}
|
|
|
|
void SocketTest::TestDeleteInReadCallbackIPv4() {
|
|
DeleteInReadCallbackInternal(kIPv4Loopback);
|
|
}
|
|
|
|
void SocketTest::TestDeleteInReadCallbackIPv6() {
|
|
MAYBE_SKIP_IPV6;
|
|
DeleteInReadCallbackInternal(kIPv6Loopback);
|
|
}
|
|
|
|
void SocketTest::TestSocketServerWaitIPv4() {
|
|
SocketServerWaitInternal(kIPv4Loopback);
|
|
}
|
|
|
|
void SocketTest::TestSocketServerWaitIPv6() {
|
|
MAYBE_SKIP_IPV6;
|
|
SocketServerWaitInternal(kIPv6Loopback);
|
|
}
|
|
|
|
void SocketTest::TestTcpIPv4() {
|
|
TcpInternal(kIPv4Loopback, kTcpInternalDataSize, -1);
|
|
}
|
|
|
|
void SocketTest::TestTcpIPv6() {
|
|
MAYBE_SKIP_IPV6;
|
|
TcpInternal(kIPv6Loopback, kTcpInternalDataSize, -1);
|
|
}
|
|
|
|
void SocketTest::TestSingleFlowControlCallbackIPv4() {
|
|
SingleFlowControlCallbackInternal(kIPv4Loopback);
|
|
}
|
|
|
|
void SocketTest::TestSingleFlowControlCallbackIPv6() {
|
|
MAYBE_SKIP_IPV6;
|
|
SingleFlowControlCallbackInternal(kIPv6Loopback);
|
|
}
|
|
|
|
void SocketTest::TestUdpIPv4() {
|
|
UdpInternal(kIPv4Loopback);
|
|
}
|
|
|
|
void SocketTest::TestUdpIPv6() {
|
|
MAYBE_SKIP_IPV6;
|
|
UdpInternal(kIPv6Loopback);
|
|
}
|
|
|
|
void SocketTest::TestUdpReadyToSendIPv4() {
|
|
#if !defined(WEBRTC_MAC)
|
|
// TODO(ronghuawu): Enable this test on mac/ios.
|
|
UdpReadyToSend(kIPv4Loopback);
|
|
#endif
|
|
}
|
|
|
|
void SocketTest::TestUdpReadyToSendIPv6() {
|
|
#if defined(WEBRTC_WIN)
|
|
// TODO(ronghuawu): Enable this test (currently flakey) on mac and linux.
|
|
MAYBE_SKIP_IPV6;
|
|
UdpReadyToSend(kIPv6Loopback);
|
|
#endif
|
|
}
|
|
|
|
void SocketTest::TestGetSetOptionsIPv4() {
|
|
GetSetOptionsInternal(kIPv4Loopback);
|
|
}
|
|
|
|
void SocketTest::TestGetSetOptionsIPv6() {
|
|
MAYBE_SKIP_IPV6;
|
|
GetSetOptionsInternal(kIPv6Loopback);
|
|
}
|
|
|
|
void SocketTest::TestSocketRecvTimestampIPv4() {
|
|
SocketRecvTimestamp(kIPv4Loopback);
|
|
}
|
|
|
|
void SocketTest::TestSocketRecvTimestampIPv6() {
|
|
MAYBE_SKIP_IPV6;
|
|
SocketRecvTimestamp(kIPv6Loopback);
|
|
}
|
|
|
|
// For unbound sockets, GetLocalAddress / GetRemoteAddress return AF_UNSPEC
|
|
// values on Windows, but an empty address of the same family on Linux/MacOS X.
|
|
bool IsUnspecOrEmptyIP(const IPAddress& address) {
|
|
#if !defined(WEBRTC_WIN)
|
|
return IPIsAny(address);
|
|
#else
|
|
return address.family() == AF_UNSPEC;
|
|
#endif
|
|
}
|
|
|
|
void SocketTest::ConnectInternal(const IPAddress& loopback) {
|
|
StreamSink sink;
|
|
SocketAddress accept_addr;
|
|
|
|
// Create client.
|
|
std::unique_ptr<AsyncSocket> client(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
sink.Monitor(client.get());
|
|
EXPECT_EQ(AsyncSocket::CS_CLOSED, client->GetState());
|
|
EXPECT_TRUE(IsUnspecOrEmptyIP(client->GetLocalAddress().ipaddr()));
|
|
|
|
// Create server and listen.
|
|
std::unique_ptr<AsyncSocket> server(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
sink.Monitor(server.get());
|
|
EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
|
|
EXPECT_EQ(0, server->Listen(5));
|
|
EXPECT_EQ(AsyncSocket::CS_CONNECTING, server->GetState());
|
|
|
|
// Ensure no pending server connections, since we haven't done anything yet.
|
|
EXPECT_FALSE(sink.Check(server.get(), SSE_READ));
|
|
EXPECT_TRUE(nullptr == server->Accept(&accept_addr));
|
|
EXPECT_TRUE(accept_addr.IsNil());
|
|
|
|
// Attempt connect to listening socket.
|
|
EXPECT_EQ(0, client->Connect(server->GetLocalAddress()));
|
|
EXPECT_FALSE(client->GetLocalAddress().IsNil());
|
|
EXPECT_NE(server->GetLocalAddress(), client->GetLocalAddress());
|
|
|
|
// Client is connecting, outcome not yet determined.
|
|
EXPECT_EQ(AsyncSocket::CS_CONNECTING, client->GetState());
|
|
EXPECT_FALSE(sink.Check(client.get(), SSE_OPEN));
|
|
EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
|
|
|
|
// Server has pending connection, accept it.
|
|
EXPECT_TRUE_WAIT((sink.Check(server.get(), SSE_READ)), kTimeout);
|
|
std::unique_ptr<AsyncSocket> accepted(server->Accept(&accept_addr));
|
|
ASSERT_TRUE(accepted);
|
|
EXPECT_FALSE(accept_addr.IsNil());
|
|
EXPECT_EQ(accepted->GetRemoteAddress(), accept_addr);
|
|
|
|
// Connected from server perspective, check the addresses are correct.
|
|
EXPECT_EQ(AsyncSocket::CS_CONNECTED, accepted->GetState());
|
|
EXPECT_EQ(server->GetLocalAddress(), accepted->GetLocalAddress());
|
|
EXPECT_EQ(client->GetLocalAddress(), accepted->GetRemoteAddress());
|
|
|
|
// Connected from client perspective, check the addresses are correct.
|
|
EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout);
|
|
EXPECT_TRUE(sink.Check(client.get(), SSE_OPEN));
|
|
EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
|
|
EXPECT_EQ(client->GetRemoteAddress(), server->GetLocalAddress());
|
|
EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress());
|
|
}
|
|
|
|
void SocketTest::ConnectWithDnsLookupInternal(const IPAddress& loopback,
|
|
const std::string& host) {
|
|
StreamSink sink;
|
|
SocketAddress accept_addr;
|
|
|
|
// Create client.
|
|
std::unique_ptr<AsyncSocket> client(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
sink.Monitor(client.get());
|
|
|
|
// Create server and listen.
|
|
std::unique_ptr<AsyncSocket> server(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
sink.Monitor(server.get());
|
|
EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
|
|
EXPECT_EQ(0, server->Listen(5));
|
|
|
|
// Attempt connect to listening socket.
|
|
SocketAddress dns_addr(server->GetLocalAddress());
|
|
dns_addr.SetIP(host);
|
|
EXPECT_EQ(0, client->Connect(dns_addr));
|
|
// TODO: Bind when doing DNS lookup.
|
|
// EXPECT_NE(kEmptyAddr, client->GetLocalAddress()); // Implicit Bind
|
|
|
|
// Client is connecting, outcome not yet determined.
|
|
EXPECT_EQ(AsyncSocket::CS_CONNECTING, client->GetState());
|
|
EXPECT_FALSE(sink.Check(client.get(), SSE_OPEN));
|
|
EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
|
|
|
|
// Server has pending connection, accept it.
|
|
EXPECT_TRUE_WAIT((sink.Check(server.get(), SSE_READ)), kTimeout);
|
|
std::unique_ptr<AsyncSocket> accepted(server->Accept(&accept_addr));
|
|
ASSERT_TRUE(accepted);
|
|
EXPECT_FALSE(accept_addr.IsNil());
|
|
EXPECT_EQ(accepted->GetRemoteAddress(), accept_addr);
|
|
|
|
// Connected from server perspective, check the addresses are correct.
|
|
EXPECT_EQ(AsyncSocket::CS_CONNECTED, accepted->GetState());
|
|
EXPECT_EQ(server->GetLocalAddress(), accepted->GetLocalAddress());
|
|
EXPECT_EQ(client->GetLocalAddress(), accepted->GetRemoteAddress());
|
|
|
|
// Connected from client perspective, check the addresses are correct.
|
|
EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout);
|
|
EXPECT_TRUE(sink.Check(client.get(), SSE_OPEN));
|
|
EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
|
|
EXPECT_EQ(client->GetRemoteAddress(), server->GetLocalAddress());
|
|
EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress());
|
|
}
|
|
|
|
void SocketTest::ConnectFailInternal(const IPAddress& loopback) {
|
|
StreamSink sink;
|
|
SocketAddress accept_addr;
|
|
|
|
// Create client.
|
|
std::unique_ptr<AsyncSocket> client(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
sink.Monitor(client.get());
|
|
|
|
// Create server, but don't listen yet.
|
|
std::unique_ptr<AsyncSocket> server(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
sink.Monitor(server.get());
|
|
EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
|
|
|
|
// Attempt connect to a non-existent socket.
|
|
// We don't connect to the server socket created above, since on
|
|
// MacOS it takes about 75 seconds to get back an error!
|
|
SocketAddress bogus_addr(loopback, 65535);
|
|
EXPECT_EQ(0, client->Connect(bogus_addr));
|
|
|
|
// Wait for connection to fail (ECONNREFUSED).
|
|
EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, client->GetState(), kTimeout);
|
|
EXPECT_FALSE(sink.Check(client.get(), SSE_OPEN));
|
|
EXPECT_TRUE(sink.Check(client.get(), SSE_ERROR));
|
|
EXPECT_TRUE(client->GetRemoteAddress().IsNil());
|
|
|
|
// Should be no pending server connections.
|
|
EXPECT_FALSE(sink.Check(server.get(), SSE_READ));
|
|
EXPECT_TRUE(nullptr == server->Accept(&accept_addr));
|
|
EXPECT_EQ(IPAddress(), accept_addr.ipaddr());
|
|
}
|
|
|
|
void SocketTest::ConnectWithDnsLookupFailInternal(const IPAddress& loopback) {
|
|
StreamSink sink;
|
|
SocketAddress accept_addr;
|
|
|
|
// Create client.
|
|
std::unique_ptr<AsyncSocket> client(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
sink.Monitor(client.get());
|
|
|
|
// Create server, but don't listen yet.
|
|
std::unique_ptr<AsyncSocket> server(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
sink.Monitor(server.get());
|
|
EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
|
|
|
|
// Attempt connect to a non-existent host.
|
|
// We don't connect to the server socket created above, since on
|
|
// MacOS it takes about 75 seconds to get back an error!
|
|
SocketAddress bogus_dns_addr("not-a-real-hostname", 65535);
|
|
EXPECT_EQ(0, client->Connect(bogus_dns_addr));
|
|
|
|
// Wait for connection to fail (EHOSTNOTFOUND).
|
|
bool dns_lookup_finished = false;
|
|
WAIT_(client->GetState() == AsyncSocket::CS_CLOSED, kTimeout,
|
|
dns_lookup_finished);
|
|
if (!dns_lookup_finished) {
|
|
RTC_LOG(LS_WARNING) << "Skipping test; DNS resolution took longer than 5 "
|
|
"seconds.";
|
|
return;
|
|
}
|
|
|
|
EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, client->GetState(), kTimeout);
|
|
EXPECT_FALSE(sink.Check(client.get(), SSE_OPEN));
|
|
EXPECT_TRUE(sink.Check(client.get(), SSE_ERROR));
|
|
EXPECT_TRUE(client->GetRemoteAddress().IsNil());
|
|
// Should be no pending server connections.
|
|
EXPECT_FALSE(sink.Check(server.get(), SSE_READ));
|
|
EXPECT_TRUE(nullptr == server->Accept(&accept_addr));
|
|
EXPECT_TRUE(accept_addr.IsNil());
|
|
}
|
|
|
|
void SocketTest::ConnectWithClosedSocketInternal(const IPAddress& loopback) {
|
|
// Create server and listen.
|
|
std::unique_ptr<AsyncSocket> server(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
|
|
EXPECT_EQ(0, server->Listen(5));
|
|
|
|
// Create a client and put in to CS_CLOSED state.
|
|
std::unique_ptr<AsyncSocket> client(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
EXPECT_EQ(0, client->Close());
|
|
EXPECT_EQ(AsyncSocket::CS_CLOSED, client->GetState());
|
|
|
|
// Connect() should reinitialize the socket, and put it in to CS_CONNECTING.
|
|
EXPECT_EQ(0, client->Connect(SocketAddress(server->GetLocalAddress())));
|
|
EXPECT_EQ(AsyncSocket::CS_CONNECTING, client->GetState());
|
|
}
|
|
|
|
void SocketTest::ConnectWhileNotClosedInternal(const IPAddress& loopback) {
|
|
// Create server and listen.
|
|
StreamSink sink;
|
|
std::unique_ptr<AsyncSocket> server(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
sink.Monitor(server.get());
|
|
EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
|
|
EXPECT_EQ(0, server->Listen(5));
|
|
// Create client, connect.
|
|
std::unique_ptr<AsyncSocket> client(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
EXPECT_EQ(0, client->Connect(SocketAddress(server->GetLocalAddress())));
|
|
EXPECT_EQ(AsyncSocket::CS_CONNECTING, client->GetState());
|
|
// Try to connect again. Should fail, but not interfere with original attempt.
|
|
EXPECT_EQ(SOCKET_ERROR,
|
|
client->Connect(SocketAddress(server->GetLocalAddress())));
|
|
|
|
// Accept the original connection.
|
|
SocketAddress accept_addr;
|
|
EXPECT_TRUE_WAIT((sink.Check(server.get(), SSE_READ)), kTimeout);
|
|
std::unique_ptr<AsyncSocket> accepted(server->Accept(&accept_addr));
|
|
ASSERT_TRUE(accepted);
|
|
EXPECT_FALSE(accept_addr.IsNil());
|
|
|
|
// Check the states and addresses.
|
|
EXPECT_EQ(AsyncSocket::CS_CONNECTED, accepted->GetState());
|
|
EXPECT_EQ(server->GetLocalAddress(), accepted->GetLocalAddress());
|
|
EXPECT_EQ(client->GetLocalAddress(), accepted->GetRemoteAddress());
|
|
EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout);
|
|
EXPECT_EQ(client->GetRemoteAddress(), server->GetLocalAddress());
|
|
EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress());
|
|
|
|
// Try to connect again, to an unresolved hostname.
|
|
// Shouldn't break anything.
|
|
EXPECT_EQ(SOCKET_ERROR, client->Connect(SocketAddress(
|
|
"localhost", server->GetLocalAddress().port())));
|
|
EXPECT_EQ(AsyncSocket::CS_CONNECTED, accepted->GetState());
|
|
EXPECT_EQ(AsyncSocket::CS_CONNECTED, client->GetState());
|
|
EXPECT_EQ(client->GetRemoteAddress(), server->GetLocalAddress());
|
|
EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress());
|
|
}
|
|
|
|
void SocketTest::ServerCloseDuringConnectInternal(const IPAddress& loopback) {
|
|
StreamSink sink;
|
|
|
|
// Create client.
|
|
std::unique_ptr<AsyncSocket> client(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
sink.Monitor(client.get());
|
|
|
|
// Create server and listen.
|
|
std::unique_ptr<AsyncSocket> server(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
sink.Monitor(server.get());
|
|
EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
|
|
EXPECT_EQ(0, server->Listen(5));
|
|
|
|
// Attempt connect to listening socket.
|
|
EXPECT_EQ(0, client->Connect(server->GetLocalAddress()));
|
|
|
|
// Close down the server while the socket is in the accept queue.
|
|
EXPECT_TRUE_WAIT(sink.Check(server.get(), SSE_READ), kTimeout);
|
|
server->Close();
|
|
|
|
// This should fail the connection for the client. Clean up.
|
|
EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, client->GetState(), kTimeout);
|
|
EXPECT_TRUE(sink.Check(client.get(), SSE_ERROR));
|
|
client->Close();
|
|
}
|
|
|
|
void SocketTest::ClientCloseDuringConnectInternal(const IPAddress& loopback) {
|
|
StreamSink sink;
|
|
SocketAddress accept_addr;
|
|
|
|
// Create client.
|
|
std::unique_ptr<AsyncSocket> client(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
sink.Monitor(client.get());
|
|
|
|
// Create server and listen.
|
|
std::unique_ptr<AsyncSocket> server(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
sink.Monitor(server.get());
|
|
EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
|
|
EXPECT_EQ(0, server->Listen(5));
|
|
|
|
// Attempt connect to listening socket.
|
|
EXPECT_EQ(0, client->Connect(server->GetLocalAddress()));
|
|
|
|
// Close down the client while the socket is in the accept queue.
|
|
EXPECT_TRUE_WAIT(sink.Check(server.get(), SSE_READ), kTimeout);
|
|
client->Close();
|
|
|
|
// The connection should still be able to be accepted.
|
|
std::unique_ptr<AsyncSocket> accepted(server->Accept(&accept_addr));
|
|
ASSERT_TRUE(accepted);
|
|
sink.Monitor(accepted.get());
|
|
EXPECT_EQ(AsyncSocket::CS_CONNECTED, accepted->GetState());
|
|
|
|
// The accepted socket should then close (possibly with err, timing-related)
|
|
EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, accepted->GetState(), kTimeout);
|
|
EXPECT_TRUE(sink.Check(accepted.get(), SSE_CLOSE) ||
|
|
sink.Check(accepted.get(), SSE_ERROR));
|
|
|
|
// The client should not get a close event.
|
|
EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
|
|
}
|
|
|
|
void SocketTest::ServerCloseInternal(const IPAddress& loopback) {
|
|
StreamSink sink;
|
|
SocketAddress accept_addr;
|
|
|
|
// Create client.
|
|
std::unique_ptr<AsyncSocket> client(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
sink.Monitor(client.get());
|
|
|
|
// Create server and listen.
|
|
std::unique_ptr<AsyncSocket> server(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
sink.Monitor(server.get());
|
|
EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
|
|
EXPECT_EQ(0, server->Listen(5));
|
|
|
|
// Attempt connection.
|
|
EXPECT_EQ(0, client->Connect(server->GetLocalAddress()));
|
|
|
|
// Accept connection.
|
|
EXPECT_TRUE_WAIT((sink.Check(server.get(), SSE_READ)), kTimeout);
|
|
std::unique_ptr<AsyncSocket> accepted(server->Accept(&accept_addr));
|
|
ASSERT_TRUE(accepted);
|
|
sink.Monitor(accepted.get());
|
|
|
|
// Both sides are now connected.
|
|
EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout);
|
|
EXPECT_TRUE(sink.Check(client.get(), SSE_OPEN));
|
|
EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress());
|
|
EXPECT_EQ(accepted->GetRemoteAddress(), client->GetLocalAddress());
|
|
|
|
// Send data to the client, and then close the connection.
|
|
EXPECT_EQ(1, accepted->Send("a", 1));
|
|
accepted->Close();
|
|
EXPECT_EQ(AsyncSocket::CS_CLOSED, accepted->GetState());
|
|
|
|
// Expect that the client is notified, and has not yet closed.
|
|
EXPECT_TRUE_WAIT(sink.Check(client.get(), SSE_READ), kTimeout);
|
|
EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
|
|
EXPECT_EQ(AsyncSocket::CS_CONNECTED, client->GetState());
|
|
|
|
// Ensure the data can be read.
|
|
char buffer[10];
|
|
EXPECT_EQ(1, client->Recv(buffer, sizeof(buffer), nullptr));
|
|
EXPECT_EQ('a', buffer[0]);
|
|
|
|
// Now we should close, but the remote address will remain.
|
|
EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, client->GetState(), kTimeout);
|
|
EXPECT_TRUE(sink.Check(client.get(), SSE_CLOSE));
|
|
EXPECT_FALSE(client->GetRemoteAddress().IsAnyIP());
|
|
|
|
// The closer should not get a close signal.
|
|
EXPECT_FALSE(sink.Check(accepted.get(), SSE_CLOSE));
|
|
EXPECT_TRUE(accepted->GetRemoteAddress().IsNil());
|
|
|
|
// And the closee should only get a single signal.
|
|
Thread::Current()->ProcessMessages(0);
|
|
EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
|
|
|
|
// Close down the client and ensure all is good.
|
|
client->Close();
|
|
EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
|
|
EXPECT_TRUE(client->GetRemoteAddress().IsNil());
|
|
}
|
|
|
|
class SocketCloser : public sigslot::has_slots<> {
|
|
public:
|
|
void OnClose(AsyncSocket* socket, int error) {
|
|
socket->Close(); // Deleting here would blow up the vector of handlers
|
|
// for the socket's signal.
|
|
}
|
|
};
|
|
|
|
void SocketTest::CloseInClosedCallbackInternal(const IPAddress& loopback) {
|
|
StreamSink sink;
|
|
SocketCloser closer;
|
|
SocketAddress accept_addr;
|
|
|
|
// Create client.
|
|
std::unique_ptr<AsyncSocket> client(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
sink.Monitor(client.get());
|
|
client->SignalCloseEvent.connect(&closer, &SocketCloser::OnClose);
|
|
|
|
// Create server and listen.
|
|
std::unique_ptr<AsyncSocket> server(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
sink.Monitor(server.get());
|
|
EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
|
|
EXPECT_EQ(0, server->Listen(5));
|
|
|
|
// Attempt connection.
|
|
EXPECT_EQ(0, client->Connect(server->GetLocalAddress()));
|
|
|
|
// Accept connection.
|
|
EXPECT_TRUE_WAIT((sink.Check(server.get(), SSE_READ)), kTimeout);
|
|
std::unique_ptr<AsyncSocket> accepted(server->Accept(&accept_addr));
|
|
ASSERT_TRUE(accepted);
|
|
sink.Monitor(accepted.get());
|
|
|
|
// Both sides are now connected.
|
|
EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout);
|
|
EXPECT_TRUE(sink.Check(client.get(), SSE_OPEN));
|
|
EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress());
|
|
EXPECT_EQ(accepted->GetRemoteAddress(), client->GetLocalAddress());
|
|
|
|
// Send data to the client, and then close the connection.
|
|
accepted->Close();
|
|
EXPECT_EQ(AsyncSocket::CS_CLOSED, accepted->GetState());
|
|
|
|
// Expect that the client is notified, and has not yet closed.
|
|
EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
|
|
EXPECT_EQ(AsyncSocket::CS_CONNECTED, client->GetState());
|
|
|
|
// Now we should be closed and invalidated
|
|
EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, client->GetState(), kTimeout);
|
|
EXPECT_TRUE(sink.Check(client.get(), SSE_CLOSE));
|
|
EXPECT_TRUE(Socket::CS_CLOSED == client->GetState());
|
|
}
|
|
|
|
// Helper class specifically for the test below.
|
|
class SocketDeleter : public sigslot::has_slots<> {
|
|
public:
|
|
explicit SocketDeleter(std::unique_ptr<AsyncSocket> socket)
|
|
: socket_(std::move(socket)) {}
|
|
|
|
void Delete(AsyncSocket* other) { socket_.reset(); }
|
|
|
|
bool deleted() const { return socket_ == nullptr; }
|
|
|
|
private:
|
|
std::unique_ptr<AsyncSocket> socket_;
|
|
};
|
|
|
|
// Tested deleting a socket within another socket's read callback. A previous
|
|
// iteration of the select loop failed in this situation, if both sockets
|
|
// became readable at the same time.
|
|
void SocketTest::DeleteInReadCallbackInternal(const IPAddress& loopback) {
|
|
std::unique_ptr<AsyncSocket> socket1(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_DGRAM));
|
|
std::unique_ptr<AsyncSocket> socket2(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_DGRAM));
|
|
EXPECT_EQ(0, socket1->Bind(SocketAddress(loopback, 0)));
|
|
EXPECT_EQ(0, socket2->Bind(SocketAddress(loopback, 0)));
|
|
EXPECT_EQ(3, socket1->SendTo("foo", 3, socket1->GetLocalAddress()));
|
|
EXPECT_EQ(3, socket2->SendTo("bar", 3, socket1->GetLocalAddress()));
|
|
// Sleep a while to ensure sends are both completed at the same time.
|
|
Thread::SleepMs(1000);
|
|
|
|
// Configure the helper class to delete socket 2 when socket 1 has a read
|
|
// event.
|
|
SocketDeleter deleter(std::move(socket2));
|
|
socket1->SignalReadEvent.connect(&deleter, &SocketDeleter::Delete);
|
|
EXPECT_TRUE_WAIT(deleter.deleted(), kTimeout);
|
|
}
|
|
|
|
class Sleeper : public MessageHandlerAutoCleanup {
|
|
public:
|
|
void OnMessage(Message* msg) override { Thread::Current()->SleepMs(500); }
|
|
};
|
|
|
|
void SocketTest::SocketServerWaitInternal(const IPAddress& loopback) {
|
|
StreamSink sink;
|
|
SocketAddress accept_addr;
|
|
|
|
// Create & connect server and client sockets.
|
|
std::unique_ptr<AsyncSocket> client(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
std::unique_ptr<AsyncSocket> server(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
sink.Monitor(client.get());
|
|
sink.Monitor(server.get());
|
|
EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
|
|
EXPECT_EQ(0, server->Listen(5));
|
|
|
|
EXPECT_EQ(0, client->Connect(server->GetLocalAddress()));
|
|
EXPECT_TRUE_WAIT((sink.Check(server.get(), SSE_READ)), kTimeout);
|
|
|
|
std::unique_ptr<AsyncSocket> accepted(server->Accept(&accept_addr));
|
|
ASSERT_TRUE(accepted);
|
|
sink.Monitor(accepted.get());
|
|
EXPECT_EQ(AsyncSocket::CS_CONNECTED, accepted->GetState());
|
|
EXPECT_EQ(server->GetLocalAddress(), accepted->GetLocalAddress());
|
|
EXPECT_EQ(client->GetLocalAddress(), accepted->GetRemoteAddress());
|
|
|
|
EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout);
|
|
EXPECT_TRUE(sink.Check(client.get(), SSE_OPEN));
|
|
EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
|
|
EXPECT_EQ(client->GetRemoteAddress(), server->GetLocalAddress());
|
|
EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress());
|
|
|
|
// Do an i/o operation, triggering an eventual callback.
|
|
EXPECT_FALSE(sink.Check(accepted.get(), SSE_READ));
|
|
char buf[1024] = {0};
|
|
|
|
EXPECT_EQ(1024, client->Send(buf, 1024));
|
|
EXPECT_FALSE(sink.Check(accepted.get(), SSE_READ));
|
|
|
|
// Shouldn't signal when blocked in a thread Send, where process_io is false.
|
|
std::unique_ptr<Thread> thread(Thread::CreateWithSocketServer());
|
|
thread->Start();
|
|
Sleeper sleeper;
|
|
TypedMessageData<AsyncSocket*> data(client.get());
|
|
thread->Send(RTC_FROM_HERE, &sleeper, 0, &data);
|
|
EXPECT_FALSE(sink.Check(accepted.get(), SSE_READ));
|
|
|
|
// But should signal when process_io is true.
|
|
EXPECT_TRUE_WAIT((sink.Check(accepted.get(), SSE_READ)), kTimeout);
|
|
EXPECT_LT(0, accepted->Recv(buf, 1024, nullptr));
|
|
}
|
|
|
|
void SocketTest::TcpInternal(const IPAddress& loopback,
|
|
size_t data_size,
|
|
ptrdiff_t max_send_size) {
|
|
StreamSink sink;
|
|
SocketAddress accept_addr;
|
|
|
|
// Create receiving client.
|
|
std::unique_ptr<AsyncSocket> receiver(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
sink.Monitor(receiver.get());
|
|
|
|
// Create server and listen.
|
|
std::unique_ptr<AsyncSocket> server(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
sink.Monitor(server.get());
|
|
EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
|
|
EXPECT_EQ(0, server->Listen(5));
|
|
|
|
// Attempt connection.
|
|
EXPECT_EQ(0, receiver->Connect(server->GetLocalAddress()));
|
|
|
|
// Accept connection which will be used for sending.
|
|
EXPECT_TRUE_WAIT((sink.Check(server.get(), SSE_READ)), kTimeout);
|
|
std::unique_ptr<AsyncSocket> sender(server->Accept(&accept_addr));
|
|
ASSERT_TRUE(sender);
|
|
sink.Monitor(sender.get());
|
|
|
|
// Both sides are now connected.
|
|
EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, receiver->GetState(), kTimeout);
|
|
EXPECT_TRUE(sink.Check(receiver.get(), SSE_OPEN));
|
|
EXPECT_EQ(receiver->GetRemoteAddress(), sender->GetLocalAddress());
|
|
EXPECT_EQ(sender->GetRemoteAddress(), receiver->GetLocalAddress());
|
|
|
|
// Create test data.
|
|
rtc::Buffer send_buffer(0, data_size);
|
|
rtc::Buffer recv_buffer(0, data_size);
|
|
for (size_t i = 0; i < data_size; ++i) {
|
|
char ch = static_cast<char>(i % 256);
|
|
send_buffer.AppendData(&ch, sizeof(ch));
|
|
}
|
|
rtc::Buffer recved_data(0, data_size);
|
|
|
|
// Send and receive a bunch of data.
|
|
size_t sent_size = 0;
|
|
bool writable = true;
|
|
bool send_called = false;
|
|
bool readable = false;
|
|
bool recv_called = false;
|
|
while (recv_buffer.size() < send_buffer.size()) {
|
|
// Send as much as we can while we're cleared to send.
|
|
while (writable && sent_size < send_buffer.size()) {
|
|
int unsent_size = static_cast<int>(send_buffer.size() - sent_size);
|
|
int sent = sender->Send(send_buffer.data() + sent_size, unsent_size);
|
|
if (!send_called) {
|
|
// The first Send() after connecting or getting writability should
|
|
// succeed and send some data.
|
|
EXPECT_GT(sent, 0);
|
|
send_called = true;
|
|
}
|
|
if (sent >= 0) {
|
|
EXPECT_LE(sent, unsent_size);
|
|
sent_size += sent;
|
|
if (max_send_size >= 0) {
|
|
EXPECT_LE(static_cast<ptrdiff_t>(sent), max_send_size);
|
|
if (sent < unsent_size) {
|
|
// If max_send_size is limiting the amount to send per call such
|
|
// that the sent amount is less than the unsent amount, we simulate
|
|
// that the socket is no longer writable.
|
|
writable = false;
|
|
}
|
|
}
|
|
} else {
|
|
ASSERT_TRUE(sender->IsBlocking());
|
|
writable = false;
|
|
}
|
|
}
|
|
|
|
// Read all the sent data.
|
|
while (recv_buffer.size() < sent_size) {
|
|
if (!readable) {
|
|
// Wait until data is available.
|
|
EXPECT_TRUE_WAIT(sink.Check(receiver.get(), SSE_READ), kTimeout);
|
|
readable = true;
|
|
recv_called = false;
|
|
}
|
|
|
|
// Receive as much as we can get in a single recv call.
|
|
int recved_size = receiver->Recv(recved_data.data(), data_size, nullptr);
|
|
|
|
if (!recv_called) {
|
|
// The first Recv() after getting readability should succeed and receive
|
|
// some data.
|
|
// TODO: The following line is disabled due to flakey pulse
|
|
// builds. Re-enable if/when possible.
|
|
// EXPECT_GT(recved_size, 0);
|
|
recv_called = true;
|
|
}
|
|
if (recved_size >= 0) {
|
|
EXPECT_LE(static_cast<size_t>(recved_size),
|
|
sent_size - recv_buffer.size());
|
|
recv_buffer.AppendData(recved_data.data(), recved_size);
|
|
} else {
|
|
ASSERT_TRUE(receiver->IsBlocking());
|
|
readable = false;
|
|
}
|
|
}
|
|
|
|
// Once all that we've sent has been received, expect to be able to send
|
|
// again.
|
|
if (!writable) {
|
|
ASSERT_TRUE_WAIT(sink.Check(sender.get(), SSE_WRITE), kTimeout);
|
|
writable = true;
|
|
send_called = false;
|
|
}
|
|
}
|
|
|
|
// The received data matches the sent data.
|
|
EXPECT_EQ(data_size, sent_size);
|
|
EXPECT_EQ(data_size, recv_buffer.size());
|
|
EXPECT_EQ(recv_buffer, send_buffer);
|
|
|
|
// Close down.
|
|
sender->Close();
|
|
EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, receiver->GetState(), kTimeout);
|
|
EXPECT_TRUE(sink.Check(receiver.get(), SSE_CLOSE));
|
|
receiver->Close();
|
|
}
|
|
|
|
void SocketTest::SingleFlowControlCallbackInternal(const IPAddress& loopback) {
|
|
StreamSink sink;
|
|
SocketAddress accept_addr;
|
|
|
|
// Create client.
|
|
std::unique_ptr<AsyncSocket> client(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
sink.Monitor(client.get());
|
|
|
|
// Create server and listen.
|
|
std::unique_ptr<AsyncSocket> server(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
|
|
sink.Monitor(server.get());
|
|
EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
|
|
EXPECT_EQ(0, server->Listen(5));
|
|
|
|
// Attempt connection.
|
|
EXPECT_EQ(0, client->Connect(server->GetLocalAddress()));
|
|
|
|
// Accept connection.
|
|
EXPECT_TRUE_WAIT((sink.Check(server.get(), SSE_READ)), kTimeout);
|
|
std::unique_ptr<AsyncSocket> accepted(server->Accept(&accept_addr));
|
|
ASSERT_TRUE(accepted);
|
|
sink.Monitor(accepted.get());
|
|
|
|
// Both sides are now connected.
|
|
EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout);
|
|
EXPECT_TRUE(sink.Check(client.get(), SSE_OPEN));
|
|
EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress());
|
|
EXPECT_EQ(accepted->GetRemoteAddress(), client->GetLocalAddress());
|
|
|
|
// Expect a writable callback from the connect.
|
|
EXPECT_TRUE_WAIT(sink.Check(accepted.get(), SSE_WRITE), kTimeout);
|
|
|
|
// Fill the socket buffer.
|
|
char buf[1024 * 16] = {0};
|
|
int sends = 0;
|
|
while (++sends && accepted->Send(&buf, arraysize(buf)) != -1) {
|
|
}
|
|
EXPECT_TRUE(accepted->IsBlocking());
|
|
|
|
// Wait until data is available.
|
|
EXPECT_TRUE_WAIT(sink.Check(client.get(), SSE_READ), kTimeout);
|
|
|
|
// Pull data.
|
|
for (int i = 0; i < sends; ++i) {
|
|
client->Recv(buf, arraysize(buf), nullptr);
|
|
}
|
|
|
|
// Expect at least one additional writable callback.
|
|
EXPECT_TRUE_WAIT(sink.Check(accepted.get(), SSE_WRITE), kTimeout);
|
|
|
|
// Adding data in response to the writeable callback shouldn't cause infinite
|
|
// callbacks.
|
|
int extras = 0;
|
|
for (int i = 0; i < 100; ++i) {
|
|
accepted->Send(&buf, arraysize(buf));
|
|
rtc::Thread::Current()->ProcessMessages(1);
|
|
if (sink.Check(accepted.get(), SSE_WRITE)) {
|
|
extras++;
|
|
}
|
|
}
|
|
EXPECT_LT(extras, 2);
|
|
|
|
// Close down.
|
|
accepted->Close();
|
|
client->Close();
|
|
}
|
|
|
|
void SocketTest::UdpInternal(const IPAddress& loopback) {
|
|
SocketAddress empty = EmptySocketAddressWithFamily(loopback.family());
|
|
// Test basic bind and connect behavior.
|
|
AsyncSocket* socket = ss_->CreateAsyncSocket(loopback.family(), SOCK_DGRAM);
|
|
EXPECT_EQ(AsyncSocket::CS_CLOSED, socket->GetState());
|
|
EXPECT_EQ(0, socket->Bind(SocketAddress(loopback, 0)));
|
|
SocketAddress addr1 = socket->GetLocalAddress();
|
|
EXPECT_EQ(0, socket->Connect(addr1));
|
|
EXPECT_EQ(AsyncSocket::CS_CONNECTED, socket->GetState());
|
|
socket->Close();
|
|
EXPECT_EQ(AsyncSocket::CS_CLOSED, socket->GetState());
|
|
delete socket;
|
|
|
|
// Test send/receive behavior.
|
|
std::unique_ptr<TestClient> client1(
|
|
new TestClient(absl::WrapUnique(AsyncUDPSocket::Create(ss_, addr1))));
|
|
std::unique_ptr<TestClient> client2(
|
|
new TestClient(absl::WrapUnique(AsyncUDPSocket::Create(ss_, empty))));
|
|
|
|
SocketAddress addr2;
|
|
EXPECT_EQ(3, client2->SendTo("foo", 3, addr1));
|
|
EXPECT_TRUE(client1->CheckNextPacket("foo", 3, &addr2));
|
|
|
|
SocketAddress addr3;
|
|
EXPECT_EQ(6, client1->SendTo("bizbaz", 6, addr2));
|
|
EXPECT_TRUE(client2->CheckNextPacket("bizbaz", 6, &addr3));
|
|
EXPECT_EQ(addr3, addr1);
|
|
// TODO: figure out what the intent is here
|
|
for (int i = 0; i < 10; ++i) {
|
|
client2.reset(
|
|
new TestClient(absl::WrapUnique(AsyncUDPSocket::Create(ss_, empty))));
|
|
|
|
SocketAddress addr4;
|
|
EXPECT_EQ(3, client2->SendTo("foo", 3, addr1));
|
|
EXPECT_TRUE(client1->CheckNextPacket("foo", 3, &addr4));
|
|
EXPECT_EQ(addr4.ipaddr(), addr2.ipaddr());
|
|
|
|
SocketAddress addr5;
|
|
EXPECT_EQ(6, client1->SendTo("bizbaz", 6, addr4));
|
|
EXPECT_TRUE(client2->CheckNextPacket("bizbaz", 6, &addr5));
|
|
EXPECT_EQ(addr5, addr1);
|
|
|
|
addr2 = addr4;
|
|
}
|
|
}
|
|
|
|
void SocketTest::UdpReadyToSend(const IPAddress& loopback) {
|
|
SocketAddress empty = EmptySocketAddressWithFamily(loopback.family());
|
|
// RFC 5737 - The blocks 192.0.2.0/24 (TEST-NET-1) ... are provided for use in
|
|
// documentation.
|
|
// RFC 3849 - 2001:DB8::/32 as a documentation-only prefix.
|
|
std::string dest =
|
|
(loopback.family() == AF_INET6) ? "2001:db8::1" : "192.0.2.0";
|
|
SocketAddress test_addr(dest, 2345);
|
|
|
|
// Test send
|
|
std::unique_ptr<TestClient> client(
|
|
new TestClient(absl::WrapUnique(AsyncUDPSocket::Create(ss_, empty))));
|
|
int test_packet_size = 1200;
|
|
std::unique_ptr<char[]> test_packet(new char[test_packet_size]);
|
|
// Init the test packet just to avoid memcheck warning.
|
|
memset(test_packet.get(), 0, test_packet_size);
|
|
// Set the send buffer size to the same size as the test packet to have a
|
|
// better chance to get EWOULDBLOCK.
|
|
int send_buffer_size = test_packet_size;
|
|
#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
|
|
send_buffer_size /= 2;
|
|
#endif
|
|
client->SetOption(rtc::Socket::OPT_SNDBUF, send_buffer_size);
|
|
|
|
int error = 0;
|
|
uint32_t start_ms = Time();
|
|
int sent_packet_num = 0;
|
|
int expected_error = EWOULDBLOCK;
|
|
while (start_ms + kTimeout > Time()) {
|
|
int ret = client->SendTo(test_packet.get(), test_packet_size, test_addr);
|
|
++sent_packet_num;
|
|
if (ret != test_packet_size) {
|
|
error = client->GetError();
|
|
if (error == expected_error) {
|
|
RTC_LOG(LS_INFO) << "Got expected error code after sending "
|
|
<< sent_packet_num << " packets.";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
EXPECT_EQ(expected_error, error);
|
|
EXPECT_FALSE(client->ready_to_send());
|
|
EXPECT_TRUE_WAIT(client->ready_to_send(), kTimeout);
|
|
RTC_LOG(LS_INFO) << "Got SignalReadyToSend";
|
|
}
|
|
|
|
void SocketTest::GetSetOptionsInternal(const IPAddress& loopback) {
|
|
std::unique_ptr<AsyncSocket> socket(
|
|
ss_->CreateAsyncSocket(loopback.family(), SOCK_DGRAM));
|
|
socket->Bind(SocketAddress(loopback, 0));
|
|
|
|
// Check SNDBUF/RCVBUF.
|
|
const int desired_size = 12345;
|
|
#if defined(WEBRTC_LINUX)
|
|
// Yes, really. It's in the kernel source.
|
|
const int expected_size = desired_size * 2;
|
|
#else // !WEBRTC_LINUX
|
|
const int expected_size = desired_size;
|
|
#endif // !WEBRTC_LINUX
|
|
int recv_size = 0;
|
|
int send_size = 0;
|
|
// get the initial sizes
|
|
ASSERT_NE(-1, socket->GetOption(Socket::OPT_RCVBUF, &recv_size));
|
|
ASSERT_NE(-1, socket->GetOption(Socket::OPT_SNDBUF, &send_size));
|
|
// set our desired sizes
|
|
ASSERT_NE(-1, socket->SetOption(Socket::OPT_RCVBUF, desired_size));
|
|
ASSERT_NE(-1, socket->SetOption(Socket::OPT_SNDBUF, desired_size));
|
|
// get the sizes again
|
|
ASSERT_NE(-1, socket->GetOption(Socket::OPT_RCVBUF, &recv_size));
|
|
ASSERT_NE(-1, socket->GetOption(Socket::OPT_SNDBUF, &send_size));
|
|
// make sure they are right
|
|
ASSERT_EQ(expected_size, recv_size);
|
|
ASSERT_EQ(expected_size, send_size);
|
|
|
|
// Check that we can't set NODELAY on a UDP socket.
|
|
int current_nd, desired_nd = 1;
|
|
ASSERT_EQ(-1, socket->GetOption(Socket::OPT_NODELAY, ¤t_nd));
|
|
ASSERT_EQ(-1, socket->SetOption(Socket::OPT_NODELAY, desired_nd));
|
|
|
|
#if defined(WEBRTC_POSIX)
|
|
// Check DSCP.
|
|
int current_dscp, desired_dscp = 1;
|
|
ASSERT_NE(-1, socket->GetOption(Socket::OPT_DSCP, ¤t_dscp));
|
|
ASSERT_NE(-1, socket->SetOption(Socket::OPT_DSCP, desired_dscp));
|
|
ASSERT_NE(-1, socket->GetOption(Socket::OPT_DSCP, ¤t_dscp));
|
|
ASSERT_EQ(desired_dscp, current_dscp);
|
|
#endif
|
|
}
|
|
|
|
void SocketTest::SocketRecvTimestamp(const IPAddress& loopback) {
|
|
std::unique_ptr<Socket> socket(
|
|
ss_->CreateSocket(loopback.family(), SOCK_DGRAM));
|
|
EXPECT_EQ(0, socket->Bind(SocketAddress(loopback, 0)));
|
|
SocketAddress address = socket->GetLocalAddress();
|
|
|
|
int64_t send_time_1 = TimeMicros();
|
|
socket->SendTo("foo", 3, address);
|
|
int64_t recv_timestamp_1;
|
|
char buffer[3];
|
|
socket->RecvFrom(buffer, 3, nullptr, &recv_timestamp_1);
|
|
EXPECT_GT(recv_timestamp_1, -1);
|
|
|
|
const int64_t kTimeBetweenPacketsMs = 100;
|
|
Thread::SleepMs(kTimeBetweenPacketsMs);
|
|
|
|
int64_t send_time_2 = TimeMicros();
|
|
socket->SendTo("bar", 3, address);
|
|
int64_t recv_timestamp_2;
|
|
socket->RecvFrom(buffer, 3, nullptr, &recv_timestamp_2);
|
|
|
|
int64_t system_time_diff = send_time_2 - send_time_1;
|
|
int64_t recv_timestamp_diff = recv_timestamp_2 - recv_timestamp_1;
|
|
// Compare against the system time at the point of sending, because
|
|
// SleepMs may not sleep for exactly the requested time.
|
|
EXPECT_NEAR(system_time_diff, recv_timestamp_diff, 10000);
|
|
}
|
|
|
|
} // namespace rtc
|