Gaoxy HTTP/3
gaoxy_http3 serves Plug applications and Gaoxy HTTP endpoints over QUIC v1 with
ALPN h3. It provides an explicit, separate HTTP/3 listener through
Gaoxy.HTTP.HTTP3.
Install
Add to mix.exs, then run mix deps.get:
defp deps do
[
{:gaoxy_http3, "~> 0.5.3"}
]
end
This brings in gaoxy_http, elixir_quic_http3, elixir_quic and ex_ssl.
Use Elixir 1.18 or newer with Erlang/OTP crypto and public-key support. The
transitive Abyss dependency builds a Rust NIF, so source builds also need
Rust/Cargo and a working system linker. Keep the matching QUIC/TLS dependency
versions selected by Mix.
Serve a Plug
defmodule MyApp.Hello do
def init(options), do: options
def call(conn, _options) do
Plug.Conn.send_resp(conn, 200, "hello over h3\n")
end
end
Add the listener to your application's supervision tree, using existing PEM certificate and private-key files:
children = [
{Gaoxy.HTTP.HTTP3,
id: :https3,
ip: {127, 0, 0, 1},
port: 4443,
tls: %{certfile: "/path/to/cert.pem", keyfile: "/path/to/key.pem"},
plug: {MyApp.Hello, []}}
]
Supervisor.start_link(children, strategy: :one_for_one)
Use a certificate valid for the hostname your client requests. The listener
binds UDP; allow UDP traffic to the configured port. A client must support
HTTP/3 and trust the certificate. For example, when curl -V lists HTTP3:
curl --http3-only --cacert /path/to/ca.pem https://localhost:4443/
Options and composition
Supply exactly one of plug: {Module, options} or
endpoint: {Module, options}. The latter implements Gaoxy.HTTP.Endpoint.
The binding requires a concrete IPv4 tuple; wildcard and IPv6 bindings are
currently rejected. The defaults are loopback, port 0 and ID :http3.
TLS credentials are required. Transport is :quic, and ALPN is fixed to h3.
Gaoxy.HTTP.HTTP3.service_spec/1 returns {:ok, service} for use in a shared
{Gaoxy, services: [...]} instance alongside TCP or HTTP/1–2 services.
Gaoxy.HTTP accepts HTTP/1 and HTTP/2; protocols: [:http3] does not enable H3.
Configure TCP HTTPS separately when clients also need H1/H2.
Start with the supplied resource limits. :limits, :http_limits and
:h3_limits are maps validated together before activation. H3 defaults allow
eight request streams per connection and 32,768-byte header blocks. Dynamic
QPACK table capacity and blocked-stream limits must both remain zero.
WebSocket and Phoenix
RFC 9220 WebSocket support requires explicit extended_connect: true.
For an external Phoenix mount, run the Phoenix Endpoint with server: false
and pass its exact socket paths through the compatibility wrapper:
plug: {Gaoxy.PhoenixWebSocket,
{MyAppWeb.Endpoint, ["/live/websocket", "/socket/websocket"]}},
extended_connect: true
These replace plug: {MyApp.Hello, []} in the listener options. Include only
paths declared by your Endpoint. Follow the
Phoenix guide
for dependencies, Endpoint supervision and socket configuration.
Browser transport selection remains client-controlled; serving a page over H3 does not prove its WebSocket uses H3. See the HTTP/3 guide for protocol limits and the native HTTP guide for shared Plug/endpoint APIs.