Skip to main content

Module http2

Module http2 

Source
Expand description

Node http2 module: a REAL, minimal HTTP/2 server over TLS+ALPN.

This is a genuine HTTP/2 server (RFC 7540 framing + RFC 7541 HPACK via the hpack crate) layered on the same blocking-rustls model as tls/https. A real h2 client (curl --http2, nghttp, Node’s http2 client) can perform a GET and receive a response body from a server built with http2.createSecureServer.

── Threading model (identical discipline to net/tls) ──────────────────── Background threads NEVER touch the JS heap (a main-thread thread_local). One thread per TCP connection owns the rustls::StreamOwned, performs the TLS handshake (negotiating ALPN h2), then runs the full HTTP/2 framing loop: reading/parsing frames and writing response frames. HPACK Encoder/Decoder (with their connection-scoped dynamic tables) live on that thread. Every JS-visible effect — building the Http2Stream/Http2Session objects, emitting stream/session/request, running listeners — happens on the main thread via posted IoTask closures. The stream objects talk back to their connection thread through an mpsc channel of H2Cmds (respond/write/end enqueue a command; the owner thread encodes+writes the frame), so reads and writes stay on the single thread that owns the stateful TLS + HPACK cipher/table state.

── What IS implemented (server) ─────────────────────────────────────────────

  • TLS handshake with ALPN offering only h2; non-h2 clients are closed.
  • Client connection preface check (PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n).
  • SETTINGS: we send our (empty, valid) SETTINGS; we ACK the client’s SETTINGS and ignore incoming SETTINGS ACKs.
  • HEADERS (type 1) inbound: PADDED + PRIORITY flags stripped, header block HPACK-decoded into pseudo-headers (:method/:path/:scheme/:authority)
    • regular headers. Fires stream (and compat request).
  • DATA (type 0) inbound: emitted on the stream as data/end (request body).
  • Http2Stream.respond(headers) → HPACK-encoded HEADERS with :status.
  • Http2Stream.write(data) / .end([data]) → DATA frames (END_STREAM on end), chunked to <= 16384 (the default SETTINGS_MAX_FRAME_SIZE) per frame.
  • PING (type 6): answered with a PING ACK echoing the 8-byte payload.
  • WINDOW_UPDATE (type 8) / PRIORITY (type 2) / RST_STREAM (type 3): accepted and ignored (large-enough default windows; see caveats).
  • GOAWAY (type 7): sent on connection close; inbound GOAWAY ends the loop.

── Honest limitations (NOT implemented — deliberately, not faked) ────────────

  • http2.connect (CLIENT) is NOT implemented — it throws a clear error.
  • http2.createServer (cleartext h2c / prior-knowledge) is NOT implemented.
  • CONTINUATION frames (type 9): a HEADERS frame WITHOUT END_HEADERS is not reassembled — that stream is skipped. Real clients pack a single small GET header block into one HEADERS frame, so this rarely triggers, but a very large request header block would be dropped rather than mis-parsed.
  • Flow control is minimal: we advertise/assume the default windows and IGNORE inbound WINDOW_UPDATE for our own send side. A response body larger than the peer’s stream/connection window (65535 bytes by default) can STALL. Small responses (the common case, and the test target) are unaffected.
  • No server push, no trailers, no PRIORITY tree, no per-stream RST bookkeeping, no ALTSVC/ORIGIN. These are absent, never stubbed to look present.

── Handler-fault isolation (why the server used to close right after HEADERS) ─ The event loop treats an Err returned from a posted IoTask as FATAL (host::drive_event_loop runs task()?), which terminates the whole process and closes every live socket. events::emit propagates a listener’s error (invoke(&f, …)?). So an exception thrown by the user’s stream/request handler used to bubble out of the connection’s on_headers IoTask and kill the server the instant the first request arrived — the client sees the TCP close as a “broken pipe” with no response. To prevent one faulty handler from taking down the server, the connection IoTasks (on_headers/on_data/on_session) CATCH handler errors, print them to stderr (like Node’s uncaught-exception output, so the cause is visible), and return Ok — the loop and other connections survive. Set HTTP2_DEBUG=1 to trace every frame in/out on stderr.

── Verification status ────────────────────────────────────────────────────── The framing (9-octet header layout, big-endian 24-bit length, frame type/flag values, HPACK usage) is written to RFC 7540/7541. It has NOT been compiled or run in this session (the parent owns the shared build), so the byte-level correctness is verified-by-construction against the RFCs, not by a live curl. See the final report for the exact curl --http2 command to confirm it.

Constants§

METHODS
http2 module functions routed through stdlib::call.
SERVER_METHODS
Instance method names for the @@native tags this module owns (exposed to stdlib::instance_has_method so a method read yields a bound method).
SESSION_METHODS
STREAM_METHODS

Functions§

call
stdlib::call entry for http2.<method>.
constant
http2.constants and other non-function properties, reachable via namespace_property → stdlib::constant.
instance_call
new_emitter_object
Build a native emitter object (@@native tag + @@on/@@once maps + extras), sharing the EventEmitter shape with events/net/tls.