| // 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 |