diff options
author | Nobuyoshi Nakada <nobu@ruby-lang.org> | 2024-02-26 12:29:23 +0900 |
---|---|---|
committer | Nobuyoshi Nakada <nobu@ruby-lang.org> | 2024-02-26 12:29:23 +0900 |
commit | a0f7de814ae5c299d6ce99bed5fb308a05d50ba0 (patch) | |
tree | 71b2d940b9186374aead7c226829f894b451bbfb /ext/socket | |
parent | 9ec342e07df6aa5e2c2e9003517753a2f1b508fd (diff) |
[Bug #20296] Fix the default assertion message
Diffstat (limited to 'ext/socket')
-rw-r--r-- | ext/socket/lib/socket.rb | 512 | ||||
-rw-r--r-- | ext/socket/mkconstants.rb | 1 | ||||
-rw-r--r-- | ext/socket/rubysocket.h | 1 |
3 files changed, 2 insertions, 512 deletions
diff --git a/ext/socket/lib/socket.rb b/ext/socket/lib/socket.rb index e953077fe6..de542223c6 100644 --- a/ext/socket/lib/socket.rb +++ b/ext/socket/lib/socket.rb @@ -599,30 +599,6 @@ class Socket < BasicSocket __accept_nonblock(exception) end - RESOLUTION_DELAY = 0.05 - private_constant :RESOLUTION_DELAY - - CONNECTION_ATTEMPT_DELAY = 0.25 - private_constant :CONNECTION_ATTEMPT_DELAY - - ADDRESS_FAMILIES = { - ipv6: Socket::AF_INET6, - ipv4: Socket::AF_INET - }.freeze - private_constant :ADDRESS_FAMILIES - - HOSTNAME_RESOLUTION_QUEUE_UPDATED = 0 - private_constant :HOSTNAME_RESOLUTION_QUEUE_UPDATED - - IPV6_ADRESS_FORMAT = /(?i)(?:(?:[0-9A-F]{1,4}:){7}(?:[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){6}(?:[0-9A-F]{1,4}::[0-9A-F]{1,4}|:(?:[0-9A-F]{1,4}:){1,5}[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){5}(?::[0-9A-F]{1,4}::[0-9A-F]{1,4}|:(?:[0-9A-F]{1,4}:){1,4}[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){4}(?::[0-9A-F]{1,4}::[0-9A-F]{1,4}|:(?:[0-9A-F]{1,4}:){1,3}[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){3}(?::[0-9A-F]{1,4}::[0-9A-F]{1,4}|:(?:[0-9A-F]{1,4}:){1,2}[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){2}(?::[0-9A-F]{1,4}::[0-9A-F]{1,4}|:(?:[0-9A-F]{1,4}:)[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){1}(?::[0-9A-F]{1,4}::[0-9A-F]{1,4}|::(?:[0-9A-F]{1,4}:){1,5}[0-9A-F]{1,4}|:)|::(?:[0-9A-F]{1,4}::[0-9A-F]{1,4}|:(?:[0-9A-F]{1,4}:){1,6}[0-9A-F]{1,4}|:))(?:%.+)?/ - private_constant :IPV6_ADRESS_FORMAT - - @tcp_fast_fallback = true - - class << self - attr_accessor :tcp_fast_fallback - end - # :call-seq: # Socket.tcp(host, port, local_host=nil, local_port=nil, [opts]) {|socket| ... } # Socket.tcp(host, port, local_host=nil, local_port=nil, [opts]) @@ -648,491 +624,8 @@ class Socket < BasicSocket # sock.close_write # puts sock.read # } - def self.tcp(host, port, local_host = nil, local_port = nil, connect_timeout: nil, resolv_timeout: nil, fast_fallback: tcp_fast_fallback, &block) # :yield: socket - unless fast_fallback - return tcp_without_fast_fallback(host, port, local_host, local_port, connect_timeout:, resolv_timeout:, &block) - end - - # Happy Eyeballs' states - # - :start - # - :v6c - # - :v4w - # - :v4c - # - :v46c - # - :v46w - # - :success - # - :failure - # - :timeout - - specified_family_name = nil - hostname_resolution_threads = [] - hostname_resolution_queue = nil - hostname_resolution_waiting = nil - hostname_resolution_expires_at = nil - selectable_addrinfos = SelectableAddrinfos.new - connecting_sockets = ConnectingSockets.new - local_addrinfos = [] - connection_attempt_delay_expires_at = nil - connection_attempt_started_at = nil - state = :start - connected_socket = nil - last_error = nil - is_windows_environment ||= (RUBY_PLATFORM =~ /mswin|mingw|cygwin/) - - ret = loop do - case state - when :start - specified_family_name, next_state = host && specified_family_name_and_next_state(host) - - if local_host && local_port - specified_family_name, next_state = specified_family_name_and_next_state(local_host) unless specified_family_name - local_addrinfos = Addrinfo.getaddrinfo(local_host, local_port, ADDRESS_FAMILIES[specified_family_name], :STREAM, timeout: resolv_timeout) - end - - if specified_family_name - addrinfos = Addrinfo.getaddrinfo(host, port, ADDRESS_FAMILIES[specified_family_name], :STREAM, timeout: resolv_timeout) - selectable_addrinfos.add(specified_family_name, addrinfos) - hostname_resolution_queue = NoHostnameResolutionQueue.new - state = next_state - next - end - - resolving_family_names = ADDRESS_FAMILIES.keys - hostname_resolution_queue = HostnameResolutionQueue.new(resolving_family_names.size) - hostname_resolution_waiting = hostname_resolution_queue.waiting_pipe - hostname_resolution_started_at = current_clocktime if resolv_timeout - hostname_resolution_args = [host, port, hostname_resolution_queue] - - hostname_resolution_threads.concat( - resolving_family_names.map { |family| - thread_args = [family, *hostname_resolution_args] - thread = Thread.new(*thread_args) { |*thread_args| hostname_resolution(*thread_args) } - Thread.pass - thread - } - ) - - hostname_resolution_retry_count = resolving_family_names.size - 1 - hostname_resolution_expires_at = hostname_resolution_started_at + resolv_timeout if resolv_timeout - - while hostname_resolution_retry_count >= 0 - remaining = resolv_timeout ? second_to_timeout(hostname_resolution_started_at + resolv_timeout) : nil - hostname_resolved, _, = IO.select(hostname_resolution_waiting, nil, nil, remaining) - - unless hostname_resolved - state = :timeout - break - end - - family_name, res = hostname_resolution_queue.get - - if res.is_a? Exception - unless (Socket.const_defined?(:EAI_ADDRFAMILY)) && - (res.is_a?(Socket::ResolutionError)) && - (res.error_code == Socket::EAI_ADDRFAMILY) - last_error = res - end - - if hostname_resolution_retry_count.zero? - state = :failure - break - end - hostname_resolution_retry_count -= 1 - else - state = case family_name - when :ipv6 then :v6c - when :ipv4 then hostname_resolution_queue.closed? ? :v4c : :v4w - end - selectable_addrinfos.add(family_name, res) - last_error = nil - break - end - end - - next - when :v4w - ipv6_resolved, _, = IO.select(hostname_resolution_waiting, nil, nil, RESOLUTION_DELAY) - - if ipv6_resolved - family_name, res = hostname_resolution_queue.get - selectable_addrinfos.add(family_name, res) unless res.is_a? Exception - state = :v46c - else - state = :v4c - end - - next - when :v4c, :v6c, :v46c - connection_attempt_started_at = current_clocktime unless connection_attempt_started_at - addrinfo = selectable_addrinfos.get - - if local_addrinfos.any? - local_addrinfo = local_addrinfos.find { |lai| lai.afamily == addrinfo.afamily } - - if local_addrinfo.nil? - if selectable_addrinfos.empty? && connecting_sockets.empty? && hostname_resolution_queue.closed? - last_error = SocketError.new 'no appropriate local address' - state = :failure - elsif selectable_addrinfos.any? - # Try other Addrinfo in next loop - else - if resolv_timeout && hostname_resolution_queue.opened? && - (current_clocktime >= hostname_resolution_expires_at) - state = :timeout - else - # Wait for connection to be established or hostname resolution in next loop - connection_attempt_delay_expires_at = nil - state = :v46w - end - end - next - end - end - - connection_attempt_delay_expires_at = current_clocktime + CONNECTION_ATTEMPT_DELAY - - begin - result = if specified_family_name && selectable_addrinfos.empty? && - connecting_sockets.empty? && hostname_resolution_queue.closed? - local_addrinfo ? - addrinfo.connect_from(local_addrinfo, timeout: connect_timeout) : - addrinfo.connect(timeout: connect_timeout) - else - socket = Socket.new(addrinfo.pfamily, addrinfo.socktype, addrinfo.protocol) - socket.bind(local_addrinfo) if local_addrinfo - socket.connect_nonblock(addrinfo, exception: false) - end - - case result - when 0 - connected_socket = socket - state = :success - when Socket - connected_socket = result - state = :success - when :wait_writable - connecting_sockets.add(socket, addrinfo) - state = :v46w - end - rescue SystemCallError => e - last_error = e - socket.close if socket && !socket.closed? - - if selectable_addrinfos.empty? && connecting_sockets.empty? && hostname_resolution_queue.closed? - state = :failure - elsif selectable_addrinfos.any? - # Try other Addrinfo in next loop - else - if resolv_timeout && hostname_resolution_queue.opened? && - (current_clocktime >= hostname_resolution_expires_at) - state = :timeout - else - # Wait for connection to be established or hostname resolution in next loop - connection_attempt_delay_expires_at = nil - state = :v46w - end - end - end - - next - when :v46w - if connect_timeout && second_to_timeout(connection_attempt_started_at + connect_timeout).zero? - state = :timeout - next - end - - remaining = second_to_timeout(connection_attempt_delay_expires_at) - hostname_resolution_waiting = hostname_resolution_waiting && hostname_resolution_queue.closed? ? nil : hostname_resolution_waiting - hostname_resolved, connectable_sockets, = IO.select(hostname_resolution_waiting, connecting_sockets.all, nil, remaining) - - if connectable_sockets&.any? - while (connectable_socket = connectable_sockets.pop) - is_connected = - if is_windows_environment - sockopt = connectable_socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_CONNECT_TIME) - sockopt.unpack('i').first >= 0 - else - sockopt = connectable_socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR) - sockopt.int.zero? - end - - if is_connected - connected_socket = connectable_socket - connecting_sockets.delete connectable_socket - connectable_sockets.each do |other_connectable_socket| - other_connectable_socket.close unless other_connectable_socket.closed? - end - state = :success - break - else - failed_ai = connecting_sockets.delete connectable_socket - inspected_ip_address = failed_ai.ipv6? ? "[#{failed_ai.ip_address}]" : failed_ai.ip_address - last_error = SystemCallError.new("connect(2) for #{inspected_ip_address}:#{failed_ai.ip_port}", sockopt.int) - connectable_socket.close unless connectable_socket.closed? - - next if connectable_sockets.any? - - if selectable_addrinfos.empty? && connecting_sockets.empty? && hostname_resolution_queue.closed? - state = :failure - elsif selectable_addrinfos.any? - # Wait for connection attempt delay timeout in next loop - else - if resolv_timeout && hostname_resolution_queue.opened? && - (current_clocktime >= hostname_resolution_expires_at) - state = :timeout - else - # Wait for connection to be established or hostname resolution in next loop - connection_attempt_delay_expires_at = nil - end - end - end - end - elsif hostname_resolved&.any? - family_name, res = hostname_resolution_queue.get - selectable_addrinfos.add(family_name, res) unless res.is_a? Exception - else - if selectable_addrinfos.empty? && connecting_sockets.empty? && hostname_resolution_queue.closed? - state = :failure - elsif selectable_addrinfos.any? - # Try other Addrinfo in next loop - state = :v46c - else - if resolv_timeout && hostname_resolution_queue.opened? && - (current_clocktime >= hostname_resolution_expires_at) - state = :timeout - else - # Wait for connection to be established or hostname resolution in next loop - connection_attempt_delay_expires_at = nil - end - end - end - - next - when :success - break connected_socket - when :failure - raise last_error - when :timeout - raise Errno::ETIMEDOUT, 'user specified timeout' - end - end - - if block_given? - begin - yield ret - ensure - ret.close - end - else - ret - end - ensure - if fast_fallback - hostname_resolution_threads.each do |thread| - thread&.exit - end - - hostname_resolution_queue&.close_all - - connecting_sockets.each do |connecting_socket| - connecting_socket.close unless connecting_socket.closed? - end - end - end - - def self.specified_family_name_and_next_state(hostname) - if hostname.match?(IPV6_ADRESS_FORMAT) then [:ipv6, :v6c] - elsif hostname.match?(/^([0-9]{1,3}\.){3}[0-9]{1,3}$/) then [:ipv4, :v4c] - end - end - private_class_method :specified_family_name_and_next_state - - def self.hostname_resolution(family, host, port, hostname_resolution_queue) - begin - resolved_addrinfos = Addrinfo.getaddrinfo(host, port, ADDRESS_FAMILIES[family], :STREAM) - hostname_resolution_queue.add_resolved(family, resolved_addrinfos) - rescue => e - hostname_resolution_queue.add_error(family, e) - end - end - private_class_method :hostname_resolution - - def self.second_to_timeout(ends_at) - return 0 unless ends_at - - remaining = (ends_at - current_clocktime) - remaining.negative? ? 0 : remaining - end - private_class_method :second_to_timeout - - def self.current_clocktime - Process.clock_gettime(Process::CLOCK_MONOTONIC) - end - private_class_method :current_clocktime - - class SelectableAddrinfos - PRIORITY_ON_V6 = [:ipv6, :ipv4] - PRIORITY_ON_V4 = [:ipv4, :ipv6] - - def initialize - @addrinfo_dict = {} - @last_family = nil - end - - def add(family_name, addrinfos) - @addrinfo_dict[family_name] = addrinfos - end - - def get - return nil if empty? - - if @addrinfo_dict.size == 1 - @addrinfo_dict.each { |_, addrinfos| return addrinfos.shift } - end - - precedences = case @last_family - when :ipv4, nil then PRIORITY_ON_V6 - when :ipv6 then PRIORITY_ON_V4 - end - - precedences.each do |family_name| - addrinfo = @addrinfo_dict[family_name]&.shift - next unless addrinfo - - @last_family = family_name - return addrinfo - end - end - - def empty? - @addrinfo_dict.all? { |_, addrinfos| addrinfos.empty? } - end - - def any? - !empty? - end - end - private_constant :SelectableAddrinfos - - class NoHostnameResolutionQueue - def waiting_pipe - nil - end - - def add_resolved(_, _) - raise StandardError, "This #{self.class} cannot respond to:" - end - - def add_error(_, _) - raise StandardError, "This #{self.class} cannot respond to:" - end - - def get - nil - end - - def opened? - false - end - - def closed? - true - end - - def close_all - # Do nothing - end - end - private_constant :NoHostnameResolutionQueue - - class HostnameResolutionQueue - def initialize(size) - @size = size - @taken_count = 0 - @rpipe, @wpipe = IO.pipe - @queue = Queue.new - @mutex = Mutex.new - end - - def waiting_pipe - [@rpipe] - end - - def add_resolved(family, resolved_addrinfos) - @mutex.synchronize do - @queue.push [family, resolved_addrinfos] - @wpipe.putc HOSTNAME_RESOLUTION_QUEUE_UPDATED - end - end - - def add_error(family, error) - @mutex.synchronize do - @queue.push [family, error] - @wpipe.putc HOSTNAME_RESOLUTION_QUEUE_UPDATED - end - end - - def get - return nil if @queue.empty? - - res = nil - - @mutex.synchronize do - @rpipe.getbyte - res = @queue.pop - end - - @taken_count += 1 - close_all if @taken_count == @size - res - end - - def closed? - @rpipe.closed? - end - - def opened? - !closed? - end - - def close_all - @queue.close unless @queue.closed? - @rpipe.close unless @rpipe.closed? - @wpipe.close unless @wpipe.closed? - end - end - private_constant :HostnameResolutionQueue - - class ConnectingSockets - def initialize - @socket_dict = {} - end - - def all - @socket_dict.keys - end - - def add(socket, addrinfo) - @socket_dict[socket] = addrinfo - end - - def delete(socket) - @socket_dict.delete socket - end - - def empty? - @socket_dict.empty? - end - - def each - @socket_dict.keys.each do |socket| - yield socket - end - end - end - private_constant :ConnectingSockets - - def self.tcp_without_fast_fallback(host, port, local_host, local_port, connect_timeout:, resolv_timeout:, &block) + # + def self.tcp(host, port, local_host = nil, local_port = nil, connect_timeout: nil, resolv_timeout: nil) # :yield: socket last_error = nil ret = nil @@ -1176,7 +669,6 @@ class Socket < BasicSocket ret end end - private_class_method :tcp_without_fast_fallback # :stopdoc: def self.ip_sockets_port0(ai_list, reuseaddr) diff --git a/ext/socket/mkconstants.rb b/ext/socket/mkconstants.rb index 04719fff28..9150c3ff67 100644 --- a/ext/socket/mkconstants.rb +++ b/ext/socket/mkconstants.rb @@ -669,7 +669,6 @@ SO_SETFIB nil Set the associated routing table for the socket (FreeBSD SO_RTABLE nil Set the routing table for this socket (OpenBSD) SO_INCOMING_CPU nil Receive the cpu attached to the socket (Linux 3.19) SO_INCOMING_NAPI_ID nil Receive the napi ID attached to a RX queue (Linux 4.12) -SO_CONNECT_TIME nil Returns the number of seconds a socket has been connected. This option is only valid for connection-oriented protocols (Windows) SOPRI_INTERACTIVE nil Interactive socket priority SOPRI_NORMAL nil Normal socket priority diff --git a/ext/socket/rubysocket.h b/ext/socket/rubysocket.h index f486db4262..283735b12c 100644 --- a/ext/socket/rubysocket.h +++ b/ext/socket/rubysocket.h @@ -35,7 +35,6 @@ #ifdef _WIN32 # include <winsock2.h> # include <ws2tcpip.h> -# include <mswsock.h> # include <iphlpapi.h> # if defined(_MSC_VER) # undef HAVE_TYPE_STRUCT_SOCKADDR_DL |