summaryrefslogtreecommitdiff
path: root/ext/socket/socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/socket/socket.c')
-rw-r--r--ext/socket/socket.c100
1 files changed, 87 insertions, 13 deletions
diff --git a/ext/socket/socket.c b/ext/socket/socket.c
index 74cb0644e6..a8e5ae8119 100644
--- a/ext/socket/socket.c
+++ b/ext/socket/socket.c
@@ -14,6 +14,8 @@ static VALUE sym_wait_writable;
static VALUE sock_s_unpack_sockaddr_in(VALUE, VALUE);
+ID tcp_fast_fallback;
+
void
rsock_sys_fail_host_port(const char *mesg, VALUE host, VALUE port)
{
@@ -387,22 +389,20 @@ rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass)
* * connect function in Microsoft's Winsock functions reference
*/
static VALUE
-sock_connect(VALUE sock, VALUE addr)
+sock_connect(VALUE self, VALUE addr)
{
VALUE rai;
- rb_io_t *fptr;
- int fd, n;
SockAddrStringValueWithAddrinfo(addr, rai);
addr = rb_str_new4(addr);
- GetOpenFile(sock, fptr);
- fd = fptr->fd;
- n = rsock_connect(fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), 0, NULL);
- if (n < 0) {
+
+ int result = rsock_connect(self, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), 0, RUBY_IO_TIMEOUT_DEFAULT);
+
+ if (result < 0) {
rsock_sys_fail_raddrinfo_or_sockaddr("connect(2)", addr, rai);
}
- return INT2FIX(n);
+ return INT2FIX(result);
}
/* :nodoc: */
@@ -965,7 +965,7 @@ sock_s_gethostbyname(VALUE obj, VALUE host)
{
rb_warn("Socket.gethostbyname is deprecated; use Addrinfo.getaddrinfo instead.");
struct rb_addrinfo *res =
- rsock_addrinfo(host, Qnil, AF_UNSPEC, SOCK_STREAM, AI_CANONNAME);
+ rsock_addrinfo(host, Qnil, AF_UNSPEC, SOCK_STREAM, AI_CANONNAME, Qnil);
return rsock_make_hostent(host, res, sock_sockaddr);
}
@@ -1183,7 +1183,7 @@ sock_s_getaddrinfo(int argc, VALUE *argv, VALUE _)
norevlookup = rsock_do_not_reverse_lookup;
}
- res = rsock_getaddrinfo(host, port, &hints, 0);
+ res = rsock_getaddrinfo(host, port, &hints, 0, Qnil);
ret = make_addrinfo(res, norevlookup);
rb_freeaddrinfo(res);
@@ -1279,7 +1279,7 @@ sock_s_getnameinfo(int argc, VALUE *argv, VALUE _)
hints.ai_socktype = (fl & NI_DGRAM) ? SOCK_DGRAM : SOCK_STREAM;
/* af */
hints.ai_family = NIL_P(af) ? PF_UNSPEC : rsock_family_arg(af);
- res = rsock_getaddrinfo(host, port, &hints, 0);
+ res = rsock_getaddrinfo(host, port, &hints, 0, Qnil);
sap = res->ai->ai_addr;
salen = res->ai->ai_addrlen;
}
@@ -1313,7 +1313,7 @@ sock_s_getnameinfo(int argc, VALUE *argv, VALUE _)
saved_errno = errno;
if (res) rb_freeaddrinfo(res);
errno = saved_errno;
- rsock_raise_socket_error("getnameinfo", error);
+ rsock_raise_resolution_error("getnameinfo", error);
UNREACHABLE_RETURN(Qnil);
}
@@ -1335,7 +1335,7 @@ sock_s_getnameinfo(int argc, VALUE *argv, VALUE _)
static VALUE
sock_s_pack_sockaddr_in(VALUE self, VALUE port, VALUE host)
{
- struct rb_addrinfo *res = rsock_addrinfo(host, port, AF_UNSPEC, 0, 0);
+ struct rb_addrinfo *res = rsock_addrinfo(host, port, AF_UNSPEC, 0, 0, Qnil);
VALUE addr = rb_str_new((char*)res->ai->ai_addr, res->ai->ai_addrlen);
rb_freeaddrinfo(res);
@@ -1856,6 +1856,67 @@ socket_s_ip_address_list(VALUE self)
#define socket_s_ip_address_list rb_f_notimplement
#endif
+/*
+ * call-seq:
+ * Socket.tcp_fast_fallback -> true or false
+ *
+ * Returns whether Happy Eyeballs Version 2 ({RFC 8305}[https://datatracker.ietf.org/doc/html/rfc8305]),
+ * which is provided starting from Ruby 3.4 when using TCPSocket.new and Socket.tcp,
+ * is enabled or disabled.
+ *
+ * If true, it is enabled for TCPSocket.new and Socket.tcp.
+ * (Note: Happy Eyeballs Version 2 is not provided when using TCPSocket.new on Windows.)
+ *
+ * If false, Happy Eyeballs Version 2 is disabled.
+ *
+ * For details on Happy Eyeballs Version 2,
+ * see {Socket.tcp_fast_fallback=}[rdoc-ref:Socket.tcp_fast_fallback=].
+ */
+VALUE socket_s_tcp_fast_fallback(VALUE self) {
+ return rb_ivar_get(rb_cSocket, tcp_fast_fallback);
+}
+
+/*
+ * call-seq:
+ * Socket.tcp_fast_fallback= -> true or false
+ *
+ * Enable or disable Happy Eyeballs Version 2 ({RFC 8305}[https://datatracker.ietf.org/doc/html/rfc8305])
+ * globally, which is provided starting from Ruby 3.4 when using TCPSocket.new and Socket.tcp.
+ *
+ * When set to true, the feature is enabled for both `TCPSocket.new` and `Socket.tcp`.
+ * (Note: This feature is not available when using TCPSocket.new on Windows.)
+ *
+ * When set to false, the behavior reverts to that of Ruby 3.3 or earlier.
+ *
+ * The default value is true if no value is explicitly set by calling this method.
+ * However, when the environment variable RUBY_TCP_NO_FAST_FALLBACK=1 is set,
+ * the default is false.
+ *
+ * To control the setting on a per-method basis, use the fast_fallback keyword argument for each method.
+ *
+ * === Happy Eyeballs Version 2
+ * Happy Eyeballs Version 2 ({RFC 8305}[https://datatracker.ietf.org/doc/html/rfc8305])
+ * is an algorithm designed to improve client socket connectivity.<br>
+ * It aims for more reliable and efficient connections by performing hostname resolution
+ * and connection attempts in parallel, instead of serially.
+ *
+ * Starting from Ruby 3.4, this method operates as follows with this algorithm:
+ *
+ * 1. Start resolving both IPv6 and IPv4 addresses concurrently.
+ * 2. Start connecting to the one of the addresses that are obtained first.<br>If IPv4 addresses are obtained first,
+ * the method waits 50 ms for IPv6 name resolution to prioritize IPv6 connections.
+ * 3. After starting a connection attempt, wait 250 ms for the connection to be established.<br>
+ * If no connection is established within this time, a new connection is started every 250 ms<br>
+ * until a connection is established or there are no more candidate addresses.<br>
+ * (Although RFC 8305 strictly specifies sorting addresses,<br>
+ * this method only alternates between IPv6 / IPv4 addresses due to the performance concerns)
+ * 4. Once a connection is established, all remaining connection attempts are canceled.
+ */
+VALUE socket_s_tcp_fast_fallback_set(VALUE self, VALUE value) {
+ rb_ivar_set(rb_cSocket, tcp_fast_fallback, value);
+ return value;
+}
+
void
Init_socket(void)
{
@@ -1984,6 +2045,16 @@ Init_socket(void)
rsock_init_socket_init();
+ const char *tcp_no_fast_fallback_config = getenv("RUBY_TCP_NO_FAST_FALLBACK");
+ VALUE fast_fallback_default;
+ if (tcp_no_fast_fallback_config == NULL || strcmp(tcp_no_fast_fallback_config, "0") == 0) {
+ fast_fallback_default = Qtrue;
+ } else {
+ fast_fallback_default = Qfalse;
+ }
+ tcp_fast_fallback = rb_intern_const("tcp_fast_fallback");
+ rb_ivar_set(rb_cSocket, tcp_fast_fallback, fast_fallback_default);
+
rb_define_method(rb_cSocket, "initialize", sock_initialize, -1);
rb_define_method(rb_cSocket, "connect", sock_connect, 1);
@@ -2027,6 +2098,9 @@ Init_socket(void)
rb_define_singleton_method(rb_cSocket, "ip_address_list", socket_s_ip_address_list, 0);
+ rb_define_singleton_method(rb_cSocket, "tcp_fast_fallback", socket_s_tcp_fast_fallback, 0);
+ rb_define_singleton_method(rb_cSocket, "tcp_fast_fallback=", socket_s_tcp_fast_fallback_set, 1);
+
#undef rb_intern
sym_wait_writable = ID2SYM(rb_intern("wait_writable"));
}