mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 10:03:42 +08:00 
			
		
		
		
	vendor: golang.org/x/net v0.23.0
full diff: https://github.com/golang/net/compare/v0.22.0...v0.23.0 Includes a fix for CVE-2023-45288, which is also addressed in go1.22.2 and go1.21.9; > http2: close connections when receiving too many headers > > Maintaining HPACK state requires that we parse and process > all HEADERS and CONTINUATION frames on a connection. > When a request's headers exceed MaxHeaderBytes, we don't > allocate memory to store the excess headers but we do > parse them. This permits an attacker to cause an HTTP/2 > endpoint to read arbitrary amounts of data, all associated > with a request which is going to be rejected. > > Set a limit on the amount of excess header frames we > will process before closing a connection. > > Thanks to Bartek Nowotarski for reporting this issue. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
		
							
								
								
									
										31
									
								
								vendor/golang.org/x/net/http2/frame.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										31
									
								
								vendor/golang.org/x/net/http2/frame.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1564,6 +1564,7 @@ func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
 | 
			
		||||
		if size > remainSize {
 | 
			
		||||
			hdec.SetEmitEnabled(false)
 | 
			
		||||
			mh.Truncated = true
 | 
			
		||||
			remainSize = 0
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		remainSize -= size
 | 
			
		||||
@@ -1576,6 +1577,36 @@ func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
 | 
			
		||||
	var hc headersOrContinuation = hf
 | 
			
		||||
	for {
 | 
			
		||||
		frag := hc.HeaderBlockFragment()
 | 
			
		||||
 | 
			
		||||
		// Avoid parsing large amounts of headers that we will then discard.
 | 
			
		||||
		// If the sender exceeds the max header list size by too much,
 | 
			
		||||
		// skip parsing the fragment and close the connection.
 | 
			
		||||
		//
 | 
			
		||||
		// "Too much" is either any CONTINUATION frame after we've already
 | 
			
		||||
		// exceeded the max header list size (in which case remainSize is 0),
 | 
			
		||||
		// or a frame whose encoded size is more than twice the remaining
 | 
			
		||||
		// header list bytes we're willing to accept.
 | 
			
		||||
		if int64(len(frag)) > int64(2*remainSize) {
 | 
			
		||||
			if VerboseLogs {
 | 
			
		||||
				log.Printf("http2: header list too large")
 | 
			
		||||
			}
 | 
			
		||||
			// It would be nice to send a RST_STREAM before sending the GOAWAY,
 | 
			
		||||
			// but the structure of the server's frame writer makes this difficult.
 | 
			
		||||
			return nil, ConnectionError(ErrCodeProtocol)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Also close the connection after any CONTINUATION frame following an
 | 
			
		||||
		// invalid header, since we stop tracking the size of the headers after
 | 
			
		||||
		// an invalid one.
 | 
			
		||||
		if invalid != nil {
 | 
			
		||||
			if VerboseLogs {
 | 
			
		||||
				log.Printf("http2: invalid header: %v", invalid)
 | 
			
		||||
			}
 | 
			
		||||
			// It would be nice to send a RST_STREAM before sending the GOAWAY,
 | 
			
		||||
			// but the structure of the server's frame writer makes this difficult.
 | 
			
		||||
			return nil, ConnectionError(ErrCodeProtocol)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if _, err := hdec.Write(frag); err != nil {
 | 
			
		||||
			return nil, ConnectionError(ErrCodeCompression)
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								vendor/golang.org/x/net/http2/pipe.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								vendor/golang.org/x/net/http2/pipe.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -77,7 +77,10 @@ func (p *pipe) Read(d []byte) (n int, err error) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var errClosedPipeWrite = errors.New("write on closed buffer")
 | 
			
		||||
var (
 | 
			
		||||
	errClosedPipeWrite        = errors.New("write on closed buffer")
 | 
			
		||||
	errUninitializedPipeWrite = errors.New("write on uninitialized buffer")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Write copies bytes from p into the buffer and wakes a reader.
 | 
			
		||||
// It is an error to write more data than the buffer can hold.
 | 
			
		||||
@@ -91,6 +94,12 @@ func (p *pipe) Write(d []byte) (n int, err error) {
 | 
			
		||||
	if p.err != nil || p.breakErr != nil {
 | 
			
		||||
		return 0, errClosedPipeWrite
 | 
			
		||||
	}
 | 
			
		||||
	// pipe.setBuffer is never invoked, leaving the buffer uninitialized.
 | 
			
		||||
	// We shouldn't try to write to an uninitialized pipe,
 | 
			
		||||
	// but returning an error is better than panicking.
 | 
			
		||||
	if p.b == nil {
 | 
			
		||||
		return 0, errUninitializedPipeWrite
 | 
			
		||||
	}
 | 
			
		||||
	return p.b.Write(d)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								vendor/golang.org/x/net/http2/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								vendor/golang.org/x/net/http2/server.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -124,6 +124,7 @@ type Server struct {
 | 
			
		||||
	// IdleTimeout specifies how long until idle clients should be
 | 
			
		||||
	// closed with a GOAWAY frame. PING frames are not considered
 | 
			
		||||
	// activity for the purposes of IdleTimeout.
 | 
			
		||||
	// If zero or negative, there is no timeout.
 | 
			
		||||
	IdleTimeout time.Duration
 | 
			
		||||
 | 
			
		||||
	// MaxUploadBufferPerConnection is the size of the initial flow
 | 
			
		||||
@@ -434,7 +435,7 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
 | 
			
		||||
	// passes the connection off to us with the deadline already set.
 | 
			
		||||
	// Write deadlines are set per stream in serverConn.newStream.
 | 
			
		||||
	// Disarm the net.Conn write deadline here.
 | 
			
		||||
	if sc.hs.WriteTimeout != 0 {
 | 
			
		||||
	if sc.hs.WriteTimeout > 0 {
 | 
			
		||||
		sc.conn.SetWriteDeadline(time.Time{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -924,7 +925,7 @@ func (sc *serverConn) serve() {
 | 
			
		||||
	sc.setConnState(http.StateActive)
 | 
			
		||||
	sc.setConnState(http.StateIdle)
 | 
			
		||||
 | 
			
		||||
	if sc.srv.IdleTimeout != 0 {
 | 
			
		||||
	if sc.srv.IdleTimeout > 0 {
 | 
			
		||||
		sc.idleTimer = time.AfterFunc(sc.srv.IdleTimeout, sc.onIdleTimer)
 | 
			
		||||
		defer sc.idleTimer.Stop()
 | 
			
		||||
	}
 | 
			
		||||
@@ -1637,7 +1638,7 @@ func (sc *serverConn) closeStream(st *stream, err error) {
 | 
			
		||||
	delete(sc.streams, st.id)
 | 
			
		||||
	if len(sc.streams) == 0 {
 | 
			
		||||
		sc.setConnState(http.StateIdle)
 | 
			
		||||
		if sc.srv.IdleTimeout != 0 {
 | 
			
		||||
		if sc.srv.IdleTimeout > 0 {
 | 
			
		||||
			sc.idleTimer.Reset(sc.srv.IdleTimeout)
 | 
			
		||||
		}
 | 
			
		||||
		if h1ServerKeepAlivesDisabled(sc.hs) {
 | 
			
		||||
@@ -2017,7 +2018,7 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
 | 
			
		||||
	// similar to how the http1 server works. Here it's
 | 
			
		||||
	// technically more like the http1 Server's ReadHeaderTimeout
 | 
			
		||||
	// (in Go 1.8), though. That's a more sane option anyway.
 | 
			
		||||
	if sc.hs.ReadTimeout != 0 {
 | 
			
		||||
	if sc.hs.ReadTimeout > 0 {
 | 
			
		||||
		sc.conn.SetReadDeadline(time.Time{})
 | 
			
		||||
		st.readDeadline = time.AfterFunc(sc.hs.ReadTimeout, st.onReadTimeout)
 | 
			
		||||
	}
 | 
			
		||||
@@ -2038,7 +2039,7 @@ func (sc *serverConn) upgradeRequest(req *http.Request) {
 | 
			
		||||
 | 
			
		||||
	// Disable any read deadline set by the net/http package
 | 
			
		||||
	// prior to the upgrade.
 | 
			
		||||
	if sc.hs.ReadTimeout != 0 {
 | 
			
		||||
	if sc.hs.ReadTimeout > 0 {
 | 
			
		||||
		sc.conn.SetReadDeadline(time.Time{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -2116,7 +2117,7 @@ func (sc *serverConn) newStream(id, pusherID uint32, state streamState) *stream
 | 
			
		||||
	st.flow.conn = &sc.flow // link to conn-level counter
 | 
			
		||||
	st.flow.add(sc.initialStreamSendWindowSize)
 | 
			
		||||
	st.inflow.init(sc.srv.initialStreamRecvWindowSize())
 | 
			
		||||
	if sc.hs.WriteTimeout != 0 {
 | 
			
		||||
	if sc.hs.WriteTimeout > 0 {
 | 
			
		||||
		st.writeDeadline = time.AfterFunc(sc.hs.WriteTimeout, st.onWriteTimeout)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										331
									
								
								vendor/golang.org/x/net/http2/testsync.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										331
									
								
								vendor/golang.org/x/net/http2/testsync.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,331 @@
 | 
			
		||||
// Copyright 2024 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
package http2
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// testSyncHooks coordinates goroutines in tests.
 | 
			
		||||
//
 | 
			
		||||
// For example, a call to ClientConn.RoundTrip involves several goroutines, including:
 | 
			
		||||
//   - the goroutine running RoundTrip;
 | 
			
		||||
//   - the clientStream.doRequest goroutine, which writes the request; and
 | 
			
		||||
//   - the clientStream.readLoop goroutine, which reads the response.
 | 
			
		||||
//
 | 
			
		||||
// Using testSyncHooks, a test can start a RoundTrip and identify when all these goroutines
 | 
			
		||||
// are blocked waiting for some condition such as reading the Request.Body or waiting for
 | 
			
		||||
// flow control to become available.
 | 
			
		||||
//
 | 
			
		||||
// The testSyncHooks also manage timers and synthetic time in tests.
 | 
			
		||||
// This permits us to, for example, start a request and cause it to time out waiting for
 | 
			
		||||
// response headers without resorting to time.Sleep calls.
 | 
			
		||||
type testSyncHooks struct {
 | 
			
		||||
	// active/inactive act as a mutex and condition variable.
 | 
			
		||||
	//
 | 
			
		||||
	//  - neither chan contains a value: testSyncHooks is locked.
 | 
			
		||||
	//  - active contains a value: unlocked, and at least one goroutine is not blocked
 | 
			
		||||
	//  - inactive contains a value: unlocked, and all goroutines are blocked
 | 
			
		||||
	active   chan struct{}
 | 
			
		||||
	inactive chan struct{}
 | 
			
		||||
 | 
			
		||||
	// goroutine counts
 | 
			
		||||
	total    int                     // total goroutines
 | 
			
		||||
	condwait map[*sync.Cond]int      // blocked in sync.Cond.Wait
 | 
			
		||||
	blocked  []*testBlockedGoroutine // otherwise blocked
 | 
			
		||||
 | 
			
		||||
	// fake time
 | 
			
		||||
	now    time.Time
 | 
			
		||||
	timers []*fakeTimer
 | 
			
		||||
 | 
			
		||||
	// Transport testing: Report various events.
 | 
			
		||||
	newclientconn func(*ClientConn)
 | 
			
		||||
	newstream     func(*clientStream)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// testBlockedGoroutine is a blocked goroutine.
 | 
			
		||||
type testBlockedGoroutine struct {
 | 
			
		||||
	f  func() bool   // blocked until f returns true
 | 
			
		||||
	ch chan struct{} // closed when unblocked
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newTestSyncHooks() *testSyncHooks {
 | 
			
		||||
	h := &testSyncHooks{
 | 
			
		||||
		active:   make(chan struct{}, 1),
 | 
			
		||||
		inactive: make(chan struct{}, 1),
 | 
			
		||||
		condwait: map[*sync.Cond]int{},
 | 
			
		||||
	}
 | 
			
		||||
	h.inactive <- struct{}{}
 | 
			
		||||
	h.now = time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
 | 
			
		||||
	return h
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// lock acquires the testSyncHooks mutex.
 | 
			
		||||
func (h *testSyncHooks) lock() {
 | 
			
		||||
	select {
 | 
			
		||||
	case <-h.active:
 | 
			
		||||
	case <-h.inactive:
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// waitInactive waits for all goroutines to become inactive.
 | 
			
		||||
func (h *testSyncHooks) waitInactive() {
 | 
			
		||||
	for {
 | 
			
		||||
		<-h.inactive
 | 
			
		||||
		if !h.unlock() {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// unlock releases the testSyncHooks mutex.
 | 
			
		||||
// It reports whether any goroutines are active.
 | 
			
		||||
func (h *testSyncHooks) unlock() (active bool) {
 | 
			
		||||
	// Look for a blocked goroutine which can be unblocked.
 | 
			
		||||
	blocked := h.blocked[:0]
 | 
			
		||||
	unblocked := false
 | 
			
		||||
	for _, b := range h.blocked {
 | 
			
		||||
		if !unblocked && b.f() {
 | 
			
		||||
			unblocked = true
 | 
			
		||||
			close(b.ch)
 | 
			
		||||
		} else {
 | 
			
		||||
			blocked = append(blocked, b)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	h.blocked = blocked
 | 
			
		||||
 | 
			
		||||
	// Count goroutines blocked on condition variables.
 | 
			
		||||
	condwait := 0
 | 
			
		||||
	for _, count := range h.condwait {
 | 
			
		||||
		condwait += count
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if h.total > condwait+len(blocked) {
 | 
			
		||||
		h.active <- struct{}{}
 | 
			
		||||
		return true
 | 
			
		||||
	} else {
 | 
			
		||||
		h.inactive <- struct{}{}
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// goRun starts a new goroutine.
 | 
			
		||||
func (h *testSyncHooks) goRun(f func()) {
 | 
			
		||||
	h.lock()
 | 
			
		||||
	h.total++
 | 
			
		||||
	h.unlock()
 | 
			
		||||
	go func() {
 | 
			
		||||
		defer func() {
 | 
			
		||||
			h.lock()
 | 
			
		||||
			h.total--
 | 
			
		||||
			h.unlock()
 | 
			
		||||
		}()
 | 
			
		||||
		f()
 | 
			
		||||
	}()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// blockUntil indicates that a goroutine is blocked waiting for some condition to become true.
 | 
			
		||||
// It waits until f returns true before proceeding.
 | 
			
		||||
//
 | 
			
		||||
// Example usage:
 | 
			
		||||
//
 | 
			
		||||
//	h.blockUntil(func() bool {
 | 
			
		||||
//		// Is the context done yet?
 | 
			
		||||
//		select {
 | 
			
		||||
//		case <-ctx.Done():
 | 
			
		||||
//		default:
 | 
			
		||||
//			return false
 | 
			
		||||
//		}
 | 
			
		||||
//		return true
 | 
			
		||||
//	})
 | 
			
		||||
//	// Wait for the context to become done.
 | 
			
		||||
//	<-ctx.Done()
 | 
			
		||||
//
 | 
			
		||||
// The function f passed to blockUntil must be non-blocking and idempotent.
 | 
			
		||||
func (h *testSyncHooks) blockUntil(f func() bool) {
 | 
			
		||||
	if f() {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ch := make(chan struct{})
 | 
			
		||||
	h.lock()
 | 
			
		||||
	h.blocked = append(h.blocked, &testBlockedGoroutine{
 | 
			
		||||
		f:  f,
 | 
			
		||||
		ch: ch,
 | 
			
		||||
	})
 | 
			
		||||
	h.unlock()
 | 
			
		||||
	<-ch
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// broadcast is sync.Cond.Broadcast.
 | 
			
		||||
func (h *testSyncHooks) condBroadcast(cond *sync.Cond) {
 | 
			
		||||
	h.lock()
 | 
			
		||||
	delete(h.condwait, cond)
 | 
			
		||||
	h.unlock()
 | 
			
		||||
	cond.Broadcast()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// broadcast is sync.Cond.Wait.
 | 
			
		||||
func (h *testSyncHooks) condWait(cond *sync.Cond) {
 | 
			
		||||
	h.lock()
 | 
			
		||||
	h.condwait[cond]++
 | 
			
		||||
	h.unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newTimer creates a new fake timer.
 | 
			
		||||
func (h *testSyncHooks) newTimer(d time.Duration) timer {
 | 
			
		||||
	h.lock()
 | 
			
		||||
	defer h.unlock()
 | 
			
		||||
	t := &fakeTimer{
 | 
			
		||||
		hooks: h,
 | 
			
		||||
		when:  h.now.Add(d),
 | 
			
		||||
		c:     make(chan time.Time),
 | 
			
		||||
	}
 | 
			
		||||
	h.timers = append(h.timers, t)
 | 
			
		||||
	return t
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// afterFunc creates a new fake AfterFunc timer.
 | 
			
		||||
func (h *testSyncHooks) afterFunc(d time.Duration, f func()) timer {
 | 
			
		||||
	h.lock()
 | 
			
		||||
	defer h.unlock()
 | 
			
		||||
	t := &fakeTimer{
 | 
			
		||||
		hooks: h,
 | 
			
		||||
		when:  h.now.Add(d),
 | 
			
		||||
		f:     f,
 | 
			
		||||
	}
 | 
			
		||||
	h.timers = append(h.timers, t)
 | 
			
		||||
	return t
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *testSyncHooks) contextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) {
 | 
			
		||||
	ctx, cancel := context.WithCancel(ctx)
 | 
			
		||||
	t := h.afterFunc(d, cancel)
 | 
			
		||||
	return ctx, func() {
 | 
			
		||||
		t.Stop()
 | 
			
		||||
		cancel()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *testSyncHooks) timeUntilEvent() time.Duration {
 | 
			
		||||
	h.lock()
 | 
			
		||||
	defer h.unlock()
 | 
			
		||||
	var next time.Time
 | 
			
		||||
	for _, t := range h.timers {
 | 
			
		||||
		if next.IsZero() || t.when.Before(next) {
 | 
			
		||||
			next = t.when
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if d := next.Sub(h.now); d > 0 {
 | 
			
		||||
		return d
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// advance advances time and causes synthetic timers to fire.
 | 
			
		||||
func (h *testSyncHooks) advance(d time.Duration) {
 | 
			
		||||
	h.lock()
 | 
			
		||||
	defer h.unlock()
 | 
			
		||||
	h.now = h.now.Add(d)
 | 
			
		||||
	timers := h.timers[:0]
 | 
			
		||||
	for _, t := range h.timers {
 | 
			
		||||
		t := t // remove after go.mod depends on go1.22
 | 
			
		||||
		t.mu.Lock()
 | 
			
		||||
		switch {
 | 
			
		||||
		case t.when.After(h.now):
 | 
			
		||||
			timers = append(timers, t)
 | 
			
		||||
		case t.when.IsZero():
 | 
			
		||||
			// stopped timer
 | 
			
		||||
		default:
 | 
			
		||||
			t.when = time.Time{}
 | 
			
		||||
			if t.c != nil {
 | 
			
		||||
				close(t.c)
 | 
			
		||||
			}
 | 
			
		||||
			if t.f != nil {
 | 
			
		||||
				h.total++
 | 
			
		||||
				go func() {
 | 
			
		||||
					defer func() {
 | 
			
		||||
						h.lock()
 | 
			
		||||
						h.total--
 | 
			
		||||
						h.unlock()
 | 
			
		||||
					}()
 | 
			
		||||
					t.f()
 | 
			
		||||
				}()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		t.mu.Unlock()
 | 
			
		||||
	}
 | 
			
		||||
	h.timers = timers
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A timer wraps a time.Timer, or a synthetic equivalent in tests.
 | 
			
		||||
// Unlike time.Timer, timer is single-use: The timer channel is closed when the timer expires.
 | 
			
		||||
type timer interface {
 | 
			
		||||
	C() <-chan time.Time
 | 
			
		||||
	Stop() bool
 | 
			
		||||
	Reset(d time.Duration) bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// timeTimer implements timer using real time.
 | 
			
		||||
type timeTimer struct {
 | 
			
		||||
	t *time.Timer
 | 
			
		||||
	c chan time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newTimeTimer creates a new timer using real time.
 | 
			
		||||
func newTimeTimer(d time.Duration) timer {
 | 
			
		||||
	ch := make(chan time.Time)
 | 
			
		||||
	t := time.AfterFunc(d, func() {
 | 
			
		||||
		close(ch)
 | 
			
		||||
	})
 | 
			
		||||
	return &timeTimer{t, ch}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newTimeAfterFunc creates an AfterFunc timer using real time.
 | 
			
		||||
func newTimeAfterFunc(d time.Duration, f func()) timer {
 | 
			
		||||
	return &timeTimer{
 | 
			
		||||
		t: time.AfterFunc(d, f),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t timeTimer) C() <-chan time.Time        { return t.c }
 | 
			
		||||
func (t timeTimer) Stop() bool                 { return t.t.Stop() }
 | 
			
		||||
func (t timeTimer) Reset(d time.Duration) bool { return t.t.Reset(d) }
 | 
			
		||||
 | 
			
		||||
// fakeTimer implements timer using fake time.
 | 
			
		||||
type fakeTimer struct {
 | 
			
		||||
	hooks *testSyncHooks
 | 
			
		||||
 | 
			
		||||
	mu   sync.Mutex
 | 
			
		||||
	when time.Time      // when the timer will fire
 | 
			
		||||
	c    chan time.Time // closed when the timer fires; mutually exclusive with f
 | 
			
		||||
	f    func()         // called when the timer fires; mutually exclusive with c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *fakeTimer) C() <-chan time.Time { return t.c }
 | 
			
		||||
 | 
			
		||||
func (t *fakeTimer) Stop() bool {
 | 
			
		||||
	t.mu.Lock()
 | 
			
		||||
	defer t.mu.Unlock()
 | 
			
		||||
	stopped := t.when.IsZero()
 | 
			
		||||
	t.when = time.Time{}
 | 
			
		||||
	return stopped
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *fakeTimer) Reset(d time.Duration) bool {
 | 
			
		||||
	if t.c != nil || t.f == nil {
 | 
			
		||||
		panic("fakeTimer only supports Reset on AfterFunc timers")
 | 
			
		||||
	}
 | 
			
		||||
	t.mu.Lock()
 | 
			
		||||
	defer t.mu.Unlock()
 | 
			
		||||
	t.hooks.lock()
 | 
			
		||||
	defer t.hooks.unlock()
 | 
			
		||||
	active := !t.when.IsZero()
 | 
			
		||||
	t.when = t.hooks.now.Add(d)
 | 
			
		||||
	if !active {
 | 
			
		||||
		t.hooks.timers = append(t.hooks.timers, t)
 | 
			
		||||
	}
 | 
			
		||||
	return active
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										298
									
								
								vendor/golang.org/x/net/http2/transport.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										298
									
								
								vendor/golang.org/x/net/http2/transport.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -147,6 +147,12 @@ type Transport struct {
 | 
			
		||||
	// waiting for their turn.
 | 
			
		||||
	StrictMaxConcurrentStreams bool
 | 
			
		||||
 | 
			
		||||
	// IdleConnTimeout is the maximum amount of time an idle
 | 
			
		||||
	// (keep-alive) connection will remain idle before closing
 | 
			
		||||
	// itself.
 | 
			
		||||
	// Zero means no limit.
 | 
			
		||||
	IdleConnTimeout time.Duration
 | 
			
		||||
 | 
			
		||||
	// ReadIdleTimeout is the timeout after which a health check using ping
 | 
			
		||||
	// frame will be carried out if no frame is received on the connection.
 | 
			
		||||
	// Note that a ping response will is considered a received frame, so if
 | 
			
		||||
@@ -178,6 +184,8 @@ type Transport struct {
 | 
			
		||||
 | 
			
		||||
	connPoolOnce  sync.Once
 | 
			
		||||
	connPoolOrDef ClientConnPool // non-nil version of ConnPool
 | 
			
		||||
 | 
			
		||||
	syncHooks *testSyncHooks
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Transport) maxHeaderListSize() uint32 {
 | 
			
		||||
@@ -302,7 +310,7 @@ type ClientConn struct {
 | 
			
		||||
	readerErr  error         // set before readerDone is closed
 | 
			
		||||
 | 
			
		||||
	idleTimeout time.Duration // or 0 for never
 | 
			
		||||
	idleTimer   *time.Timer
 | 
			
		||||
	idleTimer   timer
 | 
			
		||||
 | 
			
		||||
	mu              sync.Mutex // guards following
 | 
			
		||||
	cond            *sync.Cond // hold mu; broadcast on flow/closed changes
 | 
			
		||||
@@ -344,6 +352,60 @@ type ClientConn struct {
 | 
			
		||||
	werr error        // first write error that has occurred
 | 
			
		||||
	hbuf bytes.Buffer // HPACK encoder writes into this
 | 
			
		||||
	henc *hpack.Encoder
 | 
			
		||||
 | 
			
		||||
	syncHooks *testSyncHooks // can be nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Hook points used for testing.
 | 
			
		||||
// Outside of tests, cc.syncHooks is nil and these all have minimal implementations.
 | 
			
		||||
// Inside tests, see the testSyncHooks function docs.
 | 
			
		||||
 | 
			
		||||
// goRun starts a new goroutine.
 | 
			
		||||
func (cc *ClientConn) goRun(f func()) {
 | 
			
		||||
	if cc.syncHooks != nil {
 | 
			
		||||
		cc.syncHooks.goRun(f)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	go f()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// condBroadcast is cc.cond.Broadcast.
 | 
			
		||||
func (cc *ClientConn) condBroadcast() {
 | 
			
		||||
	if cc.syncHooks != nil {
 | 
			
		||||
		cc.syncHooks.condBroadcast(cc.cond)
 | 
			
		||||
	}
 | 
			
		||||
	cc.cond.Broadcast()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// condWait is cc.cond.Wait.
 | 
			
		||||
func (cc *ClientConn) condWait() {
 | 
			
		||||
	if cc.syncHooks != nil {
 | 
			
		||||
		cc.syncHooks.condWait(cc.cond)
 | 
			
		||||
	}
 | 
			
		||||
	cc.cond.Wait()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newTimer creates a new time.Timer, or a synthetic timer in tests.
 | 
			
		||||
func (cc *ClientConn) newTimer(d time.Duration) timer {
 | 
			
		||||
	if cc.syncHooks != nil {
 | 
			
		||||
		return cc.syncHooks.newTimer(d)
 | 
			
		||||
	}
 | 
			
		||||
	return newTimeTimer(d)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// afterFunc creates a new time.AfterFunc timer, or a synthetic timer in tests.
 | 
			
		||||
func (cc *ClientConn) afterFunc(d time.Duration, f func()) timer {
 | 
			
		||||
	if cc.syncHooks != nil {
 | 
			
		||||
		return cc.syncHooks.afterFunc(d, f)
 | 
			
		||||
	}
 | 
			
		||||
	return newTimeAfterFunc(d, f)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cc *ClientConn) contextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) {
 | 
			
		||||
	if cc.syncHooks != nil {
 | 
			
		||||
		return cc.syncHooks.contextWithTimeout(ctx, d)
 | 
			
		||||
	}
 | 
			
		||||
	return context.WithTimeout(ctx, d)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// clientStream is the state for a single HTTP/2 stream. One of these
 | 
			
		||||
@@ -425,7 +487,7 @@ func (cs *clientStream) abortStreamLocked(err error) {
 | 
			
		||||
	// TODO(dneil): Clean up tests where cs.cc.cond is nil.
 | 
			
		||||
	if cs.cc.cond != nil {
 | 
			
		||||
		// Wake up writeRequestBody if it is waiting on flow control.
 | 
			
		||||
		cs.cc.cond.Broadcast()
 | 
			
		||||
		cs.cc.condBroadcast()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -435,7 +497,7 @@ func (cs *clientStream) abortRequestBodyWrite() {
 | 
			
		||||
	defer cc.mu.Unlock()
 | 
			
		||||
	if cs.reqBody != nil && cs.reqBodyClosed == nil {
 | 
			
		||||
		cs.closeReqBodyLocked()
 | 
			
		||||
		cc.cond.Broadcast()
 | 
			
		||||
		cc.condBroadcast()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -445,10 +507,10 @@ func (cs *clientStream) closeReqBodyLocked() {
 | 
			
		||||
	}
 | 
			
		||||
	cs.reqBodyClosed = make(chan struct{})
 | 
			
		||||
	reqBodyClosed := cs.reqBodyClosed
 | 
			
		||||
	go func() {
 | 
			
		||||
	cs.cc.goRun(func() {
 | 
			
		||||
		cs.reqBody.Close()
 | 
			
		||||
		close(reqBodyClosed)
 | 
			
		||||
	}()
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type stickyErrWriter struct {
 | 
			
		||||
@@ -537,15 +599,6 @@ func authorityAddr(scheme string, authority string) (addr string) {
 | 
			
		||||
	return net.JoinHostPort(host, port)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var retryBackoffHook func(time.Duration) *time.Timer
 | 
			
		||||
 | 
			
		||||
func backoffNewTimer(d time.Duration) *time.Timer {
 | 
			
		||||
	if retryBackoffHook != nil {
 | 
			
		||||
		return retryBackoffHook(d)
 | 
			
		||||
	}
 | 
			
		||||
	return time.NewTimer(d)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RoundTripOpt is like RoundTrip, but takes options.
 | 
			
		||||
func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) {
 | 
			
		||||
	if !(req.URL.Scheme == "https" || (req.URL.Scheme == "http" && t.AllowHTTP)) {
 | 
			
		||||
@@ -573,13 +626,27 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res
 | 
			
		||||
				backoff := float64(uint(1) << (uint(retry) - 1))
 | 
			
		||||
				backoff += backoff * (0.1 * mathrand.Float64())
 | 
			
		||||
				d := time.Second * time.Duration(backoff)
 | 
			
		||||
				timer := backoffNewTimer(d)
 | 
			
		||||
				var tm timer
 | 
			
		||||
				if t.syncHooks != nil {
 | 
			
		||||
					tm = t.syncHooks.newTimer(d)
 | 
			
		||||
					t.syncHooks.blockUntil(func() bool {
 | 
			
		||||
						select {
 | 
			
		||||
						case <-tm.C():
 | 
			
		||||
						case <-req.Context().Done():
 | 
			
		||||
						default:
 | 
			
		||||
							return false
 | 
			
		||||
						}
 | 
			
		||||
						return true
 | 
			
		||||
					})
 | 
			
		||||
				} else {
 | 
			
		||||
					tm = newTimeTimer(d)
 | 
			
		||||
				}
 | 
			
		||||
				select {
 | 
			
		||||
				case <-timer.C:
 | 
			
		||||
				case <-tm.C():
 | 
			
		||||
					t.vlogf("RoundTrip retrying after failure: %v", roundTripErr)
 | 
			
		||||
					continue
 | 
			
		||||
				case <-req.Context().Done():
 | 
			
		||||
					timer.Stop()
 | 
			
		||||
					tm.Stop()
 | 
			
		||||
					err = req.Context().Err()
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
@@ -658,6 +725,9 @@ func canRetryError(err error) bool {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse bool) (*ClientConn, error) {
 | 
			
		||||
	if t.syncHooks != nil {
 | 
			
		||||
		return t.newClientConn(nil, singleUse, t.syncHooks)
 | 
			
		||||
	}
 | 
			
		||||
	host, _, err := net.SplitHostPort(addr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -666,7 +736,7 @@ func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse b
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return t.newClientConn(tconn, singleUse)
 | 
			
		||||
	return t.newClientConn(tconn, singleUse, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Transport) newTLSConfig(host string) *tls.Config {
 | 
			
		||||
@@ -732,10 +802,10 @@ func (t *Transport) maxEncoderHeaderTableSize() uint32 {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
 | 
			
		||||
	return t.newClientConn(c, t.disableKeepAlives())
 | 
			
		||||
	return t.newClientConn(c, t.disableKeepAlives(), nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, error) {
 | 
			
		||||
func (t *Transport) newClientConn(c net.Conn, singleUse bool, hooks *testSyncHooks) (*ClientConn, error) {
 | 
			
		||||
	cc := &ClientConn{
 | 
			
		||||
		t:                     t,
 | 
			
		||||
		tconn:                 c,
 | 
			
		||||
@@ -750,10 +820,15 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
 | 
			
		||||
		wantSettingsAck:       true,
 | 
			
		||||
		pings:                 make(map[[8]byte]chan struct{}),
 | 
			
		||||
		reqHeaderMu:           make(chan struct{}, 1),
 | 
			
		||||
		syncHooks:             hooks,
 | 
			
		||||
	}
 | 
			
		||||
	if hooks != nil {
 | 
			
		||||
		hooks.newclientconn(cc)
 | 
			
		||||
		c = cc.tconn
 | 
			
		||||
	}
 | 
			
		||||
	if d := t.idleConnTimeout(); d != 0 {
 | 
			
		||||
		cc.idleTimeout = d
 | 
			
		||||
		cc.idleTimer = time.AfterFunc(d, cc.onIdleTimeout)
 | 
			
		||||
		cc.idleTimer = cc.afterFunc(d, cc.onIdleTimeout)
 | 
			
		||||
	}
 | 
			
		||||
	if VerboseLogs {
 | 
			
		||||
		t.vlogf("http2: Transport creating client conn %p to %v", cc, c.RemoteAddr())
 | 
			
		||||
@@ -818,7 +893,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
 | 
			
		||||
		return nil, cc.werr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go cc.readLoop()
 | 
			
		||||
	cc.goRun(cc.readLoop)
 | 
			
		||||
	return cc, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -826,7 +901,7 @@ func (cc *ClientConn) healthCheck() {
 | 
			
		||||
	pingTimeout := cc.t.pingTimeout()
 | 
			
		||||
	// We don't need to periodically ping in the health check, because the readLoop of ClientConn will
 | 
			
		||||
	// trigger the healthCheck again if there is no frame received.
 | 
			
		||||
	ctx, cancel := context.WithTimeout(context.Background(), pingTimeout)
 | 
			
		||||
	ctx, cancel := cc.contextWithTimeout(context.Background(), pingTimeout)
 | 
			
		||||
	defer cancel()
 | 
			
		||||
	cc.vlogf("http2: Transport sending health check")
 | 
			
		||||
	err := cc.Ping(ctx)
 | 
			
		||||
@@ -1056,7 +1131,7 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error {
 | 
			
		||||
	// Wait for all in-flight streams to complete or connection to close
 | 
			
		||||
	done := make(chan struct{})
 | 
			
		||||
	cancelled := false // guarded by cc.mu
 | 
			
		||||
	go func() {
 | 
			
		||||
	cc.goRun(func() {
 | 
			
		||||
		cc.mu.Lock()
 | 
			
		||||
		defer cc.mu.Unlock()
 | 
			
		||||
		for {
 | 
			
		||||
@@ -1068,9 +1143,9 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error {
 | 
			
		||||
			if cancelled {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			cc.cond.Wait()
 | 
			
		||||
			cc.condWait()
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	})
 | 
			
		||||
	shutdownEnterWaitStateHook()
 | 
			
		||||
	select {
 | 
			
		||||
	case <-done:
 | 
			
		||||
@@ -1080,7 +1155,7 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error {
 | 
			
		||||
		cc.mu.Lock()
 | 
			
		||||
		// Free the goroutine above
 | 
			
		||||
		cancelled = true
 | 
			
		||||
		cc.cond.Broadcast()
 | 
			
		||||
		cc.condBroadcast()
 | 
			
		||||
		cc.mu.Unlock()
 | 
			
		||||
		return ctx.Err()
 | 
			
		||||
	}
 | 
			
		||||
@@ -1118,7 +1193,7 @@ func (cc *ClientConn) closeForError(err error) {
 | 
			
		||||
	for _, cs := range cc.streams {
 | 
			
		||||
		cs.abortStreamLocked(err)
 | 
			
		||||
	}
 | 
			
		||||
	cc.cond.Broadcast()
 | 
			
		||||
	cc.condBroadcast()
 | 
			
		||||
	cc.mu.Unlock()
 | 
			
		||||
	cc.closeConn()
 | 
			
		||||
}
 | 
			
		||||
@@ -1215,6 +1290,10 @@ func (cc *ClientConn) decrStreamReservationsLocked() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
 | 
			
		||||
	return cc.roundTrip(req, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cc *ClientConn) roundTrip(req *http.Request, streamf func(*clientStream)) (*http.Response, error) {
 | 
			
		||||
	ctx := req.Context()
 | 
			
		||||
	cs := &clientStream{
 | 
			
		||||
		cc:                   cc,
 | 
			
		||||
@@ -1229,9 +1308,23 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
 | 
			
		||||
		respHeaderRecv:       make(chan struct{}),
 | 
			
		||||
		donec:                make(chan struct{}),
 | 
			
		||||
	}
 | 
			
		||||
	go cs.doRequest(req)
 | 
			
		||||
	cc.goRun(func() {
 | 
			
		||||
		cs.doRequest(req)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	waitDone := func() error {
 | 
			
		||||
		if cc.syncHooks != nil {
 | 
			
		||||
			cc.syncHooks.blockUntil(func() bool {
 | 
			
		||||
				select {
 | 
			
		||||
				case <-cs.donec:
 | 
			
		||||
				case <-ctx.Done():
 | 
			
		||||
				case <-cs.reqCancel:
 | 
			
		||||
				default:
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
				return true
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
		select {
 | 
			
		||||
		case <-cs.donec:
 | 
			
		||||
			return nil
 | 
			
		||||
@@ -1292,7 +1385,24 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if streamf != nil {
 | 
			
		||||
		streamf(cs)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		if cc.syncHooks != nil {
 | 
			
		||||
			cc.syncHooks.blockUntil(func() bool {
 | 
			
		||||
				select {
 | 
			
		||||
				case <-cs.respHeaderRecv:
 | 
			
		||||
				case <-cs.abort:
 | 
			
		||||
				case <-ctx.Done():
 | 
			
		||||
				case <-cs.reqCancel:
 | 
			
		||||
				default:
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
				return true
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
		select {
 | 
			
		||||
		case <-cs.respHeaderRecv:
 | 
			
		||||
			return handleResponseHeaders()
 | 
			
		||||
@@ -1348,6 +1458,21 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) {
 | 
			
		||||
	if cc.reqHeaderMu == nil {
 | 
			
		||||
		panic("RoundTrip on uninitialized ClientConn") // for tests
 | 
			
		||||
	}
 | 
			
		||||
	var newStreamHook func(*clientStream)
 | 
			
		||||
	if cc.syncHooks != nil {
 | 
			
		||||
		newStreamHook = cc.syncHooks.newstream
 | 
			
		||||
		cc.syncHooks.blockUntil(func() bool {
 | 
			
		||||
			select {
 | 
			
		||||
			case cc.reqHeaderMu <- struct{}{}:
 | 
			
		||||
				<-cc.reqHeaderMu
 | 
			
		||||
			case <-cs.reqCancel:
 | 
			
		||||
			case <-ctx.Done():
 | 
			
		||||
			default:
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
			return true
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	select {
 | 
			
		||||
	case cc.reqHeaderMu <- struct{}{}:
 | 
			
		||||
	case <-cs.reqCancel:
 | 
			
		||||
@@ -1372,6 +1497,10 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) {
 | 
			
		||||
	}
 | 
			
		||||
	cc.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	if newStreamHook != nil {
 | 
			
		||||
		newStreamHook(cs)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere?
 | 
			
		||||
	if !cc.t.disableCompression() &&
 | 
			
		||||
		req.Header.Get("Accept-Encoding") == "" &&
 | 
			
		||||
@@ -1452,15 +1581,30 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) {
 | 
			
		||||
	var respHeaderTimer <-chan time.Time
 | 
			
		||||
	var respHeaderRecv chan struct{}
 | 
			
		||||
	if d := cc.responseHeaderTimeout(); d != 0 {
 | 
			
		||||
		timer := time.NewTimer(d)
 | 
			
		||||
		timer := cc.newTimer(d)
 | 
			
		||||
		defer timer.Stop()
 | 
			
		||||
		respHeaderTimer = timer.C
 | 
			
		||||
		respHeaderTimer = timer.C()
 | 
			
		||||
		respHeaderRecv = cs.respHeaderRecv
 | 
			
		||||
	}
 | 
			
		||||
	// Wait until the peer half-closes its end of the stream,
 | 
			
		||||
	// or until the request is aborted (via context, error, or otherwise),
 | 
			
		||||
	// whichever comes first.
 | 
			
		||||
	for {
 | 
			
		||||
		if cc.syncHooks != nil {
 | 
			
		||||
			cc.syncHooks.blockUntil(func() bool {
 | 
			
		||||
				select {
 | 
			
		||||
				case <-cs.peerClosed:
 | 
			
		||||
				case <-respHeaderTimer:
 | 
			
		||||
				case <-respHeaderRecv:
 | 
			
		||||
				case <-cs.abort:
 | 
			
		||||
				case <-ctx.Done():
 | 
			
		||||
				case <-cs.reqCancel:
 | 
			
		||||
				default:
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
				return true
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
		select {
 | 
			
		||||
		case <-cs.peerClosed:
 | 
			
		||||
			return nil
 | 
			
		||||
@@ -1609,7 +1753,7 @@ func (cc *ClientConn) awaitOpenSlotForStreamLocked(cs *clientStream) error {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		cc.pendingRequests++
 | 
			
		||||
		cc.cond.Wait()
 | 
			
		||||
		cc.condWait()
 | 
			
		||||
		cc.pendingRequests--
 | 
			
		||||
		select {
 | 
			
		||||
		case <-cs.abort:
 | 
			
		||||
@@ -1871,10 +2015,26 @@ func (cs *clientStream) awaitFlowControl(maxBytes int) (taken int32, err error)
 | 
			
		||||
			cs.flow.take(take)
 | 
			
		||||
			return take, nil
 | 
			
		||||
		}
 | 
			
		||||
		cc.cond.Wait()
 | 
			
		||||
		cc.condWait()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateHeaders(hdrs http.Header) string {
 | 
			
		||||
	for k, vv := range hdrs {
 | 
			
		||||
		if !httpguts.ValidHeaderFieldName(k) {
 | 
			
		||||
			return fmt.Sprintf("name %q", k)
 | 
			
		||||
		}
 | 
			
		||||
		for _, v := range vv {
 | 
			
		||||
			if !httpguts.ValidHeaderFieldValue(v) {
 | 
			
		||||
				// Don't include the value in the error,
 | 
			
		||||
				// because it may be sensitive.
 | 
			
		||||
				return fmt.Sprintf("value for header %q", k)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var errNilRequestURL = errors.New("http2: Request.URI is nil")
 | 
			
		||||
 | 
			
		||||
// requires cc.wmu be held.
 | 
			
		||||
@@ -1912,19 +2072,14 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check for any invalid headers and return an error before we
 | 
			
		||||
	// Check for any invalid headers+trailers and return an error before we
 | 
			
		||||
	// potentially pollute our hpack state. (We want to be able to
 | 
			
		||||
	// continue to reuse the hpack encoder for future requests)
 | 
			
		||||
	for k, vv := range req.Header {
 | 
			
		||||
		if !httpguts.ValidHeaderFieldName(k) {
 | 
			
		||||
			return nil, fmt.Errorf("invalid HTTP header name %q", k)
 | 
			
		||||
		}
 | 
			
		||||
		for _, v := range vv {
 | 
			
		||||
			if !httpguts.ValidHeaderFieldValue(v) {
 | 
			
		||||
				// Don't include the value in the error, because it may be sensitive.
 | 
			
		||||
				return nil, fmt.Errorf("invalid HTTP header value for header %q", k)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	if err := validateHeaders(req.Header); err != "" {
 | 
			
		||||
		return nil, fmt.Errorf("invalid HTTP header %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := validateHeaders(req.Trailer); err != "" {
 | 
			
		||||
		return nil, fmt.Errorf("invalid HTTP trailer %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	enumerateHeaders := func(f func(name, value string)) {
 | 
			
		||||
@@ -2143,7 +2298,7 @@ func (cc *ClientConn) forgetStreamID(id uint32) {
 | 
			
		||||
	}
 | 
			
		||||
	// Wake up writeRequestBody via clientStream.awaitFlowControl and
 | 
			
		||||
	// wake up RoundTrip if there is a pending request.
 | 
			
		||||
	cc.cond.Broadcast()
 | 
			
		||||
	cc.condBroadcast()
 | 
			
		||||
 | 
			
		||||
	closeOnIdle := cc.singleUse || cc.doNotReuse || cc.t.disableKeepAlives() || cc.goAway != nil
 | 
			
		||||
	if closeOnIdle && cc.streamsReserved == 0 && len(cc.streams) == 0 {
 | 
			
		||||
@@ -2231,7 +2386,7 @@ func (rl *clientConnReadLoop) cleanup() {
 | 
			
		||||
			cs.abortStreamLocked(err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	cc.cond.Broadcast()
 | 
			
		||||
	cc.condBroadcast()
 | 
			
		||||
	cc.mu.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -2266,10 +2421,9 @@ func (rl *clientConnReadLoop) run() error {
 | 
			
		||||
	cc := rl.cc
 | 
			
		||||
	gotSettings := false
 | 
			
		||||
	readIdleTimeout := cc.t.ReadIdleTimeout
 | 
			
		||||
	var t *time.Timer
 | 
			
		||||
	var t timer
 | 
			
		||||
	if readIdleTimeout != 0 {
 | 
			
		||||
		t = time.AfterFunc(readIdleTimeout, cc.healthCheck)
 | 
			
		||||
		defer t.Stop()
 | 
			
		||||
		t = cc.afterFunc(readIdleTimeout, cc.healthCheck)
 | 
			
		||||
	}
 | 
			
		||||
	for {
 | 
			
		||||
		f, err := cc.fr.ReadFrame()
 | 
			
		||||
@@ -2684,7 +2838,7 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
 | 
			
		||||
		})
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if !cs.firstByte {
 | 
			
		||||
	if !cs.pastHeaders {
 | 
			
		||||
		cc.logf("protocol error: received DATA before a HEADERS frame")
 | 
			
		||||
		rl.endStreamError(cs, StreamError{
 | 
			
		||||
			StreamID: f.StreamID,
 | 
			
		||||
@@ -2867,7 +3021,7 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error {
 | 
			
		||||
			for _, cs := range cc.streams {
 | 
			
		||||
				cs.flow.add(delta)
 | 
			
		||||
			}
 | 
			
		||||
			cc.cond.Broadcast()
 | 
			
		||||
			cc.condBroadcast()
 | 
			
		||||
 | 
			
		||||
			cc.initialWindowSize = s.Val
 | 
			
		||||
		case SettingHeaderTableSize:
 | 
			
		||||
@@ -2922,7 +3076,7 @@ func (rl *clientConnReadLoop) processWindowUpdate(f *WindowUpdateFrame) error {
 | 
			
		||||
 | 
			
		||||
		return ConnectionError(ErrCodeFlowControl)
 | 
			
		||||
	}
 | 
			
		||||
	cc.cond.Broadcast()
 | 
			
		||||
	cc.condBroadcast()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -2964,24 +3118,38 @@ func (cc *ClientConn) Ping(ctx context.Context) error {
 | 
			
		||||
		}
 | 
			
		||||
		cc.mu.Unlock()
 | 
			
		||||
	}
 | 
			
		||||
	errc := make(chan error, 1)
 | 
			
		||||
	go func() {
 | 
			
		||||
	var pingError error
 | 
			
		||||
	errc := make(chan struct{})
 | 
			
		||||
	cc.goRun(func() {
 | 
			
		||||
		cc.wmu.Lock()
 | 
			
		||||
		defer cc.wmu.Unlock()
 | 
			
		||||
		if err := cc.fr.WritePing(false, p); err != nil {
 | 
			
		||||
			errc <- err
 | 
			
		||||
		if pingError = cc.fr.WritePing(false, p); pingError != nil {
 | 
			
		||||
			close(errc)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if err := cc.bw.Flush(); err != nil {
 | 
			
		||||
			errc <- err
 | 
			
		||||
		if pingError = cc.bw.Flush(); pingError != nil {
 | 
			
		||||
			close(errc)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	})
 | 
			
		||||
	if cc.syncHooks != nil {
 | 
			
		||||
		cc.syncHooks.blockUntil(func() bool {
 | 
			
		||||
			select {
 | 
			
		||||
			case <-c:
 | 
			
		||||
			case <-errc:
 | 
			
		||||
			case <-ctx.Done():
 | 
			
		||||
			case <-cc.readerDone:
 | 
			
		||||
			default:
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
			return true
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	select {
 | 
			
		||||
	case <-c:
 | 
			
		||||
		return nil
 | 
			
		||||
	case err := <-errc:
 | 
			
		||||
		return err
 | 
			
		||||
	case <-errc:
 | 
			
		||||
		return pingError
 | 
			
		||||
	case <-ctx.Done():
 | 
			
		||||
		return ctx.Err()
 | 
			
		||||
	case <-cc.readerDone:
 | 
			
		||||
@@ -3150,9 +3318,17 @@ func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Transport) idleConnTimeout() time.Duration {
 | 
			
		||||
	// to keep things backwards compatible, we use non-zero values of
 | 
			
		||||
	// IdleConnTimeout, followed by using the IdleConnTimeout on the underlying
 | 
			
		||||
	// http1 transport, followed by 0
 | 
			
		||||
	if t.IdleConnTimeout != 0 {
 | 
			
		||||
		return t.IdleConnTimeout
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if t.t1 != nil {
 | 
			
		||||
		return t.t1.IdleConnTimeout
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user