Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(WIP) Refactor protocol sockets (TCP, UDP, UNIX) #6929

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ stats ?= ## Enable statistics output
progress ?= ## Enable progress output
threads ?= ## Maximum number of threads to use
debug ?= ## Add symbolic debug info
verbose ?= ## Run specs in verbose mode
verbose ?= true ## Run specs in verbose mode
junit_output ?= ## Directory to output junit results
static ?= ## Enable static linking

Expand Down
2 changes: 1 addition & 1 deletion spec/std/socket/address_spec.cr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
require "spec"
require "socket"
require "socket/address"

describe Socket::Address do
describe ".parse" do
Expand Down
2 changes: 1 addition & 1 deletion spec/std/socket/addrinfo_spec.cr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
require "spec"
require "socket"
require "socket/addrinfo"

describe Socket::Addrinfo do
describe ".resolve" do
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
require "./spec_helper"

describe Socket do
describe ".unix" do
it "creates a unix socket" do
sock = Socket.unix
sock.should be_a(Socket)
sock.family.should eq(Socket::Family::UNIX)
sock.type.should eq(Socket::Type::STREAM)
describe Socket::Raw do
it "creates a unix socket" do
sock = Socket::Raw.new(Socket::Family::UNIX, Socket::Type::STREAM)
sock.should be_a(Socket::Raw)
sock.family.should eq(Socket::Family::UNIX)
sock.type.should eq(Socket::Type::STREAM)

sock = Socket.unix(Socket::Type::DGRAM)
sock.type.should eq(Socket::Type::DGRAM)
end
sock = Socket::Raw.new(Socket::Family::UNIX, Socket::Type::DGRAM)
sock.type.should eq(Socket::Type::DGRAM)
end

it ".accept" do
server = Socket.new(Socket::Family::INET, Socket::Type::STREAM, Socket::Protocol::TCP)
server = Socket::Raw.new(Socket::Family::INET, Socket::Type::STREAM, Socket::Protocol::TCP)
port = unused_local_port
server.bind("0.0.0.0", port)
server.listen
Expand All @@ -28,19 +26,18 @@ describe Socket do
end

it "sends messages" do
port = unused_local_port
server = Socket.tcp(Socket::Family::INET6)
server.bind("::1", port)
server = Socket::Raw.new(Socket::Family::INET6, Socket::Type::STREAM)
server.bind("::1", 0)
server.listen
address = Socket::IPAddress.new("::1", port)
address = server.local_address(Socket::IPAddress)
spawn do
client = server.not_nil!.accept
client.gets.should eq "foo"
client.puts "bar"
ensure
client.try &.close
end
socket = Socket.tcp(Socket::Family::INET6)
socket = Socket::Raw.new(Socket::Family::INET6, Socket::Type::STREAM)
socket.connect(address)
socket.puts "foo"
socket.gets.should eq "bar"
Expand All @@ -52,7 +49,7 @@ describe Socket do
describe "#bind" do
each_ip_family do |family, _, any_address|
it "binds to port" do
socket = TCPSocket.new family
socket = Socket::Raw.new family, Socket::Type::STREAM
socket.bind(any_address, 0)
socket.listen

Expand Down
2 changes: 2 additions & 0 deletions spec/std/socket/tcp_server_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ describe TCPServer do

local_address = Socket::IPAddress.new(address, port)
server.local_address.should eq local_address
server.local_address?.should eq local_address

server.closed?.should be_false

Expand All @@ -23,6 +24,7 @@ describe TCPServer do
expect_raises(Errno, "getsockname: Bad file descriptor") do
server.local_address
end
server.local_address?.should be_nil
end

it "binds to port 0" do
Expand Down
66 changes: 60 additions & 6 deletions spec/std/socket/tcp_socket_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,72 @@ describe TCPSocket do

TCPServer.open(address, port) do |server|
TCPSocket.open(address, port) do |client|
client.local_address.address.should eq address

local_port = client.local_address.port
sock = server.accept

sock.closed?.should be_false
client.closed?.should be_false

sock.local_address.port.should eq(port)
sock.local_address.address.should eq(address)
local_address = Socket::IPAddress.new(address, local_port)
remote_address = Socket::IPAddress.new(address, port)

sock.local_address.should eq remote_address
sock.local_address?.should eq remote_address
client.local_address.should eq local_address
client.local_address?.should eq local_address

sock.remote_address.should eq local_address
sock.remote_address?.should eq local_address
client.remote_address.should eq remote_address
client.remote_address?.should eq remote_address
end
end
end

it "connects to server using a local address" do
port = unused_local_port
local_port = unused_local_port

TCPServer.open(address, port) do |server|
TCPSocket.open(address, port, address, local_port) do |client|
sock = server.accept

local_address = Socket::IPAddress.new(address, local_port)
remote_address = Socket::IPAddress.new(address, port)

