From 9c0ea8eb9f6a67e293112ea743b6e9b3a8033d9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Thu, 15 Mar 2018 12:08:36 +0000 Subject: [PATCH] Remove HTTP::Server#port and add HTTP::Server#bind_unused_port The return type is now an Socket::IPAddress instead of only the port number. --- spec/std/http/server/server_spec.cr | 38 ++++++++++++++--------------- spec/std/http/web_socket_spec.cr | 20 +++++++-------- src/http/server.cr | 28 +++++++++------------ 3 files changed, 40 insertions(+), 46 deletions(-) diff --git a/spec/std/http/server/server_spec.cr b/spec/std/http/server/server_spec.cr index 5cb58d0451dc..f4eefc5a4997 100644 --- a/spec/std/http/server/server_spec.cr +++ b/spec/std/http/server/server_spec.cr @@ -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 @@ -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 diff --git a/spec/std/http/web_socket_spec.cr b/spec/std/http/web_socket_spec.cr index 79d17fa56129..77a6119855de 100644 --- a/spec/std/http/web_socket_spec.cr +++ b/spec/std/http/web_socket_spec.cr @@ -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 @@ -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| @@ -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 @@ -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| diff --git a/src/http/server.cr b/src/http/server.cr index f9aca6c14c00..0c1b6e90fc66 100644 --- a/src/http/server.cr +++ b/src/http/server.cr @@ -123,22 +123,6 @@ class HTTP::Server @processor = RequestProcessor.new(handler) end - # Returns the TCP port of the first socket the server is bound to. - # - # For example you may let the system choose a port, then report it: - # ``` - # server = HTTP::Server.new { } - # server.bind 0 - # server.port # => 12345 - # ``` - def port : Int32? - @sockets.each do |socket| - if socket.is_a?(TCPServer) - return socket.local_address.port.to_i - end - end - end - # Creates a `TCPServer` and adds it as a socket, returning the local address # and port the server listens on. # @@ -163,6 +147,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