mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 10:33:35 +00:00
vendor update for CSI 0.3.0
This commit is contained in:
28
vendor/golang.org/x/net/http2/client_conn_pool.go
generated
vendored
28
vendor/golang.org/x/net/http2/client_conn_pool.go
generated
vendored
@ -52,9 +52,31 @@ const (
|
||||
noDialOnMiss = false
|
||||
)
|
||||
|
||||
// shouldTraceGetConn reports whether getClientConn should call any
|
||||
// ClientTrace.GetConn hook associated with the http.Request.
|
||||
//
|
||||
// This complexity is needed to avoid double calls of the GetConn hook
|
||||
// during the back-and-forth between net/http and x/net/http2 (when the
|
||||
// net/http.Transport is upgraded to also speak http2), as well as support
|
||||
// the case where x/net/http2 is being used directly.
|
||||
func (p *clientConnPool) shouldTraceGetConn(st clientConnIdleState) bool {
|
||||
// If our Transport wasn't made via ConfigureTransport, always
|
||||
// trace the GetConn hook if provided, because that means the
|
||||
// http2 package is being used directly and it's the one
|
||||
// dialing, as opposed to net/http.
|
||||
if _, ok := p.t.ConnPool.(noDialClientConnPool); !ok {
|
||||
return true
|
||||
}
|
||||
// Otherwise, only use the GetConn hook if this connection has
|
||||
// been used previously for other requests. For fresh
|
||||
// connections, the net/http package does the dialing.
|
||||
return !st.freshConn
|
||||
}
|
||||
|
||||
func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMiss bool) (*ClientConn, error) {
|
||||
if isConnectionCloseRequest(req) && dialOnMiss {
|
||||
// It gets its own connection.
|
||||
traceGetConn(req, addr)
|
||||
const singleUse = true
|
||||
cc, err := p.t.dialClientConn(addr, singleUse)
|
||||
if err != nil {
|
||||
@ -64,7 +86,10 @@ func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMis
|
||||
}
|
||||
p.mu.Lock()
|
||||
for _, cc := range p.conns[addr] {
|
||||
if cc.CanTakeNewRequest() {
|
||||
if st := cc.idleState(); st.canTakeNewRequest {
|
||||
if p.shouldTraceGetConn(st) {
|
||||
traceGetConn(req, addr)
|
||||
}
|
||||
p.mu.Unlock()
|
||||
return cc, nil
|
||||
}
|
||||
@ -73,6 +98,7 @@ func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMis
|
||||
p.mu.Unlock()
|
||||
return nil, ErrNoCachedConn
|
||||
}
|
||||
traceGetConn(req, addr)
|
||||
call := p.getStartDialLocked(addr)
|
||||
p.mu.Unlock()
|
||||
<-call.done
|
||||
|
8
vendor/golang.org/x/net/http2/configure_transport.go
generated
vendored
8
vendor/golang.org/x/net/http2/configure_transport.go
generated
vendored
@ -57,7 +57,7 @@ func configureTransport(t1 *http.Transport) (*Transport, error) {
|
||||
|
||||
// registerHTTPSProtocol calls Transport.RegisterProtocol but
|
||||
// converting panics into errors.
|
||||
func registerHTTPSProtocol(t *http.Transport, rt http.RoundTripper) (err error) {
|
||||
func registerHTTPSProtocol(t *http.Transport, rt noDialH2RoundTripper) (err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = fmt.Errorf("%v", e)
|
||||
@ -69,10 +69,12 @@ func registerHTTPSProtocol(t *http.Transport, rt http.RoundTripper) (err error)
|
||||
|
||||
// noDialH2RoundTripper is a RoundTripper which only tries to complete the request
|
||||
// if there's already has a cached connection to the host.
|
||||
type noDialH2RoundTripper struct{ t *Transport }
|
||||
// (The field is exported so it can be accessed via reflect from net/http; tested
|
||||
// by TestNoDialH2RoundTripperType)
|
||||
type noDialH2RoundTripper struct{ *Transport }
|
||||
|
||||
func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
res, err := rt.t.RoundTrip(req)
|
||||
res, err := rt.Transport.RoundTrip(req)
|
||||
if isNoCachedConnError(err) {
|
||||
return nil, http.ErrSkipAltProtocol
|
||||
}
|
||||
|
10
vendor/golang.org/x/net/http2/flow.go
generated
vendored
10
vendor/golang.org/x/net/http2/flow.go
generated
vendored
@ -41,10 +41,10 @@ func (f *flow) take(n int32) {
|
||||
// add adds n bytes (positive or negative) to the flow control window.
|
||||
// It returns false if the sum would exceed 2^31-1.
|
||||
func (f *flow) add(n int32) bool {
|
||||
remain := (1<<31 - 1) - f.n
|
||||
if n > remain {
|
||||
return false
|
||||
sum := f.n + n
|
||||
if (sum > n) == (f.n > 0) {
|
||||
f.n = sum
|
||||
return true
|
||||
}
|
||||
f.n += n
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
34
vendor/golang.org/x/net/http2/flow_test.go
generated
vendored
34
vendor/golang.org/x/net/http2/flow_test.go
generated
vendored
@ -49,5 +49,39 @@ func TestFlowAdd(t *testing.T) {
|
||||
if f.add(1) {
|
||||
t.Fatal("adding 1 to max shouldn't be allowed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlowAddOverflow(t *testing.T) {
|
||||
var f flow
|
||||
if !f.add(0) {
|
||||
t.Fatal("failed to add 0")
|
||||
}
|
||||
if !f.add(-1) {
|
||||
t.Fatal("failed to add -1")
|
||||
}
|
||||
if !f.add(0) {
|
||||
t.Fatal("failed to add 0")
|
||||
}
|
||||
if !f.add(1) {
|
||||
t.Fatal("failed to add 1")
|
||||
}
|
||||
if !f.add(1) {
|
||||
t.Fatal("failed to add 1")
|
||||
}
|
||||
if !f.add(0) {
|
||||
t.Fatal("failed to add 0")
|
||||
}
|
||||
if !f.add(-3) {
|
||||
t.Fatal("failed to add -3")
|
||||
}
|
||||
if got, want := f.available(), int32(-2); got != want {
|
||||
t.Fatalf("size = %d; want %d", got, want)
|
||||
}
|
||||
if !f.add(1<<31 - 1) {
|
||||
t.Fatal("failed to add 2^31-1")
|
||||
}
|
||||
if got, want := f.available(), int32(1+-3+(1<<31-1)); got != want {
|
||||
t.Fatalf("size = %d; want %d", got, want)
|
||||
}
|
||||
|
||||
}
|
||||
|
67
vendor/golang.org/x/net/http2/frame.go
generated
vendored
67
vendor/golang.org/x/net/http2/frame.go
generated
vendored
@ -14,8 +14,8 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/http2/hpack"
|
||||
"golang.org/x/net/lex/httplex"
|
||||
)
|
||||
|
||||
const frameHeaderLen = 9
|
||||
@ -733,32 +733,67 @@ func (f *SettingsFrame) IsAck() bool {
|
||||
return f.FrameHeader.Flags.Has(FlagSettingsAck)
|
||||
}
|
||||
|
||||
func (f *SettingsFrame) Value(s SettingID) (v uint32, ok bool) {
|
||||
func (f *SettingsFrame) Value(id SettingID) (v uint32, ok bool) {
|
||||
f.checkValid()
|
||||
buf := f.p
|
||||
for len(buf) > 0 {
|
||||
settingID := SettingID(binary.BigEndian.Uint16(buf[:2]))
|
||||
if settingID == s {
|
||||
return binary.BigEndian.Uint32(buf[2:6]), true
|
||||
for i := 0; i < f.NumSettings(); i++ {
|
||||
if s := f.Setting(i); s.ID == id {
|
||||
return s.Val, true
|
||||
}
|
||||
buf = buf[6:]
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Setting returns the setting from the frame at the given 0-based index.
|
||||
// The index must be >= 0 and less than f.NumSettings().
|
||||
func (f *SettingsFrame) Setting(i int) Setting {
|
||||
buf := f.p
|
||||
return Setting{
|
||||
ID: SettingID(binary.BigEndian.Uint16(buf[i*6 : i*6+2])),
|
||||
Val: binary.BigEndian.Uint32(buf[i*6+2 : i*6+6]),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *SettingsFrame) NumSettings() int { return len(f.p) / 6 }
|
||||
|
||||
// HasDuplicates reports whether f contains any duplicate setting IDs.
|
||||
func (f *SettingsFrame) HasDuplicates() bool {
|
||||
num := f.NumSettings()
|
||||
if num == 0 {
|
||||
return false
|
||||
}
|
||||
// If it's small enough (the common case), just do the n^2
|
||||
// thing and avoid a map allocation.
|
||||
if num < 10 {
|
||||
for i := 0; i < num; i++ {
|
||||
idi := f.Setting(i).ID
|
||||
for j := i + 1; j < num; j++ {
|
||||
idj := f.Setting(j).ID
|
||||
if idi == idj {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
seen := map[SettingID]bool{}
|
||||
for i := 0; i < num; i++ {
|
||||
id := f.Setting(i).ID
|
||||
if seen[id] {
|
||||
return true
|
||||
}
|
||||
seen[id] = true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ForeachSetting runs fn for each setting.
|
||||
// It stops and returns the first error.
|
||||
func (f *SettingsFrame) ForeachSetting(fn func(Setting) error) error {
|
||||
f.checkValid()
|
||||
buf := f.p
|
||||
for len(buf) > 0 {
|
||||
if err := fn(Setting{
|
||||
SettingID(binary.BigEndian.Uint16(buf[:2])),
|
||||
binary.BigEndian.Uint32(buf[2:6]),
|
||||
}); err != nil {
|
||||
for i := 0; i < f.NumSettings(); i++ {
|
||||
if err := fn(f.Setting(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
buf = buf[6:]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -1462,7 +1497,7 @@ func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
|
||||
if VerboseLogs && fr.logReads {
|
||||
fr.debugReadLoggerf("http2: decoded hpack field %+v", hf)
|
||||
}
|
||||
if !httplex.ValidHeaderFieldValue(hf.Value) {
|
||||
if !httpguts.ValidHeaderFieldValue(hf.Value) {
|
||||
invalid = headerFieldValueError(hf.Value)
|
||||
}
|
||||
isPseudo := strings.HasPrefix(hf.Name, ":")
|
||||
|
44
vendor/golang.org/x/net/http2/frame_test.go
generated
vendored
44
vendor/golang.org/x/net/http2/frame_test.go
generated
vendored
@ -1189,3 +1189,47 @@ func encodeHeaderRaw(t *testing.T, pairs ...string) []byte {
|
||||
var he hpackEncoder
|
||||
return he.encodeHeaderRaw(t, pairs...)
|
||||
}
|
||||
|
||||
func TestSettingsDuplicates(t *testing.T) {
|
||||
tests := []struct {
|
||||
settings []Setting
|
||||
want bool
|
||||
}{
|
||||
{nil, false},
|
||||
{[]Setting{{ID: 1}}, false},
|
||||
{[]Setting{{ID: 1}, {ID: 2}}, false},
|
||||
{[]Setting{{ID: 1}, {ID: 2}}, false},
|
||||
{[]Setting{{ID: 1}, {ID: 2}, {ID: 3}}, false},
|
||||
{[]Setting{{ID: 1}, {ID: 2}, {ID: 3}}, false},
|
||||
{[]Setting{{ID: 1}, {ID: 2}, {ID: 3}, {ID: 4}}, false},
|
||||
|
||||
{[]Setting{{ID: 1}, {ID: 2}, {ID: 3}, {ID: 2}}, true},
|
||||
{[]Setting{{ID: 4}, {ID: 2}, {ID: 3}, {ID: 4}}, true},
|
||||
|
||||
{[]Setting{
|
||||
{ID: 1}, {ID: 2}, {ID: 3}, {ID: 4},
|
||||
{ID: 5}, {ID: 6}, {ID: 7}, {ID: 8},
|
||||
{ID: 9}, {ID: 10}, {ID: 11}, {ID: 12},
|
||||
}, false},
|
||||
|
||||
{[]Setting{
|
||||
{ID: 1}, {ID: 2}, {ID: 3}, {ID: 4},
|
||||
{ID: 5}, {ID: 6}, {ID: 7}, {ID: 8},
|
||||
{ID: 9}, {ID: 10}, {ID: 11}, {ID: 11},
|
||||
}, true},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
fr, _ := testFramer()
|
||||
fr.WriteSettings(tt.settings...)
|
||||
f, err := fr.ReadFrame()
|
||||
if err != nil {
|
||||
t.Fatalf("%d. ReadFrame: %v", i, err)
|
||||
}
|
||||
sf := f.(*SettingsFrame)
|
||||
got := sf.HasDuplicates()
|
||||
if got != tt.want {
|
||||
t.Errorf("%d. HasDuplicates = %v; want %v", i, got, tt.want)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
26
vendor/golang.org/x/net/http2/go111.go
generated
vendored
Normal file
26
vendor/golang.org/x/net/http2/go111.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2018 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.
|
||||
|
||||
// +build go1.11
|
||||
|
||||
package http2
|
||||
|
||||
import "net/textproto"
|
||||
|
||||
func traceHasWroteHeaderField(trace *clientTrace) bool {
|
||||
return trace != nil && trace.WroteHeaderField != nil
|
||||
}
|
||||
|
||||
func traceWroteHeaderField(trace *clientTrace, k, v string) {
|
||||
if trace != nil && trace.WroteHeaderField != nil {
|
||||
trace.WroteHeaderField(k, []string{v})
|
||||
}
|
||||
}
|
||||
|
||||
func traceGot1xxResponseFunc(trace *clientTrace) func(int, textproto.MIMEHeader) error {
|
||||
if trace != nil {
|
||||
return trace.Got1xxResponse
|
||||
}
|
||||
return nil
|
||||
}
|
15
vendor/golang.org/x/net/http2/go17.go
generated
vendored
15
vendor/golang.org/x/net/http2/go17.go
generated
vendored
@ -18,6 +18,8 @@ type contextContext interface {
|
||||
context.Context
|
||||
}
|
||||
|
||||
var errCanceled = context.Canceled
|
||||
|
||||
func serverConnBaseContext(c net.Conn, opts *ServeConnOpts) (ctx contextContext, cancel func()) {
|
||||
ctx, cancel = context.WithCancel(context.Background())
|
||||
ctx = context.WithValue(ctx, http.LocalAddrContextKey, c.LocalAddr())
|
||||
@ -48,6 +50,14 @@ func (t *Transport) idleConnTimeout() time.Duration {
|
||||
|
||||
func setResponseUncompressed(res *http.Response) { res.Uncompressed = true }
|
||||
|
||||
func traceGetConn(req *http.Request, hostPort string) {
|
||||
trace := httptrace.ContextClientTrace(req.Context())
|
||||
if trace == nil || trace.GetConn == nil {
|
||||
return
|
||||
}
|
||||
trace.GetConn(hostPort)
|
||||
}
|
||||
|
||||
func traceGotConn(req *http.Request, cc *ClientConn) {
|
||||
trace := httptrace.ContextClientTrace(req.Context())
|
||||
if trace == nil || trace.GotConn == nil {
|
||||
@ -104,3 +114,8 @@ func requestTrace(req *http.Request) *clientTrace {
|
||||
func (cc *ClientConn) Ping(ctx context.Context) error {
|
||||
return cc.ping(ctx)
|
||||
}
|
||||
|
||||
// Shutdown gracefully closes the client connection, waiting for running streams to complete.
|
||||
func (cc *ClientConn) Shutdown(ctx context.Context) error {
|
||||
return cc.shutdown(ctx)
|
||||
}
|
||||
|
3
vendor/golang.org/x/net/http2/h2demo/h2demo.go
generated
vendored
3
vendor/golang.org/x/net/http2/h2demo/h2demo.go
generated
vendored
@ -160,6 +160,9 @@ func echoCapitalHandler(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "PUT required.", 400)
|
||||
return
|
||||
}
|
||||
if f, ok := w.(http.Flusher); ok {
|
||||
f.Flush()
|
||||
}
|
||||
io.Copy(flushWriter{w}, capitalizeReader{r.Body})
|
||||
}
|
||||
|
||||
|
1
vendor/golang.org/x/net/http2/h2demo/service.yaml
generated
vendored
1
vendor/golang.org/x/net/http2/h2demo/service.yaml
generated
vendored
@ -3,6 +3,7 @@ kind: Service
|
||||
metadata:
|
||||
name: h2demo
|
||||
spec:
|
||||
externalTrafficPolicy: Local
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
|
2
vendor/golang.org/x/net/http2/h2i/h2i.go
generated
vendored
2
vendor/golang.org/x/net/http2/h2i/h2i.go
generated
vendored
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !plan9,!solaris
|
||||
// +build !js,!nacl,!plan9,!solaris
|
||||
|
||||
/*
|
||||
The h2i command is an interactive HTTP/2 console.
|
||||
|
6
vendor/golang.org/x/net/http2/hpack/hpack.go
generated
vendored
6
vendor/golang.org/x/net/http2/hpack/hpack.go
generated
vendored
@ -389,6 +389,12 @@ func (d *Decoder) callEmit(hf HeaderField) error {
|
||||
|
||||
// (same invariants and behavior as parseHeaderFieldRepr)
|
||||
func (d *Decoder) parseDynamicTableSizeUpdate() error {
|
||||
// RFC 7541, sec 4.2: This dynamic table size update MUST occur at the
|
||||
// beginning of the first header block following the change to the dynamic table size.
|
||||
if d.dynTab.size > 0 {
|
||||
return DecodingError{errors.New("dynamic table size update MUST occur at the beginning of a header block")}
|
||||
}
|
||||
|
||||
buf := d.buf
|
||||
size, buf, err := readVarInt(5, buf)
|
||||
if err != nil {
|
||||
|
19
vendor/golang.org/x/net/http2/hpack/hpack_test.go
generated
vendored
19
vendor/golang.org/x/net/http2/hpack/hpack_test.go
generated
vendored
@ -720,3 +720,22 @@ func TestSaveBufLimit(t *testing.T) {
|
||||
t.Fatalf("Write error = %v; want ErrStringLength", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDynamicSizeUpdate(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
enc := NewEncoder(&buf)
|
||||
enc.SetMaxDynamicTableSize(255)
|
||||
enc.WriteField(HeaderField{Name: "foo", Value: "bar"})
|
||||
|
||||
d := NewDecoder(4096, nil)
|
||||
_, err := d.DecodeFull(buf.Bytes())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: got = %v", err)
|
||||
}
|
||||
|
||||
// must fail since the dynamic table update must be at the beginning
|
||||
_, err = d.DecodeFull(buf.Bytes())
|
||||
if err == nil {
|
||||
t.Fatalf("dynamic table size update not at the beginning of a header block")
|
||||
}
|
||||
}
|
||||
|
6
vendor/golang.org/x/net/http2/http2.go
generated
vendored
6
vendor/golang.org/x/net/http2/http2.go
generated
vendored
@ -29,7 +29,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/net/lex/httplex"
|
||||
"golang.org/x/net/http/httpguts"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -179,7 +179,7 @@ var (
|
||||
)
|
||||
|
||||
// validWireHeaderFieldName reports whether v is a valid header field
|
||||
// name (key). See httplex.ValidHeaderName for the base rules.
|
||||
// name (key). See httpguts.ValidHeaderName for the base rules.
|
||||
//
|
||||
// Further, http2 says:
|
||||
// "Just as in HTTP/1.x, header field names are strings of ASCII
|
||||
@ -191,7 +191,7 @@ func validWireHeaderFieldName(v string) bool {
|
||||
return false
|
||||
}
|
||||
for _, r := range v {
|
||||
if !httplex.IsTokenRune(r) {
|
||||
if !httpguts.IsTokenRune(r) {
|
||||
return false
|
||||
}
|
||||
if 'A' <= r && r <= 'Z' {
|
||||
|
28
vendor/golang.org/x/net/http2/http2_test.go
generated
vendored
28
vendor/golang.org/x/net/http2/http2_test.go
generated
vendored
@ -14,6 +14,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http2/hpack"
|
||||
)
|
||||
@ -197,3 +198,30 @@ func TestSorterPoolAllocs(t *testing.T) {
|
||||
t.Logf("Keys allocs = %v; want <1", allocs)
|
||||
}
|
||||
}
|
||||
|
||||
// waitCondition reports whether fn eventually returned true,
|
||||
// checking immediately and then every checkEvery amount,
|
||||
// until waitFor has elapsed, at which point it returns false.
|
||||
func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool {
|
||||
deadline := time.Now().Add(waitFor)
|
||||
for time.Now().Before(deadline) {
|
||||
if fn() {
|
||||
return true
|
||||
}
|
||||
time.Sleep(checkEvery)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// waitErrCondition is like waitCondition but with errors instead of bools.
|
||||
func waitErrCondition(waitFor, checkEvery time.Duration, fn func() error) error {
|
||||
deadline := time.Now().Add(waitFor)
|
||||
var err error
|
||||
for time.Now().Before(deadline) {
|
||||
if err = fn(); err == nil {
|
||||
return nil
|
||||
}
|
||||
time.Sleep(checkEvery)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
17
vendor/golang.org/x/net/http2/not_go111.go
generated
vendored
Normal file
17
vendor/golang.org/x/net/http2/not_go111.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2018 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.
|
||||
|
||||
// +build !go1.11
|
||||
|
||||
package http2
|
||||
|
||||
import "net/textproto"
|
||||
|
||||
func traceHasWroteHeaderField(trace *clientTrace) bool { return false }
|
||||
|
||||
func traceWroteHeaderField(trace *clientTrace, k, v string) {}
|
||||
|
||||
func traceGot1xxResponseFunc(trace *clientTrace) func(int, textproto.MIMEHeader) error {
|
||||
return nil
|
||||
}
|
8
vendor/golang.org/x/net/http2/not_go17.go
generated
vendored
8
vendor/golang.org/x/net/http2/not_go17.go
generated
vendored
@ -8,6 +8,7 @@ package http2
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
@ -18,6 +19,8 @@ type contextContext interface {
|
||||
Err() error
|
||||
}
|
||||
|
||||
var errCanceled = errors.New("canceled")
|
||||
|
||||
type fakeContext struct{}
|
||||
|
||||
func (fakeContext) Done() <-chan struct{} { return nil }
|
||||
@ -34,6 +37,7 @@ func setResponseUncompressed(res *http.Response) {
|
||||
type clientTrace struct{}
|
||||
|
||||
func requestTrace(*http.Request) *clientTrace { return nil }
|
||||
func traceGetConn(*http.Request, string) {}
|
||||
func traceGotConn(*http.Request, *ClientConn) {}
|
||||
func traceFirstResponseByte(*clientTrace) {}
|
||||
func traceWroteHeaders(*clientTrace) {}
|
||||
@ -84,4 +88,8 @@ func (cc *ClientConn) Ping(ctx contextContext) error {
|
||||
return cc.ping(ctx)
|
||||
}
|
||||
|
||||
func (cc *ClientConn) Shutdown(ctx contextContext) error {
|
||||
return cc.shutdown(ctx)
|
||||
}
|
||||
|
||||
func (t *Transport) idleConnTimeout() time.Duration { return 0 }
|
||||
|
87
vendor/golang.org/x/net/http2/server.go
generated
vendored
87
vendor/golang.org/x/net/http2/server.go
generated
vendored
@ -46,6 +46,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/http2/hpack"
|
||||
)
|
||||
|
||||
@ -1486,6 +1487,12 @@ func (sc *serverConn) processSettings(f *SettingsFrame) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if f.NumSettings() > 100 || f.HasDuplicates() {
|
||||
// This isn't actually in the spec, but hang up on
|
||||
// suspiciously large settings frames or those with
|
||||
// duplicate entries.
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
}
|
||||
if err := f.ForeachSetting(sc.processSetting); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1574,6 +1581,12 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
||||
// type PROTOCOL_ERROR."
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
}
|
||||
// RFC 7540, sec 6.1: If a DATA frame is received whose stream is not in
|
||||
// "open" or "half-closed (local)" state, the recipient MUST respond with a
|
||||
// stream error (Section 5.4.2) of type STREAM_CLOSED.
|
||||
if state == stateClosed {
|
||||
return streamError(id, ErrCodeStreamClosed)
|
||||
}
|
||||
if st == nil || state != stateOpen || st.gotTrailerHeader || st.resetQueued {
|
||||
// This includes sending a RST_STREAM if the stream is
|
||||
// in stateHalfClosedLocal (which currently means that
|
||||
@ -1607,7 +1620,10 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
||||
// Sender sending more than they'd declared?
|
||||
if st.declBodyBytes != -1 && st.bodyBytes+int64(len(data)) > st.declBodyBytes {
|
||||
st.body.CloseWithError(fmt.Errorf("sender tried to send more than declared Content-Length of %d bytes", st.declBodyBytes))
|
||||
return streamError(id, ErrCodeStreamClosed)
|
||||
// RFC 7540, sec 8.1.2.6: A request or response is also malformed if the
|
||||
// value of a content-length header field does not equal the sum of the
|
||||
// DATA frame payload lengths that form the body.
|
||||
return streamError(id, ErrCodeProtocol)
|
||||
}
|
||||
if f.Length > 0 {
|
||||
// Check whether the client has flow control quota.
|
||||
@ -1717,6 +1733,13 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
|
||||
// processing this frame.
|
||||
return nil
|
||||
}
|
||||
// RFC 7540, sec 5.1: If an endpoint receives additional frames, other than
|
||||
// WINDOW_UPDATE, PRIORITY, or RST_STREAM, for a stream that is in
|
||||
// this state, it MUST respond with a stream error (Section 5.4.2) of
|
||||
// type STREAM_CLOSED.
|
||||
if st.state == stateHalfClosedRemote {
|
||||
return streamError(id, ErrCodeStreamClosed)
|
||||
}
|
||||
return st.processTrailerHeaders(f)
|
||||
}
|
||||
|
||||
@ -1817,7 +1840,7 @@ func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error {
|
||||
if st.trailer != nil {
|
||||
for _, hf := range f.RegularFields() {
|
||||
key := sc.canonicalHeader(hf.Name)
|
||||
if !ValidTrailerHeader(key) {
|
||||
if !httpguts.ValidTrailerHeader(key) {
|
||||
// TODO: send more details to the peer somehow. But http2 has
|
||||
// no way to send debug data at a stream level. Discuss with
|
||||
// HTTP folk.
|
||||
@ -2284,7 +2307,7 @@ func (rws *responseWriterState) hasTrailers() bool { return len(rws.trailers) !=
|
||||
// written in the trailers at the end of the response.
|
||||
func (rws *responseWriterState) declareTrailer(k string) {
|
||||
k = http.CanonicalHeaderKey(k)
|
||||
if !ValidTrailerHeader(k) {
|
||||
if !httpguts.ValidTrailerHeader(k) {
|
||||
// Forbidden by RFC 7230, section 4.1.2.
|
||||
rws.conn.logf("ignoring invalid trailer %q", k)
|
||||
return
|
||||
@ -2323,7 +2346,15 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
|
||||
}
|
||||
_, hasContentType := rws.snapHeader["Content-Type"]
|
||||
if !hasContentType && bodyAllowedForStatus(rws.status) && len(p) > 0 {
|
||||
ctype = http.DetectContentType(p)
|
||||
if cto := rws.snapHeader.Get("X-Content-Type-Options"); strings.EqualFold("nosniff", cto) {
|
||||
// nosniff is an explicit directive not to guess a content-type.
|
||||
// Content-sniffing is no less susceptible to polyglot attacks via
|
||||
// hosted content when done on the server.
|
||||
ctype = "application/octet-stream"
|
||||
rws.conn.logf("http2: WriteHeader called with X-Content-Type-Options:nosniff but no Content-Type")
|
||||
} else {
|
||||
ctype = http.DetectContentType(p)
|
||||
}
|
||||
}
|
||||
var date string
|
||||
if _, ok := rws.snapHeader["Date"]; !ok {
|
||||
@ -2335,6 +2366,19 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
|
||||
foreachHeaderElement(v, rws.declareTrailer)
|
||||
}
|
||||
|
||||
// "Connection" headers aren't allowed in HTTP/2 (RFC 7540, 8.1.2.2),
|
||||
// but respect "Connection" == "close" to mean sending a GOAWAY and tearing
|
||||
// down the TCP connection when idle, like we do for HTTP/1.
|
||||
// TODO: remove more Connection-specific header fields here, in addition
|
||||
// to "Connection".
|
||||
if _, ok := rws.snapHeader["Connection"]; ok {
|
||||
v := rws.snapHeader.Get("Connection")
|
||||
delete(rws.snapHeader, "Connection")
|
||||
if v == "close" {
|
||||
rws.conn.startGracefulShutdown()
|
||||
}
|
||||
}
|
||||
|
||||
endStream := (rws.handlerDone && !rws.hasTrailers() && len(p) == 0) || isHeadResp
|
||||
err = rws.conn.writeHeaders(rws.stream, &writeResHeaders{
|
||||
streamID: rws.stream.id,
|
||||
@ -2838,41 +2882,6 @@ func new400Handler(err error) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// ValidTrailerHeader reports whether name is a valid header field name to appear
|
||||
// in trailers.
|
||||
// See: http://tools.ietf.org/html/rfc7230#section-4.1.2
|
||||
func ValidTrailerHeader(name string) bool {
|
||||
name = http.CanonicalHeaderKey(name)
|
||||
if strings.HasPrefix(name, "If-") || badTrailer[name] {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var badTrailer = map[string]bool{
|
||||
"Authorization": true,
|
||||
"Cache-Control": true,
|
||||
"Connection": true,
|
||||
"Content-Encoding": true,
|
||||
"Content-Length": true,
|
||||
"Content-Range": true,
|
||||
"Content-Type": true,
|
||||
"Expect": true,
|
||||
"Host": true,
|
||||
"Keep-Alive": true,
|
||||
"Max-Forwards": true,
|
||||
"Pragma": true,
|
||||
"Proxy-Authenticate": true,
|
||||
"Proxy-Authorization": true,
|
||||
"Proxy-Connection": true,
|
||||
"Range": true,
|
||||
"Realm": true,
|
||||
"Te": true,
|
||||
"Trailer": true,
|
||||
"Transfer-Encoding": true,
|
||||
"Www-Authenticate": true,
|
||||
}
|
||||
|
||||
// h1ServerKeepAlivesDisabled reports whether hs has its keep-alives
|
||||
// disabled. See comments on h1ServerShutdownChan above for why
|
||||
// the code is written this way.
|
||||
|
169
vendor/golang.org/x/net/http2/server_test.go
generated
vendored
169
vendor/golang.org/x/net/http2/server_test.go
generated
vendored
@ -1760,6 +1760,42 @@ func TestServer_Response_Data_Sniff_DoesntOverride(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestServer_Response_Nosniff_WithoutContentType(t *testing.T) {
|
||||
const msg = "<html>this is HTML."
|
||||
testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
|
||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
w.WriteHeader(200)
|
||||
io.WriteString(w, msg)
|
||||
return nil
|
||||
}, func(st *serverTester) {
|
||||
getSlash(st)
|
||||
hf := st.wantHeaders()
|
||||
if hf.StreamEnded() {
|
||||
t.Fatal("don't want END_STREAM, expecting data")
|
||||
}
|
||||
if !hf.HeadersEnded() {
|
||||
t.Fatal("want END_HEADERS flag")
|
||||
}
|
||||
goth := st.decodeHeader(hf.HeaderBlockFragment())
|
||||
wanth := [][2]string{
|
||||
{":status", "200"},
|
||||
{"x-content-type-options", "nosniff"},
|
||||
{"content-type", "application/octet-stream"},
|
||||
{"content-length", strconv.Itoa(len(msg))},
|
||||
}
|
||||
if !reflect.DeepEqual(goth, wanth) {
|
||||
t.Errorf("Got headers %v; want %v", goth, wanth)
|
||||
}
|
||||
df := st.wantData()
|
||||
if !df.StreamEnded() {
|
||||
t.Error("expected DATA to have END_STREAM flag")
|
||||
}
|
||||
if got := string(df.Data()); got != msg {
|
||||
t.Errorf("got DATA %q; want %q", got, msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestServer_Response_TransferEncoding_chunked(t *testing.T) {
|
||||
const msg = "hi"
|
||||
testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
|
||||
@ -2360,9 +2396,6 @@ func TestServer_NoCrash_HandlerClose_Then_ClientClose(t *testing.T) {
|
||||
// it did before.
|
||||
st.writeData(1, true, []byte("foo"))
|
||||
|
||||
// Get our flow control bytes back, since the handler didn't get them.
|
||||
st.wantWindowUpdate(0, uint32(len("foo")))
|
||||
|
||||
// Sent after a peer sends data anyway (admittedly the
|
||||
// previous RST_STREAM might've still been in-flight),
|
||||
// but they'll get the more friendly 'cancel' code
|
||||
@ -3723,3 +3756,133 @@ func TestIssue20704Race(t *testing.T) {
|
||||
resp.Body.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_Rejects_TooSmall(t *testing.T) {
|
||||
testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
|
||||
ioutil.ReadAll(r.Body)
|
||||
return nil
|
||||
}, func(st *serverTester) {
|
||||
st.writeHeaders(HeadersFrameParam{
|
||||
StreamID: 1, // clients send odd numbers
|
||||
BlockFragment: st.encodeHeader(
|
||||
":method", "POST",
|
||||
"content-length", "4",
|
||||
),
|
||||
EndStream: false, // to say DATA frames are coming
|
||||
EndHeaders: true,
|
||||
})
|
||||
st.writeData(1, true, []byte("12345"))
|
||||
|
||||
st.wantRSTStream(1, ErrCodeProtocol)
|
||||
})
|
||||
}
|
||||
|
||||
// Tests that a handler setting "Connection: close" results in a GOAWAY being sent,
|
||||
// and the connection still completing.
|
||||
func TestServerHandlerConnectionClose(t *testing.T) {
|
||||
unblockHandler := make(chan bool, 1)
|
||||
defer close(unblockHandler) // backup; in case of errors
|
||||
testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
|
||||
w.Header().Set("Connection", "close")
|
||||
w.Header().Set("Foo", "bar")
|
||||
w.(http.Flusher).Flush()
|
||||
<-unblockHandler
|
||||
return nil
|
||||
}, func(st *serverTester) {
|
||||
st.writeHeaders(HeadersFrameParam{
|
||||
StreamID: 1,
|
||||
BlockFragment: st.encodeHeader(),
|
||||
EndStream: true,
|
||||
EndHeaders: true,
|
||||
})
|
||||
var sawGoAway bool
|
||||
var sawRes bool
|
||||
for {
|
||||
f, err := st.readFrame()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
switch f := f.(type) {
|
||||
case *GoAwayFrame:
|
||||
sawGoAway = true
|
||||
unblockHandler <- true
|
||||
if f.LastStreamID != 1 || f.ErrCode != ErrCodeNo {
|
||||
t.Errorf("unexpected GOAWAY frame: %v", summarizeFrame(f))
|
||||
}
|
||||
case *HeadersFrame:
|
||||
goth := st.decodeHeader(f.HeaderBlockFragment())
|
||||
wanth := [][2]string{
|
||||
{":status", "200"},
|
||||
{"foo", "bar"},
|
||||
}
|
||||
if !reflect.DeepEqual(goth, wanth) {
|
||||
t.Errorf("got headers %v; want %v", goth, wanth)
|
||||
}
|
||||
sawRes = true
|
||||
case *DataFrame:
|
||||
if f.StreamID != 1 || !f.StreamEnded() || len(f.Data()) != 0 {
|
||||
t.Errorf("unexpected DATA frame: %v", summarizeFrame(f))
|
||||
}
|
||||
default:
|
||||
t.Logf("unexpected frame: %v", summarizeFrame(f))
|
||||
}
|
||||
}
|
||||
if !sawGoAway {
|
||||
t.Errorf("didn't see GOAWAY")
|
||||
}
|
||||
if !sawRes {
|
||||
t.Errorf("didn't see response")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestServer_Headers_HalfCloseRemote(t *testing.T) {
|
||||
var st *serverTester
|
||||
writeData := make(chan bool)
|
||||
writeHeaders := make(chan bool)
|
||||
leaveHandler := make(chan bool)
|
||||
st = newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if st.stream(1) == nil {
|
||||
t.Errorf("nil stream 1 in handler")
|
||||
}
|
||||
if got, want := st.streamState(1), stateOpen; got != want {
|
||||
t.Errorf("in handler, state is %v; want %v", got, want)
|
||||
}
|
||||
writeData <- true
|
||||
if n, err := r.Body.Read(make([]byte, 1)); n != 0 || err != io.EOF {
|
||||
t.Errorf("body read = %d, %v; want 0, EOF", n, err)
|
||||
}
|
||||
if got, want := st.streamState(1), stateHalfClosedRemote; got != want {
|
||||
t.Errorf("in handler, state is %v; want %v", got, want)
|
||||
}
|
||||
writeHeaders <- true
|
||||
|
||||
<-leaveHandler
|
||||
})
|
||||
st.greet()
|
||||
|
||||
st.writeHeaders(HeadersFrameParam{
|
||||
StreamID: 1,
|
||||
BlockFragment: st.encodeHeader(),
|
||||
EndStream: false, // keep it open
|
||||
EndHeaders: true,
|
||||
})
|
||||
<-writeData
|
||||
st.writeData(1, true, nil)
|
||||
|
||||
<-writeHeaders
|
||||
|
||||
st.writeHeaders(HeadersFrameParam{
|
||||
StreamID: 1,
|
||||
BlockFragment: st.encodeHeader(),
|
||||
EndStream: false, // keep it open
|
||||
EndHeaders: true,
|
||||
})
|
||||
|
||||
defer close(leaveHandler)
|
||||
|
||||
st.wantRSTStream(1, ErrCodeStreamClosed)
|
||||
}
|
||||
|
226
vendor/golang.org/x/net/http2/transport.go
generated
vendored
226
vendor/golang.org/x/net/http2/transport.go
generated
vendored
@ -21,15 +21,16 @@ import (
|
||||
mathrand "math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/http2/hpack"
|
||||
"golang.org/x/net/idna"
|
||||
"golang.org/x/net/lex/httplex"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -159,6 +160,7 @@ type ClientConn struct {
|
||||
cond *sync.Cond // hold mu; broadcast on flow/closed changes
|
||||
flow flow // our conn-level flow control quota (cs.flow is per stream)
|
||||
inflow flow // peer's conn-level flow control
|
||||
closing bool
|
||||
closed bool
|
||||
wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back
|
||||
goAway *GoAwayFrame // if non-nil, the GoAwayFrame we received
|
||||
@ -211,9 +213,10 @@ type clientStream struct {
|
||||
done chan struct{} // closed when stream remove from cc.streams map; close calls guarded by cc.mu
|
||||
|
||||
// owned by clientConnReadLoop:
|
||||
firstByte bool // got the first response byte
|
||||
pastHeaders bool // got first MetaHeadersFrame (actual headers)
|
||||
pastTrailers bool // got optional second MetaHeadersFrame (trailers)
|
||||
firstByte bool // got the first response byte
|
||||
pastHeaders bool // got first MetaHeadersFrame (actual headers)
|
||||
pastTrailers bool // got optional second MetaHeadersFrame (trailers)
|
||||
num1xx uint8 // number of 1xx responses seen
|
||||
|
||||
trailer http.Header // accumulated trailers
|
||||
resTrailer *http.Header // client's Response.Trailer
|
||||
@ -237,6 +240,17 @@ func awaitRequestCancel(req *http.Request, done <-chan struct{}) error {
|
||||
}
|
||||
}
|
||||
|
||||
var got1xxFuncForTests func(int, textproto.MIMEHeader) error
|
||||
|
||||
// get1xxTraceFunc returns the value of request's httptrace.ClientTrace.Got1xxResponse func,
|
||||
// if any. It returns nil if not set or if the Go version is too old.
|
||||
func (cs *clientStream) get1xxTraceFunc() func(int, textproto.MIMEHeader) error {
|
||||
if fn := got1xxFuncForTests; fn != nil {
|
||||
return fn
|
||||
}
|
||||
return traceGot1xxResponseFunc(cs.trace)
|
||||
}
|
||||
|
||||
// awaitRequestCancel waits for the user to cancel a request, its context to
|
||||
// expire, or for the request to be done (any way it might be removed from the
|
||||
// cc.streams map: peer reset, successful completion, TCP connection breakage,
|
||||
@ -423,27 +437,36 @@ func shouldRetryRequest(req *http.Request, err error, afterBodyWrite bool) (*htt
|
||||
if !canRetryError(err) {
|
||||
return nil, err
|
||||
}
|
||||
if !afterBodyWrite {
|
||||
return req, nil
|
||||
}
|
||||
// If the Body is nil (or http.NoBody), it's safe to reuse
|
||||
// this request and its Body.
|
||||
if req.Body == nil || reqBodyIsNoBody(req.Body) {
|
||||
return req, nil
|
||||
}
|
||||
// Otherwise we depend on the Request having its GetBody
|
||||
// func defined.
|
||||
|
||||
// If the request body can be reset back to its original
|
||||
// state via the optional req.GetBody, do that.
|
||||
getBody := reqGetBody(req) // Go 1.8: getBody = req.GetBody
|
||||
if getBody == nil {
|
||||
return nil, fmt.Errorf("http2: Transport: cannot retry err [%v] after Request.Body was written; define Request.GetBody to avoid this error", err)
|
||||
if getBody != nil {
|
||||
// TODO: consider a req.Body.Close here? or audit that all caller paths do?
|
||||
body, err := getBody()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newReq := *req
|
||||
newReq.Body = body
|
||||
return &newReq, nil
|
||||
}
|
||||
body, err := getBody()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
// The Request.Body can't reset back to the beginning, but we
|
||||
// don't seem to have started to read from it yet, so reuse
|
||||
// the request directly. The "afterBodyWrite" means the
|
||||
// bodyWrite process has started, which becomes true before
|
||||
// the first Read.
|
||||
if !afterBodyWrite {
|
||||
return req, nil
|
||||
}
|
||||
newReq := *req
|
||||
newReq.Body = body
|
||||
return &newReq, nil
|
||||
|
||||
return nil, fmt.Errorf("http2: Transport: cannot retry err [%v] after Request.Body was written; define Request.GetBody to avoid this error", err)
|
||||
}
|
||||
|
||||
func canRetryError(err error) bool {
|
||||
@ -567,6 +590,10 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
|
||||
// henc in response to SETTINGS frames?
|
||||
cc.henc = hpack.NewEncoder(&cc.hbuf)
|
||||
|
||||
if t.AllowHTTP {
|
||||
cc.nextStreamID = 3
|
||||
}
|
||||
|
||||
if cs, ok := c.(connectionStater); ok {
|
||||
state := cs.ConnectionState()
|
||||
cc.tlsState = &state
|
||||
@ -626,12 +653,32 @@ func (cc *ClientConn) CanTakeNewRequest() bool {
|
||||
return cc.canTakeNewRequestLocked()
|
||||
}
|
||||
|
||||
func (cc *ClientConn) canTakeNewRequestLocked() bool {
|
||||
// clientConnIdleState describes the suitability of a client
|
||||
// connection to initiate a new RoundTrip request.
|
||||
type clientConnIdleState struct {
|
||||
canTakeNewRequest bool
|
||||
freshConn bool // whether it's unused by any previous request
|
||||
}
|
||||
|
||||
func (cc *ClientConn) idleState() clientConnIdleState {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
return cc.idleStateLocked()
|
||||
}
|
||||
|
||||
func (cc *ClientConn) idleStateLocked() (st clientConnIdleState) {
|
||||
if cc.singleUse && cc.nextStreamID > 1 {
|
||||
return false
|
||||
return
|
||||
}
|
||||
return cc.goAway == nil && !cc.closed &&
|
||||
st.canTakeNewRequest = cc.goAway == nil && !cc.closed && !cc.closing &&
|
||||
int64(cc.nextStreamID)+int64(cc.pendingRequests) < math.MaxInt32
|
||||
st.freshConn = cc.nextStreamID == 1 && st.canTakeNewRequest
|
||||
return
|
||||
}
|
||||
|
||||
func (cc *ClientConn) canTakeNewRequestLocked() bool {
|
||||
st := cc.idleStateLocked()
|
||||
return st.canTakeNewRequest
|
||||
}
|
||||
|
||||
// onIdleTimeout is called from a time.AfterFunc goroutine. It will
|
||||
@ -661,6 +708,88 @@ func (cc *ClientConn) closeIfIdle() {
|
||||
cc.tconn.Close()
|
||||
}
|
||||
|
||||
var shutdownEnterWaitStateHook = func() {}
|
||||
|
||||
// Shutdown gracefully close the client connection, waiting for running streams to complete.
|
||||
// Public implementation is in go17.go and not_go17.go
|
||||
func (cc *ClientConn) shutdown(ctx contextContext) error {
|
||||
if err := cc.sendGoAway(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Wait for all in-flight streams to complete or connection to close
|
||||
done := make(chan error, 1)
|
||||
cancelled := false // guarded by cc.mu
|
||||
go func() {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
for {
|
||||
if len(cc.streams) == 0 || cc.closed {
|
||||
cc.closed = true
|
||||
done <- cc.tconn.Close()
|
||||
break
|
||||
}
|
||||
if cancelled {
|
||||
break
|
||||
}
|
||||
cc.cond.Wait()
|
||||
}
|
||||
}()
|
||||
shutdownEnterWaitStateHook()
|
||||
select {
|
||||
case err := <-done:
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
cc.mu.Lock()
|
||||
// Free the goroutine above
|
||||
cancelled = true
|
||||
cc.cond.Broadcast()
|
||||
cc.mu.Unlock()
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
func (cc *ClientConn) sendGoAway() error {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
cc.wmu.Lock()
|
||||
defer cc.wmu.Unlock()
|
||||
if cc.closing {
|
||||
// GOAWAY sent already
|
||||
return nil
|
||||
}
|
||||
// Send a graceful shutdown frame to server
|
||||
maxStreamID := cc.nextStreamID
|
||||
if err := cc.fr.WriteGoAway(maxStreamID, ErrCodeNo, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cc.bw.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Prevent new requests
|
||||
cc.closing = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes the client connection immediately.
|
||||
//
|
||||
// In-flight requests are interrupted. For a graceful shutdown, use Shutdown instead.
|
||||
func (cc *ClientConn) Close() error {
|
||||
cc.mu.Lock()
|
||||
defer cc.cond.Broadcast()
|
||||
defer cc.mu.Unlock()
|
||||
err := errors.New("http2: client connection force closed via ClientConn.Close")
|
||||
for id, cs := range cc.streams {
|
||||
select {
|
||||
case cs.resc <- resAndError{err: err}:
|
||||
default:
|
||||
}
|
||||
cs.bufPipe.CloseWithError(err)
|
||||
delete(cc.streams, id)
|
||||
}
|
||||
cc.closed = true
|
||||
return cc.tconn.Close()
|
||||
}
|
||||
|
||||
const maxAllocFrameSize = 512 << 10
|
||||
|
||||
// frameBuffer returns a scratch buffer suitable for writing DATA frames.
|
||||
@ -743,7 +872,7 @@ func checkConnHeaders(req *http.Request) error {
|
||||
if vv := req.Header["Transfer-Encoding"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && vv[0] != "chunked") {
|
||||
return fmt.Errorf("http2: invalid Transfer-Encoding request header: %q", vv)
|
||||
}
|
||||
if vv := req.Header["Connection"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && vv[0] != "close" && vv[0] != "keep-alive") {
|
||||
if vv := req.Header["Connection"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && !strings.EqualFold(vv[0], "close") && !strings.EqualFold(vv[0], "keep-alive")) {
|
||||
return fmt.Errorf("http2: invalid Connection request header: %q", vv)
|
||||
}
|
||||
return nil
|
||||
@ -951,6 +1080,9 @@ func (cc *ClientConn) awaitOpenSlotForRequest(req *http.Request) error {
|
||||
for {
|
||||
cc.lastActive = time.Now()
|
||||
if cc.closed || !cc.canTakeNewRequestLocked() {
|
||||
if waitingForConn != nil {
|
||||
close(waitingForConn)
|
||||
}
|
||||
return errClientConnUnusable
|
||||
}
|
||||
if int64(len(cc.streams))+1 <= int64(cc.maxConcurrentStreams) {
|
||||
@ -1174,7 +1306,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
|
||||
if host == "" {
|
||||
host = req.URL.Host
|
||||
}
|
||||
host, err := httplex.PunycodeHostPort(host)
|
||||
host, err := httpguts.PunycodeHostPort(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1199,11 +1331,11 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
|
||||
// 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 !httplex.ValidHeaderFieldName(k) {
|
||||
if !httpguts.ValidHeaderFieldName(k) {
|
||||
return nil, fmt.Errorf("invalid HTTP header name %q", k)
|
||||
}
|
||||
for _, v := range vv {
|
||||
if !httplex.ValidHeaderFieldValue(v) {
|
||||
if !httpguts.ValidHeaderFieldValue(v) {
|
||||
return nil, fmt.Errorf("invalid HTTP header value %q for header %q", v, k)
|
||||
}
|
||||
}
|
||||
@ -1284,9 +1416,16 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
|
||||
return nil, errRequestHeaderListSize
|
||||
}
|
||||
|
||||
trace := requestTrace(req)
|
||||
traceHeaders := traceHasWroteHeaderField(trace)
|
||||
|
||||
// Header list size is ok. Write the headers.
|
||||
enumerateHeaders(func(name, value string) {
|
||||
cc.writeHeader(strings.ToLower(name), value)
|
||||
name = strings.ToLower(name)
|
||||
cc.writeHeader(name, value)
|
||||
if traceHeaders {
|
||||
traceWroteHeaderField(trace, name, value)
|
||||
}
|
||||
})
|
||||
|
||||
return cc.hbuf.Bytes(), nil
|
||||
@ -1608,8 +1747,7 @@ func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error {
|
||||
// is the detail.
|
||||
//
|
||||
// As a special case, handleResponse may return (nil, nil) to skip the
|
||||
// frame (currently only used for 100 expect continue). This special
|
||||
// case is going away after Issue 13851 is fixed.
|
||||
// frame (currently only used for 1xx responses).
|
||||
func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFrame) (*http.Response, error) {
|
||||
if f.Truncated {
|
||||
return nil, errResponseHeaderListSize
|
||||
@ -1624,15 +1762,6 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
|
||||
return nil, errors.New("malformed response from server: malformed non-numeric status pseudo header")
|
||||
}
|
||||
|
||||
if statusCode == 100 {
|
||||
traceGot100Continue(cs.trace)
|
||||
if cs.on100 != nil {
|
||||
cs.on100() // forces any write delay timer to fire
|
||||
}
|
||||
cs.pastHeaders = false // do it all again
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
header := make(http.Header)
|
||||
res := &http.Response{
|
||||
Proto: "HTTP/2.0",
|
||||
@ -1657,6 +1786,27 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
|
||||
}
|
||||
}
|
||||
|
||||
if statusCode >= 100 && statusCode <= 199 {
|
||||
cs.num1xx++
|
||||
const max1xxResponses = 5 // arbitrary bound on number of informational responses, same as net/http
|
||||
if cs.num1xx > max1xxResponses {
|
||||
return nil, errors.New("http2: too many 1xx informational responses")
|
||||
}
|
||||
if fn := cs.get1xxTraceFunc(); fn != nil {
|
||||
if err := fn(statusCode, textproto.MIMEHeader(header)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if statusCode == 100 {
|
||||
traceGot100Continue(cs.trace)
|
||||
if cs.on100 != nil {
|
||||
cs.on100() // forces any write delay timer to fire
|
||||
}
|
||||
}
|
||||
cs.pastHeaders = false // do it all again
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
streamEnded := f.StreamEnded()
|
||||
isHead := cs.req.Method == "HEAD"
|
||||
if !streamEnded || isHead {
|
||||
@ -2244,7 +2394,7 @@ func (t *Transport) getBodyWriterState(cs *clientStream, body io.Reader) (s body
|
||||
}
|
||||
s.delay = t.expectContinueTimeout()
|
||||
if s.delay == 0 ||
|
||||
!httplex.HeaderValuesContainsToken(
|
||||
!httpguts.HeaderValuesContainsToken(
|
||||
cs.req.Header["Expect"],
|
||||
"100-continue") {
|
||||
return
|
||||
@ -2299,5 +2449,5 @@ func (s bodyWriterState) scheduleBodyWrite() {
|
||||
// isConnectionCloseRequest reports whether req should use its own
|
||||
// connection for a single request and then close the connection.
|
||||
func isConnectionCloseRequest(req *http.Request) bool {
|
||||
return req.Close || httplex.HeaderValuesContainsToken(req.Header["Connection"], "close")
|
||||
return req.Close || httpguts.HeaderValuesContainsToken(req.Header["Connection"], "close")
|
||||
}
|
||||
|
356
vendor/golang.org/x/net/http2/transport_test.go
generated
vendored
356
vendor/golang.org/x/net/http2/transport_test.go
generated
vendored
@ -18,6 +18,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/textproto"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
@ -30,6 +31,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/net/http2/hpack"
|
||||
)
|
||||
|
||||
@ -41,12 +43,13 @@ var (
|
||||
|
||||
var tlsConfigInsecure = &tls.Config{InsecureSkipVerify: true}
|
||||
|
||||
type testContext struct{}
|
||||
var canceledCtx context.Context
|
||||
|
||||
func (testContext) Done() <-chan struct{} { return make(chan struct{}) }
|
||||
func (testContext) Err() error { panic("should not be called") }
|
||||
func (testContext) Deadline() (deadline time.Time, ok bool) { return time.Time{}, false }
|
||||
func (testContext) Value(key interface{}) interface{} { return nil }
|
||||
func init() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
canceledCtx = ctx
|
||||
}
|
||||
|
||||
func TestTransportExternal(t *testing.T) {
|
||||
if !*extNet {
|
||||
@ -1190,6 +1193,77 @@ func testTransportResPattern(t *testing.T, expect100Continue, resHeader headerTy
|
||||
ct.run()
|
||||
}
|
||||
|
||||
// Issue 26189, Issue 17739: ignore unknown 1xx responses
|
||||
func TestTransportUnknown1xx(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
defer func() { got1xxFuncForTests = nil }()
|
||||
got1xxFuncForTests = func(code int, header textproto.MIMEHeader) error {
|
||||
fmt.Fprintf(&buf, "code=%d header=%v\n", code, header)
|
||||
return nil
|
||||
}
|
||||
|
||||
ct := newClientTester(t)
|
||||
ct.client = func() error {
|
||||
req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
|
||||
res, err := ct.tr.RoundTrip(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("RoundTrip: %v", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != 204 {
|
||||
return fmt.Errorf("status code = %v; want 204", res.StatusCode)
|
||||
}
|
||||
want := `code=110 header=map[Foo-Bar:[110]]
|
||||
code=111 header=map[Foo-Bar:[111]]
|
||||
code=112 header=map[Foo-Bar:[112]]
|
||||
code=113 header=map[Foo-Bar:[113]]
|
||||
code=114 header=map[Foo-Bar:[114]]
|
||||
`
|
||||
if got := buf.String(); got != want {
|
||||
t.Errorf("Got trace:\n%s\nWant:\n%s", got, want)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
ct.server = func() error {
|
||||
ct.greet()
|
||||
var buf bytes.Buffer
|
||||
enc := hpack.NewEncoder(&buf)
|
||||
|
||||
for {
|
||||
f, err := ct.fr.ReadFrame()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch f := f.(type) {
|
||||
case *WindowUpdateFrame, *SettingsFrame:
|
||||
case *HeadersFrame:
|
||||
for i := 110; i <= 114; i++ {
|
||||
buf.Reset()
|
||||
enc.WriteField(hpack.HeaderField{Name: ":status", Value: fmt.Sprint(i)})
|
||||
enc.WriteField(hpack.HeaderField{Name: "foo-bar", Value: fmt.Sprint(i)})
|
||||
ct.fr.WriteHeaders(HeadersFrameParam{
|
||||
StreamID: f.StreamID,
|
||||
EndHeaders: true,
|
||||
EndStream: false,
|
||||
BlockFragment: buf.Bytes(),
|
||||
})
|
||||
}
|
||||
buf.Reset()
|
||||
enc.WriteField(hpack.HeaderField{Name: ":status", Value: "204"})
|
||||
ct.fr.WriteHeaders(HeadersFrameParam{
|
||||
StreamID: f.StreamID,
|
||||
EndHeaders: true,
|
||||
EndStream: false,
|
||||
BlockFragment: buf.Bytes(),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
ct.run()
|
||||
|
||||
}
|
||||
|
||||
func TestTransportReceiveUndeclaredTrailer(t *testing.T) {
|
||||
ct := newClientTester(t)
|
||||
ct.client = func() error {
|
||||
@ -2022,6 +2096,11 @@ func TestTransportRejectsConnHeaders(t *testing.T) {
|
||||
value: []string{"close"},
|
||||
want: "Accept-Encoding,User-Agent",
|
||||
},
|
||||
{
|
||||
key: "Connection",
|
||||
value: []string{"CLoSe"},
|
||||
want: "Accept-Encoding,User-Agent",
|
||||
},
|
||||
{
|
||||
key: "Connection",
|
||||
value: []string{"close", "something-else"},
|
||||
@ -2032,6 +2111,11 @@ func TestTransportRejectsConnHeaders(t *testing.T) {
|
||||
value: []string{"keep-alive"},
|
||||
want: "Accept-Encoding,User-Agent",
|
||||
},
|
||||
{
|
||||
key: "Connection",
|
||||
value: []string{"Keep-ALIVE"},
|
||||
want: "Accept-Encoding,User-Agent",
|
||||
},
|
||||
{
|
||||
key: "Proxy-Connection", // just deleted and ignored
|
||||
value: []string{"keep-alive"},
|
||||
@ -2394,11 +2478,12 @@ func TestTransportHandlerBodyClose(t *testing.T) {
|
||||
}
|
||||
tr.CloseIdleConnections()
|
||||
|
||||
gd := runtime.NumGoroutine() - g0
|
||||
if gd > numReq/2 {
|
||||
if !waitCondition(5*time.Second, 100*time.Millisecond, func() bool {
|
||||
gd := runtime.NumGoroutine() - g0
|
||||
return gd < numReq/2
|
||||
}) {
|
||||
t.Errorf("appeared to leak goroutines")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// https://golang.org/issue/15930
|
||||
@ -3043,7 +3128,7 @@ func TestClientConnPing(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = cc.Ping(testContext{}); err != nil {
|
||||
if err = cc.Ping(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@ -3845,3 +3930,256 @@ func BenchmarkClientRequestHeaders(b *testing.B) {
|
||||
b.Run(" 100 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 100) })
|
||||
b.Run("1000 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 1000) })
|
||||
}
|
||||
|
||||
func activeStreams(cc *ClientConn) int {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
return len(cc.streams)
|
||||
}
|
||||
|
||||
type closeMode int
|
||||
|
||||
const (
|
||||
closeAtHeaders closeMode = iota
|
||||
closeAtBody
|
||||
shutdown
|
||||
shutdownCancel
|
||||
)
|
||||
|
||||
// See golang.org/issue/17292
|
||||
func testClientConnClose(t *testing.T, closeMode closeMode) {
|
||||
clientDone := make(chan struct{})
|
||||
defer close(clientDone)
|
||||
handlerDone := make(chan struct{})
|
||||
closeDone := make(chan struct{})
|
||||
beforeHeader := func() {}
|
||||
bodyWrite := func(w http.ResponseWriter) {}
|
||||
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
defer close(handlerDone)
|
||||
beforeHeader()
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.(http.Flusher).Flush()
|
||||
bodyWrite(w)
|
||||
select {
|
||||
case <-w.(http.CloseNotifier).CloseNotify():
|
||||
// client closed connection before completion
|
||||
if closeMode == shutdown || closeMode == shutdownCancel {
|
||||
t.Error("expected request to complete")
|
||||
}
|
||||
case <-clientDone:
|
||||
if closeMode == closeAtHeaders || closeMode == closeAtBody {
|
||||
t.Error("expected connection closed by client")
|
||||
}
|
||||
}
|
||||
}, optOnlyServer)
|
||||
defer st.Close()
|
||||
tr := &Transport{TLSClientConfig: tlsConfigInsecure}
|
||||
defer tr.CloseIdleConnections()
|
||||
cc, err := tr.dialClientConn(st.ts.Listener.Addr().String(), false)
|
||||
req, err := http.NewRequest("GET", st.ts.URL, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if closeMode == closeAtHeaders {
|
||||
beforeHeader = func() {
|
||||
if err := cc.Close(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
close(closeDone)
|
||||
}
|
||||
}
|
||||
var sendBody chan struct{}
|
||||
if closeMode == closeAtBody {
|
||||
sendBody = make(chan struct{})
|
||||
bodyWrite = func(w http.ResponseWriter) {
|
||||
<-sendBody
|
||||
b := make([]byte, 32)
|
||||
w.Write(b)
|
||||
w.(http.Flusher).Flush()
|
||||
if err := cc.Close(); err != nil {
|
||||
t.Errorf("unexpected ClientConn close error: %v", err)
|
||||
}
|
||||
close(closeDone)
|
||||
w.Write(b)
|
||||
w.(http.Flusher).Flush()
|
||||
}
|
||||
}
|
||||
res, err := cc.RoundTrip(req)
|
||||
if res != nil {
|
||||
defer res.Body.Close()
|
||||
}
|
||||
if closeMode == closeAtHeaders {
|
||||
got := fmt.Sprint(err)
|
||||
want := "http2: client connection force closed via ClientConn.Close"
|
||||
if got != want {
|
||||
t.Fatalf("RoundTrip error = %v, want %v", got, want)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("RoundTrip: %v", err)
|
||||
}
|
||||
if got, want := activeStreams(cc), 1; got != want {
|
||||
t.Errorf("got %d active streams, want %d", got, want)
|
||||
}
|
||||
}
|
||||
switch closeMode {
|
||||
case shutdownCancel:
|
||||
if err = cc.Shutdown(canceledCtx); err != errCanceled {
|
||||
t.Errorf("got %v, want %v", err, errCanceled)
|
||||
}
|
||||
if cc.closing == false {
|
||||
t.Error("expected closing to be true")
|
||||
}
|
||||
if cc.CanTakeNewRequest() == true {
|
||||
t.Error("CanTakeNewRequest to return false")
|
||||
}
|
||||
if v, want := len(cc.streams), 1; v != want {
|
||||
t.Errorf("expected %d active streams, got %d", want, v)
|
||||
}
|
||||
clientDone <- struct{}{}
|
||||
<-handlerDone
|
||||
case shutdown:
|
||||
wait := make(chan struct{})
|
||||
shutdownEnterWaitStateHook = func() {
|
||||
close(wait)
|
||||
shutdownEnterWaitStateHook = func() {}
|
||||
}
|
||||
defer func() { shutdownEnterWaitStateHook = func() {} }()
|
||||
shutdown := make(chan struct{}, 1)
|
||||
go func() {
|
||||
if err = cc.Shutdown(context.Background()); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
close(shutdown)
|
||||
}()
|
||||
// Let the shutdown to enter wait state
|
||||
<-wait
|
||||
cc.mu.Lock()
|
||||
if cc.closing == false {
|
||||
t.Error("expected closing to be true")
|
||||
}
|
||||
cc.mu.Unlock()
|
||||
if cc.CanTakeNewRequest() == true {
|
||||
t.Error("CanTakeNewRequest to return false")
|
||||
}
|
||||
if got, want := activeStreams(cc), 1; got != want {
|
||||
t.Errorf("got %d active streams, want %d", got, want)
|
||||
}
|
||||
// Let the active request finish
|
||||
clientDone <- struct{}{}
|
||||
// Wait for the shutdown to end
|
||||
select {
|
||||
case <-shutdown:
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("expected server connection to close")
|
||||
}
|
||||
case closeAtHeaders, closeAtBody:
|
||||
if closeMode == closeAtBody {
|
||||
go close(sendBody)
|
||||
if _, err := io.Copy(ioutil.Discard, res.Body); err == nil {
|
||||
t.Error("expected a Copy error, got nil")
|
||||
}
|
||||
}
|
||||
<-closeDone
|
||||
if got, want := activeStreams(cc), 0; got != want {
|
||||
t.Errorf("got %d active streams, want %d", got, want)
|
||||
}
|
||||
// wait for server to get the connection close notice
|
||||
select {
|
||||
case <-handlerDone:
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("expected server connection to close")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The client closes the connection just after the server got the client's HEADERS
|
||||
// frame, but before the server sends its HEADERS response back. The expected
|
||||
// result is an error on RoundTrip explaining the client closed the connection.
|
||||
func TestClientConnCloseAtHeaders(t *testing.T) {
|
||||
testClientConnClose(t, closeAtHeaders)
|
||||
}
|
||||
|
||||
// The client closes the connection between two server's response DATA frames.
|
||||
// The expected behavior is a response body io read error on the client.
|
||||
func TestClientConnCloseAtBody(t *testing.T) {
|
||||
testClientConnClose(t, closeAtBody)
|
||||
}
|
||||
|
||||
// The client sends a GOAWAY frame before the server finished processing a request.
|
||||
// We expect the connection not to close until the request is completed.
|
||||
func TestClientConnShutdown(t *testing.T) {
|
||||
testClientConnClose(t, shutdown)
|
||||
}
|
||||
|
||||
// The client sends a GOAWAY frame before the server finishes processing a request,
|
||||
// but cancels the passed context before the request is completed. The expected
|
||||
// behavior is the client closing the connection after the context is canceled.
|
||||
func TestClientConnShutdownCancel(t *testing.T) {
|
||||
testClientConnClose(t, shutdownCancel)
|
||||
}
|
||||
|
||||
// Issue 25009: use Request.GetBody if present, even if it seems like
|
||||
// we might not need it. Apparently something else can still read from
|
||||
// the original request body. Data race? In any case, rewinding
|
||||
// unconditionally on retry is a nicer model anyway and should
|
||||
// simplify code in the future (after the Go 1.11 freeze)
|
||||
func TestTransportUsesGetBodyWhenPresent(t *testing.T) {
|
||||
calls := 0
|
||||
someBody := func() io.ReadCloser {
|
||||
return struct{ io.ReadCloser }{ioutil.NopCloser(bytes.NewReader(nil))}
|
||||
}
|
||||
req := &http.Request{
|
||||
Body: someBody(),
|
||||
GetBody: func() (io.ReadCloser, error) {
|
||||
calls++
|
||||
return someBody(), nil
|
||||
},
|
||||
}
|
||||
|
||||
afterBodyWrite := false // pretend we haven't read+written the body yet
|
||||
req2, err := shouldRetryRequest(req, errClientConnUnusable, afterBodyWrite)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if calls != 1 {
|
||||
t.Errorf("Calls = %d; want 1", calls)
|
||||
}
|
||||
if req2 == req {
|
||||
t.Error("req2 changed")
|
||||
}
|
||||
if req2 == nil {
|
||||
t.Fatal("req2 is nil")
|
||||
}
|
||||
if req2.Body == nil {
|
||||
t.Fatal("req2.Body is nil")
|
||||
}
|
||||
if req2.GetBody == nil {
|
||||
t.Fatal("req2.GetBody is nil")
|
||||
}
|
||||
if req2.Body == req.Body {
|
||||
t.Error("req2.Body unchanged")
|
||||
}
|
||||
}
|
||||
|
||||
// Issue 22891: verify that the "https" altproto we register with net/http
|
||||
// is a certain type: a struct with one field with our *http2.Transport in it.
|
||||
func TestNoDialH2RoundTripperType(t *testing.T) {
|
||||
t1 := new(http.Transport)
|
||||
t2 := new(Transport)
|
||||
rt := noDialH2RoundTripper{t2}
|
||||
if err := registerHTTPSProtocol(t1, rt); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rv := reflect.ValueOf(rt)
|
||||
if rv.Type().Kind() != reflect.Struct {
|
||||
t.Fatalf("kind = %v; net/http expects struct", rv.Type().Kind())
|
||||
}
|
||||
if n := rv.Type().NumField(); n != 1 {
|
||||
t.Fatalf("fields = %d; net/http expects 1", n)
|
||||
}
|
||||
v := rv.Field(0)
|
||||
if _, ok := v.Interface().(*Transport); !ok {
|
||||
t.Fatalf("wrong kind %T; want *Transport", v.Interface())
|
||||
}
|
||||
}
|
||||
|
4
vendor/golang.org/x/net/http2/write.go
generated
vendored
4
vendor/golang.org/x/net/http2/write.go
generated
vendored
@ -11,8 +11,8 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/http2/hpack"
|
||||
"golang.org/x/net/lex/httplex"
|
||||
)
|
||||
|
||||
// writeFramer is implemented by any type that is used to write frames.
|
||||
@ -350,7 +350,7 @@ func encodeHeaders(enc *hpack.Encoder, h http.Header, keys []string) {
|
||||
}
|
||||
isTE := k == "transfer-encoding"
|
||||
for _, v := range vv {
|
||||
if !httplex.ValidHeaderFieldValue(v) {
|
||||
if !httpguts.ValidHeaderFieldValue(v) {
|
||||
// TODO: return an error? golang.org/issue/14048
|
||||
// For now just omit it.
|
||||
continue
|
||||
|
Reference in New Issue
Block a user