fix: Eliminate 6s delay on concurrent H2 requests#3
Merged
Conversation
The previous passthrough rewrite moved quic_send into a spawned task for body forwarding. For GET requests (no body), the task completed immediately, dropping quic_send and closing the QUIC send half. The client interpreted this as stream termination and closed before the response arrived, causing 502 "Tunnel closed" on every request. Fix: Use an mpsc channel to forward H2 body chunks to the main task, which owns quic_send for the entire request lifetime. tokio::select! handles both body forwarding and response streaming concurrently.
The transparent streaming proxy kept TCP connections alive (HTTP/1.1 default), so local servers like Next.js never closed the connection after sending their response. This meant local_read.read() blocked indefinitely, delaying the tunnel stream close by ~6 seconds. Fix: Inject Connection: close header when rewriting Host headers for non-upgrade requests. WebSocket upgrade requests preserve the original Connection: Upgrade header.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
body_stream.data().awaitblocking on GET requeststokio::select!to keepquic_sendalive for the full request lifetimeConnection: closeon proxied HTTP/1.1 requests so local servers (Next.js, etc.) close the TCP connection after responding, instead of holding it open with keep-alive (~6s idle timeout). WebSocket upgrade requests are excludedBefore
All concurrent H2 requests stalled for ~6 seconds (serialized by body buffering + keep-alive)
After
Requests flow through instantly as a transparent bidirectional pipe
Test plan