summaryrefslogtreecommitdiff
path: root/ext/socket
diff options
context:
space:
mode:
authorNobuyoshi Nakada <nobu@ruby-lang.org>2024-02-26 12:29:23 +0900
committerNobuyoshi Nakada <nobu@ruby-lang.org>2024-02-26 12:29:23 +0900
commita0f7de814ae5c299d6ce99bed5fb308a05d50ba0 (patch)
tree71b2d940b9186374aead7c226829f894b451bbfb /ext/socket
parent9ec342e07df6aa5e2c2e9003517753a2f1b508fd (diff)
[Bug #20296] Fix the default assertion message
Diffstat (limited to 'ext/socket')
-rw-r--r--ext/socket/lib/socket.rb512
-rw-r--r--ext/socket/mkconstants.rb1
-rw-r--r--ext/socket/rubysocket.h1
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