diff options
| author | Misaki Shioi <31817032+shioimm@users.noreply.github.com> | 2025-12-17 16:16:32 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-12-17 16:16:32 +0900 |
| commit | 54d3945ee53fa29186c6aa44f673a3a5ec3b38d9 (patch) | |
| tree | 136e3348b58d35df7d71e466ce48f4e13b5c6f51 /ext | |
| parent | 1506c489eef67f268a54484662011a60897bdd48 (diff) | |
Add host information to timeout error messages in `TCPSocket.new` and `Socket.tcp` (#15582)
This change adds host information to the error messages shown when a timeout occurs while passing timeout options to `TCPSocket.new` or `Socket.tcp`, for improved usability.
(When the `fast_fallback option` is enabled, there may be multiple possible destinations, so the host name is shown instead of an IP address.)
As part of this change, the error messages in `Addrinfo.getaddrinfo` and `Addrinfo#connect_internal`, both of which are used by `Socket.tcp`, have also been improved in the same way.
Diffstat (limited to 'ext')
| -rw-r--r-- | ext/socket/init.c | 9 | ||||
| -rw-r--r-- | ext/socket/ipsocket.c | 28 | ||||
| -rw-r--r-- | ext/socket/lib/socket.rb | 8 | ||||
| -rw-r--r-- | ext/socket/raddrinfo.c | 10 | ||||
| -rw-r--r-- | ext/socket/rubysocket.h | 2 |
5 files changed, 44 insertions, 13 deletions
diff --git a/ext/socket/init.c b/ext/socket/init.c index 8e405609ba..b761d601c3 100644 --- a/ext/socket/init.c +++ b/ext/socket/init.c @@ -473,7 +473,7 @@ rsock_socket(int domain, int type, int proto) /* emulate blocking connect behavior on EINTR or non-blocking socket */ static int -wait_connectable(VALUE self, VALUE timeout) +wait_connectable(VALUE self, VALUE timeout, const struct sockaddr *sockaddr, int len) { int sockerr; socklen_t sockerrlen; @@ -514,7 +514,10 @@ wait_connectable(VALUE self, VALUE 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!"); + VALUE rai = rsock_addrinfo_new((struct sockaddr *)sockaddr, len, PF_UNSPEC, 0, 0, Qnil, Qnil); + VALUE addr_str = rsock_addrinfo_inspect_sockaddr(rai); + VALUE message = rb_sprintf("user specified timeout for %" PRIsVALUE, addr_str); + rb_raise(rb_eIOTimeoutError, "%" PRIsVALUE, message); } int revents = RB_NUM2INT(result); @@ -603,7 +606,7 @@ rsock_connect(VALUE self, const struct sockaddr *sockaddr, int len, int socks, V #ifdef EINPROGRESS case EINPROGRESS: #endif - return wait_connectable(self, timeout); + return wait_connectable(self, timeout, sockaddr, len); } } return status; diff --git a/ext/socket/ipsocket.c b/ext/socket/ipsocket.c index 20d9101c11..88225f76e5 100644 --- a/ext/socket/ipsocket.c +++ b/ext/socket/ipsocket.c @@ -27,9 +27,19 @@ struct inetsock_arg }; void -rsock_raise_user_specified_timeout(void) +rsock_raise_user_specified_timeout(struct addrinfo *ai, VALUE host, VALUE port) { - rb_raise(rb_eIOTimeoutError, "user specified timeout"); + VALUE message; + + if (ai && ai->ai_addr) { + VALUE rai = rsock_addrinfo_new((struct sockaddr *)ai->ai_addr, (socklen_t)ai->ai_addrlen, PF_UNSPEC, 0, 0, Qnil, Qnil); + VALUE addr_str = rsock_addrinfo_inspect_sockaddr(rai); + message = rb_sprintf("user specified timeout for %" PRIsVALUE, addr_str); + } else { + message = rb_sprintf("user specified timeout for %" PRIsVALUE " port %" PRIsVALUE, host, port); + } + + rb_raise(rb_eIOTimeoutError, "%" PRIsVALUE, message); } static VALUE @@ -149,7 +159,9 @@ init_inetsock_internal(VALUE v) } else { VALUE elapsed = rb_funcall(current_clocktime(), '-', 1, starts_at); timeout = rb_funcall(open_timeout, '-', 1, elapsed); - if (rb_funcall(timeout, '<', 1, INT2FIX(0)) == Qtrue) rsock_raise_user_specified_timeout(); + if (rb_funcall(timeout, '<', 1, INT2FIX(0)) == Qtrue) { + rsock_raise_user_specified_timeout(res, arg->remote.host, arg->remote.serv); + } } if (status >= 0) { @@ -844,6 +856,10 @@ init_fast_fallback_inetsock_internal(VALUE v) if (!NIL_P(open_timeout)) { VALUE elapsed = rb_funcall(current_clocktime(), '-', 1, starts_at); timeout = rb_funcall(open_timeout, '-', 1, elapsed); + + if (rb_funcall(timeout, '<', 1, INT2FIX(0)) == Qtrue) { + rsock_raise_user_specified_timeout(NULL, arg->remote.host, arg->remote.serv); + } } if (NIL_P(timeout)) { if (!NIL_P(connect_timeout)) { @@ -1180,7 +1196,9 @@ init_fast_fallback_inetsock_internal(VALUE v) } } - if (is_timeout_tv(user_specified_open_timeout_at, now)) rsock_raise_user_specified_timeout(); + if (is_timeout_tv(user_specified_open_timeout_at, now)) { + rsock_raise_user_specified_timeout(NULL, arg->remote.host, arg->remote.serv); + } if (!any_addrinfos(&resolution_store)) { if (!in_progress_fds(arg->connection_attempt_fds_size) && @@ -1203,7 +1221,7 @@ init_fast_fallback_inetsock_internal(VALUE v) resolution_store.is_all_finished) && (is_timeout_tv(user_specified_connect_timeout_at, now) || !in_progress_fds(arg->connection_attempt_fds_size))) { - rsock_raise_user_specified_timeout(); + rsock_raise_user_specified_timeout(NULL, arg->remote.host, arg->remote.serv); } } } diff --git a/ext/socket/lib/socket.rb b/ext/socket/lib/socket.rb index 6d04ca2483..e74eaec43a 100644 --- a/ext/socket/lib/socket.rb +++ b/ext/socket/lib/socket.rb @@ -62,7 +62,7 @@ class Addrinfo break when :wait_writable sock.wait_writable(timeout) or - raise Errno::ETIMEDOUT, 'user specified timeout' + raise Errno::ETIMEDOUT, "user specified timeout for #{self.ip_address}:#{self.ip_port}" end while true else sock.connect(self) @@ -905,7 +905,9 @@ class Socket < BasicSocket end end - raise(IO::TimeoutError, 'user specified timeout') if expired?(now, user_specified_open_timeout_at) + if expired?(now, user_specified_open_timeout_at) + raise(IO::TimeoutError, "user specified timeout for #{host}:#{port}") + end if resolution_store.empty_addrinfos? if connecting_sockets.empty? && resolution_store.resolved_all_families? @@ -918,7 +920,7 @@ class Socket < BasicSocket if (expired?(now, user_specified_resolv_timeout_at) || resolution_store.resolved_all_families?) && (expired?(now, user_specified_connect_timeout_at) || connecting_sockets.empty?) - raise IO::TimeoutError, 'user specified timeout' + raise(IO::TimeoutError, "user specified timeout for #{host}:#{port}") end end end diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c index e4e40e591d..3dcbe7717a 100644 --- a/ext/socket/raddrinfo.c +++ b/ext/socket/raddrinfo.c @@ -597,7 +597,15 @@ start: if (need_free) free_getaddrinfo_arg(arg); - if (timedout) rsock_raise_user_specified_timeout(); + if (timedout) { + if (arg->ai) { + rsock_raise_user_specified_timeout(arg->ai, Qnil, Qnil); + } else { + VALUE host = rb_str_new_cstr(hostp); + VALUE port = rb_str_new_cstr(portp); + rsock_raise_user_specified_timeout(NULL, host, port); + } + } // If the current thread is interrupted by asynchronous exception, the following raises the exception. // But if the current thread is interrupted by timer thread, the following returns; we need to manually retry. diff --git a/ext/socket/rubysocket.h b/ext/socket/rubysocket.h index 1adccee427..638b7ede6e 100644 --- a/ext/socket/rubysocket.h +++ b/ext/socket/rubysocket.h @@ -454,7 +454,7 @@ void free_fast_fallback_getaddrinfo_shared(struct fast_fallback_getaddrinfo_shar #endif unsigned int rsock_value_timeout_to_msec(VALUE); -NORETURN(void rsock_raise_user_specified_timeout(void)); +NORETURN(void rsock_raise_user_specified_timeout(struct addrinfo *ai, VALUE host, VALUE port)); void rsock_init_basicsocket(void); void rsock_init_ipsocket(void); |
