Project

General

Profile

Feature #17187 ยป connect_timeout.patch

Glass_saga (Masaki Matsushita), 09/25/2020 11:16 AM

View differences:

ext/socket/init.c
451 451

  
452 452
/* emulate blocking connect behavior on EINTR or non-blocking socket */
453 453
static int
454
wait_connectable(int fd)
454
wait_connectable(int fd, struct timeval *timeout)
455 455
{
456 456
    int sockerr, revents;
457 457
    socklen_t sockerrlen;
......
488 488
     *
489 489
     * Note: rb_wait_for_single_fd already retries on EINTR/ERESTART
490 490
     */
491
    revents = rb_wait_for_single_fd(fd, RB_WAITFD_IN|RB_WAITFD_OUT, NULL);
491
    revents = rb_wait_for_single_fd(fd, RB_WAITFD_IN|RB_WAITFD_OUT, timeout);
492 492

  
493 493
    if (revents < 0)
494 494
        return -1;
......
503 503
       * be defensive in case some platforms set SO_ERROR on the original,
504 504
       * interrupted connect()
505 505
       */
506

  
507
	/* when the connection timed out, no errno is set and revents is 0. */
508
	if (timeout && revents == 0) {
509
	    errno = ETIMEDOUT;
510
	    return -1;
511
	}
506 512
      case EINTR:
507 513
#ifdef ERESTART
508 514
      case ERESTART:
......
550 556
#endif
551 557

  
552 558
int
553
rsock_connect(int fd, const struct sockaddr *sockaddr, int len, int socks)
559
rsock_connect(int fd, const struct sockaddr *sockaddr, int len, int socks, struct timeval *timeout)
554 560
{
555 561
    int status;
556 562
    rb_blocking_function_t *func = connect_blocking;
......
574 580
#ifdef EINPROGRESS
575 581
          case EINPROGRESS:
576 582
#endif
577
            return wait_connectable(fd);
583
            return wait_connectable(fd, timeout);
578 584
        }
579 585
    }
580 586
    return status;
ext/socket/ipsocket.c
20 20
    int type;
21 21
    int fd;
22 22
    VALUE resolv_timeout;
23
    VALUE connect_timeout;
23 24
};
24 25

  
25 26
static VALUE
......
51 52
    int family = AF_UNSPEC;
52 53
    const char *syscall = 0;
53 54
    VALUE resolv_timeout = arg->resolv_timeout;
55
    VALUE connect_timeout = arg->connect_timeout;
56
    struct timeval tv_storage;
57
    struct timeval *tv = NULL;
58

  
59
    if (!NIL_P(connect_timeout)) {
60
        tv_storage = rb_time_interval(connect_timeout);
61
        tv = &tv_storage;
62
    }
54 63

  
55 64
#ifdef HAVE_GETADDRINFO_A
56 65
    arg->remote.res = rsock_addrinfo_a(arg->remote.host, arg->remote.serv,
......
124 133

  
125 134
	    if (status >= 0) {
126 135
		status = rsock_connect(fd, res->ai_addr, res->ai_addrlen,
127
				       (type == INET_SOCKS));
136
				       (type == INET_SOCKS), tv);
128 137
		syscall = "connect(2)";
129 138
	    }
130 139
	}
......
169 178
VALUE
170 179
rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv,
171 180
	            VALUE local_host, VALUE local_serv, int type,
172
		    VALUE resolv_timeout)
181
		    VALUE resolv_timeout, VALUE connect_timeout)
173 182
{
174 183
    struct inetsock_arg arg;
175 184
    arg.sock = sock;
......
182 191
    arg.type = type;
183 192
    arg.fd = -1;
184 193
    arg.resolv_timeout = resolv_timeout;
194
    arg.connect_timeout = connect_timeout;
185 195
    return rb_ensure(init_inetsock_internal, (VALUE)&arg,
186 196
		     inetsock_cleanup, (VALUE)&arg);
187 197
}
ext/socket/rubysocket.h
350 350
int rsock_detect_cloexec(int fd);
351 351
VALUE rsock_init_sock(VALUE sock, int fd);
352 352
VALUE rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass);
353
VALUE rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv, VALUE local_host, VALUE local_serv, int type, VALUE resolv_timeout);
353
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);
354 354
VALUE rsock_init_unixsock(VALUE sock, VALUE path, int server);
355 355

  
356 356
struct rsock_send_arg {
......
375 375
			        VALUE ex, enum sock_recv_type from);
376 376
VALUE rsock_s_recvfrom(VALUE sock, int argc, VALUE *argv, enum sock_recv_type from);
377 377

  
378
int rsock_connect(int fd, const struct sockaddr *sockaddr, int len, int socks);
378
int rsock_connect(int fd, const struct sockaddr *sockaddr, int len, int socks, struct timeval *timeout);
379 379

  
380 380
VALUE rsock_s_accept(VALUE klass, int fd, struct sockaddr *sockaddr, socklen_t *len);
381 381
VALUE rsock_s_accept_nonblock(VALUE klass, VALUE ex, rb_io_t *fptr,
ext/socket/socket.c
393 393
    addr = rb_str_new4(addr);
394 394
    GetOpenFile(sock, fptr);
395 395
    fd = fptr->fd;
396
    n = rsock_connect(fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), 0);
