// 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 http2

import (
	
	
	
	
	
	mathrand 
	
	
	
	
	

	
)

// ConfigureTransport configures a net/http HTTP/1 Transport to use HTTP/2.
// It returns an error if t1 has already been HTTP/2-enabled.
//
// Use ConfigureTransports instead to configure the HTTP/2 Transport.
func ( *http.Transport) error {
	return configureTransport()
}

// ConfigureTransports configures a net/http HTTP/1 Transport to use HTTP/2.
// It returns a new HTTP/2 Transport for further configuration.
// It returns an error if t1 has already been HTTP/2-enabled.
func ( *http.Transport) (*Transport, error) {
	return configureTransports()
}

// Transport is an HTTP/2 Transport.
//
// A Transport internally caches connections to servers. It is safe
// for concurrent use by multiple goroutines.
type Transport struct {
	// DialTLSContext specifies an optional dial function with context for
	// creating TLS connections for requests.
	//
	// If DialTLSContext and DialTLS is nil, tls.Dial is used.
	//
	// If the returned net.Conn has a ConnectionState method like tls.Conn,
	// it will be used to set http.Response.TLS.
	DialTLSContext func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error)

	// DialTLS specifies an optional dial function for creating
	// TLS connections for requests.
	//
	// If DialTLSContext and DialTLS is nil, tls.Dial is used.
	//
	// Deprecated: Use DialTLSContext instead, which allows the transport
	// to cancel dials as soon as they are no longer needed.
	// If both are set, DialTLSContext takes priority.
	DialTLS func(network, addr string, cfg *tls.Config) (net.Conn, error)

	// TLSClientConfig specifies the TLS configuration to use with
	// tls.Client. If nil, the default configuration is used.
	TLSClientConfig *tls.Config

	// ConnPool optionally specifies an alternate connection pool to use.
	// If nil, the default is used.
	ConnPool ClientConnPool

	// DisableCompression, if true, prevents the Transport from
	// requesting compression with an "Accept-Encoding: gzip"
	// request header when the Request contains no existing
	// Accept-Encoding value. If the Transport requests gzip on
	// its own and gets a gzipped response, it's transparently
	// decoded in the Response.Body. However, if the user
	// explicitly requested gzip it is not automatically
	// uncompressed.
	DisableCompression bool

	// AllowHTTP, if true, permits HTTP/2 requests using the insecure,
	// plain-text "http" scheme. Note that this does not enable h2c support.
	AllowHTTP bool

	// MaxHeaderListSize is the http2 SETTINGS_MAX_HEADER_LIST_SIZE to
	// send in the initial settings frame. It is how many bytes
	// of response headers are allowed. Unlike the http2 spec, zero here
	// means to use a default limit (currently 10MB). If you actually
	// want to advertise an unlimited value to the peer, Transport
	// interprets the highest possible value here (0xffffffff or 1<<32-1)
	// to mean no limit.
	MaxHeaderListSize uint32

	// MaxReadFrameSize is the http2 SETTINGS_MAX_FRAME_SIZE to send in the
	// initial settings frame. It is the size in bytes of the largest frame
	// payload that the sender is willing to receive. If 0, no setting is
	// sent, and the value is provided by the peer, which should be 16384
	// according to the spec:
	// https://datatracker.ietf.org/doc/html/rfc7540#section-6.5.2.
	// Values are bounded in the range 16k to 16M.
	MaxReadFrameSize uint32

	// MaxDecoderHeaderTableSize optionally specifies the http2
	// SETTINGS_HEADER_TABLE_SIZE to send in the initial settings frame. It
	// informs the remote endpoint of the maximum size of the header compression
	// table used to decode header blocks, in octets. If zero, the default value
	// of 4096 is used.
	MaxDecoderHeaderTableSize uint32

	// MaxEncoderHeaderTableSize optionally specifies an upper limit for the
	// header compression table used for encoding request headers. Received
	// SETTINGS_HEADER_TABLE_SIZE settings are capped at this limit. If zero,
	// the default value of 4096 is used.
	MaxEncoderHeaderTableSize uint32

	// StrictMaxConcurrentStreams controls whether the server's
	// SETTINGS_MAX_CONCURRENT_STREAMS should be respected
	// globally. If false, new TCP connections are created to the
	// server as needed to keep each under the per-connection
	// SETTINGS_MAX_CONCURRENT_STREAMS limit. If true, the
	// server's SETTINGS_MAX_CONCURRENT_STREAMS is interpreted as
	// a global limit and callers of RoundTrip block when needed,
	// waiting for their turn.
	StrictMaxConcurrentStreams bool

	// IdleConnTimeout is the maximum amount of time an idle
	// (keep-alive) connection will remain idle before closing
	// itself.
	// Zero means no limit.
	IdleConnTimeout time.Duration

	// ReadIdleTimeout is the timeout after which a health check using ping
	// frame will be carried out if no frame is received on the connection.
	// Note that a ping response will is considered a received frame, so if
	// there is no other traffic on the connection, the health check will
	// be performed every ReadIdleTimeout interval.
	// If zero, no health check is performed.
	ReadIdleTimeout time.Duration

	// PingTimeout is the timeout after which the connection will be closed
	// if a response to Ping is not received.
	// Defaults to 15s.
	PingTimeout time.Duration

	// WriteByteTimeout is the timeout after which the connection will be
	// closed no data can be written to it. The timeout begins when data is
	// available to write, and is extended whenever any bytes are written.
	WriteByteTimeout time.Duration

	// CountError, if non-nil, is called on HTTP/2 transport errors.
	// It's intended to increment a metric for monitoring, such
	// as an expvar or Prometheus metric.
	// The errType consists of only ASCII word characters.
	CountError func(errType string)

	// Internal state, differs between wrapped and non-wrapped implementations.
	transportInternal
}

