internal/http3: report endpoint creation errors from initEndpoint Fix err shadow bug. Change-Id: Id66e8bb3d94cb9ff36c94f5bb74242b66a6a6964 Reviewed-on: https://go-review.googlesource.com/c/net/+/823104 Reviewed-by: Nicholas Husin <husin@google.com> Reviewed-by: David Chase <drchase@google.com> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Nicholas Husin <nsh@golang.org>
diff --git a/internal/http3/transport.go b/internal/http3/transport.go index 0a1b701..c9b3a8a 100644 --- a/internal/http3/transport.go +++ b/internal/http3/transport.go
@@ -142,7 +142,8 @@ if tr.opts.ListenQUIC != nil { tr.endpoint, err = tr.opts.ListenQUIC(":0", quicConfig) } else if tr.opts.ListenPacket != nil { - conn, err := tr.opts.ListenPacket("udp", ":0") + var conn net.PacketConn + conn, err = tr.opts.ListenPacket("udp", ":0") if err != nil { return err }
diff --git a/internal/http3/transport_test.go b/internal/http3/transport_test.go index e2f3b11..57ec034 100644 --- a/internal/http3/transport_test.go +++ b/internal/http3/transport_test.go
@@ -7,21 +7,73 @@ import ( "bytes" "context" + "crypto/tls" "errors" "fmt" "io" "maps" "math" + "net" "net/http" "reflect" "slices" "testing" "testing/synctest" + "time" "golang.org/x/net/internal/quic/quicwire" "golang.org/x/net/quic" ) +// unusablePacketConn is a net.PacketConn whose LocalAddr is not a valid +// UDP address, which makes quic.NewEndpoint fail. +type unusablePacketConn struct { + net.PacketConn + closed bool +} + +func (c *unusablePacketConn) LocalAddr() net.Addr { + return &net.UnixAddr{Name: "unusable", Net: "unix"} +} + +func (c *unusablePacketConn) Close() error { + c.closed = true + return nil +} + +// TestTransportInitEndpointError verifies that a transport which fails to +// create its QUIC endpoint reports the error, rather than proceeding with a +// nil endpoint. +func TestTransportInitEndpointError(t *testing.T) { + conn := &unusablePacketConn{} + tr := &transport{ + tr1: &http.Transport{TLSClientConfig: &tls.Config{}}, + opts: TransportOpts{ + ListenPacket: func(network, addr string) (net.PacketConn, error) { + return conn, nil + }, + }, + activeConns: make(map[*clientConn]struct{}), + } + + if err := tr.initEndpoint(); err == nil { + t.Fatal("initEndpoint() = nil, want error") + } + if tr.endpoint != nil { + t.Errorf("after failed initEndpoint, transport.endpoint = %v, want nil", tr.endpoint) + } + if !conn.closed { + t.Errorf("after failed initEndpoint, the net.PacketConn was not closed") + } + + // dial must report the error rather than panicking on the nil endpoint. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + if _, err := tr.dial(ctx, "127.0.0.1:443", &tls.Config{}, nil); err == nil { + t.Fatal("dial() = nil error, want error") + } +} + func TestTransportServerCreatesBidirectionalStream(t *testing.T) { // "Clients MUST treat receipt of a server-initiated bidirectional // stream as a connection error of type H3_STREAM_CREATION_ERROR [...]"