class HTTP::Server
inherits Reference
¶
A concurrent HTTP server implementation.
A server is initialized with a handler chain responsible for processing each incoming request.
require "http/server"
server = HTTP::Server.new do |context|
context.response.content_type = "text/plain"
context.response.print "Hello world!"
end
address = server.bind_tcp 8080
puts "Listening on http://#{address}"
server.listen
Request processing¶
The handler chain receives an instance of HTTP::Server::Context
that holds
the HTTP::Request
to process and a HTTP::Server::Response
which it can
configure and write to.
Each connection is processed concurrently in a separate Fiber
and can handle
multiple subsequent requests-response cycles with connection keep-alive.
Handler chain¶
The handler given to a server can simply be a block that receives an HTTP::Server::Context
,
or it can be an instance of HTTP::Handler
. An HTTP::Handler
has a #next
method to forward processing to the next handler in the chain.
For example, an initial handler might handle exceptions raised from subsequent
handlers and return a 500 Server Error
status (see HTTP::ErrorHandler
).
The next handler might log all incoming requests (see HTTP::LogHandler
).
And the final handler deals with routing and application logic.
require "http/server"
server = HTTP::Server.new([
HTTP::ErrorHandler.new,
HTTP::LogHandler.new,
HTTP::CompressHandler.new,
HTTP::StaticFileHandler.new("."),
])
server.bind_tcp "127.0.0.1", 8080
server.listen
Response object¶
The HTTP::Server::Response
object has status
and headers
properties that can be
configured before writing the response body. Once any response output has been
written, changing the status
and headers
properties has no effect.
The HTTP::Server::Response
is a write-only IO
, so all IO
methods are available
on it for sending the response body.
Binding to sockets¶
The server can be bound to one or more server sockets (see #bind
)
Supported types:
- TCP socket:
#bind_tcp
,#bind_unused_port
- TCP socket with TLS/SSL:
#bind_tls
- Unix socket
#bind_unix
#bind(uri : URI)
and #bind(uri : String)
parse socket configuration for
one of these types from an URI
. This can be useful for injecting plain text
configuration values.
Each of these methods returns the Socket::Address
that was added to this
server.
require "http/server"
server = HTTP::Server.new do |context|
context.response.content_type = "text/plain"
context.response.print "Hello world!"
end
address = server.bind_tcp "0.0.0.0", 8080
puts "Listening on http://#{address}"
server.listen
It is also possible to bind a generic Socket::Server
using
#bind(socket : Socket::Server)
which can be used for custom network protocol
configurations.
Server loop¶
After defining all server sockets to listen to, the server can be started by
calling #listen
. This call blocks until the server is closed.
A server can be closed by calling #close
. This closes the server sockets and
stops processing any new requests, even on connections with keep-alive enabled.
Currently processing requests are not interrupted but also not waited for.
In order to give them some grace period for finishing, the calling context
can add a timeout like sleep 10.seconds
after #listen
returns.
Reusing connections¶
The request processor supports reusing a connection for subsequent
requests. This is used by default for HTTP/1.1 or when requested by
the Connection: keep-alive
header. This is signalled by this header being
set on the HTTP::Server::Response
when it's passed into the handler chain.
If in the handler chain this header is overridden to Connection: close
, then
the connection will not be reused after the request has been processed.
Reusing the connection also requires that the request body (if present) is entirely consumed in the handler chain. Otherwise the connection will be closed.
Constants¶
Log = ::Log.for("http.server")
¶
::Log.for("http.server")
Class methods¶
.build_middleware(handlers, last_handler : Context -> ? = nil)
¶
(handlers, last_handler : Context -> ? = nil)
Builds all handlers as the middleware for HTTP::Server
.
.new(handlers : Array(HTTP::Handler), &handler : HTTP::Handler::HandlerProc) : self
¶
(handlers : Array(HTTP::Handler), &handler : HTTP::Handler::HandlerProc) : self
Creates a new HTTP server with a handler chain constructed from the handlers array and the given block.
.new(&handler : HTTP::Handler::HandlerProc) : self
¶
(&handler : HTTP::Handler::HandlerProc) : self
Creates a new HTTP server with the given block as handler.
.new(handlers : Array(HTTP::Handler)) : self
¶
(handlers : Array(HTTP::Handler)) : self
Creates a new HTTP server with the handlers array as handler chain.
.new(handler : HTTP::Handler | HTTP::Handler::HandlerProc)
¶
(handler : HTTP::Handler | HTTP::Handler::HandlerProc)
Creates a new HTTP server with the given handler.
Methods¶
#bind(uri : URI) : Socket::Address
¶
(uri : URI) : Socket::Address
Parses a socket configuration from uri and adds it to this server. Returns the effective address it is bound to.
require "http/server"
server = HTTP::Server.new { }
server.bind("tcp://localhost:80") # => Socket::IPAddress.new("127.0.0.1", 8080)
server.bind("unix:///tmp/server.sock") # => Socket::UNIXAddress.new("/tmp/server.sock")
server.bind("tls://127.0.0.1:443?key=private.key&cert=certificate.cert&ca=ca.crt") # => Socket::IPAddress.new("127.0.0.1", 443)
#bind(uri : String) : Socket::Address
¶
(uri : String) : Socket::Address
Parses a socket configuration from uri and adds it to this server. Returns the effective address it is bound to.
require "http/server"
server = HTTP::Server.new { }
server.bind("tcp://localhost:80") # => Socket::IPAddress.new("127.0.0.1", 8080)
server.bind("unix:///tmp/server.sock") # => Socket::UNIXAddress.new("/tmp/server.sock")
server.bind("tls://127.0.0.1:443?key=private.key&cert=certificate.cert&ca=ca.crt") # => Socket::IPAddress.new("127.0.0.1", 443)
#bind_tcp(host : String, port : Int32, reuse_port : Bool = false) : Socket::IPAddress
¶
(host : String, port : Int32, reuse_port : Bool = false) : Socket::IPAddress
Creates a TCPServer
listening on host:port
and adds it as a socket, returning the local address
and port the server listens on.
require "http/server"
server = HTTP::Server.new { }
server.bind_tcp("127.0.0.100", 8080) # => Socket::IPAddress.new("127.0.0.100", 8080)
If reuse_port is true
, it enables the SO_REUSEPORT
socket option,
which allows multiple processes to bind to the same port.
#bind_tcp(port : Int32, reuse_port : Bool = false) : Socket::IPAddress
¶
(port : Int32, reuse_port : Bool = false) : Socket::IPAddress
Creates a TCPServer
listening on 127.0.0.1:port
and adds it as a socket,
returning the local address and port the server listens on.
require "http/server"
server = HTTP::Server.new { }
server.bind_tcp(8080) # => Socket::IPAddress.new("127.0.0.1", 8080)
If reuse_port is true
, it enables the SO_REUSEPORT
socket option,
which allows multiple processes to bind to the same port.
#bind_tcp(address : Socket::IPAddress, reuse_port : Bool = false) : Socket::IPAddress
¶
(address : Socket::IPAddress, reuse_port : Bool = false) : Socket::IPAddress
Creates a TCPServer
listening on address and adds it as a socket, returning the local address
and port the server listens on.
require "http/server"
server = HTTP::Server.new { }
server.bind_tcp(Socket::IPAddress.new("127.0.0.100", 8080)) # => Socket::IPAddress.new("127.0.0.100", 8080)
server.bind_tcp(Socket::IPAddress.new("127.0.0.100", 0)) # => Socket::IPAddress.new("127.0.0.100", 35487)
If reuse_port is true
, it enables the SO_REUSEPORT
socket option,
which allows multiple processes to bind to the same port.
#bind_tls(host : String, port : Int32, context : OpenSSL::SSL::Context::Server, reuse_port : Bool = false) : Socket::IPAddress
¶
(host : String, port : Int32, context : OpenSSL::SSL::Context::Server, reuse_port : Bool = false) : Socket::IPAddress
Creates an OpenSSL::SSL::Server
and adds it as a socket.
The SSL server wraps a TCPServer
listening on host:port
.
require "http/server"
server = HTTP::Server.new { }
context = OpenSSL::SSL::Context::Server.new
context.certificate_chain = "openssl.crt"
context.private_key = "openssl.key"
server.bind_tls "127.0.0.1", 8080, context
#bind_tls(address : Socket::IPAddress, context : OpenSSL::SSL::Context::Server) : Socket::IPAddress
¶
(address : Socket::IPAddress, context : OpenSSL::SSL::Context::Server) : Socket::IPAddress
Creates an OpenSSL::SSL::Server
and adds it as a socket.
The SSL server wraps a TCPServer
listening on an unused port on host.
require "http/server"
server = HTTP::Server.new { }
context = OpenSSL::SSL::Context::Server.new
context.certificate_chain = "openssl.crt"
context.private_key = "openssl.key"
address = server.bind_tls Socket::IPAddress.new("127.0.0.1", 8000), context
#bind_tls(host : String, context : OpenSSL::SSL::Context::Server) : Socket::IPAddress
¶
(host : String, context : OpenSSL::SSL::Context::Server) : Socket::IPAddress
Creates an OpenSSL::SSL::Server
and adds it as a socket.
The SSL server wraps a TCPServer
listening on an unused port on host.
require "http/server"
server = HTTP::Server.new { }
context = OpenSSL::SSL::Context::Server.new
context.certificate_chain = "openssl.crt"
context.private_key = "openssl.key"
address = server.bind_tls "127.0.0.1", context
#bind_unix(path : String) : Socket::UNIXAddress
¶
(path : String) : Socket::UNIXAddress
Creates a UNIXServer
bound to path and adds it as a socket.
require "http/server"
server = HTTP::Server.new { }
server.bind_unix "/tmp/my-socket.sock"
#bind_unix(address : Socket::UNIXAddress) : Socket::UNIXAddress
¶
(address : Socket::UNIXAddress) : Socket::UNIXAddress
Creates a UNIXServer
bound to address and adds it as a socket.
require "http/server"
server = HTTP::Server.new { }
server.bind_unix(Socket::UNIXAddress.new("/tmp/my-socket.sock"))
#bind_unused_port(host : String = Socket::IPAddress::LOOPBACK, reuse_port : Bool = false) : Socket::IPAddress
¶
(host : String = Socket::IPAddress::LOOPBACK, reuse_port : Bool = false) : Socket::IPAddress
Creates a TCPServer
listening on an unused port and adds it as a socket.
Returns the Socket::IPAddress
with the determined port number.
require "http/server"
server = HTTP::Server.new { }
server.bind_unused_port # => Socket::IPAddress.new("127.0.0.1", 12345)
#close
¶
Gracefully terminates the server. It will process currently accepted requests, but it won't accept new connections.
#each_address(&block : Socket::Address -> )
¶
(&block : Socket::Address -> )
Enumerates all addresses this server is bound to.
#listen(port : Int32, reuse_port : Bool = false)
¶
(port : Int32, reuse_port : Bool = false)
Creates a TCPServer
listening on 127.0.0.1:port
, adds it as a socket
and starts the server. Blocks until the server is closed.
See #bind(port : Int32)
for details.
#listen(host : String, port : Int32, reuse_port : Bool = false)
¶
(host : String, port : Int32, reuse_port : Bool = false)
Creates a TCPServer
listening on host:port
, adds it as a socket
and starts the server. Blocks until the server is closed.
See #bind(host : String, port : Int32)
for details.
#max_headers_size : Int32
¶
: Int32
Returns the maximum permitted combined size for the headers in an HTTP request.
When parsing a request, the server keeps track of the amount of total bytes
consumed for all headers (including line breaks).
If combined byte size of all headers is larger than the permitted size,
the server responds with the status code 432 Request Header Fields Too Large
(see HTTP::Status::REQUEST_HEADER_FIELDS_TOO_LARGE
).
Default: HTTP::MAX_HEADERS_SIZE
#max_headers_size=(size : Int32)
¶
(size : Int32)
Sets the maximum permitted combined size for the headers in an HTTP request.
#max_request_line_size : Int32
¶
: Int32
Returns the maximum permitted size for the request line in an HTTP request.
The request line is the first line of a request, consisting of method,
resource and HTTP version and the delimiting line break.
If the request line has a larger byte size than the permitted size,
the server responds with the status code 414 URI Too Long
(see HTTP::Status::URI_TOO_LONG
).
Default: HTTP::MAX_REQUEST_LINE_SIZE
#max_request_line_size=(size : Int32)
¶
(size : Int32)
Sets the maximum permitted size for the request line in an HTTP request.