# flare.ws.server_h3

> A module of flare_mlake's API.

Server-side WebSocket over HTTP/3 (RFC 9220).

The HTTP/3 counterpart of :mod:`flare.ws.server_h2`: a client opens a
WebSocket with an extended CONNECT (``:protocol: websocket``) request
stream, the server answers 200 without ending the stream, and the
WebSocket frames ride DATA frames on it both ways. The handler shape is
the h2 sidecar's: edge-driven ``on_open`` / ``on_message`` /
``on_close`` over a per-tunnel :class:`WsOverH3ServerStream`, attached
with :meth:`flare.http.HttpServer.attach_ws_h3`.

## Structs

### `WsOverH3ServerStream`

```mojo
struct WsOverH3ServerStream
```

One WebSocket tunnel on an HTTP/3 stream. The RFC 6455 rules live in :class:`flare.ws.protocol.WsProtocol`; outbound frames are unmasked (RFC 6455 sec 5.3) and queued on the stream.

Implements: `Copyable`, `Movable`

Fields:

- `tunnel: Http3Tunnel`
- `proto: WsProtocol`
- `closed: Bool`

#### `__init__`

```mojo
def __init__(out self, tunnel: Http3Tunnel, var initial: List[UInt8])
```

#### `send_frame`

```mojo
def send_frame(mut self, mut listener: QuicListener, frame: WsFrame)
```

Queue ``frame`` on the tunnel; a CLOSE frame ends it.

#### `try_pull_frame`

```mojo
def try_pull_frame(mut self, mut listener: QuicListener) -> Optional[WsFrame]
```

The next complete message as one TEXT or BINARY frame, a CLOSE frame once the session has ended (its reply already sent), or ``None`` until more data arrives.

#### `is_closed`

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

#### `close`

```mojo
def close(mut self, mut listener: QuicListener, code: UInt16 = UInt16(1000), reason: String = "")
```

Send a CLOSE frame; the tunnel ends once it is sent.

### `WsH3Hooks`

```mojo
struct WsH3Hooks
```

A boxed :trait:`WsH3Handler` and its thunks. Copies are non-owning; the owner runs ``destroy`` once.

Implements: `Copyable`, `Movable`

Fields:

- `addr: Int`
- `open: def(Int, mut WsOverH3ServerStream, mut QuicListener) raises thin -> None`
- `msg: def(Int, mut WsOverH3ServerStream, mut QuicListener, WsFrame) raises thin -> None`
- `close: def(Int, mut WsOverH3ServerStream, mut QuicListener) raises thin -> None`
- `destroy: def(Int) thin -> None`
- `clone_box: def(Int) raises thin -> Int`

#### `__init__`

```mojo
def __init__(out self, addr: Int, open: def(Int, mut WsOverH3ServerStream, mut QuicListener) raises thin -> None, msg: def(Int, mut WsOverH3ServerStream, mut QuicListener, WsFrame) raises thin -> None, close: def(Int, mut WsOverH3ServerStream, mut QuicListener) raises thin -> None, destroy: def(Int) thin -> None, clone_box: def(Int) raises thin -> Int)
```

#### `clone`

```mojo
def clone(self) -> Self
```

Hooks over a fresh boxed copy of the handler (one per worker); the caller destroys it.

## Traits

### `WsH3Handler`

```mojo
undefined
```

Edge-driven handler for WebSocket-over-HTTP/3 tunnels, shared by every tunnel on the server like :trait:`flare.ws.WsH2Handler`.

Implements: `Copyable`, `Movable`

#### `on_open`

```mojo
def on_open(mut self, mut carrier: WsOverH3ServerStream, mut listener: QuicListener)
```

Called once when a tunnel is accepted.

#### `on_message`

```mojo
def on_message(mut self, mut carrier: WsOverH3ServerStream, mut listener: QuicListener, frame: WsFrame)
```

Called per decoded client frame.

#### `on_close`

```mojo
def on_close(mut self, mut carrier: WsOverH3ServerStream, mut listener: QuicListener)
```

Called once when the tunnel ends, whichever side ended it.

## Functions

### `make_ws_h3_hooks`

```mojo
def make_ws_h3_hooks[W: WsH3Handler](var handler: W) -> WsH3Hooks
```

Box ``handler``; the caller runs ``hooks.destroy(hooks.addr)`` once after the serve loop exits.

### `ws_h3_accept`

```mojo
def ws_h3_accept(hooks: WsH3Hooks, mut listener: QuicListener, mut tunnels: List[WsOverH3ServerStream])
```

Take every extended CONNECT stream that has arrived: a WebSocket one (``:protocol: websocket``, ``sec-websocket-version: 13``, RFC 9220 sec 3 and RFC 8441 sec 5) becomes a tunnel and gets ``on_open``; any other is refused (400 malformed, 501 another protocol) and its stream ends. Other requests are left for the HTTP handler.

### `ws_h3_dispatch`

```mojo
def ws_h3_dispatch(hooks: WsH3Hooks, mut listener: QuicListener, mut tunnels: List[WsOverH3ServerStream])
```

Hand each tunnel's arrived frames to ``on_message``; a tunnel whose CLOSE went out, whose peer ended or reset it, or that sent a bad frame gets ``on_close`` and is ended and dropped.
