summaryrefslogtreecommitdiff
path: root/spec/ruby/library/socket/fixtures
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/library/socket/fixtures')
-rw-r--r--spec/ruby/library/socket/fixtures/classes.rb91
1 files changed, 82 insertions, 9 deletions
diff --git a/spec/ruby/library/socket/fixtures/classes.rb b/spec/ruby/library/socket/fixtures/classes.rb
index b8e5d2a38d..786629d2ef 100644
--- a/spec/ruby/library/socket/fixtures/classes.rb
+++ b/spec/ruby/library/socket/fixtures/classes.rb
@@ -1,16 +1,16 @@
require 'socket'
module SocketSpecs
- # helper to get the hostname associated to 127.0.0.1
- def self.hostname
+ # helper to get the hostname associated to 127.0.0.1 or the given ip
+ def self.hostname(ip = "127.0.0.1")
# Calculate each time, without caching, since the result might
# depend on things like do_not_reverse_lookup mode, which is
# changing from test to test
- Socket.getaddrinfo("127.0.0.1", nil)[0][2]
+ Socket.getaddrinfo(ip, nil)[0][2]
end
- def self.hostnamev6
- Socket.getaddrinfo("::1", nil)[0][2]
+ def self.hostname_reverse_lookup(ip = "127.0.0.1")
+ Socket.getaddrinfo(ip, nil, 0, 0, 0, 0, true)[0][2]
end
def self.addr(which=:ipv4)
@@ -34,10 +34,12 @@ module SocketSpecs
def self.socket_path
path = tmp("unix.sock", false)
- # Check for too long unix socket path (max 108 bytes including \0 => 107)
+ # Check for too long unix socket path (max 104 bytes on macOS)
# Note that Linux accepts not null-terminated paths but the man page advises against it.
- if path.bytesize > 107
- path = "/tmp/unix_server_spec.socket"
+ if path.bytesize > 104
+ # rm_r in spec/mspec/lib/mspec/helpers/fs.rb fails against
+ # "/tmp/unix_server_spec.socket"
+ skip "too long unix socket path: #{path}"
end
rm_socket(path)
path
@@ -47,6 +49,48 @@ module SocketSpecs
File.delete(path) if File.exist?(path)
end
+ def self.ipv6_available?
+ @ipv6_available ||= begin
+ server = TCPServer.new('::1', 0)
+ rescue Errno::EAFNOSUPPORT, Errno::EADDRNOTAVAIL, SocketError
+ :no
+ else
+ server.close
+ :yes
+ end
+ @ipv6_available == :yes
+ end
+
+ def self.each_ip_protocol
+ describe 'using IPv4' do
+ yield Socket::AF_INET, '127.0.0.1', 'AF_INET'
+ end
+
+ guard -> { SocketSpecs.ipv6_available? } do
+ describe 'using IPv6' do
+ yield Socket::AF_INET6, '::1', 'AF_INET6'
+ end
+ end
+ end
+
+ def self.loop_with_timeout(timeout = TIME_TOLERANCE)
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+
+ while yield == :retry
+ if Process.clock_gettime(Process::CLOCK_MONOTONIC) - start >= timeout
+ raise RuntimeError, "Did not succeed within #{timeout} seconds"
+ end
+ end
+ end
+
+ def self.dest_addr_req_error
+ error = Errno::EDESTADDRREQ
+ platform_is :windows do
+ error = Errno::ENOTCONN
+ end
+ error
+ end
+
# TCPServer echo server accepting one connection
class SpecTCPServer
attr_reader :hostname, :port
@@ -69,7 +113,7 @@ module SocketSpecs
begin
data = socket.recv(1024)
- return if data.empty?
+ return if data.nil? || data.empty?
log "SpecTCPServer received: #{data.inspect}"
return if data == "QUIT"
@@ -90,4 +134,33 @@ module SocketSpecs
@logger.puts message if @logger
end
end
+
+ # We need to find a free port for Socket.tcp_server_loop and Socket.udp_server_loop,
+ # and the only reliable way to do that is to pass 0 as the port, but then we need to
+ # find out which one was chosen and the API doesn't let us find what it is. So we
+ # intercept one of the public API methods called by these methods.
+ class ServerLoopPortFinder < Socket
+ def self.tcp_server_sockets(*args)
+ super(*args) { |sockets|
+ @port = sockets.first.local_address.ip_port
+ yield(sockets)
+ }
+ end
+
+ def self.udp_server_sockets(*args, &block)
+ super(*args) { |sockets|
+ @port = sockets.first.local_address.ip_port
+ yield(sockets)
+ }
+ end
+
+ def self.cleanup
+ @port = nil
+ end
+
+ def self.port
+ sleep 0.001 until @port
+ @port
+ end
+ end
end