# `Quiver.HTTP3`
[🔗](https://github.com/edlontech/quiver/blob/main/lib/quiver/http3.ex#L1)

HTTP/3 datagram channel API (RFC 9297).

Opens an HTTP/3 request stream that is kept open without auto-ending and
drives a user handler with response, datagram, stream-data, trailer, and
close events. The handler may call `send_datagram/2` and
`max_datagram_size/1` against an opaque `%Quiver.HTTP3.Channel{}` to push
datagrams back.

## Sample usage

    {:ok, final_acc} =
      Quiver.HTTP3.open_datagram_channel(
        "https://h3.example/wt/session",
        [method: :connect, protocol: "webtransport"],
        fn
          {:response, 200, _hs}, channel, acc ->
            Quiver.HTTP3.send_datagram(channel, "hello")
            {:cont, acc}

          {:datagram, _payload}, _ch, acc ->
            {:cont, acc}

          {:closed, _reason}, _ch, acc ->
            {:halt, acc}
        end,
        []
      )

See `guides/http3.md` for the full cookbook including extended CONNECT
and the WebTransport-style `:protocol` header.

# `close_reason`

```elixir
@type close_reason() ::
  :peer
  | {:reset, non_neg_integer()}
  | {:goaway, non_neg_integer()}
  | {:transport, Quiver.Error.QUICTransportError.t()}
```

# `event`

```elixir
@type event() ::
  {:response, status(), headers()}
  | {:datagram, payload()}
  | {:stream_data, binary()}
  | {:trailers, headers()}
  | {:closed, close_reason()}
```

# `handler`

```elixir
@type handler() :: (event(), Quiver.HTTP3.Channel.t(), term() -&gt;
                {:cont, term()} | {:halt, term()})
```

# `headers`

```elixir
@type headers() :: [{binary(), binary()}]
```

# `payload`

```elixir
@type payload() :: binary()
```

# `status`

```elixir
@type status() :: 100..599
```

# `h3_datagrams_enabled?`

```elixir
@spec h3_datagrams_enabled?(Quiver.HTTP3.Channel.t()) :: boolean()
```

Returns `true` if both peers negotiated SETTINGS_H3_DATAGRAM=1 and the
underlying QUIC connection has `max_datagram_frame_size > 0`.

# `max_datagram_size`

```elixir
@spec max_datagram_size(Quiver.HTTP3.Channel.t()) :: non_neg_integer()
```

Returns the maximum usable datagram payload size on `channel`.

Returns `0` when the extension is not negotiated. Otherwise the value
is the per-datagram payload limit after subtracting the QSID varint
prefix `:quic_h3` prepends internally.

# `open_datagram_channel`

```elixir
@spec open_datagram_channel(String.t(), keyword(), handler(), term()) ::
  {:ok, term()} | {:error, term()}
```

Opens an HTTP/3 datagram channel and drives `handler` until it halts or
the channel closes.

Synchronous from the caller's perspective: the calling process owns the
event mailbox and runs the reduce loop.

See module docs for options and event semantics.

Returns `{:ok, acc}` on normal close or halt, `{:error, reason}` on
open failure / timeout / disabled datagrams.

# `send_datagram`

```elixir
@spec send_datagram(Quiver.HTTP3.Channel.t(), iodata()) ::
  :ok | {:error, Exception.t()}
```

Sends a datagram on `channel`. Bypasses the pool worker by calling
`:quic_h3.send_datagram/3` directly for hot-path speed.

Returns `:ok` on success or `{:error, exception}` where the exception is
either `Quiver.Error.H3DatagramsDisabled` (peer didn't negotiate) or
`Quiver.Error.H3DatagramError` (transport / sizing / lifecycle).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
