1301 Commits

Author SHA1 Message Date
Nobuyoshi Nakada
b51ae934f7 [Feature #22215] Remove vm_core dependency from gc header
Keep `internal/gc.h` usable without VM-private definitions so bundled
extensions do not inherit `vm_core.h` dependencies.
2026-08-07 09:59:15 +09:00
Hiroshi SHIBATA
82294a714f [Bug #22223] Check SO_ERROR after waiting for nonblocking connect
Darwin 27 answers the retry connect(2) on a refused nonblocking socket
with EISCONN, so the retry idiom in Addrinfo#connect_internal returned
an unconnected socket. SO_ERROR still holds the real error, so consult
it after wait_writable, as wait_connectable() in init.c already does.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 15:23:21 +09:00
Nobuyoshi Nakada
e0fc139e1b
[Misc #22206] Derive generated dependencies from Makefile targets 2026-07-23 00:54:12 +09:00
Nobuyoshi Nakada
7d4461e162
[Misc #22206] Generate dependencies without compiler builds
Replace `tool/update-deps` with a Ruby source scanner so dependency
updates no longer require GNU make, a configured build, or compiler
preprocessor output.

Keep compact source mappings and scanner declarations in dependency
files, and expand complete rules into the build directory.  Avoid the
temporary fixture headers required by the compiler-driven prototype.

Use the generated rules from GNU make, BSD make, and NMake builds.
Support out-of-tree and read-only source trees, and retain expanded
dependencies shipped in release archives when baseruby is unavailable.
2026-07-23 00:04:47 +09:00
Hiroshi SHIBATA
630f604dc4 Remove unreachable require of io/wait in socket.rb
IO#wait_writable is defined in core since Ruby 3.2, so this guard
never requires io/wait when running on the ruby built from this tree.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 18:45:52 +09:00
Nobuyoshi Nakada
403200c900
Inspect socket credentials only when the structures are defined
Specifically, on macOS, while `SCM_CREDS` is defined, the definition
of `struct cmsgcred` is disabled.
2026-07-21 17:23:43 +09:00
Nobuyoshi Nakada
b9a50a0b81 Bound the hostname terminator search
Search only the gethostname buffer for its terminator so resizing the
Ruby string does not read beyond the returned data.
2026-07-15 13:28:39 +09:00
Matt Valentine-House
5465dca954 Prefer rb_sym2str over rb_id2str(SYM2ID) 2026-06-24 22:15:52 +01:00
John Hawthorn
daffaaee73 Mark most TypedData types as THREAD_SAFE_FREE
Set RUBY_TYPED_THREAD_SAFE_FREE on TypedData types whose dfree function
is trivially safe, and only frees its own memory

Types not yet marked as THREAD_SAFE_FREE:
- id2ref_tbl_type: sets the process-global id2ref_tbl to NULL.
- mutex_data_type: unlinks itself from another thread's keeping_mutexes list.
- autoload_data_type: deletes nodes from a shared intrusive list.
- autoload_const_type: deletes itself from a shared list.
- rb_cont_data_type: mutates the shared fiber pool and the global first_jit_cont list.
- rb_fiber_data_type: delegates to cont_free.
- FiberPoolDataType: mutates shared fiber pool free-lists.
- ractor_data_type: reconfigures global VM event-hook flags and call caches.
- exported_object_registry: frees a global table under the VM lock.
- rb_box_data_type (Box::Entry): unlinks classext from other live class and module objects.
- box_ext_cleanup_type: dereferences another String object and calls unlink.
- monitor_data_type: uses the default free, so the flag has no effect.

Co-authored-by: Luke Gruber <luke.gruber@shopify.com>
2026-06-23 13:30:26 -07:00
Nobuyoshi Nakada
283bd19d15 [Feature #17944] Remove no longer used functions 2026-06-12 10:08:43 +09:00
Nobuyoshi Nakada
221c7c6644
[Feature #17944] Remove deprecated socket methods
These methods have been deprecated since nine years ago.
2026-06-11 20:12:59 +09:00
Nobuyoshi Nakada
2a5394cee7
Dump FreeBSD socket credential data wholely 2026-06-05 22:38:34 +09:00
Nobuyoshi Nakada
f78cd18c91
Reduce unnecessary intermediate strings
`"%" PRIsVALUE` just copies/appends the corresponding argument as a
string.
2026-05-30 22:34:14 +09:00
Misaki Shioi
8601f5a42e
Fix reading freed memory in rb_getaddrinfo 2026-05-20 09:13:51 +09:00
Nobuyoshi Nakada
c091c186e4
Fix thread leaks
Wait for terminated threads to finish.
2026-04-09 14:19:02 +09:00
Jean Boussier
6cd4549060 Optimize File.join common use case
`File.join` is a hotspot for common libraries such as Zeitwerk
and Bootsnap. It has a fairly flexible signature, but 99% of
the time it's called with just two (or a small number of) UTF-8 strings.

If we optimistically optimize for that use case we can cut down a large
number of type and encoding checks, significantly speeding up the method.

The one remaining expensive check we could try to optimize is `str_null_check`.
Given it's common to use the same base string for joining, we could memoize it.
Also we could precompute it for literal strings.

```
compare-ruby: ruby 4.1.0dev (2026-01-17T14:40:03Z master 00a3b71eaf) +PRISM [arm64-darwin25]
built-ruby: ruby 4.1.0dev (2026-01-18T12:10:38Z spedup-file-join 069bab58d4) +PRISM [arm64-darwin25]
warming up....

|              |compare-ruby|built-ruby|
|:-------------|-----------:|---------:|
|two_strings   |      2.475M|    9.444M|
|              |           -|     3.82x|
|many_strings  |    551.975k|    2.346M|
|              |           -|     4.25x|
|array         |    514.946k|  522.034k|
|              |           -|     1.01x|
|mixed         |    621.236k|  633.189k|
|              |           -|     1.02x|
```
2026-01-18 16:31:31 +01:00
Misaki Shioi
3fe2ebf8e4
Remove unnecessary comparison from is_local_port_fixed (#15757)
Simplify the conditions:
- Return false if the port is 0 or a negative number
- Return true if the port is a positive number
2025-12-28 17:23:50 +09:00
Misaki Shioi
a8c3d5e127
Fix: Do not fast_fallback if local_port is explicitly specified (#15732)
`fast fallback` cannot be used with explicitly specified local port,
because concurrent binds to the same `local_host:local_port`
can raise `Errno::EADDRINUSE`.

This issue is more likely to occur on hosts with `IPV6_V6ONLY` disabled,
because IPv6 binds can also occupy IPv4-mapped IPv6 address space.
2025-12-27 18:26:56 +09:00
Misaki Shioi
47244b0f30
Fix: Specifying 0 should cause an immediate timeout (#15641)
This change fixes a bug in which specifying 0 for timeout-related options (such as the `timeout` option of `Addrinfo.getaddrinfo`) incorrectly results in an infinite wait.

(This change overwrites https://github.com/ruby/ruby/pull/15626 .)
2025-12-19 15:44:35 +09:00
Misaki Shioi
42d66b894c
Fix: Do not check open_timeout twice (#15626) 2025-12-19 12:12:12 +09:00
Misaki Shioi
54d3945ee5
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.
2025-12-17 16:16:32 +09:00
Misaki Shioi
7b5691c3b0
Socket.tcp and TCPSocket.new raises IO::TiemoutError with user specified timeout (#15602)
* `Socket.tcp` and `TCPSocket.new` raises `IO::TiemoutError` with user specified timeout

In https://github.com/ruby/ruby/pull/11880, `rsock_connect()` was changed to raise `IO::TimeoutError` when a user-specified timeout occurs.
However, when `TCPSocket.new` attempts to connect to multiple destinations, it does not use `rsock_connect()`, and instead raises `Errno::ETIMEDOUT` on timeout.
As a result, the exception class raised on timeout could differ depending on whether there were multiple destinations or not.

To align this behavior with the implementation of `rsock_connect()`, this change makes `TCPSocket.new` raise `IO::TimeoutError` when a user-specified timeout occurs.
Similarly, `Socket.tcp` is updated to raise `IO::TimeoutError` when a timeout occurs within the method.
(Note that the existing behavior of `Addrinfo#connect_internal`, which Socket.tcp depends on internally and which raises `Errno::ETIMEDOUT` on timeout, is not changed.)

* [ruby/net-http] Raise `Net::OpenTimeout` when `TCPSocket.open` raises `IO::TimeoutError`.

With the changes in https://github.com/ruby/ruby/pull/15602, `TCPSocket.open` now raises `IO::TimeoutError` when a user-specified timeout occurs.
This change updates #connect to handle this case accordingly.

https://github.com/ruby/net-http/commit/f64109e1cf
2025-12-17 15:02:26 +09:00
Misaki Shioi
74a365310c
Fix: Recalculate the timeout duration considering open_timeout (#15596)
This change updates the behavior so that, when there is only a single destination and `open_timeout` is specified, the remaining `open_timeout` duration is used as the connection timeout.
2025-12-17 11:11:39 +09:00
Misaki Shioi
2b1a9afbfb
Fix: Do not pass negative timeout to Addrinfo#connect_internal (#15578)
This change fixes a bug where, with `Socket.tcp`’s `fast_fallback option` disabled, specifying `open_timeout` could unintentionally pass a negative value to `Addrinfo#connect_internal`, `causing an ArgumentError`.

```
❯ ruby -rsocket -e 'p Socket.tcp("localhost", 9292, open_timeout: 1, fast_fallback: false)'
/Users/misaki-shioi/src/install/lib/ruby/4.0.0+0/socket.rb:64:in 'IO#wait_writable': time interval must not be negative (ArgumentError)

          sock.wait_writable(timeout) or
                             ^^^^^^^
	from /Users/misaki-shioi/src/install/lib/ruby/4.0.0+0/socket.rb:64:in 'Addrinfo#connect_internal'
	from /Users/misaki-shioi/src/install/lib/ruby/4.0.0+0/socket.rb:141:in 'Addrinfo#connect'
	from /Users/misaki-shioi/src/install/lib/ruby/4.0.0+0/socket.rb:964:in 'block in Socket.tcp_without_fast_fallback'
	from /Users/misaki-shioi/src/install/lib/ruby/4.0.0+0/socket.rb:231:in 'Array#each'
	from /Users/misaki-shioi/src/install/lib/ruby/4.0.0+0/socket.rb:231:in 'Addrinfo.foreach'
	from /Users/misaki-shioi/src/install/lib/ruby/4.0.0+0/socket.rb:945:in 'Socket.tcp_without_fast_fallback'
	from /Users/misaki-shioi/src/install/lib/ruby/4.0.0+0/socket.rb:671:in 'Socket.tcp'
	from -e:1:in '<main>'
```
2025-12-16 19:52:45 +09:00
Luke Gruber
98ab418fed
Revert "Fix Socket.tcp cleanup after Thread#kill (#15131)" (#15565)
This reverts commit 3038286a4bf7832f1c42c8cc9774ee6ff19876fc.

The following CI failure scared me:
https://github.com/ruby/ruby/actions/runs/20241051861/job/58108997049

```
  1) Timeout:
  TestResolvDNS#test_multiple_servers_with_timeout_and_truncated_tcp_fallback
```

Since it could be related, I'm reverting this for now.
2025-12-15 17:07:15 -05:00
Luke Gruber
3038286a4b
Fix Socket.tcp cleanup after Thread#kill (#15131)
Socket.tcp launches ruby threads to resolve hostnames, and those threads
communicate through a queue implemented with `IO.pipe`. When the thread
that called `Socket.tcp` is killed, the resolver threads still try to
communicate through the pipe even though it may be closed. The method
`Socket.tcp_with_fast_fallback` tries to deal with this by killing the
threads in an ensure block, and then closing the pipe. However, calling
`Thread#kill` is not a blocking operation, it only sets a flag on the
thread telling it to raise during the next interrupt. The thread needs
to be joined to ensure it is terminated. The following script
demonstrates the issue:

```ruby
require "socket"

ts = []
5.times do
  ts << Thread.new do
    loop do
      1_000.times do |i|
        puts "#{i}"
        t = Thread.new do
          Socket.tcp("ruby-lang.org", 80)
        end
        sleep 0.001
        t.kill
      end
    end
  end
end
ts.each(&:join)
```

output:
```
/Users/luke/workspace/ruby-dev/ruby-build-debug/.ext/common/socket.rb:1019:in 'IO#write': closed stream (IOError)
	from /Users/luke/workspace/ruby-dev/ruby-build-debug/.ext/common/socket.rb:1019:in 'IO#putc'
	from /Users/luke/workspace/ruby-dev/ruby-build-debug/.ext/common/socket.rb:1019:in 'block in Socket::HostnameResolutionResult#add'
	from /Users/luke/workspace/ruby-dev/ruby-build-debug/.ext/common/socket.rb:1017:in 'Thread::Mutex#synchronize'
	from /Users/luke/workspace/ruby-dev/ruby-build-debug/.ext/common/socket.rb:1017:in 'Socket::HostnameResolutionResult#add'
	from /Users/luke/workspace/ruby-dev/ruby-build-debug/.ext/common/socket.rb:980:in 'Socket.resolve_hostname'
	from /Users/luke/workspace/ruby-dev/ruby-build-debug/.ext/common/socket.rb:719:in 'block (2 levels) in Socket.tcp_with_fast_fallback'
/Users/luke/workspace/ruby-dev/ruby-build-debug/.ext/common/socket.rb:1019:in 'IO#write': closed stream (IOError)
	from /Users/luke/workspace/ruby-dev/ruby-build-debug/.ext/common/socket.rb:1019:in 'IO#putc'
	from /Users/luke/workspace/ruby-dev/ruby-build-debug/.ext/common/socket.rb:1019:in 'block in Socket::HostnameResolutionResult#add'
	from /Users/luke/workspace/ruby-dev/ruby-build-debug/.ext/common/socket.rb:1017:in 'Thread::Mutex#synchronize'
	from /Users/luke/workspace/ruby-dev/ruby-build-debug/.ext/common/socket.rb:1017:in 'Socket::HostnameResolutionResult#add'
	from /Users/luke/workspace/ruby-dev/ruby-build-debug/.ext/common/socket.rb:978:in 'Socket.resolve_hostname'
	from /Users/luke/workspace/ruby-dev/ruby-build-debug/.ext/common/socket.rb:719:in 'block (2 levels) in Socket.tcp_with_fast_fallback'
```
2025-12-15 11:48:34 -05:00
Nobuyoshi Nakada
beb85e7eee
[Bug #21705] Fix segfaults on Windows
It should check the type of the argument and coercion before
converting the encoding.
2025-11-23 13:55:15 +09:00
Nobuyoshi Nakada
3dd39134cd Win32: Drop support for older than MSVC 9.0/_MSC_VER 1500
Visual C++ 2008 (9.0):
- _MSC_VER: 1500
- MSVCRT_VERSION: 90
2025-11-19 11:03:42 +09:00
Nobuyoshi Nakada
cdb9893c55 Win32: Drop support for older than MSVC 8.0/_MSC_VER 1400
Visual C++ 2005 (8.0):
- _MSC_VER: 1400
- MSVCRT_VERSION: 80
2025-11-19 11:03:42 +09:00
Luke Gruber
f100298e28
ext/socket: Set raddrinfo thread as detached before thread start (#15142)
We were seeing segfaults when calling `pthread_detach`. Apparently in
some versions of glibc there is a race between when this is called
(usually right after starting a thread) and a short-lived thread's
shutdown routine. The bug has been reported to glibc:

https://sourceware.org/bugzilla/show_bug.cgi?id=19951

I haven't been able to reproduce it on my Linux desktop but apparently
it's easier to reproduce on certain kinds of servers.

As a workaround, we can set the thread's detach state before thread
start. I don't know of a platform that doesn't have
`pthread_attr_setdetachstate`, but to be safe we check for it in
`extconf.rb` and use `pthread_detach` as a backup if it isn't available.

Fixes [Bug #21679]
2025-11-13 16:42:38 -05:00
Satoshi Tagomori
aaa1234702 update referenced filenames from namespace to box 2025-11-07 13:14:54 +09:00
Nobuyoshi Nakada
89056f4a86 [DOC] Stop documentation for internals
Stop documentation for undocumented private constants and private
class methods.  And align private method definition styles.

Also `Socket.tcp_with_fast_fallback` looks private as well as
`Socket.tcp_without_fast_fallback`.
2025-11-06 20:09:12 +09:00
Nobuyoshi Nakada
8c95c9d5ae Dispatch by platform at load
`RUBY_PLATFORM` should be invariant within the same process.
2025-11-06 20:09:12 +09:00
niku
36e7db00c9 [DOC] Tweaks for TCPSocket.new 2025-10-23 09:16:15 +09:00
Nobuyoshi Nakada
717ad9f41e
Remove an unused expression 2025-09-13 17:10:44 +09:00
Nobuyoshi Nakada
91e5647115
Get rid of strcpy
On OpenBSD:

```
ld: warning: namespace.c:731(namespace.o:(rb_namespace_local_extension)): warning: strcpy() is almost always misused, please use strlcpy()
```
2025-09-13 16:32:30 +09:00
Jean Boussier
9bc53dca08 Cast down to socklen_t explicitly in rb_getnameinfo
Similar to 19f3793a4bd6974cd66cc058fc6d2ae733337745

Fixes:

```
../../../ext/socket/raddrinfo.c:755:60: warning: implicit conversion loses integer precision: 'size_t' (aka 'unsigned long') to 'socklen_t' (aka 'unsigned int') [-Wshorten-64-to-32]
  755 |         return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
      |                ~~~~~~~~~~~                                 ^~~~~~~
../../../ext/socket/raddrinfo.c:755:45: warning: implicit conversion loses integer precision: 'size_t' (aka 'unsigned long') to 'socklen_t' (aka 'unsigned int') [-Wshorten-64-to-32]
  755 |         return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
      |                ~~~~~~~~~~~                  ^~~~~~~
```
2025-08-25 19:14:30 +02:00
Nobuyoshi Nakada
19f3793a4b
Cast down to socklen_t explicitly 2025-08-25 12:07:55 +09:00
John Hawthorn
5c96bbf36a Avoid spawning thread for trivial getnameinfo calls
When calling getnameinfo we spawn a thread because it may do a slow,
blocking reverse-DNS lookup. Spawning a thread is relatively fast (~20µs
on my Linux machine) but still an order of magnitude slower than when
getnameinfo is simply translating to a numeric IP or port, which, at
least in my tests on Linux, doesn't even make a syscall.

This commit adds a fast path for when reverse DNS isn't required: either
host isn't being fetched or NI_NUMERICHOST is set AND either the
service name isn't required or NI_NUMERICSERV is set. The service name
should only need to read /etc/services, which should be fast-ish, but
is still I/O so I kept the existing behaviour (it could be on a network
fs I guess).

I tested with:

    s = TCPSocket.open("www.ruby-lang.org", 80)
    500_000.times { Socket.unpack_sockaddr_in(s.getpeername) }

Before: 12.935s
After: 0.338s
2025-08-20 11:00:03 -07:00
Yusuke Endoh
7e64a68252 Prevent a warning: old-style function definition
ipsocket.c:57:1: warning: old-style function definition [-Wold-style-definition]
   57 | current_clocktime()
      | ^~~~~~~~~~~~~~~~~
2025-07-23 16:46:01 +09:00
Daisuke Aritomo
a46309d19a
Flag rsock_raise_user_specified_timeout() as NORETURN (#13928)
This suppresses this warning:

../../../ext/socket/ipsocket.c: In function ‘rsock_raise_user_specified_timeout’:
../../../ext/socket/ipsocket.c:30:1: warning: function might be candidate for attribute ‘noreturn’ [-Wsuggest-attribute=noreturn]
   30 | rsock_raise_user_specified_timeout()
      | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2025-07-17 23:01:57 +09:00
Nobuyoshi Nakada
10a2688fc4
Declare rsock_raise_user_specified_timeout as noreturn 2025-07-17 19:47:18 +09:00
Misaki Shioi
ba490059b4
[Feature #21347] Add open_timeout as an overall timeout option for TCPSocket.new (#13909)
* [Feature #21347] Add `open_timeout` as an overall timeout option for `TCPSocket.new`

With this change, `TCPSocket.new` now accepts the `open_timeout` option.
This option raises an exception if the specified number of seconds has elapsed since the start of the method call,
even if the operation is still in the middle of name resolution or connection attempts.

The addition of this option follows the same intent as the previously merged change to `Socket.tcp`.
[Feature #21347](https://bugs.ruby-lang.org/issues/21347)
https://github.com/ruby/ruby/pull/13368

* Tidy up: Extract rsock_raise_user_specified_timeout()

* Added a note to the documentation of `Socket.tcp`

* Fix `rsock_init_inetsock` for `FAST_FALLBACK_INIT_INETSOCK_IMPL`
2025-07-17 18:15:19 +09:00
Daisuke Aritomo
b896f80598
[Bug #21512] Socket.tcp_with_fast_fallback: Pass proper addr family to getaddrinfo (#13878)
Socket.tcp_with_fast_fallback: Pass proper addr family to getaddrinfo

Addrinfo.getaddrinfo expects Socket::AF_INET or Socket::AF_INET6 as its
third argument (family). However Socket.tcp_with_fast_fallback was
incorrectly passing :ipv4 or :ipv6.

Repro:

    require 'socket'
    Socket.tcp_with_fast_fallback('example.com', 80, '127.0.0.1')

Expected behavior: Returns a Socket object
Actual: Raises unknown socket domain: ipv4 (SocketError)
2025-07-15 20:38:57 +09:00
Jeremy Evans
0b23a8db60 Update dependencies for addition of set.h to public headers 2025-07-11 15:24:23 +09:00
Misaki Shioi
0058bee57e
Fix heap-use-after-free in rb_getaddrinfo (#13856)
This change addresses the following ASAN error:

```
==1973462==ERROR: AddressSanitizer: heap-use-after-free on address 0x5110002117dc at pc 0x749c307c8a65 bp 0x7ffc3af331d0 sp 0x7ffc3af331c8
READ of size 4 at 0x5110002117dc thread T0
    #0 0x749c307c8a64 in rb_getaddrinfo /tmp/ruby/src/trunk_asan/ext/socket/raddrinfo.c:564:14
    #1 0x749c307c8a64 in rsock_getaddrinfo /tmp/ruby/src/trunk_asan/ext/socket/raddrinfo.c:1008:21
    #2 0x749c307cac48 in rsock_addrinfo /tmp/ruby/src/trunk_asan/ext/socket/raddrinfo.c:1049:12
    #3 0x749c307b10ae in init_inetsock_internal /tmp/ruby/src/trunk_asan/ext/socket/ipsocket.c:62:23
    #4 0x562c5b2e327e in rb_ensure /tmp/ruby/src/trunk_asan/eval.c:1080:18
    #5 0x749c307aafd4 in rsock_init_inetsock /tmp/ruby/src/trunk_asan/ext/socket/ipsocket.c:1318:12
    #6 0x749c307b3b78 in tcp_svr_init /tmp/ruby/src/trunk_asan/ext/socket/tcpserver.c:39:12
```

Fixed to avoid accessing memory that has already been freed after calling `free_getaddrinfo_arg`.
2025-07-11 14:47:18 +09:00
Misaki Shioi
cdeb9c4d70
Fix timeout in Addrinfo.getaddrinfo to actually take effect (#13803)
[Bug #21506] Fix timeout in Addrinfo.getaddrinfo to actually take effect

This change fixes an issue where the timeout option in `Addrinfo.getaddrinfo` was not functioning as expected.

It also addresses a related issue where specifying `fast_fallback: false` with `resolv_timeout` for `Socket.tcp` and`TCPSocket.new` would have no effect.

The timeout option was originally introduced in:
6382f5cc91

However, the value was noy used in current implementation:
3f0e0d5c8b/ext/socket/raddrinfo.c (L1282-1308)

Therefore, even if a timeout is specified and the duration elapses during name resolution, nothing happens. This is clearly not the intended behavior.

`Addrinfo.getaddrinfo` has been made interruptible as of Feature #19965.
This change uses that feature to interrupt name resolution when the specified timeout period elapses, raising a user-specified timeout error.
The timeout can be specified in milliseconds.

The same issue affects `Socket.tcp` and `TCPSocket.new` when `resolv_timeout` is set along with `fast_fallback: false`.
`resolv_timeout` was introduced in the following commits:
6382f5cc91
511fe23fa2

The reason is that with `fast_fallback: false`, these methods internally call the same `rsock_getaddrinfo()` as `Addrinfo.getaddrinfo`.
This change addresses that as well.
2025-07-10 21:35:13 +09:00
Nobuyoshi Nakada
4592d63739
Suppress a warning in code for SOCKS5 2025-07-03 21:44:08 +09:00
Z. Liu
d77e02bd85
[Bug #21497] [ruby/socket]: add full prototype
otherwise, gcc 15 will complain:

> init.c:573:19: error: too many arguments to function ‘Rconnect’; expected 0, have 3
>   573 |     return (VALUE)Rconnect(arg->fd, arg->sockaddr, arg->len);
>       |                   ^~~~~~~~ ~~~~~~~
> In file included from init.c:11:
> rubysocket.h:294:5: note: declared here
>   294 | int Rconnect();
>       |     ^~~~~~~~

> sockssocket.c:33:9: error: too many arguments to function ‘SOCKSinit’; expected 0, have 1
>    33 |         SOCKSinit("ruby");
>       |         ^~~~~~~~~ ~~~~~~
> In file included from sockssocket.c:11:
> rubysocket.h:293:6: note: declared here
>   293 | void SOCKSinit();
>       |      ^~~~~~~~~

Signed-off-by: Z. Liu <zhixu.liu@gmail.com>
2025-07-02 01:09:52 +00:00
Misaki Shioi
c45c600e22
Add open_timeout as an overall timeout option for Socket.tcp (#13368)
* Add `open_timeout` as an overall timeout option for `Socket.tcp`

[Background]
Currently, `TCPSocket.new` and `Socket.tcp` accept two kind of timeout options:
- `resolv_timeout`, which controls the timeout for DNS resolution
- `connect_timeout`, which controls the timeout for the connection attempt

With the introduction of Happy Eyeballs Version 2 (as per [RFC 8305](https://datatracker.ietf.org/doc/html/rfc8305)) in[ Feature #20108](https://bugs.ruby-lang.org/issues/20108) and [Feature #20782](https://bugs.ruby-lang.org/issues/20782), both address resolution and connection attempts are now parallelized.
As a result, the sum of `resolv_timeout` and `connect_timeout` no longer represents the total timeout duration. This is because, in HEv2, name resolution and connection attempts are performed concurrently, causing the two timeouts to overlap.

Example:
When `resolv_timeout: 200ms` and `connect_timeout: 100ms` are set:
1. An IPv6 address  is resolved after the method starts immediately (IPv4 is still being resolved).
2. A connection attempt is initiated to the IPv6 address
3. After 100ms, `connect_timeout` is exceeded. However, since `resolv_timeout` still has 100ms left, the IPv4 resolution continues.
4. After 200ms from the start, the method raises a `resolv_timeout` error.

In this case, the total elapsed time before a timeout is 200ms, not the expected 300ms (100ms + 200ms).

Furthermore, in HEv2, connection attempts are also parallelized.
It starts a new connection attempts every 250ms for resolved addresses. This makes the definition of `connect_timeout` even more ambiguous—specifically, it becomes unclear from which point the timeout is counted.

Additionally, these methods initiate new connection attempts every 250ms (Connection Attempt Delay) for each candidate address, thereby parallelizing connection attempts. However, this behavior makes it unclear from which point in time the connect_timeout is actually measured.
Currently, a `connect_timeout` is raised only after the last connection attempt exceeds the timeout.

Example:
When `connect_timeout: 100ms` is set and 3 address candidates:
1. Start a connection attempt to the address `a`
2. 250ms after step 1, start a new connection attempt to the address `b`
3. 500ms after step 1, start a new connection attempt to the address `c`
4. 1000ms after step 3 (1000ms after starting the connection to `c`, 1250ms after starting the connection to `b,` and 1500ms after starting the connection to `a`) `connect_timeout` is raised

This behavior aims to favor successful connections by allowing more time for each attempt, but it results in a timeout model that is difficult to reason about.

These methods have supported `resolv_timeout` and `connect_timeout` options even before the introduction of HEv2. However, in many use cases, it would be more convenient if a timeout occurred after a specified duration from the start of the method. Similar functions in other languages (such as PHP, Python, and Go) typically allow specifying only an overall timeout.

[Proposal]
I propose adding an `open_timeout` option to `Socket.tcp` in this PR, which triggers a timeout after a specified duration has elapsed from the start of the method.

The name `open_timeout` aligns with the existing accessor used in `Net::HTTP`.
If `open_timeout` is specified together with `resolv_timeout` and `connect_timeout`, I propose that only `open_timeout` be used and the others be ignored. While it is possible to support combinations of `open_timeout`, `resolv_timeout`, and `connect_timeout`, doing so would require defining which timeout takes precedence in which situations. In this case, I believe it is more valuable to keep the behavior simple and easy to understand, rather than supporting more complex use cases.

If this proposal is accepted, I also plan to extend `open_timeout` support to `TCPSocket.new`.

While the long-term future of `resolv_timeout` and `connect_timeout` may warrant further discussion, I believe the immediate priority is to offer a straightforward way to specify an overall timeout.

[Outcome]
If `open_timeout` is also supported by `TCPSocket.new`, users would be able to manage total connection timeouts directly in `Net::HTTP#connect` without relying on `Timeout.timeout`.
aa0f689bf4/lib/net/http.rb (L1657)

---

* Raise an exception if it is specified together with other timeout options

> If open_timeout is specified together with resolv_timeout and connect_timeout, I propose that only open_timeout be used and the others be ignored.

Since this approach may be unclear to users, I’ve decided to explicitly raise an `ArgumentError` if these options are specified together.

* Add doc

* Fix: open_timeout error should be raised even if there are still addresses that have not been tried
2025-06-14 09:54:34 +09:00