The interop run of 11 November (D30) saw a STOP_SENDING beside every REQUEST_ERROR. The relay answered a request it rejected with REQUEST_ERROR and a FIN, then dropped the direction it was reading, and dropping a receiving direction before the peer's FIN sends STOP_SENDING (QUIC on a stream not read to its end; the WebSocket binding's WT_STOP_SENDING). -22 §6.4.2.3: requests are cancelled "using RESET_STREAM for a direction they are sending and STOP_SENDING for a direction they are receiving", and "When an endpoint rejects a request without performing any application processing, it SHOULD send a REQUEST_ERROR and FIN the stream" (the same at -23). wire.rs: a request stream on which this end sent a REQUEST_ERROR (ReqTx::send marks it) is, when its reader is dropped before the peer's FIN, read to that FIN for at most 10 s (REJECTED_LINGER) and only then stopped. Every other drop is as before. tests/request_rejection.rs: a TRACK_STATUS for a track nobody publishes, over native QUIC and the WebSocket binding, is answered REQUEST_ERROR and FIN; the requester then FINs and its direction was never stopped. Falsified: with the stream never marked rejected, the requester's direction is stopped (STOP_SENDING, code 0) and the test fails. Co-Authored-By: Claude Opus 5.5 <[email protected]>
44 lines
2.6 KiB
Rust
44 lines
2.6 KiB
Rust
//! A request the relay rejects is answered REQUEST_ERROR and FIN, and not cancelled
|
|
//! (`draft-ietf-moq-transport-22` §6.4.2.3: "When an endpoint rejects a request without
|
|
//! performing any application processing, it SHOULD send a REQUEST_ERROR and FIN the stream";
|
|
//! a request is cancelled by RESET_STREAM and STOP_SENDING). Over native QUIC and the WebSocket
|
|
//! binding, a TRACK_STATUS for a track nobody publishes is answered REQUEST_ERROR and FIN; the
|
|
//! requester then sends its own FIN, and its direction was never stopped: no STOP_SENDING came
|
|
//! beside the REQUEST_ERROR. The relay used to drop the direction it was reading as it
|
|
//! answered, and its transport stopped it.
|
|
|
|
mod common;
|
|
|
|
use common::*;
|
|
use joule_moq::draft22::{ControlMessage, Parameters, TrackRequest};
|
|
use joule_moq_relay::client::ClientOptions;
|
|
use joule_moq_relay::wire::MessageReader;
|
|
use std::time::Duration;
|
|
use tokio::time::timeout;
|
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
|
|
async fn a_rejected_request_is_answered_and_finished_never_stopped() {
|
|
timeout(Duration::from_secs(60), run()).await.expect("the test finishes in time");
|
|
}
|
|
|
|
async fn run() {
|
|
let server = relay(|_| {}).await;
|
|
for via in [Via::Quic, Via::WebSocket] {
|
|
let (c, _endpoint) = connect(&server, via, ClientOptions::default()).await;
|
|
let (mut send, recv) = timeout(STEP, c.conn.open_bi()).await.expect("in time").expect("a request stream");
|
|
let ask = ControlMessage::TrackStatus(TrackRequest { request_id: c.next_request_id(), namespace: ns(&["nobody", "publishes"]), name: name("this"), params: Parameters::new() });
|
|
send.write_all(&ask.encode().unwrap()).await.expect("the request goes");
|
|
let mut reader = MessageReader::new(recv);
|
|
let answer = timeout(STEP, reader.next()).await.expect("an answer in time").expect("it reads");
|
|
assert!(matches!(answer, Some(ControlMessage::RequestError(_))), "{via:?}: {answer:?}");
|
|
let after = timeout(STEP, reader.next()).await.expect("the relay's FIN in time").expect("it reads");
|
|
assert!(after.is_none(), "{via:?}: the relay's direction ends after its REQUEST_ERROR");
|
|
// Time for a STOP_SENDING the relay might send to arrive, then this end's own FIN.
|
|
tokio::time::sleep(Duration::from_millis(300)).await;
|
|
send.finish().expect("the requester's FIN");
|
|
let stopped = timeout(STEP, send.delivered()).await.expect("acknowledged in time").expect("no transport error");
|
|
assert_eq!(stopped, None, "{via:?}: the relay stopped the requester's direction (STOP_SENDING) beside its REQUEST_ERROR");
|
|
c.close(0, "done");
|
|
}
|
|
server.shutdown().await;
|
|
}
|