diff options
Diffstat (limited to 'ext/socket/socket.c')
| -rw-r--r-- | ext/socket/socket.c | 226 |
1 files changed, 85 insertions, 141 deletions
diff --git a/ext/socket/socket.c b/ext/socket/socket.c index c780d77cf6..7902d3b606 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: */ @@ -917,134 +917,6 @@ make_addrinfo(struct rb_addrinfo *res0, int norevlookup) return base; } -static VALUE -sock_sockaddr(struct sockaddr *addr, socklen_t len) -{ - char *ptr; - - switch (addr->sa_family) { - case AF_INET: - ptr = (char*)&((struct sockaddr_in*)addr)->sin_addr.s_addr; - len = (socklen_t)sizeof(((struct sockaddr_in*)addr)->sin_addr.s_addr); - break; -#ifdef AF_INET6 - case AF_INET6: - ptr = (char*)&((struct sockaddr_in6*)addr)->sin6_addr.s6_addr; - len = (socklen_t)sizeof(((struct sockaddr_in6*)addr)->sin6_addr.s6_addr); - break; -#endif - default: - rb_raise(rb_eSocket, "unknown socket family:%d", addr->sa_family); - break; - } - return rb_str_new(ptr, len); -} - -/* - * call-seq: - * Socket.gethostbyname(hostname) => [official_hostname, alias_hostnames, address_family, *address_list] - * - * Use Addrinfo.getaddrinfo instead. - * This method is deprecated for the following reasons: - * - * - The 3rd element of the result is the address family of the first address. - * The address families of the rest of the addresses are not returned. - * - Uncommon address representation: - * 4/16-bytes binary string to represent IPv4/IPv6 address. - * - gethostbyname() may take a long time and it may block other threads. - * (GVL cannot be released since gethostbyname() is not thread safe.) - * - This method uses gethostbyname() function already removed from POSIX. - * - * This method obtains the host information for _hostname_. - * - * p Socket.gethostbyname("hal") #=> ["localhost", ["hal"], 2, "\x7F\x00\x00\x01"] - * - */ -static VALUE -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); - return rsock_make_hostent(host, res, sock_sockaddr); -} - -/* - * call-seq: - * Socket.gethostbyaddr(address_string [, address_family]) => hostent - * - * Use Addrinfo#getnameinfo instead. - * This method is deprecated for the following reasons: - * - * - Uncommon address representation: - * 4/16-bytes binary string to represent IPv4/IPv6 address. - * - gethostbyaddr() may take a long time and it may block other threads. - * (GVL cannot be released since gethostbyname() is not thread safe.) - * - This method uses gethostbyname() function already removed from POSIX. - * - * This method obtains the host information for _address_. - * - * p Socket.gethostbyaddr([221,186,184,68].pack("CCCC")) - * #=> ["carbon.ruby-lang.org", [], 2, "\xDD\xBA\xB8D"] - * - * p Socket.gethostbyaddr([127,0,0,1].pack("CCCC")) - * ["localhost", [], 2, "\x7F\x00\x00\x01"] - * p Socket.gethostbyaddr(([0]*15+[1]).pack("C"*16)) - * #=> ["localhost", ["ip6-localhost", "ip6-loopback"], 10, - * "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"] - * - */ -static VALUE -sock_s_gethostbyaddr(int argc, VALUE *argv, VALUE _) -{ - VALUE addr, family; - struct hostent *h; - char **pch; - VALUE ary, names; - int t = AF_INET; - - rb_warn("Socket.gethostbyaddr is deprecated; use Addrinfo#getnameinfo instead."); - - rb_scan_args(argc, argv, "11", &addr, &family); - StringValue(addr); - if (!NIL_P(family)) { - t = rsock_family_arg(family); - } -#ifdef AF_INET6 - else if (RSTRING_LEN(addr) == 16) { - t = AF_INET6; - } -#endif - h = gethostbyaddr(RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), t); - if (h == NULL) { -#ifdef HAVE_HSTRERROR - extern int h_errno; - rb_raise(rb_eSocket, "%s", (char*)hstrerror(h_errno)); -#else - rb_raise(rb_eSocket, "host not found"); -#endif - } - ary = rb_ary_new(); - rb_ary_push(ary, rb_str_new2(h->h_name)); - names = rb_ary_new(); - rb_ary_push(ary, names); - if (h->h_aliases != NULL) { - for (pch = h->h_aliases; *pch; pch++) { - rb_ary_push(names, rb_str_new2(*pch)); - } - } - rb_ary_push(ary, INT2NUM(h->h_addrtype)); -#ifdef h_addr - for (pch = h->h_addr_list; *pch; pch++) { - rb_ary_push(ary, rb_str_new(*pch, h->h_length)); - } -#else - rb_ary_push(ary, rb_str_new(h->h_addr, h->h_length)); -#endif - - return ary; -} - /* * call-seq: * Socket.getservbyname(service_name) => port_number @@ -1183,7 +1055,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 +1151,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; } @@ -1335,7 +1207,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 +1728,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 +1917,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); @@ -2010,8 +1953,6 @@ Init_socket(void) rb_define_singleton_method(rb_cSocket, "socketpair", rsock_sock_s_socketpair, -1); rb_define_singleton_method(rb_cSocket, "pair", rsock_sock_s_socketpair, -1); rb_define_singleton_method(rb_cSocket, "gethostname", sock_gethostname, 0); - rb_define_singleton_method(rb_cSocket, "gethostbyname", sock_s_gethostbyname, 1); - rb_define_singleton_method(rb_cSocket, "gethostbyaddr", sock_s_gethostbyaddr, -1); rb_define_singleton_method(rb_cSocket, "getservbyname", sock_s_getservbyname, -1); rb_define_singleton_method(rb_cSocket, "getservbyport", sock_s_getservbyport, -1); rb_define_singleton_method(rb_cSocket, "getaddrinfo", sock_s_getaddrinfo, -1); @@ -2027,6 +1968,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")); } |
