Support IO#timeout
for rsock_connect
. (#11880)
This commit is contained in:
parent
2e6ddd968d
commit
ad5641fd34
Notes:
git
2024-10-11 21:08:53 +00:00
Merged-By: ioquatix <samuel@codeotaku.com>
@ -473,10 +473,11 @@ rsock_socket(int domain, int type, int proto)
|
|||||||
|
|
||||||
/* emulate blocking connect behavior on EINTR or non-blocking socket */
|
/* emulate blocking connect behavior on EINTR or non-blocking socket */
|
||||||
static int
|
static int
|
||||||
wait_connectable(int fd, struct timeval *timeout)
|
wait_connectable(VALUE self, VALUE timeout)
|
||||||
{
|
{
|
||||||
int sockerr, revents;
|
int sockerr;
|
||||||
socklen_t sockerrlen;
|
socklen_t sockerrlen;
|
||||||
|
int fd = rb_io_descriptor(self);
|
||||||
|
|
||||||
sockerrlen = (socklen_t)sizeof(sockerr);
|
sockerrlen = (socklen_t)sizeof(sockerr);
|
||||||
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &sockerrlen) < 0)
|
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &sockerrlen) < 0)
|
||||||
@ -510,7 +511,13 @@ wait_connectable(int fd, struct timeval *timeout)
|
|||||||
*
|
*
|
||||||
* Note: rb_wait_for_single_fd already retries on EINTR/ERESTART
|
* Note: rb_wait_for_single_fd already retries on EINTR/ERESTART
|
||||||
*/
|
*/
|
||||||
revents = rb_wait_for_single_fd(fd, RB_WAITFD_IN|RB_WAITFD_OUT, timeout);
|
VALUE result = rb_io_wait(self, RB_INT2NUM(RUBY_IO_READABLE|RUBY_IO_WRITABLE), timeout);
|
||||||
|
|
||||||
|
if (result == Qfalse) {
|
||||||
|
rb_raise(rb_eIOTimeoutError, "Connect timed out!");
|
||||||
|
}
|
||||||
|
|
||||||
|
int revents = RB_NUM2INT(result);
|
||||||
|
|
||||||
if (revents < 0)
|
if (revents < 0)
|
||||||
return -1;
|
return -1;
|
||||||
@ -525,12 +532,6 @@ wait_connectable(int fd, struct timeval *timeout)
|
|||||||
* be defensive in case some platforms set SO_ERROR on the original,
|
* be defensive in case some platforms set SO_ERROR on the original,
|
||||||
* interrupted connect()
|
* 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:
|
case EINTR:
|
||||||
#ifdef ERESTART
|
#ifdef ERESTART
|
||||||
case ERESTART:
|
case ERESTART:
|
||||||
@ -578,7 +579,7 @@ socks_connect_blocking(void *data)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
int
|
int
|
||||||
rsock_connect(VALUE self, const struct sockaddr *sockaddr, int len, int socks, struct timeval *timeout)
|
rsock_connect(VALUE self, const struct sockaddr *sockaddr, int len, int socks, VALUE timeout)
|
||||||
{
|
{
|
||||||
int descriptor = rb_io_descriptor(self);
|
int descriptor = rb_io_descriptor(self);
|
||||||
rb_blocking_function_t *func = connect_blocking;
|
rb_blocking_function_t *func = connect_blocking;
|
||||||
@ -602,7 +603,7 @@ rsock_connect(VALUE self, const struct sockaddr *sockaddr, int len, int socks, s
|
|||||||
#ifdef EINPROGRESS
|
#ifdef EINPROGRESS
|
||||||
case EINPROGRESS:
|
case EINPROGRESS:
|
||||||
#endif
|
#endif
|
||||||
return wait_connectable(descriptor, timeout);
|
return wait_connectable(self, timeout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
|
@ -55,13 +55,6 @@ init_inetsock_internal(VALUE v)
|
|||||||
int family = AF_UNSPEC;
|
int family = AF_UNSPEC;
|
||||||
const char *syscall = 0;
|
const char *syscall = 0;
|
||||||
VALUE connect_timeout = arg->connect_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
arg->remote.res = rsock_addrinfo(arg->remote.host, arg->remote.serv,
|
arg->remote.res = rsock_addrinfo(arg->remote.host, arg->remote.serv,
|
||||||
family, SOCK_STREAM,
|
family, SOCK_STREAM,
|
||||||
@ -130,7 +123,7 @@ init_inetsock_internal(VALUE v)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (status >= 0) {
|
if (status >= 0) {
|
||||||
status = rsock_connect(io, res->ai_addr, res->ai_addrlen, (type == INET_SOCKS), tv);
|
status = rsock_connect(io, res->ai_addr, res->ai_addrlen, (type == INET_SOCKS), connect_timeout);
|
||||||
syscall = "connect(2)";
|
syscall = "connect(2)";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -379,7 +379,7 @@ VALUE rsock_s_recvfrom_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str,
|
|||||||
VALUE ex, enum sock_recv_type from);
|
VALUE ex, enum sock_recv_type from);
|
||||||
VALUE rsock_s_recvfrom(VALUE sock, int argc, VALUE *argv, enum sock_recv_type from);
|
VALUE rsock_s_recvfrom(VALUE sock, int argc, VALUE *argv, enum sock_recv_type from);
|
||||||
|
|
||||||
int rsock_connect(VALUE self, const struct sockaddr *sockaddr, int len, int socks, struct timeval *timeout);
|
int rsock_connect(VALUE self, const struct sockaddr *sockaddr, int len, int socks, VALUE timeout);
|
||||||
|
|
||||||
VALUE rsock_s_accept(VALUE klass, VALUE io, struct sockaddr *sockaddr, socklen_t *len);
|
VALUE rsock_s_accept(VALUE klass, VALUE io, struct sockaddr *sockaddr, socklen_t *len);
|
||||||
VALUE rsock_s_accept_nonblock(VALUE klass, VALUE ex, rb_io_t *fptr,
|
VALUE rsock_s_accept_nonblock(VALUE klass, VALUE ex, rb_io_t *fptr,
|
||||||
|
@ -387,16 +387,15 @@ rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass)
|
|||||||
* * connect function in Microsoft's Winsock functions reference
|
* * connect function in Microsoft's Winsock functions reference
|
||||||
*/
|
*/
|
||||||
static VALUE
|
static VALUE
|
||||||
sock_connect(VALUE sock, VALUE addr)
|
sock_connect(VALUE self, VALUE addr)
|
||||||
{
|
{
|
||||||
VALUE rai;
|
VALUE rai;
|
||||||
rb_io_t *fptr;
|
|
||||||
|
|
||||||
SockAddrStringValueWithAddrinfo(addr, rai);
|
SockAddrStringValueWithAddrinfo(addr, rai);
|
||||||
addr = rb_str_new4(addr);
|
addr = rb_str_new4(addr);
|
||||||
GetOpenFile(sock, fptr);
|
|
||||||
|
|
||||||
int result = rsock_connect(sock, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), 0, NULL);
|
int result = rsock_connect(self, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), 0, RUBY_IO_TIMEOUT_DEFAULT);
|
||||||
|
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
rsock_sys_fail_raddrinfo_or_sockaddr("connect(2)", addr, rai);
|
rsock_sys_fail_raddrinfo_or_sockaddr("connect(2)", addr, rai);
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ udp_connect_internal(VALUE v)
|
|||||||
struct addrinfo *res;
|
struct addrinfo *res;
|
||||||
|
|
||||||
for (res = arg->res->ai; res; res = res->ai_next) {
|
for (res = arg->res->ai; res; res = res->ai_next) {
|
||||||
if (rsock_connect(arg->io, res->ai_addr, res->ai_addrlen, 0, NULL) >= 0) {
|
if (rsock_connect(arg->io, res->ai_addr, res->ai_addrlen, 0, RUBY_IO_TIMEOUT_DEFAULT) >= 0) {
|
||||||
return Qtrue;
|
return Qtrue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ static VALUE
|
|||||||
unixsock_connect_internal(VALUE a)
|
unixsock_connect_internal(VALUE a)
|
||||||
{
|
{
|
||||||
struct unixsock_arg *arg = (struct unixsock_arg *)a;
|
struct unixsock_arg *arg = (struct unixsock_arg *)a;
|
||||||
return (VALUE)rsock_connect(arg->io, (struct sockaddr*)arg->sockaddr, arg->sockaddrlen, 0, NULL);
|
return (VALUE)rsock_connect(arg->io, (struct sockaddr*)arg->sockaddr, arg->sockaddrlen, 0, RUBY_IO_TIMEOUT_DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
|
@ -53,4 +53,18 @@ describe 'Socket#connect' do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ruby_version_is "3.4" do
|
||||||
|
it "fails with timeout" do
|
||||||
|
# TEST-NET-1 IP address are reserved for documentation and example purposes.
|
||||||
|
address = Socket.pack_sockaddr_in(1, "192.0.2.1")
|
||||||
|
|
||||||
|
client = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM)
|
||||||
|
client.timeout = 0
|
||||||
|
|
||||||
|
-> {
|
||||||
|
client.connect(address)
|
||||||
|
}.should raise_error(IO::TimeoutError)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user