blob: 8e5b7ec8d0778c36e9da8bcda6ff7f88f90498c2 [file] [log] [blame]
// Copyright 2013 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.
#include "net/third_party/quic/platform/impl/quic_socket_utils.h"
#include <fcntl.h>
#include <array>
#include "net/third_party/quic/platform/api/quic_logging.h"
#include "net/third_party/quic/platform/api/quic_socket_address.h"
#include "net/third_party/quic/platform/api/quic_test.h"
#include "starboard/types.h"
namespace quic {
namespace test {
namespace {
// A test fixture is used to ensure that all sockets are closed down gracefully
// upon test completion. Also provides a convenient API to Bind not presently
// available in QuicSocketUtils.
class QuicSocketUtilsTest : public QuicTest {
protected:
~QuicSocketUtilsTest() override {
for (int fd : open_sockets_) {
close(fd);
}
}
int CreateUDPSocket(const QuicSocketAddress& address) {
bool overflow_supported = false;
int fd = QuicSocketUtils::CreateUDPSocket(
address, /*receive_buffer_size =*/kDefaultSocketReceiveBuffer,
/*send_buffer_size =*/kDefaultSocketReceiveBuffer, &overflow_supported);
if (fd != -1) {
open_sockets_.push_back(fd);
}
return fd;
}
int CreateBoundUDPSocket(QuicSocketAddress* address) {
int fd = CreateUDPSocket(*address);
*address = BindSocket(fd, *address);
if (!address->IsInitialized()) {
close(fd);
fd = -1;
}
return fd;
}
QuicSocketAddress BindSocket(int fd, const QuicSocketAddress& address) {
QuicSocketAddress bound_address;
if (fd == -1) {
return bound_address;
}
sockaddr_storage bind_addr_native = address.generic_address();
socklen_t bind_addr_size = 0;
switch (address.host().address_family()) {
case IpAddressFamily::IP_V4:
bind_addr_size = sizeof(struct sockaddr_in);
break;
case IpAddressFamily::IP_V6:
bind_addr_size = sizeof(struct sockaddr_in6);
break;
case IpAddressFamily::IP_UNSPEC:
QUIC_LOG(FATAL) << "Unspecified IP address family";
}
int rc = bind(fd, reinterpret_cast<sockaddr*>(&bind_addr_native),
bind_addr_size);
if (rc != 0) {
QUIC_LOG(ERROR) << "Failed to bind socket to " << address.ToString()
<< ": " << strerror(errno);
return bound_address;
}
rc = bound_address.FromSocket(fd);
if (rc != 0) {
QUIC_LOG(ERROR) << "Failed to get bound socket address from fd: "
<< strerror(errno);
bound_address = QuicSocketAddress();
}
return bound_address;
}
private:
std::vector<int> open_sockets_;
};
// This test verifies that QuicSocketUtils creates a non-blocking socket
// successfully by seeing if a read blocks.
TEST_F(QuicSocketUtilsTest, NonBlockingSocket) {
std::array<char, 512> buffer;
QuicIpAddress localhost = QuicIpAddress::Loopback4();
QuicSocketAddress addr(localhost, 0);
int fd = CreateUDPSocket(addr);
ASSERT_NE(-1, fd);
int fd_flags = fcntl(fd, F_GETFL, 0);
// Assert so that the test errors out quickly rather than blocking below and
// relying on timeouts.
ASSERT_TRUE(fd_flags & O_NONBLOCK) << "Socket not reporting as non-blocking";
QuicIpAddress target_server_addr;
auto walltimestamp = QuicWallTime::Zero();
QuicSocketAddress remote_addr;
int bytes_read = QuicSocketUtils::ReadPacket(fd, buffer.data(), buffer.size(),
nullptr, &target_server_addr,
&walltimestamp, &remote_addr);
EXPECT_EQ(-1, bytes_read);
}
// This test verifies that we can successfully WritePacket/ReadPacket between
// two localhost sockets.
TEST_F(QuicSocketUtilsTest, PacketRoundTrip) {
QuicIpAddress localhost = QuicIpAddress::Loopback4();
QuicSocketAddress client_addr(localhost, 0);
QuicSocketAddress server_addr(localhost, 0);
int server_fd = CreateBoundUDPSocket(&server_addr);
int client_fd = CreateUDPSocket(client_addr);
ASSERT_NE(-1, server_fd);
ASSERT_NE(-1, client_fd);
{
std::array<char, 512> write_buffer;
for (size_t i = 0; i < write_buffer.size(); i++) {
write_buffer[i] = static_cast<char>(i);
}
auto res = QuicSocketUtils::WritePacket(client_fd, write_buffer.data(),
write_buffer.size(),
QuicIpAddress(), server_addr);
ASSERT_EQ(WRITE_STATUS_OK, res.status)
<< "Failed to write with error " << res.error_code;
EXPECT_EQ(512, res.bytes_written);
}
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(server_fd, &read_fds);
timeval select_timeout;
select_timeout.tv_sec = 5;
select_timeout.tv_usec = 0;
int select_rc =
select(1 + server_fd, &read_fds, nullptr, nullptr, &select_timeout);
EXPECT_EQ(select_rc, 1) << "server_fd didn't become read selectable: "
<< errno;
{
std::array<char, 1024> read_buffer;
QuicIpAddress target_server_addr;
auto walltimestamp = QuicWallTime::Zero();
QuicSocketAddress remote_addr;
int bytes_read = QuicSocketUtils::ReadPacket(
server_fd, read_buffer.data(), read_buffer.size(), nullptr,
&target_server_addr, &walltimestamp, &remote_addr);
EXPECT_EQ(512, bytes_read);
for (int i = 0; i < bytes_read; i++) {
EXPECT_EQ(static_cast<char>(i), read_buffer[i]);
}
}
}
} // namespace
} // namespace test
} // namespace quic