sock.local_address.should eq remote_address
sock.local_address?.should eq remote_address
sock.remote_address.should eq local_address
sock.remote_address?.should eq local_address

client.remote_address.should eq remote_address
client.remote_address?.should eq remote_address
client.local_address.should eq local_address
client.local_address?.should eq local_address
end
end
end

it "connects to server using a local address with port 0" do
port = unused_local_port

TCPServer.open(address, port) do |server|
TCPSocket.open(address, port, address, 0) do |client|
sock = server.accept

local_port = client.local_address.port
local_address = Socket::IPAddress.new(address, local_port)
remote_address = Socket::IPAddress.new(address, port)

sock.local_address.should eq remote_address
sock.local_address?.should eq remote_address
client.local_address.should eq local_address
client.local_address?.should eq local_address

client.remote_address.port.should eq(port)
sock.remote_address.address.should eq address
sock.remote_address.should eq local_address
sock.remote_address?.should eq local_address
client.remote_address.should eq remote_address
client.remote_address?.should eq remote_address
end
end
end
Expand Down
16 changes: 8 additions & 8 deletions spec/std/socket/udp_socket_spec.cr
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
require "./spec_helper"
require "socket"

describe UDPSocket do
each_ip_family do |family, 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
Expand All @@ -17,12 +18,10 @@ describe UDPSocket do
it "sends and receives messages" do
port = unused_local_port

server = UDPSocket.new(family)
server.bind(address, port)
server = UDPSocket.new(address, port)
server.local_address.should eq(Socket::IPAddress.new(address, port))

client = UDPSocket.new(family)
client.bind(address, 0)
client = UDPSocket.new(address)

client.send "message", to: server.local_address
server.receive.should eq({"message", client.local_address})
Expand Down Expand Up @@ -55,11 +54,12 @@ describe UDPSocket do
end

{% if flag?(:linux) %}
it "sends broadcast message" do
# TODO: Apparently this doesn't work on the CI platform, but the spec has been
# tested to successfully run on a linux machine.
pending "sends broadcast message" do
port = unused_local_port

client = UDPSocket.new(Socket::Family::INET)
client.bind("localhost", 0)
client = UDPSocket.new("localhost")
client.broadcast = true
client.broadcast?.should be_true
client.connect("255.255.255.255", port)
Expand Down
2 changes: 2 additions & 0 deletions spec/std/socket/unix_socket_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ describe UNIXSocket do
server.local_address.path.should eq(path)

UNIXSocket.open(path) do |client|
client.remote_address.family.should eq(Socket::Family::UNIX)
client.remote_address.path.should eq(path)
client.local_address.family.should eq(Socket::Family::UNIX)
client.local_address.path.should eq(path)

Expand Down
8 changes: 4 additions & 4 deletions src/crystal/event_loop.cr
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ module Crystal::EventLoop
event
end

def self.create_fd_write_event(sock : Socket, edge_triggered : Bool = false)
def self.create_fd_write_event(sock : Socket::Raw, edge_triggered : Bool = false)
flags = LibEvent2::EventFlags::Write
flags |= LibEvent2::EventFlags::Persist | LibEvent2::EventFlags::ET if edge_triggered
event = @@eb.new_event(sock.fd, flags, sock) do |s, flags, data|
sock_ref = data.as(Socket)
sock_ref = data.as(Socket::Raw)
if flags.includes?(LibEvent2::EventFlags::Write)
sock_ref.resume_write
elsif flags.includes?(LibEvent2::EventFlags::Timeout)
Expand All @@ -64,11 +64,11 @@ module Crystal::EventLoop
event
end

def self.create_fd_read_event(sock : Socket, edge_triggered : Bool = false)
def self.create_fd_read_event(sock : Socket::Raw, edge_triggered : Bool = false)
flags = LibEvent2::EventFlags::Read
flags |= LibEvent2::EventFlags::Persist | LibEvent2::EventFlags::ET if edge_triggered
event = @@eb.new_event(sock.fd, flags, sock) do |s, flags, data|
sock_ref = data.as(Socket)
sock_ref = data.as(Socket::Raw)
if flags.includes?(LibEvent2::EventFlags::Read)
sock_ref.resume_read
elsif flags.includes?(LibEvent2::EventFlags::Timeout)
Expand Down
2 changes: 1 addition & 1 deletion src/http/client.cr
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,7 @@ class HTTP::Client
return socket if socket

hostname = @host.starts_with?('[') && @host.ends_with?(']') ? @host[1..-2] : @host
socket = TCPSocket.new hostname, @port, @dns_timeout, @connect_timeout
socket = TCPSocket.new hostname, @port, dns_timeout: @dns_timeout, connect_timeout: @connect_timeout
socket.read_timeout = @read_timeout if @read_timeout
socket.sync = false
@socket = socket
Expand Down
Loading