# flare.http.server

> A module of flare_mlake's API.

HTTP/1.1 server with buffered reads, keep-alive, and per-connection handler callbacks.

Key performance characteristics:
- Reads from the socket in chunks (configurable, default 8KB) instead of byte-at-a-time.
- Scans for the header terminator (CRLFCRLF) in the buffer before parsing.
- Supports HTTP/1.1 keep-alive (reuses connections for multiple requests).
- Serialises the full response into a single buffer for one write_all call.
- Sets recv/send timeouts on accepted sockets for DoS resilience.
- Respects HTTP/1.0 close-by-default semantics.

## Structs

### `HttpServer`

```mojo
struct HttpServer
```

A non-blocking reactor HTTP server: HTTP/1.1, HTTP/2, HTTP/3, TLS.

One ``serve`` call runs an event loop over ``kqueue`` / ``epoll``
(opt-in ``io_uring`` on Linux), so many connections are in flight at
once rather than one per calling thread. Each accepted cleartext
connection is dispatched by peeking its first 24 bytes: the RFC 9113
§3.4 client preface selects the HTTP/2 handle, anything else the
HTTP/1.1 one. A server built by :meth:`bind_tls` handshakes on the
reactor and dispatches by ALPN instead. HTTP/3 has its own listener
and its own entry point, :meth:`serve_http3`.

``num_workers >= 2`` runs one reactor per worker thread, each with
its own copy of the handler, which is why that arity additionally
requires ``Copyable``.

Reads are buffered (default 8KB chunks) for efficient I/O.
HTTP/1.1 keep-alive is enabled by default.
Recv/send timeouts are set on accepted sockets to prevent DoS.

This type is ``Movable`` but not ``Copyable``.

Example:
```mojo
def handle(req: Request) raises -> Response:
    return Response(Status.OK, body="hello".as_bytes())

var srv = HttpServer.bind(SocketAddr.localhost(8080))
srv.serve(handle)
```

Implements: `Movable`

Fields:

- `config: ServerConfig`

#### `__init__`

```mojo
def __init__(out self, var listener: TcpListener, var config: ServerConfig = ServerConfig(Int(8192), Int(8192), Int(10485760), Int(8192), True, Int(1000), Int(500), Int(5000), Int(5000), False, Int(30000), Int(30000), Int(60000), False, False, H1LeniencyConfig(allow_lf_only_line_endings=False, allow_mixed_case_method=False, allow_ows_around_colon=False, allow_obs_fold=False, allow_oversized_request_uri=False, allow_oversized_header_list=False, accept_empty_chunk_extensions=False, allow_multiple_content_length=False, allow_te_chunked_when_cl_present=False, accept_obs_text_in_field_value=False, accept_invalid_chunk_extension_chars=False, allow_leading_whitespace_before_request_line=False), Int(0), WsUpgrade(), Http2Config()))
```

#### `__deinit__`

```mojo
def __deinit__(deinit self)
```

#### `bind`

```mojo
def bind(addr: SocketAddr, var config: ServerConfig = ServerConfig(Int(8192), Int(8192), Int(10485760), Int(8192), True, Int(1000), Int(500), Int(5000), Int(5000), False, Int(30000), Int(30000), Int(60000), False, False, H1LeniencyConfig(allow_lf_only_line_endings=False, allow_mixed_case_method=False, allow_ows_around_colon=False, allow_obs_fold=False, allow_oversized_request_uri=False, allow_oversized_header_list=False, accept_empty_chunk_extensions=False, allow_multiple_content_length=False, allow_te_chunked_when_cl_present=False, accept_obs_text_in_field_value=False, accept_invalid_chunk_extension_chars=False, allow_leading_whitespace_before_request_line=False), Int(0), WsUpgrade(), Http2Config())) -> Self
```

Bind an HTTP server on ``addr``.

Args:

