// Copyright 2011 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.// HTTP client implementation. See RFC 7230 through 7235.//// This is the low-level Transport implementation of RoundTripper.// The high-level interface is in client.go.package httpimport (_)// DefaultTransport is the default implementation of [Transport] and is// used by [DefaultClient]. It establishes network connections as needed// and caches them for reuse by subsequent calls. It uses HTTP proxies// as directed by the environment variables HTTP_PROXY, HTTPS_PROXY// and NO_PROXY (or the lowercase versions thereof).varDefaultTransportRoundTripper = &Transport{Proxy: ProxyFromEnvironment,DialContext: defaultTransportDialContext(&net.Dialer{Timeout: 30 * time.Second,KeepAlive: 30 * time.Second, }),ForceAttemptHTTP2: true,MaxIdleConns: 100,IdleConnTimeout: 90 * time.Second,TLSHandshakeTimeout: 10 * time.Second,ExpectContinueTimeout: 1 * time.Second,}// DefaultMaxIdleConnsPerHost is the default value of [Transport]'s// MaxIdleConnsPerHost.constDefaultMaxIdleConnsPerHost = 2// Transport is an implementation of [RoundTripper] that supports HTTP,// HTTPS, and HTTP proxies (for either HTTP or HTTPS with CONNECT).//// By default, Transport caches connections for future re-use.// This may leave many open connections when accessing many hosts.// This behavior can be managed using [Transport.CloseIdleConnections] method// and the [Transport.MaxIdleConnsPerHost] and [Transport.DisableKeepAlives] fields.//// Transports should be reused instead of created as needed.// Transports are safe for concurrent use by multiple goroutines.//// A Transport is a low-level primitive for making HTTP and HTTPS requests.// For high-level functionality, such as cookies and redirects, see [Client].//// Transport uses HTTP/1.1 for HTTP URLs and either HTTP/1.1 or HTTP/2// for HTTPS URLs, depending on whether the server supports HTTP/2,// and how the Transport is configured. The [DefaultTransport] supports HTTP/2.// To explicitly enable HTTP/2 on a transport, set [Transport.Protocols].//// Responses with status codes in the 1xx range are either handled// automatically (100 expect-continue) or ignored. The one// exception is HTTP status code 101 (Switching Protocols), which is// considered a terminal status and returned by [Transport.RoundTrip]. To see the// ignored 1xx responses, use the httptrace trace package's// ClientTrace.Got1xxResponse.//// Transport only retries a request upon encountering a network error// if the connection has been already been used successfully and if the// request is idempotent and either has no body or has its [Request.GetBody]// defined. HTTP requests are considered idempotent if they have HTTP methods// GET, HEAD, OPTIONS, or TRACE; or if their [Header] map contains an// "Idempotency-Key" or "X-Idempotency-Key" entry. If the idempotency key// value is a zero-length slice, the request is treated as idempotent but the// header is not sent on the wire.typeTransportstruct { idleMu sync.Mutex closeIdle bool// user has requested to close all idle conns idleConn map[connectMethodKey][]*persistConn// most recently used at end idleConnWait map[connectMethodKey]wantConnQueue// waiting getConns idleLRU connLRU reqMu sync.Mutex reqCanceler map[*Request]context.CancelCauseFunc altMu sync.Mutex// guards changing altProto only altProto atomic.Value// of nil or map[string]RoundTripper, key is URI scheme connsPerHostMu sync.Mutex connsPerHost map[connectMethodKey]int connsPerHostWait map[connectMethodKey]wantConnQueue// waiting getConns dialsInProgress wantConnQueue// Proxy specifies a function to return a proxy for a given // Request. If the function returns a non-nil error, the // request is aborted with the provided error. // // The proxy type is determined by the URL scheme. "http", // "https", "socks5", and "socks5h" are supported. If the scheme is empty, // "http" is assumed. // "socks5" is treated the same as "socks5h". // // If the proxy URL contains a userinfo subcomponent, // the proxy request will pass the username and password // in a Proxy-Authorization header. // // If Proxy is nil or returns a nil *URL, no proxy is used. Proxy func(*Request) (*url.URL, error)// OnProxyConnectResponse is called when the Transport gets an HTTP response from // a proxy for a CONNECT request. It's called before the check for a 200 OK response. // If it returns an error, the request fails with that error. OnProxyConnectResponse func(ctx context.Context, proxyURL *url.URL, connectReq *Request, connectRes *Response) error// DialContext specifies the dial function for creating unencrypted TCP connections. // If DialContext is nil (and the deprecated Dial below is also nil), // then the transport dials using package net. // // DialContext runs concurrently with calls to RoundTrip. // A RoundTrip call that initiates a dial may end up using // a connection dialed previously when the earlier connection // becomes idle before the later DialContext completes. DialContext func(ctx context.Context, network, addr string) (net.Conn, error)// Dial specifies the dial function for creating unencrypted TCP connections. // // Dial runs concurrently with calls to RoundTrip. // A RoundTrip call that initiates a dial may end up using // a connection dialed previously when the earlier connection // becomes idle before the later Dial completes. // // Deprecated: Use DialContext instead, which allows the transport // to cancel dials as soon as they are no longer needed. // If both are set, DialContext takes priority. Dial func(network, addr string) (net.Conn, error)// DialTLSContext specifies an optional dial function for creating // TLS connections for non-proxied HTTPS requests. // // If DialTLSContext is nil (and the deprecated DialTLS below is also nil), // DialContext and TLSClientConfig are used. // // If DialTLSContext is set, the Dial and DialContext hooks are not used for HTTPS // requests and the TLSClientConfig and TLSHandshakeTimeout // are ignored. The returned net.Conn is assumed to already be // past the TLS handshake. DialTLSContext func(ctx context.Context, network, addr string) (net.Conn, error)// DialTLS specifies an optional dial function for creating // TLS connections for non-proxied HTTPS requests. // // 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) (net.Conn, error)// TLSClientConfig specifies the TLS configuration to use with // tls.Client. // If nil, the default configuration is used. // If non-nil, HTTP/2 support may not be enabled by default. TLSClientConfig *tls.Config// TLSHandshakeTimeout specifies the maximum amount of time to // wait for a TLS handshake. Zero means no timeout. TLSHandshakeTimeout time.Duration// DisableKeepAlives, if true, disables HTTP keep-alives and // will only use the connection to the server for a single // HTTP request. // // This is unrelated to the similarly named TCP keep-alives. DisableKeepAlives bool// 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// MaxIdleConns controls the maximum number of idle (keep-alive) // connections across all hosts. Zero means no limit. MaxIdleConns int// MaxIdleConnsPerHost, if non-zero, controls the maximum idle // (keep-alive) connections to keep per-host. If zero, // DefaultMaxIdleConnsPerHost is used. MaxIdleConnsPerHost int// MaxConnsPerHost optionally limits the total number of // connections per host, including connections in the dialing, // active, and idle states. On limit violation, dials will block. // // Zero means no limit. MaxConnsPerHost int// 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// ResponseHeaderTimeout, if non-zero, specifies the amount of // time to wait for a server's response headers after fully // writing the request (including its body, if any). This // time does not include the time to read the response body. ResponseHeaderTimeout time.Duration// ExpectContinueTimeout, if non-zero, specifies the amount of // time to wait for a server's first response headers after fully // writing the request headers if the request has an // "Expect: 100-continue" header. Zero means no timeout and // causes the body to be sent immediately, without // waiting for the server to approve. // This time does not include the time to send the request header. ExpectContinueTimeout time.Duration// TLSNextProto specifies how the Transport switches to an // alternate protocol (such as HTTP/2) after a TLS ALPN // protocol negotiation. If Transport dials a TLS connection // with a non-empty protocol name and TLSNextProto contains a // map entry for that key (such as "h2"), then the func is // called with the request's authority (such as "example.com" // or "example.com:1234") and the TLS connection. The function // must return a RoundTripper that then handles the request. // If TLSNextProto is not nil, HTTP/2 support is not enabled // automatically. TLSNextProto map[string]func(authority string, c *tls.Conn) RoundTripper// ProxyConnectHeader optionally specifies headers to send to // proxies during CONNECT requests. // To set the header dynamically, see GetProxyConnectHeader. ProxyConnectHeader Header// GetProxyConnectHeader optionally specifies a func to return // headers to send to proxyURL during a CONNECT request to the // ip:port target. // If it returns an error, the Transport's RoundTrip fails with // that error. It can return (nil, nil) to not add headers. // If GetProxyConnectHeader is non-nil, ProxyConnectHeader is // ignored. GetProxyConnectHeader func(ctx context.Context, proxyURL *url.URL, target string) (Header, error)// MaxResponseHeaderBytes specifies a limit on how many // response bytes are allowed in the server's response // header. // // Zero means to use a default limit. MaxResponseHeaderBytes int64// WriteBufferSize specifies the size of the write buffer used // when writing to the transport. // If zero, a default (currently 4KB) is used. WriteBufferSize int// ReadBufferSize specifies the size of the read buffer used // when reading from the transport. // If zero, a default (currently 4KB) is used. ReadBufferSize int// nextProtoOnce guards initialization of TLSNextProto and // h2transport (via onceSetNextProtoDefaults) nextProtoOnce sync.Once h2transport h2Transport// non-nil if http2 wired up tlsNextProtoWasNil bool// whether TLSNextProto was nil when the Once fired// ForceAttemptHTTP2 controls whether HTTP/2 is enabled when a non-zero // Dial, DialTLS, or DialContext func or TLSClientConfig is provided. // By default, use of any those fields conservatively disables HTTP/2. // To use a custom dialer or TLS config and still attempt HTTP/2 // upgrades, set this to true. ForceAttemptHTTP2 bool// HTTP2 configures HTTP/2 connections. // // This field does not yet have any effect. // See https://go.dev/issue/67813. HTTP2 *HTTP2Config// Protocols is the set of protocols supported by the transport. // // If Protocols includes UnencryptedHTTP2 and does not include HTTP1, // the transport will use unencrypted HTTP/2 for requests for http:// URLs. // // If Protocols is nil, the default is usually HTTP/1 only. // If ForceAttemptHTTP2 is true, or if TLSNextProto contains an "h2" entry, // the default is HTTP/1 and HTTP/2. Protocols *Protocols}func ( *Transport) () int {if .WriteBufferSize > 0 {return .WriteBufferSize }return4 << 10}func ( *Transport) () int {if .ReadBufferSize > 0 {return .ReadBufferSize }return4 << 10}// Clone returns a deep copy of t's exported fields.func ( *Transport) () *Transport { .nextProtoOnce.Do(.onceSetNextProtoDefaults) := &Transport{Proxy: .Proxy,OnProxyConnectResponse: .OnProxyConnectResponse,DialContext: .DialContext,Dial: .Dial,DialTLS: .DialTLS,DialTLSContext: .DialTLSContext,TLSHandshakeTimeout: .TLSHandshakeTimeout,DisableKeepAlives: .DisableKeepAlives,DisableCompression: .DisableCompression,MaxIdleConns: .MaxIdleConns,MaxIdleConnsPerHost: .MaxIdleConnsPerHost,MaxConnsPerHost: .MaxConnsPerHost,IdleConnTimeout: .IdleConnTimeout,ResponseHeaderTimeout: .ResponseHeaderTimeout,ExpectContinueTimeout: .ExpectContinueTimeout,ProxyConnectHeader: .ProxyConnectHeader.Clone(),GetProxyConnectHeader: .GetProxyConnectHeader,MaxResponseHeaderBytes: .MaxResponseHeaderBytes,ForceAttemptHTTP2: .ForceAttemptHTTP2,WriteBufferSize: .WriteBufferSize,ReadBufferSize: .ReadBufferSize, }if .TLSClientConfig != nil { .TLSClientConfig = .TLSClientConfig.Clone() }if .HTTP2 != nil { .HTTP2 = &HTTP2Config{} *.HTTP2 = *.HTTP2 }if .Protocols != nil { .Protocols = &Protocols{} *.Protocols = *.Protocols }if !.tlsNextProtoWasNil { := maps.Clone(.TLSNextProto)if == nil { = make(map[string]func( string, *tls.Conn) RoundTripper) } .TLSNextProto = }return}// h2Transport is the interface we expect to be able to call from// net/http against an *http2.Transport that's either bundled into// h2_bundle.go or supplied by the user via x/net/http2.//// We name it with the "h2" prefix to stay out of the "http2" prefix// namespace used by x/tools/cmd/bundle for h2_bundle.go.type h2Transport interface { CloseIdleConnections()}func ( *Transport) () bool {return .DialTLS != nil || .DialTLSContext != nil}var http2client = godebug.New("http2client")// onceSetNextProtoDefaults initializes TLSNextProto.// It must be called via t.nextProtoOnce.Do.func ( *Transport) () { .tlsNextProtoWasNil = (.TLSNextProto == nil)ifhttp2client.Value() == "0" {http2client.IncNonDefault()return }// If they've already configured http2 with // golang.org/x/net/http2 instead of the bundled copy, try to // get at its http2.Transport value (via the "https" // altproto map) so we can call CloseIdleConnections on it if // requested. (Issue 22891) , := .altProto.Load().(map[string]RoundTripper)if := reflect.ValueOf(["https"]); .IsValid() && .Type().Kind() == reflect.Struct && .Type().NumField() == 1 {if := .Field(0); .CanInterface() {if , := .Interface().(h2Transport); { .h2transport = return } } }if , := .TLSNextProto["h2"]; {// There's an existing HTTP/2 implementation installed.return } := .protocols()if !.HTTP2() && !.UnencryptedHTTP2() {return }ifomitBundledHTTP2 {return } , := http2configureTransports()if != nil {log.Printf("Error enabling Transport HTTP/2 support: %v", )return } .h2transport = // Auto-configure the http2.Transport's MaxHeaderListSize from // the http.Transport's MaxResponseHeaderBytes. They don't // exactly mean the same thing, but they're close. // // TODO: also add this to x/net/http2.Configure Transport, behind // a +build go1.7 build tag:if := .MaxResponseHeaderBytes; != 0 && .MaxHeaderListSize == 0 {const = 1<<32 - 1if >= { .MaxHeaderListSize = } else { .MaxHeaderListSize = uint32() } }// Server.ServeTLS clones the tls.Config before modifying it. // Transport doesn't. We may want to make the two consistent some day. // // http2configureTransport will have already set NextProtos, but adjust it again // here to remove HTTP/1.1 if the user has disabled it. .TLSClientConfig.NextProtos = adjustNextProtos(.TLSClientConfig.NextProtos, )}func ( *Transport) () Protocols {if .Protocols != nil {return *.Protocols// user-configured set }varProtocols .SetHTTP1(true) // default always includes HTTP/1switch {case .TLSNextProto != nil:// Setting TLSNextProto to an empty map is a documented way // to disable HTTP/2 on a Transport.if .TLSNextProto["h2"] != nil { .SetHTTP2(true) }case !.ForceAttemptHTTP2 && (.TLSClientConfig != nil || .Dial != nil || .DialContext != nil || .hasCustomTLSDialer()):// Be conservative and don't automatically enable // http2 if they've specified a custom TLS config or // custom dialers. Let them opt-in themselves via // Transport.Protocols.SetHTTP2(true) so we don't surprise them // by modifying their tls.Config. Issue 14275. // However, if ForceAttemptHTTP2 is true, it overrides the above checks.casehttp2client.Value() == "0":default: .SetHTTP2(true) }return}// ProxyFromEnvironment returns the URL of the proxy to use for a// given request, as indicated by the environment variables// HTTP_PROXY, HTTPS_PROXY and NO_PROXY (or the lowercase versions// thereof). Requests use the proxy from the environment variable// matching their scheme, unless excluded by NO_PROXY.//// The environment values may be either a complete URL or a// "host[:port]", in which case the "http" scheme is assumed.// An error is returned if the value is a different form.//// A nil URL and nil error are returned if no proxy is defined in the// environment, or a proxy should not be used for the given request,// as defined by NO_PROXY.//// As a special case, if req.URL.Host is "localhost" (with or without// a port number), then a nil URL and nil error will be returned.func ( *Request) (*url.URL, error) {returnenvProxyFunc()(.URL)}// ProxyURL returns a proxy function (for use in a [Transport])// that always returns the same URL.func ( *url.URL) func(*Request) (*url.URL, error) {returnfunc(*Request) (*url.URL, error) {return , nil }}// transportRequest is a wrapper around a *Request that adds// optional extra headers to write and stores any error to return// from roundTrip.type transportRequest struct { *Request// original request, not to be mutated extra Header// extra headers to write, or nil trace *httptrace.ClientTrace// optional ctx context.Context// canceled when we are done with the request cancel context.CancelCauseFunc mu sync.Mutex// guards err err error// first setError value for mapRoundTripError to consider}func ( *transportRequest) () Header {if .extra == nil { .extra = make(Header) }return .extra}func ( *transportRequest) ( error) { .mu.Lock()if .err == nil { .err = } .mu.Unlock()}// useRegisteredProtocol reports whether an alternate protocol (as registered// with Transport.RegisterProtocol) should be respected for this request.func ( *Transport) ( *Request) bool {if .URL.Scheme == "https" && .requiresHTTP1() {// If this request requires HTTP/1, don't use the // "https" alternate protocol, which is used by the // HTTP/2 code to take over requests if there's an // existing cached HTTP/2 connection.returnfalse }returntrue}// alternateRoundTripper returns the alternate RoundTripper to use// for this request if the Request's URL scheme requires one,// or nil for the normal case of using the Transport.func ( *Transport) ( *Request) RoundTripper {if !.useRegisteredProtocol() {returnnil } , := .altProto.Load().(map[string]RoundTripper)return [.URL.Scheme]}func validateHeaders( Header) string {for , := range {if !httpguts.ValidHeaderFieldName() {returnfmt.Sprintf("field name %q", ) }for , := range {if !httpguts.ValidHeaderFieldValue() {// Don't include the value in the error, // because it may be sensitive.returnfmt.Sprintf("field value for %q", ) } } }return""}// roundTrip implements a RoundTripper over HTTP.func ( *Transport) ( *Request) ( *Response, error) { .nextProtoOnce.Do(.onceSetNextProtoDefaults) := .Context() := httptrace.ContextClientTrace()if .URL == nil { .closeBody()returnnil, errors.New("http: nil Request.URL") }if .Header == nil { .closeBody()returnnil, errors.New("http: nil Request.Header") } := .URL.Scheme := == "http" || == "https"if {// Validate the outgoing headers.if := validateHeaders(.Header); != "" { .closeBody()returnnil, fmt.Errorf("net/http: invalid header %s", ) }// Validate the outgoing trailers too.if := validateHeaders(.Trailer); != "" { .closeBody()returnnil, fmt.Errorf("net/http: invalid trailer %s", ) } } := = setupRewindBody()if := .alternateRoundTripper(); != nil {if , := .RoundTrip(); != ErrSkipAltProtocol {return , }varerror , = rewindBody()if != nil {returnnil, } }if ! { .closeBody()returnnil, badStringError("unsupported protocol scheme", ) }if .Method != "" && !validMethod(.Method) { .closeBody()returnnil, fmt.Errorf("net/http: invalid method %q", .Method) }if .URL.Host == "" { .closeBody()returnnil, errors.New("http: no Host in request URL") }// Transport request context. // // If RoundTrip returns an error, it cancels this context before returning. // // If RoundTrip returns no error: // - For an HTTP/1 request, persistConn.readLoop cancels this context // after reading the request body. // - For an HTTP/2 request, RoundTrip cancels this context after the HTTP/2 // RoundTripper returns. , := context.WithCancelCause(.Context())// Convert Request.Cancel into context cancelation.if .Cancel != nil {goawaitLegacyCancel(, , ) }// Convert Transport.CancelRequest into context cancelation. // // This is lamentably expensive. CancelRequest has been deprecated for a long time // and doesn't work on HTTP/2 requests. Perhaps we should drop support for it entirely. = .prepareTransportCancel(, )deferfunc() {if != nil { () } }()for {select {case<-.Done(): .closeBody()returnnil, context.Cause()default: }// treq gets modified by roundTrip, so we need to recreate for each retry. := &transportRequest{Request: , trace: , ctx: , cancel: } , := .connectMethodForRequest()if != nil { .closeBody()returnnil, }// Get the cached or newly-created connection to either the // host (for http or https), the http proxy, or the http proxy // pre-CONNECTed to https server. In any case, we'll be ready // to send it requests. , := .getConn(, )if != nil { .closeBody()returnnil, }var *Responseif .alt != nil {// HTTP/2 path. , = .alt.RoundTrip() } else { , = .roundTrip() }if == nil {if .alt != nil {// HTTP/2 requests are not cancelable with CancelRequest, // so we have no further need for the request context. // // On the HTTP/1 path, roundTrip takes responsibility for // canceling the context after the response body is read. (errRequestDone) } .Request = return , nil }// Failed. Clean up and determine whether to retry.ifhttp2isNoCachedConnError() {if .removeIdleConn() { .decConnsPerHost(.cacheKey) } } elseif !.shouldRetryRequest(, ) {// Issue 16465: return underlying net.Conn.Read error from peek, // as we've historically done.if , := .(nothingWrittenError); { = .error }if , := .(transportReadFromServerError); { = .err }if , := .Body.(*readTrackingBody); && !.didClose {// Issue 49621: Close the request body if pconn.roundTrip // didn't do so already. This can happen if the pconn // write loop exits without reading the write request. .closeBody() }returnnil, }testHookRoundTripRetried()// Rewind the body if we're able to. , = rewindBody()if != nil {returnnil, } }}func awaitLegacyCancel( context.Context, context.CancelCauseFunc, *Request) {select {case<-.Cancel: (errRequestCanceled)case<-.Done(): }}var errCannotRewind = errors.New("net/http: cannot rewind body after connection loss")type readTrackingBody struct {io.ReadCloser didRead bool didClose bool}func ( *readTrackingBody) ( []byte) (int, error) { .didRead = truereturn .ReadCloser.Read()}func ( *readTrackingBody) () error { .didClose = truereturn .ReadCloser.Close()}// setupRewindBody returns a new request with a custom body wrapper// that can report whether the body needs rewinding.// This lets rewindBody avoid an error result when the request// does not have GetBody but the body hasn't been read at all yet.func setupRewindBody( *Request) *Request {if .Body == nil || .Body == NoBody {return } := * .Body = &readTrackingBody{ReadCloser: .Body}return &}// rewindBody returns a new request with the body rewound.// It returns req unmodified if the body does not need rewinding.// rewindBody takes care of closing req.Body when appropriate// (in all cases except when rewindBody returns req unmodified).func rewindBody( *Request) ( *Request, error) {if .Body == nil || .Body == NoBody || (!.Body.(*readTrackingBody).didRead && !.Body.(*readTrackingBody).didClose) {return , nil// nothing to rewind }if !.Body.(*readTrackingBody).didClose { .closeBody() }if .GetBody == nil {returnnil, errCannotRewind } , := .GetBody()if != nil {returnnil, } := * .Body = &readTrackingBody{ReadCloser: }return &, nil}// shouldRetryRequest reports whether we should retry sending a failed// HTTP request on a new connection. The non-nil input error is the// error from roundTrip.func ( *persistConn) ( *Request, error) bool {ifhttp2isNoCachedConnError() {// Issue 16582: if the user started a bunch of // requests at once, they can all pick the same conn // and violate the server's max concurrent streams. // Instead, match the HTTP/1 behavior for now and dial // again to get a new TCP connection, rather than failing // this request.returntrue }if == errMissingHost {// User error.returnfalse }if !.isReused() {// This was a fresh connection. There's no reason the server // should've hung up on us. // // Also, if we retried now, we could loop forever // creating new connections and retrying if the server // is just hanging up on us because it doesn't like // our request (as opposed to sending an error).returnfalse }if , := .(nothingWrittenError); {// We never wrote anything, so it's safe to retry, if there's no body or we // can "rewind" the body with GetBody.return .outgoingLength() == 0 || .GetBody != nil }if !.isReplayable() {// Don't retry non-idempotent requests.returnfalse }if , := .(transportReadFromServerError); {// We got some non-EOF net.Conn.Read failure reading // the 1st response byte from the server.returntrue }if == errServerClosedIdle {// The server replied with io.EOF while we were trying to // read the response. Probably an unfortunately keep-alive // timeout, just as the client was writing a request.returntrue }returnfalse// conservatively}// ErrSkipAltProtocol is a sentinel error value defined by Transport.RegisterProtocol.varErrSkipAltProtocol = errors.New("net/http: skip alternate protocol")// RegisterProtocol registers a new protocol with scheme.// The [Transport] will pass requests using the given scheme to rt.// It is rt's responsibility to simulate HTTP request semantics.//// RegisterProtocol can be used by other packages to provide// implementations of protocol schemes like "ftp" or "file".//// If rt.RoundTrip returns [ErrSkipAltProtocol], the Transport will// handle the [Transport.RoundTrip] itself for that one request, as if the// protocol were not registered.func ( *Transport) ( string, RoundTripper) { .altMu.Lock()defer .altMu.Unlock() , := .altProto.Load().(map[string]RoundTripper)if , := []; {panic("protocol " + + " already registered") } := maps.Clone()if == nil { = make(map[string]RoundTripper) } [] = .altProto.Store()}// CloseIdleConnections closes any connections which were previously// connected from previous requests but are now sitting idle in// a "keep-alive" state. It does not interrupt any connections currently// in use.func ( *Transport) () { .nextProtoOnce.Do(.onceSetNextProtoDefaults) .idleMu.Lock() := .idleConn .idleConn = nil .closeIdle = true// close newly idle connections .idleLRU = connLRU{} .idleMu.Unlock()for , := range {for , := range { .close(errCloseIdleConns) } } .connsPerHostMu.Lock() .dialsInProgress.all(func( *wantConn) {if .cancelCtx != nil && !.waiting() { .cancelCtx() } }) .connsPerHostMu.Unlock()if := .h2transport; != nil { .CloseIdleConnections() }}// prepareTransportCancel sets up state to convert Transport.CancelRequest into context cancelation.func ( *Transport) ( *Request, context.CancelCauseFunc) context.CancelCauseFunc {// Historically, RoundTrip has not modified the Request in any way. // We could avoid the need to keep a map of all in-flight requests by adding // a field to the Request containing its cancel func, and setting that field // while the request is in-flight. Callers aren't supposed to reuse a Request // until after the response body is closed, so this wouldn't violate any // concurrency guarantees. := func( error) { () .reqMu.Lock()delete(.reqCanceler, ) .reqMu.Unlock() } .reqMu.Lock()if .reqCanceler == nil { .reqCanceler = make(map[*Request]context.CancelCauseFunc) } .reqCanceler[] = .reqMu.Unlock()return}// CancelRequest cancels an in-flight request by closing its connection.// CancelRequest should only be called after [Transport.RoundTrip] has returned.//// Deprecated: Use [Request.WithContext] to create a request with a// cancelable context instead. CancelRequest cannot cancel HTTP/2// requests. This may become a no-op in a future release of Go.func ( *Transport) ( *Request) { .reqMu.Lock() := .reqCanceler[] .reqMu.Unlock()if != nil { (errRequestCanceled) }}//// Private implementation past this point.//var ( envProxyOnce sync.Once envProxyFuncValue func(*url.URL) (*url.URL, error))// envProxyFunc returns a function that reads the// environment variable to determine the proxy address.func envProxyFunc() func(*url.URL) (*url.URL, error) {envProxyOnce.Do(func() {envProxyFuncValue = httpproxy.FromEnvironment().ProxyFunc() })returnenvProxyFuncValue}// resetProxyConfig is used by tests.func resetProxyConfig() {envProxyOnce = sync.Once{}envProxyFuncValue = nil}func ( *Transport) ( *transportRequest) ( connectMethod, error) { .targetScheme = .URL.Scheme .targetAddr = canonicalAddr(.URL)if .Proxy != nil { .proxyURL, = .Proxy(.Request) } .onlyH1 = .requiresHTTP1()return , }// proxyAuth returns the Proxy-Authorization header to set// on requests, if applicable.func ( *connectMethod) () string {if .proxyURL == nil {return"" }if := .proxyURL.User; != nil { := .Username() , := .Password()return"Basic " + basicAuth(, ) }return""}// error values for debugging and testing, not seen by users.var ( errKeepAlivesDisabled = errors.New("http: putIdleConn: keep alives disabled") errConnBroken = errors.New("http: putIdleConn: connection is in bad state") errCloseIdle = errors.New("http: putIdleConn: CloseIdleConnections was called") errTooManyIdle = errors.New("http: putIdleConn: too many idle connections") errTooManyIdleHost = errors.New("http: putIdleConn: too many idle connections for host") errCloseIdleConns = errors.New("http: CloseIdleConnections called") errReadLoopExiting = errors.New("http: persistConn.readLoop exiting") errIdleConnTimeout = errors.New("http: idle connection timeout")// errServerClosedIdle is not seen by users for idempotent requests, but may be // seen by a user if the server shuts down an idle connection and sends its FIN // in flight with already-written POST body bytes from the client. // See https://github.com/golang/go/issues/19943#issuecomment-355607646 errServerClosedIdle = errors.New("http: server closed idle connection"))// transportReadFromServerError is used by Transport.readLoop when the// 1 byte peek read fails and we're actually anticipating a response.// Usually this is just due to the inherent keep-alive shut down race,// where the server closed the connection at the same time the client// wrote. The underlying err field is usually io.EOF or some// ECONNRESET sort of thing which varies by platform. But it might be// the user's custom net.Conn.Read error too, so we carry it along for// them to return from Transport.RoundTrip.type transportReadFromServerError struct { err error}func ( transportReadFromServerError) () error { return .err }func ( transportReadFromServerError) () string {returnfmt.Sprintf("net/http: Transport failed to read from server: %v", .err)}func ( *Transport) ( *persistConn) {if := .tryPutIdleConn(); != nil { .close() }}func ( *Transport) () int {if := .MaxIdleConnsPerHost; != 0 {return }returnDefaultMaxIdleConnsPerHost}// tryPutIdleConn adds pconn to the list of idle persistent connections awaiting// a new request.// If pconn is no longer needed or not in a good state, tryPutIdleConn returns// an error explaining why it wasn't registered.// tryPutIdleConn does not close pconn. Use putOrCloseIdleConn instead for that.func ( *Transport) ( *persistConn) error {if .DisableKeepAlives || .MaxIdleConnsPerHost < 0 {returnerrKeepAlivesDisabled }if .isBroken() {returnerrConnBroken } .markReused() .idleMu.Lock()defer .idleMu.Unlock()// HTTP/2 (pconn.alt != nil) connections do not come out of the idle list, // because multiple goroutines can use them simultaneously. // If this is an HTTP/2 connection being “returned,” we're done.if .alt != nil && .idleLRU.m[] != nil {returnnil }// Deliver pconn to goroutine waiting for idle connection, if any. // (They may be actively dialing, but this conn is ready first. // Chrome calls this socket late binding. // See https://www.chromium.org/developers/design-documents/network-stack#TOC-Connection-Management.) := .cacheKeyif , := .idleConnWait[]; { := falseif .alt == nil {// HTTP/1. // Loop over the waiting list until we find a w that isn't done already, and hand it pconn.for .len() > 0 { := .popFront()if .tryDeliver(, nil, time.Time{}) { = truebreak } } } else {// HTTP/2. // Can hand the same pconn to everyone in the waiting list, // and we still won't be done: we want to put it in the idle // list unconditionally, for any future clients too.for .len() > 0 { := .popFront() .tryDeliver(, nil, time.Time{}) } }if .len() == 0 {delete(.idleConnWait, ) } else { .idleConnWait[] = }if {returnnil } }if .closeIdle {returnerrCloseIdle }if .idleConn == nil { .idleConn = make(map[connectMethodKey][]*persistConn) } := .idleConn[]iflen() >= .maxIdleConnsPerHost() {returnerrTooManyIdleHost }for , := range {if == {log.Fatalf("dup idle pconn %p in freelist", ) } } .idleConn[] = append(, ) .idleLRU.add()if .MaxIdleConns != 0 && .idleLRU.len() > .MaxIdleConns { := .idleLRU.removeOldest() .close(errTooManyIdle) .removeIdleConnLocked() }// Set idle timer, but only for HTTP/1 (pconn.alt == nil). // The HTTP/2 implementation manages the idle timer itself // (see idleConnTimeout in h2_bundle.go).if .IdleConnTimeout > 0 && .alt == nil {if .idleTimer != nil { .idleTimer.Reset(.IdleConnTimeout) } else { .idleTimer = time.AfterFunc(.IdleConnTimeout, .closeConnIfStillIdle) } } .idleAt = time.Now()returnnil}// queueForIdleConn queues w to receive the next idle connection for w.cm.// As an optimization hint to the caller, queueForIdleConn reports whether// it successfully delivered an already-idle connection.func ( *Transport) ( *wantConn) ( bool) {if .DisableKeepAlives {returnfalse } .idleMu.Lock()defer .idleMu.Unlock()// Stop closing connections that become idle - we might want one. // (That is, undo the effect of t.CloseIdleConnections.) .closeIdle = falseif == nil {// Happens in test hook.returnfalse }// If IdleConnTimeout is set, calculate the oldest // persistConn.idleAt time we're willing to use a cached idle // conn.vartime.Timeif .IdleConnTimeout > 0 { = time.Now().Add(-.IdleConnTimeout) }// Look for most recently-used idle connection.if , := .idleConn[.key]; { := false := falseforlen() > 0 && ! { := [len()-1]// See whether this connection has been idle too long, considering // only the wall time (the Round(0)), in case this is a laptop or VM // coming out of suspend with previously cached idle connections. := !.IsZero() && .idleAt.Round(0).Before()if {// Async cleanup. Launch in its own goroutine (as if a // time.AfterFunc called it); it acquires idleMu, which we're // holding, and does a synchronous net.Conn.Close.go .closeConnIfStillIdle() }if .isBroken() || {// If either persistConn.readLoop has marked the connection // broken, but Transport.removeIdleConn has not yet removed it // from the idle list, or if this persistConn is too old (it was // idle too long), then ignore it and look for another. In both // cases it's already in the process of being closed. = [:len()-1]continue } = .tryDeliver(, nil, .idleAt)if {if .alt != nil {// HTTP/2: multiple clients can share pconn. // Leave it in the list. } else {// HTTP/1: only one client can use pconn. // Remove it from the list. .idleLRU.remove() = [:len()-1] } } = true }iflen() > 0 { .idleConn[.key] = } else {delete(.idleConn, .key) }if {return } }// Register to receive next connection that becomes idle.if .idleConnWait == nil { .idleConnWait = make(map[connectMethodKey]wantConnQueue) } := .idleConnWait[.key] .cleanFrontNotWaiting() .pushBack() .idleConnWait[.key] = returnfalse}// removeIdleConn marks pconn as dead.func ( *Transport) ( *persistConn) bool { .idleMu.Lock()defer .idleMu.Unlock()return .removeIdleConnLocked()}// t.idleMu must be held.func ( *Transport) ( *persistConn) bool {if .idleTimer != nil { .idleTimer.Stop() } .idleLRU.remove() := .cacheKey := .idleConn[]varboolswitchlen() {case0:// Nothingcase1:if [0] == {delete(.idleConn, ) = true }default:for , := range {if != {continue }// Slide down, keeping most recently-used // conns at the end.copy([:], [+1:]) .idleConn[] = [:len()-1] = truebreak } }return}var zeroDialer net.Dialerfunc ( *Transport) ( context.Context, , string) (net.Conn, error) {if .DialContext != nil { , := .DialContext(, , )if == nil && == nil { = errors.New("net/http: Transport.DialContext hook returned (nil, nil)") }return , }if .Dial != nil { , := .Dial(, )if == nil && == nil { = errors.New("net/http: Transport.Dial hook returned (nil, nil)") }return , }returnzeroDialer.DialContext(, , )}// A wantConn records state about a wanted connection// (that is, an active call to getConn).// The conn may be gotten by dialing or by finding an idle connection,// or a cancellation may make the conn no longer wanted.// These three options are racing against each other and use// wantConn to coordinate and agree about the winning outcome.type wantConn struct { cm connectMethod key connectMethodKey// cm.key()// hooks for testing to know when dials are done // beforeDial is called in the getConn goroutine when the dial is queued. // afterDial is called when the dial is completed or canceled. beforeDial func() afterDial func() mu sync.Mutex// protects ctx, done and sending of the result ctx context.Context// context for dial, cleared after delivered or canceled cancelCtx context.CancelFunc done bool// true after delivered or canceled result chanconnOrError// channel to deliver connection or error}type connOrError struct { pc *persistConn err error idleAt time.Time}// waiting reports whether w is still waiting for an answer (connection or error).func ( *wantConn) () bool { .mu.Lock()defer .mu.Unlock()return !.done}// getCtxForDial returns context for dial or nil if connection was delivered or canceled.func ( *wantConn) () context.Context { .mu.Lock()defer .mu.Unlock()return .ctx}// tryDeliver attempts to deliver pc, err to w and reports whether it succeeded.func ( *wantConn) ( *persistConn, error, time.Time) bool { .mu.Lock()defer .mu.Unlock()if .done {returnfalse }if ( == nil) == ( == nil) {panic("net/http: internal error: misuse of tryDeliver") } .ctx = nil .done = true .result <- connOrError{pc: , err: , idleAt: }close(.result)returntrue}// cancel marks w as no longer wanting a result (for example, due to cancellation).// If a connection has been delivered already, cancel returns it with t.putOrCloseIdleConn.func ( *wantConn) ( *Transport, error) { .mu.Lock()var *persistConnif .done {if , := <-.result; { = .pc } } else {close(.result) } .ctx = nil .done = true .mu.Unlock()if != nil { .putOrCloseIdleConn() }}// A wantConnQueue is a queue of wantConns.type wantConnQueue struct {// This is a queue, not a deque. // It is split into two stages - head[headPos:] and tail. // popFront is trivial (headPos++) on the first stage, and // pushBack is trivial (append) on the second stage. // If the first stage is empty, popFront can swap the // first and second stages to remedy the situation. // // This two-stage split is analogous to the use of two lists // in Okasaki's purely functional queue but without the // overhead of reversing the list when swapping stages. head []*wantConn headPos int tail []*wantConn}// len returns the number of items in the queue.func ( *wantConnQueue) () int {returnlen(.head) - .headPos + len(.tail)}// pushBack adds w to the back of the queue.func ( *wantConnQueue) ( *wantConn) { .tail = append(.tail, )}// popFront removes and returns the wantConn at the front of the queue.func ( *wantConnQueue) () *wantConn {if .headPos >= len(.head) {iflen(.tail) == 0 {returnnil }// Pick up tail as new head, clear tail. .head, .headPos, .tail = .tail, 0, .head[:0] } := .head[.headPos] .head[.headPos] = nil .headPos++return}// peekFront returns the wantConn at the front of the queue without removing it.func ( *wantConnQueue) () *wantConn {if .headPos < len(.head) {return .head[.headPos] }iflen(.tail) > 0 {return .tail[0] }returnnil}// cleanFrontNotWaiting pops any wantConns that are no longer waiting from the head of the// queue, reporting whether any were popped.func ( *wantConnQueue) () ( bool) {for { := .peekFront()if == nil || .waiting() {return } .popFront() = true }}// cleanFrontCanceled pops any wantConns with canceled dials from the head of the queue.func ( *wantConnQueue) () {for { := .peekFront()if == nil || .cancelCtx != nil {return } .popFront() }}// all iterates over all wantConns in the queue.// The caller must not modify the queue while iterating.func ( *wantConnQueue) ( func(*wantConn)) {for , := range .head[.headPos:] { () }for , := range .tail { () }}func ( *Transport) ( context.Context, , string) ( net.Conn, error) {if .DialTLSContext != nil { , = .DialTLSContext(, , ) } else { , = .DialTLS(, ) }if == nil && == nil { = errors.New("net/http: Transport.DialTLS or DialTLSContext returned (nil, nil)") }return}// getConn dials and creates a new persistConn to the target as// specified in the connectMethod. This includes doing a proxy CONNECT// and/or setting up TLS. If this doesn't return an error, the persistConn// is ready to write requests to.func ( *Transport) ( *transportRequest, connectMethod) ( *persistConn, error) { := .Request := .trace := .Context()if != nil && .GetConn != nil { .GetConn(.addr()) }// Detach from the request context's cancellation signal. // The dial should proceed even if the request is canceled, // because a future request may be able to make use of the connection. // // We retain the request context's values. , := context.WithCancel(context.WithoutCancel()) := &wantConn{cm: ,key: .key(),ctx: ,cancelCtx: ,result: make(chanconnOrError, 1),beforeDial: testHookPrePendingDial,afterDial: testHookPostPendingDial, }deferfunc() {if != nil { .cancel(, ) } }()// Queue for idle connection.if := .queueForIdleConn(); ! { .queueForDial() }// Wait for completion or cancellation.select {case := <-.result:// Trace success but only for HTTP/1. // HTTP/2 calls trace.GotConn itself.if .pc != nil && .pc.alt == nil && != nil && .GotConn != nil { := httptrace.GotConnInfo{Conn: .pc.conn,Reused: .pc.isReused(), }if !.idleAt.IsZero() { .WasIdle = true .IdleTime = time.Since(.idleAt) } .GotConn() }if .err != nil {// If the request has been canceled, that's probably // what caused r.err; if so, prefer to return the // cancellation error (see golang.org/issue/16049).select {case<-.ctx.Done(): := context.Cause(.ctx)if == errRequestCanceled { = errRequestCanceledConn }returnnil, default:// return below } }return .pc, .errcase<-.ctx.Done(): := context.Cause(.ctx)if == errRequestCanceled { = errRequestCanceledConn }returnnil, }}// queueForDial queues w to wait for permission to begin dialing.// Once w receives permission to dial, it will do so in a separate goroutine.func ( *Transport) ( *wantConn) { .beforeDial() .connsPerHostMu.Lock()defer .connsPerHostMu.Unlock()if .MaxConnsPerHost <= 0 { .startDialConnForLocked()return }if := .connsPerHost[.key]; < .MaxConnsPerHost {if .connsPerHost == nil { .connsPerHost = make(map[connectMethodKey]int) } .connsPerHost[.key] = + 1 .startDialConnForLocked()return }if .connsPerHostWait == nil { .connsPerHostWait = make(map[connectMethodKey]wantConnQueue) } := .connsPerHostWait[.key] .cleanFrontNotWaiting() .pushBack() .connsPerHostWait[.key] = }// startDialConnFor calls dialConn in a new goroutine.// t.connsPerHostMu must be held.func ( *Transport) ( *wantConn) { .dialsInProgress.cleanFrontCanceled() .dialsInProgress.pushBack()gofunc() { .dialConnFor() .connsPerHostMu.Lock()defer .connsPerHostMu.Unlock() .cancelCtx = nil }()}// dialConnFor dials on behalf of w and delivers the result to w.// dialConnFor has received permission to dial w.cm and is counted in t.connCount[w.cm.key()].// If the dial is canceled or unsuccessful, dialConnFor decrements t.connCount[w.cm.key()].func ( *Transport) ( *wantConn) {defer .afterDial() := .getCtxForDial()if == nil { .decConnsPerHost(.key)return } , := .dialConn(, .cm) := .tryDeliver(, , time.Time{})if == nil && (! || .alt != nil) {// pconn was not passed to w, // or it is HTTP/2 and can be shared. // Add to the idle connection pool. .putOrCloseIdleConn() }if != nil { .decConnsPerHost(.key) }}// decConnsPerHost decrements the per-host connection count for key,// which may in turn give a different waiting goroutine permission to dial.func ( *Transport) ( connectMethodKey) {if .MaxConnsPerHost <= 0 {return } .connsPerHostMu.Lock()defer .connsPerHostMu.Unlock() := .connsPerHost[]if == 0 {// Shouldn't happen, but if it does, the counting is buggy and could // easily lead to a silent deadlock, so report the problem loudly.panic("net/http: internal error: connCount underflow") }// Can we hand this count to a goroutine still waiting to dial? // (Some goroutines on the wait list may have timed out or // gotten a connection another way. If they're all gone, // we don't want to kick off any spurious dial operations.)if := .connsPerHostWait[]; .len() > 0 { := falsefor .len() > 0 { := .popFront()if .waiting() { .startDialConnForLocked() = truebreak } }if .len() == 0 {delete(.connsPerHostWait, ) } else {// q is a value (like a slice), so we have to store // the updated q back into the map. .connsPerHostWait[] = }if {return } }// Otherwise, decrement the recorded count.if --; == 0 {delete(.connsPerHost, ) } else { .connsPerHost[] = }}// Add TLS to a persistent connection, i.e. negotiate a TLS session. If pconn is already a TLS// tunnel, this function establishes a nested TLS session inside the encrypted channel.// The remote endpoint's name may be overridden by TLSClientConfig.ServerName.func ( *persistConn) ( context.Context, string, *httptrace.ClientTrace) error {// Initiate TLS and check remote host name against certificate. := cloneTLSConfig(.t.TLSClientConfig)if .ServerName == "" { .ServerName = }if .cacheKey.onlyH1 { .NextProtos = nil } := .conn := tls.Client(, ) := make(chanerror, 2)var *time.Timer// for canceling TLS handshakeif := .t.TLSHandshakeTimeout; != 0 { = time.AfterFunc(, func() { <- tlsHandshakeTimeoutError{} }) }gofunc() {if != nil && .TLSHandshakeStart != nil { .TLSHandshakeStart() } := .HandshakeContext()if != nil { .Stop() } <- }()if := <-; != nil { .Close()if == (tlsHandshakeTimeoutError{}) {// Now that we have closed the connection, // wait for the call to HandshakeContext to return. <- }if != nil && .TLSHandshakeDone != nil { .TLSHandshakeDone(tls.ConnectionState{}, ) }return } := .ConnectionState()if != nil && .TLSHandshakeDone != nil { .TLSHandshakeDone(, nil) } .tlsState = & .conn = returnnil}type erringRoundTripper interface { RoundTripErr() error}var testHookProxyConnectTimeout = context.WithTimeoutfunc ( *Transport) ( context.Context, connectMethod) ( *persistConn, error) { = &persistConn{t: ,cacheKey: .key(),reqch: make(chanrequestAndChan, 1),writech: make(chanwriteRequest, 1),closech: make(chanstruct{}),writeErrCh: make(chanerror, 1),writeLoopDone: make(chanstruct{}), } := httptrace.ContextClientTrace() := func( error) error {if .proxyURL != nil {// Return a typed error, per Issue 16997return &net.OpError{Op: "proxyconnect", Net: "tcp", Err: } }return }if .scheme() == "https" && .hasCustomTLSDialer() {varerror .conn, = .customDialTLS(, "tcp", .addr())if != nil {returnnil, () }if , := .conn.(*tls.Conn); {// Handshake here, in case DialTLS didn't. TLSNextProto below // depends on it for knowing the connection state.if != nil && .TLSHandshakeStart != nil { .TLSHandshakeStart() }if := .HandshakeContext(); != nil {go .conn.Close()if != nil && .TLSHandshakeDone != nil { .TLSHandshakeDone(tls.ConnectionState{}, ) }returnnil, } := .ConnectionState()if != nil && .TLSHandshakeDone != nil { .TLSHandshakeDone(, nil) } .tlsState = & } } else { , := .dial(, "tcp", .addr())if != nil {returnnil, () } .conn = if .scheme() == "https" {varstringif , _, = net.SplitHostPort(.addr()); != nil {returnnil, () }if = .addTLS(, , ); != nil {returnnil, () } } }// Proxy setup.switch {case .proxyURL == nil:// Do nothing. Not using a proxy.case .proxyURL.Scheme == "socks5" || .proxyURL.Scheme == "socks5h": := .conn := socksNewDialer("tcp", .RemoteAddr().String())if := .proxyURL.User; != nil { := &socksUsernamePassword{Username: .Username(), } .Password, _ = .Password() .AuthMethods = []socksAuthMethod{socksAuthMethodNotRequired,socksAuthMethodUsernamePassword, } .Authenticate = .Authenticate }if , := .DialWithConn(, , "tcp", .targetAddr); != nil { .Close()returnnil, }case .targetScheme == "http": .isProxy = trueif := .proxyAuth(); != "" { .mutateHeaderFunc = func( Header) { .Set("Proxy-Authorization", ) } }case .targetScheme == "https": := .connvarHeaderif .GetProxyConnectHeader != nil {varerror , = .GetProxyConnectHeader(, .proxyURL, .targetAddr)if != nil { .Close()returnnil, } } else { = .ProxyConnectHeader }if == nil { = make(Header) }if := .proxyAuth(); != "" { = .Clone() .Set("Proxy-Authorization", ) } := &Request{Method: "CONNECT",URL: &url.URL{Opaque: .targetAddr},Host: .targetAddr,Header: , }// Set a (long) timeout here to make sure we don't block forever // and leak a goroutine if the connection stops replying after // the TCP connect. , := testHookProxyConnectTimeout(, 1*time.Minute)defer () := make(chanstruct{}) // closed after CONNECT write+read is done or failsvar ( *Responseerror// write or read error )// Write the CONNECT request & read the response.gofunc() {deferclose() = .Write()if != nil {return }// Okay to use and discard buffered reader here, because // TLS server will not speak until spoken to. := bufio.NewReader() , = ReadResponse(, ) }()select {case<-.Done(): .Close() <-returnnil, .Err()case<-:// resp or err now set }if != nil { .Close()returnnil, }if .OnProxyConnectResponse != nil { = .OnProxyConnectResponse(, .proxyURL, , )if != nil { .Close()returnnil, } }if .StatusCode != 200 { , , := strings.Cut(.Status, " ") .Close()if ! {returnnil, errors.New("unknown status code") }returnnil, errors.New() } }if .proxyURL != nil && .targetScheme == "https" {if := .addTLS(, .tlsHost(), ); != nil {returnnil, } }// Possible unencrypted HTTP/2 with prior knowledge. := .tlsState == nil && .Protocols != nil && .Protocols.UnencryptedHTTP2() && !.Protocols.HTTP1()if { , := .TLSNextProto[nextProtoUnencryptedHTTP2]if ! {returnnil, errors.New("http: Transport does not support unencrypted HTTP/2") } := (.targetAddr, unencryptedTLSConn(.conn))if , := .(erringRoundTripper); {// pconn.conn was closed by next (http2configureTransports.upgradeFn).returnnil, .RoundTripErr() }return &persistConn{t: , cacheKey: .cacheKey, alt: }, nil }if := .tlsState; != nil && .NegotiatedProtocolIsMutual && .NegotiatedProtocol != "" {if , := .TLSNextProto[.NegotiatedProtocol]; { := (.targetAddr, .conn.(*tls.Conn))if , := .(erringRoundTripper); {// pconn.conn was closed by next (http2configureTransports.upgradeFn).returnnil, .RoundTripErr() }return &persistConn{t: , cacheKey: .cacheKey, alt: }, nil } } .br = bufio.NewReaderSize(, .readBufferSize()) .bw = bufio.NewWriterSize(persistConnWriter{}, .writeBufferSize())go .readLoop()go .writeLoop()return , nil}// persistConnWriter is the io.Writer written to by pc.bw.// It accumulates the number of bytes written to the underlying conn,// so the retry logic can determine whether any bytes made it across// the wire.// This is exactly 1 pointer field wide so it can go into an interface// without allocation.type persistConnWriter struct { pc *persistConn}func ( persistConnWriter) ( []byte) ( int, error) { , = .pc.conn.Write() .pc.nwrite += int64()return}// ReadFrom exposes persistConnWriter's underlying Conn to io.Copy and if// the Conn implements io.ReaderFrom, it can take advantage of optimizations// such as sendfile.func ( persistConnWriter) ( io.Reader) ( int64, error) { , = io.Copy(.pc.conn, ) .pc.nwrite += return}var _ io.ReaderFrom = (*persistConnWriter)(nil)// connectMethod is the map key (in its String form) for keeping persistent// TCP connections alive for subsequent HTTP requests.//// A connect method may be of the following types://// connectMethod.key().String() Description// ------------------------------ -------------------------// |http|foo.com http directly to server, no proxy// |https|foo.com https directly to server, no proxy// |https,h1|foo.com https directly to server w/o HTTP/2, no proxy// http://proxy.com|https|foo.com http to proxy, then CONNECT to foo.com// http://proxy.com|http http to proxy, http to anywhere after that// socks5://proxy.com|http|foo.com socks5 to proxy, then http to foo.com// socks5://proxy.com|https|foo.com socks5 to proxy, then https to foo.com// https://proxy.com|https|foo.com https to proxy, then CONNECT to foo.com// https://proxy.com|http https to proxy, http to anywhere after thattype connectMethod struct { _ incomparable proxyURL *url.URL// nil for no proxy, else full proxy URL targetScheme string// "http" or "https"// If proxyURL specifies an http or https proxy, and targetScheme is http (not https), // then targetAddr is not included in the connect method key, because the socket can // be reused for different targetAddr values. targetAddr string onlyH1 bool// whether to disable HTTP/2 and force HTTP/1}func ( *connectMethod) () connectMethodKey { := "" := .targetAddrif .proxyURL != nil { = .proxyURL.String()if (.proxyURL.Scheme == "http" || .proxyURL.Scheme == "https") && .targetScheme == "http" { = "" } }returnconnectMethodKey{proxy: ,scheme: .targetScheme,addr: ,onlyH1: .onlyH1, }}// scheme returns the first hop scheme: http, https, or socks5func ( *connectMethod) () string {if .proxyURL != nil {return .proxyURL.Scheme }return .targetScheme}// addr returns the first hop "host:port" to which we need to TCP connect.func ( *connectMethod) () string {if .proxyURL != nil {returncanonicalAddr(.proxyURL) }return .targetAddr}// tlsHost returns the host name to match against the peer's// TLS certificate.func ( *connectMethod) () string { := .targetAddrifhasPort() { = [:strings.LastIndex(, ":")] }return}// connectMethodKey is the map key version of connectMethod, with a// stringified proxy URL (or the empty string) instead of a pointer to// a URL.type connectMethodKey struct { proxy, scheme, addr string onlyH1 bool}func ( connectMethodKey) () string {// Only used by tests.varstringif .onlyH1 { = ",h1" }returnfmt.Sprintf("%s|%s%s|%s", .proxy, .scheme, , .addr)}// persistConn wraps a connection, usually a persistent one// (but may be used for non-keep-alive requests as well)type persistConn struct {// alt optionally specifies the TLS NextProto RoundTripper. // This is used for HTTP/2 today and future protocols later. // If it's non-nil, the rest of the fields are unused. alt RoundTripper t *Transport cacheKey connectMethodKey conn net.Conn tlsState *tls.ConnectionState br *bufio.Reader// from conn bw *bufio.Writer// to conn nwrite int64// bytes written reqch chanrequestAndChan// written by roundTrip; read by readLoop writech chanwriteRequest// written by roundTrip; read by writeLoop closech chanstruct{} // closed when conn closed isProxy bool sawEOF bool// whether we've seen EOF from conn; owned by readLoop readLimit int64// bytes allowed to be read; owned by readLoop// writeErrCh passes the request write error (usually nil) // from the writeLoop goroutine to the readLoop which passes // it off to the res.Body reader, which then uses it to decide // whether or not a connection can be reused. Issue 7569. writeErrCh chanerror writeLoopDone chanstruct{} // closed when write loop ends// Both guarded by Transport.idleMu: idleAt time.Time// time it last become idle idleTimer *time.Timer// holding an AfterFunc to close it mu sync.Mutex// guards following fields numExpectedResponses int closed error// set non-nil when conn is closed, before closech is closed canceledErr error// set non-nil if conn is canceled broken bool// an error has happened on this connection; marked broken so it's not reused. reused bool// whether conn has had successful request/response and is being reused.// mutateHeaderFunc is an optional func to modify extra // headers on each outbound request before it's written. (the // original Request given to RoundTrip is not modified) mutateHeaderFunc func(Header)}func ( *persistConn) () int64 {if := .t.MaxResponseHeaderBytes; != 0 {return }return10 << 20// conservative default; same as http2}func ( *persistConn) ( []byte) ( int, error) {if .readLimit <= 0 {return0, fmt.Errorf("read limit of %d bytes exhausted", .maxHeaderResponseSize()) }ifint64(len()) > .readLimit { = [:.readLimit] } , = .conn.Read()if == io.EOF { .sawEOF = true } .readLimit -= int64()return}// isBroken reports whether this connection is in a known broken state.func ( *persistConn) () bool { .mu.Lock() := .closed != nil .mu.Unlock()return}// canceled returns non-nil if the connection was closed due to// CancelRequest or due to context cancellation.func ( *persistConn) () error { .mu.Lock()defer .mu.Unlock()return .canceledErr}// isReused reports whether this connection has been used before.func ( *persistConn) () bool { .mu.Lock() := .reused .mu.Unlock()return}func ( *persistConn) ( error) { .mu.Lock()defer .mu.Unlock() .canceledErr = .closeLocked(errRequestCanceled)}// closeConnIfStillIdle closes the connection if it's still sitting idle.// This is what's called by the persistConn's idleTimer, and is run in its// own goroutine.func ( *persistConn) () { := .t .idleMu.Lock()defer .idleMu.Unlock()if , := .idleLRU.m[]; ! {// Not idle.return } .removeIdleConnLocked() .close(errIdleConnTimeout)}// mapRoundTripError returns the appropriate error value for// persistConn.roundTrip.//// The provided err is the first error that (*persistConn).roundTrip// happened to receive from its select statement.//// The startBytesWritten value should be the value of pc.nwrite before the roundTrip// started writing the request.func ( *persistConn) ( *transportRequest, int64, error) error {if == nil {returnnil }// Wait for the writeLoop goroutine to terminate to avoid data // races on callers who mutate the request on failure. // // When resc in pc.roundTrip and hence rc.ch receives a responseAndError // with a non-nil error it implies that the persistConn is either closed // or closing. Waiting on pc.writeLoopDone is hence safe as all callers // close closech which in turn ensures writeLoop returns. <-.writeLoopDone// If the request was canceled, that's better than network // failures that were likely the result of tearing down the // connection.if := .canceled(); != nil {return }// See if an error was set explicitly. .mu.Lock() := .err .mu.Unlock()if != nil {return }if == errServerClosedIdle {// Don't decoratereturn }if , := .(transportReadFromServerError); {if .nwrite == {returnnothingWrittenError{} }// Don't decoratereturn }if .isBroken() {if .nwrite == {returnnothingWrittenError{} }returnfmt.Errorf("net/http: HTTP/1.x transport connection broken: %w", ) }return}// errCallerOwnsConn is an internal sentinel error used when we hand// off a writable response.Body to the caller. We use this to prevent// closing a net.Conn that is now owned by the caller.var errCallerOwnsConn = errors.New("read loop ending; caller owns writable underlying conn")func ( *persistConn) () { := errReadLoopExiting// default value, if not changed belowdeferfunc() { .close() .t.removeIdleConn() }() := func( *transportRequest) bool { := .traceif := .t.tryPutIdleConn(); != nil { = if != nil && .PutIdleConn != nil && != errKeepAlivesDisabled { .PutIdleConn() }returnfalse }if != nil && .PutIdleConn != nil { .PutIdleConn(nil) }returntrue }// eofc is used to block caller goroutines reading from Response.Body // at EOF until this goroutines has (potentially) added the connection // back to the idle pool. := make(chanstruct{})deferclose() // unblock reader on errors// Read this once, before loop starts. (to avoid races in tests)testHookMu.Lock() := testHookReadLoopBeforeNextReadtestHookMu.Unlock() := truefor { .readLimit = .maxHeaderResponseSize() , := .br.Peek(1) .mu.Lock()if .numExpectedResponses == 0 { .readLoopPeekFailLocked() .mu.Unlock()return } .mu.Unlock() := <-.reqch := .treq.tracevar *Responseif == nil { , = .readResponse(, ) } else { = transportReadFromServerError{} = }if != nil {if .readLimit <= 0 { = fmt.Errorf("net/http: server response headers exceeded %d bytes; aborted", .maxHeaderResponseSize()) }select {case .ch<-responseAndError{err: }:case<-.callerGone:return }return } .readLimit = maxInt64// effectively no limit for response bodies .mu.Lock() .numExpectedResponses-- .mu.Unlock() := .bodyIsWritable() := .treq.Request.Method != "HEAD" && .ContentLength != 0if .Close || .treq.Request.Close || .StatusCode <= 199 || {// Don't do keep-alive on error if either party requested a close // or we get an unexpected informational (1xx) response. // StatusCode 100 is already handled above. = false }if ! || {// Put the idle conn back into the pool before we send the response // so if they process it quickly and make another request, they'll // get this same conn. But we use the unbuffered channel 'rc' // to guarantee that persistConn.roundTrip got out of its select // potentially waiting for this persistConn to close. = && !.sawEOF && .wroteRequest() && (.treq)if { = errCallerOwnsConn }select {case .ch<-responseAndError{res: }:case<-.callerGone:return } .treq.cancel(errRequestDone)// Now that they've read from the unbuffered channel, they're safely // out of the select that also waits on this goroutine to die, so // we're allowed to exit now if needed (if alive is false) ()continue } := make(chanbool, 2) := &bodyEOFSignal{body: .Body,earlyCloseFn: func() error { <- false <- // will be closed by deferred call at the end of the functionreturnnil },fn: func( error) error { := == io.EOF <- if { <- // see comment above eofc declaration } elseif != nil {if := .canceled(); != nil {return } }return }, } .Body = if .addedGzip && ascii.EqualFold(.Header.Get("Content-Encoding"), "gzip") { .Body = &gzipReader{body: } .Header.Del("Content-Encoding") .Header.Del("Content-Length") .ContentLength = -1 .Uncompressed = true }select {case .ch<-responseAndError{res: }:case<-.callerGone:return }// Before looping back to the top of this function and peeking on // the bufio.Reader, wait for the caller goroutine to finish // reading the response body. (or for cancellation or death)select {case := <-: = && && !.sawEOF && .wroteRequest() && (.treq)if { <- struct{}{} }case<-.treq.ctx.Done(): = false .cancelRequest(context.Cause(.treq.ctx))case<-.closech: = false } .treq.cancel(errRequestDone) () }}func ( *persistConn) ( error) {if .closed != nil {return }if := .br.Buffered(); > 0 { , := .br.Peek()ifis408Message() { .closeLocked(errServerClosedIdle)return } else {log.Printf("Unsolicited response received on idle HTTP channel starting with %q; err=%v", , ) } }if == io.EOF {// common case. .closeLocked(errServerClosedIdle) } else { .closeLocked(fmt.Errorf("readLoopPeekFailLocked: %w", )) }}// is408Message reports whether buf has the prefix of an// HTTP 408 Request Timeout response.// See golang.org/issue/32310.func is408Message( []byte) bool {iflen() < len("HTTP/1.x 408") {returnfalse }ifstring([:7]) != "HTTP/1." {returnfalse }returnstring([8:12]) == " 408"}// readResponse reads an HTTP response (or two, in the case of "Expect:// 100-continue") from the server. It returns the final non-100 one.// trace is optional.func ( *persistConn) ( requestAndChan, *httptrace.ClientTrace) ( *Response, error) {if != nil && .GotFirstResponseByte != nil {if , := .br.Peek(1); == nil && len() == 1 { .GotFirstResponseByte() } } := .continueChfor { , = ReadResponse(.br, .treq.Request)if != nil {return } := .StatusCodeif != nil && == StatusContinue {if != nil && .Got100Continue != nil { .Got100Continue() } <- struct{}{} = nil } := 100 <= && <= 199// treat 101 as a terminal status, see issue 26161 := && != StatusSwitchingProtocolsif {if != nil && .Got1xxResponse != nil {if := .Got1xxResponse(, textproto.MIMEHeader(.Header)); != nil {returnnil, }// If the 1xx response was delivered to the user, // then they're responsible for limiting the number of // responses. Reset the header limit. // // If the user didn't examine the 1xx response, then we // limit the size of all headers (including both 1xx // and the final response) to maxHeaderResponseSize. .readLimit = .maxHeaderResponseSize() // reset the limit }continue }break }if .isProtocolSwitch() { .Body = newReadWriteCloserBody(.br, .conn) }if != nil {// We send an "Expect: 100-continue" header, but the server // responded with a terminal status and no 100 Continue. // // If we're going to keep using the connection, we need to send the request body. // Tell writeLoop to skip sending the body if we're going to close the connection, // or to send it otherwise. // // The case where we receive a 101 Switching Protocols response is a bit // ambiguous, since we don't know what protocol we're switching to. // Conceivably, it's one that doesn't need us to send the body. // Given that we'll send the body if ExpectContinueTimeout expires, // be consistent and always send it if we aren't closing the connection.if .Close || .treq.Request.Close {close() // don't send the body; the connection will close } else { <- struct{}{} // send the body } } .TLS = .tlsStatereturn}// waitForContinue returns the function to block until// any response, timeout or connection close. After any of them,// the function returns a bool which indicates if the body should be sent.func ( *persistConn) ( <-chanstruct{}) func() bool {if == nil {returnnil }returnfunc() bool { := time.NewTimer(.t.ExpectContinueTimeout)defer .Stop()select {case , := <-:returncase<-.C:returntruecase<-.closech:returnfalse } }}func newReadWriteCloserBody( *bufio.Reader, io.ReadWriteCloser) io.ReadWriteCloser { := &readWriteCloserBody{ReadWriteCloser: }if .Buffered() != 0 { .br = }return}// readWriteCloserBody is the Response.Body type used when we want to// give users write access to the Body through the underlying// connection (TCP, unless using custom dialers). This is then// the concrete type for a Response.Body on the 101 Switching// Protocols response, as used by WebSockets, h2c, etc.type readWriteCloserBody struct { _ incomparable br *bufio.Reader// used until emptyio.ReadWriteCloser}func ( *readWriteCloserBody) ( []byte) ( int, error) {if .br != nil {if := .br.Buffered(); len() > { = [:] } , = .br.Read()if .br.Buffered() == 0 { .br = nil }return , }return .ReadWriteCloser.Read()}// nothingWrittenError wraps a write errors which ended up writing zero bytes.type nothingWrittenError struct {error}func ( nothingWrittenError) () error {return .error}func ( *persistConn) () {deferclose(.writeLoopDone)for {select {case := <-.writech: := .nwrite := .req.Request.write(.bw, .isProxy, .req.extra, .waitForContinue(.continueCh))if , := .(requestBodyReadError); { = .error// Errors reading from the user's // Request.Body are high priority. // Set it here before sending on the // channels below or calling // pc.close() which tears down // connections and causes other // errors. .req.setError() }if == nil { = .bw.Flush() }if != nil {if .nwrite == { = nothingWrittenError{} } } .writeErrCh <- // to the body reader, which might recycle us .ch <- // to the roundTrip functionif != nil { .close()return }case<-.closech:return } }}// maxWriteWaitBeforeConnReuse is how long the a Transport RoundTrip// will wait to see the Request's Body.Write result after getting a// response from the server. See comments in (*persistConn).wroteRequest.//// In tests, we set this to a large value to avoid flakiness from inconsistent// recycling of connections.var maxWriteWaitBeforeConnReuse = 50 * time.Millisecond// wroteRequest is a check before recycling a connection that the previous write// (from writeLoop above) happened and was successful.func ( *persistConn) () bool {select {case := <-.writeErrCh:// Common case: the write happened well before the response, so // avoid creating a timer.return == nildefault:// Rare case: the request was written in writeLoop above but // before it could send to pc.writeErrCh, the reader read it // all, processed it, and called us here. In this case, give the // write goroutine a bit of time to finish its send. // // Less rare case: We also get here in the legitimate case of // Issue 7569, where the writer is still writing (or stalled), // but the server has already replied. In this case, we don't // want to wait too long, and we want to return false so this // connection isn't re-used. := time.NewTimer(maxWriteWaitBeforeConnReuse)defer .Stop()select {case := <-.writeErrCh:return == nilcase<-.C:returnfalse } }}// responseAndError is how the goroutine reading from an HTTP/1 server// communicates with the goroutine doing the RoundTrip.type responseAndError struct { _ incomparable res *Response// else use this response (see res method) err error}type requestAndChan struct { _ incomparable treq *transportRequest ch chanresponseAndError// unbuffered; always send in select on callerGone// whether the Transport (as opposed to the user client code) // added the Accept-Encoding gzip header. If the Transport // set it, only then do we transparently decode the gzip. addedGzip bool// Optional blocking chan for Expect: 100-continue (for send). // If the request has an "Expect: 100-continue" header and // the server responds 100 Continue, readLoop send a value // to writeLoop via this chan. continueCh chan<- struct{} callerGone <-chanstruct{} // closed when roundTrip caller has returned}// A writeRequest is sent by the caller's goroutine to the// writeLoop's goroutine to write a request while the read loop// concurrently waits on both the write response and the server's// reply.type writeRequest struct { req *transportRequest ch chan<- error// Optional blocking chan for Expect: 100-continue (for receive). // If not nil, writeLoop blocks sending request body until // it receives from this chan. continueCh <-chanstruct{}}// httpTimeoutError represents a timeout.// It implements net.Error and wraps context.DeadlineExceeded.type timeoutError struct { err string}func ( *timeoutError) () string { return .err }func ( *timeoutError) () bool { returntrue }func ( *timeoutError) () bool { returntrue }func ( *timeoutError) ( error) bool { return == context.DeadlineExceeded }var errTimeout error = &timeoutError{"net/http: timeout awaiting response headers"}// errRequestCanceled is set to be identical to the one from h2 to facilitate// testing.var errRequestCanceled = http2errRequestCanceledvar errRequestCanceledConn = errors.New("net/http: request canceled while waiting for connection") // TODO: unify?// errRequestDone is used to cancel the round trip Context after a request is successfully done.// It should not be seen by the user.var errRequestDone = errors.New("net/http: request completed")func nop() {}// testHooks. Always non-nil.var ( testHookEnterRoundTrip = nop testHookWaitResLoop = nop testHookRoundTripRetried = nop testHookPrePendingDial = nop testHookPostPendingDial = nop testHookMu sync.Locker = fakeLocker{} // guards following testHookReadLoopBeforeNextRead = nop)func ( *persistConn) ( *transportRequest) ( *Response, error) {testHookEnterRoundTrip() .mu.Lock() .numExpectedResponses++ := .mutateHeaderFunc .mu.Unlock()if != nil { (.extraHeaders()) }// Ask for a compressed version if the caller didn't set their // own value for Accept-Encoding. We only attempt to // uncompress the gzip stream if we were the layer that // requested it. := falseif !.t.DisableCompression && .Header.Get("Accept-Encoding") == "" && .Header.Get("Range") == "" && .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: // https://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 = true .extraHeaders().Set("Accept-Encoding", "gzip") }varchanstruct{}if .ProtoAtLeast(1, 1) && .Body != nil && .expectsContinue() { = make(chanstruct{}, 1) }if .t.DisableKeepAlives && !.wantsClose() && !isProtocolSwitchHeader(.Header) { .extraHeaders().Set("Connection", "close") } := make(chanstruct{})deferclose()const = false// Write the request concurrently with waiting for a response, // in case the server decides to reply before reading our full // request body. := .nwrite := make(chanerror, 1) .writech <- writeRequest{, , } := make(chanresponseAndError) .reqch <- requestAndChan{treq: ,ch: ,addedGzip: ,continueCh: ,callerGone: , } := func( responseAndError) (*Response, error) {if (.res == nil) == (.err == nil) {panic(fmt.Sprintf("internal error: exactly one of res or err should be set; nil=%v", .res == nil)) }if { .logf("resc recv: %p, %T/%#v", .res, .err, .err) }if .err != nil {returnnil, .mapRoundTripError(, , .err) }return .res, nil }var <-chantime.Time := .ctx.Done() := .closechfor {testHookWaitResLoop()select {case := <-:if { .logf("writeErrCh recv: %T/%#v", , ) }if != nil { .close(fmt.Errorf("write error: %w", ))returnnil, .mapRoundTripError(, , ) }if := .t.ResponseHeaderTimeout; > 0 {if { .logf("starting timer for %v", ) } := time.NewTimer()defer .Stop() // prevent leaks = .C }case<-:select {case := <-:// The pconn closing raced with the response to the request, // probably after the server wrote a response and immediately // closed the connection. Use the response.return ()default: }if { .logf("closech recv: %T %#v", .closed, .closed) }returnnil, .mapRoundTripError(, , .closed)case<-:if { .logf("timeout waiting for response headers.") } .close(errTimeout)returnnil, errTimeoutcase := <-:return ()case<-:select {case := <-:// readLoop is responsible for canceling req.ctx after // it reads the response body. Check for a response racing // the context close, and use the response if available.return ()default: } .cancelRequest(context.Cause(.ctx)) } }}// tLogKey is a context WithValue key for test debugging contexts containing// a t.Logf func. See export_test.go's Request.WithT method.type tLogKey struct{}func ( *transportRequest) ( string, ...any) {if , := .Request.Context().Value(tLogKey{}).(func(string, ...any)); { (time.Now().Format(time.RFC3339Nano)+": "+, ...) }}// markReused marks this connection as having been successfully used for a// request and response.func ( *persistConn) () { .mu.Lock() .reused = true .mu.Unlock()}// close closes the underlying TCP connection and closes// the pc.closech channel.//// The provided err is only for testing and debugging; in normal// circumstances it should never be seen by users.func ( *persistConn) ( error) { .mu.Lock()defer .mu.Unlock() .closeLocked()}func ( *persistConn) ( error) {if == nil {panic("nil error") } .broken = trueif .closed == nil { .closed = .t.decConnsPerHost(.cacheKey)// Close HTTP/1 (pc.alt == nil) connection. // HTTP/2 closes its connection itself.if .alt == nil {if != errCallerOwnsConn { .conn.Close() }close(.closech) } } .mutateHeaderFunc = nil}var portMap = map[string]string{"http": "80","https": "443","socks5": "1080","socks5h": "1080",}func idnaASCIIFromURL( *url.URL) string { := .Hostname()if , := idnaASCII(); == nil { = }return}// canonicalAddr returns url.Host but always with a ":port" suffix.func canonicalAddr( *url.URL) string { := .Port()if == "" { = portMap[.Scheme] }returnnet.JoinHostPort(idnaASCIIFromURL(), )}// bodyEOFSignal is used by the HTTP/1 transport when reading response// bodies to make sure we see the end of a response body before// proceeding and reading on the connection again.//// It wraps a ReadCloser but runs fn (if non-nil) at most// once, right before its final (error-producing) Read or Close call// returns. fn should return the new error to return from Read or Close.//// If earlyCloseFn is non-nil and Close is called before io.EOF is// seen, earlyCloseFn is called instead of fn, and its return value is// the return value from Close.type bodyEOFSignal struct { body io.ReadCloser mu sync.Mutex// guards following 4 fields closed bool// whether Close has been called rerr error// sticky Read error fn func(error) error// err will be nil on Read io.EOF earlyCloseFn func() error// optional alt Close func used if io.EOF not seen}var errReadOnClosedResBody = errors.New("http: read on closed response body")func ( *bodyEOFSignal) ( []byte) ( int, error) { .mu.Lock() , := .closed, .rerr .mu.Unlock()if {return0, errReadOnClosedResBody }if != nil {return0, } , = .body.Read()if != nil { .mu.Lock()defer .mu.Unlock()if .rerr == nil { .rerr = } = .condfn() }return}func ( *bodyEOFSignal) () error { .mu.Lock()defer .mu.Unlock()if .closed {returnnil } .closed = trueif .earlyCloseFn != nil && .rerr != io.EOF {return .earlyCloseFn() } := .body.Close()return .condfn()}// caller must hold es.mu.func ( *bodyEOFSignal) ( error) error {if .fn == nil {return } = .fn() .fn = nilreturn}// gzipReader wraps a response body so it can lazily// call gzip.NewReader on the first call to Readtype gzipReader struct { _ incomparable body *bodyEOFSignal// underlying HTTP/1 response body framing zr *gzip.Reader// lazily-initialized gzip reader zerr error// any error from gzip.NewReader; sticky}func ( *gzipReader) ( []byte) ( int, error) {if .zr == nil {if .zerr == nil { .zr, .zerr = gzip.NewReader(.body) }if .zerr != nil {return0, .zerr } } .body.mu.Lock()if .body.closed { = errReadOnClosedResBody } .body.mu.Unlock()if != nil {return0, }return .zr.Read()}func ( *gzipReader) () error {return .body.Close()}type tlsHandshakeTimeoutError struct{}func (tlsHandshakeTimeoutError) () bool { returntrue }func (tlsHandshakeTimeoutError) () bool { returntrue }func (tlsHandshakeTimeoutError) () string { return"net/http: TLS handshake timeout" }// fakeLocker is a sync.Locker which does nothing. It's used to guard// test-only fields when not under test, to avoid runtime atomic// overhead.type fakeLocker struct{}func (fakeLocker) () {}func (fakeLocker) () {}// cloneTLSConfig returns a shallow clone of cfg, or a new zero tls.Config if// cfg is nil. This is safe to call even if cfg is in active use by a TLS// client or server.//// cloneTLSConfig should be an internal detail,// but widely used packages access it using linkname.// Notable members of the hall of shame include:// - github.com/searKing/golang//// Do not remove or change the type signature.// See go.dev/issue/67401.////go:linkname cloneTLSConfigfunc cloneTLSConfig( *tls.Config) *tls.Config {if == nil {return &tls.Config{} }return .Clone()}type connLRU struct { ll *list.List// list.Element.Value type is of *persistConn m map[*persistConn]*list.Element}// add adds pc to the head of the linked list.func ( *connLRU) ( *persistConn) {if .ll == nil { .ll = list.New() .m = make(map[*persistConn]*list.Element) } := .ll.PushFront()if , := .m[]; {panic("persistConn was already in LRU") } .m[] = }func ( *connLRU) () *persistConn { := .ll.Back() := .Value.(*persistConn) .ll.Remove()delete(.m, )return}// remove removes pc from cl.func ( *connLRU) ( *persistConn) {if , := .m[]; { .ll.Remove()delete(.m, ) }}// len returns the number of items in the cache.func ( *connLRU) () int {returnlen(.m)}
The pages are generated with Goldsv0.7.3. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.