396
    n = rsock_connect(fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), 0, NULL);
397 397
    if (n < 0) {
398 398
	rsock_sys_fail_raddrinfo_or_sockaddr("connect(2)", addr, rai);
399 399
    }
ext/socket/tcpserver.c
36 36
    VALUE hostname, port;
37 37

  
38 38
    rb_scan_args(argc, argv, "011", &hostname, &port);
39
    return rsock_init_inetsock(sock, hostname, port, Qnil, Qnil, INET_SERVER, Qnil);
39
    return rsock_init_inetsock(sock, hostname, port, Qnil, Qnil, INET_SERVER, Qnil, Qnil);
40 40
}
41 41

  
42 42
/*
ext/socket/tcpsocket.c
12 12

  
13 13
/*
14 14
 * call-seq:
15
 *    TCPSocket.new(remote_host, remote_port, local_host=nil, local_port=nil, resolv_timeout: nil)
15
 *    TCPSocket.new(remote_host, remote_port, local_host=nil, local_port=nil, resolv_timeout: nil, connect_timeout: nil)
16 16
 *
17 17
 * Opens a TCP connection to +remote_host+ on +remote_port+.  If +local_host+
18 18
 * and +local_port+ are specified, then those parameters are used on the local
19 19
 * end to establish the connection.
20 20
 *
21 21
 * [:resolv_timeout] specify the name resolution timeout in seconds.
22
 * [:connect_timeout] specify the timeout in seconds.
22 23
 */
23 24
static VALUE
24 25
tcp_init(int argc, VALUE *argv, VALUE sock)
......
26 27
    VALUE remote_host, remote_serv;
27 28
    VALUE local_host, local_serv;
28 29
    VALUE opt;
29
    static ID keyword_ids[1];
30
    VALUE kwargs[1];
30
    static ID keyword_ids[2];
31
    VALUE kwargs[2];
31 32
    VALUE resolv_timeout = Qnil;
33
    VALUE connect_timeout = Qnil;
32 34

  
33 35
    if (!keyword_ids[0]) {
34 36
	CONST_ID(keyword_ids[0], "resolv_timeout");
37
	CONST_ID(keyword_ids[1], "connect_timeout");
35 38
    }
36 39

  
37 40
    rb_scan_args(argc, argv, "22:", &remote_host, &remote_serv,
38 41
			&local_host, &local_serv, &opt);
39 42

  
40 43
    if (!NIL_P(opt)) {
41
	rb_get_kwargs(opt, keyword_ids, 0, 1, kwargs);
42
	if (kwargs[0] != Qundef) {
43
	    resolv_timeout = kwargs[0];
44
	}
44
	rb_get_kwargs(opt, keyword_ids, 0, 2, kwargs);
45
	if (kwargs[0] != Qundef) { resolv_timeout = kwargs[0]; }
46
	if (kwargs[1] != Qundef) { connect_timeout = kwargs[1]; }
45 47
    }
46 48

  
47 49
    return rsock_init_inetsock(sock, remote_host, remote_serv,
48 50
			       local_host, local_serv, INET_CLIENT,
49
			       resolv_timeout);
51
			       resolv_timeout, connect_timeout);
50 52
}
51 53

  
52 54
static VALUE
ext/socket/udpsocket.c
60 60
    rb_io_check_closed(fptr = arg->fptr);
61 61
    fd = fptr->fd;
62 62
    for (res = arg->res->ai; res; res = res->ai_next) {
63
	if (rsock_connect(fd, res->ai_addr, res->ai_addrlen, 0) >= 0) {
63
	if (rsock_connect(fd, res->ai_addr, res->ai_addrlen, 0, NULL) >= 0) {
64 64
	    return Qtrue;
65 65
	}
66 66
    }
ext/socket/unixsocket.c
22 22
{
23 23
    struct unixsock_arg *arg = (struct unixsock_arg *)a;
24 24
    return (VALUE)rsock_connect(arg->fd, (struct sockaddr*)arg->sockaddr,
25
			        arg->sockaddrlen, 0);
25
			        arg->sockaddrlen, 0, NULL);
26 26
}
27 27

  
28 28
static VALUE
test/socket/test_tcp.rb
69 69
    end
70 70
  end
71 71

  
72
  def test_initialize_connect_timeout
73
    assert_raise(Errno::ETIMEDOUT) do
74
      TCPSocket.new("192.0.2.1", 80, connect_timeout: 0)
75
    end
76
  end
77

  
72 78
  def test_recvfrom
73 79
    TCPServer.open("localhost", 0) {|svr|
74 80
      th = Thread.new {