aboutsummaryrefslogtreecommitdiff
path: root/src/vendor/golang.org/x/net/internal
diff options
context:
space:
mode:
authorNicholas S. Husin <nsh@golang.org>2026-03-30 19:17:03 -0400
committerNicholas Husin <nsh@golang.org>2026-04-10 08:24:28 -0700
commit2f3c778b232dd53c41e1b623d25cd9f4ab28aaa5 (patch)
tree3959d70ffde2c7c385b0cc5c62cb3ac56dbe1516 /src/vendor/golang.org/x/net/internal
parentce4459cf0ee339b3bcf0ed10427079a234aade36 (diff)
downloadgo-2f3c778b232dd53c41e1b623d25cd9f4ab28aaa5.tar.xz
net/http: add support for running HTTP tests against HTTP/3
Add support within clientserver_test.go to bring up a test HTTP/3 server and client when http3Mode testMode option is passed. To be able to reuse net/http/httptest, net/http/httptest.Server.StartTLS (and Start) have been modified so they can be called with a nil Listener. In such cases, both methods will behave identically as usual, but will not actually make its server serve or set its transport dialer, both of which requires having a listener. This should be a no-op for regular users of the package, whose entrypoint via functions such as NewServer will automatically set a local listener. Actually enabling HTTP/3 for our tests will be done in a separate CL. For #70914 Change-Id: Ibc5fc83287b6a04b46e668a54924761a92b620a4 Reviewed-on: https://go-review.googlesource.com/c/go/+/740122 Reviewed-by: Damien Neil <dneil@google.com> Reviewed-by: Nicholas Husin <husin@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/vendor/golang.org/x/net/internal')
-rw-r--r--src/vendor/golang.org/x/net/internal/http3/body.go230
-rw-r--r--src/vendor/golang.org/x/net/internal/http3/conn.go131
-rw-r--r--src/vendor/golang.org/x/net/internal/http3/doc.go10
-rw-r--r--src/vendor/golang.org/x/net/internal/http3/errors.go102
-rw-r--r--src/vendor/golang.org/x/net/internal/http3/http3.go95
-rw-r--r--src/vendor/golang.org/x/net/internal/http3/qpack.go332
-rw-r--r--src/vendor/golang.org/x/net/internal/http3/qpack_decode.go81
-rw-r--r--src/vendor/golang.org/x/net/internal/http3/qpack_encode.go45
-rw-r--r--src/vendor/golang.org/x/net/internal/http3/qpack_static.go142
-rw-r--r--src/vendor/golang.org/x/net/internal/http3/quic.go40
-rw-r--r--src/vendor/golang.org/x/net/internal/http3/roundtrip.go417
-rw-r--r--src/vendor/golang.org/x/net/internal/http3/server.go791
-rw-r--r--src/vendor/golang.org/x/net/internal/http3/settings.go66
-rw-r--r--src/vendor/golang.org/x/net/internal/http3/stream.go260
-rw-r--r--src/vendor/golang.org/x/net/internal/http3/transport.go284
-rw-r--r--src/vendor/golang.org/x/net/internal/http3/varint.go23
-rw-r--r--src/vendor/golang.org/x/net/internal/httpcommon/ascii.go53
-rw-r--r--src/vendor/golang.org/x/net/internal/httpcommon/headermap.go115
-rw-r--r--src/vendor/golang.org/x/net/internal/httpcommon/request.go467
-rw-r--r--src/vendor/golang.org/x/net/internal/quic/quicwire/wire.go150
20 files changed, 3834 insertions, 0 deletions
diff --git a/src/vendor/golang.org/x/net/internal/http3/body.go b/src/vendor/golang.org/x/net/internal/http3/body.go
new file mode 100644
index 0000000000..6db183beb8
--- /dev/null
+++ b/src/vendor/golang.org/x/net/internal/http3/body.go
@@ -0,0 +1,230 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package http3
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "net/http"
+ "net/textproto"
+ "strings"
+ "sync"
+
+ "golang.org/x/net/http/httpguts"
+)
+
+// extractTrailerFromHeader extracts the "Trailer" header values from a header
+// map, and populates a trailer map with those values as keys. The extracted
+// header values will be canonicalized.
+func extractTrailerFromHeader(header, trailer http.Header) {
+ for _, names := range header["Trailer"] {
+ names = textproto.TrimString(names)
+ for name := range strings.SplitSeq(names, ",") {
+ name = textproto.CanonicalMIMEHeaderKey(textproto.TrimString(name))
+ if !httpguts.ValidTrailerHeader(name) {
+ continue
+ }
+ trailer[name] = nil
+ }
+ }
+}
+
+// A bodyWriter writes a request or response body to a stream
+// as a series of DATA frames.
+type bodyWriter struct {
+ st *stream
+ remain int64 // -1 when content-length is not known
+ flush bool // flush the stream after every write
+ name string // "request" or "response"
+ trailer http.Header // trailer headers that will be written once bodyWriter is closed.
+ enc *qpackEncoder // QPACK encoder used by the connection.
+}
+
+func (w *bodyWriter) write(ps ...[]byte) (n int, err error) {
+ var size int64
+ for _, p := range ps {
+ size += int64(len(p))
+ }
+ // If write is called with empty byte slices, just return instead of
+ // sending out a DATA frame containing nothing.
+ if size == 0 {
+ return 0, nil
+ }
+ if w.remain >= 0 && size > w.remain {
+ return 0, &streamError{
+ code: errH3InternalError,
+ message: w.name + " body longer than specified content length",
+ }
+ }
+ w.st.writeVarint(int64(frameTypeData))
+ w.st.writeVarint(size)
+ for _, p := range ps {
+ var n2 int
+ n2, err = w.st.Write(p)
+ n += n2
+ if w.remain >= 0 {
+ w.remain -= int64(n)
+ }
+ if err != nil {
+ break
+ }
+ }
+ if w.flush && err == nil {
+ err = w.st.Flush()
+ }
+ if err != nil {
+ err = fmt.Errorf("writing %v body: %w", w.name, err)
+ }
+ return n, err
+}
+
+func (w *bodyWriter) Write(p []byte) (n int, err error) {
+ return w.write(p)
+}
+
+func (w *bodyWriter) Close() error {
+ if w.remain > 0 {
+ return errors.New(w.name + " body shorter than specified content length")
+ }
+ if len(w.trailer) > 0 {
+ encTrailer := w.enc.encode(func(f func(itype indexType, name, value string)) {
+ for name, values := range w.trailer {
+ if !httpguts.ValidHeaderFieldName(name) {
+ continue
+ }
+ for _, val := range values {
+ if !httpguts.ValidHeaderFieldValue(val) {
+ continue
+ }
+ f(mayIndex, name, val)
+ }
+ }
+ })
+ w.st.writeVarint(int64(frameTypeHeaders))
+ w.st.writeVarint(int64(len(encTrailer)))
+ w.st.Write(encTrailer)
+ }
+ if w.st != nil && w.st.stream != nil {
+ w.st.stream.CloseWrite()
+ }
+ return nil
+}
+
+// A bodyReader reads a request or response body from a stream.
+type bodyReader struct {
+ st *stream
+
+ mu sync.Mutex
+ remain int64
+ err error
+ // If not nil, the body contains an "Expect: 100-continue" header, and
+ // send100Continue should be called when Read is invoked for the first
+ // time.
+ send100Continue func()
+ // A map where the key represents the trailer header names we expect. If
+ // there is a HEADERS frame after reading DATA frames to EOF, the value of
+ // the headers will be written here, provided that the name of the header
+ // exists in the map already.
+ trailer http.Header
+}
+
+func (r *bodyReader) Read(p []byte) (n int, err error) {
+ // The HTTP/1 and HTTP/2 implementations both permit concurrent reads from a body,
+ // in the sense that the race detector won't complain.
+ // Use a mutex here to provide the same behavior.
+ r.mu.Lock()
+ defer r.mu.Unlock()
+ if r.send100Continue != nil {
+ r.send100Continue()
+ r.send100Continue = nil
+ }
+ if r.err != nil {
+ return 0, r.err
+ }
+ defer func() {
+ if err != nil {
+ r.err = err
+ }
+ }()
+ if r.st.lim == 0 {
+ // We've finished reading the previous DATA frame, so end it.
+ if err := r.st.endFrame(); err != nil {
+ return 0, err
+ }
+ }
+ // Read the next DATA frame header,
+ // if we aren't already in the middle of one.
+ for r.st.lim < 0 {
+ ftype, err := r.st.readFrameHeader()
+ if err == io.EOF && r.remain > 0 {
+ return 0, &streamError{
+ code: errH3MessageError,
+ message: "body shorter than content-length",
+ }
+ }
+ if err != nil {
+ return 0, err
+ }
+ switch ftype {
+ case frameTypeData:
+ if r.remain >= 0 && r.st.lim > r.remain {
+ return 0, &streamError{
+ code: errH3MessageError,
+ message: "body longer than content-length",
+ }
+ }
+ // Fall out of the loop and process the frame body below.
+ case frameTypeHeaders:
+ // This HEADERS frame contains the message trailers.
+ if r.remain > 0 {
+ return 0, &streamError{
+ code: errH3MessageError,
+ message: "body shorter than content-length",
+ }
+ }
+ var dec qpackDecoder
+ if err := dec.decode(r.st, func(_ indexType, name, value string) error {
+ if _, ok := r.trailer[name]; ok {
+ r.trailer.Add(name, value)
+ }
+ return nil
+ }); err != nil {
+ return 0, err
+ }
+ if err := r.st.discardFrame(); err != nil {
+ return 0, err
+ }
+ return 0, io.EOF
+ default:
+ if err := r.st.discardUnknownFrame(ftype); err != nil {
+ return 0, err
+ }
+ }
+ }
+ // We are now reading the content of a DATA frame.
+ // Fill the read buffer or read to the end of the frame,
+ // whichever comes first.
+ if int64(len(p)) > r.st.lim {
+ p = p[:r.st.lim]
+ }
+ n, err = r.st.Read(p)
+ if r.remain > 0 {
+ r.remain -= int64(n)
+ }
+ return n, err
+}
+
+func (r *bodyReader) Close() error {
+ // Unlike the HTTP/1 and HTTP/2 body readers (at the time of this comment being written),
+ // calling Close concurrently with Read will interrupt the read.
+ r.st.stream.CloseRead()
+ // Make sure that any data that has already been written to bodyReader
+ // cannot be read after it has been closed.
+ r.err = net.ErrClosed
+ r.remain = 0
+ return nil
+}
diff --git a/src/vendor/golang.org/x/net/internal/http3/conn.go b/src/vendor/golang.org/x/net/internal/http3/conn.go
new file mode 100644
index 0000000000..6a3c962b41
--- /dev/null
+++ b/src/vendor/golang.org/x/net/internal/http3/conn.go
@@ -0,0 +1,131 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package http3
+
+import (
+ "context"
+ "io"
+ "sync"
+
+ "golang.org/x/net/quic"
+)
+
+type streamHandler interface {
+ handleControlStream(*stream) error
+ handlePushStream(*stream) error
+ handleEncoderStream(*stream) error
+ handleDecoderStream(*stream) error
+ handleRequestStream(*stream) error
+ abort(error)
+}
+
+type genericConn struct {
+ mu sync.Mutex
+
+ // The peer may create exactly one control, encoder, and decoder stream.
+ // streamsCreated is a bitset of streams created so far.
+ // Bits are 1 << streamType.
+ streamsCreated uint8
+}
+
+func (c *genericConn) acceptStreams(qconn *quic.Conn, h streamHandler) {
+ for {
+ // Use context.Background: This blocks until a stream is accepted
+ // or the connection closes.
+ st, err := qconn.AcceptStream(context.Background())
+ if err != nil {
+ return // connection closed
+ }
+ if st.IsReadOnly() {
+ go c.handleUnidirectionalStream(newStream(st), h)
+ } else {
+ go c.handleRequestStream(newStream(st), h)
+ }
+ }
+}
+
+func (c *genericConn) handleUnidirectionalStream(st *stream, h streamHandler) {
+ // Unidirectional stream header: One varint with the stream type.
+ v, err := st.readVarint()
+ if err != nil {
+ h.abort(&connectionError{
+ code: errH3StreamCreationError,
+ message: "error reading unidirectional stream header",
+ })
+ return
+ }
+ stype := streamType(v)
+ if err := c.checkStreamCreation(stype); err != nil {
+ h.abort(err)
+ return
+ }
+ switch stype {
+ case streamTypeControl:
+ err = h.handleControlStream(st)
+ case streamTypePush:
+ err = h.handlePushStream(st)
+ case streamTypeEncoder:
+ err = h.handleEncoderStream(st)
+ case streamTypeDecoder:
+ err = h.handleDecoderStream(st)
+ default:
+ // "Recipients of unknown stream types MUST either abort reading
+ // of the stream or discard incoming data without further processing."
+ // https://www.rfc-editor.org/rfc/rfc9114.html#section-6.2-7
+ //
+ // We should send the H3_STREAM_CREATION_ERROR error code,
+ // but the quic package currently doesn't allow setting error codes
+ // for STOP_SENDING frames.
+ // TODO: Should CloseRead take an error code?
+ err = nil
+ }
+ if err == io.EOF {
+ err = &connectionError{
+ code: errH3ClosedCriticalStream,
+ message: streamType(stype).String() + " stream closed",
+ }
+ }
+ c.handleStreamError(st, h, err)
+}
+
+func (c *genericConn) handleRequestStream(st *stream, h streamHandler) {
+ c.handleStreamError(st, h, h.handleRequestStream(st))
+}
+
+func (c *genericConn) handleStreamError(st *stream, h streamHandler, err error) {
+ switch err := err.(type) {
+ case *connectionError:
+ h.abort(err)
+ case nil:
+ st.stream.CloseRead()
+ st.stream.CloseWrite()
+ case *streamError:
+ st.stream.CloseRead()
+ st.stream.Reset(uint64(err.code))
+ default:
+ st.stream.CloseRead()
+ st.stream.Reset(uint64(errH3InternalError))
+ }
+}
+
+func (c *genericConn) checkStreamCreation(stype streamType) error {
+ switch stype {
+ case streamTypeControl, streamTypeEncoder, streamTypeDecoder:
+ // The peer may create exactly one control, encoder, and decoder stream.
+ default:
+ return nil
+ }
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ bit := uint8(1) << stype
+ if c.streamsCreated&bit != 0 {
+ return &connectionError{
+ code: errH3StreamCreationError,
+ message: "multiple " + stype.String() + " streams created",
+ }
+ }
+ c.streamsCreated |= bit
+ return nil
+}
diff --git a/src/vendor/golang.org/x/net/internal/http3/doc.go b/src/vendor/golang.org/x/net/internal/http3/doc.go
new file mode 100644
index 0000000000..5530113f69
--- /dev/null
+++ b/src/vendor/golang.org/x/net/internal/http3/doc.go
@@ -0,0 +1,10 @@
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package http3 implements the HTTP/3 protocol.
+//
+// This package is a work in progress.
+// It is not ready for production usage.
+// Its API is subject to change without notice.
+package http3
diff --git a/src/vendor/golang.org/x/net/internal/http3/errors.go b/src/vendor/golang.org/x/net/internal/http3/errors.go
new file mode 100644
index 0000000000..273ad014a6
--- /dev/null
+++ b/src/vendor/golang.org/x/net/internal/http3/errors.go
@@ -0,0 +1,102 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package http3
+
+import "fmt"
+
+// http3Error is an HTTP/3 error code.
+type http3Error int
+
+const (
+ // https://www.rfc-editor.org/rfc/rfc9114.html#section-8.1
+ errH3NoError = http3Error(0x0100)
+ errH3GeneralProtocolError = http3Error(0x0101)
+ errH3InternalError = http3Error(0x0102)
+ errH3StreamCreationError = http3Error(0x0103)
+ errH3ClosedCriticalStream = http3Error(0x0104)
+ errH3FrameUnexpected = http3Error(0x0105)
+ errH3FrameError = http3Error(0x0106)
+ errH3ExcessiveLoad = http3Error(0x0107)
+ errH3IDError = http3Error(0x0108)
+ errH3SettingsError = http3Error(0x0109)
+ errH3MissingSettings = http3Error(0x010a)
+ errH3RequestRejected = http3Error(0x010b)
+ errH3RequestCancelled = http3Error(0x010c)
+ errH3RequestIncomplete = http3Error(0x010d)
+ errH3MessageError = http3Error(0x010e)
+ errH3ConnectError = http3Error(0x010f)
+ errH3VersionFallback = http3Error(0x0110)
+
+ // https://www.rfc-editor.org/rfc/rfc9204.html#section-8.3
+ errQPACKDecompressionFailed = http3Error(0x0200)
+ errQPACKEncoderStreamError = http3Error(0x0201)
+ errQPACKDecoderStreamError = http3Error(0x0202)
+)
+
+func (e http3Error) Error() string {
+ switch e {
+ case errH3NoError:
+ return "H3_NO_ERROR"
+ case errH3GeneralProtocolError:
+ return "H3_GENERAL_PROTOCOL_ERROR"
+ case errH3InternalError:
+ return "H3_INTERNAL_ERROR"
+ case errH3StreamCreationError:
+ return "H3_STREAM_CREATION_ERROR"
+ case errH3ClosedCriticalStream:
+ return "H3_CLOSED_CRITICAL_STREAM"
+ case errH3FrameUnexpected:
+ return "H3_FRAME_UNEXPECTED"
+ case errH3FrameError:
+ return "H3_FRAME_ERROR"
+ case errH3ExcessiveLoad:
+ return "H3_EXCESSIVE_LOAD"
+ case errH3IDError:
+ return "H3_ID_ERROR"
+ case errH3SettingsError:
+ return "H3_SETTINGS_ERROR"
+ case errH3MissingSettings:
+ return "H3_MISSING_SETTINGS"
+ case errH3RequestRejected:
+ return "H3_REQUEST_REJECTED"
+ case errH3RequestCancelled:
+ return "H3_REQUEST_CANCELLED"
+ case errH3RequestIncomplete:
+ return "H3_REQUEST_INCOMPLETE"
+ case errH3MessageError:
+ return "H3_MESSAGE_ERROR"
+ case errH3ConnectError:
+ return "H3_CONNECT_ERROR"
+ case errH3VersionFallback:
+ return "H3_VERSION_FALLBACK"
+ case errQPACKDecompressionFailed:
+ return "QPACK_DECOMPRESSION_FAILED"
+ case errQPACKEncoderStreamError:
+ return "QPACK_ENCODER_STREAM_ERROR"
+ case errQPACKDecoderStreamError:
+ return "QPACK_DECODER_STREAM_ERROR"
+ }
+ return fmt.Sprintf("H3_ERROR_%v", int(e))
+}
+
+// A streamError is an error which terminates a stream, but not the connection.
+// https://www.rfc-editor.org/rfc/rfc9114.html#section-8-1
+type streamError struct {
+ code http3Error
+ message string
+}
+
+func (e *streamError) Error() string { return e.message }
+func (e *streamError) Unwrap() error { return e.code }
+
+// A connectionError is an error which results in the entire connection closing.
+// https://www.rfc-editor.org/rfc/rfc9114.html#section-8-2
+type connectionError struct {
+ code http3Error
+ message string
+}
+
+func (e *connectionError) Error() string { return e.message }
+func (e *connectionError) Unwrap() error { return e.code }
diff --git a/src/vendor/golang.org/x/net/internal/http3/http3.go b/src/vendor/golang.org/x/net/internal/http3/http3.go
new file mode 100644
index 0000000000..189e3e749b
--- /dev/null
+++ b/src/vendor/golang.org/x/net/internal/http3/http3.go
@@ -0,0 +1,95 @@
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package http3
+
+import (
+ "context"
+ "fmt"
+)
+
+// Stream types.
+//
+// For unidirectional streams, the value is the stream type sent over the wire.
+//
+// For bidirectional streams (which are always request streams),
+// the value is arbitrary and never sent on the wire.
+type streamType int64
+
+const (
+ // Bidirectional request stream.
+ // All bidirectional streams are request streams.
+ // This stream type is never sent over the wire.
+ //
+ // https://www.rfc-editor.org/rfc/rfc9114.html#section-6.1
+ streamTypeRequest = streamType(-1)
+
+ // https://www.rfc-editor.org/rfc/rfc9114.html#section-6.2
+ streamTypeControl = streamType(0x00)
+ streamTypePush = streamType(0x01)
+
+ // https://www.rfc-editor.org/rfc/rfc9204.html#section-4.2
+ streamTypeEncoder = streamType(0x02)
+ 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:
+ return "request"
+ case streamTypeControl:
+ return "control"
+ case streamTypePush:
+ return "push"
+ case streamTypeEncoder:
+ return "encoder"
+ case streamTypeDecoder:
+ return "decoder"
+ default:
+ return "unknown"
+ }
+}
+
+// Frame types.
+type frameType int64
+
+const (
+ // https://www.rfc-editor.org/rfc/rfc9114.html#section-7.2
+ frameTypeData = frameType(0x00)
+ frameTypeHeaders = frameType(0x01)
+ frameTypeCancelPush = frameType(0x03)
+ frameTypeSettings = frameType(0x04)
+ frameTypePushPromise = frameType(0x05)
+ frameTypeGoaway = frameType(0x07)
+ frameTypeMaxPushID = frameType(0x0d)
+)
+
+func (ftype frameType) String() string {
+ switch ftype {
+ case frameTypeData:
+ return "DATA"
+ case frameTypeHeaders:
+ return "HEADERS"
+ case frameTypeCancelPush:
+ return "CANCEL_PUSH"
+ case frameTypeSettings:
+ return "SETTINGS"
+ case frameTypePushPromise:
+ return "PUSH_PROMISE"
+ case frameTypeGoaway:
+ return "GOAWAY"
+ case frameTypeMaxPushID:
+ return "MAX_PUSH_ID"
+ default:
+ return fmt.Sprintf("UNKNOWN_%d", int64(ftype))
+ }
+}
diff --git a/src/vendor/golang.org/x/net/internal/http3/qpack.go b/src/vendor/golang.org/x/net/internal/http3/qpack.go
new file mode 100644
index 0000000000..64ce99aaa0
--- /dev/null
+++ b/src/vendor/golang.org/x/net/internal/http3/qpack.go
@@ -0,0 +1,332 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package http3
+
+import (
+ "errors"
+ "io"
+
+ "golang.org/x/net/http2/hpack"
+)
+
+// QPACK (RFC 9204) header compression wire encoding.
+// https://www.rfc-editor.org/rfc/rfc9204.html
+
+// tableType is the static or dynamic table.
+//
+// The T bit in QPACK instructions indicates whether a table index refers to
+// the dynamic (T=0) or static (T=1) table. tableTypeForTBit and tableType.tbit
+// convert a T bit from the wire encoding to/from a tableType.
+type tableType byte
+
+const (
+ dynamicTable = 0x00 // T=0, dynamic table
+ staticTable = 0xff // T=1, static table
+)
+
+// tableTypeForTbit returns the table type corresponding to a T bit value.
+// The input parameter contains a byte masked to contain only the T bit.
+func tableTypeForTbit(bit byte) tableType {
+ if bit == 0 {
+ return dynamicTable
+ }
+ return staticTable
+}
+
+// tbit produces the T bit corresponding to the table type.
+// The input parameter contains a byte with the T bit set to 1,
+// and the return is either the input or 0 depending on the table type.
+func (t tableType) tbit(bit byte) byte {
+ return bit & byte(t)
+}
+
+// indexType indicates a literal's indexing status.
+//
+// The N bit in QPACK instructions indicates whether a literal is "never-indexed".
+// A never-indexed literal (N=1) must not be encoded as an indexed literal if it
+// forwarded on another connection.
+//
+// (See https://www.rfc-editor.org/rfc/rfc9204.html#section-7.1 for details on the
+// security reasons for never-indexed literals.)
+type indexType byte
+
+const (
+ mayIndex = 0x00 // N=0, not a never-indexed literal
+ neverIndex = 0xff // N=1, never-indexed literal
+)
+
+// indexTypeForNBit returns the index type corresponding to a N bit value.
+// The input parameter contains a byte masked to contain only the N bit.
+func indexTypeForNBit(bit byte) indexType {
+ if bit == 0 {
+ return mayIndex
+ }
+ return neverIndex
+}
+
+// nbit produces the N bit corresponding to the table type.
+// The input parameter contains a byte with the N bit set to 1,
+// and the return is either the input or 0 depending on the table type.
+func (t indexType) nbit(bit byte) byte {
+ return bit & byte(t)
+}
+
+// Indexed Field Line:
+//
+// 0 1 2 3 4 5 6 7
+// +---+---+---+---+---+---+---+---+
+// | 1 | T | Index (6+) |
+// +---+---+-----------------------+
+//
+// https://www.rfc-editor.org/rfc/rfc9204.html#section-4.5.2
+
+func appendIndexedFieldLine(b []byte, ttype tableType, index int) []byte {
+ const tbit = 0b_01000000
+ return appendPrefixedInt(b, 0b_1000_0000|ttype.tbit(tbit), 6, int64(index))
+}
+
+func (st *stream) decodeIndexedFieldLine(b byte) (itype indexType, name, value string, err error) {
+ index, err := st.readPrefixedIntWithByte(b, 6)
+ if err != nil {
+ return 0, "", "", err
+ }
+ const tbit = 0b_0100_0000
+ if tableTypeForTbit(b&tbit) == staticTable {
+ ent, err := staticTableEntry(index)
+ if err != nil {
+ return 0, "", "", err
+ }
+ return mayIndex, ent.name, ent.value, nil
+ } else {
+ return 0, "", "", errors.New("dynamic table is not supported yet")
+ }
+}
+
+// Literal Field Line With Name Reference:
+//
+// 0 1 2 3 4 5 6 7
+// +---+---+---+---+---+---+---+---+
+// | 0 | 1 | N | T |Name Index (4+)|
+// +---+---+---+---+---------------+
+// | H | Value Length (7+) |
+// +---+---------------------------+
+// | Value String (Length bytes) |
+// +-------------------------------+
+//
+// https://www.rfc-editor.org/rfc/rfc9204.html#section-4.5.4
+
+func appendLiteralFieldLineWithNameReference(b []byte, ttype tableType, itype indexType, nameIndex int, value string) []byte {
+ const tbit = 0b_0001_0000
+ const nbit = 0b_0010_0000
+ b = appendPrefixedInt(b, 0b_0100_0000|itype.nbit(nbit)|ttype.tbit(tbit), 4, int64(nameIndex))
+ b = appendPrefixedString(b, 0, 7, value)
+ return b
+}
+
+func (st *stream) decodeLiteralFieldLineWithNameReference(b byte) (itype indexType, name, value string, err error) {
+ nameIndex, err := st.readPrefixedIntWithByte(b, 4)
+ if err != nil {
+ return 0, "", "", err
+ }
+
+ const tbit = 0b_0001_0000
+ if tableTypeForTbit(b&tbit) == staticTable {
+ ent, err := staticTableEntry(nameIndex)
+ if err != nil {
+ return 0, "", "", err
+ }
+ name = ent.name
+ } else {
+ return 0, "", "", errors.New("dynamic table is not supported yet")
+ }
+
+ _, value, err = st.readPrefixedString(7)
+ if err != nil {
+ return 0, "", "", err
+ }
+
+ const nbit = 0b_0010_0000
+ itype = indexTypeForNBit(b & nbit)
+
+ return itype, name, value, nil
+}
+
+// Literal Field Line with Literal Name:
+//
+// 0 1 2 3 4 5 6 7
+// +---+---+---+---+---+---+---+---+
+// | 0 | 0 | 1 | N | H |NameLen(3+)|
+// +---+---+---+---+---+-----------+
+// | Name String (Length bytes) |
+// +---+---------------------------+
+// | H | Value Length (7+) |
+// +---+---------------------------+
+// | Value String (Length bytes) |
+// +-------------------------------+
+//
+// https://www.rfc-editor.org/rfc/rfc9204.html#section-4.5.6
+
+func appendLiteralFieldLineWithLiteralName(b []byte, itype indexType, name, value string) []byte {
+ const nbit = 0b_0001_0000
+ b = appendPrefixedString(b, 0b_0010_0000|itype.nbit(nbit), 3, name)
+ b = appendPrefixedString(b, 0, 7, value)
+ return b
+}
+
+func (st *stream) decodeLiteralFieldLineWithLiteralName(b byte) (itype indexType, name, value string, err error) {
+ name, err = st.readPrefixedStringWithByte(b, 3)
+ if err != nil {
+ return 0, "", "", err
+ }
+ _, value, err = st.readPrefixedString(7)
+ if err != nil {
+ return 0, "", "", err
+ }
+ const nbit = 0b_0001_0000
+ itype = indexTypeForNBit(b & nbit)
+ return itype, name, value, nil
+}
+
+// Prefixed-integer encoding from RFC 7541, section 5.1
+//
+// Prefixed integers consist of some number of bits of data,
+// N bits of encoded integer, and 0 or more additional bytes of
+// encoded integer.
+//
+// The RFCs represent this as, for example:
+//
+// 0 1 2 3 4 5 6 7
+// +---+---+---+---+---+---+---+---+
+// | 0 | 0 | 1 | Capacity (5+) |
+// +---+---+---+-------------------+
+//
+// "Capacity" is an integer with a 5-bit prefix.
+//
+// In the following functions, a "prefixLen" parameter is the number
+// of integer bits in the first byte (5 in the above example), and
+// a "firstByte" parameter is a byte containing the first byte of
+// the encoded value (0x001x_xxxx in the above example).
+//
+// https://www.rfc-editor.org/rfc/rfc9204.html#section-4.1.1
+// https://www.rfc-editor.org/rfc/rfc7541#section-5.1
+
+// readPrefixedInt reads an RFC 7541 prefixed integer from st.
+func (st *stream) readPrefixedInt(prefixLen uint8) (firstByte byte, v int64, err error) {
+ firstByte, err = st.ReadByte()
+ if err != nil {
+ return 0, 0, errQPACKDecompressionFailed
+ }
+ v, err = st.readPrefixedIntWithByte(firstByte, prefixLen)
+ return firstByte, v, err
+}
+
+// readPrefixedIntWithByte reads an RFC 7541 prefixed integer from st.
+// The first byte has already been read from the stream.
+func (st *stream) readPrefixedIntWithByte(firstByte byte, prefixLen uint8) (v int64, err error) {
+ prefixMask := (byte(1) << prefixLen) - 1
+ v = int64(firstByte & prefixMask)
+ if v != int64(prefixMask) {
+ return v, nil
+ }
+ m := 0
+ for {
+ b, err := st.ReadByte()
+ if err != nil {
+ return 0, errQPACKDecompressionFailed
+ }
+ v += int64(b&127) << m
+ m += 7
+ if b&128 == 0 {
+ break
+ }
+ }
+ return v, err
+}
+
+// appendPrefixedInt appends an RFC 7541 prefixed integer to b.
+//
+// The firstByte parameter includes the non-integer bits of the first byte.
+// The other bits must be zero.
+func appendPrefixedInt(b []byte, firstByte byte, prefixLen uint8, i int64) []byte {
+ u := uint64(i)
+ prefixMask := (uint64(1) << prefixLen) - 1
+ if u < prefixMask {
+ return append(b, firstByte|byte(u))
+ }
+ b = append(b, firstByte|byte(prefixMask))
+ u -= prefixMask
+ for u >= 128 {
+ b = append(b, 0x80|byte(u&0x7f))
+ u >>= 7
+ }
+ return append(b, byte(u))
+}
+
+// String literal encoding from RFC 7541, section 5.2
+//
+// String literals consist of a single bit flag indicating
+// whether the string is Huffman-encoded, a prefixed integer (see above),
+// and the string.
+//
+// https://www.rfc-editor.org/rfc/rfc9204.html#section-4.1.2
+// https://www.rfc-editor.org/rfc/rfc7541#section-5.2
+
+// readPrefixedString reads an RFC 7541 string from st.
+func (st *stream) readPrefixedString(prefixLen uint8) (firstByte byte, s string, err error) {
+ firstByte, err = st.ReadByte()
+ if err != nil {
+ return 0, "", errQPACKDecompressionFailed
+ }
+ s, err = st.readPrefixedStringWithByte(firstByte, prefixLen)
+ return firstByte, s, err
+}
+
+// readPrefixedStringWithByte reads an RFC 7541 string from st.
+// The first byte has already been read from the stream.
+func (st *stream) readPrefixedStringWithByte(firstByte byte, prefixLen uint8) (s string, err error) {
+ size, err := st.readPrefixedIntWithByte(firstByte, prefixLen)
+ if err != nil {
+ return "", errQPACKDecompressionFailed
+ }
+
+ hbit := byte(1) << prefixLen
+ isHuffman := firstByte&hbit != 0
+
+ // TODO: Avoid allocating here.
+ data := make([]byte, size)
+ if _, err := io.ReadFull(st, data); err != nil {
+ return "", errQPACKDecompressionFailed
+ }
+ if isHuffman {
+ // TODO: Move Huffman functions into a new package that hpack (HTTP/2)
+ // and this package can both import. Most of the hpack package isn't
+ // relevant to HTTP/3.
+ s, err := hpack.HuffmanDecodeToString(data)
+ if err != nil {
+ return "", errQPACKDecompressionFailed
+ }
+ return s, nil
+ }
+ return string(data), nil
+}
+
+// appendPrefixedString appends an RFC 7541 string to st,
+// applying Huffman encoding and setting the H bit (indicating Huffman encoding)
+// when appropriate.
+//
+// The firstByte parameter includes the non-integer bits of the first byte.
+// The other bits must be zero.
+func appendPrefixedString(b []byte, firstByte byte, prefixLen uint8, s string) []byte {
+ huffmanLen := hpack.HuffmanEncodeLength(s)
+ if huffmanLen < uint64(len(s)) {
+ hbit := byte(1) << prefixLen
+ b = appendPrefixedInt(b, firstByte|hbit, prefixLen, int64(huffmanLen))
+ b = hpack.AppendHuffmanString(b, s)
+ } else {
+ b = appendPrefixedInt(b, firstByte, prefixLen, int64(len(s)))
+ b = append(b, s...)
+ }
+ return b
+}
diff --git a/src/vendor/golang.org/x/net/internal/http3/qpack_decode.go b/src/vendor/golang.org/x/net/internal/http3/qpack_decode.go
new file mode 100644
index 0000000000..7348ae76f0
--- /dev/null
+++ b/src/vendor/golang.org/x/net/internal/http3/qpack_decode.go
@@ -0,0 +1,81 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package http3
+
+import (
+ "errors"
+ "math/bits"
+)
+
+type qpackDecoder struct {
+ // The decoder has no state for now,
+ // but that'll change once we add dynamic table support.
+ //
+ // TODO: dynamic table support.
+}
+
+func (qd *qpackDecoder) decode(st *stream, f func(itype indexType, name, value string) error) error {
+ // Encoded Field Section prefix.
+
+ // We set SETTINGS_QPACK_MAX_TABLE_CAPACITY to 0,
+ // so the Required Insert Count must be 0.
+ _, requiredInsertCount, err := st.readPrefixedInt(8)
+ if err != nil {
+ return err
+ }
+ if requiredInsertCount != 0 {
+ return errQPACKDecompressionFailed
+ }
+
+ // Delta Base. We don't use the dynamic table yet, so this may be ignored.
+ _, _, err = st.readPrefixedInt(7)
+ if err != nil {
+ return err
+ }
+
+ sawNonPseudo := false
+ for st.lim > 0 {
+ firstByte, err := st.ReadByte()
+ if err != nil {
+ return err
+ }
+ var name, value string
+ var itype indexType
+ switch bits.LeadingZeros8(firstByte) {
+ case 0:
+ // Indexed Field Line
+ itype, name, value, err = st.decodeIndexedFieldLine(firstByte)
+ case 1:
+ // Literal Field Line With Name Reference
+ itype, name, value, err = st.decodeLiteralFieldLineWithNameReference(firstByte)
+ case 2:
+ // Literal Field Line with Literal Name
+ itype, name, value, err = st.decodeLiteralFieldLineWithLiteralName(firstByte)
+ case 3:
+ // Indexed Field Line With Post-Base Index
+ err = errors.New("dynamic table is not supported yet")
+ case 4:
+ // Indexed Field Line With Post-Base Name Reference
+ err = errors.New("dynamic table is not supported yet")
+ }
+ if err != nil {
+ return err
+ }
+ if len(name) == 0 {
+ return errH3MessageError
+ }
+ if name[0] == ':' {
+ if sawNonPseudo {
+ return errH3MessageError
+ }
+ } else {
+ sawNonPseudo = true
+ }
+ if err := f(itype, name, value); err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/src/vendor/golang.org/x/net/internal/http3/qpack_encode.go b/src/vendor/golang.org/x/net/internal/http3/qpack_encode.go
new file mode 100644
index 0000000000..193f7f93be
--- /dev/null
+++ b/src/vendor/golang.org/x/net/internal/http3/qpack_encode.go
@@ -0,0 +1,45 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package http3
+
+type qpackEncoder struct {
+ // The encoder has no state for now,
+ // but that'll change once we add dynamic table support.
+ //
+ // TODO: dynamic table support.
+}
+
+func (qe *qpackEncoder) init() {
+ staticTableOnce.Do(initStaticTableMaps)
+}
+
+// encode encodes a list of headers into a QPACK encoded field section.
+//
+// The headers func must produce the same headers on repeated calls,
+// although the order may vary.
+func (qe *qpackEncoder) encode(headers func(func(itype indexType, name, value string))) []byte {
+ // Encoded Field Section prefix.
+ //
+ // We don't yet use the dynamic table, so both values here are zero.
+ var b []byte
+ b = appendPrefixedInt(b, 0, 8, 0) // Required Insert Count
+ b = appendPrefixedInt(b, 0, 7, 0) // Delta Base
+
+ headers(func(itype indexType, name, value string) {
+ if itype == mayIndex {
+ if i, ok := staticTableByNameValue[tableEntry{name, value}]; ok {
+ b = appendIndexedFieldLine(b, staticTable, i)
+ return
+ }
+ }
+ if i, ok := staticTableByName[name]; ok {
+ b = appendLiteralFieldLineWithNameReference(b, staticTable, itype, i, value)
+ } else {
+ b = appendLiteralFieldLineWithLiteralName(b, itype, name, value)
+ }
+ })
+
+ return b
+}
diff --git a/src/vendor/golang.org/x/net/internal/http3/qpack_static.go b/src/vendor/golang.org/x/net/internal/http3/qpack_static.go
new file mode 100644
index 0000000000..6c0b51c5e6
--- /dev/null
+++ b/src/vendor/golang.org/x/net/internal/http3/qpack_static.go
@@ -0,0 +1,142 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package http3
+
+import "sync"
+
+type tableEntry struct {
+ name string
+ value string
+}
+
+// staticTableEntry returns the static table entry with the given index.
+func staticTableEntry(index int64) (tableEntry, error) {
+ if index >= int64(len(staticTableEntries)) {
+ return tableEntry{}, errQPACKDecompressionFailed
+ }
+ return staticTableEntries[index], nil
+}
+
+func initStaticTableMaps() {
+ staticTableByName = make(map[string]int)
+ staticTableByNameValue = make(map[tableEntry]int)
+ for i, ent := range staticTableEntries {
+ if _, ok := staticTableByName[ent.name]; !ok {
+ staticTableByName[ent.name] = i
+ }
+ staticTableByNameValue[ent] = i
+ }
+}
+
+var (
+ staticTableOnce sync.Once
+ staticTableByName map[string]int
+ staticTableByNameValue map[tableEntry]int
+)
+
+// https://www.rfc-editor.org/rfc/rfc9204.html#appendix-A
+//
+// Note that this is different from the HTTP/2 static table.
+var staticTableEntries = [...]tableEntry{
+ 0: {":authority", ""},
+ 1: {":path", "/"},
+ 2: {"age", "0"},
+ 3: {"content-disposition", ""},
+ 4: {"content-length", "0"},
+ 5: {"cookie", ""},
+ 6: {"date", ""},
+ 7: {"etag", ""},
+ 8: {"if-modified-since", ""},
+ 9: {"if-none-match", ""},
+ 10: {"last-modified", ""},
+ 11: {"link", ""},
+ 12: {"location", ""},
+ 13: {"referer", ""},
+ 14: {"set-cookie", ""},
+ 15: {":method", "CONNECT"},
+ 16: {":method", "DELETE"},
+ 17: {":method", "GET"},
+ 18: {":method", "HEAD"},
+ 19: {":method", "OPTIONS"},
+ 20: {":method", "POST"},
+ 21: {":method", "PUT"},
+ 22: {":scheme", "http"},
+ 23: {":scheme", "https"},
+ 24: {":status", "103"},
+ 25: {":status", "200"},
+ 26: {":status", "304"},
+ 27: {":status", "404"},
+ 28: {":status", "503"},
+ 29: {"accept", "*/*"},
+ 30: {"accept", "application/dns-message"},
+ 31: {"accept-encoding", "gzip, deflate, br"},
+ 32: {"accept-ranges", "bytes"},
+ 33: {"access-control-allow-headers", "cache-control"},
+ 34: {"access-control-allow-headers", "content-type"},
+ 35: {"access-control-allow-origin", "*"},
+ 36: {"cache-control", "max-age=0"},
+ 37: {"cache-control", "max-age=2592000"},
+ 38: {"cache-control", "max-age=604800"},
+ 39: {"cache-control", "no-cache"},
+ 40: {"cache-control", "no-store"},
+ 41: {"cache-control", "public, max-age=31536000"},
+ 42: {"content-encoding", "br"},
+ 43: {"content-encoding", "gzip"},
+ 44: {"content-type", "application/dns-message"},
+ 45: {"content-type", "application/javascript"},
+ 46: {"content-type", "application/json"},
+ 47: {"content-type", "application/x-www-form-urlencoded"},
+ 48: {"content-type", "image/gif"},
+ 49: {"content-type", "image/jpeg"},
+ 50: {"content-type", "image/png"},
+ 51: {"content-type", "text/css"},
+ 52: {"content-type", "text/html; charset=utf-8"},
+ 53: {"content-type", "text/plain"},
+ 54: {"content-type", "text/plain;charset=utf-8"},
+ 55: {"range", "bytes=0-"},
+ 56: {"strict-transport-security", "max-age=31536000"},
+ 57: {"strict-transport-security", "max-age=31536000; includesubdomains"},
+ 58: {"strict-transport-security", "max-age=31536000; includesubdomains; preload"},
+ 59: {"vary", "accept-encoding"},
+ 60: {"vary", "origin"},
+ 61: {"x-content-type-options", "nosniff"},
+ 62: {"x-xss-protection", "1; mode=block"},
+ 63: {":status", "100"},
+ 64: {":status", "204"},
+ 65: {":status", "206"},
+ 66: {":status", "302"},
+ 67: {":status", "400"},
+ 68: {":status", "403"},
+ 69: {":status", "421"},
+ 70: {":status", "425"},
+ 71: {":status", "500"},
+ 72: {"accept-language", ""},
+ 73: {"access-control-allow-credentials", "FALSE"},
+ 74: {"access-control-allow-credentials", "TRUE"},
+ 75: {"access-control-allow-headers", "*"},
+ 76: {"access-control-allow-methods", "get"},
+ 77: {"access-control-allow-methods", "get, post, options"},
+ 78: {"access-control-allow-methods", "options"},
+ 79: {"access-control-expose-headers", "content-length"},
+ 80: {"access-control-request-headers", "content-type"},
+ 81: {"access-control-request-method", "get"},
+ 82: {"access-control-request-method", "post"},
+ 83: {"alt-svc", "clear"},
+ 84: {"authorization", ""},
+ 85: {"content-security-policy", "script-src 'none'; object-src 'none'; base-uri 'none'"},
+ 86: {"early-data", "1"},
+ 87: {"expect-ct", ""},
+ 88: {"forwarded", ""},
+ 89: {"if-range", ""},
+ 90: {"origin", ""},
+ 91: {"purpose", "prefetch"},
+ 92: {"server", ""},
+ 93: {"timing-allow-origin", "*"},
+ 94: {"upgrade-insecure-requests", "1"},
+ 95: {"user-agent", ""},
+ 96: {"x-forwarded-for", ""},
+ 97: {"x-frame-options", "deny"},
+ 98: {"x-frame-options", "sameorigin"},
+}
diff --git a/src/vendor/golang.org/x/net/internal/http3/quic.go b/src/vendor/golang.org/x/net/internal/http3/quic.go
new file mode 100644
index 0000000000..4f1cca179e
--- /dev/null
+++ b/src/vendor/golang.org/x/net/internal/http3/quic.go
@@ -0,0 +1,40 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package http3
+
+import (
+ "crypto/tls"
+
+ "golang.org/x/net/quic"
+)
+
+func initConfig(config *quic.Config) *quic.Config {
+ if config == nil {
+ config = &quic.Config{}
+ }
+
+ // maybeCloneTLSConfig clones the user-provided tls.Config (but only once)
+ // prior to us modifying it.
+ needCloneTLSConfig := true
+ maybeCloneTLSConfig := func() *tls.Config {
+ if needCloneTLSConfig {
+ config.TLSConfig = config.TLSConfig.Clone()
+ needCloneTLSConfig = false
+ }
+ return config.TLSConfig
+ }
+
+ if config.TLSConfig == nil {
+ config.TLSConfig = &tls.Config{}
+ needCloneTLSConfig = false
+ }
+ if config.TLSConfig.MinVersion == 0 {
+ maybeCloneTLSConfig().MinVersion = tls.VersionTLS13
+ }
+ if config.TLSConfig.NextProtos == nil {
+ maybeCloneTLSConfig().NextProtos = []string{"h3"}
+ }
+ return config
+}
diff --git a/src/vendor/golang.org/x/net/internal/http3/roundtrip.go b/src/vendor/golang.org/x/net/internal/http3/roundtrip.go
new file mode 100644
index 0000000000..2ea584b773
--- /dev/null
+++ b/src/vendor/golang.org/x/net/internal/http3/roundtrip.go
@@ -0,0 +1,417 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package http3
+
+import (
+ "errors"
+ "io"
+ "net/http"
+ "net/http/httptrace"
+ "net/textproto"
+ "strconv"
+ "sync"
+
+ "golang.org/x/net/http/httpguts"
+ "golang.org/x/net/internal/httpcommon"
+)
+
+type roundTripState struct {
+ cc *clientConn
+ st *stream
+
+ // Request body, provided by the caller.
+ onceCloseReqBody sync.Once
+ reqBody io.ReadCloser
+
+ reqBodyWriter bodyWriter
+
+ // Response.Body, provided to the caller.
+ respBody io.ReadCloser
+
+ trace *httptrace.ClientTrace
+
+ errOnce sync.Once
+ err error
+}
+
+// abort terminates the RoundTrip.
+// It returns the first fatal error encountered by the RoundTrip call.
+func (rt *roundTripState) abort(err error) error {
+ rt.errOnce.Do(func() {
+ rt.err = err
+ switch e := err.(type) {
+ case *connectionError:
+ rt.cc.abort(e)
+ case *streamError:
+ rt.st.stream.CloseRead()
+ rt.st.stream.Reset(uint64(e.code))
+ default:
+ rt.st.stream.CloseRead()
+ rt.st.stream.Reset(uint64(errH3NoError))
+ }
+ })
+ return rt.err
+}
+
+// closeReqBody closes the Request.Body, at most once.
+func (rt *roundTripState) closeReqBody() {
+ if rt.reqBody != nil {
+ rt.onceCloseReqBody.Do(func() {
+ rt.reqBody.Close()
+ })
+ }
+}
+
+// TODO: Set up the rest of the hooks that might be in rt.trace.
+func (rt *roundTripState) maybeCallGot1xxResponse(status int, h http.Header) error {
+ if rt.trace == nil || rt.trace.Got1xxResponse == nil {
+ return nil
+ }
+ return rt.trace.Got1xxResponse(status, textproto.MIMEHeader(h))
+}
+
+func (rt *roundTripState) maybeCallGot100Continue() {
+ if rt.trace == nil || rt.trace.Got100Continue == nil {
+ return
+ }
+ rt.trace.Got100Continue()
+}
+
+func (rt *roundTripState) maybeCallWait100Continue() {
+ if rt.trace == nil || rt.trace.Wait100Continue == nil {
+ return
+ }
+ rt.trace.Wait100Continue()
+}
+
+// RoundTrip sends a request on the connection.
+func (cc *clientConn) RoundTrip(req *http.Request) (_ *http.Response, err error) {
+ // Each request gets its own QUIC stream.
+ st, err := newConnStream(req.Context(), cc.qconn, streamTypeRequest)
+ if err != nil {
+ return nil, err
+ }
+ rt := &roundTripState{
+ cc: cc,
+ st: st,
+ trace: httptrace.ContextClientTrace(req.Context()),
+ }
+ defer func() {
+ if err != nil {
+ err = rt.abort(err)
+ }
+ }()
+
+ // Cancel reads/writes on the stream when the request expires.
+ st.stream.SetReadContext(req.Context())
+ st.stream.SetWriteContext(req.Context())
+
+ headers := cc.enc.encode(func(yield func(itype indexType, name, value string)) {
+ _, err = httpcommon.EncodeHeaders(req.Context(), httpcommon.EncodeHeadersParam{
+ Request: httpcommon.Request{
+ URL: req.URL,
+ Method: req.Method,
+ Host: req.Host,
+ Header: req.Header,
+ Trailer: req.Trailer,
+ ActualContentLength: actualContentLength(req),
+ },
+ AddGzipHeader: false, // TODO: add when appropriate
+ PeerMaxHeaderListSize: 0,
+ DefaultUserAgent: "Go-http-client/3",
+ }, func(name, value string) {
+ // Issue #71374: Consider supporting never-indexed fields.
+ yield(mayIndex, name, value)
+ })
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ // Write the HEADERS frame.
+ st.writeVarint(int64(frameTypeHeaders))
+ st.writeVarint(int64(len(headers)))
+ st.Write(headers)
+ if err := st.Flush(); err != nil {
+ return nil, err
+ }
+
+ var bodyAndTrailerWritten bool
+ is100ContinueReq := httpguts.HeaderValuesContainsToken(req.Header["Expect"], "100-continue")
+ if is100ContinueReq {
+ rt.maybeCallWait100Continue()
+ } else {
+ bodyAndTrailerWritten = true
+ go cc.writeBodyAndTrailer(rt, req)
+ }
+
+ // Read the response headers.
+ for {
+ ftype, err := st.readFrameHeader()
+ if err != nil {
+ return nil, err
+ }
+ switch ftype {
+ case frameTypeHeaders:
+ statusCode, h, err := cc.handleHeaders(st)
+ if err != nil {
+ return nil, err
+ }
+
+ // TODO: Handle 1xx responses.
+ if isInfoStatus(statusCode) {
+ if err := rt.maybeCallGot1xxResponse(statusCode, h); err != nil {
+ return nil, err
+ }
+ switch statusCode {
+ case 100:
+ rt.maybeCallGot100Continue()
+ if is100ContinueReq && !bodyAndTrailerWritten {
+ bodyAndTrailerWritten = true
+ go cc.writeBodyAndTrailer(rt, req)
+ continue
+ }
+ // If we did not send "Expect: 100-continue" request but
+ // received status 100 anyways, just continue per usual and
+ // let the caller decide what to do with the response.
+ default:
+ continue
+ }
+ }
+
+ // We have the response headers.
+ // Set up the response and return it to the caller.
+ contentLength, err := parseResponseContentLength(req.Method, statusCode, h)
+ if err != nil {
+ return nil, err
+ }
+
+ trailer := make(http.Header)
+ extractTrailerFromHeader(h, trailer)
+ delete(h, "Trailer")
+
+ if (contentLength != 0 && req.Method != http.MethodHead) || len(trailer) > 0 {
+ rt.respBody = &bodyReader{
+ st: st,
+ remain: contentLength,
+ trailer: trailer,
+ }
+ } else {
+ rt.respBody = http.NoBody
+ }
+ resp := &http.Response{
+ Proto: "HTTP/3.0",
+ ProtoMajor: 3,
+ Header: h,
+ StatusCode: statusCode,
+ Status: strconv.Itoa(statusCode) + " " + http.StatusText(statusCode),
+ ContentLength: contentLength,
+ Trailer: trailer,
+ Body: (*transportResponseBody)(rt),
+ }
+ // TODO: Automatic Content-Type: gzip decoding.
+ return resp, nil
+ case frameTypePushPromise:
+ if err := cc.handlePushPromise(st); err != nil {
+ return nil, err
+ }
+ default:
+ if err := st.discardUnknownFrame(ftype); err != nil {
+ return nil, err
+ }
+ }
+ }
+}
+
+// actualContentLength returns a sanitized version of req.ContentLength,
+// where 0 actually means zero (not unknown) and -1 means unknown.
+func actualContentLength(req *http.Request) int64 {
+ if req.Body == nil || req.Body == http.NoBody {
+ return 0
+ }
+ if req.ContentLength != 0 {
+ return req.ContentLength
+ }
+ return -1
+}
+
+// writeBodyAndTrailer handles writing the body and trailer for a given
+// request, if any. This function will close the write direction of the stream.
+func (cc *clientConn) writeBodyAndTrailer(rt *roundTripState, req *http.Request) {
+ defer rt.closeReqBody()
+
+ declaredTrailer := req.Trailer.Clone()
+
+ rt.reqBody = req.Body
+ rt.reqBodyWriter.st = rt.st
+ rt.reqBodyWriter.remain = actualContentLength(req)
+ rt.reqBodyWriter.flush = true
+ rt.reqBodyWriter.name = "request"
+ rt.reqBodyWriter.trailer = req.Trailer
+ rt.reqBodyWriter.enc = &cc.enc
+ if req.Body == nil {
+ rt.reqBody = http.NoBody
+ }
+
+ if _, err := io.Copy(&rt.reqBodyWriter, rt.reqBody); err != nil {
+ rt.abort(err)
+ }
+ // Get rid of any trailer that was not declared beforehand, before we
+ // close the request body which will cause the trailer headers to be
+ // written.
+ for name := range req.Trailer {
+ if _, ok := declaredTrailer[name]; !ok {
+ delete(req.Trailer, name)
+ }
+ }
+ if err := rt.reqBodyWriter.Close(); err != nil {
+ rt.abort(err)
+ }
+}
+
+// transportResponseBody is the Response.Body returned by RoundTrip.
+type transportResponseBody roundTripState
+
+// Read is Response.Body.Read.
+func (b *transportResponseBody) Read(p []byte) (n int, err error) {
+ return b.respBody.Read(p)
+}
+
+var errRespBodyClosed = errors.New("response body closed")
+
+// Close is Response.Body.Close.
+// Closing the response body is how the caller signals that they're done with a request.
+func (b *transportResponseBody) Close() error {
+ rt := (*roundTripState)(b)
+ // Close the request body, which should wake up copyRequestBody if it's
+ // currently blocked reading the body.
+ rt.closeReqBody()
+ // Close the request stream, since we're done with the request.
+ // Reset closes the sending half of the stream.
+ rt.st.stream.Reset(uint64(errH3NoError))
+ // respBody.Close is responsible for closing the receiving half.
+ err := rt.respBody.Close()
+ if err == nil {
+ err = errRespBodyClosed
+ }
+ err = rt.abort(err)
+ if err == errRespBodyClosed {
+ // No other errors occurred before closing Response.Body,
+ // so consider this a successful request.
+ return nil
+ }
+ return err
+}
+
+func parseResponseContentLength(method string, statusCode int, h http.Header) (int64, error) {
+ clens := h["Content-Length"]
+ if len(clens) == 0 {
+ return -1, nil
+ }
+
+ // We allow duplicate Content-Length headers,
+ // but only if they all have the same value.
+ for _, v := range clens[1:] {
+ if clens[0] != v {
+ return -1, &streamError{errH3MessageError, "mismatching Content-Length headers"}
+ }
+ }
+
+ // "A server MUST NOT send a Content-Length header field in any response
+ // with a status code of 1xx (Informational) or 204 (No Content).
+ // A server MUST NOT send a Content-Length header field in any 2xx (Successful)
+ // response to a CONNECT request [...]"
+ // https://www.rfc-editor.org/rfc/rfc9110#section-8.6-8
+ if (statusCode >= 100 && statusCode < 200) ||
+ statusCode == 204 ||
+ (method == "CONNECT" && statusCode >= 200 && statusCode < 300) {
+ // This is a protocol violation, but a fairly harmless one.
+ // Just ignore the header.
+ return -1, nil
+ }
+
+ contentLen, err := strconv.ParseUint(clens[0], 10, 63)
+ if err != nil {
+ return -1, &streamError{errH3MessageError, "invalid Content-Length header"}
+ }
+ return int64(contentLen), nil
+}
+
+func (cc *clientConn) handleHeaders(st *stream) (statusCode int, h http.Header, err error) {
+ haveStatus := false
+ cookie := ""
+ // Issue #71374: Consider tracking the never-indexed status of headers
+ // with the N bit set in their QPACK encoding.
+ err = cc.dec.decode(st, func(_ indexType, name, value string) error {
+ switch {
+ case name == ":status":
+ if haveStatus {
+ return &streamError{errH3MessageError, "duplicate :status"}
+ }
+ haveStatus = true
+ statusCode, err = strconv.Atoi(value)
+ if err != nil {
+ return &streamError{errH3MessageError, "invalid :status"}
+ }
+ case name[0] == ':':
+ // "Endpoints MUST treat a request or response
+ // that contains undefined or invalid
+ // pseudo-header fields as malformed."
+ // https://www.rfc-editor.org/rfc/rfc9114.html#section-4.3-3
+ return &streamError{errH3MessageError, "undefined pseudo-header"}
+ case name == "cookie":
+ // "If a decompressed field section contains multiple cookie field lines,
+ // these MUST be concatenated into a single byte string [...]"
+ // using the two-byte delimiter of "; "''
+ // https://www.rfc-editor.org/rfc/rfc9114.html#section-4.2.1-2
+ if cookie == "" {
+ cookie = value
+ } else {
+ cookie += "; " + value
+ }
+ default:
+ if h == nil {
+ h = make(http.Header)
+ }
+ // TODO: Use a per-connection canonicalization cache as we do in HTTP/2.
+ // Maybe we could put this in the QPACK decoder and have it deliver
+ // pre-canonicalized headers to us here?
+ cname := httpcommon.CanonicalHeader(name)
+ // TODO: Consider using a single []string slice for all headers,
+ // as we do in the HTTP/1 and HTTP/2 cases.
+ // This is a bit tricky, since we don't know the number of headers
+ // at the start of decoding. Perhaps it's worth doing a two-pass decode,
+ // or perhaps we should just allocate header value slices in
+ // reasonably-sized chunks.
+ h[cname] = append(h[cname], value)
+ }
+ return nil
+ })
+ if !haveStatus {
+ // "[The :status] pseudo-header field MUST be included in all responses [...]"
+ // https://www.rfc-editor.org/rfc/rfc9114.html#section-4.3.2-1
+ err = errH3MessageError
+ }
+ if cookie != "" {
+ if h == nil {
+ h = make(http.Header)
+ }
+ h["Cookie"] = []string{cookie}
+ }
+ if err := st.endFrame(); err != nil {
+ return 0, nil, err
+ }
+ return statusCode, h, err
+}
+
+func (cc *clientConn) handlePushPromise(st *stream) error {
+ // "A client MUST treat receipt of a PUSH_PROMISE frame that contains a
+ // larger push ID than the client has advertised as a connection error of H3_ID_ERROR."
+ // https://www.rfc-editor.org/rfc/rfc9114.html#section-7.2.5-5
+ return &connectionError{
+ code: errH3IDError,
+ message: "PUSH_PROMISE received when no MAX_PUSH_ID has been sent",
+ }
+}
diff --git a/src/vendor/golang.org/x/net/internal/http3/server.go b/src/vendor/golang.org/x/net/internal/http3/server.go
new file mode 100644
index 0000000000..28c8cda849
--- /dev/null
+++ b/src/vendor/golang.org/x/net/internal/http3/server.go
@@ -0,0 +1,791 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package http3
+
+import (
+ "context"
+ "crypto/tls"
+ "fmt"
+ "io"
+ "maps"
+ "net/http"
+ "slices"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "golang.org/x/net/http/httpguts"
+ "golang.org/x/net/internal/httpcommon"
+ "golang.org/x/net/quic"
+)
+
+// A server is an HTTP/3 server.
+// The zero value for server is a valid server.
+type server struct {
+ // handler to invoke for requests, http.DefaultServeMux if nil.
+ handler http.Handler
+
+ config *quic.Config
+
+ listenQUIC func(addr string, config *quic.Config) (*quic.Endpoint, error)
+
+ initOnce sync.Once
+
+ serveCtx context.Context
+ serveCtxCancel context.CancelFunc
+
+ // connClosed is used to signal that a connection has been unregistered
+ // from activeConns. That way, when shutting down gracefully, the server
+ // can avoid busy-waiting for activeConns to be empty.
+ connClosed chan any
+ mu sync.Mutex // Guards fields below.
+ activeConns map[*serverConn]struct{}
+}
+
+// netHTTPHandler is an interface that is implemented by
+// net/http.http3ServerHandler in std.
+//
+// It provides a way for information to be passed between x/net and net/http
+// that would otherwise be inaccessible, such as the TLS configs that users
+// have supplied to net/http servers.
+//
+// This allows us to integrate our HTTP/3 server implementation with the
+// net/http server when RegisterServer is called.
+type netHTTPHandler interface {
+ http.Handler
+ TLSConfig() *tls.Config
+ BaseContext() context.Context
+ Addr() string
+ ListenErrHook(err error)
+ ShutdownContext() context.Context
+}
+
+type ServerOpts struct {
+ // ListenQUIC determines how the server will open a QUIC endpoint.
+ // By default, quic.Listen("udp", addr, config) is used.
+ ListenQUIC func(addr string, config *quic.Config) (*quic.Endpoint, error)
+
+ // QUICConfig is the QUIC configuration used by the server.
+ // QUICConfig may be nil and should not be modified after calling
+ // RegisterServer.
+ // If QUICConfig.TLSConfig is nil, the TLSConfig of the net/http Server
+ // given to RegisterServer will be used.
+ QUICConfig *quic.Config
+}
+
+// RegisterServer adds HTTP/3 support to a net/http Server.
+//
+// RegisterServer must be called before s begins serving, and only affects
+// s.ListenAndServeTLS.
+func RegisterServer(s *http.Server, opts ServerOpts) {
+ if s.TLSNextProto == nil {
+ s.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
+ }
+ s.TLSNextProto["http/3"] = func(s *http.Server, c *tls.Conn, h http.Handler) {
+ stdHandler, ok := h.(netHTTPHandler)
+ if !ok {
+ panic("RegisterServer was given a server that does not implement netHTTPHandler")
+ }
+ if opts.QUICConfig == nil {
+ opts.QUICConfig = &quic.Config{}
+ }
+ if opts.QUICConfig.TLSConfig == nil {
+ opts.QUICConfig.TLSConfig = stdHandler.TLSConfig()
+ }
+ s3 := &server{
+ config: opts.QUICConfig,
+ listenQUIC: opts.ListenQUIC,
+ handler: stdHandler,
+ serveCtx: stdHandler.BaseContext(),
+ }
+ s3.init()
+ s.RegisterOnShutdown(func() {
+ s3.shutdown(stdHandler.ShutdownContext())
+ })
+ stdHandler.ListenErrHook(s3.listenAndServe(stdHandler.Addr()))
+ }
+}
+
+func (s *server) init() {
+ s.initOnce.Do(func() {
+ s.config = initConfig(s.config)
+ if s.handler == nil {
+ s.handler = http.DefaultServeMux
+ }
+ if s.serveCtx == nil {
+ s.serveCtx = context.Background()
+ }
+ if s.listenQUIC == nil {
+ s.listenQUIC = func(addr string, config *quic.Config) (*quic.Endpoint, error) {
+ return quic.Listen("udp", addr, config)
+ }
+ }
+ s.serveCtx, s.serveCtxCancel = context.WithCancel(s.serveCtx)
+ s.activeConns = make(map[*serverConn]struct{})
+ s.connClosed = make(chan any, 1)
+ })
+}
+
+// listenAndServe listens on the UDP network address addr
+// and then calls Serve to handle requests on incoming connections.
+func (s *server) listenAndServe(addr string) error {
+ s.init()
+ e, err := s.listenQUIC(addr, s.config)
+ if err != nil {
+ return err
+ }
+ go s.serve(e)
+ return nil
+}
+
+// serve accepts incoming connections on the QUIC endpoint e,
+// and handles requests from those connections.
+func (s *server) serve(e *quic.Endpoint) error {
+ s.init()
+ defer e.Close(canceledCtx)
+ for {
+ qconn, err := e.Accept(s.serveCtx)
+ if err != nil {
+ return err
+ }
+ go s.newServerConn(qconn, s.handler)
+ }
+}
+
+// shutdown attempts a graceful shutdown for the server.
+func (s *server) shutdown(ctx context.Context) {
+ // Set a reasonable default in case ctx is nil.
+ if ctx == nil {
+ var cancel context.CancelFunc
+ ctx, cancel = context.WithTimeout(context.Background(), time.Second)
+ defer cancel()
+ }
+
+ // Send GOAWAY frames to all active connections to give a chance for them
+ // to gracefully terminate.
+ s.mu.Lock()
+ for sc := range s.activeConns {
+ // TODO: Modify x/net/quic stream API so that write errors from context
+ // deadline are sticky.
+ go sc.sendGoaway()
+ }
+ s.mu.Unlock()
+
+ // Complete shutdown as soon as there are no more active connections or ctx
+ // is done, whichever comes first.
+ defer func() {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ s.serveCtxCancel()
+ for sc := range s.activeConns {
+ sc.abort(&connectionError{
+ code: errH3NoError,
+ message: "server is shutting down",
+ })
+ }
+ }()
+ noMoreConns := func() bool {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ return len(s.activeConns) == 0
+ }
+ for {
+ if noMoreConns() {
+ return
+ }
+ select {
+ case <-ctx.Done():
+ return
+ case <-s.connClosed:
+ }
+ }
+}
+
+func (s *server) registerConn(sc *serverConn) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ s.activeConns[sc] = struct{}{}
+}
+
+func (s *server) unregisterConn(sc *serverConn) {
+ s.mu.Lock()
+ delete(s.activeConns, sc)
+ s.mu.Unlock()
+ select {
+ case s.connClosed <- struct{}{}:
+ default:
+ // Channel already full. No need to send more values since we are just
+ // using this channel as a simpler sync.Cond.
+ }
+}
+
+type serverConn struct {
+ qconn *quic.Conn
+
+ genericConn // for handleUnidirectionalStream
+ enc qpackEncoder
+ dec qpackDecoder
+ handler http.Handler
+
+ // For handling shutdown.
+ controlStream *stream
+ mu sync.Mutex // Guards everything below.
+ maxRequestStreamID int64
+ goawaySent bool
+}
+
+func (s *server) newServerConn(qconn *quic.Conn, handler http.Handler) {
+ sc := &serverConn{
+ qconn: qconn,
+ handler: handler,
+ }
+ s.registerConn(sc)
+ defer s.unregisterConn(sc)
+ sc.enc.init()
+
+ // Create control stream and send SETTINGS frame.
+ // TODO: Time out on creating stream.
+ var err error
+ sc.controlStream, err = newConnStream(context.Background(), sc.qconn, streamTypeControl)
+ if err != nil {
+ return
+ }
+ sc.controlStream.writeSettings()
+ sc.controlStream.Flush()
+
+ sc.acceptStreams(sc.qconn, sc)
+}
+
+func (sc *serverConn) handleControlStream(st *stream) error {
+ // "A SETTINGS frame MUST be sent as the first frame of each control stream [...]"
+ // https://www.rfc-editor.org/rfc/rfc9114.html#section-7.2.4-2
+ if err := st.readSettings(func(settingsType, settingsValue int64) error {
+ switch settingsType {
+ case settingsMaxFieldSectionSize:
+ _ = settingsValue // TODO
+ case settingsQPACKMaxTableCapacity:
+ _ = settingsValue // TODO
+ case settingsQPACKBlockedStreams:
+ _ = settingsValue // TODO
+ default:
+ // Unknown settings types are ignored.
+ }
+ return nil
+ }); err != nil {
+ return err
+ }
+
+ for {
+ ftype, err := st.readFrameHeader()
+ if err != nil {
+ return err
+ }
+ switch ftype {
+ case frameTypeCancelPush:
+ // "If a server receives a CANCEL_PUSH frame for a push ID
+ // that has not yet been mentioned by a PUSH_PROMISE frame,
+ // this MUST be treated as a connection error of type H3_ID_ERROR."
+ // https://www.rfc-editor.org/rfc/rfc9114.html#section-7.2.3-8
+ return &connectionError{
+ code: errH3IDError,
+ message: "CANCEL_PUSH for unsent push ID",
+ }
+ case frameTypeGoaway:
+ return errH3NoError
+ default:
+ // Unknown frames are ignored.
+ if err := st.discardUnknownFrame(ftype); err != nil {
+ return err
+ }
+ }
+ }
+}
+
+func (sc *serverConn) handleEncoderStream(*stream) error {
+ // TODO
+ return nil
+}
+
+func (sc *serverConn) handleDecoderStream(*stream) error {
+ // TODO
+ return nil
+}
+
+func (sc *serverConn) handlePushStream(*stream) error {
+ // "[...] if a server receives a client-initiated push stream,
+ // this MUST be treated as a connection error of type H3_STREAM_CREATION_ERROR."
+ // https://www.rfc-editor.org/rfc/rfc9114.html#section-6.2.2-3
+ return &connectionError{
+ code: errH3StreamCreationError,
+ message: "client created push stream",
+ }
+}
+
+type pseudoHeader struct {
+ method string
+ scheme string
+ path string
+ authority string
+}
+
+func (sc *serverConn) parseHeader(st *stream) (http.Header, pseudoHeader, error) {
+ ftype, err := st.readFrameHeader()
+ if err != nil {
+ return nil, pseudoHeader{}, err
+ }
+ if ftype != frameTypeHeaders {
+ return nil, pseudoHeader{}, err
+ }
+ header := make(http.Header)
+ var pHeader pseudoHeader
+ var dec qpackDecoder
+ if err := dec.decode(st, func(_ indexType, name, value string) error {
+ switch name {
+ case ":method":
+ pHeader.method = value
+ case ":scheme":
+ pHeader.scheme = value
+ case ":path":
+ pHeader.path = value
+ case ":authority":
+ pHeader.authority = value
+ default:
+ header.Add(name, value)
+ }
+ return nil
+ }); err != nil {
+ return nil, pseudoHeader{}, err
+ }
+ if err := st.endFrame(); err != nil {
+ return nil, pseudoHeader{}, err
+ }
+ return header, pHeader, nil
+}
+
+func (sc *serverConn) sendGoaway() {
+ sc.mu.Lock()
+ if sc.goawaySent || sc.controlStream == nil {
+ sc.mu.Unlock()
+ return
+ }
+ sc.goawaySent = true
+ sc.mu.Unlock()
+
+ // No lock in this section in case writing to stream blocks. This is safe
+ // since sc.maxRequestStreamID is only updated when sc.goawaySent is false.
+ sc.controlStream.writeVarint(int64(frameTypeGoaway))
+ sc.controlStream.writeVarint(int64(sizeVarint(uint64(sc.maxRequestStreamID))))
+ sc.controlStream.writeVarint(sc.maxRequestStreamID)
+ sc.controlStream.Flush()
+}
+
+// requestShouldGoAway returns true if st has a stream ID that is equal or
+// greater than the ID we have sent in a GOAWAY frame, if any.
+func (sc *serverConn) requestShouldGoaway(st *stream) bool {
+ sc.mu.Lock()
+ defer sc.mu.Unlock()
+ if sc.goawaySent {
+ return st.stream.ID() >= sc.maxRequestStreamID
+ } else {
+ sc.maxRequestStreamID = max(sc.maxRequestStreamID, st.stream.ID())
+ return false
+ }
+}
+
+func (sc *serverConn) handleRequestStream(st *stream) error {
+ if sc.requestShouldGoaway(st) {
+ return &streamError{
+ code: errH3RequestRejected,
+ message: "GOAWAY request with equal or lower ID than the stream has been sent",
+ }
+ }
+ header, pHeader, err := sc.parseHeader(st)
+ if err != nil {
+ return err
+ }
+
+ reqInfo := httpcommon.NewServerRequest(httpcommon.ServerRequestParam{
+ Method: pHeader.method,
+ Scheme: pHeader.scheme,
+ Authority: pHeader.authority,
+ Path: pHeader.path,
+ Header: header,
+ })
+ if reqInfo.InvalidReason != "" {
+ return &streamError{
+ code: errH3MessageError,
+ message: reqInfo.InvalidReason,
+ }
+ }
+
+ var body io.ReadCloser
+ contentLength := int64(-1)
+ if n, err := strconv.Atoi(header.Get("Content-Length")); err == nil {
+ contentLength = int64(n)
+ }
+ if contentLength != 0 || len(reqInfo.Trailer) != 0 {
+ body = &bodyReader{
+ st: st,
+ remain: contentLength,
+ trailer: reqInfo.Trailer,
+ }
+ } else {
+ body = http.NoBody
+ }
+
+ req := &http.Request{
+ Proto: "HTTP/3.0",
+ Method: pHeader.method,
+ Host: pHeader.authority,
+ URL: reqInfo.URL,
+ RequestURI: reqInfo.RequestURI,
+ Trailer: reqInfo.Trailer,
+ ProtoMajor: 3,
+ RemoteAddr: sc.qconn.RemoteAddr().String(),
+ Body: body,
+ Header: header,
+ ContentLength: contentLength,
+ }
+ defer req.Body.Close()
+
+ rw := &responseWriter{
+ st: st,
+ headers: make(http.Header),
+ trailer: make(http.Header),
+ bb: make(bodyBuffer, 0, defaultBodyBufferCap),
+ cannotHaveBody: req.Method == "HEAD",
+ bw: &bodyWriter{
+ st: st,
+ remain: -1,
+ flush: false,
+ name: "response",
+ enc: &sc.enc,
+ },
+ }
+ defer rw.close()
+ if reqInfo.NeedsContinue {
+ req.Body.(*bodyReader).send100Continue = func() {
+ rw.WriteHeader(100)
+ }
+ }
+
+ // TODO: handle panic coming from the HTTP handler.
+ sc.handler.ServeHTTP(rw, req)
+ return nil
+}
+
+// abort closes the connection with an error.
+func (sc *serverConn) abort(err error) {
+ if e, ok := err.(*connectionError); ok {
+ sc.qconn.Abort(&quic.ApplicationError{
+ Code: uint64(e.code),
+ Reason: e.message,
+ })
+ } else {
+ sc.qconn.Abort(err)
+ }
+}
+
+// responseCanHaveBody reports whether a given response status code permits a
+// body. See RFC 7230, section 3.3.
+func responseCanHaveBody(status int) bool {
+ switch {
+ case status >= 100 && status <= 199:
+ return false
+ case status == 204:
+ return false
+ case status == 304:
+ return false
+ }
+ return true
+}
+
+type responseWriter struct {
+ st *stream
+ bw *bodyWriter
+ mu sync.Mutex
+ headers http.Header
+ trailer http.Header
+ bb bodyBuffer
+ wroteHeader bool // Non-1xx header has been (logically) written.
+ statusCode int // Status of the response that will be sent in HEADERS frame.
+ statusCodeSet bool // Status of the response has been set via a call to WriteHeader.
+ cannotHaveBody bool // Response should not have a body (e.g. response to a HEAD request).
+ bodyLenLeft int // How much of the content body is left to be sent, set via "Content-Length" header. -1 if unknown.
+}
+
+func (rw *responseWriter) Header() http.Header {
+ return rw.headers
+}
+
+// prepareTrailerForWriteLocked populates any pre-declared trailer header with
+// its value, and passes it to bodyWriter so it can be written after body EOF.
+// Caller must hold rw.mu.
+func (rw *responseWriter) prepareTrailerForWriteLocked() {
+ for name := range rw.trailer {
+ if val, ok := rw.headers[name]; ok {
+ rw.trailer[name] = val
+ } else {
+ delete(rw.trailer, name)
+ }
+ }
+ if len(rw.trailer) > 0 {
+ rw.bw.trailer = rw.trailer
+ }
+}
+
+// writeHeaderLockedOnce writes the final response header. If rw.wroteHeader is
+// true, calling this method is a no-op. Sending informational status headers
+// should be done using writeInfoHeaderLocked, rather than this method.
+// Caller must hold rw.mu.
+func (rw *responseWriter) writeHeaderLockedOnce() {
+ if rw.wroteHeader {
+ return
+ }
+ if !responseCanHaveBody(rw.statusCode) {
+ rw.cannotHaveBody = true
+ }
+ // If there is any Trailer declared in headers, save them so we know which
+ // trailers have been pre-declared. Also, write back the extracted value,
+ // which is canonicalized, to rw.Header for consistency.
+ if _, ok := rw.headers["Trailer"]; ok {
+ extractTrailerFromHeader(rw.headers, rw.trailer)
+ rw.headers.Set("Trailer", strings.Join(slices.Sorted(maps.Keys(rw.trailer)), ", "))
+ }
+
+ rw.bb.inferHeader(rw.headers, rw.statusCode)
+ encHeaders := rw.bw.enc.encode(func(f func(itype indexType, name, value string)) {
+ f(mayIndex, ":status", strconv.Itoa(rw.statusCode))
+ for name, values := range rw.headers {
+ if !httpguts.ValidHeaderFieldName(name) {
+ continue
+ }
+ for _, val := range values {
+ if !httpguts.ValidHeaderFieldValue(val) {
+ continue
+ }
+ // Issue #71374: Consider supporting never-indexed fields.
+ f(mayIndex, name, val)
+ }
+ }
+ })
+
+ rw.st.writeVarint(int64(frameTypeHeaders))
+ rw.st.writeVarint(int64(len(encHeaders)))
+ rw.st.Write(encHeaders)
+ rw.wroteHeader = true
+}
+
+// writeHeaderLocked writes informational status headers (i.e. status 1XX).
+// If a non-informational status header has been written via
+// writeHeaderLockedOnce, this method is a no-op.
+// Caller must hold rw.mu.
+func (rw *responseWriter) writeHeaderLocked(statusCode int) {
+ if rw.wroteHeader {
+ return
+ }
+ encHeaders := rw.bw.enc.encode(func(f func(itype indexType, name, value string)) {
+ f(mayIndex, ":status", strconv.Itoa(statusCode))
+ for name, values := range rw.headers {
+ if name == "Content-Length" || name == "Transfer-Encoding" {
+ continue
+ }
+ if !httpguts.ValidHeaderFieldName(name) {
+ continue
+ }
+ for _, val := range values {
+ if !httpguts.ValidHeaderFieldValue(val) {
+ continue
+ }
+ // Issue #71374: Consider supporting never-indexed fields.
+ f(mayIndex, name, val)
+ }
+ }
+ })
+ rw.st.writeVarint(int64(frameTypeHeaders))
+ rw.st.writeVarint(int64(len(encHeaders)))
+ rw.st.Write(encHeaders)
+}
+
+func isInfoStatus(status int) bool {
+ return status >= 100 && status < 200
+}
+
+// checkWriteHeaderCode is a copy of net/http's checkWriteHeaderCode.
+func checkWriteHeaderCode(code int) {
+ // Issue 22880: require valid WriteHeader status codes.
+ // For now we only enforce that it's three digits.
+ // In the future we might block things over 599 (600 and above aren't defined
+ // at http://httpwg.org/specs/rfc7231.html#status.codes).
+ // But for now any three digits.
+ //
+ // We used to send "HTTP/1.1 000 0" on the wire in responses but there's
+ // no equivalent bogus thing we can realistically send in HTTP/3,
+ // so we'll consistently panic instead and help people find their bugs
+ // early. (We can't return an error from WriteHeader even if we wanted to.)
+ if code < 100 || code > 999 {
+ panic(fmt.Sprintf("invalid WriteHeader code %v", code))
+ }
+}
+
+func (rw *responseWriter) WriteHeader(statusCode int) {
+ // TODO: handle sending informational status headers (e.g. 103).
+ rw.mu.Lock()
+ defer rw.mu.Unlock()
+ if rw.statusCodeSet {
+ return
+ }
+ checkWriteHeaderCode(statusCode)
+
+ // Informational headers can be sent multiple times, and should be flushed
+ // immediately.
+ if isInfoStatus(statusCode) {
+ rw.writeHeaderLocked(statusCode)
+ rw.st.Flush()
+ return
+ }
+
+ // Non-informational headers should only be set once, and should be
+ // buffered.
+ rw.statusCodeSet = true
+ rw.statusCode = statusCode
+ if n, err := strconv.Atoi(rw.Header().Get("Content-Length")); err == nil {
+ rw.bodyLenLeft = n
+ } else {
+ rw.bodyLenLeft = -1 // Unknown.
+ }
+}
+
+// trimWriteLocked trims a byte slice, b, such that the length of b will not
+// exceed rw.bodyLenLeft. This method will update rw.bodyLenLeft when trimming
+// b, and will also return whether b was trimmed or not.
+// Caller must hold rw.mu.
+func (rw *responseWriter) trimWriteLocked(b []byte) ([]byte, bool) {
+ if rw.bodyLenLeft < 0 {
+ return b, false
+ }
+ n := min(len(b), rw.bodyLenLeft)
+ rw.bodyLenLeft -= n
+ return b[:n], n != len(b)
+}
+
+func (rw *responseWriter) Write(b []byte) (n int, err error) {
+ // Calling Write implicitly calls WriteHeader(200) if WriteHeader has not
+ // been called before.
+ rw.WriteHeader(http.StatusOK)
+ rw.mu.Lock()
+ defer rw.mu.Unlock()
+
+ if rw.statusCode == http.StatusNotModified {
+ return 0, http.ErrBodyNotAllowed
+ }
+
+ b, trimmed := rw.trimWriteLocked(b)
+ if trimmed {
+ defer func() {
+ err = http.ErrContentLength
+ }()
+ }
+
+ // If b fits entirely in our body buffer, save it to the buffer and return
+ // early so we can coalesce small writes.
+ // As a special case, we always want to save b to the buffer even when b is
+ // big if we had yet to write our header, so we can infer headers like
+ // "Content-Type" with as much information as possible.
+ initialBLen := len(b)
+ initialBufLen := len(rw.bb)
+ if !rw.wroteHeader || len(b) <= cap(rw.bb)-len(rw.bb) {
+ b = rw.bb.write(b)
+ if len(b) == 0 {
+ return initialBLen, nil
+ }
+ }
+
+ // Reaching this point means that our buffer has been sufficiently filled.
+ // Therefore, we now want to:
+ // 1. Infer and write response headers based on our body buffer, if not
+ // done yet.
+ // 2. Write our body buffer and the rest of b (if any).
+ // 3. Reset the current body buffer so it can be used again.
+ rw.writeHeaderLockedOnce()
+ if rw.cannotHaveBody {
+ return initialBLen, nil
+ }
+ if n, err := rw.bw.write(rw.bb, b); err != nil {
+ return max(0, n-initialBufLen), err
+ }
+ rw.bb.discard()
+ return initialBLen, nil
+}
+
+func (rw *responseWriter) Flush() {
+ // Calling Flush implicitly calls WriteHeader(200) if WriteHeader has not
+ // been called before.
+ rw.WriteHeader(http.StatusOK)
+ rw.mu.Lock()
+ defer rw.mu.Unlock()
+ rw.writeHeaderLockedOnce()
+ if !rw.cannotHaveBody {
+ rw.bw.Write(rw.bb)
+ rw.bb.discard()
+ }
+ rw.st.Flush()
+}
+
+func (rw *responseWriter) close() error {
+ rw.Flush()
+ rw.mu.Lock()
+ defer rw.mu.Unlock()
+ rw.prepareTrailerForWriteLocked()
+ if err := rw.bw.Close(); err != nil {
+ return err
+ }
+ return rw.st.stream.Close()
+}
+
+// defaultBodyBufferCap is the default number of bytes of body that we are
+// willing to save in a buffer for the sake of inferring headers and coalescing
+// small writes. 512 was chosen to be consistent with how much
+// http.DetectContentType is willing to read.
+const defaultBodyBufferCap = 512
+
+// bodyBuffer is a buffer used to store body content of a response.
+type bodyBuffer []byte
+
+// write writes b to the buffer. It returns a new slice of b, which contains
+// any remaining data that could not be written to the buffer, if any.
+func (bb *bodyBuffer) write(b []byte) []byte {
+ n := min(len(b), cap(*bb)-len(*bb))
+ *bb = append(*bb, b[:n]...)
+ return b[n:]
+}
+
+// discard resets the buffer so it can be used again.
+func (bb *bodyBuffer) discard() {
+ *bb = (*bb)[:0]
+}
+
+// inferHeader populates h with the header values that we can infer from our
+// current buffer content, if not already explicitly set. This method should be
+// called only once with as much body content as possible in the buffer, before
+// a HEADERS frame is sent, and before discard has been called. Doing so
+// properly is the responsibility of the caller.
+func (bb *bodyBuffer) inferHeader(h http.Header, status int) {
+ if _, ok := h["Date"]; !ok {
+ h.Set("Date", time.Now().UTC().Format(http.TimeFormat))
+ }
+ // If the Content-Encoding is non-blank, we shouldn't
+ // sniff the body. See Issue golang.org/issue/31753.
+ _, hasCE := h["Content-Encoding"]
+ _, hasCT := h["Content-Type"]
+ if !hasCE && !hasCT && responseCanHaveBody(status) && len(*bb) > 0 {
+ h.Set("Content-Type", http.DetectContentType(*bb))
+ }
+ // We can technically infer Content-Length too here, as long as the entire
+ // response body fits within hi.buf and does not require flushing. However,
+ // we have chosen not to do so for now as Content-Length is not very
+ // important for HTTP/3, and such inconsistent behavior might be confusing.
+}
diff --git a/src/vendor/golang.org/x/net/internal/http3/settings.go b/src/vendor/golang.org/x/net/internal/http3/settings.go
new file mode 100644
index 0000000000..2d2ca0e705
--- /dev/null
+++ b/src/vendor/golang.org/x/net/internal/http3/settings.go
@@ -0,0 +1,66 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package http3
+
+const (
+ // https://www.rfc-editor.org/rfc/rfc9114.html#section-7.2.4.1
+ settingsMaxFieldSectionSize = 0x06
+
+ // https://www.rfc-editor.org/rfc/rfc9204.html#section-5
+ settingsQPACKMaxTableCapacity = 0x01
+ settingsQPACKBlockedStreams = 0x07
+)
+
+// writeSettings writes a complete SETTINGS frame.
+// Its parameter is a list of alternating setting types and values.
+func (st *stream) writeSettings(settings ...int64) {
+ var size int64
+ for _, s := range settings {
+ // Settings values that don't fit in a QUIC varint ([0,2^62)) will panic here.
+ size += int64(sizeVarint(uint64(s)))
+ }
+ st.writeVarint(int64(frameTypeSettings))
+ st.writeVarint(size)
+ for _, s := range settings {
+ st.writeVarint(s)
+ }
+}
+
+// readSettings reads a complete SETTINGS frame, including the frame header.
+func (st *stream) readSettings(f func(settingType, value int64) error) error {
+ frameType, err := st.readFrameHeader()
+ if err != nil || frameType != frameTypeSettings {
+ return &connectionError{
+ code: errH3MissingSettings,
+ message: "settings not sent on control stream",
+ }
+ }
+ for st.lim > 0 {
+ settingsType, err := st.readVarint()
+ if err != nil {
+ return err
+ }
+ settingsValue, err := st.readVarint()
+ if err != nil {
+ return err
+ }
+
+ // Use of HTTP/2 settings where there is no corresponding HTTP/3 setting
+ // is an error.
+ // https://www.rfc-editor.org/rfc/rfc9114.html#section-7.2.4.1-5
+ switch settingsType {
+ case 0x02, 0x03, 0x04, 0x05:
+ return &connectionError{
+ code: errH3SettingsError,
+ message: "use of reserved setting",
+ }
+ }
+
+ if err := f(settingsType, settingsValue); err != nil {
+ return err
+ }
+ }
+ return st.endFrame()
+}
diff --git a/src/vendor/golang.org/x/net/internal/http3/stream.go b/src/vendor/golang.org/x/net/internal/http3/stream.go
new file mode 100644
index 0000000000..93294d43de
--- /dev/null
+++ b/src/vendor/golang.org/x/net/internal/http3/stream.go
@@ -0,0 +1,260 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package http3
+
+import (
+ "context"
+ "io"
+
+ "golang.org/x/net/quic"
+)
+
+// A stream wraps a QUIC stream, providing methods to read/write various values.
+type stream struct {
+ stream *quic.Stream
+
+ // lim is the current read limit.
+ // Reading a frame header sets the limit to the end of the frame.
+ // Reading past the limit or reading less than the limit and ending the frame
+ // results in an error.
+ // -1 indicates no limit.
+ lim int64
+}
+
+// newConnStream creates a new stream on a connection.
+// It writes the stream header for unidirectional streams.
+//
+// The stream returned by newStream is not flushed,
+// and will not be sent to the peer until the caller calls
+// Flush or writes enough data to the stream.
+func newConnStream(ctx context.Context, qconn *quic.Conn, stype streamType) (*stream, error) {
+ var qs *quic.Stream
+ var err error
+ if stype == streamTypeRequest {
+ // Request streams are bidirectional.
+ qs, err = qconn.NewStream(ctx)
+ } else {
+ // All other streams are unidirectional.
+ qs, err = qconn.NewSendOnlyStream(ctx)
+ }
+ if err != nil {
+ return nil, err
+ }
+ st := &stream{
+ stream: qs,
+ lim: -1, // no limit
+ }
+ if stype != streamTypeRequest {
+ // Unidirectional stream header.
+ st.writeVarint(int64(stype))
+ }
+ return st, err
+}
+
+func newStream(qs *quic.Stream) *stream {
+ return &stream{
+ stream: qs,
+ lim: -1, // no limit
+ }
+}
+
+// readFrameHeader reads the type and length fields of an HTTP/3 frame.
+// It sets the read limit to the end of the frame.
+//
+// https://www.rfc-editor.org/rfc/rfc9114.html#section-7.1
+func (st *stream) readFrameHeader() (ftype frameType, err error) {
+ if st.lim >= 0 {
+ // We shouldn't call readFrameHeader before ending the previous frame.
+ return 0, errH3FrameError
+ }
+ ftype, err = readVarint[frameType](st)
+ if err != nil {
+ return 0, err
+ }
+ size, err := st.readVarint()
+ if err != nil {
+ return 0, err
+ }
+ st.lim = size
+ return ftype, nil
+}
+
+// endFrame is called after reading a frame to reset the read limit.
+// It returns an error if the entire contents of a frame have not been read.
+func (st *stream) endFrame() error {
+ if st.lim != 0 {
+ return &connectionError{
+ code: errH3FrameError,
+ message: "invalid HTTP/3 frame",
+ }
+ }
+ st.lim = -1
+ return nil
+}
+
+// readFrameData returns the remaining data in the current frame.
+func (st *stream) readFrameData() ([]byte, error) {
+ if st.lim < 0 {
+ return nil, errH3FrameError
+ }
+ // TODO: Pool buffers to avoid allocation here.
+ b := make([]byte, st.lim)
+ _, err := io.ReadFull(st, b)
+ if err != nil {
+ return nil, err
+ }
+ return b, nil
+}
+
+// ReadByte reads one byte from the stream.
+func (st *stream) ReadByte() (b byte, err error) {
+ if err := st.recordBytesRead(1); err != nil {
+ return 0, err
+ }
+ b, err = st.stream.ReadByte()
+ if err != nil {
+ if err == io.EOF && st.lim < 0 {
+ return 0, io.EOF
+ }
+ return 0, errH3FrameError
+ }
+ return b, nil
+}
+
+// Read reads from the stream.
+func (st *stream) Read(b []byte) (int, error) {
+ n, err := st.stream.Read(b)
+ if e2 := st.recordBytesRead(n); e2 != nil {
+ return 0, e2
+ }
+ if err == io.EOF {
+ if st.lim == 0 {
+ // EOF at end of frame, ignore.
+ return n, nil
+ } else if st.lim > 0 {
+ // EOF inside frame, error.
+ return 0, errH3FrameError
+ } else {
+ // EOF outside of frame, surface to caller.
+ return n, io.EOF
+ }
+ }
+ if err != nil {
+ return 0, errH3FrameError
+ }
+ return n, nil
+}
+
+// discardUnknownFrame discards an unknown frame.
+//
+// HTTP/3 requires that unknown frames be ignored on all streams.
+// However, a known frame appearing in an unexpected place is a fatal error,
+// so this returns an error if the frame is one we know.
+func (st *stream) discardUnknownFrame(ftype frameType) error {
+ switch ftype {
+ case frameTypeData,
+ frameTypeHeaders,
+ frameTypeCancelPush,
+ frameTypeSettings,
+ frameTypePushPromise,
+ frameTypeGoaway,
+ frameTypeMaxPushID:
+ return &connectionError{
+ code: errH3FrameUnexpected,
+ message: "unexpected " + ftype.String() + " frame",
+ }
+ }
+ return st.discardFrame()
+}
+
+// discardFrame discards any remaining data in the current frame and resets the read limit.
+func (st *stream) discardFrame() error {
+ // TODO: Consider adding a *quic.Stream method to discard some amount of data.
+ for range st.lim {
+ _, err := st.stream.ReadByte()
+ if err != nil {
+ return &streamError{errH3FrameError, err.Error()}
+ }
+ }
+ st.lim = -1
+ return nil
+}
+
+// Write writes to the stream.
+func (st *stream) Write(b []byte) (int, error) { return st.stream.Write(b) }
+
+// Flush commits data written to the stream.
+func (st *stream) Flush() error { return st.stream.Flush() }
+
+// readVarint reads a QUIC variable-length integer from the stream.
+func (st *stream) readVarint() (v int64, err error) {
+ b, err := st.stream.ReadByte()
+ if err != nil {
+ return 0, err
+ }
+ v = int64(b & 0x3f)
+ n := 1 << (b >> 6)
+ for i := 1; i < n; i++ {
+ b, err := st.stream.ReadByte()
+ if err != nil {
+ return 0, errH3FrameError
+ }
+ v = (v << 8) | int64(b)
+ }
+ if err := st.recordBytesRead(n); err != nil {
+ return 0, err
+ }
+ return v, nil
+}
+
+// readVarint reads a varint of a particular type.
+func readVarint[T ~int64 | ~uint64](st *stream) (T, error) {
+ v, err := st.readVarint()
+ return T(v), err
+}
+
+// writeVarint writes a QUIC variable-length integer to the stream.
+func (st *stream) writeVarint(v int64) {
+ switch {
+ case v <= (1<<6)-1:
+ st.stream.WriteByte(byte(v))
+ case v <= (1<<14)-1:
+ st.stream.WriteByte((1 << 6) | byte(v>>8))
+ st.stream.WriteByte(byte(v))
+ case v <= (1<<30)-1:
+ st.stream.WriteByte((2 << 6) | byte(v>>24))
+ st.stream.WriteByte(byte(v >> 16))
+ st.stream.WriteByte(byte(v >> 8))
+ st.stream.WriteByte(byte(v))
+ case v <= (1<<62)-1:
+ st.stream.WriteByte((3 << 6) | byte(v>>56))
+ st.stream.WriteByte(byte(v >> 48))
+ st.stream.WriteByte(byte(v >> 40))
+ st.stream.WriteByte(byte(v >> 32))
+ st.stream.WriteByte(byte(v >> 24))
+ st.stream.WriteByte(byte(v >> 16))
+ st.stream.WriteByte(byte(v >> 8))
+ st.stream.WriteByte(byte(v))
+ default:
+ panic("varint too large")
+ }
+}
+
+// recordBytesRead records that n bytes have been read.
+// It returns an error if the read passes the current limit.
+func (st *stream) recordBytesRead(n int) error {
+ if st.lim < 0 {
+ return nil
+ }
+ st.lim -= int64(n)
+ if st.lim < 0 {
+ st.stream = nil // panic if we try to read again
+ return &connectionError{
+ code: errH3FrameError,
+ message: "invalid HTTP/3 frame",
+ }
+ }
+ return nil
+}
diff --git a/src/vendor/golang.org/x/net/internal/http3/transport.go b/src/vendor/golang.org/x/net/internal/http3/transport.go
new file mode 100644
index 0000000000..a99824c9f4
--- /dev/null
+++ b/src/vendor/golang.org/x/net/internal/http3/transport.go
@@ -0,0 +1,284 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package http3
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "net/url"
+ "sync"
+
+ "golang.org/x/net/quic"
+)
+
+// A transport is an HTTP/3 transport.
+//
+// It does not manage a pool of connections,
+// and therefore does not implement net/http.RoundTripper.
+//
+// TODO: Provide a way to register an HTTP/3 transport with a net/http.transport's
+// connection pool.
+type transport struct {
+ // config is the QUIC configuration used for client connections.
+ config *quic.Config
+
+ mu sync.Mutex // Guards fields below.
+ // endpoint is the QUIC endpoint used by connections created by the
+ // transport. If CloseIdleConnections is called when activeConns is empty,
+ // endpoint will be unset. If unset, endpoint will be initialized by any
+ // call to dial.
+ endpoint *quic.Endpoint
+ activeConns map[*clientConn]struct{}
+ inFlightDials int
+}
+
+// netHTTPTransport implements the net/http.dialClientConner interface,
+// allowing our HTTP/3 transport to integrate with net/http.
+type netHTTPTransport struct {
+ *transport
+}
+
+// RoundTrip is defined since Transport.RegisterProtocol takes in a
+// RoundTripper. However, this method will never be used as net/http's
+// dialClientConner interface does not have a RoundTrip method and will only
+// use DialClientConn to create a new RoundTripper.
+func (t netHTTPTransport) RoundTrip(*http.Request) (*http.Response, error) {
+ panic("netHTTPTransport.RoundTrip should never be called")
+}
+
+func (t netHTTPTransport) DialClientConn(ctx context.Context, addr string, _ *url.URL, _ func()) (http.RoundTripper, error) {
+ return t.transport.dial(ctx, addr)
+}
+
+// RegisterTransport configures a net/http HTTP/1 Transport to use HTTP/3.
+//
+// TODO: most likely, add another arg for transport configuration.
+func RegisterTransport(tr *http.Transport) {
+ tr3 := &transport{
+ // initConfig will clone the tr.TLSClientConfig.
+ config: initConfig(&quic.Config{
+ TLSConfig: tr.TLSClientConfig,
+ }),
+ activeConns: make(map[*clientConn]struct{}),
+ }
+ tr.RegisterProtocol("http/3", netHTTPTransport{tr3})
+}
+
+func (tr *transport) incInFlightDials() {
+ tr.mu.Lock()
+ defer tr.mu.Unlock()
+ tr.inFlightDials++
+}
+
+func (tr *transport) decInFlightDials() {
+ tr.mu.Lock()
+ defer tr.mu.Unlock()
+ tr.inFlightDials--
+}
+
+func (tr *transport) initEndpoint() (err error) {
+ tr.mu.Lock()
+ defer tr.mu.Unlock()
+ if tr.endpoint == nil {
+ tr.endpoint, err = quic.Listen("udp", ":0", nil)
+ }
+ return err
+}
+
+// dial creates a new HTTP/3 client connection.
+func (tr *transport) dial(ctx context.Context, target string) (*clientConn, error) {
+ tr.incInFlightDials()
+ defer tr.decInFlightDials()
+
+ if err := tr.initEndpoint(); err != nil {
+ return nil, err
+ }
+ qconn, err := tr.endpoint.Dial(ctx, "udp", target, tr.config)
+ if err != nil {
+ return nil, err
+ }
+ return tr.newClientConn(ctx, qconn)
+}
+
+// CloseIdleConnections is called by net/http.Transport.CloseIdleConnections
+// after all existing idle connections are closed using http3.clientConn.Close.
+//
+// When the transport has no active connections anymore, calling this method
+// will make the transport clean up any shared resources that are no longer
+// required, such as its QUIC endpoint.
+func (tr *transport) CloseIdleConnections() {
+ tr.mu.Lock()
+ defer tr.mu.Unlock()
+ if tr.endpoint == nil || len(tr.activeConns) > 0 || tr.inFlightDials > 0 {
+ return
+ }
+ tr.endpoint.Close(canceledCtx)
+ tr.endpoint = nil
+}
+
+// A clientConn is a client HTTP/3 connection.
+//
+// Multiple goroutines may invoke methods on a clientConn simultaneously.
+type clientConn struct {
+ qconn *quic.Conn
+ genericConn
+
+ enc qpackEncoder
+ dec qpackDecoder
+}
+
+func (tr *transport) registerConn(cc *clientConn) {
+ tr.mu.Lock()
+ defer tr.mu.Unlock()
+ tr.activeConns[cc] = struct{}{}
+}
+
+func (tr *transport) unregisterConn(cc *clientConn) {
+ tr.mu.Lock()
+ defer tr.mu.Unlock()
+ delete(tr.activeConns, cc)
+}
+
+func (tr *transport) newClientConn(ctx context.Context, qconn *quic.Conn) (*clientConn, error) {
+ cc := &clientConn{
+ qconn: qconn,
+ }
+ tr.registerConn(cc)
+ cc.enc.init()
+
+ // Create control stream and send SETTINGS frame.
+ controlStream, err := newConnStream(ctx, cc.qconn, streamTypeControl)
+ if err != nil {
+ tr.unregisterConn(cc)
+ return nil, fmt.Errorf("http3: cannot create control stream: %v", err)
+ }
+ controlStream.writeSettings()
+ controlStream.Flush()
+
+ go func() {
+ cc.acceptStreams(qconn, cc)
+ tr.unregisterConn(cc)
+ }()
+ return cc, nil
+}
+
+// TODO: implement the rest of net/http.ClientConn methods beyond Close.
+func (cc *clientConn) Close() error {
+ // We need to use Close rather than Abort on the QUIC connection.
+ // Otherwise, when a net/http.Transport.CloseIdleConnections is called, it
+ // might call the http3.transport.CloseIdleConnections prior to all idle
+ // connections being fully closed; this would make it unable to close its
+ // QUIC endpoint, making http3.transport.CloseIdleConnections a no-op
+ // unintentionally.
+ return cc.qconn.Close()
+}
+
+func (cc *clientConn) Err() error {
+ return nil
+}
+
+func (cc *clientConn) Reserve() error {
+ return nil
+}
+
+func (cc *clientConn) Release() {
+}
+
+func (cc *clientConn) Available() int {
+ return 0
+}
+
+func (cc *clientConn) InFlight() int {
+ return 0
+}
+
+func (cc *clientConn) handleControlStream(st *stream) error {
+ // "A SETTINGS frame MUST be sent as the first frame of each control stream [...]"
+ // https://www.rfc-editor.org/rfc/rfc9114.html#section-7.2.4-2
+ if err := st.readSettings(func(settingsType, settingsValue int64) error {
+ switch settingsType {
+ case settingsMaxFieldSectionSize:
+ _ = settingsValue // TODO
+ case settingsQPACKMaxTableCapacity:
+ _ = settingsValue // TODO
+ case settingsQPACKBlockedStreams:
+ _ = settingsValue // TODO
+ default:
+ // Unknown settings types are ignored.
+ }
+ return nil
+ }); err != nil {
+ return err
+ }
+
+ for {
+ ftype, err := st.readFrameHeader()
+ if err != nil {
+ return err
+ }
+ switch ftype {
+ case frameTypeCancelPush:
+ // "If a CANCEL_PUSH frame is received that references a push ID
+ // greater than currently allowed on the connection,
+ // this MUST be treated as a connection error of type H3_ID_ERROR."
+ // https://www.rfc-editor.org/rfc/rfc9114.html#section-7.2.3-7
+ return &connectionError{
+ code: errH3IDError,
+ message: "CANCEL_PUSH received when no MAX_PUSH_ID has been sent",
+ }
+ case frameTypeGoaway:
+ // TODO: Wait for requests to complete before closing connection.
+ return errH3NoError
+ default:
+ // Unknown frames are ignored.
+ if err := st.discardUnknownFrame(ftype); err != nil {
+ return err
+ }
+ }
+ }
+}
+
+func (cc *clientConn) handleEncoderStream(*stream) error {
+ // TODO
+ return nil
+}
+
+func (cc *clientConn) handleDecoderStream(*stream) error {
+ // TODO
+ return nil
+}
+
+func (cc *clientConn) handlePushStream(*stream) error {
+ // "A client MUST treat receipt of a push stream as a connection error
+ // of type H3_ID_ERROR when no MAX_PUSH_ID frame has been sent [...]"
+ // https://www.rfc-editor.org/rfc/rfc9114.html#section-4.6-3
+ return &connectionError{
+ code: errH3IDError,
+ message: "push stream created when no MAX_PUSH_ID has been sent",
+ }
+}
+
+func (cc *clientConn) handleRequestStream(st *stream) error {
+ // "Clients MUST treat receipt of a server-initiated bidirectional
+ // stream as a connection error of type H3_STREAM_CREATION_ERROR [...]"
+ // https://www.rfc-editor.org/rfc/rfc9114.html#section-6.1-3
+ return &connectionError{
+ code: errH3StreamCreationError,
+ message: "server created bidirectional stream",
+ }
+}
+
+// abort closes the connection with an error.
+func (cc *clientConn) abort(err error) {
+ if e, ok := err.(*connectionError); ok {
+ cc.qconn.Abort(&quic.ApplicationError{
+ Code: uint64(e.code),
+ Reason: e.message,
+ })
+ } else {
+ cc.qconn.Abort(err)
+ }
+}
diff --git a/src/vendor/golang.org/x/net/internal/http3/varint.go b/src/vendor/golang.org/x/net/internal/http3/varint.go
new file mode 100644
index 0000000000..bee3d71fb3
--- /dev/null
+++ b/src/vendor/golang.org/x/net/internal/http3/varint.go
@@ -0,0 +1,23 @@
+// Copyright 2026 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package http3
+
+// sizeVarint returns the size of the variable-length integer encoding of f.
+// Copied from internal/quic/quicwire to break dependency that makes bundling
+// into std more complicated.
+func sizeVarint(v uint64) int {
+ switch {
+ case v <= 63:
+ return 1
+ case v <= 16383:
+ return 2
+ case v <= 1073741823:
+ return 4
+ case v <= 4611686018427387903:
+ return 8
+ default:
+ panic("varint too large")
+ }
+}
diff --git a/src/vendor/golang.org/x/net/internal/httpcommon/ascii.go b/src/vendor/golang.org/x/net/internal/httpcommon/ascii.go
new file mode 100644
index 0000000000..ed14da5afc
--- /dev/null
+++ b/src/vendor/golang.org/x/net/internal/httpcommon/ascii.go
@@ -0,0 +1,53 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package httpcommon
+
+import "strings"
+
+// The HTTP protocols are defined in terms of ASCII, not Unicode. This file
+// contains helper functions which may use Unicode-aware functions which would
+// otherwise be unsafe and could introduce vulnerabilities if used improperly.
+
+// asciiEqualFold is strings.EqualFold, ASCII only. It reports whether s and t
+// are equal, ASCII-case-insensitively.
+func asciiEqualFold(s, t string) bool {
+ if len(s) != len(t) {
+ return false
+ }
+ for i := 0; i < len(s); i++ {
+ if lower(s[i]) != lower(t[i]) {
+ return false
+ }
+ }
+ return true
+}
+
+// lower returns the ASCII lowercase version of b.
+func lower(b byte) byte {
+ if 'A' <= b && b <= 'Z' {
+ return b + ('a' - 'A')
+ }
+ return b
+}
+
+// isASCIIPrint returns whether s is ASCII and printable according to
+// https://tools.ietf.org/html/rfc20#section-4.2.
+func isASCIIPrint(s string) bool {
+ for i := 0; i < len(s); i++ {
+ if s[i] < ' ' || s[i] > '~' {
+ return false
+ }
+ }
+ return true
+}
+
+// asciiToLower returns the lowercase version of s if s is ASCII and printable,
+// and whether or not it was.
+func asciiToLower(s string) (lower string, ok bool) {
+ if !isASCIIPrint(s) {
+ return "", false
+ }
+ return strings.ToLower(s), true
+}
diff --git a/src/vendor/golang.org/x/net/internal/httpcommon/headermap.go b/src/vendor/golang.org/x/net/internal/httpcommon/headermap.go
new file mode 100644
index 0000000000..92483d8e41
--- /dev/null
+++ b/src/vendor/golang.org/x/net/internal/httpcommon/headermap.go
@@ -0,0 +1,115 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package httpcommon
+
+import (
+ "net/textproto"
+ "sync"
+)
+
+var (
+ commonBuildOnce sync.Once
+ commonLowerHeader map[string]string // Go-Canonical-Case -> lower-case
+ commonCanonHeader map[string]string // lower-case -> Go-Canonical-Case
+)
+
+func buildCommonHeaderMapsOnce() {
+ commonBuildOnce.Do(buildCommonHeaderMaps)
+}
+
+func buildCommonHeaderMaps() {
+ common := []string{
+ "accept",
+ "accept-charset",
+ "accept-encoding",
+ "accept-language",
+ "accept-ranges",
+ "age",
+ "access-control-allow-credentials",
+ "access-control-allow-headers",
+ "access-control-allow-methods",
+ "access-control-allow-origin",
+ "access-control-expose-headers",
+ "access-control-max-age",
+ "access-control-request-headers",
+ "access-control-request-method",
+ "allow",
+ "authorization",
+ "cache-control",
+ "content-disposition",
+ "content-encoding",
+ "content-language",
+ "content-length",
+ "content-location",
+ "content-range",
+ "content-type",
+ "cookie",
+ "date",
+ "etag",
+ "expect",
+ "expires",
+ "from",
+ "host",
+ "if-match",
+ "if-modified-since",
+ "if-none-match",
+ "if-unmodified-since",
+ "last-modified",
+ "link",
+ "location",
+ "max-forwards",
+ "origin",
+ "proxy-authenticate",
+ "proxy-authorization",
+ "range",
+ "referer",
+ "refresh",
+ "retry-after",
+ "server",
+ "set-cookie",
+ "strict-transport-security",
+ "trailer",
+ "transfer-encoding",
+ "user-agent",
+ "vary",
+ "via",
+ "www-authenticate",
+ "x-forwarded-for",
+ "x-forwarded-proto",
+ }
+ commonLowerHeader = make(map[string]string, len(common))
+ commonCanonHeader = make(map[string]string, len(common))
+ for _, v := range common {
+ chk := textproto.CanonicalMIMEHeaderKey(v)
+ commonLowerHeader[chk] = v
+ commonCanonHeader[v] = chk
+ }
+}
+
+// LowerHeader returns the lowercase form of a header name,
+// used on the wire for HTTP/2 and HTTP/3 requests.
+func LowerHeader(v string) (lower string, ascii bool) {
+ buildCommonHeaderMapsOnce()
+ if s, ok := commonLowerHeader[v]; ok {
+ return s, true
+ }
+ return asciiToLower(v)
+}
+
+// CanonicalHeader canonicalizes a header name. (For example, "host" becomes "Host".)
+func CanonicalHeader(v string) string {
+ buildCommonHeaderMapsOnce()
+ if s, ok := commonCanonHeader[v]; ok {
+ return s
+ }
+ return textproto.CanonicalMIMEHeaderKey(v)
+}
+
+// CachedCanonicalHeader returns the canonical form of a well-known header name.
+func CachedCanonicalHeader(v string) (string, bool) {
+ buildCommonHeaderMapsOnce()
+ s, ok := commonCanonHeader[v]
+ return s, ok
+}
diff --git a/src/vendor/golang.org/x/net/internal/httpcommon/request.go b/src/vendor/golang.org/x/net/internal/httpcommon/request.go
new file mode 100644
index 0000000000..1e10f89ebf
--- /dev/null
+++ b/src/vendor/golang.org/x/net/internal/httpcommon/request.go
@@ -0,0 +1,467 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package httpcommon
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "net/http/httptrace"
+ "net/textproto"
+ "net/url"
+ "sort"
+ "strconv"
+ "strings"
+
+ "golang.org/x/net/http/httpguts"
+ "golang.org/x/net/http2/hpack"
+)
+
+var (
+ ErrRequestHeaderListSize = errors.New("request header list larger than peer's advertised limit")
+)
+
+// Request is a subset of http.Request.
+// It'd be simpler to pass an *http.Request, of course, but we can't depend on net/http
+// without creating a dependency cycle.
+type Request struct {
+ URL *url.URL
+ Method string
+ Host string
+ Header map[string][]string
+ Trailer map[string][]string
+ ActualContentLength int64 // 0 means 0, -1 means unknown
+}
+
+// EncodeHeadersParam is parameters to EncodeHeaders.
+type EncodeHeadersParam struct {
+ Request Request
+
+ // AddGzipHeader indicates that an "accept-encoding: gzip" header should be
+ // added to the request.
+ AddGzipHeader bool
+
+ // PeerMaxHeaderListSize, when non-zero, is the peer's MAX_HEADER_LIST_SIZE setting.
+ PeerMaxHeaderListSize uint64
+
+ // DefaultUserAgent is the User-Agent header to send when the request
+ // neither contains a User-Agent nor disables it.
+ DefaultUserAgent string
+}
+
+// EncodeHeadersResult is the result of EncodeHeaders.
+type EncodeHeadersResult struct {
+ HasBody bool
+ HasTrailers bool
+}
+
+// EncodeHeaders constructs request headers common to HTTP/2 and HTTP/3.
+// It validates a request and calls headerf with each pseudo-header and header
+// for the request.
+// The headerf function is called with the validated, canonicalized header name.
+func EncodeHeaders(ctx context.Context, param EncodeHeadersParam, headerf func(name, value string)) (res EncodeHeadersResult, _ error) {
+ req := param.Request
+
+ // Check for invalid connection-level headers.
+ if err := checkConnHeaders(req.Header); err != nil {
+ return res, err
+ }
+
+ if req.URL == nil {
+ return res, errors.New("Request.URL is nil")
+ }
+
+ host := req.Host
+ if host == "" {
+ host = req.URL.Host
+ }
+ host, err := httpguts.PunycodeHostPort(host)
+ if err != nil {
+ return res, err
+ }
+ if !httpguts.ValidHostHeader(host) {
+ return res, errors.New("invalid Host header")
+ }
+
+ // isNormalConnect is true if this is a non-extended CONNECT request.
+ isNormalConnect := false
+ var protocol string
+ if vv := req.Header[":protocol"]; len(vv) > 0 {
+ protocol = vv[0]
+ }
+ if req.Method == "CONNECT" && protocol == "" {
+ isNormalConnect = true
+ } else if protocol != "" && req.Method != "CONNECT" {
+ return res, errors.New("invalid :protocol header in non-CONNECT request")
+ }
+
+ // Validate the path, except for non-extended CONNECT requests which have no path.
+ var path string
+ if !isNormalConnect {
+ path = req.URL.RequestURI()
+ if !validPseudoPath(path) {
+ orig := path
+ path = strings.TrimPrefix(path, req.URL.Scheme+"://"+host)
+ if !validPseudoPath(path) {
+ if req.URL.Opaque != "" {
+ return res, fmt.Errorf("invalid request :path %q from URL.Opaque = %q", orig, req.URL.Opaque)
+ } else {
+ return res, fmt.Errorf("invalid request :path %q", orig)
+ }
+ }
+ }
+ }
+
+ // Check for any invalid headers+trailers and return an error before we
+ // potentially pollute our hpack state. (We want to be able to
+ // continue to reuse the hpack encoder for future requests)
+ if err := validateHeaders(req.Header); err != "" {
+ return res, fmt.Errorf("invalid HTTP header %s", err)
+ }
+ if err := validateHeaders(req.Trailer); err != "" {
+ return res, fmt.Errorf("invalid HTTP trailer %s", err)
+ }
+
+ trailers, err := commaSeparatedTrailers(req.Trailer)
+ if err != nil {
+ return res, err
+ }
+
+ enumerateHeaders := func(f func(name, value string)) {
+ // 8.1.2.3 Request Pseudo-Header Fields
+ // The :path pseudo-header field includes the path and query parts of the
+ // target URI (the path-absolute production and optionally a '?' character
+ // followed by the query production, see Sections 3.3 and 3.4 of
+ // [RFC3986]).
+ f(":authority", host)
+ m := req.Method
+ if m == "" {
+ m = "GET"
+ }
+ f(":method", m)
+ if !isNormalConnect {
+ f(":path", path)
+ f(":scheme", req.URL.Scheme)
+ }
+ if protocol != "" {
+ f(":protocol", protocol)
+ }
+ if trailers != "" {
+ f("trailer", trailers)
+ }
+
+ var didUA bool
+ for k, vv := range req.Header {
+ if asciiEqualFold(k, "host") || asciiEqualFold(k, "content-length") {
+ // Host is :authority, already sent.
+ // Content-Length is automatic, set below.
+ continue
+ } else if asciiEqualFold(k, "connection") ||
+ asciiEqualFold(k, "proxy-connection") ||
+ asciiEqualFold(k, "transfer-encoding") ||
+ asciiEqualFold(k, "upgrade") ||
+ asciiEqualFold(k, "keep-alive") {
+ // Per 8.1.2.2 Connection-Specific Header
+ // Fields, don't send connection-specific
+ // fields. We have already checked if any
+ // are error-worthy so just ignore the rest.
+ continue
+ } else if asciiEqualFold(k, "user-agent") {
+ // Match Go's http1 behavior: at most one
+ // User-Agent. If set to nil or empty string,
+ // then omit it. Otherwise if not mentioned,
+ // include the default (below).
+ didUA = true
+ if len(vv) < 1 {
+ continue
+ }
+ vv = vv[:1]
+ if vv[0] == "" {
+ continue
+ }
+ } else if asciiEqualFold(k, "cookie") {
+ // Per 8.1.2.5 To allow for better compression efficiency, the
+ // Cookie header field MAY be split into separate header fields,
+ // each with one or more cookie-pairs.
+ for _, v := range vv {
+ for {
+ p := strings.IndexByte(v, ';')
+ if p < 0 {
+ break
+ }
+ f("cookie", v[:p])
+ p++
+ // strip space after semicolon if any.
+ for p+1 <= len(v) && v[p] == ' ' {
+ p++
+ }
+ v = v[p:]
+ }
+ if len(v) > 0 {
+ f("cookie", v)
+ }
+ }
+ continue
+ } else if k == ":protocol" {
+ // :protocol pseudo-header was already sent above.
+ continue
+ }
+
+ for _, v := range vv {
+ f(k, v)
+ }
+ }
+ if shouldSendReqContentLength(req.Method, req.ActualContentLength) {
+ f("content-length", strconv.FormatInt(req.ActualContentLength, 10))
+ }
+ if param.AddGzipHeader {
+ f("accept-encoding", "gzip")
+ }
+ if !didUA {
+ f("user-agent", param.DefaultUserAgent)
+ }
+ }
+
+ // Do a first pass over the headers counting bytes to ensure
+ // we don't exceed cc.peerMaxHeaderListSize. This is done as a
+ // separate pass before encoding the headers to prevent
+ // modifying the hpack state.
+ if param.PeerMaxHeaderListSize > 0 {
+ hlSize := uint64(0)
+ enumerateHeaders(func(name, value string) {
+ hf := hpack.HeaderField{Name: name, Value: value}
+ hlSize += uint64(hf.Size())
+ })
+
+ if hlSize > param.PeerMaxHeaderListSize {
+ return res, ErrRequestHeaderListSize
+ }
+ }
+
+ trace := httptrace.ContextClientTrace(ctx)
+
+ // Header list size is ok. Write the headers.
+ enumerateHeaders(func(name, value string) {
+ name, ascii := LowerHeader(name)
+ if !ascii {
+ // Skip writing invalid headers. Per RFC 7540, Section 8.1.2, header
+ // field names have to be ASCII characters (just as in HTTP/1.x).
+ return
+ }
+
+ headerf(name, value)
+
+ if trace != nil && trace.WroteHeaderField != nil {
+ trace.WroteHeaderField(name, []string{value})
+ }
+ })
+
+ res.HasBody = req.ActualContentLength != 0
+ res.HasTrailers = trailers != ""
+ return res, nil
+}
+
+// IsRequestGzip reports whether we should add an Accept-Encoding: gzip header
+// for a request.
+func IsRequestGzip(method string, header map[string][]string, disableCompression bool) bool {
+ // TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere?
+ if !disableCompression &&
+ len(header["Accept-Encoding"]) == 0 &&
+ len(header["Range"]) == 0 &&
+ method != "HEAD" {
+ // Request gzip only, not deflate. Deflate is ambiguous and
+ // not as universally supported anyway.
+ // See: https://zlib.net/zlib_faq.html#faq39
+ //
+ // Note that we don't request this for HEAD requests,
+ // due to a bug in nginx:
+ // http://trac.nginx.org/nginx/ticket/358
+ // https://golang.org/issue/5522
+ //
+ // We don't request gzip if the request is for a range, since
+ // auto-decoding a portion of a gzipped document will just fail
+ // anyway. See https://golang.org/issue/8923
+ return true
+ }
+ return false
+}
+
+// checkConnHeaders checks whether req has any invalid connection-level headers.
+//
+// https://www.rfc-editor.org/rfc/rfc9114.html#section-4.2-3
+// https://www.rfc-editor.org/rfc/rfc9113.html#section-8.2.2-1
+//
+// Certain headers are special-cased as okay but not transmitted later.
+// For example, we allow "Transfer-Encoding: chunked", but drop the header when encoding.
+func checkConnHeaders(h map[string][]string) error {
+ if vv := h["Upgrade"]; len(vv) > 0 && (vv[0] != "" && vv[0] != "chunked") {
+ return fmt.Errorf("invalid Upgrade request header: %q", vv)
+ }
+ if vv := h["Transfer-Encoding"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && vv[0] != "chunked") {
+ return fmt.Errorf("invalid Transfer-Encoding request header: %q", vv)
+ }
+ if vv := h["Connection"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && !asciiEqualFold(vv[0], "close") && !asciiEqualFold(vv[0], "keep-alive")) {
+ return fmt.Errorf("invalid Connection request header: %q", vv)
+ }
+ return nil
+}
+
+func commaSeparatedTrailers(trailer map[string][]string) (string, error) {
+ keys := make([]string, 0, len(trailer))
+ for k := range trailer {
+ k = CanonicalHeader(k)
+ switch k {
+ case "Transfer-Encoding", "Trailer", "Content-Length":
+ return "", fmt.Errorf("invalid Trailer key %q", k)
+ }
+ keys = append(keys, k)
+ }
+ if len(keys) > 0 {
+ sort.Strings(keys)
+ return strings.Join(keys, ","), nil
+ }
+ return "", nil
+}
+
+// validPseudoPath reports whether v is a valid :path pseudo-header
+// value. It must be either:
+//
+// - a non-empty string starting with '/'
+// - the string '*', for OPTIONS requests.
+//
+// For now this is only used a quick check for deciding when to clean
+// up Opaque URLs before sending requests from the Transport.
+// See golang.org/issue/16847
+//
+// We used to enforce that the path also didn't start with "//", but
+// Google's GFE accepts such paths and Chrome sends them, so ignore
+// that part of the spec. See golang.org/issue/19103.
+func validPseudoPath(v string) bool {
+ return (len(v) > 0 && v[0] == '/') || v == "*"
+}
+
+func validateHeaders(hdrs map[string][]string) string {
+ for k, vv := range hdrs {
+ if !httpguts.ValidHeaderFieldName(k) && k != ":protocol" {
+ return fmt.Sprintf("name %q", k)
+ }
+ for _, v := range vv {
+ if !httpguts.ValidHeaderFieldValue(v) {
+ // Don't include the value in the error,
+ // because it may be sensitive.
+ return fmt.Sprintf("value for header %q", k)
+ }
+ }
+ }
+ return ""
+}
+
+// shouldSendReqContentLength reports whether we should send
+// a "content-length" request header. This logic is basically a copy of the net/http
+// transferWriter.shouldSendContentLength.
+// The contentLength is the corrected contentLength (so 0 means actually 0, not unknown).
+// -1 means unknown.
+func shouldSendReqContentLength(method string, contentLength int64) bool {
+ if contentLength > 0 {
+ return true
+ }
+ if contentLength < 0 {
+ return false
+ }
+ // For zero bodies, whether we send a content-length depends on the method.
+ // It also kinda doesn't matter for http2 either way, with END_STREAM.
+ switch method {
+ case "POST", "PUT", "PATCH":
+ return true
+ default:
+ return false
+ }
+}
+
+// ServerRequestParam is parameters to NewServerRequest.
+type ServerRequestParam struct {
+ Method string
+ Scheme, Authority, Path string
+ Protocol string
+ Header map[string][]string
+}
+
+// ServerRequestResult is the result of NewServerRequest.
+type ServerRequestResult struct {
+ // Various http.Request fields.
+ URL *url.URL
+ RequestURI string
+ Trailer map[string][]string
+
+ NeedsContinue bool // client provided an "Expect: 100-continue" header
+
+ // If the request should be rejected, this is a short string suitable for passing
+ // to the http2 package's CountError function.
+ // It might be a bit odd to return errors this way rather than returning an error,
+ // but this ensures we don't forget to include a CountError reason.
+ InvalidReason string
+}
+
+func NewServerRequest(rp ServerRequestParam) ServerRequestResult {
+ needsContinue := httpguts.HeaderValuesContainsToken(rp.Header["Expect"], "100-continue")
+ if needsContinue {
+ delete(rp.Header, "Expect")
+ }
+ // Merge Cookie headers into one "; "-delimited value.
+ if cookies := rp.Header["Cookie"]; len(cookies) > 1 {
+ rp.Header["Cookie"] = []string{strings.Join(cookies, "; ")}
+ }
+
+ // Setup Trailers
+ var trailer map[string][]string
+ for _, v := range rp.Header["Trailer"] {
+ for _, key := range strings.Split(v, ",") {
+ key = textproto.CanonicalMIMEHeaderKey(textproto.TrimString(key))
+ switch key {
+ case "Transfer-Encoding", "Trailer", "Content-Length":
+ // Bogus. (copy of http1 rules)
+ // Ignore.
+ default:
+ if trailer == nil {
+ trailer = make(map[string][]string)
+ }
+ trailer[key] = nil
+ }
+ }
+ }
+ delete(rp.Header, "Trailer")
+
+ // "':authority' MUST NOT include the deprecated userinfo subcomponent
+ // for "http" or "https" schemed URIs."
+ // https://www.rfc-editor.org/rfc/rfc9113.html#section-8.3.1-2.3.8
+ if strings.IndexByte(rp.Authority, '@') != -1 && (rp.Scheme == "http" || rp.Scheme == "https") {
+ return ServerRequestResult{
+ InvalidReason: "userinfo_in_authority",
+ }
+ }
+
+ var url_ *url.URL
+ var requestURI string
+ if rp.Method == "CONNECT" && rp.Protocol == "" {
+ url_ = &url.URL{Host: rp.Authority}
+ requestURI = rp.Authority // mimic HTTP/1 server behavior
+ } else {
+ var err error
+ url_, err = url.ParseRequestURI(rp.Path)
+ if err != nil {
+ return ServerRequestResult{
+ InvalidReason: "bad_path",
+ }
+ }
+ requestURI = rp.Path
+ }
+
+ return ServerRequestResult{
+ URL: url_,
+ NeedsContinue: needsContinue,
+ RequestURI: requestURI,
+ Trailer: trailer,
+ }
+}
diff --git a/src/vendor/golang.org/x/net/internal/quic/quicwire/wire.go b/src/vendor/golang.org/x/net/internal/quic/quicwire/wire.go
new file mode 100644
index 0000000000..1a06a22519
--- /dev/null
+++ b/src/vendor/golang.org/x/net/internal/quic/quicwire/wire.go
@@ -0,0 +1,150 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quicwire encodes and decode QUIC/HTTP3 wire encoding types,
+// particularly variable-length integers.
+package quicwire
+
+import "encoding/binary"
+
+const (
+ MaxVarintSize = 8 // encoded size in bytes
+ MaxVarint = (1 << 62) - 1
+)
+
+// ConsumeVarint parses a variable-length integer, reporting its length.
+// It returns a negative length upon an error.
+//
+// https://www.rfc-editor.org/rfc/rfc9000.html#section-16
+func ConsumeVarint(b []byte) (v uint64, n int) {
+ if len(b) < 1 {
+ return 0, -1
+ }
+ b0 := b[0] & 0x3f
+ switch b[0] >> 6 {
+ case 0:
+ return uint64(b0), 1
+ case 1:
+ if len(b) < 2 {
+ return 0, -1
+ }
+ return uint64(b0)<<8 | uint64(b[1]), 2
+ case 2:
+ if len(b) < 4 {
+ return 0, -1
+ }
+ return uint64(b0)<<24 | uint64(b[1])<<16 | uint64(b[2])<<8 | uint64(b[3]), 4
+ case 3:
+ if len(b) < 8 {
+ return 0, -1
+ }
+ return uint64(b0)<<56 | uint64(b[1])<<48 | uint64(b[2])<<40 | uint64(b[3])<<32 | uint64(b[4])<<24 | uint64(b[5])<<16 | uint64(b[6])<<8 | uint64(b[7]), 8
+ }
+ return 0, -1
+}
+
+// ConsumeVarintInt64 parses a variable-length integer as an int64.
+func ConsumeVarintInt64(b []byte) (v int64, n int) {
+ u, n := ConsumeVarint(b)
+ // QUIC varints are 62-bits large, so this conversion can never overflow.
+ return int64(u), n
+}
+
+// AppendVarint appends a variable-length integer to b.
+//
+// https://www.rfc-editor.org/rfc/rfc9000.html#section-16
+func AppendVarint(b []byte, v uint64) []byte {
+ switch {
+ case v <= 63:
+ return append(b, byte(v))
+ case v <= 16383:
+ return append(b, (1<<6)|byte(v>>8), byte(v))
+ case v <= 1073741823:
+ return append(b, (2<<6)|byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
+ case v <= 4611686018427387903:
+ return append(b, (3<<6)|byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
+ default:
+ panic("varint too large")
+ }
+}
+
+// SizeVarint returns the size of the variable-length integer encoding of f.
+func SizeVarint(v uint64) int {
+ switch {
+ case v <= 63:
+ return 1
+ case v <= 16383:
+ return 2
+ case v <= 1073741823:
+ return 4
+ case v <= 4611686018427387903:
+ return 8
+ default:
+ panic("varint too large")
+ }
+}
+
+// ConsumeUint32 parses a 32-bit fixed-length, big-endian integer, reporting its length.
+// It returns a negative length upon an error.
+func ConsumeUint32(b []byte) (uint32, int) {
+ if len(b) < 4 {
+ return 0, -1
+ }
+ return binary.BigEndian.Uint32(b), 4
+}
+
+// ConsumeUint64 parses a 64-bit fixed-length, big-endian integer, reporting its length.
+// It returns a negative length upon an error.
+func ConsumeUint64(b []byte) (uint64, int) {
+ if len(b) < 8 {
+ return 0, -1
+ }
+ return binary.BigEndian.Uint64(b), 8
+}
+
+// ConsumeUint8Bytes parses a sequence of bytes prefixed with an 8-bit length,
+// reporting the total number of bytes consumed.
+// It returns a negative length upon an error.
+func ConsumeUint8Bytes(b []byte) ([]byte, int) {
+ if len(b) < 1 {
+ return nil, -1
+ }
+ size := int(b[0])
+ const n = 1
+ if size > len(b[n:]) {
+ return nil, -1
+ }
+ return b[n:][:size], size + n
+}
+
+// AppendUint8Bytes appends a sequence of bytes prefixed by an 8-bit length.
+func AppendUint8Bytes(b, v []byte) []byte {
+ if len(v) > 0xff {
+ panic("uint8-prefixed bytes too large")
+ }
+ b = append(b, uint8(len(v)))
+ b = append(b, v...)
+ return b
+}
+
+// ConsumeVarintBytes parses a sequence of bytes preceded by a variable-length integer length,
+// reporting the total number of bytes consumed.
+// It returns a negative length upon an error.
+func ConsumeVarintBytes(b []byte) ([]byte, int) {
+ size, n := ConsumeVarint(b)
+ if n < 0 {
+ return nil, -1
+ }
+ if size > uint64(len(b[n:])) {
+ return nil, -1
+ }
+ return b[n:][:size], int(size) + n
+}
+
+// AppendVarintBytes appends a sequence of bytes prefixed by a variable-length integer length.
+func AppendVarintBytes(b, v []byte) []byte {
+ b = AppendVarint(b, uint64(len(v)))
+ b = append(b, v...)
+ return b
+}