- `addr`: Local address to listen on.
- `config`: HTTP/1.1 server configuration (optional).

Raises: AddressInUse: If the port is already bound.
NetworkError: For any other OS error.

```mojo
def bind(var addrs: List[SocketAddr], var config: ServerConfig = ServerConfig(Int(8192), Int(8192), Int(10485760), Int(8192), True, Int(1000), Int(500), Int(5000), Int(5000), False, Int(30000), Int(30000), Int(60000), False, False, H1LeniencyConfig(allow_lf_only_line_endings=False, allow_mixed_case_method=False, allow_ows_around_colon=False, allow_obs_fold=False, allow_oversized_request_uri=False, allow_oversized_header_list=False, accept_empty_chunk_extensions=False, allow_multiple_content_length=False, allow_te_chunked_when_cl_present=False, accept_obs_text_in_field_value=False, accept_invalid_chunk_extension_chars=False, allow_leading_whitespace_before_request_line=False), Int(0), WsUpgrade(), Http2Config())) -> Self
```

Bind an HTTP server on multiple addresses simultaneously.

The list overload of :meth:`bind`. ``bind`` takes either a
single address or a list (the pre-0.11 ``bind_many`` was removed
in v0.12).

Each address gets its own ``TcpListener`` fd; the unified
reactor loop accepts on all of them and dispatches each
accepted connection through the same handler. Useful for
binding the same service on both IPv4 and IPv6, on
multiple ports for split traffic classes (e.g. internal
admin port + public service port), or on a UNIX socket
plus a TCP socket sharing one process (after the upcoming
``UdsListener`` integration).

``addrs`` must be non-empty. The first address is the
"primary" listener (used by ``local_addr()`` and any
legacy single-listener call site); the remainder become
extras. All addresses bind in order before any returns,
so a partial-bind failure leaves no half-bound state
(already-bound listeners are dropped + closed by the
``TcpListener.__deinit__``).

Composes with multi-worker serving: ``serve(handler,
num_workers=M)`` on a multi-address server gives N addresses
x M workers, where each pairing owns its own
``SO_REUSEPORT`` listener (N*M in total, bound serially on
the scheduler thread). The listeners created here are closed
first, because ``SO_REUSEPORT`` only composes with other
``SO_REUSEPORT`` sockets and these are plain binds.

Still single-listener: ``serve_streaming``, which raises when
extras are present rather than leaving an address unserved.

Example:

```mojo
var srv = HttpServer.bind(
    [SocketAddr.localhost(8080), SocketAddr.localhost(8081)],
)
srv.serve(handler)
```

Args:

- `addrs`: One or more local addresses to listen on.
    Order matters: ``addrs[0]`` is the primary.
- `config`: HTTP/1.1 server configuration (optional).

Raises: Error: If ``addrs`` is empty.
AddressInUse: If any port is already bound.
NetworkError: For any other OS error.

#### `local_addrs`

```mojo
def local_addrs(self) -> List[SocketAddr]
```

Return the bound addresses, primary first then extras in the order they were passed to :meth:`bind`. Always returns at least one entry.

#### `bind_with_http3`

```mojo
def bind_with_http3(tcp_addr: SocketAddr, var udp_cfg: QuicServerConfig, var config: ServerConfig = ServerConfig(Int(8192), Int(8192), Int(10485760), Int(8192), True, Int(1000), Int(500), Int(5000), Int(5000), False, Int(30000), Int(30000), Int(60000), False, False, H1LeniencyConfig(allow_lf_only_line_endings=False, allow_mixed_case_method=False, allow_ows_around_colon=False, allow_obs_fold=False, allow_oversized_request_uri=False, allow_oversized_header_list=False, accept_empty_chunk_extensions=False, allow_multiple_content_length=False, allow_te_chunked_when_cl_present=False, accept_obs_text_in_field_value=False, accept_invalid_chunk_extension_chars=False, allow_leading_whitespace_before_request_line=False), Int(0), WsUpgrade(), Http2Config())) -> Self
```

