| 
									
										
										
										
											2024-10-07 12:02:26 +00:00
										 |  |  | // 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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-20 09:03:10 +00:00
										 |  |  | // configFromTransport merges configuration settings from h2 and h2.t1.HTTP2 | 
					
						
							| 
									
										
										
										
											2024-10-07 12:02:26 +00:00
										 |  |  | // (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 | 
					
						
							|  |  |  | } |