Skip to content

Commit

Permalink
Remove HTTP::Server#port and add HTTP::Server#bind_unused_port
Browse files Browse the repository at this point in the history
The return type is now an Socket::IPAddress instead of only the port number.
  • Loading branch information
straight-shoota committed Mar 14, 2018
1 parent 7096036 commit 013a078
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 30 deletions.
38 changes: 18 additions & 20 deletions spec/std/http/server/server_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -194,22 +194,19 @@ module HTTP
end

describe HTTP::Server do
it "re-sets special port zero after bind" do
it "binds to unused port" do
server = Server.new { |ctx| }
server.bind 0
server.port.should_not eq(0)
end
address = server.bind_unused_port
address.port.should_not eq(0)

it "re-sets port to nil after close" do
server = Server.new { |ctx| }
server.bind 0
server.close
server.port.should be_nil
port = server.bind(0).port
port.should_not eq(0)
end

it "doesn't raise on accept after close #2692" do
server = Server.new { }
server.bind "0.0.0.0", 0
server.bind_unused_port

spawn do
server.close
Expand All @@ -221,34 +218,35 @@ module HTTP

it "reuses the TCP port (SO_REUSEPORT)" do
s1 = Server.new { |ctx| }
s1.bind(0, reuse_port: true)
address = s1.bind_unused_port(reuse_port: true)

s2 = Server.new { |ctx| }
s2.bind(s1.port.not_nil!, reuse_port: true)
s2.bind(address.port, reuse_port: true)

s1.close
s2.close
end

it "binds to different interfaces" do
it "binds to different ports" do
server = Server.new do |context|
context.response.print "Test Server (#{context.request.headers["Host"]?})"
end

address1 = server.bind(0)
address2 = server.bind(0)
tcp_server = TCPServer.new("127.0.0.1", 0)
server.bind tcp_server
address1 = tcp_server.local_address

address2 = server.bind_unused_port

port1 = address1.port
port2 = address2.port
port1.should_not eq port2
address1.should_not eq address2

spawn { server.listen }

Fiber.yield

HTTP::Client.get("http://127.0.0.1:#{port2}/").body.should eq "Test Server (127.0.0.1:#{port2})"
HTTP::Client.get("http://127.0.0.1:#{port1}/").body.should eq "Test Server (127.0.0.1:#{port1})"
HTTP::Client.get("http://127.0.0.1:#{port1}/").body.should eq "Test Server (127.0.0.1:#{port1})"
HTTP::Client.get("http://#{address2}/").body.should eq "Test Server (#{address2})"
HTTP::Client.get("http://#{address1}/").body.should eq "Test Server (#{address1})"
HTTP::Client.get("http://#{address1}/").body.should eq "Test Server (#{address1})"
end
end

Expand Down
20 changes: 10 additions & 10 deletions spec/std/http/web_socket_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ describe HTTP::WebSocket do
end

it "negotiates over HTTP correctly" do
port_chan = Channel(Int32).new
address_chan = Channel(Socket::IPAddress).new

spawn do
http_ref = nil
Expand All @@ -314,14 +314,14 @@ describe HTTP::WebSocket do
end

http_server = http_ref = HTTP::Server.new([ws_handler])
http_server.bind 0
port_chan.send(http_server.port.not_nil!)
address = http_server.bind_unused_port
address_chan.send(address)
http_server.listen
end

listen_port = port_chan.receive
listen_address = address_chan.receive

ws2 = HTTP::WebSocket.new("ws://127.0.0.1:#{listen_port}/foo/bar?query=arg&yes=please")
ws2 = HTTP::WebSocket.new("ws://#{listen_address}/foo/bar?query=arg&yes=please")

random = Random::Secure.hex
ws2.on_message do |str|
Expand All @@ -334,7 +334,7 @@ describe HTTP::WebSocket do
end

it "negotiates over HTTPS correctly" do
port_chan = Channel(Int32).new
address_chan = Channel(Socket::IPAddress).new

spawn do
http_ref = nil
Expand All @@ -354,15 +354,15 @@ describe HTTP::WebSocket do
tls = http_server.tls = OpenSSL::SSL::Context::Server.new
tls.certificate_chain = File.join(__DIR__, "../openssl/ssl/openssl.crt")
tls.private_key = File.join(__DIR__, "../openssl/ssl/openssl.key")
http_server.bind 0
port_chan.send(http_server.port.not_nil!)
address = http_server.bind_unused_port
address_chan.send(address)
http_server.listen
end

listen_port = port_chan.receive
listen_address = address_chan.receive

client_context = OpenSSL::SSL::Context::Client.insecure
ws2 = HTTP::WebSocket.new("127.0.0.1", port: listen_port, path: "/", tls: client_context)
ws2 = HTTP::WebSocket.new(listen_address.address, port: listen_address.port, path: "/", tls: client_context)

random = Random::Secure.hex
ws2.on_message do |str|
Expand Down
12 changes: 12 additions & 0 deletions src/http/server.cr
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,18 @@ class HTTP::Server
bind "127.0.0.1", port, reuse_port
end

# Creates a `TCPServer` listening on an unused port and adds it as a socket.
#
# Returns the `Socket::IPAddress` with the determined port number.
#
# ```
# server = HTTP::Server.new { }
# server.bind_unused_port # => Socket::IPAddress.new("127.0.0.1", 12345)
# ```
def bind_unused_port(host : String = "127.0.0.1", reuse_port : Bool = false) : Socket::IPAddress
bind host, 0, reuse_port
end

# Adds a `Socket::Server` *socket* to this server.
def bind(socket : Socket::Server) : Socket::Server
@sockets << socket
Expand Down

0 comments on commit 013a078

Please sign in to comment.