config.go raw

   1  // 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  
   5  package http2
   6  
   7  import (
   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.
  28  type http2Config struct {
  29  	MaxConcurrentStreams         uint32
  30  	StrictMaxConcurrentRequests  bool
  31  	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.
  45  func 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  	}
  59  	fillNetHTTPConfig(&conf, h1.HTTP2)
  60  	setConfigDefaults(&conf, true)
  61  	return conf
  62  }
  63  
  64  // configFromTransport merges configuration settings from h2 and h2.t1.HTTP2
  65  // (the net/http Transport).
  66  func configFromTransport(h2 *Transport) http2Config {
  67  	conf := http2Config{
  68  		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,
  75  	}
  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 {
  86  		fillNetHTTPConfig(&conf, h2.t1.HTTP2)
  87  	}
  88  	setConfigDefaults(&conf, false)
  89  	return conf
  90  }
  91  
  92  func setDefault[T ~int | ~int32 | ~uint32 | ~int64](v *T, minval, maxval, defval T) {
  93  	if *v < minval || *v > maxval {
  94  		*v = defval
  95  	}
  96  }
  97  
  98  func 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.
 118  func 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  }
 125  
 126  func 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  }
 170