net/http/internal/http2: modernize the package

This CL is mostly generated by running go fix. Manual edits have also been
selectively done to modernize the package where doing so is straightforward; for
example, using slices.Contains in lieu of strSliceContains.

Change-Id: Ie2942481672c56c370e2df0f172cf3e480a12bc5
Reviewed-on: https://go-review.googlesource.com/c/go/+/757220
Reviewed-by: Nicholas Husin <husin@google.com>
Reviewed-by: Nicholas Husin <nsh@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
qiulaidongfeng
2026-03-13 23:43:05 +08:00
committed by Nicholas Husin
parent 224489f11c
commit e1bc5cea82
19 changed files with 119 additions and 169 deletions

View File

@@ -10,6 +10,7 @@ import (
"context"
"errors"
"net"
"slices"
"sync"
)
@@ -211,10 +212,8 @@ func (c *addConnCall) run(t *Transport, key string, nc net.Conn) {
// p.mu must be held
func (p *clientConnPool) addConnLocked(key string, cc *ClientConn) {
for _, v := range p.conns[key] {
if v == cc {
return
}
if slices.Contains(p.conns[key], cc) {
return
}
if p.conns == nil {
p.conns = make(map[string][]*ClientConn)

View File

@@ -21,11 +21,11 @@ import (
// improved enough that we can instead allocate chunks like this:
// make([]byte, max(16<<10, expectedBytesRemaining))
var dataChunkPools = [...]sync.Pool{
{New: func() interface{} { return new([1 << 10]byte) }},
{New: func() interface{} { return new([2 << 10]byte) }},
{New: func() interface{} { return new([4 << 10]byte) }},
{New: func() interface{} { return new([8 << 10]byte) }},
{New: func() interface{} { return new([16 << 10]byte) }},
{New: func() any { return new([1 << 10]byte) }},
{New: func() any { return new([2 << 10]byte) }},
{New: func() any { return new([4 << 10]byte) }},
{New: func() any { return new([8 << 10]byte) }},
{New: func() any { return new([16 << 10]byte) }},
}
func getDataBufferChunk(size int64) []byte {
@@ -121,10 +121,7 @@ func (b *dataBuffer) Write(p []byte) (int, error) {
// If the last chunk is empty, allocate a new chunk. Try to allocate
// enough to fully copy p plus any additional bytes we expect to
// receive. However, this may allocate less than len(p).
want := int64(len(p))
if b.expected > want {
want = b.expected
}
want := max(b.expected, int64(len(p)))
chunk := b.lastChunkOrAlloc(want)
n := copy(chunk[b.w:], p)
p = p[n:]

View File

@@ -8,36 +8,37 @@ import (
"bytes"
"fmt"
"reflect"
"strings"
"testing"
)
func fmtDataChunk(chunk []byte) string {
out := ""
var out strings.Builder
var last byte
var count int
for _, c := range chunk {
if c != last {
if count > 0 {
out += fmt.Sprintf(" x %d ", count)
out.WriteString(fmt.Sprintf(" x %d ", count))
count = 0
}
out += string([]byte{c})
out.WriteString(string([]byte{c}))
last = c
}
count++
}
if count > 0 {
out += fmt.Sprintf(" x %d", count)
out.WriteString(fmt.Sprintf(" x %d", count))
}
return out
return out.String()
}
func fmtDataChunks(chunks [][]byte) string {
var out string
var out strings.Builder
for _, chunk := range chunks {
out += fmt.Sprintf("{%q}", fmtDataChunk(chunk))
out.WriteString(fmt.Sprintf("{%q}", fmtDataChunk(chunk)))
}
return out
return out.String()
}
func testDataBuffer(t *testing.T, wantBytes []byte, setup func(t *testing.T) *dataBuffer) {

View File

@@ -104,14 +104,14 @@ func (e StreamError) As(target any) bool {
if dstType.NumField() != numField {
return false
}
for i := 0; i < numField; i++ {
for i := range numField {
sf := srcType.Field(i)
df := dstType.Field(i)
if sf.Name != df.Name || !sf.Type.ConvertibleTo(df.Type) {
return false
}
}
for i := 0; i < numField; i++ {
for i := range numField {
df := dst.Field(i)
df.Set(src.Field(i).Convert(df.Type()))
}

View File

@@ -170,7 +170,7 @@ func init() {
testHookOnPanicMu = new(sync.Mutex)
}
func SetTestHookOnPanic(t testing.TB, f func(sc *serverConn, panicVal interface{}) (rePanic bool)) {
func SetTestHookOnPanic(t testing.TB, f func(sc *serverConn, panicVal any) (rePanic bool)) {
testHookOnPanicMu.Lock()
defer testHookOnPanicMu.Unlock()
old := testHookOnPanic

View File

@@ -196,7 +196,7 @@ func (h FrameHeader) writeDebug(buf *bytes.Buffer) {
if h.Flags != 0 {
buf.WriteString(" flags=")
set := 0
for i := uint8(0); i < 8; i++ {
for i := range uint8(8) {
if h.Flags&(1<<i) == 0 {
continue
}
@@ -229,7 +229,7 @@ func (h *FrameHeader) invalidate() { h.valid = false }
// frame header bytes.
// Used only by ReadFrameHeader.
var fhBytes = sync.Pool{
New: func() interface{} {
New: func() any {
buf := make([]byte, frameHeaderLen)
return &buf
},
@@ -343,8 +343,8 @@ type Framer struct {
debugFramer *Framer // only use for logging written writes
debugFramerBuf *bytes.Buffer
debugReadLoggerf func(string, ...interface{})
debugWriteLoggerf func(string, ...interface{})
debugReadLoggerf func(string, ...any)
debugWriteLoggerf func(string, ...any)
frameCache *frameCache // nil if frames aren't reused (default)
}
@@ -838,7 +838,7 @@ func (f *SettingsFrame) HasDuplicates() bool {
// 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++ {
for i := range num {
idi := f.Setting(i).ID
for j := i + 1; j < num; j++ {
idj := f.Setting(j).ID
@@ -850,7 +850,7 @@ func (f *SettingsFrame) HasDuplicates() bool {
return false
}
seen := map[SettingID]bool{}
for i := 0; i < num; i++ {
for i := range num {
id := f.Setting(i).ID
if seen[id] {
return true

View File

@@ -1132,7 +1132,7 @@ func TestMetaFrameHeader(t *testing.T) {
tests := [...]struct {
name string
w func(*Framer)
want interface{} // *MetaHeaderFrame or error
want any // *MetaHeaderFrame or error
wantErrReason string
maxHeaderListSize uint32
}{
@@ -1181,7 +1181,7 @@ func TestMetaFrameHeader(t *testing.T) {
name: "max_header_list_truncated",
w: func(f *Framer) {
var pairs = []string{":method", "GET", ":path", "/"}
for i := 0; i < 100; i++ {
for range 100 {
pairs = append(pairs, "foo", "bar")
}
all := encodeHeaderRaw(t, pairs...)
@@ -1279,7 +1279,7 @@ func TestMetaFrameHeader(t *testing.T) {
name = fmt.Sprintf("test index %d", i)
}
var got interface{}
var got any
var err error
got, err = f.ReadFrame()
if err != nil {
@@ -1302,7 +1302,7 @@ func TestMetaFrameHeader(t *testing.T) {
}
}
}
str := func(v interface{}) string {
str := func(v any) string {
if _, ok := v.(error); ok {
return fmt.Sprintf("error %v", v)
} else {
@@ -1325,21 +1325,21 @@ func TestSetReuseFrames(t *testing.T) {
// SetReuseFrames only currently implements reuse of DataFrames.
firstDf := readAndVerifyDataFrame("ABC", 3, fr, buf, t)
for i := 0; i < 10; i++ {
for range 10 {
df := readAndVerifyDataFrame("XYZ", 3, fr, buf, t)
if df != firstDf {
t.Errorf("Expected Framer to return references to the same DataFrame. Have %v and %v", &df, &firstDf)
}
}
for i := 0; i < 10; i++ {
for range 10 {
df := readAndVerifyDataFrame("", 0, fr, buf, t)
if df != firstDf {
t.Errorf("Expected Framer to return references to the same DataFrame. Have %v and %v", &df, &firstDf)
}
}
for i := 0; i < 10; i++ {
for range 10 {
df := readAndVerifyDataFrame("HHH", 3, fr, buf, t)
if df != firstDf {
t.Errorf("Expected Framer to return references to the same DataFrame. Have %v and %v", &df, &firstDf)
@@ -1354,7 +1354,7 @@ func TestSetReuseFramesMoreThanOnce(t *testing.T) {
firstDf := readAndVerifyDataFrame("ABC", 3, fr, buf, t)
fr.SetReuseFrames()
for i := 0; i < 10; i++ {
for range 10 {
df := readAndVerifyDataFrame("XYZ", 3, fr, buf, t)
// SetReuseFrames should be idempotent
fr.SetReuseFrames()
@@ -1367,11 +1367,11 @@ func TestSetReuseFramesMoreThanOnce(t *testing.T) {
func TestNoSetReuseFrames(t *testing.T) {
fr, buf := testFramer()
const numNewDataFrames = 10
dfSoFar := make([]interface{}, numNewDataFrames)
dfSoFar := make([]any, numNewDataFrames)
// Check that DataFrames are not reused if SetReuseFrames wasn't called.
// SetReuseFrames only currently implements reuse of DataFrames.
for i := 0; i < numNewDataFrames; i++ {
for i := range numNewDataFrames {
df := readAndVerifyDataFrame("XYZ", 3, fr, buf, t)
for _, item := range dfSoFar {
if df == item {

View File

@@ -79,7 +79,7 @@ func curGoroutineID() uint64 {
}
var littleBuf = sync.Pool{
New: func() interface{} {
New: func() any {
buf := make([]byte, 64)
return &buf
},

View File

@@ -14,7 +14,7 @@ func TestGoroutineLock(t *testing.T) {
g := newGoroutineLock()
g.check()
sawPanic := make(chan interface{})
sawPanic := make(chan any)
go func() {
defer func() { sawPanic <- recover() }()
g.check() // should panic

View File

@@ -20,7 +20,7 @@ import (
"fmt"
"net"
"os"
"sort"
"slices"
"strconv"
"strings"
"sync"
@@ -273,7 +273,7 @@ func newBufferedWriter(conn net.Conn, timeout time.Duration) *bufferedWriter {
const bufWriterPoolBufferSize = 4 << 10
var bufWriterPool = sync.Pool{
New: func() interface{} {
New: func() any {
return bufio.NewWriterSize(nil, bufWriterPoolBufferSize)
},
}
@@ -376,16 +376,12 @@ type connectionStater interface {
ConnectionState() tls.ConnectionState
}
var sorterPool = sync.Pool{New: func() interface{} { return new(sorter) }}
var sorterPool = sync.Pool{New: func() any { return new(sorter) }}
type sorter struct {
v []string // owned by sorter
}
func (s *sorter) Len() int { return len(s.v) }
func (s *sorter) Swap(i, j int) { s.v[i], s.v[j] = s.v[j], s.v[i] }
func (s *sorter) Less(i, j int) bool { return s.v[i] < s.v[j] }
// Keys returns the sorted keys of h.
//
// The returned slice is only valid until s used again or returned to
@@ -396,19 +392,10 @@ func (s *sorter) Keys(h Header) []string {
keys = append(keys, k)
}
s.v = keys
sort.Sort(s)
slices.Sort(s.v)
return keys
}
func (s *sorter) SortStrings(ss []string) {
// Our sorter works on s.v, which sorter owns, so
// stash it away while we sort the user's buffer.
save := s.v
s.v = ss
sort.Sort(s)
s.v = save
}
// incomparable is a zero-width, non-comparable type. Adding it to a struct
// makes that struct also non-comparable, and generally doesn't add
// any size (as long as it's first).

View File

@@ -45,7 +45,6 @@ func TestSettingString(t *testing.T) {
}
func TestSorterPoolAllocs(t *testing.T) {
ss := []string{"a", "b", "c"}
h := Header{
"a": nil,
"b": nil,
@@ -53,12 +52,6 @@ func TestSorterPoolAllocs(t *testing.T) {
}
sorter := new(sorter)
if allocs := testing.AllocsPerRun(100, func() {
sorter.SortStrings(ss)
}); allocs >= 1 {
t.Logf("SortStrings allocs = %v; want <1", allocs)
}
if allocs := testing.AllocsPerRun(5, func() {
if len(sorter.Keys(h)) != 3 {
t.Fatal("wrong result")

View File

@@ -44,6 +44,7 @@ import (
"os"
"reflect"
"runtime"
"slices"
"strconv"
"strings"
"sync"
@@ -73,7 +74,7 @@ var (
)
var responseWriterStatePool = sync.Pool{
New: func() interface{} {
New: func() any {
rws := &responseWriterState{}
rws.bw = bufio.NewWriterSize(chunkWriter{rws}, handlerChunkWriteSize)
return rws
@@ -84,7 +85,7 @@ var responseWriterStatePool = sync.Pool{
var (
testHookOnConn func()
testHookOnPanicMu *sync.Mutex // nil except in tests
testHookOnPanic func(sc *serverConn, panicVal interface{}) (rePanic bool)
testHookOnPanic func(sc *serverConn, panicVal any) (rePanic bool)
)
// Server is an HTTP/2 server.
@@ -357,7 +358,7 @@ func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, newf func(*serverCon
streams: make(map[uint32]*stream),
readFrameCh: make(chan readFrameResult),
wantWriteFrameCh: make(chan FrameWriteRequest, 8),
serveMsgCh: make(chan interface{}, 8),
serveMsgCh: make(chan any, 8),
wroteFrameCh: make(chan frameWriteResult, 1), // buffered; one send in writeFrameAsync
bodyReadCh: make(chan bodyReadMsg), // buffering doesn't matter either way
doneServing: make(chan struct{}),
@@ -510,7 +511,7 @@ type serverConn struct {
wantWriteFrameCh chan FrameWriteRequest // from handlers -> serve
wroteFrameCh chan frameWriteResult // from writeFrameAsync -> serve, tickles more frame writes
bodyReadCh chan bodyReadMsg // from handlers -> serve
serveMsgCh chan interface{} // misc messages & code to send to / run on the serve loop
serveMsgCh chan any // misc messages & code to send to / run on the serve loop
flow outflow // conn-wide (not stream-specific) outbound flow control
inflow inflow // conn-wide inbound flow control
tlsState *tls.ConnectionState // shared by all handlers, like net/http
@@ -667,13 +668,13 @@ func (sc *serverConn) setConnState(state ConnState) {
sc.hs.ConnState(sc.conn, state)
}
func (sc *serverConn) vlogf(format string, args ...interface{}) {
func (sc *serverConn) vlogf(format string, args ...any) {
if VerboseLogs {
sc.logf(format, args...)
}
}
func (sc *serverConn) logf(format string, args ...interface{}) {
func (sc *serverConn) logf(format string, args ...any) {
if lg := sc.hs.ErrorLog(); lg != nil {
lg.Printf(format, args...)
} else {
@@ -721,7 +722,7 @@ func isClosedConnError(err error) bool {
return false
}
func (sc *serverConn) condlogf(err error, format string, args ...interface{}) {
func (sc *serverConn) condlogf(err error, format string, args ...any) {
if err == nil {
return
}
@@ -1042,7 +1043,7 @@ func (sc *serverConn) onIdleTimer() { sc.sendServeMsg(idleTimerMsg) }
func (sc *serverConn) onReadIdleTimer() { sc.sendServeMsg(readIdleTimerMsg) }
func (sc *serverConn) onShutdownTimer() { sc.sendServeMsg(shutdownTimerMsg) }
func (sc *serverConn) sendServeMsg(msg interface{}) {
func (sc *serverConn) sendServeMsg(msg any) {
sc.serveG.checkNotOn() // NOT
select {
case sc.serveMsgCh <- msg:
@@ -1087,7 +1088,7 @@ func (sc *serverConn) readPreface() error {
}
var writeDataPool = sync.Pool{
New: func() interface{} { return new(writeData) },
New: func() any { return new(writeData) },
}
// writeDataFromHandler writes DATA response frames from a handler on
@@ -2596,7 +2597,7 @@ func (rws *responseWriterState) declareTrailer(k string) {
rws.conn.logf("ignoring invalid trailer %q", k)
return
}
if !strSliceContains(rws.trailers, k) {
if !slices.Contains(rws.trailers, k) {
rws.trailers = append(rws.trailers, k)
}
}
@@ -2759,9 +2760,7 @@ func (rws *responseWriterState) promoteUndeclaredTrailers() {
}
if len(rws.trailers) > 1 {
sorter := sorterPool.Get().(*sorter)
sorter.SortStrings(rws.trailers)
sorterPool.Put(sorter)
slices.Sort(rws.trailers)
}
}
@@ -3206,7 +3205,7 @@ func foreachHeaderElement(v string, fn func(string)) {
fn(v)
return
}
for _, f := range strings.Split(v, ",") {
for f := range strings.SplitSeq(v, ",") {
if f = textproto.TrimString(f); f != "" {
fn(f)
}

View File

@@ -112,7 +112,7 @@ func testServer_Push_Success(t testing.TB) {
// Send one request, which should push two responses.
st.greet()
getSlash(st)
for k := 0; k < 3; k++ {
for k := range 3 {
select {
case <-time.After(2 * time.Second):
t.Errorf("timeout waiting for handler %d to finish", k)
@@ -277,7 +277,7 @@ func testServer_Push_SuccessNoRace(t testing.TB) {
// Send one request, which should push one response.
st.greet()
getSlash(st)
for k := 0; k < 2; k++ {
for k := range 2 {
select {
case <-time.After(2 * time.Second):
t.Errorf("timeout waiting for handler %d to finish", k)
@@ -543,7 +543,7 @@ func testServer_Push_Underflow(t testing.TB) {
// Send several requests.
st.greet()
const numRequests = 4
for i := 0; i < numRequests; i++ {
for i := range numRequests {
st.writeHeaders(HeadersFrameParam{
StreamID: uint32(1 + i*2), // clients send odd numbers
BlockFragment: st.encodeHeader(),

View File

@@ -119,7 +119,7 @@ func (w twriter) Write(p []byte) (n int, err error) {
return len(p), nil
}
func newTestServer(t testing.TB, handler http.HandlerFunc, opts ...interface{}) *httptest.Server {
func newTestServer(t testing.TB, handler http.HandlerFunc, opts ...any) *httptest.Server {
t.Helper()
if handler == nil {
handler = func(w http.ResponseWriter, req *http.Request) {}
@@ -173,7 +173,7 @@ var optQuiet = func(server *http.Server) {
server.ErrorLog = log.New(io.Discard, "", 0)
}
func newServerTester(t testing.TB, handler http.HandlerFunc, opts ...interface{}) *serverTester {
func newServerTester(t testing.TB, handler http.HandlerFunc, opts ...any) *serverTester {
t.Helper()
h1server := &http.Server{}
@@ -436,7 +436,7 @@ func (st *serverTester) greetAndCheckSettings(checkSetting func(s Setting) error
var gotSettingsAck bool
var gotWindowUpdate bool
for i := 0; i < 2; i++ {
for range 2 {
f := st.readFrame()
if f == nil {
st.t.Fatal("wanted a settings ACK and window update, got none")
@@ -1218,7 +1218,7 @@ func testServer_MaxQueuedControlFrames(t testing.TB) {
// Send maxQueuedControlFrames pings, plus a few extra
// to account for ones that enter the server's write buffer.
const extraPings = 2
for i := 0; i < MaxQueuedControlFrames+extraPings; i++ {
for range MaxQueuedControlFrames + extraPings {
pingData := [8]byte{1, 2, 3, 4, 5, 6, 7, 8}
st.fr.WritePing(false, pingData)
}
@@ -1230,7 +1230,7 @@ func testServer_MaxQueuedControlFrames(t testing.TB) {
st.advance(GoAwayTimeout)
// Some frames may have persisted in the server's buffers.
for i := 0; i < 10; i++ {
for range 10 {
if st.readFrame() == nil {
break
}
@@ -1925,7 +1925,6 @@ func TestServerRejectsContentLengthWithSignNewRequests(t *testing.T) {
}
for _, tt := range tests {
tt := tt
synctestSubtest(t, tt.name, func(t testing.TB) {
writeReq := func(st *serverTester) {
st.writeHeaders(HeadersFrameParam{
@@ -2404,7 +2403,7 @@ func testServer_Rejects_Too_Many_Streams(t testing.TB) {
})
}
var calls []*serverHandlerCall
for i := 0; i < DefaultMaxStreams; i++ {
for range DefaultMaxStreams {
sendReq(streamID())
calls = append(calls, st.nextHandlerCall())
}
@@ -2451,7 +2450,7 @@ func TestServer_Response_ManyHeaders_With_Continuation(t *testing.T) {
func testServer_Response_ManyHeaders_With_Continuation(t testing.TB) {
testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
h := w.Header()
for i := 0; i < 5000; i++ {
for i := range 5000 {
h.Set(fmt.Sprintf("x-header-%d", i), fmt.Sprintf("x-value-%d", i))
}
return nil
@@ -2526,10 +2525,10 @@ func testServer_NoCrash_HandlerClose_Then_ClientClose(t testing.TB) {
// previously.
var (
panMu sync.Mutex
panicVal interface{}
panicVal any
)
SetTestHookOnPanic(t, func(sc *ServerConn, pv interface{}) bool {
SetTestHookOnPanic(t, func(sc *ServerConn, pv any) bool {
panMu.Lock()
panicVal = pv
panMu.Unlock()
@@ -3544,7 +3543,7 @@ func testIssue20704Race(t testing.TB) {
)
ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
for i := 0; i < itemCount; i++ {
for range itemCount {
_, err := w.Write(make([]byte, itemSize))
if err != nil {
return
@@ -3559,7 +3558,7 @@ func testIssue20704Race(t testing.TB) {
defer tr.CloseIdleConnections()
cl := &http.Client{Transport: tr}
for i := 0; i < 1000; i++ {
for range 1000 {
resp, err := cl.Get(ts.URL)
if err != nil {
t.Fatal(err)
@@ -3792,7 +3791,7 @@ func TestContentEncodingNoSniffing(t *testing.T) {
// setting Content-Encoding as an interface instead of a string
// directly, so as to differentiate between 3 states:
// unset, empty string "" and set string "foo/bar".
contentEncoding interface{}
contentEncoding any
wantContentType string
}
@@ -4271,7 +4270,7 @@ func testServerMaxHandlerGoroutines(t testing.TB) {
// Reset them all, but only after the handler goroutines have started.
var stops []chan bool
streamID := uint32(1)
for i := 0; i < maxHandlers; i++ {
for range maxHandlers {
st.writeHeaders(HeadersFrameParam{
StreamID: streamID,
BlockFragment: st.encodeHeader(),
@@ -4294,7 +4293,7 @@ func testServerMaxHandlerGoroutines(t testing.TB) {
streamID += 2
// Start another two requests. Don't reset these.
for i := 0; i < 2; i++ {
for range 2 {
st.writeHeaders(HeadersFrameParam{
StreamID: streamID,
BlockFragment: st.encodeHeader(),
@@ -4322,7 +4321,7 @@ func testServerMaxHandlerGoroutines(t testing.TB) {
// Make a bunch more requests.
// Eventually, the server tells us to go away.
for i := 0; i < 5*maxHandlers; i++ {
for range 5 * maxHandlers {
st.writeHeaders(HeadersFrameParam{
StreamID: streamID,
BlockFragment: st.encodeHeader(),
@@ -4358,7 +4357,7 @@ func testServerContinuationFlood(t testing.TB) {
BlockFragment: st.encodeHeader(),
EndStream: true,
})
for i := 0; i < 1000; i++ {
for i := range 1000 {
st.fr.WriteContinuation(1, false, st.encodeHeaderRaw(
fmt.Sprintf("x-%v", i), "1234567890",
))
@@ -4511,7 +4510,7 @@ func testServerWriteByteTimeout(t testing.TB) {
})
// Read a few bytes, staying just under WriteByteTimeout.
for i := 0; i < 10; i++ {
for i := range 10 {
st.advance(timeout - 1)
if n, err := st.cc.Read(make([]byte, 1)); n != 1 || err != nil {
t.Fatalf("read %v: %v, %v; want 1, nil", i, n, err)

View File

@@ -708,7 +708,7 @@ func (t *Transport) newTLSConfig(host string) *tls.Config {
if t.TLSClientConfig != nil {
*cfg = *t.TLSClientConfig.Clone()
}
if !strSliceContains(cfg.NextProtos, NextProtoTLS) {
if !slices.Contains(cfg.NextProtos, NextProtoTLS) {
cfg.NextProtos = append([]string{NextProtoTLS}, cfg.NextProtos...)
}
if cfg.ServerName == "" {
@@ -1794,10 +1794,7 @@ var (
// Request.ContentLength+1, 512KB)).
func (cs *clientStream) frameScratchBufferLen(maxFrameSize int) int {
const max = 512 << 10
n := int64(maxFrameSize)
if n > max {
n = max
}
n := min(int64(maxFrameSize), max)
if cl := cs.reqBodyContentLength; cl != -1 && cl+1 < n {
// Add an extra byte past the declared content-length to
// give the caller's Request.Body io.Reader a chance to
@@ -3066,21 +3063,21 @@ var (
errRequestHeaderListSize = httpcommon.ErrRequestHeaderListSize
)
func (cc *ClientConn) logf(format string, args ...interface{}) {
func (cc *ClientConn) logf(format string, args ...any) {
cc.t.logf(format, args...)
}
func (cc *ClientConn) vlogf(format string, args ...interface{}) {
func (cc *ClientConn) vlogf(format string, args ...any) {
cc.t.vlogf(format, args...)
}
func (t *Transport) vlogf(format string, args ...interface{}) {
func (t *Transport) vlogf(format string, args ...any) {
if VerboseLogs {
t.logf(format, args...)
}
}
func (t *Transport) logf(format string, args ...interface{}) {
func (t *Transport) logf(format string, args ...any) {
log.Printf(format, args...)
}
@@ -3089,15 +3086,6 @@ type missingBody struct{}
func (missingBody) Close() error { return nil }
func (missingBody) Read([]byte) (int, error) { return 0, io.ErrUnexpectedEOF }
func strSliceContains(ss []string, s string) bool {
for _, v := range ss {
if v == s {
return true
}
}
return false
}
type erringRoundTripper struct{ err error }
func (rt erringRoundTripper) RoundTripErr() error { return rt.err }

View File

@@ -123,7 +123,7 @@ func TestIdleConnTimeout(t *testing.T) {
tr.IdleConnTimeout = test.idleConnTimeout
})
var tc *testClientConn
for i := 0; i < 3; i++ {
for i := range 3 {
req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
rt := tt.roundTrip(req)
@@ -413,7 +413,7 @@ func testTransportGetGotConnHooks(t *testing.T, useClient bool) {
getConns int32
gotConns int32
)
for i := 0; i < 2; i++ {
for i := range 2 {
trace := &httptrace.ClientTrace{
GetConn: func(hostport string) {
atomic.AddInt32(&getConns, 1)
@@ -1373,7 +1373,7 @@ func testTransportChecksResponseHeaderListSize(t testing.TB) {
hdr := []string{":status", "200"}
large := strings.Repeat("a", 1<<10)
for i := 0; i < 5042; i++ {
for range 5042 {
hdr = append(hdr, large, large)
}
hbf := tc.makeHeaderBlockFragment(hdr...)
@@ -1571,7 +1571,7 @@ func TestTransportDisableKeepAlives_Concurrency(t *testing.T) {
c := &http.Client{Transport: tr}
var reqs sync.WaitGroup
const N = 20
for i := 0; i < N; i++ {
for i := range N {
reqs.Add(1)
if i == N-1 {
// For the final request, try to make all the
@@ -1877,7 +1877,6 @@ func TestTransportRejectsContentLengthWithSign(t *testing.T) {
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Length", tt.cl[0])
@@ -2464,7 +2463,7 @@ func testTransportBodyDoubleEndStream(t testing.TB) {
tr := newTransport(t)
for i := 0; i < 2; i++ {
for i := range 2 {
req, _ := http.NewRequest("POST", ts.URL, byteAndEOFReader('a'))
req.ContentLength = 1
res, err := tr.RoundTrip(req)
@@ -2658,7 +2657,7 @@ func TestTransportCancelDataResponseRace(t *testing.T) {
io.WriteString(w, msg)
return
}
for i := 0; i < 50; i++ {
for i := range 50 {
io.WriteString(w, "Some data.")
w.(http.Flusher).Flush()
if i == 2 {
@@ -2795,7 +2794,7 @@ func testTransportPingWhenReadingMultiplePings(t testing.TB) {
),
})
for i := 0; i < 5; i++ {
for range 5 {
// No ping yet...
time.Sleep(999 * time.Millisecond)
if f := tc.readFrame(); f != nil {
@@ -3157,7 +3156,7 @@ func TestTransportRequestsLowServerLimit(t *testing.T) {
}
const reqCount = 3
for i := 0; i < reqCount; i++ {
for range reqCount {
req, err := http.NewRequest("GET", ts.URL, nil)
if err != nil {
t.Fatal(err)
@@ -3196,7 +3195,7 @@ func testTransportRequestsStallAtServerLimit(t *testing.T) {
// Start maxConcurrent+2 requests.
// The server does not respond to any of them yet.
var rts []*testRoundTrip
for k := 0; k < maxConcurrent+2; k++ {
for k := range maxConcurrent + 2 {
req, _ := http.NewRequest("GET", fmt.Sprintf("https://dummy.tld/%d", k), nil)
if k == maxConcurrent {
req.Cancel = cancelClientRequest
@@ -3378,7 +3377,7 @@ func benchSimpleRoundTrip(b *testing.B, nReqHeaders, nResHeader int) {
b.ReportAllocs()
ts := newTestServer(b,
func(w http.ResponseWriter, r *http.Request) {
for i := 0; i < nResHeader; i++ {
for i := range nResHeader {
name := fmt.Sprint("A-", i)
w.Header().Set(name, "*")
}
@@ -3393,7 +3392,7 @@ func benchSimpleRoundTrip(b *testing.B, nReqHeaders, nResHeader int) {
b.Fatal(err)
}
for i := 0; i < nReqHeaders; i++ {
for i := range nReqHeaders {
name := fmt.Sprint("A-", i)
req.Header.Set(name, "*")
}
@@ -3496,7 +3495,7 @@ func benchLargeDownloadRoundTrip(b *testing.B, frameSize uint32) {
w.Header().Set("Content-Length", strconv.Itoa(transferSize))
w.Header().Set("Content-Transfer-Encoding", "binary")
var data [1024 * 1024]byte
for i := 0; i < transferSize/(1024*1024); i++ {
for range transferSize / (1024 * 1024) {
w.Write(data[:])
}
}, optQuiet,
@@ -3922,7 +3921,7 @@ func TestTransportBodyRewindRace(t *testing.T) {
var wg sync.WaitGroup
wg.Add(clients)
for i := 0; i < clients; i++ {
for range clients {
req, err := http.NewRequest("POST", ts.URL, bytes.NewBufferString("abcdef"))
if err != nil {
t.Fatalf("unexpected new request error: %v", err)
@@ -4164,10 +4163,8 @@ func TestTransportFrameBufferReuse(t *testing.T) {
var wg sync.WaitGroup
defer wg.Wait()
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for range 10 {
wg.Go(func() {
req, err := http.NewRequest("POST", ts.URL, strings.NewReader(filler))
if err != nil {
t.Error(err)
@@ -4187,7 +4184,7 @@ func TestTransportFrameBufferReuse(t *testing.T) {
if res != nil && res.Body != nil {
res.Body.Close()
}
}()
})
}
}
@@ -4232,7 +4229,6 @@ func TestTransportBlockingRequestWrite(t *testing.T) {
return req, err
},
}} {
test := test
t.Run(test.name, func(t *testing.T) {
ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
if v := r.Header.Get("Big"); v != "" && v != filler {
@@ -4880,8 +4876,7 @@ func TestDialRaceResumesDial(t *testing.T) {
ctx1, cancel1 := context.WithCancel(context.Background())
defer cancel1()
req1 := req.WithContext(ctx1)
ctx2, cancel2 := context.WithCancel(context.Background())
defer cancel2()
ctx2 := t.Context()
req2 := req.WithContext(ctx2)
errCh := make(chan error)
go func() {
@@ -4994,7 +4989,7 @@ func TestIssue67671(t *testing.T) {
tr.Protocols = protocols("h2c")
req, _ := http.NewRequest("GET", ts.URL, nil)
req.Close = true
for i := 0; i < 2; i++ {
for range 2 {
res, err := tr.RoundTrip(req)
if err != nil {
t.Fatal(err)
@@ -5289,7 +5284,7 @@ func testTransportConnBecomesUnresponsive(t testing.TB) {
// Send more requests.
// None receive a response.
// Each is canceled.
for i := 0; i < maxConcurrent; i++ {
for i := range maxConcurrent {
t.Logf("request %v receives no response and is canceled", i)
ctx, cancel := context.WithCancel(context.Background())
req := Must(http.NewRequestWithContext(ctx, "GET", "https://dummy.tld/", nil))

View File

@@ -115,10 +115,7 @@ func (wr FrameWriteRequest) Consume(n int32) (FrameWriteRequest, FrameWriteReque
}
// Might need to split after applying limits.
allowed := wr.stream.flow.available()
if n < allowed {
allowed = n
}
allowed := min(n, wr.stream.flow.available())
if wr.stream.sc.maxFrameSize < allowed {
allowed = wr.stream.sc.maxFrameSize
}

View File

@@ -5,9 +5,10 @@
package http2
import (
"cmp"
"fmt"
"math"
"sort"
"slices"
)
// RFC 7540, Section 5.3.5: the default weight is 16.
@@ -199,7 +200,19 @@ func (n *priorityNodeRFC7540) walkReadyInOrder(openParent bool, tmp *[]*priority
*tmp = append(*tmp, n.kids)
n.kids.setParent(nil)
}
sort.Sort(sortPriorityNodeSiblingsRFC7540(*tmp))
slices.SortFunc(*tmp, func(i, k *priorityNodeRFC7540) int {
// Prefer the subtree that has sent fewer bytes relative to its weight.
// See sections 5.3.2 and 5.3.4.
wi, bi := float64(i.weight)+1, float64(i.subtreeBytes)
wk, bk := float64(k.weight)+1, float64(k.subtreeBytes)
if bi == 0 && bk == 0 {
return cmp.Compare(wk, wi)
}
if bk == 0 {
return 0
}
return cmp.Compare(bi/bk, wi/wk)
})
for i := len(*tmp) - 1; i >= 0; i-- {
(*tmp)[i].setParent(n) // setParent inserts at the head of n.kids
}
@@ -211,24 +224,6 @@ func (n *priorityNodeRFC7540) walkReadyInOrder(openParent bool, tmp *[]*priority
return false
}
type sortPriorityNodeSiblingsRFC7540 []*priorityNodeRFC7540
func (z sortPriorityNodeSiblingsRFC7540) Len() int { return len(z) }
func (z sortPriorityNodeSiblingsRFC7540) Swap(i, k int) { z[i], z[k] = z[k], z[i] }
func (z sortPriorityNodeSiblingsRFC7540) Less(i, k int) bool {
// Prefer the subtree that has sent fewer bytes relative to its weight.
// See sections 5.3.2 and 5.3.4.
wi, bi := float64(z[i].weight)+1, float64(z[i].subtreeBytes)
wk, bk := float64(z[k].weight)+1, float64(z[k].subtreeBytes)
if bi == 0 && bk == 0 {
return wi >= wk
}
if bk == 0 {
return false
}
return bi/bk <= wi/wk
}
type priorityWriteSchedulerRFC7540 struct {
// root is the root of the priority tree, where root.id = 0.
// The root queues control frames that are not associated with any stream.

View File

@@ -33,12 +33,12 @@ func TestRoundRobinScheduler(t *testing.T) {
ws.Push(wr)
}
const controlFrames = 2
for i := 0; i < controlFrames; i++ {
for range controlFrames {
ws.Push(makeWriteNonStreamRequest())
}
// We should get the control frames first.
for i := 0; i < controlFrames; i++ {
for range controlFrames {
wr, ok := ws.Pop()
if !ok || wr.StreamID() != 0 {
t.Fatalf("wr.Pop() = stream %v, %v; want 0, true", wr.StreamID(), ok)