Add configurable timeouts and curate default timeout settings

This commit is contained in:
Marco Jantke 2017-08-18 15:34:04 +02:00 committed by Traefiker
parent d84ccbc52a
commit 14a0d66410
10 changed files with 460 additions and 76 deletions

View file

@ -1,12 +1,9 @@
package main
import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
fmtlog "log"
"net/http"
"os"
"path/filepath"
"reflect"
@ -29,7 +26,6 @@ import (
"github.com/coreos/go-systemd/daemon"
"github.com/docker/libkv/store"
"github.com/satori/go.uuid"
"golang.org/x/net/http2"
)
func main() {
@ -178,28 +174,6 @@ func run(traefikConfiguration *server.TraefikConfiguration) {
// load global configuration
globalConfiguration := traefikConfiguration.GlobalConfiguration
http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = globalConfiguration.MaxIdleConnsPerHost
if globalConfiguration.InsecureSkipVerify {
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
if len(globalConfiguration.RootCAs) > 0 {
roots := x509.NewCertPool()
for _, cert := range globalConfiguration.RootCAs {
certContent, err := cert.Read()
if err != nil {
log.Error("Error while read RootCAs", err)
continue
}
roots.AppendCertsFromPEM(certContent)
}
tr := http.DefaultTransport.(*http.Transport)
tr.TLSClientConfig = &tls.Config{RootCAs: roots}
http2.ConfigureTransport(tr)
}
if globalConfiguration.File != nil && len(globalConfiguration.File.Filename) == 0 {
// no filename, setting to global config file
if len(traefikConfiguration.ConfigFile) != 0 {

View file

@ -68,7 +68,12 @@
#
# ProvidersThrottleDuration = "2s"
# IdleTimeout: maximum amount of time an idle (keep-alive) connection will remain idle before closing itself.
# IdleTimeout
#
# Deprecated - see [respondingTimeouts] section. In the case both settings are configured, the deprecated option will
# be overwritten.
#
# IdleTimeout is the maximum amount of time an idle (keep-alive) connection will remain idle before closing itself.
# This is set to enforce closing of stale client connections.
# Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw
# values (digits). If no units are provided, the value is parsed assuming seconds.
@ -330,6 +335,76 @@ To write JSON format logs, specify `json` as the format:
# interval = "30s"
```
## Responding timeouts
```
# respondingTimeouts are timeouts for incoming requests to the Traefik instance.
#
# Optional
#
[respondingTimeouts]
# readTimeout is the maximum duration for reading the entire request, including the body.
# If zero, no timeout exists.
# Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw
# values (digits). If no units are provided, the value is parsed assuming seconds.
#
# Optional
# Default: "0s"
#
# readTimeout = "5s"
# writeTimeout is the maximum duration before timing out writes of the response. It covers the time from the end of
# the request header read to the end of the response write.
# If zero, no timeout exists.
# Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw
# values (digits). If no units are provided, the value is parsed assuming seconds.
#
# Optional
# Default: "0s"
#
# writeTimeout = "5s"
# idleTimeout is the maximum duration an idle (keep-alive) connection will remain idle before closing itself.
# If zero, no timeout exists.
# Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw
# values (digits). If no units are provided, the value is parsed assuming seconds.
#
# Optional
# Default: "180s"
#
# idleTimeout = "360s"
```
## Forwarding timeouts
```
# forwardingTimeouts are timeouts for requests forwarded to the backend servers.
#
# Optional
#
[forwardingTimeouts]
# dialTimeout is the amount of time to wait until a connection to a backend server can be established.
# If zero, no timeout exists.
# Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw
# values (digits). If no units are provided, the value is parsed assuming seconds.
#
# Optional
# Default: "30s"
#
# dialTimeout = "30s"
# responseHeaderTimeout is the amount of time to wait for a server's response headers after fully writing the request (including its body, if any).
# If zero, no timeout exists.
# Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw
# values (digits). If no units are provided, the value is parsed assuming seconds.
#
# Optional
# Default: "0s"
#
# responseHeaderTimeout = "0s"
```
## ACME (Let's Encrypt) configuration
```toml

View file

@ -0,0 +1,38 @@
logLevel = "DEBUG"
defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.http]
address = ":8000"
[accessLog]
format = "json"
[web]
address = ":8080"
[forwardingTimeouts]
dialTimeout = "300ms"
responseHeaderTimeout = "300ms"
[file]
[backends]
[backends.backend1]
[backends.backend1.servers.server1]
# Non-routable IP address that should always deliver a dial timeout.
# See: https://stackoverflow.com/questions/100841/artificially-create-a-connection-timeout-error#answer-904609
url = "http://10.255.255.1"
[backends.backend2]
[backends.backend2.servers.server2]
url = "http://{{.TimeoutEndpoint}}:9000"
[frontends]
[frontends.frontend1]
backend = "backend1"
[frontends.frontend1.routes.test_1]
rule = "Path:/dialTimeout"
[frontends.frontend2]
backend = "backend2"
[frontends.frontend2.routes.test_2]
rule = "Path:/responseHeaderTimeout"

View file

@ -25,6 +25,7 @@ func init() {
check.Suite(&SimpleSuite{})
check.Suite(&AccessLogSuite{})
check.Suite(&HTTPSSuite{})
check.Suite(&TimeoutSuite{})
check.Suite(&FileSuite{})
check.Suite(&HealthCheckSuite{})
check.Suite(&DockerSuite{})

View file

@ -0,0 +1,7 @@
timeoutEndpoint:
image: yaman/timeout
environment:
- PROTO=http
- PORT=9000
ports:
- "9000:9000"

View file

@ -0,0 +1,44 @@
package integration
import (
"net/http"
"os"
"time"
"github.com/containous/traefik/integration/try"
"github.com/go-check/check"
checker "github.com/vdemeester/shakers"
)
type TimeoutSuite struct{ BaseSuite }
func (s *TimeoutSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "timeout")
s.composeProject.Start(c)
}
func (s *TimeoutSuite) TestForwardingTimeouts(c *check.C) {
httpTimeoutEndpoint := s.composeProject.Container(c, "timeoutEndpoint").NetworkSettings.IPAddress
file := s.adaptFile(c, "fixtures/timeout/forwarding_timeouts.toml", struct {
TimeoutEndpoint string
}{httpTimeoutEndpoint})
defer os.Remove(file)
cmd, _ := s.cmdTraefik(withConfigFile(file))
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 60*time.Second, try.BodyContains("Path:/dialTimeout"))
c.Assert(err, checker.IsNil)
// This simulates a DialTimeout when connecting to the backend server.
response, err := http.Get("http://127.0.0.1:8000/dialTimeout")
c.Assert(err, checker.IsNil)
c.Assert(response.StatusCode, checker.Equals, http.StatusGatewayTimeout)
// This simulates a ResponseHeaderTimeout.
response, err = http.Get("http://127.0.0.1:8000/responseHeaderTimeout?sleep=1000")
c.Assert(err, checker.IsNil)
c.Assert(response.StatusCode, checker.Equals, http.StatusGatewayTimeout)
}

View file

@ -28,8 +28,15 @@ import (
"github.com/containous/traefik/types"
)
// DefaultHealthCheckInterval is the default health check interval.
const DefaultHealthCheckInterval = 30 * time.Second
const (
// DefaultHealthCheckInterval is the default health check interval.
DefaultHealthCheckInterval = 30 * time.Second
// DefaultDialTimeout when connecting to a backend server.
DefaultDialTimeout = 30 * time.Second
// DefaultIdleTimeout before closing an idle connection.
DefaultIdleTimeout = 180 * time.Second
)
// TraefikConfiguration holds GlobalConfiguration and other stuff
type TraefikConfiguration struct {
@ -54,11 +61,13 @@ type GlobalConfiguration struct {
DefaultEntryPoints DefaultEntryPoints `description:"Entrypoints to be used by frontends that do not specify any entrypoint"`
ProvidersThrottleDuration flaeg.Duration `description:"Backends throttle duration: minimum duration between 2 events from providers before applying a new configuration. It avoids unnecessary reloads if multiples events are sent in a short amount of time."`
MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used"`
IdleTimeout flaeg.Duration `description:"maximum amount of time an idle (keep-alive) connection will remain idle before closing itself."`
IdleTimeout flaeg.Duration `description:"(Deprecated) maximum amount of time an idle (keep-alive) connection will remain idle before closing itself."` // Deprecated
InsecureSkipVerify bool `description:"Disable SSL certificate verification"`
RootCAs RootCAs `description:"Add cert file for self-signed certicate"`
Retry *Retry `description:"Enable retry sending request if network error"`
HealthCheck *HealthCheckConfig `description:"Health check parameters"`
RespondingTimeouts *RespondingTimeouts `description:"Timeouts for incoming requests to the Traefik instance"`
ForwardingTimeouts *ForwardingTimeouts `description:"Timeouts for requests forwarded to the backend servers"`
Docker *docker.Provider `description:"Enable Docker backend with default settings"`
File *file.Provider `description:"Enable File backend with default settings"`
Web *WebProvider `description:"Enable Web backend with default settings"`
@ -411,6 +420,19 @@ type HealthCheckConfig struct {
Interval flaeg.Duration `description:"Default periodicity of enabled health checks"`
}
// RespondingTimeouts contains timeout configurations for incoming requests to the Traefik instance.
type RespondingTimeouts struct {
ReadTimeout flaeg.Duration `description:"ReadTimeout is the maximum duration for reading the entire request, including the body. If zero, no timeout is set"`
WriteTimeout flaeg.Duration `description:"WriteTimeout is the maximum duration before timing out writes of the response. If zero, no timeout is set"`
IdleTimeout flaeg.Duration `description:"IdleTimeout is the maximum amount duration an idle (keep-alive) connection will remain idle before closing itself. Defaults to 180 seconds. If zero, no timeout is set"`
}
// ForwardingTimeouts contains timeout configurations for forwarding requests to the backend servers.
type ForwardingTimeouts struct {
DialTimeout flaeg.Duration `description:"The amount of time to wait until a connection to a backend server can be established. Defaults to 30 seconds. If zero, no timeout exists"`
ResponseHeaderTimeout flaeg.Duration `description:"The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists"`
}
// NewTraefikDefaultPointersConfiguration creates a TraefikConfiguration with pointers default values
func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
//default Docker
@ -573,10 +595,16 @@ func NewTraefikConfiguration() *TraefikConfiguration {
DefaultEntryPoints: []string{},
ProvidersThrottleDuration: flaeg.Duration(2 * time.Second),
MaxIdleConnsPerHost: 200,
IdleTimeout: flaeg.Duration(180 * time.Second),
IdleTimeout: flaeg.Duration(0),
HealthCheck: &HealthCheckConfig{
Interval: flaeg.Duration(DefaultHealthCheckInterval),
},
RespondingTimeouts: &RespondingTimeouts{
IdleTimeout: flaeg.Duration(DefaultIdleTimeout),
},
ForwardingTimeouts: &ForwardingTimeouts{
DialTimeout: flaeg.Duration(DefaultDialTimeout),
},
CheckNewVersion: true,
},
ConfigFile: "",

View file

@ -7,6 +7,7 @@ import (
"encoding/json"
"errors"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
@ -33,23 +34,25 @@ import (
"github.com/vulcand/oxy/forward"
"github.com/vulcand/oxy/roundrobin"
"github.com/vulcand/oxy/utils"
"golang.org/x/net/http2"
)
var oxyLogger = &OxyLogger{}
// Server is the reverse-proxy/load-balancer engine
type Server struct {
serverEntryPoints serverEntryPoints
configurationChan chan types.ConfigMessage
configurationValidatedChan chan types.ConfigMessage
signals chan os.Signal
stopChan chan bool
providers []provider.Provider
currentConfigurations safe.Safe
globalConfiguration GlobalConfiguration
accessLoggerMiddleware *accesslog.LogHandler
routinesPool *safe.Pool
leadership *cluster.Leadership
serverEntryPoints serverEntryPoints
configurationChan chan types.ConfigMessage
configurationValidatedChan chan types.ConfigMessage
signals chan os.Signal
stopChan chan bool
providers []provider.Provider
currentConfigurations safe.Safe
globalConfiguration GlobalConfiguration
accessLoggerMiddleware *accesslog.LogHandler
routinesPool *safe.Pool
leadership *cluster.Leadership
defaultForwardingRoundTripper http.RoundTripper
}
type serverEntryPoints map[string]*serverEntryPoint
@ -82,6 +85,8 @@ func NewServer(globalConfiguration GlobalConfiguration) *Server {
server.currentConfigurations.Set(currentConfigurations)
server.globalConfiguration = globalConfiguration
server.routinesPool = safe.NewPool(context.Background())
server.defaultForwardingRoundTripper = createHTTPTransport(globalConfiguration)
if globalConfiguration.Cluster != nil {
// leadership creation if cluster mode
server.leadership = cluster.NewLeadership(server.routinesPool.Ctx(), globalConfiguration.Cluster)
@ -101,6 +106,60 @@ func NewServer(globalConfiguration GlobalConfiguration) *Server {
return server
}
// createHTTPTransport creates an http.Transport configured with the GlobalConfiguration settings.
// For the settings that can't be configured in Traefik it uses the default http.Transport settings.
// An exception to this is the MaxIdleConns setting as we only provide the option MaxIdleConnsPerHost
// in Traefik at this point in time. Setting this value to the default of 100 could lead to confusing
// behaviour and backwards compatibility issues.
func createHTTPTransport(globalConfiguration GlobalConfiguration) *http.Transport {
dialer := &net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}
if globalConfiguration.ForwardingTimeouts != nil {
dialer.Timeout = time.Duration(globalConfiguration.ForwardingTimeouts.DialTimeout)
}
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: dialer.DialContext,
MaxIdleConnsPerHost: globalConfiguration.MaxIdleConnsPerHost,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
if globalConfiguration.ForwardingTimeouts != nil {
transport.ResponseHeaderTimeout = time.Duration(globalConfiguration.ForwardingTimeouts.ResponseHeaderTimeout)
}
if globalConfiguration.InsecureSkipVerify {
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
if len(globalConfiguration.RootCAs) > 0 {
transport.TLSClientConfig = &tls.Config{
RootCAs: createRootCACertPool(globalConfiguration.RootCAs),
}
http2.ConfigureTransport(transport)
}
return transport
}
func createRootCACertPool(rootCAs RootCAs) *x509.CertPool {
roots := x509.NewCertPool()
for _, cert := range rootCAs {
certContent, err := cert.Read()
if err != nil {
log.Error("Error while read RootCAs", err)
continue
}
roots.AppendCertsFromPEM(certContent)
}
return roots
}
// Start starts the server.
func (server *Server) Start() {
server.startHTTPServers()
@ -224,7 +283,7 @@ func (server *Server) setupServerEntryPoint(newServerEntryPointName string, newS
}
serverMiddlewares = append(serverMiddlewares, ipWhitelistMiddleware)
}
newsrv, err := server.prepareServer(newServerEntryPointName, newServerEntryPoint.httpRouter, server.globalConfiguration.EntryPoints[newServerEntryPointName], serverMiddlewares...)
newsrv, err := server.prepareServer(newServerEntryPointName, server.globalConfiguration.EntryPoints[newServerEntryPointName], newServerEntryPoint.httpRouter, serverMiddlewares...)
if err != nil {
log.Fatal("Error preparing server: ", err)
}
@ -551,14 +610,17 @@ func (server *Server) startServer(srv *http.Server, globalConfiguration GlobalCo
}
}
func (server *Server) prepareServer(entryPointName string, router *middlewares.HandlerSwitcher, entryPoint *EntryPoint, middlewares ...negroni.Handler) (*http.Server, error) {
log.Infof("Preparing server %s %+v", entryPointName, entryPoint)
func (server *Server) prepareServer(entryPointName string, entryPoint *EntryPoint, router *middlewares.HandlerSwitcher, middlewares ...negroni.Handler) (*http.Server, error) {
readTimeout, writeTimeout, idleTimeout := buildServerTimeouts(server.globalConfiguration)
log.Infof("Preparing server %s %+v with readTimeout=%s writeTimeout=%s idleTimeout=%s", entryPointName, entryPoint, readTimeout, writeTimeout, idleTimeout)
// middlewares
var negroni = negroni.New()
n := negroni.New()
for _, middleware := range middlewares {
negroni.Use(middleware)
n.Use(middleware)
}
negroni.UseHandler(router)
n.UseHandler(router)
tlsConfig, err := server.createTLSConfig(entryPointName, entryPoint.TLS, router)
if err != nil {
log.Errorf("Error creating TLS config: %s", err)
@ -566,13 +628,37 @@ func (server *Server) prepareServer(entryPointName string, router *middlewares.H
}
return &http.Server{
Addr: entryPoint.Address,
Handler: negroni,
TLSConfig: tlsConfig,
IdleTimeout: time.Duration(server.globalConfiguration.IdleTimeout),
Addr: entryPoint.Address,
Handler: n,
TLSConfig: tlsConfig,
ReadTimeout: readTimeout,
WriteTimeout: writeTimeout,
IdleTimeout: idleTimeout,
}, nil
}
func buildServerTimeouts(globalConfig GlobalConfiguration) (readTimeout, writeTimeout, idleTimeout time.Duration) {
readTimeout = time.Duration(0)
writeTimeout = time.Duration(0)
if globalConfig.RespondingTimeouts != nil {
readTimeout = time.Duration(globalConfig.RespondingTimeouts.ReadTimeout)
writeTimeout = time.Duration(globalConfig.RespondingTimeouts.WriteTimeout)
}
// When RespondingTimeouts.IdleTimout is configured, always use that setting
if globalConfig.RespondingTimeouts != nil {
idleTimeout = time.Duration(globalConfig.RespondingTimeouts.IdleTimeout)
} else if globalConfig.IdleTimeout != 0 {
// Backwards compatibility for deprecated IdleTimeout
idleTimeout = time.Duration(globalConfig.IdleTimeout)
} else {
// Default value if neither the deprecated IdleTimeout nor the new RespondingTimeouts.IdleTimout are configured
idleTimeout = time.Duration(DefaultIdleTimeout)
}
return readTimeout, writeTimeout, idleTimeout
}
func (server *Server) buildEntryPoints(globalConfiguration GlobalConfiguration) map[string]*serverEntryPoint {
serverEntryPoints := make(map[string]*serverEntryPoint)
for entryPointName := range globalConfiguration.EntryPoints {
@ -584,15 +670,22 @@ func (server *Server) buildEntryPoints(globalConfiguration GlobalConfiguration)
return serverEntryPoints
}
// clientTLSRoundTripper is used for forwarding client authentication to
// backend server
func clientTLSRoundTripper(config *tls.Config) http.RoundTripper {
if config == nil {
return http.DefaultTransport
}
return &http.Transport{
TLSClientConfig: config,
// getRoundTripper will either use server.defaultForwardingRoundTripper or create a new one
// given a custom TLS configuration is passed and the passTLSCert option is set to true.
func (server *Server) getRoundTripper(globalConfiguration GlobalConfiguration, passTLSCert bool, tls *TLS) (http.RoundTripper, error) {
if passTLSCert {
tlsConfig, err := createClientTLSConfig(tls)
if err != nil {
log.Errorf("Failed to create TLSClientConfig: %s", err)
return nil, err
}
transport := createHTTPTransport(globalConfiguration)
transport.TLSClientConfig = tlsConfig
return transport, nil
}
return server.defaultForwardingRoundTripper, nil
}
// LoadConfig returns a new gorilla.mux Route from the specified global configuration and the dynamic
@ -660,29 +753,20 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo
if backends[entryPointName+frontend.Backend] == nil {
log.Debugf("Creating backend %s", frontend.Backend)
var (
tlsConfig *tls.Config
err error
lb http.Handler
)
if frontend.PassTLSCert {
tlsConfig, err = createClientTLSConfig(entryPoint.TLS)
if err != nil {
log.Errorf("Failed to create TLS config for frontend %s: %v", frontendName, err)
continue frontend
}
roundTripper, err := server.getRoundTripper(globalConfiguration, frontend.PassTLSCert, entryPoint.TLS)
if err != nil {
log.Errorf("Failed to create RoundTripper for frontend %s: %v", frontendName, err)
log.Errorf("Skipping frontend %s...", frontendName)
continue frontend
}
// passing nil will use the roundtripper http.DefaultTransport
rt := clientTLSRoundTripper(tlsConfig)
fwd, err := forward.New(
forward.Logger(oxyLogger),
forward.PassHostHeader(frontend.PassHostHeader),
forward.RoundTripper(rt),
forward.RoundTripper(roundTripper),
forward.ErrorHandler(errorHandler),
)
if err != nil {
log.Errorf("Error creating forwarder for frontend %s: %v", frontendName, err)
log.Errorf("Skipping frontend %s...", frontendName)
@ -720,6 +804,7 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo
sticky = roundrobin.NewStickySession(cookiename)
}
var lb http.Handler
switch lbMethod {
case types.Drr:
log.Debugf("Creating load-balancer drr")

View file

@ -36,6 +36,86 @@ func (lb *testLoadBalancer) Servers() []*url.URL {
return []*url.URL{}
}
func TestPrepareServerTimeouts(t *testing.T) {
tests := []struct {
desc string
globalConfig GlobalConfiguration
wantIdleTimeout time.Duration
wantReadTimeout time.Duration
wantWriteTimeout time.Duration
}{
{
desc: "full configuration",
globalConfig: GlobalConfiguration{
RespondingTimeouts: &RespondingTimeouts{
IdleTimeout: flaeg.Duration(10 * time.Second),
ReadTimeout: flaeg.Duration(12 * time.Second),
WriteTimeout: flaeg.Duration(14 * time.Second),
},
},
wantIdleTimeout: time.Duration(10 * time.Second),
wantReadTimeout: time.Duration(12 * time.Second),
wantWriteTimeout: time.Duration(14 * time.Second),
},
{
desc: "using defaults",
globalConfig: GlobalConfiguration{},
wantIdleTimeout: time.Duration(180 * time.Second),
wantReadTimeout: time.Duration(0 * time.Second),
wantWriteTimeout: time.Duration(0 * time.Second),
},
{
desc: "deprecated IdleTimeout configured",
globalConfig: GlobalConfiguration{
IdleTimeout: flaeg.Duration(45 * time.Second),
},
wantIdleTimeout: time.Duration(45 * time.Second),
wantReadTimeout: time.Duration(0 * time.Second),
wantWriteTimeout: time.Duration(0 * time.Second),
},
{
desc: "deprecated and new IdleTimeout configured",
globalConfig: GlobalConfiguration{
IdleTimeout: flaeg.Duration(45 * time.Second),
RespondingTimeouts: &RespondingTimeouts{
IdleTimeout: flaeg.Duration(80 * time.Second),
},
},
wantIdleTimeout: time.Duration(80 * time.Second),
wantReadTimeout: time.Duration(0 * time.Second),
wantWriteTimeout: time.Duration(0 * time.Second),
},
}
for _, test := range tests {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
entryPointName := "http"
entryPoint := &EntryPoint{Address: "localhost:8080"}
router := middlewares.NewHandlerSwitcher(mux.NewRouter())
srv := NewServer(test.globalConfig)
httpServer, err := srv.prepareServer(entryPointName, entryPoint, router)
if err != nil {
t.Fatalf("Unexpected error when preparing srv: %s", err)
}
if httpServer.IdleTimeout != test.wantIdleTimeout {
t.Errorf("Got %s as IdleTimeout, want %s", httpServer.IdleTimeout, test.wantIdleTimeout)
}
if httpServer.ReadTimeout != test.wantReadTimeout {
t.Errorf("Got %s as ReadTimeout, want %s", httpServer.ReadTimeout, test.wantReadTimeout)
}
if httpServer.WriteTimeout != test.wantWriteTimeout {
t.Errorf("Got %s as WriteTimeout, want %s", httpServer.WriteTimeout, test.wantWriteTimeout)
}
})
}
}
func TestServerMultipleFrontendRules(t *testing.T) {
cases := []struct {
expression string

View file

@ -366,6 +366,58 @@
#
# interval = "30s"
# Timeout settings for the http servers Traefik starts
#
# Optional
#
# [respondingTimeouts]
# ReadTimeout is the maximum duration for reading the entire request, including the body.
# If zero, no timeout exists.
#
# Optional
# Default: "0s"
#
# readTimeout = "5s"
# WriteTimeout is the maximum duration before timing out writes of the response.
# If zero, no timeout exists.
#
# Optional
# Default: "0s"
#
# writeTimeout = "5s"
# IdleTimeout is the maximum amount duration an idle (keep-alive) connection will remain idle before closing itself.
# Defaults to 180 seconds.
# If zero, no timeout exists.
#
# Optional
# Default: "180s"
#
# idleTimeout = "360s"
# Timeout settings for requests forwarded to the Backend Servers
#
# Optional
#
# [forwardingTimeouts]
# The amount of time to wait until a connection to a Backend Server can be established.
# If zero, no timeout exists.
#
# Optional
# Default: "30s"
#
# dialTimeout = "30s"
# The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists
#
# Optional
# Default: "0s"
#
# responseHeaderTimeout = "0s"
################################################################
# Web configuration backend
################################################################