quinn-rs / quinn

Async-friendly QUIC implementation in Rust
Apache License 2.0
3.76k stars 380 forks source link

Is there a way to detect stream creation by the peer before data is written? #1405

Closed rom1v closed 2 years ago

rom1v commented 2 years ago

Hi,

I'm fairly new in QUIC, so I have a question regarding a behavior I observe with quinn.

When a "server" awaits for the creation of a new stream (either uni or bi), the creation is not signaled until the other peer actually writes data:

let _stream = new_conn.uni_streams.try_next().await?

Here is what I observe:

$ target/debug/server
[server] [2117ms] connection accepted: addr=127.0.0.1:49668
[server] [2117ms] waiting stream opening...
[server] [7119ms] stream opened  <----- only 5 seconds after, i.e. when the client actually wrote data
$ target/debug/client
[client] [15ms] connected: addr=127.0.0.1:1234
[client] [15ms] stream opened
[client] [5017ms] data sent

I'd like the server to be notified "immediately" (without waiting for the client to write actual data to the stream) of stream creation, i.e. I'd like new_conn.uni_streams.try_next().await to return as soon as the stream is created.

Here are the main parts of the sample.

The server listens on port 1234, accepts the connection, then awaits for a uni stream to be created:

    let server_config = ServerConfig::with_single_cert(cert_chain, private_key)?;

    let bind_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 1234);
    let (endpoint, mut incoming) = Endpoint::server(server_config, bind_addr)?;

    let incoming_conn = incoming.next().await.unwrap(); // Option, not Result
    let mut new_conn = incoming_conn.await?;
    println!("[server] [{}ms] connection accepted: addr={}", start.elapsed().as_millis(), new_conn.connection.remote_address());

    println!("[server] [{}ms] waiting stream opening...", start.elapsed().as_millis());
    let _stream = new_conn.uni_streams.try_next().await?;
    println!("[server] [{}ms] stream opened", start.elapsed().as_millis());

The client connects to the server, immediately open a uni stream, awaits 5 seconds, then write to the stream:

    let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 1234);

    let new_conn = endpoint.connect(server_addr, "localhost")?.await?;
    println!("[client] [{}ms] connected: addr={}", start.elapsed().as_millis(), new_conn.connection.remote_address());

    let mut send = new_conn.connection.open_uni().await?;
    println!("[client] [{}ms] stream opened", start.elapsed().as_millis());

    tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;

    send.write_all("Hello, world!".as_bytes()).await?;
    println!("[client] [{}ms] data sent", start.elapsed().as_millis());

    send.finish().await?;

    new_conn.connection.close(0u32.into(), b"done");

The full sample project is available here: quinn-example.

Is it the expected behavior? Is it configurable, or does the QUIC protocol forbid to signal stream creation until data is written?

Ralith commented 2 years ago

Refer to the documentation:

Streams are cheap and instantaneous to open unless blocked by flow control. As a consequence, the peer won’t be notified that a stream has been opened until the stream is actually used.

If you want the peer to see something, send something. If you have nothing to send, that's an indication that maybe the peer should be the one opening the stream.

rom1v commented 2 years ago

OK, thank you for your answer :+1: