mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	Add connect_timeout to TCPSocket
Add connect_timeout to TCPSocket.new in the same way as Socket.tcp. Closes [Feature #17187]
This commit is contained in:
		
							parent
							
								
									658b4ff609
								
							
						
					
					
						commit
						78f188524f
					
				
					 10 changed files with 48 additions and 20 deletions
				
			
		
							
								
								
									
										4
									
								
								NEWS.md
									
										
									
									
									
								
							
							
						
						
									
										4
									
								
								NEWS.md
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -348,6 +348,10 @@ Outstanding ones only.
 | 
			
		|||
 | 
			
		||||
    * 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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -69,6 +69,12 @@ class TestSocket_TCPSocket < Test::Unit::TestCase
 | 
			
		|||
    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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue