blob: 8a7a89d016ed6dbe1d5b1b682ef11d8d5a3b7ff2 [file] [log] [blame]
mgouda64be8822025-05-30 10:44:00 +05301// Copyright 2024 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package http2
6
7import (
8 "math"
9 "net/http"
10 "time"
11)
12
13// http2Config is a package-internal version of net/http.HTTP2Config.
14//
15// http.HTTP2Config was added in Go 1.24.
16// When running with a version of net/http that includes HTTP2Config,
17// we merge the configuration with the fields in Transport or Server
18// to produce an http2Config.
19//
20// Zero valued fields in http2Config are interpreted as in the
21// net/http.HTTPConfig documentation.
22//
23// Precedence order for reconciling configurations is:
24//
25// - Use the net/http.{Server,Transport}.HTTP2Config value, when non-zero.
26// - Otherwise use the http2.{Server.Transport} value.
27// - If the resulting value is zero or out of range, use a default.
28type http2Config struct {
29 MaxConcurrentStreams uint32
Abhay Kumara2ae5992025-11-10 14:02:24 +000030 StrictMaxConcurrentRequests bool
mgouda64be8822025-05-30 10:44:00 +053031 MaxDecoderHeaderTableSize uint32
32 MaxEncoderHeaderTableSize uint32
33 MaxReadFrameSize uint32
34 MaxUploadBufferPerConnection int32
35 MaxUploadBufferPerStream int32
36 SendPingTimeout time.Duration
37 PingTimeout time.Duration
38 WriteByteTimeout time.Duration
39 PermitProhibitedCipherSuites bool
40 CountError func(errType string)
41}
42
43// configFromServer merges configuration settings from
44// net/http.Server.HTTP2Config and http2.Server.
45func configFromServer(h1 *http.Server, h2 *Server) http2Config {
46 conf := http2Config{
47 MaxConcurrentStreams: h2.MaxConcurrentStreams,
48 MaxEncoderHeaderTableSize: h2.MaxEncoderHeaderTableSize,
49 MaxDecoderHeaderTableSize: h2.MaxDecoderHeaderTableSize,
50 MaxReadFrameSize: h2.MaxReadFrameSize,
51 MaxUploadBufferPerConnection: h2.MaxUploadBufferPerConnection,
52 MaxUploadBufferPerStream: h2.MaxUploadBufferPerStream,
53 SendPingTimeout: h2.ReadIdleTimeout,
54 PingTimeout: h2.PingTimeout,
55 WriteByteTimeout: h2.WriteByteTimeout,
56 PermitProhibitedCipherSuites: h2.PermitProhibitedCipherSuites,
57 CountError: h2.CountError,
58 }
Abhay Kumara2ae5992025-11-10 14:02:24 +000059 fillNetHTTPConfig(&conf, h1.HTTP2)
mgouda64be8822025-05-30 10:44:00 +053060 setConfigDefaults(&conf, true)
61 return conf
62}
63
64// configFromTransport merges configuration settings from h2 and h2.t1.HTTP2
65// (the net/http Transport).
66func configFromTransport(h2 *Transport) http2Config {
67 conf := http2Config{
Abhay Kumara2ae5992025-11-10 14:02:24 +000068 StrictMaxConcurrentRequests: h2.StrictMaxConcurrentStreams,
69 MaxEncoderHeaderTableSize: h2.MaxEncoderHeaderTableSize,
70 MaxDecoderHeaderTableSize: h2.MaxDecoderHeaderTableSize,
71 MaxReadFrameSize: h2.MaxReadFrameSize,
72 SendPingTimeout: h2.ReadIdleTimeout,
73 PingTimeout: h2.PingTimeout,
74 WriteByteTimeout: h2.WriteByteTimeout,
mgouda64be8822025-05-30 10:44:00 +053075 }
76
77 // Unlike most config fields, where out-of-range values revert to the default,
78 // Transport.MaxReadFrameSize clips.
79 if conf.MaxReadFrameSize < minMaxFrameSize {
80 conf.MaxReadFrameSize = minMaxFrameSize
81 } else if conf.MaxReadFrameSize > maxFrameSize {
82 conf.MaxReadFrameSize = maxFrameSize
83 }
84
85 if h2.t1 != nil {
Abhay Kumara2ae5992025-11-10 14:02:24 +000086 fillNetHTTPConfig(&conf, h2.t1.HTTP2)
mgouda64be8822025-05-30 10:44:00 +053087 }
88 setConfigDefaults(&conf, false)
89 return conf
90}
91
92func setDefault[T ~int | ~int32 | ~uint32 | ~int64](v *T, minval, maxval, defval T) {
93 if *v < minval || *v > maxval {
94 *v = defval
95 }
96}
97
98func setConfigDefaults(conf *http2Config, server bool) {
99 setDefault(&conf.MaxConcurrentStreams, 1, math.MaxUint32, defaultMaxStreams)
100 setDefault(&conf.MaxEncoderHeaderTableSize, 1, math.MaxUint32, initialHeaderTableSize)
101 setDefault(&conf.MaxDecoderHeaderTableSize, 1, math.MaxUint32, initialHeaderTableSize)
102 if server {
103 setDefault(&conf.MaxUploadBufferPerConnection, initialWindowSize, math.MaxInt32, 1<<20)
104 } else {
105 setDefault(&conf.MaxUploadBufferPerConnection, initialWindowSize, math.MaxInt32, transportDefaultConnFlow)
106 }
107 if server {
108 setDefault(&conf.MaxUploadBufferPerStream, 1, math.MaxInt32, 1<<20)
109 } else {
110 setDefault(&conf.MaxUploadBufferPerStream, 1, math.MaxInt32, transportDefaultStreamFlow)
111 }
112 setDefault(&conf.MaxReadFrameSize, minMaxFrameSize, maxFrameSize, defaultMaxReadFrameSize)
113 setDefault(&conf.PingTimeout, 1, math.MaxInt64, 15*time.Second)
114}
115
116// adjustHTTP1MaxHeaderSize converts a limit in bytes on the size of an HTTP/1 header
117// to an HTTP/2 MAX_HEADER_LIST_SIZE value.
118func adjustHTTP1MaxHeaderSize(n int64) int64 {
119 // http2's count is in a slightly different unit and includes 32 bytes per pair.
120 // So, take the net/http.Server value and pad it up a bit, assuming 10 headers.
121 const perFieldOverhead = 32 // per http2 spec
122 const typicalHeaders = 10 // conservative
123 return n + typicalHeaders*perFieldOverhead
124}
Abhay Kumara2ae5992025-11-10 14:02:24 +0000125
126func fillNetHTTPConfig(conf *http2Config, h2 *http.HTTP2Config) {
127 if h2 == nil {
128 return
129 }
130 if h2.MaxConcurrentStreams != 0 {
131 conf.MaxConcurrentStreams = uint32(h2.MaxConcurrentStreams)
132 }
133 if http2ConfigStrictMaxConcurrentRequests(h2) {
134 conf.StrictMaxConcurrentRequests = true
135 }
136 if h2.MaxEncoderHeaderTableSize != 0 {
137 conf.MaxEncoderHeaderTableSize = uint32(h2.MaxEncoderHeaderTableSize)
138 }
139 if h2.MaxDecoderHeaderTableSize != 0 {
140 conf.MaxDecoderHeaderTableSize = uint32(h2.MaxDecoderHeaderTableSize)
141 }
142 if h2.MaxConcurrentStreams != 0 {
143 conf.MaxConcurrentStreams = uint32(h2.MaxConcurrentStreams)
144 }
145 if h2.MaxReadFrameSize != 0 {
146 conf.MaxReadFrameSize = uint32(h2.MaxReadFrameSize)
147 }
148 if h2.MaxReceiveBufferPerConnection != 0 {
149 conf.MaxUploadBufferPerConnection = int32(h2.MaxReceiveBufferPerConnection)
150 }
151 if h2.MaxReceiveBufferPerStream != 0 {
152 conf.MaxUploadBufferPerStream = int32(h2.MaxReceiveBufferPerStream)
153 }
154 if h2.SendPingTimeout != 0 {
155 conf.SendPingTimeout = h2.SendPingTimeout
156 }
157 if h2.PingTimeout != 0 {
158 conf.PingTimeout = h2.PingTimeout
159 }
160 if h2.WriteByteTimeout != 0 {
161 conf.WriteByteTimeout = h2.WriteByteTimeout
162 }
163 if h2.PermitProhibitedCipherSuites {
164 conf.PermitProhibitedCipherSuites = true
165 }
166 if h2.CountError != nil {
167 conf.CountError = h2.CountError
168 }
169}