diff --git a/spec/std/http/server/server_spec.cr b/spec/std/http/server/server_spec.cr index c0c562433704..f377ddfa8f8a 100644 --- a/spec/std/http/server/server_spec.cr +++ b/spec/std/http/server/server_spec.cr @@ -1,5 +1,8 @@ require "spec" require "http/server" +{% unless flag?(:without_openssl) %} +require "../../../support/ssl" +{% end %} private class RaiseErrno < IO def initialize(@value : Int32) @@ -284,6 +287,32 @@ module HTTP end end + {% unless flag?(:without_openssl) %} + describe "#bind_ssl" do + it "binds SSL server context" do + server = Server.new do |context| + context.response.puts "Test Server (#{context.request.headers["Host"]?})" + context.response.close + end + + server_context, client_context = ssl_context_pair + + socket = SSLServer.new(TCPServer.new("127.0.0.1", 0), server_context) + server.bind socket + ip_address1 = server.bind_ssl "127.0.0.1", 0, server_context + ip_address2 = socket.local_address + + spawn server.listen + Fiber.yield + + HTTP::Client.get("https://#{ip_address1}", tls: client_context).body.should eq "Test Server (#{ip_address1})\n" + HTTP::Client.get("https://#{ip_address2}", tls: client_context).body.should eq "Test Server (#{ip_address2})\n" + + server.close + end + end + {% end %} + describe "#listen" do it "fails after listen" do server = Server.new { } diff --git a/spec/std/http/web_socket_spec.cr b/spec/std/http/web_socket_spec.cr index 77a6119855de..f9de842cb3e7 100644 --- a/spec/std/http/web_socket_spec.cr +++ b/spec/std/http/web_socket_spec.cr @@ -1,6 +1,7 @@ require "spec" require "http/web_socket" require "random/secure" +require "../../../support/ssl" private def assert_text_packet(packet, size, final = false) assert_packet packet, HTTP::WebSocket::Protocol::Opcode::TEXT, size, final: final @@ -336,6 +337,8 @@ describe HTTP::WebSocket do it "negotiates over HTTPS correctly" do address_chan = Channel(Socket::IPAddress).new + server_context, client_context = ssl_context_pair + spawn do http_ref = nil ws_handler = HTTP::WebSocketHandler.new do |ws, ctx| @@ -351,17 +354,14 @@ describe HTTP::WebSocket do end http_server = http_ref = HTTP::Server.new([ws_handler]) - 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") - address = http_server.bind_unused_port + + address = http_server.bind_ssl("127.0.0.1", context: server_context) address_chan.send(address) http_server.listen end listen_address = address_chan.receive - client_context = OpenSSL::SSL::Context::Client.insecure ws2 = HTTP::WebSocket.new(listen_address.address, port: listen_address.port, path: "/", tls: client_context) random = Random::Secure.hex diff --git a/spec/support/ssl.cr b/spec/support/ssl.cr index 13bfb07ff29b..14da61b3001d 100644 --- a/spec/support/ssl.cr +++ b/spec/support/ssl.cr @@ -1,3 +1,5 @@ +require "openssl" + def ssl_context_pair server_context = OpenSSL::SSL::Context::Server.new server_context.certificate_chain = File.join("spec", "std", "openssl", "ssl", "openssl.crt") diff --git a/src/http/server.cr b/src/http/server.cr index faf0bb93e70c..190c767a563c 100644 --- a/src/http/server.cr +++ b/src/http/server.cr @@ -95,10 +95,6 @@ require "./common" # server.listen # ``` class HTTP::Server - {% if !flag?(:without_openssl) %} - property tls : OpenSSL::SSL::Context::Server? - {% end %} - @sockets = [] of Socket::Server # Returns `true` if this server is closed. @@ -162,6 +158,41 @@ class HTTP::Server bind_tcp host, 0, reuse_port end + # Creates a `SSLServer` and adds it as a socket. + # + # The SSL server wraps a `TCPServer` listenting on `host:port`. + # + # ``` + # server = HTTP::Server.new { } + # context = OpenSSL::SSL::Context::Server.new + # context.certificate_chain = "openssl.crt" + # context.private_key = "openssl.key" + # server.bind_ssl "127.0.0.1", 8080, context + # ``` + def bind_ssl(host : String, port : Int32, context : OpenSSL::SSL::Context::Server = OpenSSL::SSL::Context::Server.new, reuse_port : Bool = false) : Socket::IPAddress + tcp_server = TCPServer.new(host, port, reuse_port) + server = SSLServer.new(tcp_server, context) + + bind(server) + + tcp_server.local_address + end + + # Creates a `SSLServer` and adds it as a socket. + # + # The SSL server wraps a `TCPServer` listenting on an unused port on *host*. + # + # ``` + # server = HTTP::Server.new { } + # context = OpenSSL::SSL::Context::Server.new + # context.certificate_chain = "openssl.crt" + # context.private_key = "openssl.key" + # address = server.bind_ssl "127.0.0.1", context + # ``` + def bind_ssl(host : String, context : OpenSSL::SSL::Context::Server = OpenSSL::SSL::Context::Server.new) : Socket::IPAddress + bind_ssl(host, 0, context) + end + # Adds a `Socket::Server` *socket* to this server. def bind(socket : Socket::Server) : Nil raise "Can't add socket to running server" if listening? @@ -250,12 +281,6 @@ class HTTP::Server io.sync = false end - {% if !flag?(:without_openssl) %} - if tls = @tls - io = OpenSSL::SSL::Socket::Server.new(io, tls, sync_close: true) - end - {% end %} - @processor.process(io, io) end