Repository: go
Issue: net/http: HTTP/2 configuration API
Labels: Proposal,Proposal-Accepted

This issue is part of a project to move `x/net/http2` into `std`: #67810

Configuring HTTP/2-specific protocol options currently requires users to import the [golang.org/x/net/http2](https://pkg.go.dev/golang.org/x/net/http2) package and call [http2.ConfigureServer](https://pkg.go.dev/golang.org/x/net/http2#ConfigureServer) or [http2.ConfigureTransports](https://pkg.go.dev/golang.org/x/net/http2#ConfigureTransports).

Configuring options in this fashion has the side effect of replacing the bundled HTTP/2 implementation in `net/http` with the one in `golang.org/x/net/http2`.

I propose adding HTTP/2 configuration options to `net/http`, permitting HTTP/2 servers and clients to be configured without importing an external package and separating configuration from version selection.

Many of the HTTP/2 settings are identical for server and client connections. For example, the `MaxDecoderHeaderTableSize` field of [http2.Server](https://pkg.go.dev/golang.org/x/net/http2#Server.MaxDecoderHeaderTableSize) and [http2.Transport](https://pkg.go.dev/golang.org/x/net/http2#Transport.MaxDecoderHeaderTableSize) sets the SETTINGS_HEADER_TABLE_SIZE setting sent to the peer. We will unify these settings into a single configuration struct, and add this configuration to `http.Server` and `http.Transport`.

```go
type HTTP2Config struct {
	// MaxConcurrentStreams optionally specifies the number of
	// concurrent streams that a peer may have open at a
	// time (SETTINGS_MAX_CONCURRENT_STREAMS).
	// If zero, MaxConcurrentStreams defaults to at least 100.
	MaxConcurrentStreams uint32

	// MaxDecoderHeaderTableSize optionally specifies the http2
	// SETTINGS_HEADER_TABLE_SIZE to send in the initial settings frame. It
	// informs the remote endpoint of the maximum size of the header compression
	// table used to decode header blocks, in octets. If zero, the default value
	// of 4096 is used.
	MaxDecoderHeaderTableSize uint32

	// MaxEncoderHeaderTableSize optionally specifies an upper limit for the
	// header compression table used for encoding request headers. Received
	// SETTINGS_HEADER_TABLE_SIZE settings are capped at this limit. If zero,
	// the default value of 4096 is used.
	MaxEncoderHeaderTableSize uint32

	// MaxReadFrameSize optionally specifies the largest frame
	// this endpoint is willing to read (SETTINGS_MAX_FRAME_SIZE).
	// A valid value is between 16k and 16M, inclusive.
	// If zero or otherwise invalid, a default value is used.
	MaxReadFrameSize uint32

	// MaxUploadBufferPerConnection is the size of the initial flow
	// control window for each connection. The HTTP/2 spec does not
	// allow this to be smaller than 65535 or larger than 2^32-1.
	// If the value is outside this range, a default value will be
	// used instead.
	MaxUploadBufferPerConnection int32

	// MaxUploadBufferPerStream is the size of the initial flow control
	// window for each stream. The HTTP/2 spec does not allow this to
	// be larger than 2^32-1. If the value is zero or larger than the
	// maximum, a default value will be used instead.
	MaxUploadBufferPerStream int32

	// SendPingTimeout is the timeout after which a health check using a ping
	// frame will be carried out if no frame is received on the connection.
	// If zero, no health check is performed.
	SendPingTimeout time.Duration

	// PingTimeout is the timeout after which the connection will be closed
	// if a response to a ping is not received.
	// If zero, a default of 15 seconds is used.
	PingTimeout time.Duration

	// WriteByteTimeout is the timeout after which a connection will be
	// closed if no data can be written to it. The timeout begins when data is
	// available to write, and is extended whenever any bytes are written.
	WriteByteTimeout time.Duration

	// PermitProhibitedCipherSuites, if true, permits the use of
	// cipher suites prohibited by the HTTP/2 spec.
	PermitProhibitedCipherSuites bool

	// CountError, if non-nil, is called on HTTP/2 errors.
	// It's intended to increment a metric for monitoring, such
	// as an expvar or Prometheus metric.
	// The errType consists of only ASCII word characters.
	CountError func(errType string)
}

type Server struct { // contains unchanged fields
	HTTP2 HTTP2Config
}

type Transport struct { // contains unchanged fields
	HTTP2 HTTP2Config
}
```

The `SendPingTImeout` and `PingTimeout` fields assume #67812 is accepted, and the `WriteByteTimeout` field assumes #67811 is accepted.

The [http2.Transport.StrictMaxConcurrentStreams](https://pkg.go.dev/golang.org/x/net/http2#Transport.StrictMaxConcurrentStreams) field controls whether a new connection should be opened to a server if an existing connection has exceeded its stream limit. For example, if an HTTP/2 server advertises a stream limit of 100 concurrent streams, then the 101st concurrent stream opened by the client will block waiting for an existing stream to complete when `StrictMaxConcurrentStreams` is true, or create a new connection when it is false. There is no equivalent to this setting for HTTP/1 connections, which only support a single concurrent request per connection. We will add this setting to `http.Transport`, since it could be used to configure HTTP/3 connections (if and when we support HTTP/3):

```go
type Transport struct { // contains unchanged fields
	// StrictMaxConcurrentRequests controls whether an HTTP/2 server's
	// concurrency limit should be respected globally.
	// If true, new requests sent when an connection's concurrency limit has been exceeded
	// will block until an existing request completes.
	// If false, an additional connection will be opened if all existing connections are at their limit.
	StrictMaxConcurrentRequests bool
}
```