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 [...]"