Allow handling ACME challenges with custom routers
This commit is contained in:
parent
d547b943df
commit
0cf2032c15
12 changed files with 142 additions and 24 deletions
|
@ -108,6 +108,9 @@ Entry points definition. (Default: ```false```)
|
||||||
`--entrypoints.<name>.address`:
|
`--entrypoints.<name>.address`:
|
||||||
Entry point address.
|
Entry point address.
|
||||||
|
|
||||||
|
`--entrypoints.<name>.allowacmebypass`:
|
||||||
|
Enables handling of ACME TLS and HTTP challenges with custom routers. (Default: ```false```)
|
||||||
|
|
||||||
`--entrypoints.<name>.forwardedheaders.insecure`:
|
`--entrypoints.<name>.forwardedheaders.insecure`:
|
||||||
Trust all forwarded headers. (Default: ```false```)
|
Trust all forwarded headers. (Default: ```false```)
|
||||||
|
|
||||||
|
|
|
@ -108,6 +108,9 @@ Entry points definition. (Default: ```false```)
|
||||||
`TRAEFIK_ENTRYPOINTS_<NAME>_ADDRESS`:
|
`TRAEFIK_ENTRYPOINTS_<NAME>_ADDRESS`:
|
||||||
Entry point address.
|
Entry point address.
|
||||||
|
|
||||||
|
`TRAEFIK_ENTRYPOINTS_<NAME>_ALLOWACMEBYPASS`:
|
||||||
|
Enables handling of ACME TLS and HTTP challenges with custom routers. (Default: ```false```)
|
||||||
|
|
||||||
`TRAEFIK_ENTRYPOINTS_<NAME>_FORWARDEDHEADERS_INSECURE`:
|
`TRAEFIK_ENTRYPOINTS_<NAME>_FORWARDEDHEADERS_INSECURE`:
|
||||||
Trust all forwarded headers. (Default: ```false```)
|
Trust all forwarded headers. (Default: ```false```)
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
[entryPoints]
|
[entryPoints]
|
||||||
[entryPoints.EntryPoint0]
|
[entryPoints.EntryPoint0]
|
||||||
address = "foobar"
|
address = "foobar"
|
||||||
|
allowACMEByPass = true
|
||||||
[entryPoints.EntryPoint0.transport]
|
[entryPoints.EntryPoint0.transport]
|
||||||
keepAliveMaxTime = "42s"
|
keepAliveMaxTime = "42s"
|
||||||
keepAliveMaxRequests = 42
|
keepAliveMaxRequests = 42
|
||||||
|
|
|
@ -16,6 +16,7 @@ serversTransport:
|
||||||
entryPoints:
|
entryPoints:
|
||||||
EntryPoint0:
|
EntryPoint0:
|
||||||
address: foobar
|
address: foobar
|
||||||
|
allowACMEByPass: true
|
||||||
transport:
|
transport:
|
||||||
lifeCycle:
|
lifeCycle:
|
||||||
requestAcceptGraceTimeout: 42s
|
requestAcceptGraceTimeout: 42s
|
||||||
|
|
|
@ -233,6 +233,35 @@ 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.
|
||||||
|
|
||||||
|
### AllowACMEByPass
|
||||||
|
|
||||||
|
_Optional, Default=false_
|
||||||
|
|
||||||
|
`allowACMEByPass` determines whether a user defined router can handle ACME TLS or HTTP challenges instead of the Traefik dedicated one.
|
||||||
|
This option can be used when a Traefik instance has one or more certificate resolvers configured,
|
||||||
|
but is also used to route challenges connections/requests to services that could also initiate their own ACME challenges.
|
||||||
|
|
||||||
|
??? info "No Certificate Resolvers configured"
|
||||||
|
|
||||||
|
It is not necessary to use the `allowACMEByPass' option certificate option if no certificate resolver is defined.
|
||||||
|
In fact, Traefik will automatically allow ACME TLS or HTTP requests to be handled by custom routers in this case, since there can be no concurrency with its own challenge handlers.
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
entryPoints:
|
||||||
|
foo:
|
||||||
|
allowACMEByPass: true
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
[entryPoints.foo]
|
||||||
|
[entryPoints.foo.allowACMEByPass]
|
||||||
|
allowACMEByPass = true
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab="CLI"
|
||||||
|
--entryPoints.name.allowACMEByPass=true
|
||||||
|
```
|
||||||
|
|
||||||
### HTTP/2
|
### HTTP/2
|
||||||
|
|
||||||
#### `maxConcurrentStreams`
|
#### `maxConcurrentStreams`
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
// EntryPoint holds the entry point configuration.
|
// EntryPoint holds the entry point configuration.
|
||||||
type EntryPoint struct {
|
type EntryPoint struct {
|
||||||
Address string `description:"Entry point address." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
|
Address string `description:"Entry point address." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
|
||||||
|
AllowACMEByPass bool `description:"Enables handling of ACME TLS and HTTP challenges with custom routers." json:"allowACMEByPass,omitempty" toml:"allowACMEByPass,omitempty" yaml:"allowACMEByPass,omitempty"`
|
||||||
Transport *EntryPointsTransport `description:"Configures communication between clients and Traefik." json:"transport,omitempty" toml:"transport,omitempty" yaml:"transport,omitempty" export:"true"`
|
Transport *EntryPointsTransport `description:"Configures communication between clients and Traefik." json:"transport,omitempty" toml:"transport,omitempty" yaml:"transport,omitempty" export:"true"`
|
||||||
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"`
|
||||||
|
|
|
@ -87,15 +87,27 @@ func (i *Provider) createConfiguration(ctx context.Context) *dynamic.Configurati
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Provider) acme(cfg *dynamic.Configuration) {
|
func (i *Provider) acme(cfg *dynamic.Configuration) {
|
||||||
var eps []string
|
allowACMEByPass := map[string]bool{}
|
||||||
|
for name, ep := range i.staticCfg.EntryPoints {
|
||||||
|
allowACMEByPass[name] = ep.AllowACMEByPass
|
||||||
|
}
|
||||||
|
|
||||||
|
var eps []string
|
||||||
|
var epsByPass []string
|
||||||
uniq := map[string]struct{}{}
|
uniq := map[string]struct{}{}
|
||||||
for _, resolver := range i.staticCfg.CertificatesResolvers {
|
for _, resolver := range i.staticCfg.CertificatesResolvers {
|
||||||
if resolver.ACME != nil && resolver.ACME.HTTPChallenge != nil && resolver.ACME.HTTPChallenge.EntryPoint != "" {
|
if resolver.ACME != nil && resolver.ACME.HTTPChallenge != nil && resolver.ACME.HTTPChallenge.EntryPoint != "" {
|
||||||
if _, ok := uniq[resolver.ACME.HTTPChallenge.EntryPoint]; !ok {
|
if _, ok := uniq[resolver.ACME.HTTPChallenge.EntryPoint]; ok {
|
||||||
eps = append(eps, resolver.ACME.HTTPChallenge.EntryPoint)
|
continue
|
||||||
uniq[resolver.ACME.HTTPChallenge.EntryPoint] = struct{}{}
|
|
||||||
}
|
}
|
||||||
|
uniq[resolver.ACME.HTTPChallenge.EntryPoint] = struct{}{}
|
||||||
|
|
||||||
|
if allowByPass, ok := allowACMEByPass[resolver.ACME.HTTPChallenge.EntryPoint]; ok && allowByPass {
|
||||||
|
epsByPass = append(epsByPass, resolver.ACME.HTTPChallenge.EntryPoint)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
eps = append(eps, resolver.ACME.HTTPChallenge.EntryPoint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,6 +122,17 @@ func (i *Provider) acme(cfg *dynamic.Configuration) {
|
||||||
cfg.HTTP.Routers["acme-http"] = rt
|
cfg.HTTP.Routers["acme-http"] = rt
|
||||||
cfg.HTTP.Services["acme-http"] = &dynamic.Service{}
|
cfg.HTTP.Services["acme-http"] = &dynamic.Service{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(epsByPass) > 0 {
|
||||||
|
rt := &dynamic.Router{
|
||||||
|
Rule: "PathPrefix(`/.well-known/acme-challenge/`)",
|
||||||
|
EntryPoints: epsByPass,
|
||||||
|
Service: "acme-http@internal",
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.HTTP.Routers["acme-http-bypass"] = rt
|
||||||
|
cfg.HTTP.Services["acme-http"] = &dynamic.Service{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Provider) redirection(ctx context.Context, cfg *dynamic.Configuration) {
|
func (i *Provider) redirection(ctx context.Context, cfg *dynamic.Configuration) {
|
||||||
|
|
|
@ -21,6 +21,8 @@ const defaultBufSize = 4096
|
||||||
|
|
||||||
// Router is a TCP router.
|
// Router is a TCP router.
|
||||||
type Router struct {
|
type Router struct {
|
||||||
|
acmeTLSPassthrough bool
|
||||||
|
|
||||||
// Contains TCP routes.
|
// Contains TCP routes.
|
||||||
muxerTCP tcpmuxer.Muxer
|
muxerTCP tcpmuxer.Muxer
|
||||||
// Contains TCP TLS routes.
|
// Contains TCP TLS routes.
|
||||||
|
@ -148,7 +150,7 @@ func (r *Router) ServeTCP(conn tcp.WriteCloser) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handling ACME-TLS/1 challenges.
|
// Handling ACME-TLS/1 challenges.
|
||||||
if slices.Contains(hello.protos, tlsalpn01.ACMETLS1Protocol) {
|
if !r.acmeTLSPassthrough && slices.Contains(hello.protos, tlsalpn01.ACMETLS1Protocol) {
|
||||||
r.acmeTLSALPNHandler().ServeTCP(r.GetConn(conn, hello.peeked))
|
r.acmeTLSALPNHandler().ServeTCP(r.GetConn(conn, hello.peeked))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -303,6 +305,10 @@ func (r *Router) SetHTTPSHandler(handler http.Handler, config *tls.Config) {
|
||||||
r.httpsTLSConfig = config
|
r.httpsTLSConfig = config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Router) EnableACMETLSPassthrough() {
|
||||||
|
r.acmeTLSPassthrough = true
|
||||||
|
}
|
||||||
|
|
||||||
// Conn is a connection proxy that handles Peeked bytes.
|
// Conn is a connection proxy that handles Peeked bytes.
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
// Peeked are the bytes that have been read from Conn for the purposes of route matching,
|
// Peeked are the bytes that have been read from Conn for the purposes of route matching,
|
||||||
|
|
|
@ -209,9 +209,10 @@ func Test_Routing(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
routers []applyRouter
|
routers []applyRouter
|
||||||
checks []checkCase
|
checks []checkCase
|
||||||
|
allowACMETLSPassthrough bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "No routers",
|
desc: "No routers",
|
||||||
|
@ -268,6 +269,18 @@ func Test_Routing(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "TCP TLS passthrough catches ACME TLS",
|
||||||
|
allowACMETLSPassthrough: true,
|
||||||
|
routers: []applyRouter{routerTCPTLSCatchAllPassthrough},
|
||||||
|
checks: []checkCase{
|
||||||
|
{
|
||||||
|
desc: "ACME TLS Challenge",
|
||||||
|
checkRouter: checkACMETLS,
|
||||||
|
expectedError: "tls: first record does not look like a TLS handshake",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
desc: "Single TCP CatchAll router",
|
desc: "Single TCP CatchAll router",
|
||||||
routers: []applyRouter{routerTCPCatchAll},
|
routers: []applyRouter{routerTCPCatchAll},
|
||||||
|
@ -578,6 +591,10 @@ func Test_Routing(t *testing.T) {
|
||||||
router, err := manager.buildEntryPointHandler(context.Background(), dynConf.TCPRouters, dynConf.Routers, nil, nil)
|
router, err := manager.buildEntryPointHandler(context.Background(), dynConf.TCPRouters, dynConf.Routers, nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
if test.allowACMETLSPassthrough {
|
||||||
|
router.EnableACMETLSPassthrough()
|
||||||
|
}
|
||||||
|
|
||||||
epListener, err := net.Listen("tcp", "127.0.0.1:0")
|
epListener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -699,7 +716,7 @@ func routerTCPTLSCatchAll(conf *runtime.Configuration) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// routerTCPTLSCatchAllPassthrough a TCP TLS CatchAll Passthrough - HostSNI(`*`) router with TLS 1.0 config.
|
// routerTCPTLSCatchAllPassthrough a TCP TLS CatchAll Passthrough - HostSNI(`*`) router with TLS 1.2 config.
|
||||||
func routerTCPTLSCatchAllPassthrough(conf *runtime.Configuration) {
|
func routerTCPTLSCatchAllPassthrough(conf *runtime.Configuration) {
|
||||||
conf.TCPRouters["tcp-tls-catchall-passthrough"] = &runtime.TCPRouterInfo{
|
conf.TCPRouters["tcp-tls-catchall-passthrough"] = &runtime.TCPRouterInfo{
|
||||||
TCPRouter: &dynamic.TCPRouter{
|
TCPRouter: &dynamic.TCPRouter{
|
||||||
|
|
|
@ -21,25 +21,37 @@ import (
|
||||||
|
|
||||||
// RouterFactory the factory of TCP/UDP routers.
|
// RouterFactory the factory of TCP/UDP routers.
|
||||||
type RouterFactory struct {
|
type RouterFactory struct {
|
||||||
entryPointsTCP []string
|
entryPointsTCP []string
|
||||||
entryPointsUDP []string
|
entryPointsUDP []string
|
||||||
|
allowACMEByPass map[string]bool
|
||||||
|
|
||||||
|
managerFactory *service.ManagerFactory
|
||||||
|
|
||||||
managerFactory *service.ManagerFactory
|
|
||||||
metricsRegistry metrics.Registry
|
metricsRegistry metrics.Registry
|
||||||
|
|
||||||
pluginBuilder middleware.PluginsBuilder
|
pluginBuilder middleware.PluginsBuilder
|
||||||
|
chainBuilder *middleware.ChainBuilder
|
||||||
chainBuilder *middleware.ChainBuilder
|
tlsManager *tls.Manager
|
||||||
tlsManager *tls.Manager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRouterFactory creates a new RouterFactory.
|
// NewRouterFactory creates a new RouterFactory.
|
||||||
func NewRouterFactory(staticConfiguration static.Configuration, managerFactory *service.ManagerFactory, tlsManager *tls.Manager,
|
func NewRouterFactory(staticConfiguration static.Configuration, managerFactory *service.ManagerFactory, tlsManager *tls.Manager,
|
||||||
chainBuilder *middleware.ChainBuilder, pluginBuilder middleware.PluginsBuilder, metricsRegistry metrics.Registry,
|
chainBuilder *middleware.ChainBuilder, pluginBuilder middleware.PluginsBuilder, metricsRegistry metrics.Registry,
|
||||||
) *RouterFactory {
|
) *RouterFactory {
|
||||||
|
handlesTLSChallenge := false
|
||||||
|
for _, resolver := range staticConfiguration.CertificatesResolvers {
|
||||||
|
if resolver.ACME.TLSChallenge != nil {
|
||||||
|
handlesTLSChallenge = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allowACMEByPass := map[string]bool{}
|
||||||
var entryPointsTCP, entryPointsUDP []string
|
var entryPointsTCP, entryPointsUDP []string
|
||||||
for name, cfg := range staticConfiguration.EntryPoints {
|
for name, ep := range staticConfiguration.EntryPoints {
|
||||||
protocol, err := cfg.GetProtocol()
|
allowACMEByPass[name] = ep.AllowACMEByPass || !handlesTLSChallenge
|
||||||
|
|
||||||
|
protocol, err := ep.GetProtocol()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Should never happen because Traefik should not start if protocol is invalid.
|
// Should never happen because Traefik should not start if protocol is invalid.
|
||||||
log.WithoutContext().Errorf("Invalid protocol: %v", err)
|
log.WithoutContext().Errorf("Invalid protocol: %v", err)
|
||||||
|
@ -60,6 +72,7 @@ func NewRouterFactory(staticConfiguration static.Configuration, managerFactory *
|
||||||
tlsManager: tlsManager,
|
tlsManager: tlsManager,
|
||||||
chainBuilder: chainBuilder,
|
chainBuilder: chainBuilder,
|
||||||
pluginBuilder: pluginBuilder,
|
pluginBuilder: pluginBuilder,
|
||||||
|
allowACMEByPass: allowACMEByPass,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +100,12 @@ func (f *RouterFactory) CreateRouters(rtConf *runtime.Configuration) (map[string
|
||||||
rtTCPManager := tcprouter.NewManager(rtConf, svcTCPManager, middlewaresTCPBuilder, handlersNonTLS, handlersTLS, f.tlsManager)
|
rtTCPManager := tcprouter.NewManager(rtConf, svcTCPManager, middlewaresTCPBuilder, handlersNonTLS, handlersTLS, f.tlsManager)
|
||||||
routersTCP := rtTCPManager.BuildHandlers(ctx, f.entryPointsTCP)
|
routersTCP := rtTCPManager.BuildHandlers(ctx, f.entryPointsTCP)
|
||||||
|
|
||||||
|
for ep, r := range routersTCP {
|
||||||
|
if allowACMEByPass, ok := f.allowACMEByPass[ep]; ok && allowACMEByPass {
|
||||||
|
r.EnableACMETLSPassthrough()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// UDP
|
// UDP
|
||||||
svcUDPManager := udp.NewManager(rtConf)
|
svcUDPManager := udp.NewManager(rtConf)
|
||||||
rtUDPManager := udprouter.NewManager(rtConf, svcUDPManager)
|
rtUDPManager := udprouter.NewManager(rtConf, svcUDPManager)
|
||||||
|
|
|
@ -172,7 +172,10 @@ func NewTCPEntryPoint(ctx context.Context, configuration *static.EntryPoint, hos
|
||||||
return nil, fmt.Errorf("error preparing server: %w", err)
|
return nil, fmt.Errorf("error preparing server: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
rt := &tcprouter.Router{}
|
rt, err := tcprouter.NewRouter()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error preparing tcp router: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
reqDecorator := requestdecorator.New(hostResolverConfig)
|
reqDecorator := requestdecorator.New(hostResolverConfig)
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestShutdownHijacked(t *testing.T) {
|
func TestShutdownHijacked(t *testing.T) {
|
||||||
router := &tcprouter.Router{}
|
router, err := tcprouter.NewRouter()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
router.SetHTTPHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
router.SetHTTPHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
conn, _, err := rw.(http.Hijacker).Hijack()
|
conn, _, err := rw.(http.Hijacker).Hijack()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -34,7 +36,9 @@ func TestShutdownHijacked(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShutdownHTTP(t *testing.T) {
|
func TestShutdownHTTP(t *testing.T) {
|
||||||
router := &tcprouter.Router{}
|
router, err := tcprouter.NewRouter()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
router.SetHTTPHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
router.SetHTTPHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
rw.WriteHeader(http.StatusOK)
|
rw.WriteHeader(http.StatusOK)
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
|
@ -167,7 +171,9 @@ func TestReadTimeoutWithoutFirstByte(t *testing.T) {
|
||||||
}, nil)
|
}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
router := &tcprouter.Router{}
|
router, err := tcprouter.NewRouter()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
router.SetHTTPHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
router.SetHTTPHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
rw.WriteHeader(http.StatusOK)
|
rw.WriteHeader(http.StatusOK)
|
||||||
}))
|
}))
|
||||||
|
@ -204,7 +210,9 @@ func TestReadTimeoutWithFirstByte(t *testing.T) {
|
||||||
}, nil)
|
}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
router := &tcprouter.Router{}
|
router, err := tcprouter.NewRouter()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
router.SetHTTPHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
router.SetHTTPHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
rw.WriteHeader(http.StatusOK)
|
rw.WriteHeader(http.StatusOK)
|
||||||
}))
|
}))
|
||||||
|
@ -244,7 +252,9 @@ func TestKeepAliveMaxRequests(t *testing.T) {
|
||||||
}, nil)
|
}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
router := &tcprouter.Router{}
|
router, err := tcprouter.NewRouter()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
router.SetHTTPHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
router.SetHTTPHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
rw.WriteHeader(http.StatusOK)
|
rw.WriteHeader(http.StatusOK)
|
||||||
}))
|
}))
|
||||||
|
@ -290,7 +300,9 @@ func TestKeepAliveMaxTime(t *testing.T) {
|
||||||
}, nil)
|
}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
router := &tcprouter.Router{}
|
router, err := tcprouter.NewRouter()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
router.SetHTTPHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
router.SetHTTPHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
rw.WriteHeader(http.StatusOK)
|
rw.WriteHeader(http.StatusOK)
|
||||||
}))
|
}))
|
||||||
|
|
Loading…
Reference in a new issue