Conversation
Serve HTTP through livery 0.8.0 instead of cowboy, and drive the test
suites with livery_client instead of hackney.
- The listener is owned by a new supervised hornbeam_listener;
hornbeam:info/0 and hornbeam:is_running/0 replace ranch introspection.
- hornbeam_handler is a livery handler. WSGI and ASGI responses resolve
through livery_resp:stream_deferred/1 in the per-request process, so
request-body chunks and Python events share one mailbox.
- WebSocket runs on the ws_handler behaviour (new hornbeam_ws_handler);
hornbeam_websocket keeps the upgrade entry point, scope building, and
the session registry.
- Duplicate request headers are preserved in the ASGI scope and joined
with ", " in WSGI HTTP_* keys.
- New max_body option (default infinity) caps the request body.
http_version selects which protocols to serve. The option was documented
but read by nothing; it now drives which listeners bind, defaulting to
['HTTP/1.1']. 'HTTP/2' and 'HTTP/3' exist only over TLS and are refused
when ssl is off rather than quietly downgraded.
- ['HTTP/1.1', 'HTTP/2'] takes livery's ALPN listener, so one TLS port
serves both and a client that will not speak h2 still gets HTTP/1.1.
This restores what cowboy's start_tls did.
- 'HTTP/3' adds a QUIC listener, on the bind port number by default or on
the new http3_port, advertised through Alt-Svc. Its cert and key are
converted from the configured PEM files to the DER shapes quic wants.
- Each listener carries its own handler state, so server_scheme and
server_port describe the listener that took the request rather than the
service as a whole.
- max_request_line_size, max_header_size and max_headers now reach h1,
which answers 414 or 431 instead of ignoring them.
Fix a crash the HTTP/2 work exposed: the ASGI and WSGI producers only
handled {error, closed} from a stream write, so a HEAD response over h2 or
h3, which answers {error, invalid_stream_state} where HTTP/1.1 silently
drops the body, took the handler down.
Breaking: routes are livery router entries
{Method | '_', Pattern, HandlerFun | {Mod, Fun}} rather than cowboy
{Path, HandlerModule, Opts} tuples; cowboy handler modules are rejected.
max_header_size and friends changed from inert to enforced. The dead
hornbeam_wsgi build_environ path is removed.
benoitc
force-pushed
the
feature/livery-http
branch
from
August 12, 2026 09:30
97bf8df to
a12e0b5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hornbeam serves HTTP with livery 0.8.0 now, not cowboy. The test suites use
livery_clientinstead of hackney.What changed
The listener is owned by a supervised
hornbeam_listener.hornbeam:info/0andhornbeam:is_running/0replace the ranch introspection.hornbeam_handleris a livery handler: WSGI and ASGI responses resolve throughlivery_resp:stream_deferred/1in the request process, so body chunks and Python events arrive in one mailbox. WebSocket uses thews_handlerbehaviour in the newhornbeam_ws_handler.Duplicate request headers are kept in the ASGI scope and joined with
", "in the WSGIHTTP_*keys. Newmax_bodyoption, defaultinfinity.HTTP/2 and HTTP/3
http_versionwas documented but nothing read it. It selects the listeners now, default['HTTP/1.1']:['HTTP/1.1']['HTTP/1.1', 'HTTP/2']['HTTP/2']'HTTP/3'http3_port, announced withAlt-SvcHTTP/2 and HTTP/3 only exist over TLS, so they fail at startup with
{error, {http_version_requires_ssl, Version}}ifsslis false. It is better than serving HTTP/1.1 without saying anything.The ALPN listener gives back what cowboy did with
start_tls: a client which does not speak h2 gets HTTP/1.1 on the same port instead of a refused handshake.For HTTP/3 the certificate and the key are converted from the PEM files to the DER form quic wants. Each listener carries its own handler state, so
server_schemeandserver_portdescribe the listener which took the request, not the service.hornbeam:info/0returns a list of ports per protocol, because one port can serve two protocols:#{running => true, listeners => #{h1 => [443], h2 => [443], h3 => [443]}}Request limits
max_request_line_size,max_header_sizeandmax_headerswere accepted and documented, but they reached nothing. They reach h1 now, which answers 414 or 431.One bug fixed
A HEAD response over h2 or h3 killed the handler. The ASGI and WSGI producers only handled
{error, closed}from a stream write, and h2/h3 answer{error, invalid_stream_state}when a body is written to a stream which must not carry one. HTTP/1.1 just drops it, so nothing showed before.Breaking
Routes are livery entries
{Method | '_', Pattern, HandlerFun | {Mod, Fun}}, not cowboy{Path, HandlerModule, Opts}tuples. Cowboy handler modules give{error, {invalid_route, _}}.The three limit keys pass from inert to enforced, so a configuration which set them loosely applies them now.
max_header_sizemaps on the h1 limit for one header value.hornbeam_wsgiis removed, thebuild_environ/1,2path was dead code.Known limitation
103 Early Hints work on HTTP/1.1 and HTTP/2 but not HTTP/3, which has no interim response in livery yet. The
http.response.early_hintsASGI extension is announced accordingly.