Bind an HTTP server that speaks h1 / h2c / h2 over TCP on ``tcp_addr`` AND h3 over QUIC/UDP on the address in ``udp_cfg``.

The TLS ALPN list advertised by the TCP listener (h2,
http/1.1) and the QUIC listener (h3 only) is what tells
peers which wire is reachable on which transport. The
decision function
:func:`flare.http.alpn_dispatch.dispatch_alpn` routes the
negotiated ALPN identifier to the matching driver:

* ``"h3"`` -> the QUIC listener / :class:`Http3Connection`.
* ``"h2"`` -> the TCP h2 reactor / :class:`Http2ConnHandle`.
* ``"http/1.1"`` / empty -> the TCP h1 reactor /
  :class:`ConnHandle`.
* h2c upgrade hint -> H2C (TCP path only).

The two wires have two entry points. :meth:`serve` drives the
TCP reactor only; the UDP listener is driven by
:meth:`serve_http3`, so a caller serving both spawns one OS
thread per loop. The UDP listener is also reachable via
:meth:`local_http3_addr` / :meth:`tick_http3_once` for tests
that want to drive the h3 path without spinning up the full
reactor. Closing the server (via :meth:`close` or
``__deinit__``) closes both listeners.

Args:

- `tcp_addr`: Local TCP address for h1 / h2c / h2.
- `udp_cfg`: :class:`QuicServerConfig` for the h3 UDP
    bind. ``udp_cfg.host`` / ``udp_cfg.port`` apply
    to the QUIC listener; the rest of the config
    (CC choice, idle timeout, ...) is passed
    through.
- `config`: HTTP/1.1 server configuration (optional).

Raises: AddressInUse: If either port is already bound.
NetworkError: For any other OS error.

#### `bind_tls`

```mojo
def bind_tls(addr: SocketAddr, cert_file: String, key_file: String, var alpn: List[String] = List(), var config: ServerConfig = ServerConfig(Int(8192), Int(8192), Int(10485760), Int(8192), True, Int(1000), Int(500), Int(5000), Int(5000), False, Int(30000), Int(30000), Int(60000), False, False, H1LeniencyConfig(allow_lf_only_line_endings=False, allow_mixed_case_method=False, allow_ows_around_colon=False, allow_obs_fold=False, allow_oversized_request_uri=False, allow_oversized_header_list=False, accept_empty_chunk_extensions=False, allow_multiple_content_length=False, allow_te_chunked_when_cl_present=False, accept_obs_text_in_field_value=False, accept_invalid_chunk_extension_chars=False, allow_leading_whitespace_before_request_line=False), Int(0), WsUpgrade(), Http2Config())) -> Self
```

Bind an HTTPS (TLS-terminated HTTP/1.1) server on ``addr``.

Loads ``cert_file`` / ``key_file`` into a server ``SSL_CTX`` and
stashes it on the returned server; :meth:`serve` then drives
each accepted connection through a non-blocking
:class:`flare.http._reactor.tls_conn_handle.TlsConnHandle`
(handshake + ``SSL_read`` / ``SSL_write``) before dispatching the
decrypted request to the handler. The plaintext reactor path is
untouched.

Args:

- `addr`: Local address to listen on.
- `cert_file`: Path to the PEM server certificate (chain).
- `key_file`: Path to the PEM server private key.
- `alpn`: ALPN protocol identifiers to advertise, in preference
    order (e.g. ``["h2", "http/1.1"]``). Empty (the default)
    advertises no ALPN and serves HTTP/1.1. Offering ``h2``
    is served: the connection is handshaken on the reactor
    and the negotiated identifier selects the HTTP/2 or
    HTTP/1.1 handle.
- `config`: HTTP/1.1 server configuration (optional).

