Changelog

View Source

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[1.3.0] - 2026-10-19

Added

  • timeouts => #{hibernate => timeout()}: the quiet time after which a connection with nothing in flight hibernates. Default 1000 ms. 0 hibernates on every quiet moment, infinity never

Changed

  • A connection hibernates after the hibernate timeout of quiet, not after every request or WebSocket frame. The idle timeout still fires on time: the quiet wait consumes part of the idle budget and the hibernation timer gets the rest
  • Requires nhttp_lib 2.0.0
  • WebSocket frames on HTTP/1.1, HTTP/2 and HTTP/3 are decoded with nhttp_ws:feed/2. The decoder keeps the partial frame, so the server holds no WebSocket buffer of its own
  • A WebSocket session on HTTP/1.1 reads with {active, 100} instead of {active, once}, and sends the replies to all frames of one read in one socket write. A 64 B echo costs about 30% fewer reductions
  • A WebSocket session checks which optional handle_ws_* callbacks the handler exports once, when it opens. A callback added by a code reload applies to sessions opened after the reload

Fixed

  • HTTP/3: a WebSocket frame or push chunk that QUIC flow control refuses waits per stream and leaves in order on stream_writable. A Close+FIN waits in the same queue. Before, it was dropped and the send returned ok
  • A WebSocket handler reply that holds a CLOSE ends the session, and the frames after it are not sent. A reply or close with a reserved close code, a reason over 123 bytes or a control payload over 125 bytes closes the session with 1011
  • HTTP/1.1: nhttp_ws:send/2 on a closed socket returns {error, gone}, not {error, closed}
  • The opts() type declares the request limit options (max_body_size, max_headers, max_header_size, max_header_name_length, max_header_value_length, max_uri_length). The listener already read them, but a caller that set one failed dialyzer
  • get_port/1, get_port/2 and get_ports/1 took about one second to return the TCP port on an idle listener, because they waited for an acceptor to leave accept. The server now reads the port that the listener registers at startup

[1.2.1] - 2026-09-30

Changed

  • Requires nhttp_lib 1.2.1

Fixed

  • A {stream, Spec, State} result with an invalid Spec on HTTP/2 ended the whole connection. The server now resets only that stream

[1.2.0] - 2026-09-28

Added

  • Listener options h2_initial_window_size and h2_max_frame_size, aliases of the h2_settings keys
  • Listener option h2_response_delay, a fixed or uniform delay before the response headers of a {reply, _, _} result on HTTP/2
  • Listener options h2_connection_window_policy and h2_stream_window_policy, credit policies for the HTTP/2 receive windows with the shapes eager, {threshold, N}, {delay, Ms} and never
  • The on_response shape of both credit policies, which sends the credit for a request ahead of the HEADERS of its {reply, _, _} response, and the listener option h2_credit_batch that batches the connection credit

Changed

  • Updates nhttp_lib reference
  • Response bodies, producer chunks and WebSocket frames on HTTP/2 go through the codec send queue. The server keeps no remainder buffer of its own, and one connection grant reaches every blocked stream, one frame per stream per turn
  • Every RST_STREAM the server sends goes through the codec, which closes the stream and purges the octets it holds for it
  • h2_settings accepts max_send_buffer and max_queued_streams, the bounds of the codec send queue. The server defaults both to infinity. A response body, producer chunk or WebSocket frame that the bound refuses ends its stream with RST_STREAM(ENHANCE_YOUR_CALM). A producer sees {error, closed} and a WebSocket session closes with {transport, send_buffer_full}

Fixed

  • A WebSocket-over-HTTP/2 frame that outran the send window was dropped without a record
  • One large response was able to take a whole connection credit ahead of smaller ones
  • The NO_ERROR RST_STREAM that asks a client to stop sending a request body went out before the response was complete, and the buffered body then followed it on a closed stream (RFC 9113 Section 5.1 and Section 8.1)

[1.1.1] - 2026-09-10

Changed

  • Requires nhttp_lib 1.1.1

[1.1.0] - 2026-09-10

Changed

  • Requires nhttp_lib 1.1.0
  • Socket re-arming for the next read is centralized. A setopts error on a peer that closed ends the HTTP/1.1, HTTP/2, and WebSocket loops as a normal close
  • The nhttp.http.request.duration metric records native time units. The consumer converts them

Fixed

[1.0.4] - 2026-08-24

Fixed

  • HTTP/1.1 connections no longer crash when the peer closes the socket. A setopts call that gives an error now closes the connection normally. A body read that gives an error tells the handler that the peer closed the connection

[1.0.3] - 2026-08-20

Changed

  • Requires nhttp_lib 1.0.4 for correct HTTP/1.1 Content-Length handling. An empty-body response now emits Content-Length: 0, a 1xx, 204, or 304 response emits no Content-Length field, and a 2xx response to a CONNECT request can suppress it (RFC 9110 Section 8.6)

[1.0.2] - 2026-08-13

Added

  • Support for Erlang/OTP 29.

[1.0.1] - 2026-08-12

Fixed

  • WebSocket fragmented messages on HTTP/1.1. The connection keeps decoder state across reads, so a message split over several frames is reassembled and delivered once (RFC 6455 section 5.4)
  • WebSocket CLOSE codes for decode errors. A text payload or CLOSE reason that is not valid UTF-8 gives 1007, a message over max_message_size gives 1009, and other framing errors keep 1002. Before, every decode error gave 1002

Changed

  • Requires nhttp_lib 1.0.3 for the stateful frame decoder and incremental UTF-8 validation

Added

  • WebSocket RFC 6455 compliance tests with Autobahn. Run them with make ws-compliance

[1.0.0] - 2026-06-01

Initial public release.

Added

  • HTTP/1.1 and HTTP/2 server with automatic protocol selection via TLS ALPN (RFC 9112, RFC 9113)
  • HTTP/3 server over QUIC via in-process nquic library mode (RFC 9114)
  • Handler behaviour (nhttp_handler) with a small callback set for request handling, streaming, and WebSocket sessions
  • WebSocket support across HTTP/1.1, HTTP/2, and HTTP/3 with one callback set (RFC 6455, RFC 8441, RFC 9220)
  • Streaming responses via producer funs with end-to-end backpressure
  • Streaming request bodies with handler-driven backpressure
  • Server-Sent Events helpers (nhttp_sse)
  • CORS preflight and response header helpers (nhttp_cors)
  • Response compression (gzip / deflate) with MIME-aware defaults
  • PROXY protocol v1/v2 for HAProxy / AWS NLB deployments
  • SNI with static lookup table and dynamic callback
  • 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
  • Per-stream handler workers on HTTP/2 to isolate slow handlers