|
|
|
@ -18,16 +18,20 @@ |
|
|
|
|
|
|
|
|
|
#include <gtest/gtest.h> |
|
|
|
|
|
|
|
|
|
#include "src/core/lib/address_utils/parse_address.h" |
|
|
|
|
#include "src/core/lib/channel/channel_args.h" |
|
|
|
|
#include "src/core/lib/gprpp/time.h" |
|
|
|
|
#include "src/core/lib/iomgr/port.h" |
|
|
|
|
#include "test/core/util/port.h" |
|
|
|
|
#include "test/core/util/test_config.h" |
|
|
|
|
|
|
|
|
|
// This test won't work except with posix sockets enabled
|
|
|
|
|
#ifdef GRPC_POSIX_SOCKET_TCP_CLIENT |
|
|
|
|
|
|
|
|
|
#include <errno.h> |
|
|
|
|
#include <fcntl.h> |
|
|
|
|
#include <netinet/in.h> |
|
|
|
|
#include <poll.h> |
|
|
|
|
#include <string.h> |
|
|
|
|
#include <sys/socket.h> |
|
|
|
|
#include <unistd.h> |
|
|
|
@ -207,27 +211,88 @@ void test_fails(void) { |
|
|
|
|
|
|
|
|
|
void test_connect_cancellation_succeeds(void) { |
|
|
|
|
gpr_log(GPR_ERROR, "---- starting test_connect_cancellation_succeeds() ----"); |
|
|
|
|
auto target_ipv6_addr_uri = *grpc_core::URI::Parse(absl::StrCat( |
|
|
|
|
"ipv6:[::1]:", std::to_string(grpc_pick_unused_port_or_die()))); |
|
|
|
|
auto target_ipv4_addr_uri = *grpc_core::URI::Parse(absl::StrCat( |
|
|
|
|
"ipv4:127.0.0.1:", std::to_string(grpc_pick_unused_port_or_die()))); |
|
|
|
|
grpc_resolved_address resolved_addr; |
|
|
|
|
struct sockaddr_in* addr = |
|
|
|
|
reinterpret_cast<struct sockaddr_in*>(resolved_addr.addr); |
|
|
|
|
int svr_fd; |
|
|
|
|
grpc_closure done; |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
|
|
|
|
|
memset(&resolved_addr, 0, sizeof(resolved_addr)); |
|
|
|
|
resolved_addr.len = static_cast<socklen_t>(sizeof(struct sockaddr_in)); |
|
|
|
|
addr->sin_family = AF_INET; |
|
|
|
|
|
|
|
|
|
bool tried_ipv4 = false; |
|
|
|
|
ASSERT_TRUE(grpc_parse_uri(target_ipv6_addr_uri, &resolved_addr)); |
|
|
|
|
auto try_bind = [&](int sock) { |
|
|
|
|
return (sock >= 0 && |
|
|
|
|
bind(sock, reinterpret_cast<sockaddr*>(resolved_addr.addr), |
|
|
|
|
resolved_addr.len) == 0); |
|
|
|
|
}; |
|
|
|
|
/* create a phony server */ |
|
|
|
|
svr_fd = socket(AF_INET, SOCK_STREAM, 0); |
|
|
|
|
ASSERT_GE(svr_fd, 0); |
|
|
|
|
ASSERT_EQ(bind(svr_fd, (struct sockaddr*)addr, (socklen_t)resolved_addr.len), |
|
|
|
|
0); |
|
|
|
|
svr_fd = socket(AF_INET6, SOCK_STREAM, 0); |
|
|
|
|
// Try ipv6
|
|
|
|
|
if (!try_bind(svr_fd)) { |
|
|
|
|
if (svr_fd >= 0) { |
|
|
|
|
close(svr_fd); |
|
|
|
|
} |
|
|
|
|
// Failed to bind ipv6. Try ipv4
|
|
|
|
|
ASSERT_TRUE(grpc_parse_uri(target_ipv4_addr_uri, &resolved_addr)); |
|
|
|
|
svr_fd = socket(AF_INET, SOCK_STREAM, 0); |
|
|
|
|
tried_ipv4 = true; |
|
|
|
|
if (!try_bind(svr_fd)) { |
|
|
|
|
if (svr_fd >= 0) { |
|
|
|
|
close(svr_fd); |
|
|
|
|
} |
|
|
|
|
gpr_log(GPR_ERROR, |
|
|
|
|
"Skipping test. Failed to create a phony server bound to ipv6 or " |
|
|
|
|
"ipv4 address"); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ASSERT_EQ(listen(svr_fd, 1), 0); |
|
|
|
|
|
|
|
|
|
std::vector<int> client_sockets; |
|
|
|
|
bool create_more_client_connections = true; |
|
|
|
|
// Create and connect client sockets until the connection attempt times out.
|
|
|
|
|
// Even if the backlog specified to listen is 1, the kernel continues to
|
|
|
|
|
// accept a certain number of SYN packets before dropping them. This loop
|
|
|
|
|
// attempts to identify the number of new connection attempts that will
|
|
|
|
|
// be allowed by the kernel before any subsequent connection attempts
|
|
|
|
|
// become pending indefinitely.
|
|
|
|
|
while (create_more_client_connections) { |
|
|
|
|
const int kOne = 1; |
|
|
|
|
int client_socket = socket(tried_ipv4 ? AF_INET : AF_INET6, SOCK_STREAM, 0); |
|
|
|
|
ASSERT_GE(client_socket, 0); |
|
|
|
|
setsockopt(client_socket, SOL_SOCKET, SO_REUSEADDR, &kOne, sizeof(kOne)); |
|
|
|
|
// Make fd non-blocking.
|
|
|
|
|
int flags = fcntl(client_socket, F_GETFL, 0); |
|
|
|
|
ASSERT_EQ(fcntl(client_socket, F_SETFL, flags | O_NONBLOCK), 0); |
|
|
|
|
|
|
|
|
|
if (connect(client_socket, reinterpret_cast<sockaddr*>(resolved_addr.addr), |
|
|
|
|
resolved_addr.len) == -1) { |
|
|
|
|
if (errno == EINPROGRESS) { |
|
|
|
|
struct pollfd pfd; |
|
|
|
|
pfd.fd = client_socket; |
|
|
|
|
pfd.events = POLLOUT; |
|
|
|
|
pfd.revents = 0; |
|
|
|
|
int ret = poll(&pfd, 1, 1000); |
|
|
|
|
if (ret == -1) { |
|
|
|
|
FAIL() << "poll() failed during connect; errno=" << errno; |
|
|
|
|
} else if (ret == 0) { |
|
|
|
|
// current connection attempt timed out. It indicates that the
|
|
|
|
|
// kernel will cause any subsequent connection attempts to
|
|
|
|
|
// become pending indefinitely.
|
|
|
|
|
create_more_client_connections = false; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
FAIL() << "Failed to connect to the server. errno=%d" << errno; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
client_sockets.push_back(client_socket); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// connect to it. accept() is not called on the bind socket. So the connection
|
|
|
|
|
// should appear to be stuck giving ample time to try to cancel it.
|
|
|
|
|
ASSERT_EQ(getsockname(svr_fd, (struct sockaddr*)addr, |
|
|
|
|
ASSERT_EQ(getsockname(svr_fd, reinterpret_cast<sockaddr*>(resolved_addr.addr), |
|
|
|
|
(socklen_t*)&resolved_addr.len), |
|
|
|
|
0); |
|
|
|
|
GRPC_CLOSURE_INIT(&done, must_succeed, nullptr, grpc_schedule_on_exec_ctx); |
|
|
|
@ -240,6 +305,9 @@ void test_connect_cancellation_succeeds(void) { |
|
|
|
|
&resolved_addr, grpc_core::Timestamp::InfFuture()); |
|
|
|
|
ASSERT_GT(connection_handle, 0); |
|
|
|
|
ASSERT_EQ(grpc_tcp_client_cancel_connect(connection_handle), true); |
|
|
|
|
for (auto sock : client_sockets) { |
|
|
|
|
close(sock); |
|
|
|
|
} |
|
|
|
|
close(svr_fd); |
|
|
|
|
gpr_log(GPR_ERROR, "---- finished test_connect_cancellation_succeeds() ----"); |
|
|
|
|
} |
|
|
|
|