Raises: AddressInUse: If the port is already bound.
NetworkError: For any other OS error.
Error: If the cert / key fails to load.

```mojo
def bind_tls(addr: SocketAddr, var tls: TlsServerConfig, var config: ServerConfig = ServerConfig(Int(8192), Int(8192), Int(10485760), Int(8192), True, Int(1000), Int(500), Int(5000), Int(5000), False, Int(30000), Int(30000), Int(60000), False, False, H1LeniencyConfig(allow_lf_only_line_endings=False, allow_mixed_case_method=False, allow_ows_around_colon=False, allow_obs_fold=False, allow_oversized_request_uri=False, allow_oversized_header_list=False, accept_empty_chunk_extensions=False, allow_multiple_content_length=False, allow_te_chunked_when_cl_present=False, accept_obs_text_in_field_value=False, accept_invalid_chunk_extension_chars=False, allow_leading_whitespace_before_request_line=False), Int(0), WsUpgrade(), Http2Config())) -> Self
```

Bind an HTTPS server from a full :class:`TlsServerConfig`.

The context is built by :class:`TlsAcceptor`, so everything the
config carries applies to the reactor's handshakes: ALPN, client
certificate verification (mTLS), the minimum protocol version and
the session-ticket policy (off unless enabled). The cert/key form
of :meth:`bind_tls` reached none of these. A misconfiguration
raises here, at bind time.

#### `reload_tls`

```mojo
def reload_tls(self)
```

Re-read every certificate and key file the server was bound with and serve them to new handshakes. Safe to call from another thread while :meth:`serve` runs; connections already up keep their certificate. A pair that fails to load raises, and that pair stays as it was.

#### `has_http3`

```mojo
def has_http3(self) -> Bool
```

Whether this server has an h3 UDP listener bound.

#### `local_http3_addr`

```mojo
def local_http3_addr(self) -> SocketAddr
```

Return the local address of the h3 UDP listener.

Raises: Error: If no h3 listener is bound.

#### `advertised_alpn_protocols`

```mojo
def advertised_alpn_protocols(self) -> List[String]
```

Return the ALPN identifier list this server expects to advertise on its TLS handshakes. The TCP listener advertises ``["h2", "http/1.1"]`` and the QUIC listener advertises ``["h3"]``; here we surface the union (for diagnostics + the ``alpn_dispatch_demo`` example).

Order matters: server preference is highest -> lowest,
which :func:`flare.http.alpn_dispatch.negotiate_alpn`
consumes verbatim.

#### `route_alpn`

```mojo
def route_alpn(self, alpn: String) -> Int
```

Map a negotiated ALPN identifier to a :class:`flare.http.alpn_dispatch.WireProtocol` codepoint, cross-checked against which listeners this server has bound. ``"h3"`` routes to ``WireProtocol.HTTP_3`` only when this server has an h3 listener; otherwise the decision raises so the reactor can close the connection with ``no_application_protocol``.

Args:

- `alpn`: The ALPN identifier returned by the TLS
    handshake (empty string == "no ALPN advertised").

Raises: Error: If ``alpn == "h3"`` but no h3 listener is
bound.

#### `tick_http3_once`

```mojo
def tick_http3_once(mut self, now_ms: UInt64) -> Int
```

Advance the h3 listener's timer wheel one tick. Test- only entry point used to validate the bind path; returns the number of connections still alive after the sweep.

Raises: Error: If no h3 listener is bound.

#### `pump_http3_handler_once`

```mojo
def pump_http3_handler_once[H: Handler & Copyable](mut self, mut handler: H) -> Int
```

Drain every connection's H3 dispatcher once: for each completed request stream the handler is invoked with the materialized :class:`Request`, the resulting :class:`Response` is encoded into the slot's H3 outbox via :meth:`QuicListener.emit_http3_response`, and the outbound bytes accumulate in the per-(slot, stream_id) egress buffer.

