Skip to content

Latest commit

 

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

nhttp

Hex.pm CI

HTTP/1.1, HTTP/2, and HTTP/3 server for Erlang/OTP 27+.

Getting started

%% rebar.config
{deps, [nhttp]}.

Minimal server

-module(my_handler).
-behaviour(nhttp_handler).
-export([init/1, handle_request/2]).

init(_Args) ->
    {ok, #{}}.

handle_request(#{method := get, path := <<"/">>}, State) ->
    {reply, nhttp_resp:ok(<<"Hello, World!">>), State};
handle_request(_Req, State) ->
    {reply, nhttp_resp:not_found(), State}.
{ok, Pid} = nhttp:start_link(my_server, #{
    port => 8080,
    handler => my_handler
}).

See the examples/ directory for runnable handler skeletons covering plain HTTP, streaming responses, WebSocket, SSE, CORS, and chunked uploads.

TLS with HTTP/1.1 + HTTP/2 (ALPN)

{ok, Pid} = nhttp:start_link(my_server, #{
    port => 8443,
    versions => [http1_1, http2],
    handler => my_handler,
    tls => #{certfile => "server.pem", keyfile => "server.key"}
}).

HTTP/3 over QUIC

HTTP/3 runs over QUIC on a UDP socket. HTTP/1.1 and HTTP/2 run over TCP on their own socket. List all three versions and a single listener opens both sockets for you, and sets Alt-Svc on the TCP side so clients know where to find HTTP/3.

{ok, Pid} = nhttp:start_link(my_server, #{
    port => 8443,
    versions => [http1_1, http2, http3],
    handler => my_handler,
    tls => #{certfile => "server.pem", keyfile => "server.key"}
}).

%% nhttp:get_ports(Pid) => #{tcp => 8443, quic => 8443}

Features

  • HTTP/1.1 (RFC 9112) and HTTP/2 (RFC 9113) with automatic protocol selection via TLS ALPN
  • HTTP/3 over QUIC (RFC 9114), in-process nquic library mode
  • WebSocket over HTTP/1.1 (RFC 6455), HTTP/2 (RFC 8441), and HTTP/3 (RFC 9220) with one callback set
  • Server-Sent Events helpers (W3C SSE)
  • CORS preflight and response header helpers (Fetch)
  • PROXY protocol v1/v2 for HAProxy and AWS NLB deployments
  • SNI with static lookup table and/or dynamic callback
  • Streaming responses via producer funs with end-to-end backpressure
  • Streaming request bodies with handler-driven backpressure
  • Response compression (gzip / deflate) with MIME-aware defaults
  • OpenTelemetry spans and metrics (opt-in)
  • Graceful shutdown with connection draining
  • One process per connection, with HTTP/2 and HTTP/3 streams multiplexed inside it

HTTP/2 test affordances

These listener options exist so that a client can be tested against a slow or stingy HTTP/2 peer. Each one defaults to the current behavior and changes nothing on HTTP/1.1 or HTTP/3.

Option Default Effect
h2_initial_window_size 65535 Alias of initial_window_size in h2_settings: the receive window that each new stream grants the peer.
h2_max_frame_size 16384 Alias of max_frame_size in h2_settings: the largest frame payload the server accepts.
h2_response_delay 0 Milliseconds to hold the response headers of a {reply, _, _} result. {uniform, MinMs, MaxMs} draws a value per response.
h2_connection_window_policy eager Credit policy for the connection receive window.
h2_stream_window_policy eager Credit policy for each stream receive window.
h2_credit_batch 0 Octets per WINDOW_UPDATE under the on_response connection policy. 0 sends the whole accumulator with each response. Valid only when a policy is on_response.

An alias must equal the h2_settings key when both are present. The delay applies to {reply, _, _} results only. Error responses, producer streams and WebSocket upgrades go out at once.

A credit policy has five shapes. eager sends a WINDOW_UPDATE for each body chunk as soon as the handler consumed it. {threshold, N} holds the credit until the uncredited octets reach N, then sends the accumulated total. {delay, Ms} sends the credit for each chunk Ms milliseconds after the handler consumed it. on_response holds the credit of a request until the HEADERS of its {reply, _, _} result go out. The credit goes in the same socket write, ahead of the HEADERS. never sends no credit.

The two policies are independent. No policy credits more than the handler consumed. Error responses, {abort, _, _} results and stream resets release no on_response credit. A stream that closes before its delayed credit is due gets no stream WINDOW_UPDATE. The connection credit for those octets still goes out.

Under the on_response connection policy the responded octets move into one accumulator. Each time the accumulator reaches h2_credit_batch, one WINDOW_UPDATE of exactly that size goes out with the response, and the remainder carries. This example imitates APNs, which credits half of its window at a time, sends no stream credit, and answers 129 to 388 ms after the request:

#{
    h2_connection_window_policy => on_response,
    h2_credit_batch => 32830,
    h2_stream_window_policy => never,
    h2_response_delay => {uniform, 129, 388}
}

Documentation

nhttp on HexDocs

License

Apache License 2.0

About

HTTP/1.1, HTTP/2, and HTTP/3 server for Erlang/OTP 27+.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Contributors

Languages