Allow HTTP/2 max concurrent stream configuration
This commit is contained in:
parent
0d7d5a0318
commit
8c56d1a338
9 changed files with 84 additions and 4 deletions
|
@ -147,8 +147,11 @@ Subject alternative names.
|
||||||
`--entrypoints.<name>.http.tls.options`:
|
`--entrypoints.<name>.http.tls.options`:
|
||||||
Default TLS options for the routers linked to the entry point.
|
Default TLS options for the routers linked to the entry point.
|
||||||
|
|
||||||
|
`--entrypoints.<name>.http2.maxconcurrentstreams`:
|
||||||
|
Specifies the number of concurrent streams per connection that each client is allowed to initiate. (Default: ```250```)
|
||||||
|
|
||||||
`--entrypoints.<name>.http3`:
|
`--entrypoints.<name>.http3`:
|
||||||
HTTP3 configuration. (Default: ```false```)
|
HTTP/3 configuration. (Default: ```false```)
|
||||||
|
|
||||||
`--entrypoints.<name>.http3.advertisedport`:
|
`--entrypoints.<name>.http3.advertisedport`:
|
||||||
UDP port to advertise, on which HTTP/3 is available. (Default: ```0```)
|
UDP port to advertise, on which HTTP/3 is available. (Default: ```0```)
|
||||||
|
|
|
@ -114,8 +114,11 @@ Trust only forwarded headers from selected IPs.
|
||||||
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP`:
|
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP`:
|
||||||
HTTP configuration.
|
HTTP configuration.
|
||||||
|
|
||||||
|
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP2_MAXCONCURRENTSTREAMS`:
|
||||||
|
Specifies the number of concurrent streams per connection that each client is allowed to initiate. (Default: ```250```)
|
||||||
|
|
||||||
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP3`:
|
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP3`:
|
||||||
HTTP3 configuration. (Default: ```false```)
|
HTTP/3 configuration. (Default: ```false```)
|
||||||
|
|
||||||
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP3_ADVERTISEDPORT`:
|
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP3_ADVERTISEDPORT`:
|
||||||
UDP port to advertise, on which HTTP/3 is available. (Default: ```0```)
|
UDP port to advertise, on which HTTP/3 is available. (Default: ```0```)
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
trustedIPs = ["foobar", "foobar"]
|
trustedIPs = ["foobar", "foobar"]
|
||||||
[entryPoints.EntryPoint0.udp]
|
[entryPoints.EntryPoint0.udp]
|
||||||
timeout = 42
|
timeout = 42
|
||||||
|
[entryPoints.EntryPoint0.http2]
|
||||||
|
maxConcurrentStreams = 42
|
||||||
[entryPoints.EntryPoint0.http3]
|
[entryPoints.EntryPoint0.http3]
|
||||||
advertisedPort = 42
|
advertisedPort = 42
|
||||||
[entryPoints.EntryPoint0.http]
|
[entryPoints.EntryPoint0.http]
|
||||||
|
|
|
@ -32,6 +32,8 @@ entryPoints:
|
||||||
trustedIPs:
|
trustedIPs:
|
||||||
- foobar
|
- foobar
|
||||||
- foobar
|
- foobar
|
||||||
|
http2:
|
||||||
|
maxConcurrentStreams: 42
|
||||||
http3:
|
http3:
|
||||||
advertisedPort: 42
|
advertisedPort: 42
|
||||||
udp:
|
udp:
|
||||||
|
|
|
@ -100,6 +100,8 @@ They can be defined by using a file (YAML or TOML) or CLI arguments.
|
||||||
entryPoints:
|
entryPoints:
|
||||||
name:
|
name:
|
||||||
address: ":8888" # same as ":8888/tcp"
|
address: ":8888" # same as ":8888/tcp"
|
||||||
|
http2:
|
||||||
|
maxConcurrentStreams: 42
|
||||||
http3:
|
http3:
|
||||||
advertisedPort: 8888
|
advertisedPort: 8888
|
||||||
transport:
|
transport:
|
||||||
|
@ -127,6 +129,8 @@ They can be defined by using a file (YAML or TOML) or CLI arguments.
|
||||||
[entryPoints]
|
[entryPoints]
|
||||||
[entryPoints.name]
|
[entryPoints.name]
|
||||||
address = ":8888" # same as ":8888/tcp"
|
address = ":8888" # same as ":8888/tcp"
|
||||||
|
[entryPoints.name.http2]
|
||||||
|
maxConcurrentStreams = 42
|
||||||
[entryPoints.name.http3]
|
[entryPoints.name.http3]
|
||||||
advertisedPort = 8888
|
advertisedPort = 8888
|
||||||
[entryPoints.name.transport]
|
[entryPoints.name.transport]
|
||||||
|
@ -148,6 +152,7 @@ They can be defined by using a file (YAML or TOML) or CLI arguments.
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
## Static configuration
|
## Static configuration
|
||||||
--entryPoints.name.address=:8888 # same as :8888/tcp
|
--entryPoints.name.address=:8888 # same as :8888/tcp
|
||||||
|
--entryPoints.name.http2.maxConcurrentStreams=42
|
||||||
--entryPoints.name.http3.advertisedport=8888
|
--entryPoints.name.http3.advertisedport=8888
|
||||||
--entryPoints.name.transport.lifeCycle.requestAcceptGraceTimeout=42
|
--entryPoints.name.transport.lifeCycle.requestAcceptGraceTimeout=42
|
||||||
--entryPoints.name.transport.lifeCycle.graceTimeOut=42
|
--entryPoints.name.transport.lifeCycle.graceTimeOut=42
|
||||||
|
@ -223,6 +228,32 @@ If both TCP and UDP are wanted for the same port, two entryPoints definitions ar
|
||||||
|
|
||||||
Full details for how to specify `address` can be found in [net.Listen](https://golang.org/pkg/net/#Listen) (and [net.Dial](https://golang.org/pkg/net/#Dial)) of the doc for go.
|
Full details for how to specify `address` can be found in [net.Listen](https://golang.org/pkg/net/#Listen) (and [net.Dial](https://golang.org/pkg/net/#Dial)) of the doc for go.
|
||||||
|
|
||||||
|
### HTTP/2
|
||||||
|
|
||||||
|
#### `maxConcurrentStreams`
|
||||||
|
|
||||||
|
_Optional, Default=250_
|
||||||
|
|
||||||
|
`maxConcurrentStreams` specifies the number of concurrent streams per connection that each client is allowed to initiate.
|
||||||
|
The `maxConcurrentStreams` value must be greater than zero.
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
entryPoints:
|
||||||
|
foo:
|
||||||
|
http2:
|
||||||
|
maxConcurrentStreams: 250
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
[entryPoints.foo]
|
||||||
|
[entryPoints.foo.http2]
|
||||||
|
maxConcurrentStreams = 250
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab="CLI"
|
||||||
|
--entryPoints.name.http2.maxConcurrentStreams=250
|
||||||
|
```
|
||||||
|
|
||||||
### HTTP/3
|
### HTTP/3
|
||||||
|
|
||||||
#### `http3`
|
#### `http3`
|
||||||
|
|
|
@ -16,7 +16,8 @@ type EntryPoint struct {
|
||||||
ProxyProtocol *ProxyProtocol `description:"Proxy-Protocol configuration." json:"proxyProtocol,omitempty" toml:"proxyProtocol,omitempty" yaml:"proxyProtocol,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
ProxyProtocol *ProxyProtocol `description:"Proxy-Protocol configuration." json:"proxyProtocol,omitempty" toml:"proxyProtocol,omitempty" yaml:"proxyProtocol,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||||
ForwardedHeaders *ForwardedHeaders `description:"Trust client forwarding headers." json:"forwardedHeaders,omitempty" toml:"forwardedHeaders,omitempty" yaml:"forwardedHeaders,omitempty" export:"true"`
|
ForwardedHeaders *ForwardedHeaders `description:"Trust client forwarding headers." json:"forwardedHeaders,omitempty" toml:"forwardedHeaders,omitempty" yaml:"forwardedHeaders,omitempty" export:"true"`
|
||||||
HTTP HTTPConfig `description:"HTTP configuration." json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" export:"true"`
|
HTTP HTTPConfig `description:"HTTP configuration." json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" export:"true"`
|
||||||
HTTP3 *HTTP3Config `description:"HTTP3 configuration." json:"http3,omitempty" toml:"http3,omitempty" yaml:"http3,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
HTTP2 *HTTP2Config `description:"HTTP/2 configuration." json:"http2,omitempty" toml:"http2,omitempty" yaml:"http2,omitempty" export:"true"`
|
||||||
|
HTTP3 *HTTP3Config `description:"HTTP/3 configuration." json:"http3,omitempty" toml:"http3,omitempty" yaml:"http3,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||||
UDP *UDPConfig `description:"UDP configuration." json:"udp,omitempty" toml:"udp,omitempty" yaml:"udp,omitempty"`
|
UDP *UDPConfig `description:"UDP configuration." json:"udp,omitempty" toml:"udp,omitempty" yaml:"udp,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +51,8 @@ func (ep *EntryPoint) SetDefaults() {
|
||||||
ep.ForwardedHeaders = &ForwardedHeaders{}
|
ep.ForwardedHeaders = &ForwardedHeaders{}
|
||||||
ep.UDP = &UDPConfig{}
|
ep.UDP = &UDPConfig{}
|
||||||
ep.UDP.SetDefaults()
|
ep.UDP.SetDefaults()
|
||||||
|
ep.HTTP2 = &HTTP2Config{}
|
||||||
|
ep.HTTP2.SetDefaults()
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTPConfig is the HTTP configuration of an entry point.
|
// HTTPConfig is the HTTP configuration of an entry point.
|
||||||
|
@ -59,6 +62,16 @@ type HTTPConfig struct {
|
||||||
TLS *TLSConfig `description:"Default TLS configuration for the routers linked to the entry point." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
TLS *TLSConfig `description:"Default TLS configuration for the routers linked to the entry point." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTTP2Config is the HTTP2 configuration of an entry point.
|
||||||
|
type HTTP2Config struct {
|
||||||
|
MaxConcurrentStreams int32 `description:"Specifies the number of concurrent streams per connection that each client is allowed to initiate." json:"maxConcurrentStreams,omitempty" toml:"maxConcurrentStreams,omitempty" yaml:"maxConcurrentStreams,omitempty" export:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDefaults sets the default values.
|
||||||
|
func (c *HTTP2Config) SetDefaults() {
|
||||||
|
c.MaxConcurrentStreams = 250 // https://cs.opensource.google/go/x/net/+/cd36cc07:http2/server.go;l=58
|
||||||
|
}
|
||||||
|
|
||||||
// HTTP3Config is the HTTP3 configuration of an entry point.
|
// HTTP3Config is the HTTP3 configuration of an entry point.
|
||||||
type HTTP3Config struct {
|
type HTTP3Config struct {
|
||||||
AdvertisedPort int `description:"UDP port to advertise, on which HTTP/3 is available." json:"advertisedPort,omitempty" toml:"advertisedPort,omitempty" yaml:"advertisedPort,omitempty" export:"true"`
|
AdvertisedPort int `description:"UDP port to advertise, on which HTTP/3 is available." json:"advertisedPort,omitempty" toml:"advertisedPort,omitempty" yaml:"advertisedPort,omitempty" export:"true"`
|
||||||
|
|
|
@ -7,6 +7,8 @@ import (
|
||||||
stdlog "log"
|
stdlog "log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
@ -507,6 +509,10 @@ type httpServer struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func createHTTPServer(ctx context.Context, ln net.Listener, configuration *static.EntryPoint, withH2c bool, reqDecorator *requestdecorator.RequestDecorator) (*httpServer, error) {
|
func createHTTPServer(ctx context.Context, ln net.Listener, configuration *static.EntryPoint, withH2c bool, reqDecorator *requestdecorator.RequestDecorator) (*httpServer, error) {
|
||||||
|
if configuration.HTTP2.MaxConcurrentStreams < 0 {
|
||||||
|
return nil, errors.New("max concurrent streams value must be greater than or equal to zero")
|
||||||
|
}
|
||||||
|
|
||||||
httpSwitcher := middlewares.NewHandlerSwitcher(router.BuildDefaultHTTPRouter())
|
httpSwitcher := middlewares.NewHandlerSwitcher(router.BuildDefaultHTTPRouter())
|
||||||
|
|
||||||
next, err := alice.New(requestdecorator.WrapHandler(reqDecorator)).Then(httpSwitcher)
|
next, err := alice.New(requestdecorator.WrapHandler(reqDecorator)).Then(httpSwitcher)
|
||||||
|
@ -524,7 +530,9 @@ func createHTTPServer(ctx context.Context, ln net.Listener, configuration *stati
|
||||||
}
|
}
|
||||||
|
|
||||||
if withH2c {
|
if withH2c {
|
||||||
handler = h2c.NewHandler(handler, &http2.Server{})
|
handler = h2c.NewHandler(handler, &http2.Server{
|
||||||
|
MaxConcurrentStreams: uint32(configuration.HTTP2.MaxConcurrentStreams),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
serverHTTP := &http.Server{
|
serverHTTP := &http.Server{
|
||||||
|
@ -535,6 +543,20 @@ func createHTTPServer(ctx context.Context, ln net.Listener, configuration *stati
|
||||||
IdleTimeout: time.Duration(configuration.Transport.RespondingTimeouts.IdleTimeout),
|
IdleTimeout: time.Duration(configuration.Transport.RespondingTimeouts.IdleTimeout),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConfigureServer configures HTTP/2 with the MaxConcurrentStreams option for the given server.
|
||||||
|
// Also keeping behavior the same as
|
||||||
|
// https://cs.opensource.google/go/go/+/refs/tags/go1.17.7:src/net/http/server.go;l=3262
|
||||||
|
if !strings.Contains(os.Getenv("GODEBUG"), "http2server=0") {
|
||||||
|
err = http2.ConfigureServer(serverHTTP, &http2.Server{
|
||||||
|
MaxConcurrentStreams: uint32(configuration.HTTP2.MaxConcurrentStreams),
|
||||||
|
NewWriteScheduler: func() http2.WriteScheduler { return http2.NewPriorityWriteScheduler(nil) },
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("configure HTTP/2 server: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
listener := newHTTPForwarder(ln)
|
listener := newHTTPForwarder(ln)
|
||||||
go func() {
|
go func() {
|
||||||
err := serverHTTP.Serve(listener)
|
err := serverHTTP.Serve(listener)
|
||||||
|
|
|
@ -88,6 +88,7 @@ func TestHTTP3AdvertisedPort(t *testing.T) {
|
||||||
Address: "127.0.0.1:8090",
|
Address: "127.0.0.1:8090",
|
||||||
Transport: epConfig,
|
Transport: epConfig,
|
||||||
ForwardedHeaders: &static.ForwardedHeaders{},
|
ForwardedHeaders: &static.ForwardedHeaders{},
|
||||||
|
HTTP2: &static.HTTP2Config{},
|
||||||
HTTP3: &static.HTTP3Config{
|
HTTP3: &static.HTTP3Config{
|
||||||
AdvertisedPort: 8080,
|
AdvertisedPort: 8080,
|
||||||
},
|
},
|
||||||
|
|
|
@ -83,6 +83,7 @@ func testShutdown(t *testing.T, router *tcprouter.Router) {
|
||||||
Address: "127.0.0.1:0",
|
Address: "127.0.0.1:0",
|
||||||
Transport: epConfig,
|
Transport: epConfig,
|
||||||
ForwardedHeaders: &static.ForwardedHeaders{},
|
ForwardedHeaders: &static.ForwardedHeaders{},
|
||||||
|
HTTP2: &static.HTTP2Config{},
|
||||||
}, nil)
|
}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -166,6 +167,7 @@ func TestReadTimeoutWithoutFirstByte(t *testing.T) {
|
||||||
Address: ":0",
|
Address: ":0",
|
||||||
Transport: epConfig,
|
Transport: epConfig,
|
||||||
ForwardedHeaders: &static.ForwardedHeaders{},
|
ForwardedHeaders: &static.ForwardedHeaders{},
|
||||||
|
HTTP2: &static.HTTP2Config{},
|
||||||
}, nil)
|
}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -202,6 +204,7 @@ func TestReadTimeoutWithFirstByte(t *testing.T) {
|
||||||
Address: ":0",
|
Address: ":0",
|
||||||
Transport: epConfig,
|
Transport: epConfig,
|
||||||
ForwardedHeaders: &static.ForwardedHeaders{},
|
ForwardedHeaders: &static.ForwardedHeaders{},
|
||||||
|
HTTP2: &static.HTTP2Config{},
|
||||||
}, nil)
|
}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue