// The HTTP/2 stream state machine (RFC 7540 §5.1) and the §5.1.1 identifier rules.
// A stream walks idle -> open -> half-closed -> closed as END_STREAM headers and
// data are sent and received; an illegal frame for the current state is rejected
// rather than silently accepted. This drives one unary call's stream through its
// whole lifecycle from the client side, then shows a rejected transition and the
// odd/even id rules that keep client- and server-initiated streams from colliding.
//
// moon run src/examples/03-stream-lifecycle
///|
#coverage.skip
fn main {
let s = @moonrpc.Stream::new(1)
println("stream \{1}: start \{s.state}")
try {
// The client opens the stream with request HEADERS, streams the request body,
// and half-closes with END_STREAM — now it can only receive.
println(" send HEADERS -> \{s.send(Headers(end_stream=false))}")
println(" send DATA(END_STREAM) -> \{s.send(Data(end_stream=true))}")
// The server's response HEADERS, body, and grpc-status trailer arrive; the
// END_STREAM trailer closes the stream.
println(" recv HEADERS -> \{s.recv(Headers(end_stream=false))}")
println(" recv DATA -> \{s.recv(Data(end_stream=false))}")
println(" recv HEADERS(END_STREAM)-> \{s.recv(Headers(end_stream=true))}")
} catch {
e => println(" unexpected: \{e}")
}
// A fresh stream cannot receive DATA before any HEADERS opened it — the state
// machine raises instead of inventing a transition.
let idle = @moonrpc.Stream::new(3)
let outcome = try {
let _ = idle.recv(Data(end_stream=false))
"accepted (unexpected)"
} catch {
e => "rejected -> \{e}"
}
println("illegal DATA in idle: \{outcome}")
// §5.1.1: clients open odd streams, servers open even ones, 0 is the connection
// control stream that neither may open.
println("id 1 client-initiated: \{@moonrpc.stream_is_client_initiated(1)}")
println("id 2 server-initiated: \{@moonrpc.stream_is_server_initiated(2)}")
println(
"id 2 legal for a client to open: \{@moonrpc.stream_id_valid_for_initiator(2, by_client=true)}",
)
println(
"id 0 legal for anyone to open: \{@moonrpc.stream_id_valid_for_initiator(0, by_client=true)}",
)
}