Server-Initiated Bidirectional Streams Not Received by Client
## Environment
- **web-transport-quinn version**: 0.10
- **quinn version**: 0.11
- **Platform**: Linux x86_64
- **Client**: moq-clock (moq-rs) publisher using web-transport-quinn 0.10
## Issue Description
Server-initiated bidirectional streams created via `Session::open_bi()` are successfully opened and written to on the server side, but never arrive at the client's `accept_bi()` loop.
## Expected Behavior
When the server calls `session.open_bi()`, writes data immediately, and flushes the stream (following Quinn's documented requirement), the client should be notified and able to accept the stream via its `accept_bi()` loop.
## Actual Behavior
The server successfully:
- Opens bidirectional stream via `session.open_bi()`
- Writes data immediately (2 bytes: `01 00`)
- Flushes the stream
- Spawns receive handler for incoming data
- Logs "Bidirectional stream fully initialized and ready"
However, the client:
- Never receives notification of the new stream
- `accept_bi()` loop never yields the stream
- No logs about incoming bidirectional stream
- No errors on either side
## Reproduction
### Server Code (Rust with JNI bridge)
```rust
// Open bidirectional stream
let (send, recv) = session.open_bi().await
.map_err(|e| anyhow!("Failed to open WebTransport bidirectional stream: {}", e))?;
info!("Successfully opened WebTransport bidirectional stream (synthetic ID: {})", stream_id);
// Register and write data IMMEDIATELY (per Quinn requirement)
let handle = SendStreamHandle::WebTransport(tokio::sync::Mutex::new(send));
let handle_arc = Arc::new(handle);
match handle_arc.as_ref() {
SendStreamHandle::WebTransport(send_mutex) => {
let mut send = send_mutex.lock().await;
send.write_all(&data_vec).await
.map_err(|e| anyhow!("Failed to write to WebTransport stream: {}", e))?;
// CRITICAL: Flush to ensure data is sent to network
send.flush().await
.map_err(|e| anyhow!("Failed to flush WebTransport stream: {}", e))?;
}
_ => unreachable!(),
}
info!("Wrote and flushed {} bytes of initial data to bidirectional stream {}", data_vec.len(), stream_id);
// Spawn receive task
tokio::spawn(handle_bidirectional_receive(session_id, stream_id, recv, callback.clone()));
```
### Client Code (moq-rs/moq-clock)
```rust
// moq/src/lite/publisher.rs lines 25-41
pub async fn run(mut self) -> Result<(), Error> {
loop {
let mut stream = Stream::accept(&self.session).await?; // <-- Never receives stream
let kind = stream.reader.decode().await?;
if let Err(err) = match kind {
lite::ControlType::Announce => self.recv_announce(stream).await,
lite::ControlType::Subscribe => self.recv_subscribe(stream).await,
_ => Err(Error::UnexpectedStream),
} {
tracing::warn!(%err, "control stream error");
}
}
}
```
### Server Logs (Success on Server Side)
```
INFO quinn-jni - Opening WebTransport bidirectional stream (synthetic ID: 100000) via session.open_bi()
INFO quinn-jni - Successfully opened WebTransport bidirectional stream (synthetic ID: 100000)
INFO quinn-jni - Registered bidirectional stream 100000 send side in SEND_STREAMS
INFO quinn-jni - Wrote and flushed 2 bytes of initial data to bidirectional stream 100000
INFO quinn-jni - Flushing 0 pending writes for stream 100000
INFO quinn-jni - Flushed 0 pending writes successfully
INFO quinn-jni - Spawned receive handler task for bidirectional stream 100000, task ID: Id(26)
INFO quinn-jni - Bidirectional stream 100000 fully initialized and ready
INFO quinn-jni - Starting receive handler for WebTransport bidirectional stream 100000 on session 1
```
### Client Logs (No Stream Received)
```
TRACE moq_lite::session - sending client setup
TRACE moq_lite::session - received server setup
DEBUG moq_lite::session - connected version=4279086337
```
No mention of any incoming bidirectional stream.
## Investigation Results
1. **Quinn Requirement Met**: Per Quinn documentation, "The Connection that calls open_bi() must write to its SendStream before the other Connection is able to accept_bi()." Our code writes data immediately after `open_bi()` and flushes.
2. **No Errors**: Neither server nor client logs show any errors related to stream creation or delivery.
3. **Client Accept Loop Working**: The same client successfully accepts bidirectional streams initiated by itself (client-to-server direction works fine).
4. **Session Valid**: The WebTransport session is active and healthy (control stream 0 works bidirectionally).
## Hypothesis
There may be an issue in web-transport-quinn's handling of server-initiated bidirectional streams where:
- The HTTP/3 layer doesn't properly notify the peer about the new stream
- Stream multiplexing for server-initiated streams has a bug
- There's a missing handshake or acknowledgment step
## Workaround
Send messages on the existing control stream (stream 0) instead of creating new bidirectional streams. This aligns with MoQ Transport's approach of using a single persistent bidirectional control stream.
## Additional Context
- Client-initiated bidirectional streams work fine in both directions
- Unidirectional streams work fine in both directions
- QUIC datagrams work fine
- This is blocking MoQ Lite relay implementation where the relay needs to send ANNOUNCE_PLEASE to publishers
- The same code pattern works with native Quinn connections (not WebTransport)
## Questions
1. Does web-transport-quinn v0.10 officially support server-initiated bidirectional streams?
2. Are there any known limitations or additional requirements beyond Quinn's standard `open_bi()` + immediate write pattern?
3. Is there HTTP/3 or WebTransport protocol-specific handling needed for server-initiated streams?
## Request
Please clarify if this is a known limitation, a bug, or if additional implementation steps are required for server-initiated bidirectional streams in web-transport-quinn.
0 条评论