diff options
Diffstat (limited to 'test/socket/test_socket.rb')
-rw-r--r-- | test/socket/test_socket.rb | 287 |
1 files changed, 269 insertions, 18 deletions
diff --git a/test/socket/test_socket.rb b/test/socket/test_socket.rb index 9807db3674..6a057e866f 100644 --- a/test/socket/test_socket.rb +++ b/test/socket/test_socket.rb @@ -1,9 +1,5 @@ # frozen_string_literal: true -# tentatively disabled due to IPv6 configuration issue on Solaris CI -# http://rubyci.s3.amazonaws.com/solaris10-gcc/ruby-master/log/20210729T040002Z.fail.html.gz -return if /solaris/ =~ RUBY_PLATFORM - begin require "socket" require "tmpdir" @@ -95,20 +91,20 @@ class TestSocket < Test::Unit::TestCase def test_getaddrinfo # This should not send a DNS query because AF_UNIX. - assert_raise(SocketError) { Socket.getaddrinfo("www.kame.net", 80, "AF_UNIX") } + assert_raise(Socket::ResolutionError) { Socket.getaddrinfo("www.kame.net", 80, "AF_UNIX") } end def test_getaddrinfo_raises_no_errors_on_port_argument_of_0 # [ruby-core:29427] assert_nothing_raised('[ruby-core:29427]'){ Socket.getaddrinfo('localhost', 0, Socket::AF_INET, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) } assert_nothing_raised('[ruby-core:29427]'){ Socket.getaddrinfo('localhost', '0', Socket::AF_INET, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) } assert_nothing_raised('[ruby-core:29427]'){ Socket.getaddrinfo('localhost', '00', Socket::AF_INET, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) } - assert_raise(SocketError, '[ruby-core:29427]'){ Socket.getaddrinfo(nil, nil, Socket::AF_INET, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) } + assert_raise(Socket::ResolutionError, '[ruby-core:29427]'){ Socket.getaddrinfo(nil, nil, Socket::AF_INET, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) } assert_nothing_raised('[ruby-core:29427]'){ TCPServer.open('localhost', 0) {} } end def test_getnameinfo - assert_raise(SocketError) { Socket.getnameinfo(["AF_UNIX", 80, "0.0.0.0"]) } + assert_raise(Socket::ResolutionError) { Socket.getnameinfo(["AF_UNIX", 80, "0.0.0.0"]) } assert_raise(ArgumentError) {Socket.getnameinfo(["AF_INET", "http\0", "example.net"])} assert_raise(ArgumentError) {Socket.getnameinfo(["AF_INET", "http", "example.net\0"])} end @@ -344,10 +340,14 @@ class TestSocket < Test::Unit::TestCase end def test_udp_server + # http://rubyci.s3.amazonaws.com/rhel_zlinux/ruby-master/log/20230312T023302Z.fail.html.gz + # Errno::EHOSTUNREACH: No route to host - recvmsg(2) + omit if 'rhel_zlinux' == ENV['RUBYCI_NICKNAME'] + begin ifaddrs = Socket.getifaddrs rescue NotImplementedError - skip "Socket.getifaddrs not implemented" + omit "Socket.getifaddrs not implemented" end ifconfig = nil @@ -441,19 +441,18 @@ class TestSocket < Test::Unit::TestCase } rescue NotImplementedError, Errno::ENOSYS skipped = true - skip "need sendmsg and recvmsg: #{$!}" + omit "need sendmsg and recvmsg: #{$!}" rescue RuntimeError skipped = true - skip "UDP server is no response: #{$!}" + omit "UDP server is no response: #{$!}" ensure if th - if skipped - Thread.kill th unless th.join(10) - else + unless skipped Addrinfo.udp("127.0.0.1", port).connect {|s| s.sendmsg "exit" } - unless th.join(10) - Thread.kill th - th.join(10) + end + unless th.join(10) + th.kill.join(10) + unless skipped raise "thread killed" end end @@ -490,13 +489,14 @@ class TestSocket < Test::Unit::TestCase end while IO.select([r], nil, nil, 0.1).nil? n end - timeout = (defined?(RubyVM::JIT) && RubyVM::JIT.enabled? ? 120 : 30) # for --jit-wait + timeout = (defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? ? 120 : 30) # for --jit-wait assert_equal([[s1],[],[]], IO.select([s1], nil, nil, timeout)) msg, _, _, stamp = s1.recvmsg assert_equal("a", msg) assert(stamp.cmsg_is?(:SOCKET, type)) w.close # stop th n = th.value + th = nil n > 1 and warn "UDP packet loss for #{type} over loopback, #{n} tries needed" t2 = Time.now.strftime("%Y-%m-%d") @@ -505,6 +505,10 @@ class TestSocket < Test::Unit::TestCase t = stamp.timestamp assert_match(pat, t.strftime("%Y-%m-%d")) stamp + ensure + if th and !th.join(10) + th.kill.join(10) + end end end @@ -587,7 +591,7 @@ class TestSocket < Test::Unit::TestCase ensure serv_thread.value.close server.close - end + end unless RUBY_PLATFORM.include?("freebsd") def test_connect_timeout host = "127.0.0.1" @@ -766,4 +770,251 @@ class TestSocket < Test::Unit::TestCase s2.close end + def test_resolurion_error_error_code + begin + Socket.getaddrinfo("example.com", 80, "AF_UNIX") + rescue => e + assert_include([Socket::EAI_FAMILY, Socket::EAI_FAIL], e.error_code) + end + end + + def test_tcp_socket_v6_hostname_resolved_earlier + opts = %w[-rsocket -W1] + assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}" + + begin; + begin + server = TCPServer.new("::1", 0) + rescue Errno::EADDRNOTAVAIL # IPv6 is not supported + exit + end + + server_thread = Thread.new { server.accept } + port = server.addr[1] + + Addrinfo.define_singleton_method(:getaddrinfo) do |_, _, family, *_| + case family + when Socket::AF_INET6 then [Addrinfo.tcp("::1", port)] + when Socket::AF_INET then sleep(10); [Addrinfo.tcp("127.0.0.1", port)] + end + end + + socket = Socket.tcp("localhost", port) + assert_true(socket.remote_address.ipv6?) + server_thread.value.close + server.close + socket.close if socket && !socket.closed? + end; + end + + def test_tcp_socket_v4_hostname_resolved_earlier + opts = %w[-rsocket -W1] + assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}" + + begin; + server = TCPServer.new("127.0.0.1", 0) + port = server.addr[1] + + Addrinfo.define_singleton_method(:getaddrinfo) do |_, _, family, *_| + case family + when Socket::AF_INET6 then sleep(10); [Addrinfo.tcp("::1", port)] + when Socket::AF_INET then [Addrinfo.tcp("127.0.0.1", port)] + end + end + + server_thread = Thread.new { server.accept } + socket = Socket.tcp("localhost", port) + assert_true(socket.remote_address.ipv4?) + server_thread.value.close + server.close + socket.close if socket && !socket.closed? + end; + end + + def test_tcp_socket_v6_hostname_resolved_in_resolution_delay + opts = %w[-rsocket -W1] + assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}" + + begin; + begin + server = TCPServer.new("::1", 0) + rescue Errno::EADDRNOTAVAIL # IPv6 is not supported + exit + end + + port = server.addr[1] + delay_time = 0.025 # Socket::RESOLUTION_DELAY (private) is 0.05 + + Addrinfo.define_singleton_method(:getaddrinfo) do |_, _, family, *_| + case family + when Socket::AF_INET6 then sleep(delay_time); [Addrinfo.tcp("::1", port)] + when Socket::AF_INET then [Addrinfo.tcp("127.0.0.1", port)] + end + end + + server_thread = Thread.new { server.accept } + socket = Socket.tcp("localhost", port) + assert_true(socket.remote_address.ipv6?) + server_thread.value.close + server.close + socket.close if socket && !socket.closed? + end; + end + + def test_tcp_socket_v6_hostname_resolved_earlier_and_v6_server_is_not_listening + opts = %w[-rsocket -W1] + assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}" + + begin; + ipv4_address = "127.0.0.1" + ipv4_server = Socket.new(Socket::AF_INET, :STREAM) + ipv4_server.bind(Socket.pack_sockaddr_in(0, ipv4_address)) + port = ipv4_server.connect_address.ip_port + + Addrinfo.define_singleton_method(:getaddrinfo) do |_, _, family, *_| + case family + when Socket::AF_INET6 then [Addrinfo.tcp("::1", port)] + when Socket::AF_INET then sleep(0.001); [Addrinfo.tcp(ipv4_address, port)] + end + end + + ipv4_server_thread = Thread.new { ipv4_server.listen(1); ipv4_server.accept } + socket = Socket.tcp("localhost", port) + assert_equal(ipv4_address, socket.remote_address.ip_address) + + accepted, _ = ipv4_server_thread.value + accepted.close + ipv4_server.close + socket.close if socket && !socket.closed? + end; + end + + def test_tcp_socket_resolv_timeout + opts = %w[-rsocket -W1] + assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}" + + begin; + Addrinfo.define_singleton_method(:getaddrinfo) { |*_| sleep } + port = TCPServer.new("localhost", 0).addr[1] + + assert_raise(Errno::ETIMEDOUT) do + Socket.tcp("localhost", port, resolv_timeout: 0.01) + end + end; + end + + def test_tcp_socket_resolv_timeout_with_connection_failure + opts = %w[-rsocket -W1] + assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}" + + begin; + server = TCPServer.new("127.0.0.1", 12345) + _, port, = server.addr + + Addrinfo.define_singleton_method(:getaddrinfo) do |_, _, family, *_| + if family == Socket::AF_INET6 + sleep + else + [Addrinfo.tcp("127.0.0.1", port)] + end + end + + server.close + + assert_raise(Errno::ETIMEDOUT) do + Socket.tcp("localhost", port, resolv_timeout: 0.01) + end + end; + end + + def test_tcp_socket_one_hostname_resolution_succeeded_at_least + opts = %w[-rsocket -W1] + assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}" + + begin; + begin + server = TCPServer.new("::1", 0) + rescue Errno::EADDRNOTAVAIL # IPv6 is not supported + exit + end + + port = server.addr[1] + + Addrinfo.define_singleton_method(:getaddrinfo) do |_, _, family, *_| + case family + when Socket::AF_INET6 then [Addrinfo.tcp("::1", port)] + when Socket::AF_INET then sleep(0.001); raise SocketError + end + end + + server_thread = Thread.new { server.accept } + socket = nil + + assert_nothing_raised do + socket = Socket.tcp("localhost", port) + end + + server_thread.value.close + server.close + socket.close if socket && !socket.closed? + end; + end + + def test_tcp_socket_all_hostname_resolution_failed + opts = %w[-rsocket -W1] + assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}" + + begin; + Addrinfo.define_singleton_method(:getaddrinfo) do |_, _, family, *_| + case family + when Socket::AF_INET6 then raise SocketError + when Socket::AF_INET then sleep(0.001); raise SocketError, "Last hostname resolution error" + end + end + port = TCPServer.new("localhost", 0).addr[1] + + assert_raise_with_message(SocketError, "Last hostname resolution error") do + Socket.tcp("localhost", port) + end + end; + end + + def test_tcp_socket_v6_address_passed + opts = %w[-rsocket -W1] + assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}" + + begin; + begin + server = TCPServer.new("::1", 0) + rescue Errno::EADDRNOTAVAIL # IPv6 is not supported + exit + end + + _, port, = server.addr + + Addrinfo.define_singleton_method(:getaddrinfo) do |*_| + [Addrinfo.tcp("::1", port)] + end + + server_thread = Thread.new { server.accept } + socket = Socket.tcp("::1", port) + + assert_true(socket.remote_address.ipv6?) + server_thread.value.close + server.close + socket.close if socket && !socket.closed? + end; + end + + def test_tcp_socket_fast_fallback_is_false + server = TCPServer.new("127.0.0.1", 0) + _, port, = server.addr + server_thread = Thread.new { server.accept } + socket = Socket.tcp("127.0.0.1", port, fast_fallback: false) + + assert_true(socket.remote_address.ipv4?) + server_thread.value.close + server.close + socket.close if socket && !socket.closed? + end end if defined?(Socket) |