Feature #17187 » connect_timeout.patch
| ext/socket/init.c | ||
|---|---|---|
| /* 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; | ||
| ... | ... | |
|      * | ||
|      * 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; | ||
| ... | ... | |
|        * 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: | ||
| ... | ... | |
| #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; | ||
| ... | ... | |
| #ifdef EINPROGRESS | ||
|           case EINPROGRESS: | ||
| #endif | ||
|             return wait_connectable(fd); | ||
|             return wait_connectable(fd, timeout); | ||
|         } | ||
|     } | ||
|     return status; | ||
| ext/socket/ipsocket.c | ||
|---|---|---|
|     int type; | ||
|     int fd; | ||
|     VALUE resolv_timeout; | ||
|     VALUE connect_timeout; | ||
| }; | ||
| static VALUE | ||
| ... | ... | |
|     int family = AF_UNSPEC; | ||
|     const char *syscall = 0; | ||
|     VALUE resolv_timeout = arg->resolv_timeout; | ||
|     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; | ||
|     } | ||
| #ifdef HAVE_GETADDRINFO_A | ||
|     arg->remote.res = rsock_addrinfo_a(arg->remote.host, arg->remote.serv, | ||
| ... | ... | |
| 	    if (status >= 0) { | ||
| 		status = rsock_connect(fd, res->ai_addr, res->ai_addrlen, | ||
| 				       (type == INET_SOCKS)); | ||
| 				       (type == INET_SOCKS), tv); | ||
| 		syscall = "connect(2)"; | ||
| 	    } | ||
| 	} | ||
| ... | ... | |
| 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; | ||
| ... | ... | |
|     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); | ||
| } | ||
| ext/socket/rubysocket.h | ||
|---|---|---|
| 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 { | ||
| ... | ... | |
| 			        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, | ||
| ext/socket/socket.c | ||
|---|---|---|
|     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); | ||
|     } | ||
| ext/socket/tcpserver.c | ||
|---|---|---|
|     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); | ||
| } | ||
| /* | ||
| ext/socket/tcpsocket.c | ||
|---|---|---|
| /* | ||
|  * 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) | ||
| ... | ... | |
|     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 | ||
| ext/socket/udpsocket.c | ||
|---|---|---|
|     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; | ||
| 	} | ||
|     } | ||
| ext/socket/unixsocket.c | ||
|---|---|---|
| { | ||
|     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 | ||
| test/socket/test_tcp.rb | ||
|---|---|---|
|     end | ||
|   end | ||
|   def test_initialize_connect_timeout | ||
|     assert_raise(Errno::ETIMEDOUT) do | ||
|       TCPSocket.new("192.0.2.1", 80, connect_timeout: 0) | ||
|     end | ||
|   end | ||
|   def test_recvfrom | ||
|     TCPServer.open("localhost", 0) {|svr| | ||
|       th = Thread.new { | ||