internal/http3: avoid blocking when closing a server's QUIC endpoint

Several tests such as TestServerReceivePushStream are currently flaky.
This is because the synctest test finishes before the test server
finishes closing its QUIC endpoint.

To prevent this flakiness, make sure that the server uses a canceled
context when closing the QUIC endpoint, so as to not block.

Fixes golang/go#78100
Fixes golang/go#78101
Fixes golang/go#78102

Change-Id: I88a257d68dfbcf4e1f18b600a2f11dfdabecb078
Reviewed-on: https://go-review.googlesource.com/c/net/+/754740
Reviewed-by: Damien Neil <dneil@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Nicholas Husin <husin@google.com>
This commit is contained in:
Nicholas S. Husin
2026-03-12 12:33:51 -04:00
committed by Nicholas Husin
parent 316e20ce34
commit af2121a04e
4 changed files with 15 additions and 16 deletions

View File

@@ -4,7 +4,10 @@
package http3
import "fmt"
import (
"context"
"fmt"
)
// Stream types.
//
@@ -31,6 +34,14 @@ const (
streamTypeDecoder = streamType(0x03)
)
// canceledCtx is a canceled Context.
// Used for performing non-blocking QUIC operations.
var canceledCtx = func() context.Context {
ctx, cancel := context.WithCancel(context.Background())
cancel()
return ctx
}()
func (stype streamType) String() string {
switch stype {
case streamTypeRequest:

View File

@@ -128,7 +128,7 @@ func (s *server) listenAndServe(addr string) error {
// and handles requests from those connections.
func (s *server) serve(e *quic.Endpoint) error {
s.init()
defer e.Close(s.serveCtx)
defer e.Close(canceledCtx)
for {
qconn, err := e.Accept(s.serveCtx)
if err != nil {

View File

@@ -126,9 +126,7 @@ func (cc *clientConn) close() error {
cc.qconn.Abort(nil)
// Return any existing error from the peer, but don't wait for it.
ctx, cancel := context.WithCancel(context.Background())
cancel()
return cc.qconn.Wait(ctx)
return cc.qconn.Wait(canceledCtx)
}
func (cc *clientConn) handleControlStream(st *stream) error {

View File

@@ -160,9 +160,7 @@ func (ts *testQUICStream) wantIdle(reason string) {
ts.t.Helper()
synctest.Wait()
qs := ts.stream.stream
ctx, cancel := context.WithCancel(context.Background())
cancel()
qs.SetReadContext(ctx)
qs.SetReadContext(canceledCtx)
if _, err := qs.Read(make([]byte, 1)); !errors.Is(err, context.Canceled) {
ts.t.Fatalf("%v: want stream to be idle, but stream has content", reason)
}
@@ -523,11 +521,3 @@ func (tc *testClientConn) roundTrip(req *http.Request) *testRoundTrip {
}()
return rt
}
// canceledCtx is a canceled Context.
// Used for performing non-blocking QUIC operations.
var canceledCtx = func() context.Context {
ctx, cancel := context.WithCancel(context.Background())
cancel()
return ctx
}()