| // 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" |
| "time" |
| ) |
| |
| // Config must be kept in sync with net/http.HTTP2Config. |
| type Config struct { |
| MaxConcurrentStreams int |
| StrictMaxConcurrentRequests bool |
| MaxDecoderHeaderTableSize int |
| MaxEncoderHeaderTableSize int |
| MaxReadFrameSize int |
| MaxReceiveBufferPerConnection int |
| MaxReceiveBufferPerStream int |
| SendPingTimeout time.Duration |
| PingTimeout time.Duration |
| WriteByteTimeout time.Duration |
| PermitProhibitedCipherSuites bool |
| CountError func(errType string) |
| } |
| |
| func configFromServer(h1 ServerConfig) Config { |
| conf := Config{} |
| fillNetHTTPConfig(&conf, h1.HTTP2Config()) |
| setConfigDefaults(&conf, true) |
| return conf |
| } |
| |
| func configFromTransport(h2 *Transport) Config { |
| conf := Config{} |
| if h2.t1 != nil { |
| fillNetHTTPConfig(&conf, h2.t1.HTTP2Config()) |
| } |
| 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 *Config, server bool) { |
| setDefault(&conf.MaxConcurrentStreams, 1, math.MaxInt32, defaultMaxStreams) |
| setDefault(&conf.MaxEncoderHeaderTableSize, 1, math.MaxInt32, initialHeaderTableSize) |
| setDefault(&conf.MaxDecoderHeaderTableSize, 1, math.MaxInt32, initialHeaderTableSize) |
| if server { |
| setDefault(&conf.MaxReceiveBufferPerConnection, initialWindowSize, math.MaxInt32, 1<<20) |
| } else { |
| setDefault(&conf.MaxReceiveBufferPerConnection, initialWindowSize, math.MaxInt32, transportDefaultConnFlow) |
| } |
| if server { |
| setDefault(&conf.MaxReceiveBufferPerStream, 1, math.MaxInt32, 1<<20) |
| } else { |
| setDefault(&conf.MaxReceiveBufferPerStream, 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 |
| } |
| |
| func fillNetHTTPConfig(conf *Config, h2 Config) { |
| if h2.MaxConcurrentStreams != 0 { |
| conf.MaxConcurrentStreams = h2.MaxConcurrentStreams |
| } |
| if h2.StrictMaxConcurrentRequests { |
| conf.StrictMaxConcurrentRequests = true |
| } |
| if h2.MaxEncoderHeaderTableSize != 0 { |
| conf.MaxEncoderHeaderTableSize = h2.MaxEncoderHeaderTableSize |
| } |
| if h2.MaxDecoderHeaderTableSize != 0 { |
| conf.MaxDecoderHeaderTableSize = h2.MaxDecoderHeaderTableSize |
| } |
| if h2.MaxConcurrentStreams != 0 { |
| conf.MaxConcurrentStreams = h2.MaxConcurrentStreams |
| } |
| if h2.MaxReadFrameSize != 0 { |
| conf.MaxReadFrameSize = h2.MaxReadFrameSize |
| } |
| if h2.MaxReceiveBufferPerConnection != 0 { |
| conf.MaxReceiveBufferPerConnection = h2.MaxReceiveBufferPerConnection |
| } |
| if h2.MaxReceiveBufferPerStream != 0 { |
| conf.MaxReceiveBufferPerStream = h2.MaxReceiveBufferPerStream |
| } |
| if h2.SendPingTimeout != 0 { |
| conf.SendPingTimeout = h2.SendPingTimeout |
| } |
| if h2.PingTimeout != 0 { |
| conf.PingTimeout = h2.PingTimeout |
| } |
| if h2.WriteByteTimeout != 0 { |
| conf.WriteByteTimeout = h2.WriteByteTimeout |
| } |
| if h2.PermitProhibitedCipherSuites { |
| conf.PermitProhibitedCipherSuites = true |
| } |
| if h2.CountError != nil { |
| conf.CountError = h2.CountError |
| } |
| } |