// Serving an HTTP/3 request on a request stream (RFC 9114 §4.1): a client opens a bidirectional
// stream carrying a HEADERS frame (the pseudo-headers :method/:scheme/:authority/:path and the
// regular headers) and DATA frames (the body); the server decodes that into a request, runs a
// handler, and writes the response — a HEADERS frame with :status and the DATA of the body —
// back on the same stream. This is the connection-level routing over the HTTP/3 message codec, a
// mooncat HTTP/3 server's request/response path.
///|
/// A decoded HTTP/3 request: the pseudo-headers pulled out, the remaining header fields, and the
/// body.
pub(all) struct Http3Request {
meth : String
scheme : String
authority : String
path : String
headers : Array[(String, String)]
body : Bytes
}
///|
/// Decode an HTTP/3 request stream into a request, separating the pseudo-headers from the
/// regular ones (RFC 9114 §4.3.1). `None` when the required `:method` and `:path` are absent.
/// Raises on a malformed stream.
pub fn http3_parse_request(
bytes : Bytes,
) -> Http3Request? raise Http3FrameError {
let (all_headers, body) = http3_decode_message(bytes)
let mut meth = ""
let mut scheme = ""
let mut authority = ""
let mut path = ""
let regular : Array[(String, String)] = []
for h in all_headers {
match h.0 {
":method" => meth = h.1
":scheme" => scheme = h.1
":authority" => authority = h.1
":path" => path = h.1
_ => regular.push(h)
}
}
if meth == "" || path == "" {
None
} else {
Some({ meth, scheme, authority, path, headers: regular, body, })
}
}
///|
/// Parse a request stream, rejecting any frame the protocol does not allow on a request stream
/// (RFC 9114 §7.2): a control-only frame such as SETTINGS is an H3_FRAME_UNEXPECTED connection error
/// here, not silently ignored. Otherwise as `http3_parse_request` — `None` when the required
/// `:method` / `:path` are absent. This is the request path with frame-placement validation applied.
pub fn http3_parse_request_strict(
bytes : Bytes,
) -> Http3Request? raise Http3FrameError {
let (frames, _) = http3_frame_decode_all(bytes)
for frame in frames {
http3_check_frame(frame, RequestCtx)
}
http3_parse_request(bytes)
}
///|
/// An HTTP/3 response: the status, the header fields, and the body.
pub(all) struct Http3Response {
status : String
headers : Array[(String, String)]
body : Bytes
}
///|
/// Encode the response as an HTTP/3 response stream (a HEADERS frame with `:status`, then DATA).
pub fn Http3Response::encode(self : Http3Response) -> Bytes {
http3_response(self.status, self.headers, self.body)
}
///|
/// Serve one HTTP/3 request stream: decode the request, apply `handler`, and encode its
/// response stream. A request missing its pseudo-headers is answered with a 400. Raises on a
/// malformed request stream.
pub fn http3_serve(
request_bytes : Bytes,
handler : (Http3Request) -> Http3Response,
) -> Bytes raise Http3FrameError {
match http3_parse_request(request_bytes) {
Some(req) => handler(req).encode()
None => Http3Response::{ status: "400", headers: [], body: b"", }.encode()
}
}