Skip to content

Commit

Permalink
Add support for Unix sockets to HTTP::Server
Browse files Browse the repository at this point in the history
  • Loading branch information
straight-shoota committed Mar 6, 2018
1 parent b72d0dd commit a20b633
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 1 deletion.
55 changes: 55 additions & 0 deletions spec/std/http/server/server_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,23 @@ private class ReverseResponseOutput < IO
end
end

# TODO: replace with `HTTP::Client` once it supports connecting to Unix socket.
private def unix_request(path)
UNIXSocket.open(path) do |io|
io << "GET / HTTP/1.1"
io << "\r\n\r\n"
io.flush

loop do
line = io.gets || break

if line.empty?
return io.gets
end
end
end
end

module HTTP
class Server
describe Response do
Expand Down Expand Up @@ -263,6 +280,44 @@ module HTTP
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})"
end

{% if flag?(:unix) %}
it "binds to different unix sockets" do
path1 = "/tmp/socket1"
path2 = "/tmp/socket2"

begin
server = Server.new do |context|
socket = context.socket

socket.should be_a(UNIXSocket)
if socket.is_a? UNIXSocket
context.response.puts "Test Server (#{socket.local_address})"
end
end

socket1 = server.bind UNIXServer.new(path1)
socket2 = server.bind "unix:#{path2}"

spawn do
server.listen
end

Fiber.yield

unix_request(path1).should eq "Test Server (#{path1})"
unix_request(path2).should eq "Test Server (#{path2})"

server.close

File.exists?(path1).should be_false
File.exists?(path2).should be_false
ensure
File.delete(path1) if File.exists?(path1)
File.delete(path2) if File.exists?(path2)
end
end
{% end %}
end

describe HTTP::Server::RequestProcessor do
Expand Down
39 changes: 38 additions & 1 deletion src/http/server.cr
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,44 @@ class HTTP::Server
tcp_server.local_address.port
end

# Creates a `TCPServer` bound to *address* and adds it as a socket.
# Creates a `Socket::Server` listenting to *address* and adds it as a socket.
#
# The *address* can either be `host:port` or a Unix socket prefixed by `unix:`.
# This works only on systems supporting Unix sockets.
#
# ```
# server = HTTP::Server.new { }
# server.bind "127.0.0.1:8080"
# server.bind "unix:/tmp/my-socket"
# ```
def bind(address : String, reuse_port : Bool = false) : Socket::Server
if address.starts_with?("unix:")
bind(Socket::UNIXAddress.new(address[5..-1])).as(Socket::Server)
else
host, _, port = address.partition(':')
if host == "*" || host.empty?
host = "0.0.0.0"
end
if port.empty?
port = 80
else
port = port.to_i
end
bind(host, port, reuse_port).as(Socket::Server)
end
end

# Creates a `UNIXServer` bound to *address* and adds it as an interface.
#
# ```
# server = HTTP::Server.new { }
# server.bind Socket::UNIXAddress.new("/tmp/my-socket")
# ```
def bind(address : Socket::UNIXAddress) : UNIXServer
bind UNIXServer.new(address.path)
end

# Creates a `TCPServer` bound to *address* and adds it as an interface.
#
# ```
# server = HTTP::Server.new { }
Expand Down

0 comments on commit a20b633

Please sign in to comment.