var (
	errClientConnClosed         = errors.New("http2: client conn is closed")
	errClientConnNotEstablished = errors.New("http2: client conn could not be established")
	errClientConnGotGoAway      = errors.New("http2: Transport received Server's graceful shutdown GOAWAY")
	errClientConnForceClosed    = errors.New("http2: client connection force closed via ClientConn.Close")
	errClientConnUnusable       = errors.New("http2: client conn not usable")
)

// ClientConnPool manages a pool of HTTP/2 client connections.
type ClientConnPool interface {
	// GetClientConn returns a specific HTTP/2 connection (usually
	// a TLS-TCP connection) to an HTTP/2 server. On success, the
	// returned ClientConn accounts for the upcoming RoundTrip
	// call, so the caller should not omit it. If the caller needs
	// to, ClientConn.RoundTrip can be called with a bogus
	// new(http.Request) to release the stream reservation.
	GetClientConn(req *http.Request, addr string) (*ClientConn, error)
	MarkDead(*ClientConn)
}

// ClientConnState describes the state of a ClientConn.
type ClientConnState struct {
	// Closed is whether the connection is closed.
	Closed bool

	// Closing is whether the connection is in the process of
	// closing. It may be closing due to shutdown, being a
	// single-use connection, being marked as DoNotReuse, or
	// having received a GOAWAY frame.
	Closing bool

	// StreamsActive is how many streams are active.
	StreamsActive int

	// StreamsReserved is how many streams have been reserved via
	// ClientConn.ReserveNewRequest.
	StreamsReserved int

	// StreamsPending is how many requests have been sent in excess
	// of the peer's advertised MaxConcurrentStreams setting and
	// are waiting for other streams to complete.
	StreamsPending int

	// MaxConcurrentStreams is how many concurrent streams the
	// peer advertised as acceptable. Zero means no SETTINGS
	// frame has been received yet.
	MaxConcurrentStreams uint32

	// LastIdle, if non-zero, is when the connection last
	// transitioned to idle state.
	LastIdle time.Time
}

// RoundTripOpt are options for the Transport.RoundTripOpt method.
type RoundTripOpt struct {
	// OnlyCachedConn controls whether RoundTripOpt may
	// create a new TCP connection. If set true and
	// no cached connection is available, RoundTripOpt
	// will return ErrNoCachedConn.

	// OnlyCachedConn was broken in https://go.dev/cl/16699.
	OnlyCachedConn bool

	allowHTTP bool // allow http:// URLs
}

func ( *Transport) ( *http.Request) (*http.Response, error) {
	return .RoundTripOpt(, RoundTripOpt{})
}

// RoundTripOpt is like RoundTrip, but takes options.
func ( *Transport) ( *http.Request,  RoundTripOpt) (*http.Response, error) {
	return .roundTripOpt(, )
}

// CloseIdleConnections closes any connections which were previously
// connected from previous requests but are now sitting idle.
// It does not interrupt any connections currently in use.
func ( *Transport) () {
	.closeIdleConnections()
}

func ( *Transport) ( net.Conn) (*ClientConn, error) {
	return .newUserClientConn()
}

// authorityAddr returns a given authority (a host/IP, or host:port / ip:port)
// and returns a host:port. The port 443 is added if needed.
func ( string,  string) ( string) {
	, ,  := net.SplitHostPort()
	if  != nil { // authority didn't have a port
		 = 
		 = ""
	}
	if  == "" { // authority's port was empty
		 = "443"
		if  == "http" {
			 = "80"
		}
	}
	if ,  := idna.ToASCII();  == nil {
		 = 
	}
	// IPv6 address literal, without a port:
	if strings.HasPrefix(, "[") && strings.HasSuffix(, "]") {
		return  + ":" + 
	}
	return net.JoinHostPort(, )
}

