| // Copyright 2024 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package http2 |
| |
| import ( |
| "math" |
| "net/http" |
| "time" |
| ) |
| |
| // http2Config is a package-internal version of net/http.HTTP2Config. |
| // |
| // http.HTTP2Config was added in Go 1.24. |
| // When running with a version of net/http that includes HTTP2Config, |
| // we merge the configuration with the fields in Transport or Server |
| // to produce an http2Config. |
| // |
| // Zero valued fields in http2Config are interpreted as in the |
| // net/http.HTTPConfig documentation. |
| // |
| // Precedence order for reconciling configurations is: |
| // |
| // - Use the net/http.{Server,Transport}.HTTP2Config value, when non-zero. |
| // - Otherwise use the http2.{Server.Transport} value. |
| // - If the resulting value is zero or out of range, use a default. |
| type http2Config struct { |
| MaxConcurrentStreams uint32 |
| MaxDecoderHeaderTableSize uint32 |
| MaxEncoderHeaderTableSize uint32 |
| MaxReadFrameSize uint32 |
| MaxUploadBufferPerConnection int32 |
| MaxUploadBufferPerStream int32 |
| SendPingTimeout time.Duration |
| PingTimeout time.Duration |
| WriteByteTimeout time.Duration |
| PermitProhibitedCipherSuites bool |
| CountError func(errType string) |
| } |
| |
| // configFromServer merges configuration settings from |
| // net/http.Server.HTTP2Config and http2.Server. |
| func configFromServer(h1 *http.Server, h2 *Server) http2Config { |
| conf := http2Config{ |
| MaxConcurrentStreams: h2.MaxConcurrentStreams, |
| MaxEncoderHeaderTableSize: h2.MaxEncoderHeaderTableSize, |
| MaxDecoderHeaderTableSize: h2.MaxDecoderHeaderTableSize, |
| MaxReadFrameSize: h2.MaxReadFrameSize, |
| MaxUploadBufferPerConnection: h2.MaxUploadBufferPerConnection, |
| MaxUploadBufferPerStream: h2.MaxUploadBufferPerStream, |
| SendPingTimeout: h2.ReadIdleTimeout, |
| PingTimeout: h2.PingTimeout, |
| WriteByteTimeout: h2.WriteByteTimeout, |
| PermitProhibitedCipherSuites: h2.PermitProhibitedCipherSuites, |
| CountError: h2.CountError, |
| } |
| fillNetHTTPServerConfig(&conf, h1) |
| setConfigDefaults(&conf, true) |
| return conf |
| } |
| |
| // configFromServer merges configuration settings from h2 and h2.t1.HTTP2 |
| // (the net/http Transport). |
| func configFromTransport(h2 *Transport) http2Config { |
| conf := http2Config{ |
| MaxEncoderHeaderTableSize: h2.MaxEncoderHeaderTableSize, |
| MaxDecoderHeaderTableSize: h2.MaxDecoderHeaderTableSize, |
| MaxReadFrameSize: h2.MaxReadFrameSize, |
| SendPingTimeout: h2.ReadIdleTimeout, |
| PingTimeout: h2.PingTimeout, |
| WriteByteTimeout: h2.WriteByteTimeout, |
| } |
| |
| // Unlike most config fields, where out-of-range values revert to the default, |
| // Transport.MaxReadFrameSize clips. |
| if conf.MaxReadFrameSize < minMaxFrameSize { |
| conf.MaxReadFrameSize = minMaxFrameSize |
| } else if conf.MaxReadFrameSize > maxFrameSize { |
| conf.MaxReadFrameSize = maxFrameSize |
| } |
| |
| if h2.t1 != nil { |
| fillNetHTTPTransportConfig(&conf, h2.t1) |
| } |
| setConfigDefaults(&conf, false) |
| return conf |
| } |
| |
| func setDefault[T ~int | ~int32 | ~uint32 | ~int64](v *T, minval, maxval, defval T) { |
| if *v < minval || *v > maxval { |
| *v = defval |
| } |
| } |
| |
| func setConfigDefaults(conf *http2Config, server bool) { |
| setDefault(&conf.MaxConcurrentStreams, 1, math.MaxUint32, defaultMaxStreams) |
| setDefault(&conf.MaxEncoderHeaderTableSize, 1, math.MaxUint32, initialHeaderTableSize) |
| setDefault(&conf.MaxDecoderHeaderTableSize, 1, math.MaxUint32, initialHeaderTableSize) |
| if server { |
| setDefault(&conf.MaxUploadBufferPerConnection, initialWindowSize, math.MaxInt32, 1<<20) |
| } else { |
| setDefault(&conf.MaxUploadBufferPerConnection, initialWindowSize, math.MaxInt32, transportDefaultConnFlow) |
| } |
| if server { |
| setDefault(&conf.MaxUploadBufferPerStream, 1, math.MaxInt32, 1<<20) |
| } else { |
| setDefault(&conf.MaxUploadBufferPerStream, 1, math.MaxInt32, transportDefaultStreamFlow) |
| } |
| setDefault(&conf.MaxReadFrameSize, minMaxFrameSize, maxFrameSize, defaultMaxReadFrameSize) |
| setDefault(&conf.PingTimeout, 1, math.MaxInt64, 15*time.Second) |
| } |
| |
| // adjustHTTP1MaxHeaderSize converts a limit in bytes on the size of an HTTP/1 header |
| // to an HTTP/2 MAX_HEADER_LIST_SIZE value. |
| func adjustHTTP1MaxHeaderSize(n int64) int64 { |
| // http2's count is in a slightly different unit and includes 32 bytes per pair. |
| // So, take the net/http.Server value and pad it up a bit, assuming 10 headers. |
| const perFieldOverhead = 32 // per http2 spec |
| const typicalHeaders = 10 // conservative |
| return n + typicalHeaders*perFieldOverhead |
| } |