ruby-changes:64070
From: Masaki <ko1@a...>
Date: Thu, 10 Dec 2020 20:55:02 +0900 (JST)
Subject: [ruby-changes:64070] 78f188524f (master): Add connect_timeout to TCPSocket
https://git.ruby-lang.org/ruby.git/commit/?id=78f188524f From 78f188524f551c97b1a7a44ae13514729f1a21c7 Mon Sep 17 00:00:00 2001 From: Masaki Matsushita <glass.saga@g...> Date: Fri, 25 Sep 2020 16:20:18 +0900 Subject: Add connect_timeout to TCPSocket Add connect_timeout to TCPSocket.new in the same way as Socket.tcp. Closes [Feature #17187] diff --git a/NEWS.md b/NEWS.md index 6bf5d94..b698ce4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -348,6 +348,10 @@ Outstanding ones only. https://github.com/ruby/ruby/blob/trunk/NEWS.md#L348 * Update to IRB 1.2.6 +* Socket + + * Add :connect_timeout to TCPSocket.new [[Feature #17187]] + * Net::HTTP * Net::HTTP#verify_hostname= and Net::HTTP#verify_hostname have been diff --git a/ext/socket/init.c b/ext/socket/init.c index 0604e8b..af46b8e 100644 --- a/ext/socket/init.c +++ b/ext/socket/init.c @@ -451,7 +451,7 @@ rsock_socket(int domain, int type, int proto) https://github.com/ruby/ruby/blob/trunk/ext/socket/init.c#L451 /* 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) https://github.com/ruby/ruby/blob/trunk/ext/socket/init.c#L488 * * 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) https://github.com/ruby/ruby/blob/trunk/ext/socket/init.c#L503 * 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) https://github.com/ruby/ruby/blob/trunk/ext/socket/init.c#L556 #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) https://github.com/ruby/ruby/blob/trunk/ext/socket/init.c#L580 #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 646bc7c..72fea78 100644 --- a/ext/socket/ipsocket.c +++ b/ext/socket/ipsocket.c @@ -20,6 +20,7 @@ struct inetsock_arg https://github.com/ruby/ruby/blob/trunk/ext/socket/ipsocket.c#L20 int type; int fd; VALUE resolv_timeout; + VALUE connect_timeout; }; static VALUE @@ -50,6 +51,14 @@ init_inetsock_internal(VALUE v) https://github.com/ruby/ruby/blob/trunk/ext/socket/ipsocket.c#L51 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) https://github.com/ruby/ruby/blob/trunk/ext/socket/ipsocket.c#L125 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) https://github.com/ruby/ruby/blob/trunk/ext/socket/ipsocket.c#L170 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, https://github.com/ruby/ruby/blob/trunk/ext/socket/ipsocket.c#L183 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 b0e494f..f0e4f3a 100644 --- a/ext/socket/rubysocket.h +++ b/ext/socket/rubysocket.h @@ -346,7 +346,7 @@ int rsock_socket(int domain, int type, int proto); https://github.com/ruby/ruby/blob/trunk/ext/socket/rubysocket.h#L346 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, https://github.com/ruby/ruby/blob/trunk/ext/socket/rubysocket.h#L371 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 96365fb..189e5c2 100644 --- a/ext/socket/socket.c +++ b/ext/socket/socket.c @@ -393,7 +393,7 @@ sock_connect(VALUE sock, VALUE addr) https://github.com/ruby/ruby/blob/trunk/ext/socket/socket.c#L393 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 ad31e16..7634420 100644 --- a/ext/socket/tcpserver.c +++ b/ext/socket/tcpserver.c @@ -36,7 +36,7 @@ tcp_svr_init(int argc, VALUE *argv, VALUE sock) https://github.com/ruby/ruby/blob/trunk/ext/socket/tcpserver.c#L36 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 1446e39..4bd6f87 100644 --- a/ext/socket/tcpsocket.c +++ b/ext/socket/tcpsocket.c @@ -12,13 +12,14 @@ https://github.com/ruby/ruby/blob/trunk/ext/socket/tcpsocket.c#L12 /* * 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) https://github.com/ruby/ruby/blob/trunk/ext/socket/tcpsocket.c#L27 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 6ef8242..593f055 100644 --- a/ext/socket/udpsocket.c +++ b/ext/socket/udpsocket.c @@ -60,7 +60,7 @@ udp_connect_internal(VALUE v) https://github.com/ruby/ruby/blob/trunk/ext/socket/udpsocket.c#L60 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/un (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/