[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]