QUIC transport for MoonBit.
// A protected packet, from the connection ID up (RFC 9001 §5.2–§5.4).
let (_, server) = @crypto.initial(dcid[:])
let number = @packet.number(1L, None)
let header = long.header(number[:], payload=payload.length() + 16)
let wire = server.seal(header[:], payload[:], number=1L)
// And back, with the packet number recovered against what this space has seen.
let (plain, n) = server.open(wire[:], largest=0L).unwrap()
let frames = @packet.read_payload(plain)moon run src/examples/02-frames moon run src/examples/05-ack moon run src/examples/08-streams
moon run src/examples/03-params moon run src/examples/06-flow moon run src/examples/09-sender
moon run src/examples/04-crypto moon run src/examples/07-recovery| Specification | State | |
|---|---|---|
| frame | §19 every frame type, and §19.3.1 the ACK range encoding both ways | 0.1.0 |
| packet | §17 long and short headers, Retry, Version Negotiation, §17.1 packet numbers, §12.3 the three spaces | 0.1.0 |
| crypto | §5 packet protection, header protection and the Retry tag, §6 key update | 0.1.0 |
| recovery | RFC 9002 the round-trip estimate, both loss thresholds, the probe timeout, and NewReno, CUBIC (RFC 9438) and BBR behind one controller trait, with persistent congestion; §7.7 pacing as a token bucket over bytes | 0.1.0 |
| stream | §2 stream IDs and reassembly, §3 both state machines, §4 data flow control and the stream quota, and a round-robin scheduler | 0.1.0 |
| conn | §18 transport parameters, §12.3 the three spaces and their CRYPTO streams, the client and server handshakes, the connection table and event loop with its timers and amplification limit (§8.1, §10, §14.1), §9 migration and path validation, §10.3 stateless reset, RFC 9221 datagrams, 0-RTT packets, §6 Retry and version negotiation, the frame and parameter checks §12.4 and §18 require, many client connections on one id space, and the first stage of multipath | 0.1.0 |
let server : @conn.Endpoint[Addr] = @conn.Endpoint::server(
@conn.Config::default(),
@tls.Server::new(chain, key, alpn=["h3"]),
rand=entropy,
)
server.recv(datagram, from, now)
for conn in server.take_busy() {
for event in conn.take_events() {
match event {
Readable(id) => {
let bytes = conn.read(id)
conn.write(id, reply(bytes), fin=conn.finished(id))
}
_ => ()
}
}
}
while server.poll_out(now) is Some((datagram, to)) {
send(datagram, to)
}let client = @conn.Client::new(
@tls.Client::new(roots, alpn=["h3"]),
"a.example",
@conn.encode(params),
rand=entropy,
dcid=Bytes::make(8, 1),
scid=Bytes::make(8, 2),
now,
)
for datagram in client.poll_out(now) { send(datagram, to) }
for datagram in receive() {
client.recv(datagram, now)
for answer in client.poll_out(now) { send(answer, to) }
}client.tick(now)
for datagram in client.poll_out(now) { send(datagram, to) }client.write(0UL, request, fin=true)
for datagram in client.poll_out(now) { send(datagram, to) }let endpoint = @conn.ClientEndpoint::new(@conn.Config::default(), rand=entropy)
let id = endpoint.connect(
@tls.Client::new(roots, alpn=["h3"]),
"a.example",
params,
now,
)
endpoint.recv(datagram, now)
for answer in endpoint.poll_out(now) { send(answer, to) }
endpoint.tick(now)
let client = endpoint.client(id).unwrap()let paths = conn.paths()
paths.on_max_path_id(2UL) // what the peer said it will accept
paths.bind(1UL, spare_cid) // give a path an id of its own
conn.send(@conn.Level::Application, [@frame.PathStatus(path_id=1UL, seq=1UL, available=true)])let cfg = { ..@conn.Config::default(), max_datagram_frame_size: 1200UL }
let taken = conn.send_datagram(payload)
for datagram in conn.take_datagrams() { handle(datagram) }@crypto.initial(dcid[:]) // AEAD_AES_128_GCM, RFC 9001 §5.2
@crypto.Keys::of(secret[:], suite=@crypto.aes256) // AEAD_AES_256_GCM
@crypto.Keys::of(secret[:], suite=@crypto.chacha20) // AEAD_CHACHA20_POLY1305@recovery.State::new() // NewReno
@recovery.State::new(control=@recovery.Cubic::new()) // CUBIC, RFC 9438
@recovery.State::new(control=@recovery.Bbr::new()) // BBR| h3spec case | RFC | code |
|---|---|---|
| STREAM offset past the stream's credit | 4.1 | FLOW_CONTROL_ERROR |
| stream id past the limit | 4.6 | STREAM_LIMIT_ERROR |
| initial_source_connection_id missing | 7.3 | TRANSPORT_PARAMETER_ERROR |
| a client declaring a server's parameter | 18.2 | TRANSPORT_PARAMETER_ERROR |
| a parameter whose value is not a varint | 18.1 | TRANSPORT_PARAMETER_ERROR |
| max_udp_payload_size below 1200 | 18.2 | TRANSPORT_PARAMETER_ERROR |
| ack_delay_exponent above 20 | 18.2 | TRANSPORT_PARAMETER_ERROR |
| max_ack_delay at or above 2^14 | 18.2 | TRANSPORT_PARAMETER_ERROR |
| initial_max_streams_* past 2^60 | 18.2 | TRANSPORT_PARAMETER_ERROR |
| ACK range below packet zero | 19.3.1 | FRAME_ENCODING_ERROR |
| ACK of a packet never sent | 13.1 | PROTOCOL_VIOLATION |
| a frame of unknown type | 12.4 | FRAME_ENCODING_ERROR |
| a packet with no frames | 12.4 | PROTOCOL_VIOLATION |
| reserved bits set in a Handshake or 1-RTT packet | 17.2 | PROTOCOL_VIOLATION |
| CRYPTO buffered past the limit | 7.5 | CRYPTO_BUFFER_EXCEEDED |
| PATH_CHALLENGE, DATA_BLOCKED or STREAM_DATA_BLOCKED in a Handshake packet | 12.4 | PROTOCOL_VIOLATION |
| RETIRE_CONNECTION_ID for an id never issued | 19.16 | PROTOCOL_VIOLATION |
| RESET_STREAM or STREAM on a send-only stream | 19.4, 19.8 | STREAM_STATE_ERROR |
| STREAM on a locally initiated stream not opened | 19.8 | STREAM_STATE_ERROR |
| STOP_SENDING or MAX_STREAM_DATA for a stream that does not exist | 19.5, 19.10 | STREAM_STATE_ERROR |
| MAX_STREAM_DATA for a receive-only stream | 19.10 | STREAM_STATE_ERROR |
| invalid MAX_STREAMS | 19.11 | FRAME_ENCODING_ERROR |
| invalid STREAMS_BLOCKED | 19.14 | STREAM_LIMIT_ERROR |
| NEW_CONNECTION_ID with a zero-length id or a bad retire_prior_to | 19.15 | FRAME_ENCODING_ERROR |
| NEW_TOKEN or HANDSHAKE_DONE from a client | 19.7, 19.20 | PROTOCOL_VIOLATION |
| KeyUpdate in Handshake or 1-RTT | RFC 9001 §6 | CRYPTO_ERROR with unexpected_message |
| EndOfEarlyData, or no ALPN overlap | RFC 8446 | CRYPTO_ERROR with the alert |
Install
Download zipQUIC transport for MoonBit.