Returns the number of (slot, stream) pairs dispatched
this pass. Zero when no H3 request is ready. The buffered
bytes leave the wire via the 1-RTT STREAM egress drain
once the slot's 1-RTT keys are installed.

Raises: Error: If no h3 listener is bound.

#### `serve_http3`

```mojo
def serve_http3[H: Handler & Copyable](mut self, var handler: H)
```

Run the QUIC reactor with H3 handler dispatch as a single-threaded loop.

This is the H3-aware blocking entry point: each iteration
runs :meth:`QuicListener.tick` to drain one inbound UDP
datagram + drive the QUIC + rustls state machines, then
:meth:`pump_http3_handler_once` to dispatch any completed
H3 request streams through ``handler``, then
:meth:`QuicListener.advance_timers` so PTO + idle +
ack-delay callbacks fire on time. Exits cleanly once
the h3 listener's stop flag flips
(:meth:`QuicListener.shutdown`).

The serve-loop pairs the TCP unified reactor (the
canonical :meth:`serve` overloads above) as a peer entry
point; callers running both wires spawn one OS thread per
loop.

Raises: Error: If no h3 listener is bound.
NetworkError: On fatal listener errors;
per-connection errors close the offending
connection silently inside ``tick``.

#### `attach_ws_h3`

```mojo
def attach_ws_h3[W: WsH3Handler](mut self, var ws_handler: W)
```

Serve WebSocket over HTTP/3 (RFC 9220) through ``ws_handler``: extended CONNECT is advertised, and ``:protocol: websocket`` requests become tunnels handed to it while :meth:`serve_http3`'s handler serves everything else. Another ``:protocol`` gets 501. Call after :meth:`bind_with_http3` and before :meth:`serve_http3`; each worker serves its own copy.

#### `serve`

```mojo
def serve(mut self, handler: def(Request) raises thin -> Response, num_workers: Int = Int(1), pin_cores: Bool = True)
```

Run the reactor loop, calling ``handler`` per request.

Plain-function overload: pass a ``def(Request) raises -> Response``
and the server wraps it in a ``FnHandler`` internally. This is
the -compatible shape; the argument list is extended with
``num_workers`` / ``pin_cores`` to match the Handler-typed
overload below so every user has one entry point to learn.

- ``num_workers == 1`` (default): single-threaded reactor
  (kqueue on macOS, epoll on Linux). Same hot path as the
  ``serve``.
- ``num_workers >= 2``: multicore — N workers
  via ``flare.runtime.scheduler.Scheduler``. By default
  each worker binds its own ``SO_REUSEPORT`` listener
  (the kernel hashes new 4-tuples to one of N listeners;
  matches actix_web's listener strategy and gives the
  highest steady-state throughput). Export
  ``FLARE_REUSEPORT_WORKERS=0`` before launch to switch
  back to the single shared listener with
  ``EPOLLEXCLUSIVE`` (Linux >= 4.5), which trades
  7-22 % req/s (handler vs static fast path) for a
  uniformly tighter p99.99 σ under sustained load; see
  ``docs/benchmark.md``. The exclusive wakeup goes to one
  waiting worker per event, so connections are not spread
  evenly: a lightly loaded server may keep landing them on the
  same worker (see ``Reactor.register_exclusive``).

For Router / middleware / stateful-struct handlers, use the
Handler-typed overload ``serve[H: Handler & Copyable]``.

Args:

- `handler`: Called once per parsed request.
- `num_workers`: Worker count. ``<= 0`` is coerced to 1.
    Values > 256 are rejected (see ``Scheduler.start``).
- `pin_cores`: On Linux, pin worker N to core ``N % num_cpus``.
    Ignored when ``num_workers == 1``. No-op on macOS.

Raises: NetworkError: On fatal listener errors; per-connection errors
close the offending connection silently.
Error: If a worker fails to spawn when
``num_workers >= 2``.