func ( *Transport) ( *http.Request,  RoundTripOpt,  ClientConnPool) (*http.Response, error) {
	 := authorityAddr(.URL.Scheme, .URL.Host)
	for  := 0; ; ++ {
		,  := .GetClientConn(, )
		if  != nil {
			.vlogf("http2: Transport failed to get client conn for %s: %v", , )
			return nil, 
		}
		 := !atomic.CompareAndSwapUint32(&.atomicReused, 0, 1)
		traceGotConn(, , )
		,  := .RoundTrip()
		if  != nil &&  <= 6 {
			 := 
			if ,  = shouldRetryRequest(, );  == nil {
				// After the first retry, do exponential backoff with 10% jitter.
				if  == 0 {
					.vlogf("RoundTrip retrying after failure: %v", )
					continue
				}
				 := float64(uint(1) << (uint() - 1))
				 +=  * (0.1 * mathrand.Float64())
				 := time.Second * time.Duration()
				 := time.NewTimer()
				select {
				case <-.C:
					.vlogf("RoundTrip retrying after failure: %v", )
					continue
				case <-.Context().Done():
					.Stop()
					 = .Context().Err()
				}
			}
		}
		if  == errClientConnNotEstablished {
			// This ClientConn was created recently,
			// this is the first request to use it,
			// and the connection is closed and not usable.
			//
			// In this state, cc.idleTimer will remove the conn from the pool
			// when it fires. Stop the timer and remove it here so future requests
			// won't try to use this connection.
			//
			// If the timer has already fired and we're racing it, the redundant
			// call to MarkDead is harmless.
			.stopIdleTimer()
			.MarkDead()
		}
		if  != nil {
			.vlogf("RoundTrip failure: %v", )
			return nil, 
		}
		return , nil
	}
}

// shouldRetryRequest is called by RoundTrip when a request fails to get
// response headers. It is always called with a non-nil error.
// It returns either a request to retry (either the same request, or a
// modified clone), or an error if the request can't be replayed.
func ( *http.Request,  error) (*http.Request, error) {
	if !canRetryError() {
		return nil, 
	}
	// If the Body is nil (or http.NoBody), it's safe to reuse
	// this request and its Body.
	if .Body == nil || .Body == http.NoBody {
		return , nil
	}

	// If the request body can be reset back to its original
	// state via the optional req.GetBody, do that.
	if .GetBody != nil {
		,  := .GetBody()
		if  != nil {
			return nil, 
		}
		 := *
		.Body = 
		return &, nil
	}

	// The Request.Body can't reset back to the beginning, but we
	// don't seem to have started to read from it yet, so reuse
	// the request directly.
	if  == errClientConnUnusable {
		return , nil
	}

	return nil, fmt.Errorf("http2: Transport: cannot retry err [%v] after Request.Body was written; define Request.GetBody to avoid this error", )
}

func ( error) bool {
	if  == errClientConnUnusable ||  == errClientConnGotGoAway {
		return true
	}
	if ,  := .(StreamError);  {
		return .Code == ErrCodeRefusedStream
	}
	return false
}

func ( *Transport) ( string,  ...interface{}) {
	if VerboseLogs {
		.logf(, ...)
	}
}

func ( *Transport) ( string,  ...interface{}) {
	log.Printf(, ...)
}

func ( *Transport) ( context.Context, ,  string,  *tls.Config) (net.Conn, error) {
	if .DialTLSContext != nil {
		return .DialTLSContext(, , , )
	} else if .DialTLS != nil {
		return .DialTLS(, , )
	}

	,  := .dialTLSWithContext(, , , )
	if  != nil {
		return nil, 
	}
	 := .ConnectionState()
	if  := .NegotiatedProtocol;  != NextProtoTLS {
		return nil, fmt.Errorf("http2: unexpected ALPN protocol %q; want %q", , NextProtoTLS)
	}
	if !.NegotiatedProtocolIsMutual {
		return nil, errors.New("http2: could not negotiate protocol mutually")
	}
	return , nil
}

// dialTLSWithContext uses tls.Dialer, added in Go 1.15, to open a TLS
// connection.
func ( *Transport) ( context.Context, ,  string,  *tls.Config) (*tls.Conn, error) {
	 := &tls.Dialer{
		Config: ,
	}
	,  := .DialContext(, , )
	if  != nil {
		return nil, 
	}
	 := .(*tls.Conn) // DialContext comment promises this will always succeed
	return , nil
}

// GoAwayError is returned by the Transport when the server closes the
// TCP connection after sending a GOAWAY frame.
type GoAwayError struct {
	LastStreamID uint32
	ErrCode      ErrCode
	DebugData    string
}

func ( GoAwayError) () string {
	return fmt.Sprintf("http2: server sent GOAWAY and closed the connection; LastStreamID=%v, ErrCode=%v, debug=%q",
		.LastStreamID, .ErrCode, .DebugData)
}

// noCachedConnError is the concrete type of ErrNoCachedConn, which
// needs to be detected by net/http regardless of whether it's its
// bundled version (in h2_bundle.go with a rewritten type name) or
// from a user's x/net/http2. As such, as it has a unique method name
// (IsHTTP2NoCachedConnError) that net/http sniffs for via func
// isNoCachedConnError.
type noCachedConnError struct{}

func (noCachedConnError) () {}
func (noCachedConnError) () string             { return "http2: no cached connection was available" }

// isNoCachedConnError reports whether err is of type noCachedConnError
// or its equivalent renamed type in net/http2's h2_bundle.go. Both types
// may coexist in the same running program.
func ( error) bool {
	,  := .(interface{ () })
	return 
}

var ErrNoCachedConn error = noCachedConnError{}