1// Copyright 2015 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45// Transport code.67package http289import (10 "bufio"11 "bytes"12 "compress/flate"13 "compress/gzip"14 "context"15 "crypto/rand"16 "crypto/tls"17 "errors"18 "fmt"19 "io"20 "io/fs"21 "log"22 "math"23 "math/bits"24 mathrand "math/rand"25 "net"26 "net/http/httptrace"27 "net/http/internal"28 "net/http/internal/httpcommon"29 "net/textproto"30 "slices"31 "strconv"32 "strings"33 "sync"34 "sync/atomic"35 "time"3637 "golang.org/x/net/http/httpguts"38 "golang.org/x/net/http2/hpack"39 "golang.org/x/net/idna"40)4142const (43 // transportDefaultConnFlow is how many connection-level flow control44 // tokens we give the server at start-up, past the default 64k.45 transportDefaultConnFlow = 1 << 304647 // transportDefaultStreamFlow is how many stream-level flow48 // control tokens we announce to the peer, and how many bytes49 // we buffer per stream.50 transportDefaultStreamFlow = 4 << 205152 defaultUserAgent = "Go-http-client/2.0"5354 // initialMaxConcurrentStreams is a connections maxConcurrentStreams until55 // it's received servers initial SETTINGS frame, which corresponds with the56 // spec's minimum recommended value.57 initialMaxConcurrentStreams = 1005859 // defaultMaxConcurrentStreams is a connections default maxConcurrentStreams60 // if the server doesn't include one in its initial SETTINGS frame.61 defaultMaxConcurrentStreams = 100062)6364// Transport is an HTTP/2 Transport.65//66// A Transport internally caches connections to servers. It is safe67// for concurrent use by multiple goroutines.68type Transport struct {69 t1 TransportConfig70 connPool noDialClientConnPool71 *transportTestHooks72}7374// Hook points used for testing.75// Outside of tests, t.transportTestHooks is nil and these all have minimal implementations.76// Inside tests, see the testSyncHooks function docs.7778type transportTestHooks struct {79 newclientconn func(*ClientConn)80}8182func (t *Transport) maxHeaderListSize() uint32 {83 n := t.t1.MaxHeaderListSize()84 if b := t.t1.MaxResponseHeaderBytes(); b != 0 {85 n = b86 if n > 0 {87 n = adjustHTTP1MaxHeaderSize(n)88 }89 }90 if n <= 0 {91 return 10 << 2092 }93 if n >= 0xffffffff {94 return 095 }96 return uint32(n)97}9899func (t *Transport) disableCompression() bool {100 return t.t1 != nil && t.t1.DisableCompression()101}102103func NewTransport(t1 TransportConfig) *Transport {104 connPool := new(clientConnPool)105 t2 := &Transport{106 connPool: noDialClientConnPool{connPool},107 t1: t1,108 }109 connPool.t = t2110 return t2111}112113func (t *Transport) AddConn(scheme, authority string, c net.Conn) error {114 addr := authorityAddr(scheme, authority)115 used, err := t.connPool.addConnIfNeeded(addr, t, c)116 if !used {117 go c.Close()118 }119 return err120}121122// unencryptedTransport is a Transport with a RoundTrip method that123// always permits http:// URLs.124type unencryptedTransport Transport125126func (t *unencryptedTransport) RoundTrip(req *ClientRequest) (*ClientResponse, error) {127 return (*Transport)(t).RoundTripOpt(req, RoundTripOpt{})128}129130// ClientConn is the state of a single HTTP/2 client connection to an131// HTTP/2 server.132type ClientConn struct {133 t *Transport134 tconn net.Conn // usually *tls.Conn, except specialized impls135 tlsState *tls.ConnectionState // nil only for specialized impls136 atomicReused uint32 // whether conn is being reused; atomic137 singleUse bool // whether being used for a single http.Request138 getConnCalled bool // used by clientConnPool139140 // readLoop goroutine fields:141 readerDone chan struct{} // closed on error142 readerErr error // set before readerDone is closed143144 idleTimeout time.Duration // or 0 for never145 idleTimer *time.Timer146147 mu sync.Mutex // guards following148 cond *sync.Cond // hold mu; broadcast on flow/closed changes149 flow outflow // our conn-level flow control quota (cs.outflow is per stream)150 inflow inflow // peer's conn-level flow control151 doNotReuse bool // whether conn is marked to not be reused for any future requests152 closing bool153 closed bool154 closedOnIdle bool // true if conn was closed for idleness155 seenSettings bool // true if we've seen a settings frame, false otherwise156 seenSettingsChan chan struct{} // closed when seenSettings is true or frame reading fails157 wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back158 goAway *GoAwayFrame // if non-nil, the GoAwayFrame we received159 goAwayDebug string // goAway frame's debug data, retained as a string160 streams map[uint32]*clientStream // client-initiated161 streamsReserved int // incr by ReserveNewRequest; decr on RoundTrip162 nextStreamID uint32163 pendingRequests int // requests blocked and waiting to be sent because len(streams) == maxConcurrentStreams164 pings map[[8]byte]chan struct{} // in flight ping data to notification channel165 br *bufio.Reader166 lastActive time.Time167 lastIdle time.Time // time last idle168 // Settings from peer: (also guarded by wmu)169 maxFrameSize uint32170 maxConcurrentStreams uint32171 peerMaxHeaderListSize uint64172 peerMaxHeaderTableSize uint32173 initialWindowSize uint32174 initialStreamRecvWindowSize int32175 readIdleTimeout time.Duration176 pingTimeout time.Duration177 extendedConnectAllowed bool178 strictMaxConcurrentStreams bool179180 // rstStreamPingsBlocked works around an unfortunate gRPC behavior.181 // gRPC strictly limits the number of PING frames that it will receive.182 // The default is two pings per two hours, but the limit resets every time183 // the gRPC endpoint sends a HEADERS or DATA frame. See golang/go#70575.184 //185 // rstStreamPingsBlocked is set after receiving a response to a PING frame186 // bundled with an RST_STREAM (see pendingResets below), and cleared after187 // receiving a HEADERS or DATA frame.188 rstStreamPingsBlocked bool189190 // pendingResets is the number of RST_STREAM frames we have sent to the peer,191 // without confirming that the peer has received them. When we send a RST_STREAM,192 // we bundle it with a PING frame, unless a PING is already in flight. We count193 // the reset stream against the connection's concurrency limit until we get194 // a PING response. This limits the number of requests we'll try to send to a195 // completely unresponsive connection.196 pendingResets int197198 // readBeforeStreamID is the smallest stream ID that has not been followed by199 // a frame read from the peer. We use this to determine when a request may200 // have been sent to a completely unresponsive connection:201 // If the request ID is less than readBeforeStreamID, then we have had some202 // indication of life on the connection since sending the request.203 readBeforeStreamID uint32204205 // reqHeaderMu is a 1-element semaphore channel controlling access to sending new requests.206 // Write to reqHeaderMu to lock it, read from it to unlock.207 // Lock reqmu BEFORE mu or wmu.208 reqHeaderMu chan struct{}209210 // internalStateHook reports state changes back to the net/http.ClientConn.211 // Note that this is different from the user state hook registered by212 // net/http.ClientConn.SetStateHook: The internal hook calls ClientConn,213 // which calls the user hook.214 internalStateHook func()215216 // wmu is held while writing.217 // Acquire BEFORE mu when holding both, to avoid blocking mu on network writes.218 // Only acquire both at the same time when changing peer settings.219 wmu sync.Mutex220 bw *bufio.Writer221 fr *Framer222 werr error // first write error that has occurred223 hbuf bytes.Buffer // HPACK encoder writes into this224 henc *hpack.Encoder225}226227// clientStream is the state for a single HTTP/2 stream. One of these228// is created for each Transport.RoundTrip call.229type clientStream struct {230 cc *ClientConn231232 // Fields of Request that we may access even after the response body is closed.233 ctx context.Context234 reqCancel <-chan struct{}235236 trace *httptrace.ClientTrace // or nil237 ID uint32238 bufPipe pipe // buffered pipe with the flow-controlled response payload239 requestedGzip bool240 isHead bool241242 abortOnce sync.Once243 abort chan struct{} // closed to signal stream should end immediately244 abortErr error // set if abort is closed245246 peerClosed chan struct{} // closed when the peer sends an END_STREAM flag247 donec chan struct{} // closed after the stream is in the closed state248 on100 chan struct{} // buffered; written to if a 100 is received249250 // detached, guarded by cc.mu, indicates that the writeRequest251 // goroutine has exited without waiting for the stream to end, and252 // that cleanupWriteRequest should instead be run (on a new goroutine)253 // by whichever of abortStreamLocked or clientConnReadLoop.endStream254 // ends the stream. It is cleared when that cleanup is scheduled.255 // See clientStream.detach.256 detached bool257258 // stopCtxWatch, if non-nil, cancels the context.AfterFunc watching259 // for request context cancellation on behalf of a detached stream.260 // It is set (under cc.mu) at most once, by detach, before detached261 // is set, and is called by cleanupWriteRequest.262 stopCtxWatch func() bool263264 // respHeaderTimeoutTimer, guarded by cc.mu, is a timer enforcing265 // Transport.ResponseHeaderTimeout on behalf of a detached stream.266 // It is armed by detach if response headers haven't yet arrived, and267 // stopped when they do (clientConnReadLoop.processHeaders) or when268 // the stream ends (cleanupWriteRequest).269 respHeaderTimeoutTimer *time.Timer270271 respHeaderRecv chan struct{} // closed when headers are received272 res *ClientResponse // set if respHeaderRecv is closed273274 flow outflow // guarded by cc.mu275 inflow inflow // guarded by cc.mu276 bytesRemain int64 // -1 means unknown; owned by transportResponseBody.Read277 readErr error // sticky read error; owned by transportResponseBody.Read278279 reqBody io.ReadCloser280 reqBodyContentLength int64 // -1 means unknown281 reqBodyClosed chan struct{} // guarded by cc.mu; non-nil on Close, closed when done282283 // owned by writeRequest:284 sentEndStream bool // sent an END_STREAM flag to the peer285 sentHeaders bool286287 // owned by clientConnReadLoop:288 firstByte bool // got the first response byte289 pastHeaders bool // got first MetaHeadersFrame (actual headers)290 pastTrailers bool // got optional second MetaHeadersFrame (trailers)291 readClosed bool // peer sent an END_STREAM flag292 readAborted bool // read loop reset the stream293 totalHeaderSize int64 // total size of 1xx headers seen294295 trailer Header // accumulated trailers296 resTrailer *Header // client's Response.Trailer297298 staticResp ClientResponse299}300301var got1xxFuncForTests func(int, textproto.MIMEHeader) error302303// get1xxTraceFunc returns the value of request's httptrace.ClientTrace.Got1xxResponse func,304// if any. It returns nil if not set or if the Go version is too old.305func (cs *clientStream) get1xxTraceFunc() func(int, textproto.MIMEHeader) error {306 if fn := got1xxFuncForTests; fn != nil {307 return fn308 }309 return traceGot1xxResponseFunc(cs.trace)310}311312func (cs *clientStream) abortStream(err error) {313 cs.cc.mu.Lock()314 defer cs.cc.mu.Unlock()315 cs.abortStreamLocked(err)316}317318func (cs *clientStream) abortStreamLocked(err error) {319 cs.abortOnce.Do(func() {320 cs.abortErr = err321 close(cs.abort)322 })323 if cs.detached {324 cs.detached = false325 go cs.cleanupWriteRequest(cs.abortErr)326 }327 if cs.reqBody != nil {328 cs.closeReqBodyLocked()329 }330 // TODO(dneil): Clean up tests where cs.cc.cond is nil.331 if cs.cc.cond != nil {332 // Wake up writeRequestBody if it is waiting on flow control.333 cs.cc.cond.Broadcast()334 }335}336337func (cs *clientStream) abortRequestBodyWrite() {338 cc := cs.cc339 cc.mu.Lock()340 defer cc.mu.Unlock()341 if cs.reqBody != nil && cs.reqBodyClosed == nil {342 cs.closeReqBodyLocked()343 cc.cond.Broadcast()344 }345}346347func (cs *clientStream) closeReqBodyLocked() {348 if cs.reqBodyClosed != nil {349 return350 }351 cs.reqBodyClosed = make(chan struct{})352 reqBodyClosed := cs.reqBodyClosed353 go func() {354 cs.reqBody.Close()355 close(reqBodyClosed)356 }()357}358359type stickyErrWriter struct {360 conn net.Conn361 timeout time.Duration362 err *error363}364365func (sew stickyErrWriter) Write(p []byte) (n int, err error) {366 if *sew.err != nil {367 return 0, *sew.err368 }369 n, err = writeWithByteTimeout(sew.conn, sew.timeout, p)370 *sew.err = err371 return n, err372}373374// noCachedConnError is the concrete type of ErrNoCachedConn, which375// needs to be detected by net/http regardless of whether it's its376// bundled version (in h2_bundle.go with a rewritten type name) or377// from a user's x/net/http2. As such, as it has a unique method name378// (IsHTTP2NoCachedConnError) that net/http sniffs for via func379// isNoCachedConnError.380type noCachedConnError struct{}381382func (noCachedConnError) IsHTTP2NoCachedConnError() {}383func (noCachedConnError) Error() string { return "http2: no cached connection was available" }384385// isNoCachedConnError reports whether err is of type noCachedConnError386// or its equivalent renamed type in net/http2's h2_bundle.go. Both types387// may coexist in the same running program.388func isNoCachedConnError(err error) bool {389 _, ok := err.(interface{ IsHTTP2NoCachedConnError() })390 return ok391}392393var ErrNoCachedConn error = noCachedConnError{}394395// RoundTripOpt are options for the Transport.RoundTripOpt method.396type RoundTripOpt struct {397 // OnlyCachedConn controls whether RoundTripOpt may398 // create a new TCP connection. If set true and399 // no cached connection is available, RoundTripOpt400 // will return ErrNoCachedConn.401 OnlyCachedConn bool402}403404func (t *Transport) RoundTrip(req *ClientRequest) (*ClientResponse, error) {405 return t.RoundTripOpt(req, RoundTripOpt{})406}407408// authorityAddr returns a given authority (a host/IP, or host:port / ip:port)409// and returns a host:port. The port 443 is added if needed.410func authorityAddr(scheme string, authority string) (addr string) {411 host, port, err := net.SplitHostPort(authority)412 if err != nil { // authority didn't have a port413 host = authority414 port = ""415 }416 if port == "" { // authority's port was empty417 port = "443"418 if scheme == "http" {419 port = "80"420 }421 }422 if a, err := idna.ToASCII(host); err == nil {423 host = a424 }425 // IPv6 address literal, without a port:426 if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {427 return host + ":" + port428 }429 return net.JoinHostPort(host, port)430}431432// RoundTripOpt is like RoundTrip, but takes options.433func (t *Transport) RoundTripOpt(req *ClientRequest, opt RoundTripOpt) (*ClientResponse, error) {434 switch req.URL.Scheme {435 case "https":436 case "http":437 default:438 return nil, errors.New("http2: unsupported scheme")439 }440441 addr := authorityAddr(req.URL.Scheme, req.URL.Host)442 for retry := 0; ; retry++ {443 cc, err := t.connPool.GetClientConn(req, addr)444 if err != nil {445 t.vlogf("http2: Transport failed to get client conn for %s: %v", addr, err)446 return nil, err447 }448 reused := !atomic.CompareAndSwapUint32(&cc.atomicReused, 0, 1)449 traceGotConn(req, cc, reused)450 res, err := cc.RoundTrip(req)451 if err != nil && retry <= 6 {452 roundTripErr := err453 if req, err = shouldRetryRequest(req, err); err == nil {454 // After the first retry, do exponential backoff with 10% jitter.455 if retry == 0 {456 t.vlogf("RoundTrip retrying after failure: %v", roundTripErr)457 continue458 }459 backoff := float64(uint(1) << (uint(retry) - 1))460 backoff += backoff * (0.1 * mathrand.Float64())461 d := time.Second * time.Duration(backoff)462 tm := time.NewTimer(d)463 select {464 case <-tm.C:465 t.vlogf("RoundTrip retrying after failure: %v", roundTripErr)466 continue467 case <-req.Context.Done():468 tm.Stop()469 err = req.Context.Err()470 }471 }472 }473 if err == errClientConnNotEstablished {474 // This ClientConn was created recently,475 // this is the first request to use it,476 // and the connection is closed and not usable.477 //478 // In this state, cc.idleTimer will remove the conn from the pool479 // when it fires. Stop the timer and remove it here so future requests480 // won't try to use this connection.481 //482 // If the timer has already fired and we're racing it, the redundant483 // call to MarkDead is harmless.484 if cc.idleTimer != nil {485 cc.idleTimer.Stop()486 }487 t.connPool.MarkDead(cc)488 }489 if err != nil {490 t.vlogf("RoundTrip failure: %v", err)491 return nil, err492 }493 return res, nil494 }495}496497func (t *Transport) IdleConnStrsForTesting() []string {498 var ret []string499 t.connPool.mu.Lock()500 defer t.connPool.mu.Unlock()501 for k, ccs := range t.connPool.conns {502 for _, cc := range ccs {503 if cc.idleState().canTakeNewRequest {504 ret = append(ret, k)505 }506 }507 }508 slices.Sort(ret)509 return ret510}511512// CloseIdleConnections closes any connections which were previously513// connected from previous requests but are now sitting idle.514// It does not interrupt any connections currently in use.515func (t *Transport) CloseIdleConnections() {516 t.connPool.closeIdleConnections()517}518519var (520 errClientConnClosed = errors.New("http2: client conn is closed")521 errClientConnUnusable = errors.New("http2: client conn not usable")522 errClientConnNotEstablished = errors.New("http2: client conn could not be established")523 errClientConnGotGoAway = errors.New("http2: Transport received Server's graceful shutdown GOAWAY")524 errClientConnForceClosed = errors.New("http2: client connection force closed via ClientConn.Close")525)526527// shouldRetryRequest is called by RoundTrip when a request fails to get528// response headers. It is always called with a non-nil error.529// It returns either a request to retry or an error if the request can't be replayed.530// If the request is retried, it always clones the request (since requests531// contain an unreusable clientStream).532func shouldRetryRequest(req *ClientRequest, err error) (*ClientRequest, error) {533 if !canRetryError(err) {534 return nil, err535 }536 // If the Body is nil (or http.NoBody), it's safe to reuse this request's Body.537 if req.Body == nil || req.Body == NoBody {538 return req.Clone(), nil539 }540541 // If the request body can be reset back to its original542 // state via the optional req.GetBody, do that.543 if req.GetBody != nil {544 body, err := req.GetBody()545 if err != nil {546 return nil, err547 }548 newReq := req.Clone()549 newReq.Body = body550 return newReq, nil551 }552553 // The Request.Body can't reset back to the beginning, but we554 // don't seem to have started to read from it yet, so reuse the body.555 if err == errClientConnUnusable {556 return req.Clone(), nil557 }558559 return nil, fmt.Errorf("http2: Transport: cannot retry err [%v] after Request.Body was written; define Request.GetBody to avoid this error", err)560}561562func canRetryError(err error) bool {563 if err == errClientConnUnusable || err == errClientConnGotGoAway {564 return true565 }566 if se, ok := err.(StreamError); ok {567 return se.Code == ErrCodeRefusedStream568 }569 return false570}571572func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse bool) (*ClientConn, error) {573 if t.transportTestHooks != nil {574 return t.newClientConn(nil, singleUse, nil)575 }576 host, _, err := net.SplitHostPort(addr)577 if err != nil {578 return nil, err579 }580 tconn, err := t.dialTLS(ctx, "tcp", addr, t.newTLSConfig(host))581 if err != nil {582 return nil, err583 }584 return t.newClientConn(tconn, singleUse, nil)585}586587func (t *Transport) newTLSConfig(host string) *tls.Config {588 cfg := new(tls.Config)589 if !slices.Contains(cfg.NextProtos, NextProtoTLS) {590 cfg.NextProtos = append([]string{NextProtoTLS}, cfg.NextProtos...)591 }592 if cfg.ServerName == "" {593 cfg.ServerName = host594 }595 return cfg596}597598func (t *Transport) dialTLS(ctx context.Context, network, addr string, tlsCfg *tls.Config) (net.Conn, error) {599 tlsCn, err := t.dialTLSWithContext(ctx, network, addr, tlsCfg)600 if err != nil {601 return nil, err602 }603 state := tlsCn.ConnectionState()604 if p := state.NegotiatedProtocol; p != NextProtoTLS {605 return nil, fmt.Errorf("http2: unexpected ALPN protocol %q; want %q", p, NextProtoTLS)606 }607 if !state.NegotiatedProtocolIsMutual {608 return nil, errors.New("http2: could not negotiate protocol mutually")609 }610 return tlsCn, nil611}612613// disableKeepAlives reports whether connections should be closed as614// soon as possible after handling the first request.615func (t *Transport) disableKeepAlives() bool {616 return t.t1 != nil && t.t1.DisableKeepAlives()617}618619func (t *Transport) expectContinueTimeout() time.Duration {620 if t.t1 == nil {621 return 0622 }623 return t.t1.ExpectContinueTimeout()624}625626func (t *Transport) NewClientConn(c net.Conn, internalStateHook func()) (NetHTTPClientConn, error) {627 cc, err := t.newClientConn(c, t.disableKeepAlives(), internalStateHook)628 if err != nil {629 return NetHTTPClientConn{}, err630 }631632 // RoundTrip should block when the conn is at its concurrency limit,633 // not return an error. Setting strictMaxConcurrentStreams enables this.634 cc.strictMaxConcurrentStreams = true635636 return NetHTTPClientConn{cc}, nil637}638639func (t *Transport) newClientConn(c net.Conn, singleUse bool, internalStateHook func()) (*ClientConn, error) {640 conf := configFromTransport(t)641 cc := &ClientConn{642 t: t,643 tconn: c,644 readerDone: make(chan struct{}),645 nextStreamID: 1,646 maxFrameSize: 16 << 10, // spec default647 initialWindowSize: 65535, // spec default648 initialStreamRecvWindowSize: int32(conf.MaxReceiveBufferPerStream),649 maxConcurrentStreams: initialMaxConcurrentStreams, // "infinite", per spec. Use a smaller value until we have received server settings.650 strictMaxConcurrentStreams: conf.StrictMaxConcurrentRequests,651 peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead.652 streams: make(map[uint32]*clientStream),653 singleUse: singleUse,654 seenSettingsChan: make(chan struct{}),655 wantSettingsAck: true,656 readIdleTimeout: conf.SendPingTimeout,657 pingTimeout: conf.PingTimeout,658 pings: make(map[[8]byte]chan struct{}),659 reqHeaderMu: make(chan struct{}, 1),660 lastActive: time.Now(),661 internalStateHook: internalStateHook,662 }663 if t.transportTestHooks != nil {664 t.transportTestHooks.newclientconn(cc)665 c = cc.tconn666 }667 if VerboseLogs {668 t.vlogf("http2: Transport creating client conn %p to %v", cc, c.RemoteAddr())669 }670671 cc.cond = sync.NewCond(&cc.mu)672 cc.flow.add(int32(initialWindowSize))673674 // TODO: adjust this writer size to account for frame size +675 // MTU + crypto/tls record padding.676 cc.bw = bufio.NewWriter(stickyErrWriter{677 conn: c,678 timeout: conf.WriteByteTimeout,679 err: &cc.werr,680 })681 cc.br = bufio.NewReader(c)682 cc.fr = NewFramer(cc.bw, cc.br)683 cc.fr.SetMaxReadFrameSize(uint32(conf.MaxReadFrameSize))684 if conf.CountError != nil {685 cc.fr.countError = conf.CountError686 }687 maxHeaderTableSize := uint32(conf.MaxDecoderHeaderTableSize)688 cc.fr.ReadMetaHeaders = hpack.NewDecoder(maxHeaderTableSize, nil)689 cc.fr.MaxHeaderListSize = t.maxHeaderListSize()690691 cc.henc = hpack.NewEncoder(&cc.hbuf)692 cc.henc.SetMaxDynamicTableSizeLimit(uint32(conf.MaxEncoderHeaderTableSize))693 cc.peerMaxHeaderTableSize = initialHeaderTableSize694695 if cs, ok := c.(connectionStater); ok {696 state := cs.ConnectionState()697 cc.tlsState = &state698 }699700 initialSettings := []Setting{701 {ID: SettingEnablePush, Val: 0},702 {ID: SettingInitialWindowSize, Val: uint32(cc.initialStreamRecvWindowSize)},703 }704 initialSettings = append(initialSettings, Setting{ID: SettingMaxFrameSize, Val: uint32(conf.MaxReadFrameSize)})705 if max := t.maxHeaderListSize(); max != 0 {706 initialSettings = append(initialSettings, Setting{ID: SettingMaxHeaderListSize, Val: max})707 }708 if maxHeaderTableSize != initialHeaderTableSize {709 initialSettings = append(initialSettings, Setting{ID: SettingHeaderTableSize, Val: maxHeaderTableSize})710 }711712 cc.bw.Write(clientPreface)713 cc.fr.WriteSettings(initialSettings...)714 cc.fr.WriteWindowUpdate(0, uint32(conf.MaxReceiveBufferPerConnection))715 cc.inflow.init(int32(conf.MaxReceiveBufferPerConnection) + initialWindowSize)716 cc.bw.Flush()717 if cc.werr != nil {718 cc.Close()719 return nil, cc.werr720 }721722 // Start the idle timer after the connection is fully initialized.723 if d := t.idleConnTimeout(); d != 0 {724 cc.idleTimeout = d725 cc.idleTimer = time.AfterFunc(d, cc.onIdleTimeout)726 }727728 go cc.readLoop()729 return cc, nil730}731732func (cc *ClientConn) healthCheck() {733 pingTimeout := cc.pingTimeout734 // We don't need to periodically ping in the health check, because the readLoop of ClientConn will735 // trigger the healthCheck again if there is no frame received.736 ctx, cancel := context.WithTimeout(context.Background(), pingTimeout)737 defer cancel()738 cc.vlogf("http2: Transport sending health check")739 err := cc.Ping(ctx)740 if err != nil {741 cc.vlogf("http2: Transport health check failure: %v", err)742 cc.closeForLostPing()743 } else {744 cc.vlogf("http2: Transport health check success")745 }746}747748// SetDoNotReuse marks cc as not reusable for future HTTP requests.749func (cc *ClientConn) SetDoNotReuse() {750 cc.mu.Lock()751 defer cc.mu.Unlock()752 cc.doNotReuse = true753}754755// CanTakeNewRequest reports whether the connection can take a new request,756// meaning it has not been closed or received or sent a GOAWAY.757//758// If the caller is going to immediately make a new request on this759// connection, use ReserveNewRequest instead.760func (cc *ClientConn) CanTakeNewRequest() bool {761 cc.mu.Lock()762 defer cc.mu.Unlock()763 return cc.canTakeNewRequestLocked()764}765766// ReserveNewRequest is like CanTakeNewRequest but also reserves a767// concurrent stream in cc. The reservation is decremented on the768// next call to RoundTrip.769func (cc *ClientConn) ReserveNewRequest() bool {770 cc.mu.Lock()771 defer cc.mu.Unlock()772 if st := cc.idleStateLocked(); !st.canTakeNewRequest {773 return false774 }775 cc.streamsReserved++776 return true777}778779// ClientConnState describes the state of a ClientConn.780type ClientConnState struct {781 // Closed is whether the connection is closed.782 Closed bool783784 // Closing is whether the connection is in the process of785 // closing. It may be closing due to shutdown, being a786 // single-use connection, being marked as DoNotReuse, or787 // having received a GOAWAY frame.788 Closing bool789790 // StreamsActive is how many streams are active.791 StreamsActive int792793 // StreamsReserved is how many streams have been reserved via794 // ClientConn.ReserveNewRequest.795 StreamsReserved int796797 // StreamsPending is how many requests have been sent in excess798 // of the peer's advertised MaxConcurrentStreams setting and799 // are waiting for other streams to complete.800 StreamsPending int801802 // MaxConcurrentStreams is how many concurrent streams the803 // peer advertised as acceptable. Zero means no SETTINGS804 // frame has been received yet.805 MaxConcurrentStreams uint32806807 // LastIdle, if non-zero, is when the connection last808 // transitioned to idle state.809 LastIdle time.Time810}811812// State returns a snapshot of cc's state.813func (cc *ClientConn) State() ClientConnState {814 cc.wmu.Lock()815 maxConcurrent := cc.maxConcurrentStreams816 if !cc.seenSettings {817 maxConcurrent = 0818 }819 cc.wmu.Unlock()820821 cc.mu.Lock()822 defer cc.mu.Unlock()823 return ClientConnState{824 Closed: cc.closed,825 Closing: cc.closing || cc.singleUse || cc.doNotReuse || cc.goAway != nil,826 StreamsActive: len(cc.streams) + cc.pendingResets,827 StreamsReserved: cc.streamsReserved,828 StreamsPending: cc.pendingRequests,829 LastIdle: cc.lastIdle,830 MaxConcurrentStreams: maxConcurrent,831 }832}833834// clientConnIdleState describes the suitability of a client835// connection to initiate a new RoundTrip request.836type clientConnIdleState struct {837 canTakeNewRequest bool838}839840func (cc *ClientConn) idleState() clientConnIdleState {841 cc.mu.Lock()842 defer cc.mu.Unlock()843 return cc.idleStateLocked()844}845846func (cc *ClientConn) idleStateLocked() (st clientConnIdleState) {847 if cc.singleUse && cc.nextStreamID > 1 {848 return849 }850 var maxConcurrentOkay bool851 if cc.strictMaxConcurrentStreams {852 // We'll tell the caller we can take a new request to853 // prevent the caller from dialing a new TCP854 // connection, but then we'll block later before855 // writing it.856 maxConcurrentOkay = true857 } else {858 // We can take a new request if the total of859 // - active streams;860 // - reservation slots for new streams; and861 // - streams for which we have sent a RST_STREAM and a PING,862 // but received no subsequent frame863 // is less than the concurrency limit.864 maxConcurrentOkay = cc.currentRequestCountLocked() < int(cc.maxConcurrentStreams)865 }866867 st.canTakeNewRequest = maxConcurrentOkay && cc.isUsableLocked()868869 // If this connection has never been used for a request and is closed,870 // then let it take a request (which will fail).871 // If the conn was closed for idleness, we're racing the idle timer;872 // don't try to use the conn. (Issue #70515.)873 //874 // This avoids a situation where an error early in a connection's lifetime875 // goes unreported.876 if cc.nextStreamID == 1 && cc.streamsReserved == 0 && cc.closed && !cc.closedOnIdle {877 st.canTakeNewRequest = true878 }879880 return881}882883func (cc *ClientConn) isUsableLocked() bool {884 return cc.goAway == nil &&885 !cc.closed &&886 !cc.closing &&887 !cc.doNotReuse &&888 int64(cc.nextStreamID)+2*int64(cc.pendingRequests) < math.MaxInt32 &&889 !cc.tooIdleLocked()890}891892// canReserveLocked reports whether a net/http.ClientConn can reserve a slot on this conn.893//894// This follows slightly different rules than clientConnIdleState.canTakeNewRequest.895// We only permit reservations up to the conn's concurrency limit.896// This differs from ClientConn.ReserveNewRequest, which permits reservations897// past the limit when StrictMaxConcurrentStreams is set.898func (cc *ClientConn) canReserveLocked() bool {899 if cc.currentRequestCountLocked() >= int(cc.maxConcurrentStreams) {900 return false901 }902 if !cc.isUsableLocked() {903 return false904 }905 return true906}907908// currentRequestCountLocked reports the number of concurrency slots currently in use,909// including active streams, reserved slots, and reset streams waiting for acknowledgement.910func (cc *ClientConn) currentRequestCountLocked() int {911 return len(cc.streams) + cc.streamsReserved + cc.pendingResets912}913914func (cc *ClientConn) canTakeNewRequestLocked() bool {915 st := cc.idleStateLocked()916 return st.canTakeNewRequest917}918919// availableLocked reports the number of concurrency slots available.920func (cc *ClientConn) availableLocked() int {921 if !cc.canTakeNewRequestLocked() {922 return 0923 }924 return max(0, int(cc.maxConcurrentStreams)-cc.currentRequestCountLocked())925}926927// tooIdleLocked reports whether this connection has been been sitting idle928// for too much wall time.929func (cc *ClientConn) tooIdleLocked() bool {930 // The Round(0) strips the monontonic clock reading so the931 // times are compared based on their wall time. We don't want932 // to reuse a connection that's been sitting idle during933 // VM/laptop suspend if monotonic time was also frozen.934 return cc.idleTimeout != 0 && !cc.lastIdle.IsZero() && time.Since(cc.lastIdle.Round(0)) > cc.idleTimeout935}936937// onIdleTimeout is called from a time.AfterFunc goroutine. It will938// only be called when we're idle, but because we're coming from a new939// goroutine, there could be a new request coming in at the same time,940// so this simply calls the synchronized closeIfIdle to shut down this941// connection. The timer could just call closeIfIdle, but this is more942// clear.943func (cc *ClientConn) onIdleTimeout() {944 cc.closeIfIdle()945}946947func (cc *ClientConn) closeConn() {948 t := time.AfterFunc(250*time.Millisecond, cc.forceCloseConn)949 defer t.Stop()950 cc.tconn.Close()951 cc.maybeCallStateHook()952}953954// A tls.Conn.Close can hang for a long time if the peer is unresponsive.955// Try to shut it down more aggressively.956func (cc *ClientConn) forceCloseConn() {957 tc, ok := cc.tconn.(*tls.Conn)958 if !ok {959 return960 }961 if nc := tc.NetConn(); nc != nil {962 nc.Close()963 }964}965966func (cc *ClientConn) closeIfIdle() {967 cc.mu.Lock()968 if len(cc.streams) > 0 || cc.streamsReserved > 0 {969 cc.mu.Unlock()970 return971 }972 cc.closed = true973 cc.closedOnIdle = true974 nextID := cc.nextStreamID975 // TODO: do clients send GOAWAY too? maybe? Just Close:976 cc.mu.Unlock()977978 if VerboseLogs {979 cc.vlogf("http2: Transport closing idle conn %p (forSingleUse=%v, maxStream=%v)", cc, cc.singleUse, nextID-2)980 }981 cc.closeConn()982}983984func (cc *ClientConn) isDoNotReuseAndIdle() bool {985 cc.mu.Lock()986 defer cc.mu.Unlock()987 return cc.doNotReuse && len(cc.streams) == 0988}989990var shutdownEnterWaitStateHook = func() {}991992// Shutdown gracefully closes the client connection, waiting for running streams to complete.993func (cc *ClientConn) Shutdown(ctx context.Context) error {994 if err := cc.sendGoAway(); err != nil {995 return err996 }997 // Wait for all in-flight streams to complete or connection to close998 done := make(chan struct{})999 cancelled := false // guarded by cc.mu1000 go func() {1001 cc.mu.Lock()1002 defer cc.mu.Unlock()1003 for {1004 if len(cc.streams) == 0 || cc.closed {1005 cc.closed = true1006 close(done)1007 break1008 }1009 if cancelled {1010 break1011 }1012 cc.cond.Wait()1013 }1014 }()1015 shutdownEnterWaitStateHook()1016 select {1017 case <-done:1018 cc.closeConn()1019 return nil1020 case <-ctx.Done():1021 cc.mu.Lock()1022 // Free the goroutine above1023 cancelled = true1024 cc.cond.Broadcast()1025 cc.mu.Unlock()1026 return ctx.Err()1027 }1028}10291030func (cc *ClientConn) sendGoAway() error {1031 cc.mu.Lock()1032 closing := cc.closing1033 cc.closing = true1034 maxStreamID := cc.nextStreamID1035 cc.mu.Unlock()1036 if closing {1037 // GOAWAY sent already1038 return nil1039 }10401041 cc.wmu.Lock()1042 defer cc.wmu.Unlock()1043 // Send a graceful shutdown frame to server1044 if err := cc.fr.WriteGoAway(maxStreamID, ErrCodeNo, nil); err != nil {1045 return err1046 }1047 if err := cc.bw.Flush(); err != nil {1048 return err1049 }1050 // Prevent new requests1051 return nil1052}10531054// closes the client connection immediately. In-flight requests are interrupted.1055// err is sent to streams.1056func (cc *ClientConn) closeForError(err error) {1057 cc.mu.Lock()1058 cc.closed = true1059 for _, cs := range cc.streams {1060 cs.abortStreamLocked(err)1061 }1062 cc.cond.Broadcast()1063 cc.mu.Unlock()1064 cc.closeConn()1065}10661067// Close closes the client connection immediately.1068//1069// In-flight requests are interrupted. For a graceful shutdown, use Shutdown instead.1070func (cc *ClientConn) Close() error {1071 cc.closeForError(errClientConnForceClosed)1072 return nil1073}10741075// closes the client connection immediately. In-flight requests are interrupted.1076func (cc *ClientConn) closeForLostPing() {1077 err := errors.New("http2: client connection lost")1078 if f := cc.fr.countError; f != nil {1079 f("conn_close_lost_ping")1080 }1081 cc.closeForError(err)1082}10831084// errRequestCanceled is a copy of net/http's errRequestCanceled because it's not1085// exported. At least they'll be DeepEqual for h1-vs-h2 comparisons tests.1086var errRequestCanceled = internal.ErrRequestCanceled10871088func (cc *ClientConn) responseHeaderTimeout() time.Duration {1089 if cc.t.t1 != nil {1090 return cc.t.t1.ResponseHeaderTimeout()1091 }1092 // No way to do this (yet?) with just an http2.Transport. Probably1093 // no need. Request.Cancel this is the new way. We only need to support1094 // this for compatibility with the old http.Transport fields when1095 // we're doing transparent http2.1096 return 01097}10981099// actualContentLength returns a sanitized version of1100// req.ContentLength, where 0 actually means zero (not unknown) and -11101// means unknown.1102func actualContentLength(req *ClientRequest) int64 {1103 if req.Body == nil || req.Body == NoBody {1104 return 01105 }1106 if req.ContentLength != 0 {1107 return req.ContentLength1108 }1109 return -11110}11111112func (cc *ClientConn) decrStreamReservations() {1113 cc.mu.Lock()1114 defer cc.mu.Unlock()1115 cc.decrStreamReservationsLocked()1116}11171118func (cc *ClientConn) decrStreamReservationsLocked() {1119 if cc.streamsReserved > 0 {1120 cc.streamsReserved--1121 }1122}11231124func (cc *ClientConn) RoundTrip(req *ClientRequest) (*ClientResponse, error) {1125 return cc.roundTrip(req, nil)1126}11271128func (cc *ClientConn) roundTrip(req *ClientRequest, streamf func(*clientStream)) (*ClientResponse, error) {1129 ctx := req.Context1130 req.stream = clientStream{1131 cc: cc,1132 ctx: ctx,1133 reqCancel: req.Cancel,1134 isHead: req.Method == "HEAD",1135 reqBody: req.Body,1136 reqBodyContentLength: actualContentLength(req),1137 trace: httptrace.ContextClientTrace(ctx),1138 peerClosed: make(chan struct{}),1139 abort: make(chan struct{}),1140 respHeaderRecv: make(chan struct{}),1141 donec: make(chan struct{}),1142 resTrailer: req.ResTrailer,1143 }1144 cs := &req.stream11451146 cs.requestedGzip = httpcommon.IsRequestGzip(req.Method, req.Header, cc.t.disableCompression())11471148 go cs.doRequest(req, streamf)11491150 waitDone := func() error {1151 select {1152 case <-cs.donec:1153 return nil1154 case <-ctx.Done():1155 return ctx.Err()1156 case <-cs.reqCancel:1157 return errRequestCanceled1158 }1159 }11601161 handleResponseHeaders := func() (*ClientResponse, error) {1162 res := cs.res1163 if res.StatusCode > 299 {1164 // On error or status code 3xx, 4xx, 5xx, etc abort any1165 // ongoing write, assuming that the server doesn't care1166 // about our request body. If the server replied with 1xx or1167 // 2xx, however, then assume the server DOES potentially1168 // want our body (e.g. full-duplex streaming:1169 // golang.org/issue/13444). If it turns out the server1170 // doesn't, they'll RST_STREAM us soon enough. This is a1171 // heuristic to avoid adding knobs to Transport. Hopefully1172 // we can keep it.1173 cs.abortRequestBodyWrite()1174 }1175 res.TLS = cc.tlsState1176 if res.Body == NoBody && actualContentLength(req) == 0 {1177 // If there isn't a request or response body still being1178 // written, then wait for the stream to be closed before1179 // RoundTrip returns.1180 if err := waitDone(); err != nil {1181 return nil, err1182 }1183 }1184 return res, nil1185 }11861187 cancelRequest := func(cs *clientStream, err error) error {1188 cs.cc.mu.Lock()1189 bodyClosed := cs.reqBodyClosed1190 cs.cc.mu.Unlock()1191 // Wait for the request body to be closed.1192 //1193 // If nothing closed the body before now, abortStreamLocked1194 // will have started a goroutine to close it.1195 //1196 // Closing the body before returning avoids a race condition1197 // with net/http checking its readTrackingBody to see if the1198 // body was read from or closed. See golang/go#60041.1199 //1200 // The body is closed in a separate goroutine without the1201 // connection mutex held, but dropping the mutex before waiting1202 // will keep us from holding it indefinitely if the body1203 // close is slow for some reason.1204 if bodyClosed != nil {1205 <-bodyClosed1206 }1207 return err1208 }12091210 for {1211 select {1212 case <-cs.respHeaderRecv:1213 return handleResponseHeaders()1214 case <-cs.abort:1215 select {1216 case <-cs.respHeaderRecv:1217 // If both cs.respHeaderRecv and cs.abort are signaling,1218 // pick respHeaderRecv. The server probably wrote the1219 // response and immediately reset the stream.1220 // golang.org/issue/496451221 return handleResponseHeaders()1222 default:1223 waitDone()1224 return nil, cs.abortErr1225 }1226 case <-ctx.Done():1227 err := ctx.Err()1228 cs.abortStream(err)1229 return nil, cancelRequest(cs, err)1230 case <-cs.reqCancel:1231 cs.abortStream(errRequestCanceled)1232 return nil, cancelRequest(cs, errRequestCanceled)1233 }1234 }1235}12361237// doRequest runs for the duration of the request lifetime.1238//1239// It sends the request and performs post-request cleanup (closing Request.Body, etc.),1240// except when writeRequest detaches from the stream, in which case cleanup is1241// performed at stream end by whoever ends it. See clientStream.detach.1242func (cs *clientStream) doRequest(req *ClientRequest, streamf func(*clientStream)) {1243 err := cs.writeRequest(req, streamf)1244 if err == errStreamDetached {1245 return1246 }1247 cs.cleanupWriteRequest(err)1248}12491250// errStreamDetached is a sentinel returned by writeRequest to tell doRequest1251// that the stream detached and cleanupWriteRequest will be called at stream1252// end by whoever ends it. It is never returned to users.1253var errStreamDetached = errors.New("http2: internal sentinel; stream detached from writeRequest goroutine")12541255// detach arranges for cleanupWriteRequest to run when the stream ends (the1256// peer half-closes it, it's aborted, or the request context is canceled),1257// letting the writeRequest goroutine exit instead of parking until then.1258//1259// This matters for servers and proxies with many concurrent long-lived1260// response streams (long polls): without it, each in-flight request pins a1261// goroutine and its stack for the stream's lifetime doing nothing but1262// waiting.1263//1264// respHeaderTimeout, if non-zero, gives the Transport.ResponseHeaderTimeout1265// to enforce on the detached stream if response headers haven't arrived yet.1266//1267// It reports whether the stream was detached. It returns false if the stream1268// has already ended, in which case the caller should wait for the stream end1269// events itself (they're already pending).1270func (cs *clientStream) detach(respHeaderTimeout time.Duration) bool {1271 cc := cs.cc1272 cc.mu.Lock()1273 defer cc.mu.Unlock()1274 select {1275 case <-cs.peerClosed:1276 return false1277 case <-cs.abort:1278 return false1279 default:1280 }1281 if respHeaderTimeout != 0 {1282 select {1283 case <-cs.respHeaderRecv:1284 // Headers already arrived; nothing to enforce.1285 default:1286 cs.respHeaderTimeoutTimer = time.AfterFunc(respHeaderTimeout, func() {1287 cc.mu.Lock()1288 defer cc.mu.Unlock()1289 select {1290 case <-cs.respHeaderRecv:1291 // Headers arrived after all; we lost a race1292 // with the Stop in processHeaders. Not a1293 // timeout.1294 return1295 default:1296 }1297 cs.abortStreamLocked(errTimeout)1298 })1299 }1300 }1301 // Watch for request context cancellation without parking a goroutine1302 // on ctx.Done(). If the context was canceled already, AfterFunc runs1303 // the func in a new goroutine, which blocks acquiring cc.mu until we1304 // return.1305 //1306 // stopCtxWatch must be assigned before detached is set: once detached1307 // is set, an abort or peer close can schedule cleanupWriteRequest1308 // (which calls stopCtxWatch) as soon as we release cc.mu.1309 cs.stopCtxWatch = context.AfterFunc(cs.ctx, func() {1310 cs.abortStream(cs.ctx.Err())1311 })1312 cs.detached = true1313 return true1314}13151316var errExtendedConnectNotSupported = errors.New("net/http: extended connect not supported by peer")13171318// writeRequest sends a request.1319//1320// It returns nil after the request is written, the response read,1321// and the request stream is half-closed by the peer.1322//1323// It returns non-nil if the request ends otherwise.1324// If the returned error is StreamError, the error Code may be used in resetting the stream.1325func (cs *clientStream) writeRequest(req *ClientRequest, streamf func(*clientStream)) (err error) {1326 cc := cs.cc1327 ctx := cs.ctx13281329 // wait for setting frames to be received, a server can change this value later,1330 // but we just wait for the first settings frame1331 var isExtendedConnect bool1332 if req.Method == "CONNECT" && req.Header.Get(":protocol") != "" {1333 isExtendedConnect = true1334 }13351336 // Acquire the new-request lock by writing to reqHeaderMu.1337 // This lock guards the critical section covering allocating a new stream ID1338 // (requires mu) and creating the stream (requires wmu).1339 if cc.reqHeaderMu == nil {1340 panic("RoundTrip on uninitialized ClientConn") // for tests1341 }1342 if isExtendedConnect {1343 select {1344 case <-cs.reqCancel:1345 return errRequestCanceled1346 case <-ctx.Done():1347 return ctx.Err()1348 case <-cc.seenSettingsChan:1349 if !cc.extendedConnectAllowed {1350 return errExtendedConnectNotSupported1351 }1352 }1353 }1354 select {1355 case cc.reqHeaderMu <- struct{}{}:1356 case <-cs.reqCancel:1357 return errRequestCanceled1358 case <-ctx.Done():1359 return ctx.Err()1360 }13611362 cc.mu.Lock()1363 if cc.idleTimer != nil {1364 cc.idleTimer.Stop()1365 }1366 cc.decrStreamReservationsLocked()1367 if err := cc.awaitOpenSlotForStreamLocked(cs); err != nil {1368 cc.mu.Unlock()1369 <-cc.reqHeaderMu1370 return err1371 }1372 cc.addStreamLocked(cs) // assigns stream ID1373 if isConnectionCloseRequest(req) {1374 cc.doNotReuse = true1375 }1376 cc.mu.Unlock()13771378 if streamf != nil {1379 streamf(cs)1380 }13811382 continueTimeout := cc.t.expectContinueTimeout()1383 if continueTimeout != 0 {1384 if !httpguts.HeaderValuesContainsToken(req.Header["Expect"], "100-continue") {1385 continueTimeout = 01386 } else {1387 cs.on100 = make(chan struct{}, 1)1388 }1389 }13901391 // Past this point (where we send request headers), it is possible for1392 // RoundTrip to return successfully. Since the RoundTrip contract permits1393 // the caller to "mutate or reuse" the Request after closing the Response's Body,1394 // we must take care when referencing the Request from here on.1395 err = cs.encodeAndWriteHeaders(req)1396 <-cc.reqHeaderMu1397 if err != nil {1398 return err1399 }14001401 hasBody := cs.reqBodyContentLength != 01402 if !hasBody {1403 cs.sentEndStream = true1404 } else {1405 if continueTimeout != 0 {1406 traceWait100Continue(cs.trace)1407 timer := time.NewTimer(continueTimeout)1408 select {1409 case <-timer.C:1410 err = nil1411 case <-cs.on100:1412 err = nil1413 case <-cs.abort:1414 err = cs.abortErr1415 case <-ctx.Done():1416 err = ctx.Err()1417 case <-cs.reqCancel:1418 err = errRequestCanceled1419 }1420 timer.Stop()1421 if err != nil {1422 traceWroteRequest(cs.trace, err)1423 return err1424 }1425 }14261427 if err = cs.writeRequestBody(req); err != nil {1428 if err != errStopReqBodyWrite {1429 traceWroteRequest(cs.trace, err)1430 return err1431 }1432 } else {1433 cs.sentEndStream = true1434 }1435 }14361437 traceWroteRequest(cs.trace, err)14381439 // If the request is fully sent and there's nothing left for this1440 // goroutine to do but wait for the stream to end, detach from the1441 // stream and exit rather than pinning this goroutine (and its stack)1442 // for the lifetime of what may be a very long-lived response stream.1443 // The remaining cases below then run cleanupWriteRequest from the1444 // stream-end event sites instead:1445 // - peerClosed and abort schedule it directly1446 // (abortStreamLocked, clientConnReadLoop.endStream)1447 // - ctx.Done is handled via context.AfterFunc in detach1448 // - ResponseHeaderTimeout is enforced by a time.AfterFunc timer,1449 // armed in detach and stopped when headers arrive1450 // The deprecated Request.Cancel channel can only be watched by a1451 // goroutine, so that (rare) case keeps the historical behavior of1452 // waiting here.1453 if cs.sentEndStream && cs.reqCancel == nil && cs.detach(cc.responseHeaderTimeout()) {1454 return errStreamDetached1455 }14561457 var respHeaderTimer <-chan time.Time1458 var respHeaderRecv chan struct{}1459 if d := cc.responseHeaderTimeout(); d != 0 {1460 timer := time.NewTimer(d)1461 defer timer.Stop()1462 respHeaderTimer = timer.C1463 respHeaderRecv = cs.respHeaderRecv1464 }14651466 // Wait until the peer half-closes its end of the stream,1467 // or until the request is aborted (via context, error, or otherwise),1468 // whichever comes first.1469 for {1470 select {1471 case <-cs.peerClosed:1472 return nil1473 case <-respHeaderTimer:1474 return errTimeout1475 case <-respHeaderRecv:1476 respHeaderRecv = nil1477 respHeaderTimer = nil // keep waiting for END_STREAM1478 case <-cs.abort:1479 return cs.abortErr1480 case <-ctx.Done():1481 return ctx.Err()1482 case <-cs.reqCancel:1483 return errRequestCanceled1484 }1485 }1486}14871488func (cs *clientStream) encodeAndWriteHeaders(req *ClientRequest) error {1489 cc := cs.cc1490 ctx := cs.ctx14911492 cc.wmu.Lock()1493 defer cc.wmu.Unlock()14941495 // If the request was canceled while waiting for cc.mu, just quit.1496 select {1497 case <-cs.abort:1498 return cs.abortErr1499 case <-ctx.Done():1500 return ctx.Err()1501 case <-cs.reqCancel:1502 return errRequestCanceled1503 default:1504 }15051506 // Encode headers.1507 //1508 // we send: HEADERS{1}, CONTINUATION{0,} + DATA{0,} (DATA is1509 // sent by writeRequestBody below, along with any Trailers,1510 // again in form HEADERS{1}, CONTINUATION{0,})1511 cc.hbuf.Reset()1512 res, err := encodeRequestHeaders(req, cs.requestedGzip, cc.peerMaxHeaderListSize, func(name, value string) {1513 cc.writeHeader(name, value)1514 })1515 if err != nil {1516 return fmt.Errorf("http2: %w", err)1517 }1518 hdrs := cc.hbuf.Bytes()15191520 // Write the request.1521 endStream := !res.HasBody && !res.HasTrailers1522 cs.sentHeaders = true1523 err = cc.writeHeaders(cs.ID, endStream, int(cc.maxFrameSize), hdrs)1524 traceWroteHeaders(cs.trace)1525 return err1526}15271528func encodeRequestHeaders(req *ClientRequest, addGzipHeader bool, peerMaxHeaderListSize uint64, headerf func(name, value string)) (httpcommon.EncodeHeadersResult, error) {1529 return httpcommon.EncodeHeaders(req.Context, httpcommon.EncodeHeadersParam{1530 Request: httpcommon.Request{1531 Header: req.Header,1532 Trailer: req.Trailer,1533 URL: req.URL,1534 Host: req.Host,1535 Method: req.Method,1536 ActualContentLength: actualContentLength(req),1537 },1538 AddGzipHeader: addGzipHeader,1539 PeerMaxHeaderListSize: peerMaxHeaderListSize,1540 DefaultUserAgent: defaultUserAgent,1541 }, headerf)1542}15431544// cleanupWriteRequest performs post-request tasks.1545//1546// If err (the result of writeRequest) is non-nil and the stream is not closed,1547// cleanupWriteRequest will send a reset to the peer.1548func (cs *clientStream) cleanupWriteRequest(err error) {1549 cc := cs.cc15501551 if cs.stopCtxWatch != nil {1552 cs.stopCtxWatch()1553 }15541555 if cs.ID == 0 {1556 // We were canceled before creating the stream, so return our reservation.1557 cc.decrStreamReservations()1558 }15591560 // TODO: write h12Compare test showing whether1561 // Request.Body is closed by the Transport,1562 // and in multiple cases: server replies <=299 and >2991563 // while still writing request body1564 cc.mu.Lock()1565 if t := cs.respHeaderTimeoutTimer; t != nil {1566 t.Stop()1567 cs.respHeaderTimeoutTimer = nil1568 }1569 mustCloseBody := false1570 if cs.reqBody != nil && cs.reqBodyClosed == nil {1571 mustCloseBody = true1572 cs.reqBodyClosed = make(chan struct{})1573 }1574 bodyClosed := cs.reqBodyClosed1575 closeOnIdle := cc.singleUse || cc.doNotReuse || cc.t.disableKeepAlives() || cc.goAway != nil1576 // Have we read any frames from the connection since sending this request?1577 readSinceStream := cc.readBeforeStreamID > cs.ID1578 cc.mu.Unlock()1579 if mustCloseBody {1580 cs.reqBody.Close()1581 close(bodyClosed)1582 }1583 if bodyClosed != nil {1584 <-bodyClosed1585 }15861587 if err != nil && cs.sentEndStream {1588 // If the connection is closed immediately after the response is read,1589 // we may be aborted before finishing up here. If the stream was closed1590 // cleanly on both sides, there is no error.1591 select {1592 case <-cs.peerClosed:1593 err = nil1594 default:1595 }1596 }1597 if err != nil {1598 cs.abortStream(err) // possibly redundant, but harmless1599 if cs.sentHeaders {1600 if se, ok := err.(StreamError); ok {1601 if se.Cause != errFromPeer {1602 cc.writeStreamReset(cs.ID, se.Code, false, err)1603 }1604 } else {1605 // We're cancelling an in-flight request.1606 //1607 // This could be due to the server becoming unresponsive.1608 // To avoid sending too many requests on a dead connection,1609 // if we haven't read any frames from the connection since1610 // sending this request, we let it continue to consume1611 // a concurrency slot until we can confirm the server is1612 // still responding.1613 // We do this by sending a PING frame along with the RST_STREAM1614 // (unless a ping is already in flight).1615 //1616 // For simplicity, we don't bother tracking the PING payload:1617 // We reset cc.pendingResets any time we receive a PING ACK.1618 //1619 // We skip this if the conn is going to be closed on idle,1620 // because it's short lived and will probably be closed before1621 // we get the ping response.1622 ping := false1623 if !closeOnIdle && !readSinceStream {1624 cc.mu.Lock()1625 // rstStreamPingsBlocked works around a gRPC behavior:1626 // see comment on the field for details.1627 if !cc.rstStreamPingsBlocked {1628 if cc.pendingResets == 0 {1629 ping = true1630 }1631 cc.pendingResets++1632 }1633 cc.mu.Unlock()1634 }1635 cc.writeStreamReset(cs.ID, ErrCodeCancel, ping, err)1636 }1637 }1638 cs.bufPipe.CloseWithError(err) // no-op if already closed1639 } else {1640 if cs.sentHeaders && !cs.sentEndStream {1641 cc.writeStreamReset(cs.ID, ErrCodeNo, false, nil)1642 }1643 cs.bufPipe.CloseWithError(errRequestCanceled)1644 }1645 if cs.ID != 0 {1646 cc.forgetStreamID(cs.ID)1647 }16481649 cc.wmu.Lock()1650 werr := cc.werr1651 cc.wmu.Unlock()1652 if werr != nil {1653 cc.Close()1654 }16551656 close(cs.donec)1657 cc.maybeCallStateHook()1658}16591660// awaitOpenSlotForStreamLocked waits until len(streams) < maxConcurrentStreams.1661// Must hold cc.mu.1662func (cc *ClientConn) awaitOpenSlotForStreamLocked(cs *clientStream) error {1663 for {1664 if cc.closed && cc.nextStreamID == 1 && cc.streamsReserved == 0 {1665 // This is the very first request sent to this connection.1666 // Return a fatal error which aborts the retry loop.1667 return errClientConnNotEstablished1668 }1669 cc.lastActive = time.Now()1670 if cc.closed || !cc.canTakeNewRequestLocked() {1671 return errClientConnUnusable1672 }1673 cc.lastIdle = time.Time{}1674 if cc.currentRequestCountLocked() < int(cc.maxConcurrentStreams) {1675 return nil1676 }1677 cc.pendingRequests++1678 cc.cond.Wait()1679 cc.pendingRequests--1680 select {1681 case <-cs.abort:1682 return cs.abortErr1683 default:1684 }1685 }1686}16871688// requires cc.wmu be held1689func (cc *ClientConn) writeHeaders(streamID uint32, endStream bool, maxFrameSize int, hdrs []byte) error {1690 first := true // first frame written (HEADERS is first, then CONTINUATION)1691 for len(hdrs) > 0 && cc.werr == nil {1692 chunk := hdrs1693 if len(chunk) > maxFrameSize {1694 chunk = chunk[:maxFrameSize]1695 }1696 hdrs = hdrs[len(chunk):]1697 endHeaders := len(hdrs) == 01698 if first {1699 cc.fr.WriteHeaders(HeadersFrameParam{1700 StreamID: streamID,1701 BlockFragment: chunk,1702 EndStream: endStream,1703 EndHeaders: endHeaders,1704 })1705 first = false1706 } else {1707 cc.fr.WriteContinuation(streamID, endHeaders, chunk)1708 }1709 }1710 cc.bw.Flush()1711 return cc.werr1712}17131714// internal error values; they don't escape to callers1715var (1716 // abort request body write; don't send cancel1717 errStopReqBodyWrite = errors.New("http2: aborting request body write")17181719 // abort request body write, but send stream reset of cancel.1720 errStopReqBodyWriteAndCancel = errors.New("http2: canceling request")17211722 errReqBodyTooLong = errors.New("http2: request body larger than specified content length")1723)17241725// frameScratchBufferLen returns the length of a buffer to use for1726// outgoing request bodies to read/write to/from.1727//1728// It returns max(1, min(peer's advertised max frame size,1729// Request.ContentLength+1, 512KB)).1730func (cs *clientStream) frameScratchBufferLen(maxFrameSize int) int {1731 const max = 512 << 101732 n := min(int64(maxFrameSize), max)1733 if cl := cs.reqBodyContentLength; cl != -1 && cl+1 < n {1734 // Add an extra byte past the declared content-length to1735 // give the caller's Request.Body io.Reader a chance to1736 // give us more bytes than they declared, so we can catch it1737 // early.1738 n = cl + 11739 }1740 if n < 1 {1741 return 11742 }1743 return int(n) // doesn't truncate; max is 512K1744}17451746// Seven bufPools manage different frame sizes. This helps to avoid scenarios where long-running1747// streaming requests using small frame sizes occupy large buffers initially allocated for prior1748// requests needing big buffers. The size ranges are as follows:1749// {0 KB, 16 KB], {16 KB, 32 KB], {32 KB, 64 KB], {64 KB, 128 KB], {128 KB, 256 KB],1750// {256 KB, 512 KB], {512 KB, infinity}1751// In practice, the maximum scratch buffer size should not exceed 512 KB due to1752// frameScratchBufferLen(maxFrameSize), thus the "infinity pool" should never be used.1753// It exists mainly as a safety measure, for potential future increases in max buffer size.1754var bufPools [7]sync.Pool // of *[]byte1755func bufPoolIndex(size int) int {1756 if size <= 16384 {1757 return 01758 }1759 size -= 11760 bits := bits.Len(uint(size))1761 index := bits - 141762 if index >= len(bufPools) {1763 return len(bufPools) - 11764 }1765 return index1766}17671768func (cs *clientStream) writeRequestBody(req *ClientRequest) (err error) {1769 cc := cs.cc1770 body := cs.reqBody1771 sentEnd := false // whether we sent the final DATA frame w/ END_STREAM17721773 hasTrailers := req.Trailer != nil1774 remainLen := cs.reqBodyContentLength1775 hasContentLen := remainLen != -117761777 cc.mu.Lock()1778 maxFrameSize := int(cc.maxFrameSize)1779 cc.mu.Unlock()17801781 // Scratch buffer for reading into & writing from.1782 scratchLen := cs.frameScratchBufferLen(maxFrameSize)1783 var buf []byte1784 index := bufPoolIndex(scratchLen)1785 if bp, ok := bufPools[index].Get().(*[]byte); ok && len(*bp) >= scratchLen {1786 defer bufPools[index].Put(bp)1787 buf = *bp1788 } else {1789 buf = make([]byte, scratchLen)1790 defer bufPools[index].Put(&buf)1791 }17921793 var sawEOF bool1794 for !sawEOF {1795 n, err := body.Read(buf)1796 if hasContentLen {1797 remainLen -= int64(n)1798 if remainLen == 0 && err == nil {1799 // The request body's Content-Length was predeclared and1800 // we just finished reading it all, but the underlying io.Reader1801 // returned the final chunk with a nil error (which is one of1802 // the two valid things a Reader can do at EOF). Because we'd prefer1803 // to send the END_STREAM bit early, double-check that we're actually1804 // at EOF. Subsequent reads should return (0, EOF) at this point.1805 // If either value is different, we return an error in one of two ways below.1806 var scratch [1]byte1807 var n1 int1808 n1, err = body.Read(scratch[:])1809 remainLen -= int64(n1)1810 }1811 if remainLen < 0 {1812 err = errReqBodyTooLong1813 return err1814 }1815 }1816 if err != nil {1817 cc.mu.Lock()1818 bodyClosed := cs.reqBodyClosed != nil1819 cc.mu.Unlock()1820 switch {1821 case bodyClosed:1822 return errStopReqBodyWrite1823 case err == io.EOF:1824 sawEOF = true1825 err = nil1826 default:1827 return err1828 }1829 }18301831 remain := buf[:n]1832 for len(remain) > 0 && err == nil {1833 var allowed int321834 allowed, err = cs.awaitFlowControl(len(remain))1835 if err != nil {1836 return err1837 }1838 cc.wmu.Lock()1839 data := remain[:allowed]1840 remain = remain[allowed:]1841 sentEnd = sawEOF && len(remain) == 0 && !hasTrailers1842 err = cc.fr.WriteData(cs.ID, sentEnd, data)1843 if err == nil {1844 // TODO(bradfitz): this flush is for latency, not bandwidth.1845 // Most requests won't need this. Make this opt-in or1846 // opt-out? Use some heuristic on the body type? Nagel-like1847 // timers? Based on 'n'? Only last chunk of this for loop,1848 // unless flow control tokens are low? For now, always.1849 // If we change this, see comment below.1850 err = cc.bw.Flush()1851 }1852 cc.wmu.Unlock()1853 }1854 if err != nil {1855 return err1856 }1857 }18581859 if sentEnd {1860 // Already sent END_STREAM (which implies we have no1861 // trailers) and flushed, because currently all1862 // WriteData frames above get a flush. So we're done.1863 return nil1864 }18651866 // Since the RoundTrip contract permits the caller to "mutate or reuse"1867 // a request after the Response's Body is closed, verify that this hasn't1868 // happened before accessing the trailers.1869 cc.mu.Lock()1870 trailer := req.Trailer1871 err = cs.abortErr1872 cc.mu.Unlock()1873 if err != nil {1874 return err1875 }18761877 cc.wmu.Lock()1878 defer cc.wmu.Unlock()1879 var trls []byte1880 if len(trailer) > 0 {1881 trls, err = cc.encodeTrailers(trailer)1882 if err != nil {1883 return err1884 }1885 }18861887 // Two ways to send END_STREAM: either with trailers, or1888 // with an empty DATA frame.1889 if len(trls) > 0 {1890 err = cc.writeHeaders(cs.ID, true, maxFrameSize, trls)1891 } else {1892 err = cc.fr.WriteData(cs.ID, true, nil)1893 }1894 if ferr := cc.bw.Flush(); ferr != nil && err == nil {1895 err = ferr1896 }1897 return err1898}18991900// awaitFlowControl waits for [1, min(maxBytes, cc.cs.maxFrameSize)] flow1901// control tokens from the server.1902// It returns either the non-zero number of tokens taken or an error1903// if the stream is dead.1904func (cs *clientStream) awaitFlowControl(maxBytes int) (taken int32, err error) {1905 cc := cs.cc1906 ctx := cs.ctx1907 cc.mu.Lock()1908 defer cc.mu.Unlock()1909 for {1910 if cc.closed {1911 return 0, errClientConnClosed1912 }1913 if cs.reqBodyClosed != nil {1914 return 0, errStopReqBodyWrite1915 }1916 select {1917 case <-cs.abort:1918 return 0, cs.abortErr1919 case <-ctx.Done():1920 return 0, ctx.Err()1921 case <-cs.reqCancel:1922 return 0, errRequestCanceled1923 default:1924 }1925 if a := cs.flow.available(); a > 0 {1926 take := a1927 if int(take) > maxBytes {19281929 take = int32(maxBytes) // can't truncate int; take is int321930 }1931 if take > int32(cc.maxFrameSize) {1932 take = int32(cc.maxFrameSize)1933 }1934 cs.flow.take(take)1935 return take, nil1936 }1937 cc.cond.Wait()1938 }1939}19401941// requires cc.wmu be held.1942func (cc *ClientConn) encodeTrailers(trailer Header) ([]byte, error) {1943 cc.hbuf.Reset()19441945 hlSize := uint64(0)1946 for k, vv := range trailer {1947 for _, v := range vv {1948 hf := hpack.HeaderField{Name: k, Value: v}1949 hlSize += uint64(hf.Size())1950 }1951 }1952 if hlSize > cc.peerMaxHeaderListSize {1953 return nil, errRequestHeaderListSize1954 }19551956 for k, vv := range trailer {1957 lowKey, ascii := httpcommon.LowerHeader(k)1958 if !ascii {1959 // Skip writing invalid headers. Per RFC 7540, Section 8.1.2, header1960 // field names have to be ASCII characters (just as in HTTP/1.x).1961 continue1962 }1963 // Transfer-Encoding, etc.. have already been filtered at the1964 // start of RoundTrip1965 for _, v := range vv {1966 cc.writeHeader(lowKey, v)1967 }1968 }1969 return cc.hbuf.Bytes(), nil1970}19711972func (cc *ClientConn) writeHeader(name, value string) {1973 if VerboseLogs {1974 log.Printf("http2: Transport encoding header %q = %q", name, value)1975 }1976 cc.henc.WriteField(hpack.HeaderField{Name: name, Value: value})1977}19781979type resAndError struct {1980 _ incomparable1981 res *ClientResponse1982 err error1983}19841985// requires cc.mu be held.1986func (cc *ClientConn) addStreamLocked(cs *clientStream) {1987 cs.flow.add(int32(cc.initialWindowSize))1988 cs.flow.setConnFlow(&cc.flow)1989 cs.inflow.init(cc.initialStreamRecvWindowSize)1990 cs.ID = cc.nextStreamID1991 cc.nextStreamID += 21992 cc.streams[cs.ID] = cs1993 if cs.ID == 0 {1994 panic("assigned stream ID 0")1995 }1996}19971998func (cc *ClientConn) forgetStreamID(id uint32) {1999 cc.mu.Lock()2000 slen := len(cc.streams)
Findings
✓ No findings reported for this file.