summaryrefslogtreecommitdiff
path: root/vendor/golang.org/x/net/http2
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/net/http2')
-rw-r--r--vendor/golang.org/x/net/http2/h2c/h2c.go6
-rw-r--r--vendor/golang.org/x/net/http2/headermap.go18
-rw-r--r--vendor/golang.org/x/net/http2/hpack/encode.go5
-rw-r--r--vendor/golang.org/x/net/http2/hpack/static_table.go188
-rw-r--r--vendor/golang.org/x/net/http2/hpack/tables.go78
-rw-r--r--vendor/golang.org/x/net/http2/server.go236
-rw-r--r--vendor/golang.org/x/net/http2/transport.go113
7 files changed, 487 insertions, 157 deletions
diff --git a/vendor/golang.org/x/net/http2/h2c/h2c.go b/vendor/golang.org/x/net/http2/h2c/h2c.go
index 2b77ffdaf..a72bbed1b 100644
--- a/vendor/golang.org/x/net/http2/h2c/h2c.go
+++ b/vendor/golang.org/x/net/http2/h2c/h2c.go
@@ -109,6 +109,7 @@ func (s h2cHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if http2VerboseLogs {
log.Printf("h2c: error h2c upgrade: %v", err)
}
+ w.WriteHeader(http.StatusInternalServerError)
return
}
defer conn.Close()
@@ -167,7 +168,10 @@ func h2cUpgrade(w http.ResponseWriter, r *http.Request) (_ net.Conn, settings []
return nil, nil, errors.New("h2c: connection does not support Hijack")
}
- body, _ := io.ReadAll(r.Body)
+ body, err := io.ReadAll(r.Body)
+ if err != nil {
+ return nil, nil, err
+ }
r.Body = io.NopCloser(bytes.NewBuffer(body))
conn, rw, err := hijacker.Hijack()
diff --git a/vendor/golang.org/x/net/http2/headermap.go b/vendor/golang.org/x/net/http2/headermap.go
index 9e12941da..149b3dd20 100644
--- a/vendor/golang.org/x/net/http2/headermap.go
+++ b/vendor/golang.org/x/net/http2/headermap.go
@@ -27,7 +27,14 @@ func buildCommonHeaderMaps() {
"accept-language",
"accept-ranges",
"age",
+ "access-control-allow-credentials",
+ "access-control-allow-headers",
+ "access-control-allow-methods",
"access-control-allow-origin",
+ "access-control-expose-headers",
+ "access-control-max-age",
+ "access-control-request-headers",
+ "access-control-request-method",
"allow",
"authorization",
"cache-control",
@@ -53,6 +60,7 @@ func buildCommonHeaderMaps() {
"link",
"location",
"max-forwards",
+ "origin",
"proxy-authenticate",
"proxy-authorization",
"range",
@@ -68,6 +76,8 @@ func buildCommonHeaderMaps() {
"vary",
"via",
"www-authenticate",
+ "x-forwarded-for",
+ "x-forwarded-proto",
}
commonLowerHeader = make(map[string]string, len(common))
commonCanonHeader = make(map[string]string, len(common))
@@ -85,3 +95,11 @@ func lowerHeader(v string) (lower string, ascii bool) {
}
return asciiToLower(v)
}
+
+func canonicalHeader(v string) string {
+ buildCommonHeaderMapsOnce()
+ if s, ok := commonCanonHeader[v]; ok {
+ return s
+ }
+ return http.CanonicalHeaderKey(v)
+}
diff --git a/vendor/golang.org/x/net/http2/hpack/encode.go b/vendor/golang.org/x/net/http2/hpack/encode.go
index 6886dc163..46219da2b 100644
--- a/vendor/golang.org/x/net/http2/hpack/encode.go
+++ b/vendor/golang.org/x/net/http2/hpack/encode.go
@@ -116,6 +116,11 @@ func (e *Encoder) SetMaxDynamicTableSize(v uint32) {
e.dynTab.setMaxSize(v)
}
+// MaxDynamicTableSize returns the current dynamic header table size.
+func (e *Encoder) MaxDynamicTableSize() (v uint32) {
+ return e.dynTab.maxSize
+}
+
// SetMaxDynamicTableSizeLimit changes the maximum value that can be
// specified in SetMaxDynamicTableSize to v. By default, it is set to
// 4096, which is the same size of the default dynamic header table
diff --git a/vendor/golang.org/x/net/http2/hpack/static_table.go b/vendor/golang.org/x/net/http2/hpack/static_table.go
new file mode 100644
index 000000000..754a1eb91
--- /dev/null
+++ b/vendor/golang.org/x/net/http2/hpack/static_table.go
@@ -0,0 +1,188 @@
+// go generate gen.go
+// Code generated by the command above; DO NOT EDIT.
+
+package hpack
+
+var staticTable = &headerFieldTable{
+ evictCount: 0,
+ byName: map[string]uint64{
+ ":authority": 1,
+ ":method": 3,
+ ":path": 5,
+ ":scheme": 7,
+ ":status": 14,
+ "accept-charset": 15,
+ "accept-encoding": 16,
+ "accept-language": 17,
+ "accept-ranges": 18,
+ "accept": 19,
+ "access-control-allow-origin": 20,
+ "age": 21,
+ "allow": 22,
+ "authorization": 23,
+ "cache-control": 24,
+ "content-disposition": 25,
+ "content-encoding": 26,
+ "content-language": 27,
+ "content-length": 28,
+ "content-location": 29,
+ "content-range": 30,
+ "content-type": 31,
+ "cookie": 32,
+ "date": 33,
+ "etag": 34,
+ "expect": 35,
+ "expires": 36,
+ "from": 37,
+ "host": 38,
+ "if-match": 39,
+ "if-modified-since": 40,
+ "if-none-match": 41,
+ "if-range": 42,
+ "if-unmodified-since": 43,
+ "last-modified": 44,
+ "link": 45,
+ "location": 46,
+ "max-forwards": 47,
+ "proxy-authenticate": 48,
+ "proxy-authorization": 49,
+ "range": 50,
+ "referer": 51,
+ "refresh": 52,
+ "retry-after": 53,
+ "server": 54,
+ "set-cookie": 55,
+ "strict-transport-security": 56,
+ "transfer-encoding": 57,
+ "user-agent": 58,
+ "vary": 59,
+ "via": 60,
+ "www-authenticate": 61,
+ },
+ byNameValue: map[pairNameValue]uint64{
+ {name: ":authority", value: ""}: 1,
+ {name: ":method", value: "GET"}: 2,
+ {name: ":method", value: "POST"}: 3,
+ {name: ":path", value: "/"}: 4,
+ {name: ":path", value: "/index.html"}: 5,
+ {name: ":scheme", value: "http"}: 6,
+ {name: ":scheme", value: "https"}: 7,
+ {name: ":status", value: "200"}: 8,
+ {name: ":status", value: "204"}: 9,
+ {name: ":status", value: "206"}: 10,
+ {name: ":status", value: "304"}: 11,
+ {name: ":status", value: "400"}: 12,
+ {name: ":status", value: "404"}: 13,
+ {name: ":status", value: "500"}: 14,
+ {name: "accept-charset", value: ""}: 15,
+ {name: "accept-encoding", value: "gzip, deflate"}: 16,
+ {name: "accept-language", value: ""}: 17,
+ {name: "accept-ranges", value: ""}: 18,
+ {name: "accept", value: ""}: 19,
+ {name: "access-control-allow-origin", value: ""}: 20,
+ {name: "age", value: ""}: 21,
+ {name: "allow", value: ""}: 22,
+ {name: "authorization", value: ""}: 23,
+ {name: "cache-control", value: ""}: 24,
+ {name: "content-disposition", value: ""}: 25,
+ {name: "content-encoding", value: ""}: 26,
+ {name: "content-language", value: ""}: 27,
+ {name: "content-length", value: ""}: 28,
+ {name: "content-location", value: ""}: 29,
+ {name: "content-range", value: ""}: 30,
+ {name: "content-type", value: ""}: 31,
+ {name: "cookie", value: ""}: 32,
+ {name: "date", value: ""}: 33,
+ {name: "etag", value: ""}: 34,
+ {name: "expect", value: ""}: 35,
+ {name: "expires", value: ""}: 36,
+ {name: "from", value: ""}: 37,
+ {name: "host", value: ""}: 38,
+ {name: "if-match", value: ""}: 39,
+ {name: "if-modified-since", value: ""}: 40,
+ {name: "if-none-match", value: ""}: 41,
+ {name: "if-range", value: ""}: 42,
+ {name: "if-unmodified-since", value: ""}: 43,
+ {name: "last-modified", value: ""}: 44,
+ {name: "link", value: ""}: 45,
+ {name: "location", value: ""}: 46,
+ {name: "max-forwards", value: ""}: 47,
+ {name: "proxy-authenticate", value: ""}: 48,
+ {name: "proxy-authorization", value: ""}: 49,
+ {name: "range", value: ""}: 50,
+ {name: "referer", value: ""}: 51,
+ {name: "refresh", value: ""}: 52,
+ {name: "retry-after", value: ""}: 53,
+ {name: "server", value: ""}: 54,
+ {name: "set-cookie", value: ""}: 55,
+ {name: "strict-transport-security", value: ""}: 56,
+ {name: "transfer-encoding", value: ""}: 57,
+ {name: "user-agent", value: ""}: 58,
+ {name: "vary", value: ""}: 59,
+ {name: "via", value: ""}: 60,
+ {name: "www-authenticate", value: ""}: 61,
+ },
+ ents: []HeaderField{
+ {Name: ":authority", Value: "", Sensitive: false},
+ {Name: ":method", Value: "GET", Sensitive: false},
+ {Name: ":method", Value: "POST", Sensitive: false},
+ {Name: ":path", Value: "/", Sensitive: false},
+ {Name: ":path", Value: "/index.html", Sensitive: false},
+ {Name: ":scheme", Value: "http", Sensitive: false},
+ {Name: ":scheme", Value: "https", Sensitive: false},
+ {Name: ":status", Value: "200", Sensitive: false},
+ {Name: ":status", Value: "204", Sensitive: false},
+ {Name: ":status", Value: "206", Sensitive: false},
+ {Name: ":status", Value: "304", Sensitive: false},
+ {Name: ":status", Value: "400", Sensitive: false},
+ {Name: ":status", Value: "404", Sensitive: false},
+ {Name: ":status", Value: "500", Sensitive: false},
+ {Name: "accept-charset", Value: "", Sensitive: false},
+ {Name: "accept-encoding", Value: "gzip, deflate", Sensitive: false},
+ {Name: "accept-language", Value: "", Sensitive: false},
+ {Name: "accept-ranges", Value: "", Sensitive: false},
+ {Name: "accept", Value: "", Sensitive: false},
+ {Name: "access-control-allow-origin", Value: "", Sensitive: false},
+ {Name: "age", Value: "", Sensitive: false},
+ {Name: "allow", Value: "", Sensitive: false},
+ {Name: "authorization", Value: "", Sensitive: false},
+ {Name: "cache-control", Value: "", Sensitive: false},
+ {Name: "content-disposition", Value: "", Sensitive: false},
+ {Name: "content-encoding", Value: "", Sensitive: false},
+ {Name: "content-language", Value: "", Sensitive: false},
+ {Name: "content-length", Value: "", Sensitive: false},
+ {Name: "content-location", Value: "", Sensitive: false},
+ {Name: "content-range", Value: "", Sensitive: false},
+ {Name: "content-type", Value: "", Sensitive: false},
+ {Name: "cookie", Value: "", Sensitive: false},
+ {Name: "date", Value: "", Sensitive: false},
+ {Name: "etag", Value: "", Sensitive: false},
+ {Name: "expect", Value: "", Sensitive: false},
+ {Name: "expires", Value: "", Sensitive: false},
+ {Name: "from", Value: "", Sensitive: false},
+ {Name: "host", Value: "", Sensitive: false},
+ {Name: "if-match", Value: "", Sensitive: false},
+ {Name: "if-modified-since", Value: "", Sensitive: false},
+ {Name: "if-none-match", Value: "", Sensitive: false},
+ {Name: "if-range", Value: "", Sensitive: false},
+ {Name: "if-unmodified-since", Value: "", Sensitive: false},
+ {Name: "last-modified", Value: "", Sensitive: false},
+ {Name: "link", Value: "", Sensitive: false},
+ {Name: "location", Value: "", Sensitive: false},
+ {Name: "max-forwards", Value: "", Sensitive: false},
+ {Name: "proxy-authenticate", Value: "", Sensitive: false},
+ {Name: "proxy-authorization", Value: "", Sensitive: false},
+ {Name: "range", Value: "", Sensitive: false},
+ {Name: "referer", Value: "", Sensitive: false},
+ {Name: "refresh", Value: "", Sensitive: false},
+ {Name: "retry-after", Value: "", Sensitive: false},
+ {Name: "server", Value: "", Sensitive: false},
+ {Name: "set-cookie", Value: "", Sensitive: false},
+ {Name: "strict-transport-security", Value: "", Sensitive: false},
+ {Name: "transfer-encoding", Value: "", Sensitive: false},
+ {Name: "user-agent", Value: "", Sensitive: false},
+ {Name: "vary", Value: "", Sensitive: false},
+ {Name: "via", Value: "", Sensitive: false},
+ {Name: "www-authenticate", Value: "", Sensitive: false},
+ },
+}
diff --git a/vendor/golang.org/x/net/http2/hpack/tables.go b/vendor/golang.org/x/net/http2/hpack/tables.go
index a66cfbea6..8cbdf3f01 100644
--- a/vendor/golang.org/x/net/http2/hpack/tables.go
+++ b/vendor/golang.org/x/net/http2/hpack/tables.go
@@ -96,8 +96,7 @@ func (t *headerFieldTable) evictOldest(n int) {
// meaning t.ents is reversed for dynamic tables. Hence, when t is a dynamic
// table, the return value i actually refers to the entry t.ents[t.len()-i].
//
-// All tables are assumed to be a dynamic tables except for the global
-// staticTable pointer.
+// All tables are assumed to be a dynamic tables except for the global staticTable.
//
// See Section 2.3.3.
func (t *headerFieldTable) search(f HeaderField) (i uint64, nameValueMatch bool) {
@@ -125,81 +124,6 @@ func (t *headerFieldTable) idToIndex(id uint64) uint64 {
return k + 1
}
-// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07#appendix-B
-var staticTable = newStaticTable()
-var staticTableEntries = [...]HeaderField{
- {Name: ":authority"},
- {Name: ":method", Value: "GET"},
- {Name: ":method", Value: "POST"},
- {Name: ":path", Value: "/"},
- {Name: ":path", Value: "/index.html"},
- {Name: ":scheme", Value: "http"},
- {Name: ":scheme", Value: "https"},
- {Name: ":status", Value: "200"},
- {Name: ":status", Value: "204"},
- {Name: ":status", Value: "206"},
- {Name: ":status", Value: "304"},
- {Name: ":status", Value: "400"},
- {Name: ":status", Value: "404"},
- {Name: ":status", Value: "500"},
- {Name: "accept-charset"},
- {Name: "accept-encoding", Value: "gzip, deflate"},
- {Name: "accept-language"},
- {Name: "accept-ranges"},
- {Name: "accept"},
- {Name: "access-control-allow-origin"},
- {Name: "age"},
- {Name: "allow"},
- {Name: "authorization"},
- {Name: "cache-control"},
- {Name: "content-disposition"},
- {Name: "content-encoding"},
- {Name: "content-language"},
- {Name: "content-length"},
- {Name: "content-location"},
- {Name: "content-range"},
- {Name: "content-type"},
- {Name: "cookie"},
- {Name: "date"},
- {Name: "etag"},
- {Name: "expect"},
- {Name: "expires"},
- {Name: "from"},
- {Name: "host"},
- {Name: "if-match"},
- {Name: "if-modified-since"},
- {Name: "if-none-match"},
- {Name: "if-range"},
- {Name: "if-unmodified-since"},
- {Name: "last-modified"},
- {Name: "link"},
- {Name: "location"},
- {Name: "max-forwards"},
- {Name: "proxy-authenticate"},
- {Name: "proxy-authorization"},
- {Name: "range"},
- {Name: "referer"},
- {Name: "refresh"},
- {Name: "retry-after"},
- {Name: "server"},
- {Name: "set-cookie"},
- {Name: "strict-transport-security"},
- {Name: "transfer-encoding"},
- {Name: "user-agent"},
- {Name: "vary"},
- {Name: "via"},
- {Name: "www-authenticate"},
-}
-
-func newStaticTable() *headerFieldTable {
- t := &headerFieldTable{}
- t.init()
- for _, e := range staticTableEntries[:] {
- t.addEntry(e)
- }
- return t
-}
-
var huffmanCodes = [256]uint32{
0x1ff8,
0x7fffd8,
diff --git a/vendor/golang.org/x/net/http2/server.go b/vendor/golang.org/x/net/http2/server.go
index 43cc2a34a..4eb7617fa 100644
--- a/vendor/golang.org/x/net/http2/server.go
+++ b/vendor/golang.org/x/net/http2/server.go
@@ -98,6 +98,19 @@ type Server struct {
// the HTTP/2 spec's recommendations.
MaxConcurrentStreams uint32
+ // MaxDecoderHeaderTableSize optionally specifies the http2
+ // SETTINGS_HEADER_TABLE_SIZE to send in the initial settings frame. It
+ // informs the remote endpoint of the maximum size of the header compression
+ // table used to decode header blocks, in octets. If zero, the default value
+ // of 4096 is used.
+ MaxDecoderHeaderTableSize uint32
+
+ // MaxEncoderHeaderTableSize optionally specifies an upper limit for the
+ // header compression table used for encoding request headers. Received
+ // SETTINGS_HEADER_TABLE_SIZE settings are capped at this limit. If zero,
+ // the default value of 4096 is used.
+ MaxEncoderHeaderTableSize uint32
+
// MaxReadFrameSize optionally specifies the largest frame
// this server is willing to read. A valid value is between
// 16k and 16M, inclusive. If zero or otherwise invalid, a
@@ -170,6 +183,20 @@ func (s *Server) maxConcurrentStreams() uint32 {
return defaultMaxStreams
}
+func (s *Server) maxDecoderHeaderTableSize() uint32 {
+ if v := s.MaxDecoderHeaderTableSize; v > 0 {
+ return v
+ }
+ return initialHeaderTableSize
+}
+
+func (s *Server) maxEncoderHeaderTableSize() uint32 {
+ if v := s.MaxEncoderHeaderTableSize; v > 0 {
+ return v
+ }
+ return initialHeaderTableSize
+}
+
// maxQueuedControlFrames is the maximum number of control frames like
// SETTINGS, PING and RST_STREAM that will be queued for writing before
// the connection is closed to prevent memory exhaustion attacks.
@@ -394,7 +421,6 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
advMaxStreams: s.maxConcurrentStreams(),
initialStreamSendWindowSize: initialWindowSize,
maxFrameSize: initialMaxFrameSize,
- headerTableSize: initialHeaderTableSize,
serveG: newGoroutineLock(),
pushEnabled: true,
sawClientPreface: opts.SawClientPreface,
@@ -424,12 +450,13 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
sc.flow.add(initialWindowSize)
sc.inflow.add(initialWindowSize)
sc.hpackEncoder = hpack.NewEncoder(&sc.headerWriteBuf)
+ sc.hpackEncoder.SetMaxDynamicTableSizeLimit(s.maxEncoderHeaderTableSize())
fr := NewFramer(sc.bw, c)
if s.CountError != nil {
fr.countError = s.CountError
}
- fr.ReadMetaHeaders = hpack.NewDecoder(initialHeaderTableSize, nil)
+ fr.ReadMetaHeaders = hpack.NewDecoder(s.maxDecoderHeaderTableSize(), nil)
fr.MaxHeaderListSize = sc.maxHeaderListSize()
fr.SetMaxReadFrameSize(s.maxReadFrameSize())
sc.framer = fr
@@ -559,9 +586,9 @@ type serverConn struct {
streams map[uint32]*stream
initialStreamSendWindowSize int32
maxFrameSize int32
- headerTableSize uint32
peerMaxHeaderListSize uint32 // zero means unknown (default)
canonHeader map[string]string // http2-lower-case -> Go-Canonical-Case
+ canonHeaderKeysSize int // canonHeader keys size in bytes
writingFrame bool // started writing a frame (on serve goroutine or separate)
writingFrameAsync bool // started a frame on its own goroutine but haven't heard back on wroteFrameCh
needsFrameFlush bool // last frame write wasn't a flush
@@ -622,7 +649,9 @@ type stream struct {
resetQueued bool // RST_STREAM queued for write; set by sc.resetStream
gotTrailerHeader bool // HEADER frame for trailers was seen
wroteHeaders bool // whether we wrote headers (not status 100)
+ readDeadline *time.Timer // nil if unused
writeDeadline *time.Timer // nil if unused
+ closeErr error // set before cw is closed
trailer http.Header // accumulated trailers
reqTrailer http.Header // handler's Request.Trailer
@@ -738,6 +767,13 @@ func (sc *serverConn) condlogf(err error, format string, args ...interface{}) {
}
}
+// maxCachedCanonicalHeadersKeysSize is an arbitrarily-chosen limit on the size
+// of the entries in the canonHeader cache.
+// This should be larger than the size of unique, uncommon header keys likely to
+// be sent by the peer, while not so high as to permit unreasonable memory usage
+// if the peer sends an unbounded number of unique header keys.
+const maxCachedCanonicalHeadersKeysSize = 2048
+
func (sc *serverConn) canonicalHeader(v string) string {
sc.serveG.check()
buildCommonHeaderMapsOnce()
@@ -753,14 +789,10 @@ func (sc *serverConn) canonicalHeader(v string) string {
sc.canonHeader = make(map[string]string)
}
cv = http.CanonicalHeaderKey(v)
- // maxCachedCanonicalHeaders is an arbitrarily-chosen limit on the number of
- // entries in the canonHeader cache. This should be larger than the number
- // of unique, uncommon header keys likely to be sent by the peer, while not
- // so high as to permit unreasonable memory usage if the peer sends an unbounded
- // number of unique header keys.
- const maxCachedCanonicalHeaders = 32
- if len(sc.canonHeader) < maxCachedCanonicalHeaders {
+ size := 100 + len(v)*2 // 100 bytes of map overhead + key + value
+ if sc.canonHeaderKeysSize+size <= maxCachedCanonicalHeadersKeysSize {
sc.canonHeader[v] = cv
+ sc.canonHeaderKeysSize += size
}
return cv
}
@@ -862,6 +894,7 @@ func (sc *serverConn) serve() {
{SettingMaxFrameSize, sc.srv.maxReadFrameSize()},
{SettingMaxConcurrentStreams, sc.advMaxStreams},
{SettingMaxHeaderListSize, sc.maxHeaderListSize()},
+ {SettingHeaderTableSize, sc.srv.maxDecoderHeaderTableSize()},
{SettingInitialWindowSize, uint32(sc.srv.initialStreamRecvWindowSize())},
},
})
@@ -869,7 +902,9 @@ func (sc *serverConn) serve() {
// Each connection starts with initialWindowSize inflow tokens.
// If a higher value is configured, we add more tokens.
- sc.sendWindowUpdate(nil)
+ if diff := sc.srv.initialConnRecvWindowSize() - initialWindowSize; diff > 0 {
+ sc.sendWindowUpdate(nil, int(diff))
+ }
if err := sc.readPreface(); err != nil {
sc.condlogf(err, "http2: server: error reading preface from client %v: %v", sc.conn.RemoteAddr(), err)
@@ -946,6 +981,8 @@ func (sc *serverConn) serve() {
}
case *startPushRequest:
sc.startPush(v)
+ case func(*serverConn):
+ v(sc)
default:
panic(fmt.Sprintf("unexpected type %T", v))
}
@@ -1459,6 +1496,21 @@ func (sc *serverConn) processFrame(f Frame) error {
sc.sawFirstSettings = true
}
+ // Discard frames for streams initiated after the identified last
+ // stream sent in a GOAWAY, or all frames after sending an error.
+ // We still need to return connection-level flow control for DATA frames.
+ // RFC 9113 Section 6.8.
+ if sc.inGoAway && (sc.goAwayCode != ErrCodeNo || f.Header().StreamID > sc.maxClientStreamID) {
+
+ if f, ok := f.(*DataFrame); ok {
+ if sc.inflow.available() < int32(f.Length) {
+ return sc.countError("data_flow", streamError(f.Header().StreamID, ErrCodeFlowControl))
+ }
+ sc.sendWindowUpdate(nil, int(f.Length)) // conn-level
+ }
+ return nil
+ }
+
switch f := f.(type) {
case *SettingsFrame:
return sc.processSettings(f)
@@ -1501,9 +1553,6 @@ func (sc *serverConn) processPing(f *PingFrame) error {
// PROTOCOL_ERROR."
return sc.countError("ping_on_stream", ConnectionError(ErrCodeProtocol))
}
- if sc.inGoAway && sc.goAwayCode != ErrCodeNo {
- return nil
- }
sc.writeFrame(FrameWriteRequest{write: writePingAck{f}})
return nil
}
@@ -1565,6 +1614,9 @@ func (sc *serverConn) closeStream(st *stream, err error) {
panic(fmt.Sprintf("invariant; can't close stream in state %v", st.state))
}
st.state = stateClosed
+ if st.readDeadline != nil {
+ st.readDeadline.Stop()
+ }
if st.writeDeadline != nil {
st.writeDeadline.Stop()
}
@@ -1586,10 +1638,18 @@ func (sc *serverConn) closeStream(st *stream, err error) {
if p := st.body; p != nil {
// Return any buffered unread bytes worth of conn-level flow control.
// See golang.org/issue/16481
- sc.sendWindowUpdate(nil)
+ sc.sendWindowUpdate(nil, p.Len())
p.CloseWithError(err)
}
+ if e, ok := err.(StreamError); ok {
+ if e.Cause != nil {
+ err = e.Cause
+ } else {
+ err = errStreamClosed
+ }
+ }
+ st.closeErr = err
st.cw.Close() // signals Handler's CloseNotifier, unblocks writes, etc
sc.writeSched.CloseStream(st.id)
}
@@ -1632,7 +1692,6 @@ func (sc *serverConn) processSetting(s Setting) error {
}
switch s.ID {
case SettingHeaderTableSize:
- sc.headerTableSize = s.Val
sc.hpackEncoder.SetMaxDynamicTableSize(s.Val)
case SettingEnablePush:
sc.pushEnabled = s.Val != 0
@@ -1686,16 +1745,6 @@ func (sc *serverConn) processSettingInitialWindowSize(val uint32) error {
func (sc *serverConn) processData(f *DataFrame) error {
sc.serveG.check()
id := f.Header().StreamID
- if sc.inGoAway && (sc.goAwayCode != ErrCodeNo || id > sc.maxClientStreamID) {
- // Discard all DATA frames if the GOAWAY is due to an
- // error, or:
- //
- // Section 6.8: After sending a GOAWAY frame, the sender
- // can discard frames for streams initiated by the
- // receiver with identifiers higher than the identified
- // last stream.
- return nil
- }
data := f.Data()
state, st := sc.state(id)
@@ -1734,7 +1783,7 @@ func (sc *serverConn) processData(f *DataFrame) error {
// sendWindowUpdate, which also schedules sending the
// frames.
sc.inflow.take(int32(f.Length))
- sc.sendWindowUpdate(nil) // conn-level
+ sc.sendWindowUpdate(nil, int(f.Length)) // conn-level
if st != nil && st.resetQueued {
// Already have a stream error in flight. Don't send another.
@@ -1752,7 +1801,7 @@ func (sc *serverConn) processData(f *DataFrame) error {
return sc.countError("data_flow", streamError(id, ErrCodeFlowControl))
}
sc.inflow.take(int32(f.Length))
- sc.sendWindowUpdate(nil) // conn-level
+ sc.sendWindowUpdate(nil, int(f.Length)) // conn-level
st.body.CloseWithError(fmt.Errorf("sender tried to send more than declared Content-Length of %d bytes", st.declBodyBytes))
// RFC 7540, sec 8.1.2.6: A request or response is also malformed if the
@@ -1770,7 +1819,7 @@ func (sc *serverConn) processData(f *DataFrame) error {
if len(data) > 0 {
wrote, err := st.body.Write(data)
if err != nil {
- sc.sendWindowUpdate32(nil, int32(f.Length)-int32(wrote))
+ sc.sendWindowUpdate(nil, int(f.Length)-wrote)
return sc.countError("body_write_err", streamError(id, ErrCodeStreamClosed))
}
if wrote != len(data) {
@@ -1838,19 +1887,27 @@ func (st *stream) copyTrailersToHandlerRequest() {
}
}
+// onReadTimeout is run on its own goroutine (from time.AfterFunc)
+// when the stream's ReadTimeout has fired.
+func (st *stream) onReadTimeout() {
+ // Wrap the ErrDeadlineExceeded to avoid callers depending on us
+ // returning the bare error.
+ st.body.CloseWithError(fmt.Errorf("%w", os.ErrDeadlineExceeded))
+}
+
// onWriteTimeout is run on its own goroutine (from time.AfterFunc)
// when the stream's WriteTimeout has fired.
func (st *stream) onWriteTimeout() {
- st.sc.writeFrameFromHandler(FrameWriteRequest{write: streamError(st.id, ErrCodeInternal)})
+ st.sc.writeFrameFromHandler(FrameWriteRequest{write: StreamError{
+ StreamID: st.id,
+ Code: ErrCodeInternal,
+ Cause: os.ErrDeadlineExceeded,
+ }})
}
func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
sc.serveG.check()
id := f.StreamID
- if sc.inGoAway {
- // Ignore.
- return nil
- }
// http://tools.ietf.org/html/rfc7540#section-5.1.1
// Streams initiated by a client MUST use odd-numbered stream
// identifiers. [...] An endpoint that receives an unexpected
@@ -1953,6 +2010,9 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
// (in Go 1.8), though. That's a more sane option anyway.
if sc.hs.ReadTimeout != 0 {
sc.conn.SetReadDeadline(time.Time{})
+ if st.body != nil {
+ st.readDeadline = time.AfterFunc(sc.hs.ReadTimeout, st.onReadTimeout)
+ }
}
go sc.runHandler(rw, req, handler)
@@ -2021,9 +2081,6 @@ func (sc *serverConn) checkPriority(streamID uint32, p PriorityParam) error {
}
func (sc *serverConn) processPriority(f *PriorityFrame) error {
- if sc.inGoAway {
- return nil
- }
if err := sc.checkPriority(f.StreamID, f.PriorityParam); err != nil {
return err
}
@@ -2322,39 +2379,24 @@ func (sc *serverConn) noteBodyReadFromHandler(st *stream, n int, err error) {
func (sc *serverConn) noteBodyRead(st *stream, n int) {
sc.serveG.check()
- sc.sendWindowUpdate(nil) // conn-level
+ sc.sendWindowUpdate(nil, n) // conn-level
if st.state != stateHalfClosedRemote && st.state != stateClosed {
// Don't send this WINDOW_UPDATE if the stream is closed
// remotely.
- sc.sendWindowUpdate(st)
+ sc.sendWindowUpdate(st, n)
}
}
// st may be nil for conn-level
-func (sc *serverConn) sendWindowUpdate(st *stream) {
+func (sc *serverConn) sendWindowUpdate(st *stream, n int) {
sc.serveG.check()
-
- var n int32
- if st == nil {
- if avail, windowSize := sc.inflow.available(), sc.srv.initialConnRecvWindowSize(); avail > windowSize/2 {
- return
- } else {
- n = windowSize - avail
- }
- } else {
- if avail, windowSize := st.inflow.available(), sc.srv.initialStreamRecvWindowSize(); avail > windowSize/2 {
- return
- } else {
- n = windowSize - avail
- }
- }
// "The legal range for the increment to the flow control
// window is 1 to 2^31-1 (2,147,483,647) octets."
// A Go Read call on 64-bit machines could in theory read
// a larger Read than this. Very unlikely, but we handle it here
// rather than elsewhere for now.
const maxUint31 = 1<<31 - 1
- for n >= maxUint31 {
+ for n > maxUint31 {
sc.sendWindowUpdate32(st, maxUint31)
n -= maxUint31
}
@@ -2474,7 +2516,15 @@ type responseWriterState struct {
type chunkWriter struct{ rws *responseWriterState }
-func (cw chunkWriter) Write(p []byte) (n int, err error) { return cw.rws.writeChunk(p) }
+func (cw chunkWriter) Write(p []byte) (n int, err error) {
+ n, err = cw.rws.writeChunk(p)
+ if err == errStreamClosed {
+ // If writing failed because the stream has been closed,
+ // return the reason it was closed.
+ err = cw.rws.stream.closeErr
+ }
+ return n, err
+}
func (rws *responseWriterState) hasTrailers() bool { return len(rws.trailers) > 0 }
@@ -2668,23 +2718,85 @@ func (rws *responseWriterState) promoteUndeclaredTrailers() {
}
}
+func (w *responseWriter) SetReadDeadline(deadline time.Time) error {
+ st := w.rws.stream
+ if !deadline.IsZero() && deadline.Before(time.Now()) {
+ // If we're setting a deadline in the past, reset the stream immediately
+ // so writes after SetWriteDeadline returns will fail.
+ st.onReadTimeout()
+ return nil
+ }
+ w.rws.conn.sendServeMsg(func(sc *serverConn) {
+ if st.readDeadline != nil {
+ if !st.readDeadline.Stop() {
+ // Deadline already exceeded, or stream has been closed.
+ return
+ }
+ }
+ if deadline.IsZero() {
+ st.readDeadline = nil
+ } else if st.readDeadline == nil {
+ st.readDeadline = time.AfterFunc(deadline.Sub(time.Now()), st.onReadTimeout)
+ } else {
+ st.readDeadline.Reset(deadline.Sub(time.Now()))
+ }
+ })
+ return nil
+}
+
+func (w *responseWriter) SetWriteDeadline(deadline time.Time) error {
+ st := w.rws.stream
+ if !deadline.IsZero() && deadline.Before(time.Now()) {
+ // If we're setting a deadline in the past, reset the stream immediately
+ // so writes after SetWriteDeadline returns will fail.
+ st.onWriteTimeout()
+ return nil
+ }
+ w.rws.conn.sendServeMsg(func(sc *serverConn) {
+ if st.writeDeadline != nil {
+ if !st.writeDeadline.Stop() {
+ // Deadline already exceeded, or stream has been closed.
+ return
+ }
+ }
+ if deadline.IsZero() {
+ st.writeDeadline = nil
+ } else if st.writeDeadline == nil {
+ st.writeDeadline = time.AfterFunc(deadline.Sub(time.Now()), st.onWriteTimeout)
+ } else {
+ st.writeDeadline.Reset(deadline.Sub(time.Now()))
+ }
+ })
+ return nil
+}
+
func (w *responseWriter) Flush() {
+ w.FlushError()
+}
+
+func (w *responseWriter) FlushError() error {
rws := w.rws
if rws == nil {
panic("Header called after Handler finished")
}
+ var err error
if rws.bw.Buffered() > 0 {
- if err := rws.bw.Flush(); err != nil {
- // Ignore the error. The frame writer already knows.
- return
- }
+ err = rws.bw.Flush()
} else {
// The bufio.Writer won't call chunkWriter.Write
// (writeChunk with zero bytes, so we have to do it
// ourselves to force the HTTP response header and/or
// final DATA frame (with END_STREAM) to be sent.
- rws.writeChunk(nil)
+ _, err = chunkWriter{rws}.Write(nil)
+ if err == nil {
+ select {
+ case <-rws.stream.cw:
+ err = rws.stream.closeErr
+ default:
+ }
+ }
}
+ return err
}
func (w *responseWriter) CloseNotify() <-chan bool {
diff --git a/vendor/golang.org/x/net/http2/transport.go b/vendor/golang.org/x/net/http2/transport.go
index c5d005bba..30f706e6c 100644
--- a/vendor/golang.org/x/net/http2/transport.go
+++ b/vendor/golang.org/x/net/http2/transport.go
@@ -16,6 +16,7 @@ import (
"errors"
"fmt"
"io"
+ "io/fs"
"log"
"math"
mathrand "math/rand"
@@ -117,6 +118,28 @@ type Transport struct {
// to mean no limit.
MaxHeaderListSize uint32
+ // MaxReadFrameSize is the http2 SETTINGS_MAX_FRAME_SIZE to send in the
+ // initial settings frame. It is the size in bytes of the largest frame
+ // payload that the sender is willing to receive. If 0, no setting is
+ // sent, and the value is provided by the peer, which should be 16384
+ // according to the spec:
+ // https://datatracker.ietf.org/doc/html/rfc7540#section-6.5.2.
+ // Values are bounded in the range 16k to 16M.
+ MaxReadFrameSize uint32
+
+ // MaxDecoderHeaderTableSize optionally specifies the http2
+ // SETTINGS_HEADER_TABLE_SIZE to send in the initial settings frame. It
+ // informs the remote endpoint of the maximum size of the header compression
+ // table used to decode header blocks, in octets. If zero, the default value
+ // of 4096 is used.
+ MaxDecoderHeaderTableSize uint32
+
+ // MaxEncoderHeaderTableSize optionally specifies an upper limit for the
+ // header compression table used for encoding request headers. Received
+ // SETTINGS_HEADER_TABLE_SIZE settings are capped at this limit. If zero,
+ // the default value of 4096 is used.
+ MaxEncoderHeaderTableSize uint32
+
// StrictMaxConcurrentStreams controls whether the server's
// SETTINGS_MAX_CONCURRENT_STREAMS should be respected
// globally. If false, new TCP connections are created to the
@@ -170,6 +193,19 @@ func (t *Transport) maxHeaderListSize() uint32 {
return t.MaxHeaderListSize
}
+func (t *Transport) maxFrameReadSize() uint32 {
+ if t.MaxReadFrameSize == 0 {
+ return 0 // use the default provided by the peer
+ }
+ if t.MaxReadFrameSize < minMaxFrameSize {
+ return minMaxFrameSize
+ }
+ if t.MaxReadFrameSize > maxFrameSize {
+ return maxFrameSize
+ }
+ return t.MaxReadFrameSize
+}
+
func (t *Transport) disableCompression() bool {
return t.DisableCompression || (t.t1 != nil && t.t1.DisableCompression)
}
@@ -292,10 +328,11 @@ type ClientConn struct {
lastActive time.Time
lastIdle time.Time // time last idle
// Settings from peer: (also guarded by wmu)
- maxFrameSize uint32
- maxConcurrentStreams uint32
- peerMaxHeaderListSize uint64
- initialWindowSize uint32
+ maxFrameSize uint32
+ maxConcurrentStreams uint32
+ peerMaxHeaderListSize uint64
+ peerMaxHeaderTableSize uint32
+ initialWindowSize uint32
// reqHeaderMu is a 1-element semaphore channel controlling access to sending new requests.
// Write to reqHeaderMu to lock it, read from it to unlock.
@@ -501,6 +538,15 @@ 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)) {
@@ -526,11 +572,14 @@ 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)
select {
- case <-time.After(time.Second * time.Duration(backoff)):
+ case <-timer.C:
t.vlogf("RoundTrip retrying after failure: %v", err)
continue
case <-req.Context().Done():
+ timer.Stop()
err = req.Context().Err()
}
}
@@ -668,6 +717,20 @@ func (t *Transport) expectContinueTimeout() time.Duration {
return t.t1.ExpectContinueTimeout
}
+func (t *Transport) maxDecoderHeaderTableSize() uint32 {
+ if v := t.MaxDecoderHeaderTableSize; v > 0 {
+ return v
+ }
+ return initialHeaderTableSize
+}
+
+func (t *Transport) maxEncoderHeaderTableSize() uint32 {
+ if v := t.MaxEncoderHeaderTableSize; v > 0 {
+ return v
+ }
+ return initialHeaderTableSize
+}
+
func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
return t.newClientConn(c, t.disableKeepAlives())
}
@@ -708,15 +771,19 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
})
cc.br = bufio.NewReader(c)
cc.fr = NewFramer(cc.bw, cc.br)
+ if t.maxFrameReadSize() != 0 {
+ cc.fr.SetMaxReadFrameSize(t.maxFrameReadSize())
+ }
if t.CountError != nil {
cc.fr.countError = t.CountError
}
- cc.fr.ReadMetaHeaders = hpack.NewDecoder(initialHeaderTableSize, nil)
+ maxHeaderTableSize := t.maxDecoderHeaderTableSize()
+ cc.fr.ReadMetaHeaders = hpack.NewDecoder(maxHeaderTableSize, nil)
cc.fr.MaxHeaderListSize = t.maxHeaderListSize()
- // TODO: SetMaxDynamicTableSize, SetMaxDynamicTableSizeLimit on
- // henc in response to SETTINGS frames?
cc.henc = hpack.NewEncoder(&cc.hbuf)
+ cc.henc.SetMaxDynamicTableSizeLimit(t.maxEncoderHeaderTableSize())
+ cc.peerMaxHeaderTableSize = initialHeaderTableSize
if t.AllowHTTP {
cc.nextStreamID = 3
@@ -731,9 +798,15 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
{ID: SettingEnablePush, Val: 0},
{ID: SettingInitialWindowSize, Val: transportDefaultStreamFlow},
}
+ if max := t.maxFrameReadSize(); max != 0 {
+ initialSettings = append(initialSettings, Setting{ID: SettingMaxFrameSize, Val: max})
+ }
if max := t.maxHeaderListSize(); max != 0 {
initialSettings = append(initialSettings, Setting{ID: SettingMaxHeaderListSize, Val: max})
}
+ if maxHeaderTableSize != initialHeaderTableSize {
+ initialSettings = append(initialSettings, Setting{ID: SettingHeaderTableSize, Val: maxHeaderTableSize})
+ }
cc.bw.Write(clientPreface)
cc.fr.WriteSettings(initialSettings...)
@@ -1075,7 +1148,7 @@ var errRequestCanceled = errors.New("net/http: request canceled")
func commaSeparatedTrailers(req *http.Request) (string, error) {
keys := make([]string, 0, len(req.Trailer))
for k := range req.Trailer {
- k = http.CanonicalHeaderKey(k)
+ k = canonicalHeader(k)
switch k {
case "Transfer-Encoding", "Trailer", "Content-Length":
return "", fmt.Errorf("invalid Trailer key %q", k)
@@ -1612,7 +1685,7 @@ func (cs *clientStream) writeRequestBody(req *http.Request) (err error) {
var sawEOF bool
for !sawEOF {
- n, err := body.Read(buf[:len(buf)])
+ n, err := body.Read(buf)
if hasContentLen {
remainLen -= int64(n)
if remainLen == 0 && err == nil {
@@ -1915,7 +1988,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
// Header list size is ok. Write the headers.
enumerateHeaders(func(name, value string) {
- name, ascii := asciiToLower(name)
+ name, ascii := lowerHeader(name)
if !ascii {
// Skip writing invalid headers. Per RFC 7540, Section 8.1.2, header
// field names have to be ASCII characters (just as in HTTP/1.x).
@@ -1968,7 +2041,7 @@ func (cc *ClientConn) encodeTrailers(trailer http.Header) ([]byte, error) {
}
for k, vv := range trailer {
- lowKey, ascii := asciiToLower(k)
+ lowKey, ascii := lowerHeader(k)
if !ascii {
// Skip writing invalid headers. Per RFC 7540, Section 8.1.2, header
// field names have to be ASCII characters (just as in HTTP/1.x).
@@ -2301,7 +2374,7 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
Status: status + " " + http.StatusText(statusCode),
}
for _, hf := range regularFields {
- key := http.CanonicalHeaderKey(hf.Name)
+ key := canonicalHeader(hf.Name)
if key == "Trailer" {
t := res.Trailer
if t == nil {
@@ -2309,7 +2382,7 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
res.Trailer = t
}
foreachHeaderElement(hf.Value, func(v string) {
- t[http.CanonicalHeaderKey(v)] = nil
+ t[canonicalHeader(v)] = nil
})
} else {
vv := header[key]
@@ -2414,7 +2487,7 @@ func (rl *clientConnReadLoop) processTrailers(cs *clientStream, f *MetaHeadersFr
trailer := make(http.Header)
for _, hf := range f.RegularFields() {
- key := http.CanonicalHeaderKey(hf.Name)
+ key := canonicalHeader(hf.Name)
trailer[key] = append(trailer[key], hf.Value)
}
cs.trailer = trailer
@@ -2760,8 +2833,10 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error {
cc.cond.Broadcast()
cc.initialWindowSize = s.Val
+ case SettingHeaderTableSize:
+ cc.henc.SetMaxDynamicTableSize(s.Val)
+ cc.peerMaxHeaderTableSize = s.Val
default:
- // TODO(bradfitz): handle more settings? SETTINGS_HEADER_TABLE_SIZE probably.
cc.vlogf("Unhandled Setting: %v", s)
}
return nil
@@ -2985,7 +3060,11 @@ func (gz *gzipReader) Read(p []byte) (n int, err error) {
}
func (gz *gzipReader) Close() error {
- return gz.body.Close()
+ if err := gz.body.Close(); err != nil {
+ return err
+ }
+ gz.zerr = fs.ErrClosed
+ return nil
}
type errorReader struct{ err error }