Feature #17187 ยป connect_timeout.patch
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 { |