```mojo
def serve[H: Handler](mut self, var handler: H)
```

Run the single-worker reactor loop with any ``Handler``.

The arity-1 overload that accepts ``Handler``-only types
without requiring ``Copyable``. This is the right entry
point for ``Router`` (which carries heap-allocated boxed
struct handlers and is not safely ``Copyable`` for every
struct shape), middleware-wrapping handler chains whose
innermost element is a ``Router``, or any other
``Handler``-only struct.

For multi-worker mode (``num_workers >= 2``), the handler
type must be ``Copyable`` because each worker gets its
own ``H.copy()``. Use the parametric ``serve[H: Handler &
Copyable](handler, num_workers, pin_cores)`` overload
below for that.

Args:

- `handler`: The request handler (ownership transferred).

Raises: NetworkError: On fatal listener errors; per-connection
errors close the offending connection silently.

```mojo
def serve[H: Handler & Copyable](mut self, var handler: H, num_workers: Int, pin_cores: Bool = True)
```

Run the multi-worker reactor loop with a ``Copyable Handler``.

Each worker gets its own ``H.copy()`` and runs an independent
reactor on its own thread; they share a single listener fd
via ``flare.runtime.scheduler.Scheduler``. ``Copyable`` is
required here because of the per-worker copy.

- ``num_workers == 1``: routed back to the single-worker
  overload; this overload's ``Copyable`` constraint is
  stricter than necessary but the dispatch is still
  correct.
- ``num_workers >= 2``: multicore — N workers
  sharing a single listener fd via
  ``flare.runtime.scheduler.Scheduler``.

Args:

- `handler`: The request handler (ownership transferred).
- `num_workers`: Worker count. ``<= 0`` is coerced to 1.
    Values > 256 are rejected (see ``Scheduler.start``).
- `pin_cores`: On Linux, pin worker N to core ``N % num_cpus``.
    Ignored when ``num_workers == 1``. No-op on macOS.

Raises: NetworkError: On fatal listener errors; per-connection errors
close the offending connection silently.
Error: If a worker fails to spawn when
``num_workers >= 2``.

#### `attach_ws_h2`

```mojo
def attach_ws_h2[W: WsH2Handler](mut self, var ws_handler: W)
```

Bridge RFC 8441 Extended CONNECT tunnels to ``ws_handler``.

Serves ordinary HTTP through whatever handler :meth:`serve` is
given and, on the same HTTP/2 server, routes ``:protocol=
websocket`` tunnels to this sidecar. Advertising
``SETTINGS_ENABLE_CONNECT_PROTOCOL`` is turned on for you.

Call before :meth:`serve`. One instance serves every tunnel on a
worker; with several workers each serves its own copy. A
single-worker server bound to several addresses cannot carry it
(``serve`` raises).

**Changed in v0.11.** This replaces the two-handler
``serve(handler, ws_handler)`` overload (removed in v0.12). The old shape made a
WebSocket sidecar look like an argument to serving, when it is a
property of the server -- the same mistake the ``h2_config``
argument made.

Args:

- `ws_handler`: The WS-over-h2 sidecar (ownership transferred).

#### `serve_streaming`

```mojo
def serve_streaming[H: StreamHandler](mut self, var handler: H, max_in_flight: Int = Int(0), retry_after_s: Int = Int(1))
```

Run the single-threaded streaming reactor loop.

The typed-streaming counterpart of ``serve``: instead of a
request/response ``Handler``, it drives a ``StreamHandler``
through the per-connection lifecycle (``on_open`` /
``on_writable`` / ``on_upstream`` / ``on_close``) over the
reactor, owning the EPOLLOUT-driven outbound drain so the front
never blocks the event loop. Use this for multiplexing /
streaming fronts (e.g. a streaming proxy).

Single-listener, single-worker. Extra ``bind(List[SocketAddr])`` listeners
and multi-worker mode are not supported here yet.

Args:

