diff options
Diffstat (limited to 'vendor/golang.org/x/net/http2')
| -rw-r--r-- | vendor/golang.org/x/net/http2/h2c/h2c.go | 2 | ||||
| -rw-r--r-- | vendor/golang.org/x/net/http2/server.go | 9 | ||||
| -rw-r--r-- | vendor/golang.org/x/net/http2/transport.go | 21 | ||||
| -rw-r--r-- | vendor/golang.org/x/net/http2/writesched.go | 3 | ||||
| -rw-r--r-- | vendor/golang.org/x/net/http2/writesched_roundrobin.go | 119 | 
5 files changed, 146 insertions, 8 deletions
| diff --git a/vendor/golang.org/x/net/http2/h2c/h2c.go b/vendor/golang.org/x/net/http2/h2c/h2c.go index a72bbed1b..2d6bf861b 100644 --- a/vendor/golang.org/x/net/http2/h2c/h2c.go +++ b/vendor/golang.org/x/net/http2/h2c/h2c.go @@ -44,7 +44,7 @@ func init() {  // HTTP/1, but unlikely to occur in practice and (2) Upgrading from HTTP/1 to  // h2c - this works by using the HTTP/1 Upgrade header to request an upgrade to  // h2c. When either of those situations occur we hijack the HTTP/1 connection, -// convert it to a HTTP/2 connection and pass the net.Conn to http2.ServeConn. +// convert it to an HTTP/2 connection and pass the net.Conn to http2.ServeConn.  type h2cHandler struct {  	Handler http.Handler  	s       *http2.Server diff --git a/vendor/golang.org/x/net/http2/server.go b/vendor/golang.org/x/net/http2/server.go index cd057f398..033b6e6db 100644 --- a/vendor/golang.org/x/net/http2/server.go +++ b/vendor/golang.org/x/net/http2/server.go @@ -441,7 +441,7 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {  	if s.NewWriteScheduler != nil {  		sc.writeSched = s.NewWriteScheduler()  	} else { -		sc.writeSched = NewPriorityWriteScheduler(nil) +		sc.writeSched = newRoundRobinWriteScheduler()  	}  	// These start at the RFC-specified defaults. If there is a higher @@ -2429,7 +2429,7 @@ type requestBody struct {  	conn          *serverConn  	closeOnce     sync.Once // for use by Close only  	sawEOF        bool      // for use by Read only -	pipe          *pipe     // non-nil if we have a HTTP entity message body +	pipe          *pipe     // non-nil if we have an HTTP entity message body  	needsContinue bool      // need to send a 100-continue  } @@ -2569,7 +2569,8 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {  				clen = ""  			}  		} -		if clen == "" && rws.handlerDone && bodyAllowedForStatus(rws.status) && (len(p) > 0 || !isHeadResp) { +		_, hasContentLength := rws.snapHeader["Content-Length"] +		if !hasContentLength && clen == "" && rws.handlerDone && bodyAllowedForStatus(rws.status) && (len(p) > 0 || !isHeadResp) {  			clen = strconv.Itoa(len(p))  		}  		_, hasContentType := rws.snapHeader["Content-Type"] @@ -2774,7 +2775,7 @@ func (w *responseWriter) FlushError() error {  		err = rws.bw.Flush()  	} else {  		// The bufio.Writer won't call chunkWriter.Write -		// (writeChunk with zero bytes, so we have to do it +		// (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.  		_, err = chunkWriter{rws}.Write(nil) diff --git a/vendor/golang.org/x/net/http2/transport.go b/vendor/golang.org/x/net/http2/transport.go index ac90a2631..4f08ccba9 100644 --- a/vendor/golang.org/x/net/http2/transport.go +++ b/vendor/golang.org/x/net/http2/transport.go @@ -1268,8 +1268,8 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {  	cancelRequest := func(cs *clientStream, err error) error {  		cs.cc.mu.Lock() -		defer cs.cc.mu.Unlock()  		cs.abortStreamLocked(err) +		bodyClosed := cs.reqBodyClosed  		if cs.ID != 0 {  			// This request may have failed because of a problem with the connection,  			// or for some unrelated reason. (For example, the user might have canceled @@ -1284,6 +1284,23 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {  			// will not help.  			cs.cc.doNotReuse = true  		} +		cs.cc.mu.Unlock() +		// Wait for the request body to be closed. +		// +		// If nothing closed the body before now, abortStreamLocked +		// will have started a goroutine to close it. +		// +		// Closing the body before returning avoids a race condition +		// with net/http checking its readTrackingBody to see if the +		// body was read from or closed. See golang/go#60041. +		// +		// The body is closed in a separate goroutine without the +		// connection mutex held, but dropping the mutex before waiting +		// will keep us from holding it indefinitely if the body +		// close is slow for some reason. +		if bodyClosed != nil { +			<-bodyClosed +		}  		return err  	} @@ -1899,7 +1916,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail  		// 8.1.2.3 Request Pseudo-Header Fields  		// The :path pseudo-header field includes the path and query parts of the  		// target URI (the path-absolute production and optionally a '?' character -		// followed by the query production (see Sections 3.3 and 3.4 of +		// followed by the query production, see Sections 3.3 and 3.4 of  		// [RFC3986]).  		f(":authority", host)  		m := req.Method diff --git a/vendor/golang.org/x/net/http2/writesched.go b/vendor/golang.org/x/net/http2/writesched.go index c7cd00173..cc893adc2 100644 --- a/vendor/golang.org/x/net/http2/writesched.go +++ b/vendor/golang.org/x/net/http2/writesched.go @@ -184,7 +184,8 @@ func (wr *FrameWriteRequest) replyToWriter(err error) {  // writeQueue is used by implementations of WriteScheduler.  type writeQueue struct { -	s []FrameWriteRequest +	s          []FrameWriteRequest +	prev, next *writeQueue  }  func (q *writeQueue) empty() bool { return len(q.s) == 0 } diff --git a/vendor/golang.org/x/net/http2/writesched_roundrobin.go b/vendor/golang.org/x/net/http2/writesched_roundrobin.go new file mode 100644 index 000000000..54fe86322 --- /dev/null +++ b/vendor/golang.org/x/net/http2/writesched_roundrobin.go @@ -0,0 +1,119 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package http2 + +import ( +	"fmt" +	"math" +) + +type roundRobinWriteScheduler struct { +	// control contains control frames (SETTINGS, PING, etc.). +	control writeQueue + +	// streams maps stream ID to a queue. +	streams map[uint32]*writeQueue + +	// stream queues are stored in a circular linked list. +	// head is the next stream to write, or nil if there are no streams open. +	head *writeQueue + +	// pool of empty queues for reuse. +	queuePool writeQueuePool +} + +// newRoundRobinWriteScheduler constructs a new write scheduler. +// The round robin scheduler priorizes control frames +// like SETTINGS and PING over DATA frames. +// When there are no control frames to send, it performs a round-robin +// selection from the ready streams. +func newRoundRobinWriteScheduler() WriteScheduler { +	ws := &roundRobinWriteScheduler{ +		streams: make(map[uint32]*writeQueue), +	} +	return ws +} + +func (ws *roundRobinWriteScheduler) OpenStream(streamID uint32, options OpenStreamOptions) { +	if ws.streams[streamID] != nil { +		panic(fmt.Errorf("stream %d already opened", streamID)) +	} +	q := ws.queuePool.get() +	ws.streams[streamID] = q +	if ws.head == nil { +		ws.head = q +		q.next = q +		q.prev = q +	} else { +		// Queues are stored in a ring. +		// Insert the new stream before ws.head, putting it at the end of the list. +		q.prev = ws.head.prev +		q.next = ws.head +		q.prev.next = q +		q.next.prev = q +	} +} + +func (ws *roundRobinWriteScheduler) CloseStream(streamID uint32) { +	q := ws.streams[streamID] +	if q == nil { +		return +	} +	if q.next == q { +		// This was the only open stream. +		ws.head = nil +	} else { +		q.prev.next = q.next +		q.next.prev = q.prev +		if ws.head == q { +			ws.head = q.next +		} +	} +	delete(ws.streams, streamID) +	ws.queuePool.put(q) +} + +func (ws *roundRobinWriteScheduler) AdjustStream(streamID uint32, priority PriorityParam) {} + +func (ws *roundRobinWriteScheduler) Push(wr FrameWriteRequest) { +	if wr.isControl() { +		ws.control.push(wr) +		return +	} +	q := ws.streams[wr.StreamID()] +	if q == nil { +		// This is a closed stream. +		// wr should not be a HEADERS or DATA frame. +		// We push the request onto the control queue. +		if wr.DataSize() > 0 { +			panic("add DATA on non-open stream") +		} +		ws.control.push(wr) +		return +	} +	q.push(wr) +} + +func (ws *roundRobinWriteScheduler) Pop() (FrameWriteRequest, bool) { +	// Control and RST_STREAM frames first. +	if !ws.control.empty() { +		return ws.control.shift(), true +	} +	if ws.head == nil { +		return FrameWriteRequest{}, false +	} +	q := ws.head +	for { +		if wr, ok := q.consume(math.MaxInt32); ok { +			ws.head = q.next +			return wr, true +		} +		q = q.next +		if q == ws.head { +			break +		} +	} +	return FrameWriteRequest{}, false +} | 
