summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
authorMasaki Matsushita <glass.saga@gmail.com>2020-09-25 16:20:18 +0900
committerMasaki Matsushita <glass.saga@gmail.com>2020-12-10 20:52:29 +0900
commit78f188524f551c97b1a7a44ae13514729f1a21c7 (patch)
treeeb334c023f4571b883221cbaa7a149352ac83b9c /ext
parent658b4ff60934b9fb6845e214fda83229e631e366 (diff)
Add connect_timeout to TCPSocket
Add connect_timeout to TCPSocket.new in the same way as Socket.tcp. Closes [Feature #17187]
Diffstat (limited to 'ext')
-rw-r--r--ext/socket/init.c14
-rw-r--r--ext/socket/ipsocket.c14
-rw-r--r--ext/socket/rubysocket.h4
-rw-r--r--ext/socket/socket.c2
-rw-r--r--ext/socket/tcpserver.c2
-rw-r--r--ext/socket/tcpsocket.c18
-rw-r--r--ext/socket/udpsocket.c2
-rw-r--r--ext/socket/unixsocket.c2
8 files changed, 38 insertions, 20 deletions
diff --git a/ext/socket/init.c b/ext/socket/init.c
index 0604e8b72f..af46b8edaa 100644
--- a/ext/socket/init.c
+++ b/ext/socket/init.c
@@ -451,7 +451,7 @@ rsock_socket(int domain, int type, int proto)
/* emulate blocking connect behavior on EINTR or non-blocking socket */
static int
-wait_connectable(int fd)
+wait_connectable(int fd, struct timeval *timeout)
{
int sockerr, revents;
socklen_t sockerrlen;
@@ -488,7 +488,7 @@ wait_connectable(int fd)
*
* Note: rb_wait_for_single_fd already retries on EINTR/ERESTART
*/
- revents = rb_wait_for_single_fd(fd, RB_WAITFD_IN|RB_WAITFD_OUT, NULL);
+ revents = rb_wait_for_single_fd(fd, RB_WAITFD_IN|RB_WAITFD_OUT, timeout);
if (revents < 0)
return -1;
@@ -503,6 +503,12 @@ wait_connectable(int fd)
* be defensive in case some platforms set SO_ERROR on the original,
* interrupted connect()
*/
+
+ /* when the connection timed out, no errno is set and revents is 0. */
+ if (timeout && revents == 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
case EINTR:
#ifdef ERESTART
case ERESTART:
@@ -550,7 +556,7 @@ socks_connect_blocking(void *data)
#endif
int
-rsock_connect(int fd, const struct sockaddr *sockaddr, int len, int socks)
+rsock_connect(int fd, const struct sockaddr *sockaddr, int len, int socks, struct timeval *timeout)
{
int status;
rb_blocking_function_t *func = connect_blocking;
@@ -574,7 +580,7 @@ rsock_connect(int fd, const struct sockaddr *sockaddr, int len, int socks)
#ifdef EINPROGRESS
case EINPROGRESS:
#endif
- return wait_connectable(fd);
+ return wait_connectable(fd, timeout);
}
}
return status;
diff --git a/ext/socket/ipsocket.c b/ext/socket/ipsocket.c
index 646bc7c758..72fea789a5 100644
--- a/ext/socket/ipsocket.c
+++ b/ext/socket/ipsocket.c
@@ -20,6 +20,7 @@ struct inetsock_arg
int type;
int fd;
VALUE resolv_timeout;
+ VALUE connect_timeout;
};
static VALUE
@@ -50,6 +51,14 @@ init_inetsock_internal(VALUE v)
int fd, status = 0, local = 0;
int family = AF_UNSPEC;
const char *syscall = 0;
+ VALUE connect_timeout = arg->connect_timeout;
+ struct timeval tv_storage;
+ struct timeval *tv = NULL;
+
+ if (!NIL_P(connect_timeout)) {
+ tv_storage = rb_time_interval(connect_timeout);
+ tv = &tv_storage;
+ }
arg->remote.res = rsock_addrinfo(arg->remote.host, arg->remote.serv,
family, SOCK_STREAM,
@@ -116,7 +125,7 @@ init_inetsock_internal(VALUE v)
if (status >= 0) {
status = rsock_connect(fd, res->ai_addr, res->ai_addrlen,
- (type == INET_SOCKS));
+ (type == INET_SOCKS), tv);
syscall = "connect(2)";
}
}
@@ -161,7 +170,7 @@ init_inetsock_internal(VALUE v)
VALUE
rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv,
VALUE local_host, VALUE local_serv, int type,
- VALUE resolv_timeout)
+ VALUE resolv_timeout, VALUE connect_timeout)
{
struct inetsock_arg arg;
arg.sock = sock;
@@ -174,6 +183,7 @@ rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv,
arg.type = type;
arg.fd = -1;
arg.resolv_timeout = resolv_timeout;
+ arg.connect_timeout = connect_timeout;
return rb_ensure(init_inetsock_internal, (VALUE)&arg,
inetsock_cleanup, (VALUE)&arg);
}
diff --git a/ext/socket/rubysocket.h b/ext/socket/rubysocket.h
index b0e494f54e..f0e4f3a4a0 100644
--- a/ext/socket/rubysocket.h
+++ b/ext/socket/rubysocket.h
@@ -346,7 +346,7 @@ int rsock_socket(int domain, int type, int proto);
int rsock_detect_cloexec(int fd);
VALUE rsock_init_sock(VALUE sock, int fd);
VALUE rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass);
-VALUE rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv, VALUE local_host, VALUE local_serv, int type, VALUE resolv_timeout);
+VALUE rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv, VALUE local_host, VALUE local_serv, int type, VALUE resolv_timeout, VALUE connect_timeout);
VALUE rsock_init_unixsock(VALUE sock, VALUE path, int server);
struct rsock_send_arg {
@@ -371,7 +371,7 @@ VALUE rsock_s_recvfrom_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str,
VALUE ex, enum sock_recv_type from);
VALUE rsock_s_recvfrom(VALUE sock, int argc, VALUE *argv, enum sock_recv_type from);
-int rsock_connect(int fd, const struct sockaddr *sockaddr, int len, int socks);
+int rsock_connect(int fd, const struct sockaddr *sockaddr, int len, int socks, struct timeval *timeout);
VALUE rsock_s_accept(VALUE klass, int fd, struct sockaddr *sockaddr, socklen_t *len);
VALUE rsock_s_accept_nonblock(VALUE klass, VALUE ex, rb_io_t *fptr,
diff --git a/ext/socket/socket.c b/ext/socket/socket.c
index 96365fbf91..189e5c2784 100644
--- a/ext/socket/socket.c
+++ b/ext/socket/socket.c
@@ -393,7 +393,7 @@ sock_connect(VALUE sock, VALUE addr)
addr = rb_str_new4(addr);
GetOpenFile(sock, fptr);
fd = fptr->fd;
- n = rsock_connect(fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), 0);
+ n = rsock_connect(fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), 0, NULL);
if (n < 0) {
rsock_sys_fail_raddrinfo_or_sockaddr("connect(2)", addr, rai);
}
diff --git a/ext/socket/tcpserver.c b/ext/socket/tcpserver.c
index ad31e16306..7634420e38 100644
--- a/ext/socket/tcpserver.c
+++ b/ext/socket/tcpserver.c
@@ -36,7 +36,7 @@ tcp_svr_init(int argc, VALUE *argv, VALUE sock)
VALUE hostname, port;
rb_scan_args(argc, argv, "011", &hostname, &port);
- return rsock_init_inetsock(sock, hostname, port, Qnil, Qnil, INET_SERVER, Qnil);
+ return rsock_init_inetsock(sock, hostname, port, Qnil, Qnil, INET_SERVER, Qnil, Qnil);
}
/*
diff --git a/ext/socket/tcpsocket.c b/ext/socket/tcpsocket.c
index 1446e390e4..4bd6f87406 100644
--- a/ext/socket/tcpsocket.c
+++ b/ext/socket/tcpsocket.c
@@ -12,13 +12,14 @@
/*
* call-seq:
- * TCPSocket.new(remote_host, remote_port, local_host=nil, local_port=nil, resolv_timeout: nil)
+ * TCPSocket.new(remote_host, remote_port, local_host=nil, local_port=nil, resolv_timeout: nil, connect_timeout: nil)
*
* Opens a TCP connection to +remote_host+ on +remote_port+. If +local_host+
* and +local_port+ are specified, then those parameters are used on the local
* end to establish the connection.
*
* [:resolv_timeout] specify the name resolution timeout in seconds.
+ * [:connect_timeout] specify the timeout in seconds.
*/
static VALUE
tcp_init(int argc, VALUE *argv, VALUE sock)
@@ -26,27 +27,28 @@ tcp_init(int argc, VALUE *argv, VALUE sock)
VALUE remote_host, remote_serv;
VALUE local_host, local_serv;
VALUE opt;
- static ID keyword_ids[1];
- VALUE kwargs[1];
+ static ID keyword_ids[2];
+ VALUE kwargs[2];
VALUE resolv_timeout = Qnil;
+ VALUE connect_timeout = Qnil;
if (!keyword_ids[0]) {
CONST_ID(keyword_ids[0], "resolv_timeout");
+ CONST_ID(keyword_ids[1], "connect_timeout");
}
rb_scan_args(argc, argv, "22:", &remote_host, &remote_serv,
&local_host, &local_serv, &opt);
if (!NIL_P(opt)) {
- rb_get_kwargs(opt, keyword_ids, 0, 1, kwargs);
- if (kwargs[0] != Qundef) {
- resolv_timeout = kwargs[0];
- }
+ rb_get_kwargs(opt, keyword_ids, 0, 2, kwargs);
+ if (kwargs[0] != Qundef) { resolv_timeout = kwargs[0]; }
+ if (kwargs[1] != Qundef) { connect_timeout = kwargs[1]; }
}
return rsock_init_inetsock(sock, remote_host, remote_serv,
local_host, local_serv, INET_CLIENT,
- resolv_timeout);
+ resolv_timeout, connect_timeout);
}
static VALUE
diff --git a/ext/socket/udpsocket.c b/ext/socket/udpsocket.c
index 6ef8242a1e..593f05522d 100644
--- a/ext/socket/udpsocket.c
+++ b/ext/socket/udpsocket.c
@@ -60,7 +60,7 @@ udp_connect_internal(VALUE v)
rb_io_check_closed(fptr = arg->fptr);
fd = fptr->fd;
for (res = arg->res->ai; res; res = res->ai_next) {
- if (rsock_connect(fd, res->ai_addr, res->ai_addrlen, 0) >= 0) {
+ if (rsock_connect(fd, res->ai_addr, res->ai_addrlen, 0, NULL) >= 0) {
return Qtrue;
}
}
diff --git a/ext/socket/unixsocket.c b/ext/socket/unixsocket.c
index 53a50958ed..857cfa6002 100644
--- a/ext/socket/unixsocket.c
+++ b/ext/socket/unixsocket.c
@@ -22,7 +22,7 @@ unixsock_connect_internal(VALUE a)
{
struct unixsock_arg *arg = (struct unixsock_arg *)a;
return (VALUE)rsock_connect(arg->fd, (struct sockaddr*)arg->sockaddr,
- arg->sockaddrlen, 0);
+ arg->sockaddrlen, 0, NULL);
}
static VALUE