- `handler`: The streaming front (ownership transferred). One
    instance services every connection.
- `max_in_flight`: Admission cap; ``0`` (default) = unlimited.
    When the live-connection count is at the cap, a new
    connection is refused with a 503 + ``Retry-After`` instead
    of hanging in the accept backlog.
- `retry_after_s`: ``Retry-After`` seconds advertised in the 503.

Raises: NetworkError: On fatal listener / reactor errors;
per-connection errors close the offending connection.

```mojo
def serve_streaming[H: StreamHandler & Copyable](mut self, var handler: H, num_workers: Int, pin_cores: Bool = True, max_in_flight: Int = Int(0), retry_after_s: Int = Int(1))
```

Multi-worker streaming reactor (N workers over the scheduler).

The :trait:`StreamHandler` twin of ``serve(handler, num_workers)``:
each worker runs its own copy of ``handler`` on its own reactor +
connection table (state is fully per-worker; the ``H: Copyable``
bound clones the handler once per worker before spawn). Listener
sharing (per-worker SO_REUSEPORT vs one EPOLLEXCLUSIVE fd) is the
scheduler's decision via ``FLARE_REUSEPORT_WORKERS``.

``num_workers <= 1`` falls through to the single-worker loop.
Single-listener only: extra ``bind(List[SocketAddr])`` listeners are rejected
(multi-worker uses SO_REUSEPORT, not multi-address).

#### `serve_cancellable`

```mojo
def serve_cancellable[CH: CancelHandler](mut self, var handler: CH)
```

Run the cancel-aware reactor loop with a ``CancelHandler``.

Single-threaded entry point. The reactor allocates one
``CancelCell`` per connection, hands a ``Cancel`` handle bound
to it into ``handler.serve(req, cancel)``, and flips the cell
on:

- ``CancelReason.PEER_CLOSED`` -- peer FIN observed before the
  response was queued.
- ``CancelReason.TIMEOUT`` -- idle-timeout driven.
- ``CancelReason.SHUTDOWN`` -- listener stop requested.

For plain ``Handler``s that don't observe cancellation, wrap
with ``WithCancel[H](inner=h)`` to plug them into this entry
point unchanged. This is the **shared-middleware path** for the
cancel-aware reactor: a full ``Handler`` middleware stack over a
``Router`` composes directly, because every middleware layer is
itself a ``Handler`` and ``WithCancel`` bridges the whole stack
in one wrap (cancel-awareness composes with routing)::

    var r = Router()
    r.get("/", home)
    var stack = Logger(RequestId(r^), prefix="[app]")
    var srv = HttpServer.bind(SocketAddr.localhost(8080))
    srv.serve_cancellable(WithCancel(stack^))

To run the *same* stack inside a streaming front (one process,
one logging/auth/routing stack behind both a REST control plane
and a token-streaming endpoint), call ``stack.serve(req)`` in the
``StreamHandler`` and frame the result with
``StreamConn.send_response`` (see
:mod:`flare.http.streaming_server`).

Args:

- `handler`: Cancel-aware request handler (ownership transferred).

Raises: NetworkError: On fatal listener errors.
Error: If a TLS context is bound, or ``bind`` was given several
addresses (these loops serve one listener; use ``serve``).

#### `serve_view`

```mojo
def serve_view[VH: ViewHandler](mut self, var handler: VH)
```

Run the view-aware reactor loop with a ``ViewHandler``.

Single-threaded entry point. Per-request the reactor:

1. Reads bytes into ``ConnHandle.read_buf``.
2. Parses the request as a ``RequestView`` borrowing into
   ``read_buf`` (no per-header String alloc, no body copy).
3. Dispatches into ``handler.serve_view(view, cancel)`` —
   ``view.body()`` returns ``Span[UInt8, origin]`` directly.
4. Serialises the response and resets ``read_buf`` for
   the next pipelined request.

