Server Guide
HTTP.jl 2.0 supports both high-level request handlers and lower-level stream handlers. The right choice depends on how much control you need over read/write sequencing.
Request Handlers
Use HTTP.serve! or HTTP.serve when your application naturally maps Request -> Response.
using HTTP
server = HTTP.serve!("127.0.0.1", 0; listenany = true) do req
payload = "handled " * req.target
return HTTP.Response(
200;
headers = ["X-Handler" => "request"],
body = payload,
)
end
base_url = "http://127.0.0.1:$(HTTP.port(server))"
resp = HTTP.get(base_url * "/health"; proxy = HTTP.ProxyConfig())
HTTP.forceclose(server)
(status = resp.status, header = HTTP.header(resp, "X-Handler"), body = String(resp.body))This is the simplest server path and the best default for ordinary APIs.
Stream Handlers
Use HTTP.listen! when you need lower-level ownership of the connection lifecycle. HTTP.streamhandler is the bridge when you want stream server mechanics with a request-style handler body.
using HTTP
stream_server = HTTP.listen!(
HTTP.streamhandler() do req
return HTTP.Response(201; body = "stream handler")
end,
"127.0.0.1",
0;
listenany = true,
)
stream_url = "http://127.0.0.1:$(HTTP.port(stream_server))"
stream_resp = HTTP.get(stream_url * "/echo"; status_exception = false, proxy = HTTP.ProxyConfig())
HTTP.forceclose(stream_server)
(status = stream_resp.status, body = String(stream_resp.body))Stream handlers are the right tool when you need:
- pull-based request body reads
- push-based or incremental response writing
- trailers or custom sequencing
- long-lived handlers that cannot be expressed as a single eager
Response
Server Lifecycle
The returned Server handle is operationally important. Hold onto it so you can:
- inspect the bound port with
HTTP.port(server) - block on completion with
wait(server) - close or force-close the server explicitly during shutdown
HTTP.forceclose(server) is the fast shutdown path when you need to stop accepting and serving immediately.
Every server timeout has both a seconds-valued keyword and a nanosecond-valued _ns keyword:
using HTTP
handler = req -> HTTP.Response(200; body = "ok")
server = HTTP.serve!(
handler,
"127.0.0.1",
8080;
read_header_timeout_ns = 5_000_000_000,
read_timeout_ns = 30_000_000_000,
write_timeout_ns = 30_000_000_000,
idle_timeout_ns = 120_000_000_000,
)using HTTP
handler = req -> HTTP.Response(200; body = "ok")
server = HTTP.serve!(
handler,
"127.0.0.1",
8080;
read_timeout = 30,
read_header_timeout = 5,
write_timeout = 30,
idle_timeout = 120,
)The older readtimeout keyword is accepted as a seconds-valued migration alias for read_timeout.
Routing and Middleware
Use HTTP.Router when you want route matching without bringing in a larger web framework:
using HTTP
router = HTTP.Router()
HTTP.register!(router, "GET", "/users/{id}") do req
id = HTTP.getparam(req, "id")
return HTTP.Response(200; body = "user " * id)
end
server = HTTP.serve!(router, "127.0.0.1", 8080)Middleware is just function composition around handlers. For example, apply a handler timeout to every registered route:
using HTTP
timeout = HTTP.Handlers.handlertimeout(5.0; status = 503)
router = HTTP.Router(
req -> HTTP.Response(404),
req -> HTTP.Response(405),
timeout,
)The router stores route metadata on the request context. Read it with HTTP.getroute, HTTP.getparams, and HTTP.getparam.
Static Files
HTTP.fileserver(root) returns a normal request handler rooted at a directory. It serves static files, normalizes directory redirects, can fall back to a single-page-app entrypoint, and emits conditional and range-aware responses.
using HTTP
handler = HTTP.fileserver("public"; spa_fallback = "index.html")
server = HTTP.serve!(handler, "127.0.0.1", 8080)For lower-level control, use HTTP.servefile(request, path) when you already resolved a filesystem path, or HTTP.servecontent(request, source) when the bytes/string/seekable IO content is already in hand. These helpers populate content type, Last-Modified, ETag, Accept-Ranges, and Content-Range headers as appropriate, and honor conditional and range request headers before returning a Response.
SSE and Long-Lived Responses
HTTP.jl exposes SSEEvent, SSEStream, and sse_stream for server-sent events. Use these when you want a proper text/event-stream response instead of hand-assembling event lines.
using HTTP
server = HTTP.serve!("127.0.0.1", 8080) do req
return HTTP.sse_stream(200) do stream
write(stream, HTTP.SSEEvent("ready"; event = "status", id = "1"))
end
endHTTP/2 Servers
The same server entrypoints can serve HTTP/2. For browser and most production clients, configure TLS so ALPN can select h2; for cleartext prior-knowledge clients, HTTP.jl accepts the HTTP/2 connection preface on the normal listener. Most applications do not need a separate server API for HTTP/2; use the normal serve!, listen!, and streamhandler surfaces.