1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
|
require "./spec_helper"
require "socket"
describe UDPSocket, tags: "network" do
# Note: This spec fails with a IPv6 address. See pending below.
it "#remote_address resets after connect" do
socket = UDPSocket.new
socket.connect("127.0.0.1", 1)
socket.remote_address.port.should eq 1
socket.connect("127.0.0.1", 2)
socket.remote_address.port.should eq 2
socket.close
end
pending "#connect with a IPv6 address" do
socket = UDPSocket.new
socket.connect("::1", 1)
socket.close
end
each_ip_family do |family, address, unspecified_address|
it "#bind" do
port = unused_local_port
socket = UDPSocket.new(family)
socket.bind(address, port)
socket.local_address.should eq(Socket::IPAddress.new(address, port))
socket.close
socket = UDPSocket.new(family)
socket.bind(address, 0)
socket.local_address.address.should eq address
end
it "sends and receives messages" do
port = unused_local_port
server = UDPSocket.new(family)
server.bind(address, port)
server.local_address.should eq(Socket::IPAddress.new(address, port))
client = UDPSocket.new(family)
client.bind(address, 0)
client.send "message", to: server.local_address
server.receive.should eq({"message", client.local_address})
client.connect(address, port)
client.local_address.family.should eq(family)
client.local_address.address.should eq(address)
client.remote_address.should eq(Socket::IPAddress.new(address, port))
client.send "message"
server.receive.should eq({"message", client.local_address})
client.send("laus deo semper")
buffer = uninitialized UInt8[256]
bytes_read, client_addr = server.receive(buffer.to_slice)
message = String.new(buffer.to_slice[0, bytes_read])
message.should eq("laus deo semper")
client.send("laus deo semper")
# WSA errors with WSAEMSGSIZE if the buffer is not large enough to receive the message
{% unless flag?(:win32) %}
bytes_read, client_addr = server.receive(buffer.to_slice[0, 4])
message = String.new(buffer.to_slice[0, bytes_read])
message.should eq("laus")
{% end %}
client.close
server.close
end
if {{ flag?(:darwin) }} && family == Socket::Family::INET6
# Darwin is failing to join IPv6 multicast groups on older versions.
# However this is known to work on macOS Mojave with Darwin 18.2.0.
# Darwin also has a bug that prevents selecting the "default" interface.
# https://lists.apple.com/archives/darwin-kernel/2014/Mar/msg00012.html
pending "joins and transmits to multicast groups"
elsif {{ flag?(:solaris) }} && family == Socket::Family::INET
# TODO: figure out why updating `multicast_loopback` produces a
# `setsockopt 18: Invalid argument` error
pending "joins and transmits to multicast groups"
else
pending "joins and transmits to multicast groups" do
udp = UDPSocket.new(family)
port = unused_local_port
udp.bind(unspecified_address, port)
udp.multicast_loopback = false
udp.multicast_loopback?.should eq(false)
udp.multicast_hops = 4
udp.multicast_hops.should eq(4)
udp.multicast_hops = 0
udp.multicast_hops.should eq(0)
addr = case family
when Socket::Family::INET
expect_raises(Socket::Error, "Unsupported IP address family: INET. For use with IPv6 only") do
udp.multicast_interface 0
end
begin
udp.multicast_interface Socket::IPAddress.new(unspecified_address, 0)
rescue e : Socket::Error
if e.os_error == Errno::ENOPROTOOPT
pending!("Multicast device selection not available on this host")
else
raise e
end
end
Socket::IPAddress.new("224.0.0.254", port)
when Socket::Family::INET6
expect_raises(Socket::Error, "Unsupported IP address family: INET6. For use with IPv4 only") do
udp.multicast_interface(Socket::IPAddress.new(unspecified_address, 0))
end
begin
udp.multicast_interface(0)
rescue e : Socket::Error
if e.os_error == Errno::ENOPROTOOPT
pending!("Multicast device selection not available on this host")
else
raise e
end
end
Socket::IPAddress.new("ff02::102", port)
else
raise "Unsupported IP address family: #{family}"
end
begin
udp.join_group(addr)
rescue e : Socket::Error
if e.os_error == Errno::ENODEV
pending!("Multicast device selection not available on this host")
else
raise e
end
end
udp.multicast_loopback = true
udp.multicast_loopback?.should eq(true)
udp.send("testing", addr)
udp.read_timeout = 1.second
begin
udp.receive[0].should eq("testing")
rescue IO::TimeoutError
# Since this test doesn't run over the loopback interface, this test
# fails when there is a firewall in use. Don't fail in that case.
end
udp.leave_group(addr)
udp.send("testing", addr)
# Test that nothing was received after leaving the multicast group
spawn do
sleep 100.milliseconds
udp.close
end
expect_raises(IO::Error) { udp.receive }
udp.closed?.should be_true
end
end
end
{% if flag?(:linux) || flag?(:win32) %}
it "sends broadcast message" do
port = unused_local_port
client = UDPSocket.new(Socket::Family::INET)
client.bind("localhost", 0)
client.broadcast = true
client.broadcast?.should be_true
client.connect("255.255.255.255", port)
client.send("broadcast").should eq(9)
client.close
end
{% else %}
pending "sends broadcast message"
{% end %}
end
|