Use this entry point for handlers that benefit from
zero-copy reads — multipart upload parsers, large-body
echos, anything that scans the body without re-encoding
it. For ``Handler.serve(req: Request)`` plug-in,
wrap with ``WithViewCancel[H](inner=h)`` (the adapter
does ``view.into_owned()`` and forwards).

Args:

- `handler`: View-aware request handler (ownership
    transferred).

Raises: NetworkError: On fatal listener errors.
Error: If a TLS context is bound, or ``bind`` was given several
addresses (these loops serve one listener; use ``serve``).

#### `serve_static`

```mojo
def serve_static(mut self, resp: StaticResponse, num_workers: Int = Int(1), pin_cores: Bool = True)
```

Run the reactor loop in static-response mode.

Every parsed request — regardless of path, method, or body — is
answered with the pre-encoded ``resp`` bytes. The reactor:

1. Reads until the end of the headers (``\r\n\r\n``).
2. Consumes the declared ``Content-Length`` bytes and discards
   them (no ``Request`` struct, no handler call).
3. Writes ``resp.keepalive_bytes`` or ``resp.close_bytes`` into
   the write queue in a single ``memcpy``, then returns the
   socket to readable-interest for the next pipelined request.

Intended for health-check endpoints, TFB plaintext benchmarks,
and any workload where the response body is genuinely static.
For heterogeneous routes that happen to share static bodies,
combine ``serve_static`` under a reverse-proxy router upstream
of the flare process.

Args:

- `resp`: Pre-encoded static response from
    ``precompute_response(...)``.
- `num_workers`: Reactor workers. Above 1 the response is served by
    ``num_workers`` threads, each with its own listener.
- `pin_cores`: Pin worker N to core N % num_cpus (Linux only).

Raises: NetworkError: On fatal listener errors; per-connection
errors close the offending connection silently.
Error: If a TLS context is bound, or ``bind`` was given several
addresses (these loops serve one listener; use ``serve``).

#### `local_addr`

```mojo
def local_addr(self) -> SocketAddr
```

Return the local address the server is bound to.

#### `close`

```mojo
def close(mut self)
```

Stop accepting new connections and break the reactor loop.

**Hard stop.** In-flight handlers may be cut mid-write — there
is no wait. Use ``drain(timeout_ms)`` for a graceful tear-down.

Idempotent. The loop finishes processing in-flight events before
returning; a concurrent caller from another thread can use this to
request graceful shutdown (the reactor's wakeup fd will be notified
automatically next iteration).

#### `drain`

```mojo
def drain(mut self, timeout_ms: Int) -> ShutdownReport
```

Graceful shutdown.

Closes the listening socket so no new connections are accepted,
keeps the reactor running for ``timeout_ms`` milliseconds so
in-flight requests and responses can finish, then breaks the
reactor loop. When the loop stops it flips ``Cancel.SHUTDOWN``
on whatever is still live and closes it.

Wires ``ServerConfig.shutdown_timeout_ms`` into a real
wait-for-drain loop.

Notes:
``drain`` is called from another thread than the one
running ``serve()``, and it blocks the caller for the whole
``timeout_ms`` -- it cannot return early when the last
connection finishes, because the reactor publishes no
live-connection count on this path (idle keep-alive
connections would also count as live). Pass the longest
wait you can tolerate.

The single-threaded reactor cannot preempt a synchronous
handler -- the handler runs to completion even if it
ignores ``Cancel.SHUTDOWN``. Cancel-aware handlers
(``CancelHandler``) observe the ``CancelReason.SHUTDOWN``
flip and short-circuit on their next ``cancel.cancelled()``
poll once the window has elapsed.

Args:

- `timeout_ms`: Milliseconds to keep serving in-flight
    connections. ``0`` is a hard stop (equivalent to
    ``close()``). Negative values are clamped to ``0``.

Raises: NetworkError: If the listener cannot be closed.
