2017-04-17 20:47:53 +00:00
package server
2016-01-13 21:45:49 +00:00
import (
2016-08-16 15:26:10 +00:00
"context"
2016-01-13 21:45:49 +00:00
"crypto/tls"
2016-06-15 20:38:40 +00:00
"crypto/x509"
2016-01-29 19:34:17 +00:00
"encoding/json"
2016-01-13 21:45:49 +00:00
"errors"
2017-10-10 12:50:03 +00:00
"fmt"
2016-06-15 20:38:40 +00:00
"io/ioutil"
2017-11-18 00:10:03 +00:00
stdlog "log"
2017-08-18 13:34:04 +00:00
"net"
2016-02-26 14:29:53 +00:00
"net/http"
"net/url"
"os"
"os/signal"
"reflect"
"sort"
2017-10-30 11:54:03 +00:00
"strings"
2017-04-04 09:36:23 +00:00
"sync"
2016-02-26 14:29:53 +00:00
"time"
2016-02-19 22:55:23 +00:00
2017-08-25 19:32:03 +00:00
"github.com/armon/go-proxyproto"
2016-06-03 15:58:33 +00:00
"github.com/containous/mux"
2016-08-18 11:03:10 +00:00
"github.com/containous/traefik/cluster"
2017-08-25 14:10:03 +00:00
"github.com/containous/traefik/configuration"
2016-11-26 18:48:49 +00:00
"github.com/containous/traefik/healthcheck"
2016-08-18 12:20:11 +00:00
"github.com/containous/traefik/log"
2017-08-23 18:46:03 +00:00
"github.com/containous/traefik/metrics"
2016-02-24 15:43:39 +00:00
"github.com/containous/traefik/middlewares"
2017-05-09 12:02:44 +00:00
"github.com/containous/traefik/middlewares/accesslog"
2017-09-18 15:48:07 +00:00
mauth "github.com/containous/traefik/middlewares/auth"
2018-04-11 11:54:03 +00:00
"github.com/containous/traefik/middlewares/errorpages"
2018-01-31 18:10:04 +00:00
"github.com/containous/traefik/middlewares/redirect"
2018-01-10 16:48:04 +00:00
"github.com/containous/traefik/middlewares/tracing"
2016-02-24 15:43:39 +00:00
"github.com/containous/traefik/provider"
2018-03-05 19:54:04 +00:00
"github.com/containous/traefik/provider/acme"
2018-02-26 14:34:04 +00:00
"github.com/containous/traefik/rules"
2016-03-31 16:57:08 +00:00
"github.com/containous/traefik/safe"
2017-10-10 09:10:02 +00:00
"github.com/containous/traefik/server/cookie"
2018-03-06 09:12:04 +00:00
traefiktls "github.com/containous/traefik/tls"
2016-02-24 15:43:39 +00:00
"github.com/containous/traefik/types"
2017-10-10 12:50:03 +00:00
"github.com/containous/traefik/whitelist"
2017-11-17 09:26:03 +00:00
"github.com/eapache/channels"
2018-01-22 11:16:03 +00:00
"github.com/sirupsen/logrus"
2017-08-25 14:10:03 +00:00
thoas_stats "github.com/thoas/stats"
2018-03-01 15:42:04 +00:00
"github.com/unrolled/secure"
2017-07-19 10:02:51 +00:00
"github.com/urfave/negroni"
2018-01-31 14:32:04 +00:00
"github.com/vulcand/oxy/buffer"
2016-06-15 17:07:33 +00:00
"github.com/vulcand/oxy/connlimit"
"github.com/vulcand/oxy/forward"
2017-09-09 11:36:03 +00:00
"github.com/vulcand/oxy/ratelimit"
2016-06-15 17:07:33 +00:00
"github.com/vulcand/oxy/roundrobin"
"github.com/vulcand/oxy/utils"
2017-08-18 13:34:04 +00:00
"golang.org/x/net/http2"
2016-01-13 21:45:49 +00:00
)
2018-04-11 14:30:04 +00:00
var httpServerLogger = stdlog . New ( log . WriterLevel ( logrus . DebugLevel ) , "" , 0 )
2016-01-13 21:46:44 +00:00
2016-01-13 21:45:49 +00:00
// Server is the reverse-proxy/load-balancer engine
type Server struct {
2017-08-18 13:34:04 +00:00
serverEntryPoints serverEntryPoints
configurationChan chan types . ConfigMessage
configurationValidatedChan chan types . ConfigMessage
signals chan os . Signal
stopChan chan bool
currentConfigurations safe . Safe
2018-01-23 11:44:03 +00:00
providerConfigUpdateMap map [ string ] chan types . ConfigMessage
2017-08-25 14:10:03 +00:00
globalConfiguration configuration . GlobalConfiguration
2017-08-18 13:34:04 +00:00
accessLoggerMiddleware * accesslog . LogHandler
2018-01-10 16:48:04 +00:00
tracingMiddleware * tracing . Tracing
2017-08-18 13:34:04 +00:00
routinesPool * safe . Pool
leadership * cluster . Leadership
defaultForwardingRoundTripper http . RoundTripper
2017-08-23 18:46:03 +00:00
metricsRegistry metrics . Registry
2018-01-29 13:58:03 +00:00
provider provider . Provider
2018-03-05 19:54:04 +00:00
configurationListeners [ ] func ( types . Configuration )
2016-01-13 21:45:49 +00:00
}
2016-02-25 17:30:13 +00:00
type serverEntryPoints map [ string ] * serverEntryPoint
2016-01-29 19:34:17 +00:00
type serverEntryPoint struct {
2018-03-05 19:54:04 +00:00
httpServer * http . Server
listener net . Listener
httpRouter * middlewares . HandlerSwitcher
certs safe . Safe
onDemandListener func ( string ) ( * tls . Certificate , error )
2016-01-29 19:34:17 +00:00
}
2016-01-13 21:45:49 +00:00
// NewServer returns an initialized Server.
2018-01-29 13:58:03 +00:00
func NewServer ( globalConfiguration configuration . GlobalConfiguration , provider provider . Provider ) * Server {
2016-01-13 21:45:49 +00:00
server := new ( Server )
2018-01-29 13:58:03 +00:00
server . provider = provider
2016-02-25 17:30:13 +00:00
server . serverEntryPoints = make ( map [ string ] * serverEntryPoint )
2016-05-19 18:09:01 +00:00
server . configurationChan = make ( chan types . ConfigMessage , 100 )
server . configurationValidatedChan = make ( chan types . ConfigMessage , 100 )
2016-01-29 19:34:17 +00:00
server . signals = make ( chan os . Signal , 1 )
2016-04-13 18:36:23 +00:00
server . stopChan = make ( chan bool , 1 )
2017-08-11 10:04:58 +00:00
server . configureSignals ( )
2017-08-25 14:10:03 +00:00
currentConfigurations := make ( types . Configurations )
2016-04-13 18:36:23 +00:00
server . currentConfigurations . Set ( currentConfigurations )
2018-01-23 11:44:03 +00:00
server . providerConfigUpdateMap = make ( map [ string ] chan types . ConfigMessage )
2016-01-13 21:45:49 +00:00
server . globalConfiguration = globalConfiguration
2017-11-09 15:12:04 +00:00
if server . globalConfiguration . API != nil {
server . globalConfiguration . API . CurrentConfigurations = & server . currentConfigurations
}
2016-08-18 11:03:10 +00:00
server . routinesPool = safe . NewPool ( context . Background ( ) )
2017-08-18 13:34:04 +00:00
server . defaultForwardingRoundTripper = createHTTPTransport ( globalConfiguration )
2018-01-10 16:48:04 +00:00
server . tracingMiddleware = globalConfiguration . Tracing
if globalConfiguration . Tracing != nil && globalConfiguration . Tracing . Backend != "" {
server . tracingMiddleware . Setup ( )
}
2018-01-26 10:58:03 +00:00
server . metricsRegistry = registerMetricClients ( globalConfiguration . Metrics )
2017-08-23 18:46:03 +00:00
2016-08-18 11:03:10 +00:00
if globalConfiguration . Cluster != nil {
// leadership creation if cluster mode
server . leadership = cluster . NewLeadership ( server . routinesPool . Ctx ( ) , globalConfiguration . Cluster )
}
2016-01-13 21:45:49 +00:00
2017-05-25 11:25:53 +00:00
if globalConfiguration . AccessLogsFile != "" {
globalConfiguration . AccessLog = & types . AccessLog { FilePath : globalConfiguration . AccessLogsFile , Format : accesslog . CommonFormat }
}
if globalConfiguration . AccessLog != nil {
var err error
server . accessLoggerMiddleware , err = accesslog . NewLogHandler ( globalConfiguration . AccessLog )
if err != nil {
log . Warnf ( "Unable to create log handler: %s" , err )
}
2017-05-22 19:39:29 +00:00
}
2016-01-13 21:45:49 +00:00
return server
}
2017-08-18 13:34:04 +00:00
// 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.
2017-08-25 14:10:03 +00:00
func createHTTPTransport ( globalConfiguration configuration . GlobalConfiguration ) * http . Transport {
2017-08-18 13:34:04 +00:00
dialer := & net . Dialer {
2017-09-20 16:14:03 +00:00
Timeout : configuration . DefaultDialTimeout ,
2017-08-18 13:34:04 +00:00
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 ) ,
}
}
2017-10-10 10:14:03 +00:00
http2 . ConfigureTransport ( transport )
2017-08-18 13:34:04 +00:00
return transport
}
2018-03-06 09:12:04 +00:00
func createRootCACertPool ( rootCAs traefiktls . RootCAs ) * x509 . CertPool {
2017-08-18 13:34:04 +00:00
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
}
2016-10-25 15:59:39 +00:00
// Start starts the server.
2017-11-24 18:18:03 +00:00
func ( s * Server ) Start ( ) {
s . startHTTPServers ( )
s . startLeadership ( )
s . routinesPool . Go ( func ( stop chan bool ) {
s . listenProviders ( stop )
2016-03-31 16:57:08 +00:00
} )
2017-11-24 18:18:03 +00:00
s . routinesPool . Go ( func ( stop chan bool ) {
s . listenConfigurations ( stop )
2016-03-31 16:57:08 +00:00
} )
2018-01-29 13:58:03 +00:00
s . startProvider ( )
2017-11-24 18:18:03 +00:00
go s . listenSignals ( )
2016-10-25 15:59:39 +00:00
}
2018-03-14 12:14:03 +00:00
// StartWithContext starts the server and Stop/Close it when context is Done
func ( s * Server ) StartWithContext ( ctx context . Context ) {
go func ( ) {
defer s . Close ( )
<- ctx . Done ( )
log . Info ( "I have to go..." )
reqAcceptGraceTimeOut := time . Duration ( s . globalConfiguration . LifeCycle . RequestAcceptGraceTimeout )
2018-03-22 17:18:03 +00:00
if s . globalConfiguration . Ping != nil && reqAcceptGraceTimeOut > 0 {
s . globalConfiguration . Ping . SetTerminating ( )
}
2018-03-14 12:14:03 +00:00
if reqAcceptGraceTimeOut > 0 {
log . Infof ( "Waiting %s for incoming requests to cease" , reqAcceptGraceTimeOut )
time . Sleep ( reqAcceptGraceTimeOut )
}
log . Info ( "Stopping server gracefully" )
s . Stop ( )
} ( )
s . Start ( )
}
2016-10-25 15:59:39 +00:00
// Wait blocks until server is shutted down.
2017-11-24 18:18:03 +00:00
func ( s * Server ) Wait ( ) {
<- s . stopChan
2016-01-13 21:45:49 +00:00
}
// Stop stops the server
2017-11-24 18:18:03 +00:00
func ( s * Server ) Stop ( ) {
2017-03-09 22:27:09 +00:00
defer log . Info ( "Server stopped" )
var wg sync . WaitGroup
2017-11-24 18:18:03 +00:00
for sepn , sep := range s . serverEntryPoints {
2017-03-09 22:27:09 +00:00
wg . Add ( 1 )
go func ( serverEntryPointName string , serverEntryPoint * serverEntryPoint ) {
defer wg . Done ( )
2017-11-24 18:18:03 +00:00
graceTimeOut := time . Duration ( s . globalConfiguration . LifeCycle . GraceTimeOut )
2017-03-27 09:51:53 +00:00
ctx , cancel := context . WithTimeout ( context . Background ( ) , graceTimeOut )
log . Debugf ( "Waiting %s seconds before killing connections on entrypoint %s..." , graceTimeOut , serverEntryPointName )
2017-03-09 22:27:09 +00:00
if err := serverEntryPoint . httpServer . Shutdown ( ctx ) ; err != nil {
log . Debugf ( "Wait is over due to: %s" , err )
serverEntryPoint . httpServer . Close ( )
}
2016-07-13 15:50:57 +00:00
cancel ( )
2017-03-09 22:27:09 +00:00
log . Debugf ( "Entrypoint %s closed" , serverEntryPointName )
} ( sepn , sep )
2016-01-29 19:34:17 +00:00
}
2017-03-09 22:27:09 +00:00
wg . Wait ( )
2017-11-24 18:18:03 +00:00
s . stopChan <- true
2016-01-13 21:45:49 +00:00
}
// Close destroys the server
2017-11-24 18:18:03 +00:00
func ( s * Server ) Close ( ) {
2018-03-14 12:14:03 +00:00
ctx , cancel := context . WithTimeout ( context . Background ( ) , 10 * time . Second )
2016-07-13 15:50:57 +00:00
go func ( ctx context . Context ) {
<- ctx . Done ( )
if ctx . Err ( ) == context . Canceled {
return
} else if ctx . Err ( ) == context . DeadlineExceeded {
2018-03-14 12:14:03 +00:00
panic ( "Timeout while stopping traefik, killing instance ✝" )
2016-07-13 15:50:57 +00:00
}
} ( ctx )
2017-08-23 18:46:03 +00:00
stopMetricsClients ( )
2017-11-24 18:18:03 +00:00
s . stopLeadership ( )
s . routinesPool . Cleanup ( )
close ( s . configurationChan )
close ( s . configurationValidatedChan )
signal . Stop ( s . signals )
close ( s . signals )
close ( s . stopChan )
if s . accessLoggerMiddleware != nil {
if err := s . accessLoggerMiddleware . Close ( ) ; err != nil {
2017-05-22 19:39:29 +00:00
log . Errorf ( "Error closing access log file: %s" , err )
}
}
2016-07-13 15:50:57 +00:00
cancel ( )
2016-01-13 21:45:49 +00:00
}
2017-11-24 18:18:03 +00:00
func ( s * Server ) startLeadership ( ) {
if s . leadership != nil {
s . leadership . Participate ( s . routinesPool )
2016-08-18 11:03:10 +00:00
}
}
2017-11-24 18:18:03 +00:00
func ( s * Server ) stopLeadership ( ) {
if s . leadership != nil {
s . leadership . Stop ( )
2016-08-18 11:03:10 +00:00
}
}
2017-11-24 18:18:03 +00:00
func ( s * Server ) startHTTPServers ( ) {
s . serverEntryPoints = s . buildEntryPoints ( s . globalConfiguration )
2017-01-12 13:34:54 +00:00
2017-11-24 18:18:03 +00:00
for newServerEntryPointName , newServerEntryPoint := range s . serverEntryPoints {
serverEntryPoint := s . setupServerEntryPoint ( newServerEntryPointName , newServerEntryPoint )
go s . startServer ( serverEntryPoint , s . globalConfiguration )
2017-07-08 10:21:14 +00:00
}
}
2017-11-24 18:18:03 +00:00
func ( s * Server ) setupServerEntryPoint ( newServerEntryPointName string , newServerEntryPoint * serverEntryPoint ) * serverEntryPoint {
2017-08-25 14:10:03 +00:00
serverMiddlewares := [ ] negroni . Handler { middlewares . NegroniRecoverHandler ( ) }
2017-11-09 15:12:04 +00:00
serverInternalMiddlewares := [ ] negroni . Handler { middlewares . NegroniRecoverHandler ( ) }
2018-01-10 16:48:04 +00:00
if s . tracingMiddleware . IsEnabled ( ) {
serverMiddlewares = append ( serverMiddlewares , s . tracingMiddleware . NewEntryPoint ( newServerEntryPointName ) )
}
2017-11-24 18:18:03 +00:00
if s . accessLoggerMiddleware != nil {
serverMiddlewares = append ( serverMiddlewares , s . accessLoggerMiddleware )
}
if s . metricsRegistry . IsEnabled ( ) {
2018-01-26 10:58:03 +00:00
serverMiddlewares = append ( serverMiddlewares , middlewares . NewEntryPointMetricsMiddleware ( s . metricsRegistry , newServerEntryPointName ) )
2017-11-24 18:18:03 +00:00
}
if s . globalConfiguration . API != nil {
2017-11-30 11:18:03 +00:00
if s . globalConfiguration . API . Stats == nil {
s . globalConfiguration . API . Stats = thoas_stats . New ( )
}
2017-11-24 18:18:03 +00:00
serverMiddlewares = append ( serverMiddlewares , s . globalConfiguration . API . Stats )
if s . globalConfiguration . API . Statistics != nil {
2017-11-30 11:18:03 +00:00
if s . globalConfiguration . API . StatsRecorder == nil {
s . globalConfiguration . API . StatsRecorder = middlewares . NewStatsRecorder ( s . globalConfiguration . API . Statistics . RecentErrors )
}
2017-11-24 18:18:03 +00:00
serverMiddlewares = append ( serverMiddlewares , s . globalConfiguration . API . StatsRecorder )
2017-08-25 14:10:03 +00:00
}
2017-07-08 10:21:14 +00:00
}
2018-03-23 16:40:04 +00:00
2017-11-24 18:18:03 +00:00
if s . globalConfiguration . EntryPoints [ newServerEntryPointName ] . Auth != nil {
2018-01-10 16:48:04 +00:00
authMiddleware , err := mauth . NewAuthenticator ( s . globalConfiguration . EntryPoints [ newServerEntryPointName ] . Auth , s . tracingMiddleware )
2017-07-08 10:21:14 +00:00
if err != nil {
log . Fatal ( "Error starting server: " , err )
2016-09-28 21:07:06 +00:00
}
2018-01-11 09:04:03 +00:00
serverMiddlewares = append ( serverMiddlewares , s . wrapNegroniHandlerWithAccessLog ( authMiddleware , fmt . Sprintf ( "Auth for entrypoint %s" , newServerEntryPointName ) ) )
2017-11-09 15:12:04 +00:00
serverInternalMiddlewares = append ( serverInternalMiddlewares , authMiddleware )
2017-07-08 10:21:14 +00:00
}
2018-03-23 16:40:04 +00:00
2017-11-24 18:18:03 +00:00
if s . globalConfiguration . EntryPoints [ newServerEntryPointName ] . Compress {
2017-07-08 10:21:14 +00:00
serverMiddlewares = append ( serverMiddlewares , & middlewares . Compress { } )
}
2018-03-23 16:40:04 +00:00
ipWhitelistMiddleware , err := buildIPWhiteLister (
s . globalConfiguration . EntryPoints [ newServerEntryPointName ] . WhiteList ,
s . globalConfiguration . EntryPoints [ newServerEntryPointName ] . WhitelistSourceRange )
if err != nil {
log . Fatal ( "Error starting server: " , err )
}
if ipWhitelistMiddleware != nil {
2018-01-11 09:04:03 +00:00
serverMiddlewares = append ( serverMiddlewares , s . wrapNegroniHandlerWithAccessLog ( ipWhitelistMiddleware , fmt . Sprintf ( "ipwhitelister for entrypoint %s" , newServerEntryPointName ) ) )
2017-11-09 15:12:04 +00:00
serverInternalMiddlewares = append ( serverInternalMiddlewares , ipWhitelistMiddleware )
2016-02-25 17:30:13 +00:00
}
2018-03-23 16:40:04 +00:00
2017-11-24 18:18:03 +00:00
newSrv , listener , err := s . prepareServer ( newServerEntryPointName , s . globalConfiguration . EntryPoints [ newServerEntryPointName ] , newServerEntryPoint . httpRouter , serverMiddlewares , serverInternalMiddlewares )
2017-07-08 10:21:14 +00:00
if err != nil {
log . Fatal ( "Error preparing server: " , err )
}
2017-11-24 18:18:03 +00:00
serverEntryPoint := s . serverEntryPoints [ newServerEntryPointName ]
2017-08-25 14:10:03 +00:00
serverEntryPoint . httpServer = newSrv
2017-08-25 19:32:03 +00:00
serverEntryPoint . listener = listener
2017-07-08 10:21:14 +00:00
return serverEntryPoint
2016-02-25 17:30:13 +00:00
}
2017-11-24 18:18:03 +00:00
func ( s * Server ) listenProviders ( stop chan bool ) {
2016-01-13 21:45:49 +00:00
for {
2016-04-13 18:36:23 +00:00
select {
case <- stop :
return
2017-11-24 18:18:03 +00:00
case configMsg , ok := <- s . configurationChan :
2017-11-09 11:16:03 +00:00
if ! ok || configMsg . Configuration == nil {
2016-04-13 18:36:23 +00:00
return
}
2017-11-24 18:18:03 +00:00
s . preLoadConfiguration ( configMsg )
2017-11-09 11:16:03 +00:00
}
}
}
2017-11-24 18:18:03 +00:00
func ( s * Server ) preLoadConfiguration ( configMsg types . ConfigMessage ) {
providersThrottleDuration := time . Duration ( s . globalConfiguration . ProvidersThrottleDuration )
s . defaultConfigurationValues ( configMsg . Configuration )
currentConfigurations := s . currentConfigurations . Get ( ) . ( types . Configurations )
2017-11-09 11:16:03 +00:00
jsonConf , _ := json . Marshal ( configMsg . Configuration )
log . Debugf ( "Configuration received from provider %s: %s" , configMsg . ProviderName , string ( jsonConf ) )
2018-01-23 15:30:07 +00:00
if configMsg . Configuration == nil || configMsg . Configuration . Backends == nil && configMsg . Configuration . Frontends == nil && configMsg . Configuration . TLS == nil {
2017-11-09 11:16:03 +00:00
log . Infof ( "Skipping empty Configuration for provider %s" , configMsg . ProviderName )
} else if reflect . DeepEqual ( currentConfigurations [ configMsg . ProviderName ] , configMsg . Configuration ) {
log . Infof ( "Skipping same configuration for provider %s" , configMsg . ProviderName )
} else {
2018-01-23 11:44:03 +00:00
providerConfigUpdateCh , ok := s . providerConfigUpdateMap [ configMsg . ProviderName ]
if ! ok {
providerConfigUpdateCh = make ( chan types . ConfigMessage )
s . providerConfigUpdateMap [ configMsg . ProviderName ] = providerConfigUpdateCh
2017-11-24 18:18:03 +00:00
s . routinesPool . Go ( func ( stop chan bool ) {
2018-03-15 21:22:03 +00:00
s . throttleProviderConfigReload ( providersThrottleDuration , s . configurationValidatedChan , providerConfigUpdateCh , stop )
2017-11-09 11:16:03 +00:00
} )
2016-06-22 16:31:14 +00:00
}
2018-01-23 11:44:03 +00:00
providerConfigUpdateCh <- configMsg
2017-11-17 09:26:03 +00:00
}
}
// throttleProviderConfigReload throttles the configuration reload speed for a single provider.
// It will immediately publish a new configuration and then only publish the next configuration after the throttle duration.
// Note that in the case it receives N new configs in the timeframe of the throttle duration after publishing,
// it will publish the last of the newly received configurations.
2018-03-15 21:22:03 +00:00
func ( s * Server ) throttleProviderConfigReload ( throttle time . Duration , publish chan <- types . ConfigMessage , in <- chan types . ConfigMessage , stop chan bool ) {
2017-11-17 09:26:03 +00:00
ring := channels . NewRingChannel ( 1 )
2017-12-06 13:54:03 +00:00
defer ring . Close ( )
2017-11-17 09:26:03 +00:00
2018-03-15 21:22:03 +00:00
s . routinesPool . Go ( func ( stop chan bool ) {
2017-11-17 09:26:03 +00:00
for {
select {
case <- stop :
return
case nextConfig := <- ring . Out ( ) :
publish <- nextConfig . ( types . ConfigMessage )
time . Sleep ( throttle )
}
}
} )
for {
select {
case <- stop :
return
case nextConfig := <- in :
ring . In ( ) <- nextConfig
}
2016-06-22 16:31:14 +00:00
}
}
2017-11-24 18:18:03 +00:00
func ( s * Server ) defaultConfigurationValues ( configuration * types . Configuration ) {
2016-06-22 16:31:14 +00:00
if configuration == nil || configuration . Frontends == nil {
return
}
2018-01-11 09:04:03 +00:00
configureFrontends ( configuration . Frontends , s . globalConfiguration . DefaultEntryPoints )
configureBackends ( configuration . Backends )
2016-01-13 21:45:49 +00:00
}
2017-11-24 18:18:03 +00:00
func ( s * Server ) listenConfigurations ( stop chan bool ) {
2016-01-13 21:45:49 +00:00
for {
2016-04-13 18:36:23 +00:00
select {
case <- stop :
return
2017-11-24 18:18:03 +00:00
case configMsg , ok := <- s . configurationValidatedChan :
2017-11-09 11:16:03 +00:00
if ! ok || configMsg . Configuration == nil {
2016-04-13 18:36:23 +00:00
return
2016-01-13 21:45:49 +00:00
}
2017-11-24 18:18:03 +00:00
s . loadConfiguration ( configMsg )
2017-11-09 11:16:03 +00:00
}
}
}
// loadConfiguration manages dynamically frontends, backends and TLS configurations
2017-11-24 18:18:03 +00:00
func ( s * Server ) loadConfiguration ( configMsg types . ConfigMessage ) {
currentConfigurations := s . currentConfigurations . Get ( ) . ( types . Configurations )
2017-11-09 11:16:03 +00:00
// Copy configurations to new map so we don't change current if LoadConfig fails
newConfigurations := make ( types . Configurations )
for k , v := range currentConfigurations {
newConfigurations [ k ] = v
}
newConfigurations [ configMsg . ProviderName ] = configMsg . Configuration
2018-01-26 10:58:03 +00:00
s . metricsRegistry . ConfigReloadsCounter ( ) . Add ( 1 )
2017-11-24 18:18:03 +00:00
newServerEntryPoints , err := s . loadConfig ( newConfigurations , s . globalConfiguration )
2017-11-09 11:16:03 +00:00
if err == nil {
2018-01-26 10:58:03 +00:00
s . metricsRegistry . LastConfigReloadSuccessGauge ( ) . Set ( float64 ( time . Now ( ) . Unix ( ) ) )
2017-11-09 11:16:03 +00:00
for newServerEntryPointName , newServerEntryPoint := range newServerEntryPoints {
2017-11-24 18:18:03 +00:00
s . serverEntryPoints [ newServerEntryPointName ] . httpRouter . UpdateHandler ( newServerEntryPoint . httpRouter . GetHandler ( ) )
2017-12-21 13:16:03 +00:00
if s . globalConfiguration . EntryPoints [ newServerEntryPointName ] . TLS == nil {
if newServerEntryPoint . certs . Get ( ) != nil {
2017-12-08 10:02:03 +00:00
log . Debugf ( "Certificates not added to non-TLS entryPoint %s." , newServerEntryPointName )
}
2017-12-21 13:16:03 +00:00
} else {
2017-11-24 18:18:03 +00:00
s . serverEntryPoints [ newServerEntryPointName ] . certs . Set ( newServerEntryPoint . certs . Get ( ) )
2017-11-09 11:16:03 +00:00
}
2017-11-24 18:18:03 +00:00
log . Infof ( "Server configuration reloaded on %s" , s . serverEntryPoints [ newServerEntryPointName ] . httpServer . Addr )
2017-11-09 11:16:03 +00:00
}
2017-11-24 18:18:03 +00:00
s . currentConfigurations . Set ( newConfigurations )
2018-03-05 19:54:04 +00:00
for _ , listener := range s . configurationListeners {
listener ( * configMsg . Configuration )
}
2017-11-24 18:18:03 +00:00
s . postLoadConfiguration ( )
2017-11-09 11:16:03 +00:00
} else {
2018-01-26 10:58:03 +00:00
s . metricsRegistry . ConfigReloadsFailureCounter ( ) . Add ( 1 )
s . metricsRegistry . LastConfigReloadFailureGauge ( ) . Set ( float64 ( time . Now ( ) . Unix ( ) ) )
2017-11-09 11:16:03 +00:00
log . Error ( "Error loading new configuration, aborted " , err )
}
}
2016-04-13 18:36:23 +00:00
2018-03-05 19:54:04 +00:00
// AddListener adds a new listener function used when new configuration is provided
func ( s * Server ) AddListener ( listener func ( types . Configuration ) ) {
if s . configurationListeners == nil {
s . configurationListeners = make ( [ ] func ( types . Configuration ) , 0 )
}
s . configurationListeners = append ( s . configurationListeners , listener )
}
// SetOnDemandListener adds a new listener function used when a request is caught
func ( s * serverEntryPoint ) SetOnDemandListener ( listener func ( string ) ( * tls . Certificate , error ) ) {
s . onDemandListener = listener
}
2017-11-09 11:16:03 +00:00
// loadHTTPSConfiguration add/delete HTTPS certificate managed dynamically
2018-03-06 09:12:04 +00:00
func ( s * Server ) loadHTTPSConfiguration ( configurations types . Configurations , defaultEntryPoints configuration . DefaultEntryPoints ) ( map [ string ] map [ string ] * tls . Certificate , error ) {
newEPCertificates := make ( map [ string ] map [ string ] * tls . Certificate )
2017-11-09 11:16:03 +00:00
// Get all certificates
for _ , configuration := range configurations {
2018-01-23 15:30:07 +00:00
if configuration . TLS != nil && len ( configuration . TLS ) > 0 {
2018-03-06 09:12:04 +00:00
if err := traefiktls . SortTLSPerEntryPoints ( configuration . TLS , newEPCertificates , defaultEntryPoints ) ; err != nil {
2017-11-09 11:16:03 +00:00
return nil , err
2016-06-22 16:31:14 +00:00
}
2017-11-09 11:16:03 +00:00
}
}
return newEPCertificates , nil
}
2016-06-22 16:31:14 +00:00
2018-03-27 14:18:03 +00:00
// getCertificate allows to customize tlsConfig.GetCertificate behaviour to get the certificates inserted dynamically
2017-11-09 11:16:03 +00:00
func ( s * serverEntryPoint ) getCertificate ( clientHello * tls . ClientHelloInfo ) ( * tls . Certificate , error ) {
2018-03-05 19:54:04 +00:00
domainToCheck := types . CanonicalDomain ( clientHello . ServerName )
2017-11-09 11:16:03 +00:00
if s . certs . Get ( ) != nil {
2018-03-06 09:12:04 +00:00
for domains , cert := range s . certs . Get ( ) . ( map [ string ] * tls . Certificate ) {
2018-03-27 14:18:03 +00:00
for _ , certDomain := range strings . Split ( domains , "," ) {
if types . MatchDomain ( domainToCheck , certDomain ) {
2017-11-09 11:16:03 +00:00
return cert , nil
2016-04-13 18:36:23 +00:00
}
2016-01-13 21:45:49 +00:00
}
}
2018-02-27 10:10:03 +00:00
log . Debugf ( "No certificate provided dynamically can check the domain %q, a per default certificate will be used." , domainToCheck )
2016-01-13 21:45:49 +00:00
}
2018-03-05 19:54:04 +00:00
if s . onDemandListener != nil {
return s . onDemandListener ( domainToCheck )
}
2017-11-09 11:16:03 +00:00
return nil , nil
2016-01-13 21:45:49 +00:00
}
2017-11-24 18:18:03 +00:00
func ( s * Server ) postLoadConfiguration ( ) {
2018-01-26 10:58:03 +00:00
metrics . OnConfigurationUpdate ( )
2018-03-05 19:54:04 +00:00
if s . globalConfiguration . ACME == nil || s . leadership == nil || ! s . leadership . IsLeader ( ) {
2016-08-18 12:20:11 +00:00
return
}
2018-03-05 19:54:04 +00:00
2017-11-24 18:18:03 +00:00
if s . globalConfiguration . ACME . OnHostRule {
currentConfigurations := s . currentConfigurations . Get ( ) . ( types . Configurations )
2017-08-25 14:10:03 +00:00
for _ , config := range currentConfigurations {
for _ , frontend := range config . Frontends {
2017-01-05 11:32:56 +00:00
// check if one of the frontend entrypoints is configured with TLS
2017-04-07 13:48:58 +00:00
// and is configured with ACME
2018-01-02 08:42:03 +00:00
acmeEnabled := false
2017-11-09 11:16:03 +00:00
for _ , entryPoint := range frontend . EntryPoints {
2017-11-24 18:18:03 +00:00
if s . globalConfiguration . ACME . EntryPoint == entryPoint && s . globalConfiguration . EntryPoints [ entryPoint ] . TLS != nil {
2018-01-02 08:42:03 +00:00
acmeEnabled = true
2017-01-05 11:32:56 +00:00
break
2016-08-05 18:42:45 +00:00
}
}
2018-01-02 08:42:03 +00:00
if acmeEnabled {
2017-01-05 11:32:56 +00:00
for _ , route := range frontend . Routes {
2018-02-26 14:34:04 +00:00
rules := rules . Rules { }
2017-01-05 11:32:56 +00:00
domains , err := rules . ParseDomains ( route . Rule )
if err != nil {
log . Errorf ( "Error parsing domains: %v" , err )
} else {
2017-11-24 18:18:03 +00:00
s . globalConfiguration . ACME . LoadCertificateForDomains ( domains )
2017-01-05 11:32:56 +00:00
}
}
}
2016-08-05 18:42:45 +00:00
}
}
}
}
2018-01-29 13:58:03 +00:00
func ( s * Server ) startProvider ( ) {
2016-01-13 21:45:49 +00:00
// start providers
2018-01-29 13:58:03 +00:00
providerType := reflect . TypeOf ( s . provider )
jsonConf , err := json . Marshal ( s . provider )
if err != nil {
log . Debugf ( "Unable to marshal provider conf %v with error: %v" , providerType , err )
2016-01-13 21:45:49 +00:00
}
2018-01-29 13:58:03 +00:00
log . Infof ( "Starting provider %v %s" , providerType , jsonConf )
currentProvider := s . provider
safe . Go ( func ( ) {
err := currentProvider . Provide ( s . configurationChan , s . routinesPool , s . globalConfiguration . Constraints )
if err != nil {
log . Errorf ( "Error starting provider %v: %s" , providerType , err )
}
} )
2016-01-13 21:45:49 +00:00
}
2018-03-06 09:12:04 +00:00
func createClientTLSConfig ( entryPointName string , tlsOption * traefiktls . TLS ) ( * tls . Config , error ) {
2017-04-06 22:10:02 +00:00
if tlsOption == nil {
return nil , errors . New ( "no TLS provided" )
}
2018-03-05 19:54:04 +00:00
config , err := tlsOption . Certificates . CreateTLSConfig ( entryPointName )
2017-04-06 22:10:02 +00:00
if err != nil {
return nil , err
}
if len ( tlsOption . ClientCAFiles ) > 0 {
2017-11-10 09:30:04 +00:00
log . Warnf ( "Deprecated configuration found during client TLS configuration creation: %s. Please use %s (which allows to make the CA Files optional)." , "tls.ClientCAFiles" , "tls.ClientCA.files" )
tlsOption . ClientCA . Files = tlsOption . ClientCAFiles
tlsOption . ClientCA . Optional = false
}
if len ( tlsOption . ClientCA . Files ) > 0 {
2017-04-06 22:10:02 +00:00
pool := x509 . NewCertPool ( )
2017-11-10 09:30:04 +00:00
for _ , caFile := range tlsOption . ClientCA . Files {
2017-04-06 22:10:02 +00:00
data , err := ioutil . ReadFile ( caFile )
if err != nil {
return nil , err
}
if ! pool . AppendCertsFromPEM ( data ) {
return nil , errors . New ( "invalid certificate(s) in " + caFile )
}
}
config . RootCAs = pool
}
config . BuildNameToCertificate ( )
return config , nil
}
2016-01-13 21:45:49 +00:00
// creates a TLS config that allows terminating HTTPS for multiple domains using SNI
2018-03-06 09:12:04 +00:00
func ( s * Server ) createTLSConfig ( entryPointName string , tlsOption * traefiktls . TLS , router * middlewares . HandlerSwitcher ) ( * tls . Config , error ) {
2016-01-29 19:34:17 +00:00
if tlsOption == nil {
return nil , nil
}
2016-01-13 21:45:49 +00:00
2018-03-05 19:54:04 +00:00
config , err := tlsOption . Certificates . CreateTLSConfig ( entryPointName )
2016-06-27 10:19:14 +00:00
if err != nil {
return nil , err
2016-03-21 10:10:18 +00:00
}
2018-03-06 09:12:04 +00:00
s . serverEntryPoints [ entryPointName ] . certs . Set ( make ( map [ string ] * tls . Certificate ) )
2016-11-09 16:56:41 +00:00
// ensure http2 enabled
config . NextProtos = [ ] string { "h2" , "http/1.1" }
2016-06-15 20:38:40 +00:00
if len ( tlsOption . ClientCAFiles ) > 0 {
2017-11-10 09:30:04 +00:00
log . Warnf ( "Deprecated configuration found during TLS configuration creation: %s. Please use %s (which allows to make the CA Files optional)." , "tls.ClientCAFiles" , "tls.ClientCA.files" )
tlsOption . ClientCA . Files = tlsOption . ClientCAFiles
tlsOption . ClientCA . Optional = false
}
if len ( tlsOption . ClientCA . Files ) > 0 {
2016-06-15 20:38:40 +00:00
pool := x509 . NewCertPool ( )
2017-11-10 09:30:04 +00:00
for _ , caFile := range tlsOption . ClientCA . Files {
2016-06-15 20:38:40 +00:00
data , err := ioutil . ReadFile ( caFile )
if err != nil {
return nil , err
}
ok := pool . AppendCertsFromPEM ( data )
if ! ok {
return nil , errors . New ( "invalid certificate(s) in " + caFile )
}
}
config . ClientCAs = pool
2017-11-10 09:30:04 +00:00
if tlsOption . ClientCA . Optional {
config . ClientAuth = tls . VerifyClientCertIfGiven
} else {
config . ClientAuth = tls . RequireAndVerifyClientCert
}
2016-06-15 20:38:40 +00:00
}
2017-11-24 18:18:03 +00:00
if s . globalConfiguration . ACME != nil {
2018-01-25 11:02:04 +00:00
if entryPointName == s . globalConfiguration . ACME . EntryPoint {
checkOnDemandDomain := func ( domain string ) bool {
routeMatch := & mux . RouteMatch { }
router := router . GetHandler ( )
match := router . Match ( & http . Request { URL : & url . URL { } , Host : domain } , routeMatch )
if match && routeMatch . Route != nil {
return true
2016-03-21 10:10:18 +00:00
}
2018-01-25 11:02:04 +00:00
return false
}
2018-03-05 19:54:04 +00:00
err := s . globalConfiguration . ACME . CreateClusterConfig ( s . leadership , config , & s . serverEntryPoints [ entryPointName ] . certs , checkOnDemandDomain )
if err != nil {
return nil , err
2016-03-21 10:10:18 +00:00
}
}
2017-11-09 11:16:03 +00:00
} else {
2017-11-24 18:18:03 +00:00
config . GetCertificate = s . serverEntryPoints [ entryPointName ] . getCertificate
2016-03-21 10:10:18 +00:00
}
if len ( config . Certificates ) == 0 {
return nil , errors . New ( "No certificates found for TLS entrypoint " + entryPointName )
2016-01-13 21:45:49 +00:00
}
// BuildNameToCertificate parses the CommonName and SubjectAlternateName fields
// in each certificate and populates the config.NameToCertificate map.
config . BuildNameToCertificate ( )
2018-03-05 19:54:04 +00:00
if acme . IsEnabled ( ) {
if entryPointName == acme . Get ( ) . EntryPoint {
acme . Get ( ) . SetStaticCertificates ( config . NameToCertificate )
acme . Get ( ) . SetDynamicCertificates ( & s . serverEntryPoints [ entryPointName ] . certs )
if acme . Get ( ) . OnDemand {
s . serverEntryPoints [ entryPointName ] . SetOnDemandListener ( acme . Get ( ) . ListenRequest )
}
}
}
// Set the minimum TLS version if set in the config TOML
2018-03-06 09:12:04 +00:00
if minConst , exists := traefiktls . MinVersion [ s . globalConfiguration . EntryPoints [ entryPointName ] . TLS . MinVersion ] ; exists {
2016-09-20 06:06:06 +00:00
config . PreferServerCipherSuites = true
config . MinVersion = minConst
}
2018-03-05 19:54:04 +00:00
// Set the list of CipherSuites if set in the config TOML
2017-11-24 18:18:03 +00:00
if s . globalConfiguration . EntryPoints [ entryPointName ] . TLS . CipherSuites != nil {
2018-03-05 19:54:04 +00:00
// if our list of CipherSuites is defined in the entrypoint config, we can re-initilize the suites list as empty
2016-09-20 06:06:06 +00:00
config . CipherSuites = make ( [ ] uint16 , 0 )
2017-11-24 18:18:03 +00:00
for _ , cipher := range s . globalConfiguration . EntryPoints [ entryPointName ] . TLS . CipherSuites {
2018-03-06 09:12:04 +00:00
if cipherConst , exists := traefiktls . CipherSuites [ cipher ] ; exists {
2016-09-20 06:06:06 +00:00
config . CipherSuites = append ( config . CipherSuites , cipherConst )
} else {
2018-03-05 19:54:04 +00:00
// CipherSuite listed in the toml does not exist in our listed
2016-09-20 06:06:06 +00:00
return nil , errors . New ( "Invalid CipherSuite: " + cipher )
}
}
}
2016-01-13 21:45:49 +00:00
return config , nil
}
2017-11-24 18:18:03 +00:00
func ( s * Server ) startServer ( serverEntryPoint * serverEntryPoint , globalConfiguration configuration . GlobalConfiguration ) {
2017-08-25 19:32:03 +00:00
log . Infof ( "Starting server on %s" , serverEntryPoint . httpServer . Addr )
2017-03-09 22:27:09 +00:00
var err error
2017-08-25 19:32:03 +00:00
if serverEntryPoint . httpServer . TLSConfig != nil {
err = serverEntryPoint . httpServer . ServeTLS ( serverEntryPoint . listener , "" , "" )
2016-01-13 21:45:49 +00:00
} else {
2017-08-25 19:32:03 +00:00
err = serverEntryPoint . httpServer . Serve ( serverEntryPoint . listener )
2017-03-09 22:27:09 +00:00
}
2017-10-11 08:38:03 +00:00
if err != http . ErrServerClosed {
2017-03-09 22:27:09 +00:00
log . Error ( "Error creating server: " , err )
2016-01-13 21:45:49 +00:00
}
}
2017-11-24 18:18:03 +00:00
func ( s * Server ) addInternalRoutes ( entryPointName string , router * mux . Router ) {
if s . globalConfiguration . Metrics != nil && s . globalConfiguration . Metrics . Prometheus != nil && s . globalConfiguration . Metrics . Prometheus . EntryPoint == entryPointName {
2017-11-09 15:12:04 +00:00
metrics . PrometheusHandler { } . AddRoutes ( router )
}
2017-11-24 18:18:03 +00:00
if s . globalConfiguration . Rest != nil && s . globalConfiguration . Rest . EntryPoint == entryPointName {
s . globalConfiguration . Rest . AddRoutes ( router )
2017-11-09 15:12:04 +00:00
}
2017-11-24 18:18:03 +00:00
if s . globalConfiguration . API != nil && s . globalConfiguration . API . EntryPoint == entryPointName {
s . globalConfiguration . API . AddRoutes ( router )
2017-11-09 15:12:04 +00:00
}
}
2017-11-24 18:18:03 +00:00
func ( s * Server ) addInternalPublicRoutes ( entryPointName string , router * mux . Router ) {
if s . globalConfiguration . Ping != nil && s . globalConfiguration . Ping . EntryPoint != "" && s . globalConfiguration . Ping . EntryPoint == entryPointName {
s . globalConfiguration . Ping . AddRoutes ( router )
2017-11-09 15:12:04 +00:00
}
2018-04-03 08:00:07 +00:00
if s . globalConfiguration . API != nil && s . globalConfiguration . API . EntryPoint == entryPointName && s . leadership != nil {
s . leadership . AddRoutes ( router )
}
2018-01-17 17:46:03 +00:00
}
2018-01-15 15:04:05 +00:00
2018-01-17 17:46:03 +00:00
func ( s * Server ) addACMERoutes ( entryPointName string , router * mux . Router ) {
2018-01-15 15:04:05 +00:00
if s . globalConfiguration . ACME != nil && s . globalConfiguration . ACME . HTTPChallenge != nil && s . globalConfiguration . ACME . HTTPChallenge . EntryPoint == entryPointName {
s . globalConfiguration . ACME . AddRoutes ( router )
2018-03-05 19:54:04 +00:00
} else if acme . IsEnabled ( ) && acme . Get ( ) . HTTPChallenge != nil && acme . Get ( ) . HTTPChallenge . EntryPoint == entryPointName {
acme . Get ( ) . AddRoutes ( router )
2018-01-15 15:04:05 +00:00
}
2017-11-09 15:12:04 +00:00
}
2017-11-24 18:18:03 +00:00
func ( s * Server ) prepareServer ( entryPointName string , entryPoint * configuration . EntryPoint , router * middlewares . HandlerSwitcher , middlewares [ ] negroni . Handler , internalMiddlewares [ ] negroni . Handler ) ( * http . Server , net . Listener , error ) {
readTimeout , writeTimeout , idleTimeout := buildServerTimeouts ( s . globalConfiguration )
2017-08-18 13:34:04 +00:00
log . Infof ( "Preparing server %s %+v with readTimeout=%s writeTimeout=%s idleTimeout=%s" , entryPointName , entryPoint , readTimeout , writeTimeout , idleTimeout )
2016-01-13 21:45:49 +00:00
// middlewares
2017-08-18 13:34:04 +00:00
n := negroni . New ( )
2016-01-13 21:45:49 +00:00
for _ , middleware := range middlewares {
2017-08-18 13:34:04 +00:00
n . Use ( middleware )
2016-01-13 21:45:49 +00:00
}
2017-08-18 13:34:04 +00:00
n . UseHandler ( router )
2017-11-09 15:12:04 +00:00
path := "/"
2017-11-24 18:18:03 +00:00
if s . globalConfiguration . Web != nil && s . globalConfiguration . Web . Path != "" {
path = s . globalConfiguration . Web . Path
2017-11-09 15:12:04 +00:00
}
2017-11-24 18:18:03 +00:00
internalMuxRouter := s . buildInternalRouter ( entryPointName , path , internalMiddlewares )
2017-11-09 15:12:04 +00:00
internalMuxRouter . NotFoundHandler = n
2017-11-24 18:18:03 +00:00
tlsConfig , err := s . createTLSConfig ( entryPointName , entryPoint . TLS , router )
2016-01-13 21:45:49 +00:00
if err != nil {
2017-01-12 10:04:11 +00:00
log . Errorf ( "Error creating TLS config: %s" , err )
2017-08-25 19:32:03 +00:00
return nil , nil , err
}
listener , err := net . Listen ( "tcp" , entryPoint . Address )
if err != nil {
log . Error ( "Error opening listener " , err )
2017-09-07 18:14:03 +00:00
return nil , nil , err
2017-08-25 19:32:03 +00:00
}
2017-10-10 12:50:03 +00:00
if entryPoint . ProxyProtocol != nil {
2018-03-23 16:40:04 +00:00
IPs , err := whitelist . NewIP ( entryPoint . ProxyProtocol . TrustedIPs , entryPoint . ProxyProtocol . Insecure , false )
2017-10-10 12:50:03 +00:00
if err != nil {
2017-11-09 15:12:04 +00:00
return nil , nil , fmt . Errorf ( "error creating whitelist: %s" , err )
2017-10-10 12:50:03 +00:00
}
log . Infof ( "Enabling ProxyProtocol for trusted IPs %v" , entryPoint . ProxyProtocol . TrustedIPs )
listener = & proxyproto . Listener {
Listener : listener ,
SourceCheck : func ( addr net . Addr ) ( bool , error ) {
ip , ok := addr . ( * net . TCPAddr )
if ! ok {
2017-11-09 15:12:04 +00:00
return false , fmt . Errorf ( "type error %v" , addr )
2017-10-10 12:50:03 +00:00
}
return IPs . ContainsIP ( ip . IP )
} ,
}
2016-01-13 21:45:49 +00:00
}
2017-03-09 22:27:09 +00:00
return & http . Server {
2017-08-25 19:32:03 +00:00
Addr : entryPoint . Address ,
2017-11-09 15:12:04 +00:00
Handler : internalMuxRouter ,
2017-08-25 19:32:03 +00:00
TLSConfig : tlsConfig ,
ReadTimeout : readTimeout ,
WriteTimeout : writeTimeout ,
IdleTimeout : idleTimeout ,
2017-11-18 00:10:03 +00:00
ErrorLog : httpServerLogger ,
2017-08-25 19:32:03 +00:00
} ,
listener ,
nil
2016-01-13 21:45:49 +00:00
}
2017-11-24 18:18:03 +00:00
func ( s * Server ) buildInternalRouter ( entryPointName , path string , internalMiddlewares [ ] negroni . Handler ) * mux . Router {
2017-11-09 15:12:04 +00:00
internalMuxRouter := mux . NewRouter ( )
internalMuxRouter . StrictSlash ( true )
internalMuxRouter . SkipClean ( true )
internalMuxSubrouter := internalMuxRouter . PathPrefix ( path ) . Subrouter ( )
internalMuxSubrouter . StrictSlash ( true )
internalMuxSubrouter . SkipClean ( true )
2017-11-24 18:18:03 +00:00
s . addInternalRoutes ( entryPointName , internalMuxSubrouter )
2017-11-09 15:12:04 +00:00
internalMuxRouter . Walk ( wrapRoute ( internalMiddlewares ) )
2017-11-24 18:18:03 +00:00
s . addInternalPublicRoutes ( entryPointName , internalMuxSubrouter )
2018-01-17 17:46:03 +00:00
s . addACMERoutes ( entryPointName , internalMuxRouter )
2017-11-09 15:12:04 +00:00
return internalMuxRouter
}
// wrapRoute with middlewares
func wrapRoute ( middlewares [ ] negroni . Handler ) func ( * mux . Route , * mux . Router , [ ] * mux . Route ) error {
return func ( route * mux . Route , router * mux . Router , ancestors [ ] * mux . Route ) error {
middles := append ( middlewares , negroni . Wrap ( route . GetHandler ( ) ) )
route . Handler ( negroni . New ( middles ... ) )
return nil
}
}
2017-08-25 14:10:03 +00:00
func buildServerTimeouts ( globalConfig configuration . GlobalConfiguration ) ( readTimeout , writeTimeout , idleTimeout time . Duration ) {
2017-08-18 13:34:04 +00:00
readTimeout = time . Duration ( 0 )
writeTimeout = time . Duration ( 0 )
if globalConfig . RespondingTimeouts != nil {
readTimeout = time . Duration ( globalConfig . RespondingTimeouts . ReadTimeout )
writeTimeout = time . Duration ( globalConfig . RespondingTimeouts . WriteTimeout )
}
2017-09-20 16:14:03 +00:00
// Prefer legacy idle timeout parameter for backwards compatibility reasons
if globalConfig . IdleTimeout > 0 {
2017-08-18 13:34:04 +00:00
idleTimeout = time . Duration ( globalConfig . IdleTimeout )
2017-09-20 16:14:03 +00:00
log . Warn ( "top-level idle timeout configuration has been deprecated -- please use responding timeouts" )
} else if globalConfig . RespondingTimeouts != nil {
idleTimeout = time . Duration ( globalConfig . RespondingTimeouts . IdleTimeout )
2017-08-18 13:34:04 +00:00
} else {
2017-12-18 08:14:03 +00:00
idleTimeout = configuration . DefaultIdleTimeout
2017-08-18 13:34:04 +00:00
}
return readTimeout , writeTimeout , idleTimeout
}
2017-11-24 18:18:03 +00:00
func ( s * Server ) buildEntryPoints ( globalConfiguration configuration . GlobalConfiguration ) map [ string ] * serverEntryPoint {
2016-02-25 17:30:13 +00:00
serverEntryPoints := make ( map [ string ] * serverEntryPoint )
2016-01-29 19:34:17 +00:00
for entryPointName := range globalConfiguration . EntryPoints {
2017-11-24 18:18:03 +00:00
router := s . buildDefaultHTTPRouter ( )
2016-02-25 17:30:13 +00:00
serverEntryPoints [ entryPointName ] = & serverEntryPoint {
2016-03-04 10:32:23 +00:00
httpRouter : middlewares . NewHandlerSwitcher ( router ) ,
2016-01-29 19:34:17 +00:00
}
}
return serverEntryPoints
}
2017-08-18 13:34:04 +00:00
// 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.
2018-03-06 09:12:04 +00:00
func ( s * Server ) getRoundTripper ( entryPointName string , globalConfiguration configuration . GlobalConfiguration , passTLSCert bool , tls * traefiktls . TLS ) ( http . RoundTripper , error ) {
2017-08-18 13:34:04 +00:00
if passTLSCert {
2017-11-09 11:16:03 +00:00
tlsConfig , err := createClientTLSConfig ( entryPointName , tls )
2017-08-18 13:34:04 +00:00
if err != nil {
log . Errorf ( "Failed to create TLSClientConfig: %s" , err )
return nil , err
}
transport := createHTTPTransport ( globalConfiguration )
transport . TLSClientConfig = tlsConfig
return transport , nil
2017-04-06 22:10:02 +00:00
}
2017-08-18 13:34:04 +00:00
2017-11-24 18:18:03 +00:00
return s . defaultForwardingRoundTripper , nil
2017-04-06 22:10:02 +00:00
}
2017-12-02 18:25:29 +00:00
// loadConfig returns a new gorilla.mux Route from the specified global configuration and the dynamic
2016-01-13 21:45:49 +00:00
// provider configurations.
2017-11-24 18:18:03 +00:00
func ( s * Server ) loadConfig ( configurations types . Configurations , globalConfiguration configuration . GlobalConfiguration ) ( map [ string ] * serverEntryPoint , error ) {
serverEntryPoints := s . buildEntryPoints ( globalConfiguration )
2017-03-10 18:43:20 +00:00
redirectHandlers := make ( map [ string ] negroni . Handler )
2016-01-13 21:45:49 +00:00
backends := map [ string ] http . Handler { }
2017-08-25 14:10:03 +00:00
backendsHealthCheck := map [ string ] * healthcheck . BackendHealthCheck { }
2018-04-11 11:54:03 +00:00
var errorPageHandlers [ ] * errorpages . Handler
2017-05-03 08:20:33 +00:00
errorHandler := NewRecordingErrorHandler ( middlewares . DefaultNetErrorRecorder { } )
2017-04-13 14:37:07 +00:00
2018-02-16 10:04:04 +00:00
for providerName , config := range configurations {
2017-08-25 14:10:03 +00:00
frontendNames := sortedFrontendNamesForConfig ( config )
2016-06-20 13:19:52 +00:00
frontend :
2016-02-19 22:55:23 +00:00
for _ , frontendName := range frontendNames {
2017-08-25 14:10:03 +00:00
frontend := config . Frontends [ frontendName ]
2016-02-19 22:55:23 +00:00
2016-01-29 19:34:17 +00:00
log . Debugf ( "Creating frontend %s" , frontendName )
2016-07-04 17:30:32 +00:00
2018-01-25 11:02:04 +00:00
var frontendEntryPoints [ ] string
for _ , entryPointName := range frontend . EntryPoints {
if _ , ok := serverEntryPoints [ entryPointName ] ; ! ok {
log . Errorf ( "Undefined entrypoint '%s' for frontend %s" , entryPointName , frontendName )
} else {
frontendEntryPoints = append ( frontendEntryPoints , entryPointName )
}
}
frontend . EntryPoints = frontendEntryPoints
2016-03-22 00:32:02 +00:00
if len ( frontend . EntryPoints ) == 0 {
2018-01-25 11:02:04 +00:00
log . Errorf ( "No entrypoint defined for frontend %s" , frontendName )
2016-06-20 13:19:52 +00:00
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-03-22 00:32:02 +00:00
}
2016-01-29 19:34:17 +00:00
for _ , entryPointName := range frontend . EntryPoints {
log . Debugf ( "Wiring frontend %s to entryPoint %s" , frontendName , entryPointName )
2017-04-19 09:14:05 +00:00
2018-02-26 14:34:04 +00:00
newServerRoute := & types . ServerRoute { Route : serverEntryPoints [ entryPointName ] . httpRouter . GetHandler ( ) . NewRoute ( ) . Name ( frontendName ) }
2016-01-29 19:34:17 +00:00
for routeName , route := range frontend . Routes {
2016-03-30 17:05:43 +00:00
err := getRoute ( newServerRoute , & route )
2016-01-29 19:34:17 +00:00
if err != nil {
2016-06-20 13:19:52 +00:00
log . Errorf ( "Error creating route for frontend %s: %v" , frontendName , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-01-29 19:34:17 +00:00
}
2016-03-30 17:05:43 +00:00
log . Debugf ( "Creating route %s %s" , routeName , route . Rule )
2016-01-13 21:45:49 +00:00
}
2017-04-19 09:14:05 +00:00
2016-01-29 19:34:17 +00:00
entryPoint := globalConfiguration . EntryPoints [ entryPointName ]
2017-08-25 14:10:03 +00:00
n := negroni . New ( )
2018-02-26 08:34:03 +00:00
if entryPoint . Redirect != nil && entryPointName != entryPoint . Redirect . EntryPoint {
2016-01-29 19:34:17 +00:00
if redirectHandlers [ entryPointName ] != nil {
2017-08-25 14:10:03 +00:00
n . Use ( redirectHandlers [ entryPointName ] )
2017-12-15 10:48:03 +00:00
} else if handler , err := s . buildRedirectHandler ( entryPointName , entryPoint . Redirect ) ; err != nil {
2016-06-20 13:19:52 +00:00
log . Errorf ( "Error loading entrypoint configuration for frontend %s: %v" , frontendName , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-01-29 19:34:17 +00:00
} else {
2018-01-11 09:04:03 +00:00
handlerToUse := s . wrapNegroniHandlerWithAccessLog ( handler , fmt . Sprintf ( "entrypoint redirect for %s" , frontendName ) )
n . Use ( handlerToUse )
redirectHandlers [ entryPointName ] = handlerToUse
2016-01-13 21:45:49 +00:00
}
2017-03-10 18:43:20 +00:00
}
2018-02-16 10:04:04 +00:00
if backends [ entryPointName + providerName + frontend . Backend ] == nil {
2017-03-10 18:43:20 +00:00
log . Debugf ( "Creating backend %s" , frontend . Backend )
2017-04-06 22:10:02 +00:00
2017-11-24 18:18:03 +00:00
roundTripper , err := s . getRoundTripper ( entryPointName , globalConfiguration , frontend . PassTLSCert , entryPoint . TLS )
2017-08-18 13:34:04 +00:00
if err != nil {
log . Errorf ( "Failed to create RoundTripper for frontend %s: %v" , frontendName , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2017-04-06 22:10:02 +00:00
}
2017-10-16 10:46:03 +00:00
rewriter , err := NewHeaderRewriter ( entryPoint . ForwardedHeaders . TrustedIPs , entryPoint . ForwardedHeaders . Insecure )
if err != nil {
log . Errorf ( "Error creating rewriter for frontend %s: %v" , frontendName , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
2018-01-02 09:10:04 +00:00
headerMiddleware := middlewares . NewHeaderFromStruct ( frontend . Headers )
2018-03-01 15:42:04 +00:00
secureMiddleware := middlewares . NewSecure ( frontend . Headers )
2017-11-23 16:40:03 +00:00
2018-03-01 15:42:04 +00:00
var responseModifier = buildModifyResponse ( secureMiddleware , headerMiddleware )
2018-01-10 16:48:04 +00:00
var fwd http . Handler
fwd , err = forward . New (
2017-11-22 17:20:03 +00:00
forward . Stream ( true ) ,
2017-05-03 08:20:33 +00:00
forward . PassHostHeader ( frontend . PassHostHeader ) ,
2017-08-18 13:34:04 +00:00
forward . RoundTripper ( roundTripper ) ,
2017-05-03 08:20:33 +00:00
forward . ErrorHandler ( errorHandler ) ,
2017-10-16 10:46:03 +00:00
forward . Rewriter ( rewriter ) ,
2017-11-23 16:40:03 +00:00
forward . ResponseModifier ( responseModifier ) ,
2017-05-03 08:20:33 +00:00
)
2017-08-18 13:34:04 +00:00
2017-04-06 22:10:02 +00:00
if err != nil {
log . Errorf ( "Error creating forwarder for frontend %s: %v" , frontendName , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
2018-01-10 16:48:04 +00:00
if s . tracingMiddleware . IsEnabled ( ) {
tm := s . tracingMiddleware . NewForwarderMiddleware ( frontendName , frontend . Backend )
next := fwd
fwd = http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
tm . ServeHTTP ( w , r , next . ServeHTTP )
} )
}
2017-05-22 19:39:29 +00:00
var rr * roundrobin . RoundRobin
var saveFrontend http . Handler
2017-11-24 18:18:03 +00:00
if s . accessLoggerMiddleware != nil {
2017-05-22 19:39:29 +00:00
saveBackend := accesslog . NewSaveBackend ( fwd , frontend . Backend )
saveFrontend = accesslog . NewSaveFrontend ( saveBackend , frontendName )
rr , _ = roundrobin . New ( saveFrontend )
} else {
rr , _ = roundrobin . New ( fwd )
}
2017-04-06 22:10:02 +00:00
2017-08-25 14:10:03 +00:00
if config . Backends [ frontend . Backend ] == nil {
2017-03-10 18:43:20 +00:00
log . Errorf ( "Undefined backend '%s' for frontend %s" , frontend . Backend , frontendName )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
2016-05-13 14:22:11 +00:00
2017-08-25 14:10:03 +00:00
lbMethod , err := types . NewLoadBalancerMethod ( config . Backends [ frontend . Backend ] . LoadBalancer )
2017-03-10 18:43:20 +00:00
if err != nil {
2017-08-25 14:10:03 +00:00
log . Errorf ( "Error loading load balancer method '%+v' for frontend %s: %v" , config . Backends [ frontend . Backend ] . LoadBalancer , frontendName , err )
2017-03-10 18:43:20 +00:00
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
2016-05-13 14:22:11 +00:00
2017-03-10 18:43:20 +00:00
var sticky * roundrobin . StickySession
2017-10-10 09:10:02 +00:00
var cookieName string
if stickiness := config . Backends [ frontend . Backend ] . LoadBalancer . Stickiness ; stickiness != nil {
cookieName = cookie . GetName ( stickiness . CookieName , frontend . Backend )
2017-08-25 14:10:03 +00:00
sticky = roundrobin . NewStickySession ( cookieName )
2017-03-10 18:43:20 +00:00
}
2016-05-13 14:22:11 +00:00
2017-08-18 13:34:04 +00:00
var lb http . Handler
2017-04-13 14:37:07 +00:00
switch lbMethod {
case types . Drr :
log . Debugf ( "Creating load-balancer drr" )
2017-11-22 17:20:03 +00:00
rebalancer , _ := roundrobin . NewRebalancer ( rr )
2017-10-10 09:10:02 +00:00
if sticky != nil {
2017-08-25 14:10:03 +00:00
log . Debugf ( "Sticky session with cookie %v" , cookieName )
2017-11-22 17:20:03 +00:00
rebalancer , _ = roundrobin . NewRebalancer ( rr , roundrobin . RebalancerStickySession ( sticky ) )
2017-04-13 14:37:07 +00:00
}
lb = rebalancer
2018-01-26 10:58:03 +00:00
if err := s . configureLBServers ( rebalancer , config , frontend ) ; err != nil {
2017-07-08 08:33:17 +00:00
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
2017-08-25 14:10:03 +00:00
hcOpts := parseHealthCheckOptions ( rebalancer , frontend . Backend , config . Backends [ frontend . Backend ] . HealthCheck , globalConfiguration . HealthCheck )
2017-07-08 08:33:17 +00:00
if hcOpts != nil {
log . Debugf ( "Setting up backend health check %s" , * hcOpts )
2017-11-24 18:18:03 +00:00
hcOpts . Transport = s . defaultForwardingRoundTripper
2018-01-03 11:32:03 +00:00
backendsHealthCheck [ entryPointName + frontend . Backend ] = healthcheck . NewBackendHealthCheck ( * hcOpts , frontend . Backend )
2016-01-29 19:34:17 +00:00
}
2017-07-10 10:11:44 +00:00
lb = middlewares . NewEmptyBackendHandler ( rebalancer , lb )
2017-04-13 14:37:07 +00:00
case types . Wrr :
log . Debugf ( "Creating load-balancer wrr" )
2017-10-10 09:10:02 +00:00
if sticky != nil {
2017-08-25 14:10:03 +00:00
log . Debugf ( "Sticky session with cookie %v" , cookieName )
2017-11-24 18:18:03 +00:00
if s . accessLoggerMiddleware != nil {
2017-05-22 19:39:29 +00:00
rr , _ = roundrobin . New ( saveFrontend , roundrobin . EnableStickySession ( sticky ) )
} else {
rr , _ = roundrobin . New ( fwd , roundrobin . EnableStickySession ( sticky ) )
}
2017-04-13 14:37:07 +00:00
}
lb = rr
2018-01-26 10:58:03 +00:00
if err := s . configureLBServers ( rr , config , frontend ) ; err != nil {
2017-07-08 08:33:17 +00:00
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-04-13 08:11:36 +00:00
}
2017-08-25 14:10:03 +00:00
hcOpts := parseHealthCheckOptions ( rr , frontend . Backend , config . Backends [ frontend . Backend ] . HealthCheck , globalConfiguration . HealthCheck )
2017-04-13 14:37:07 +00:00
if hcOpts != nil {
log . Debugf ( "Setting up backend health check %s" , * hcOpts )
2017-11-24 18:18:03 +00:00
hcOpts . Transport = s . defaultForwardingRoundTripper
2018-01-03 11:32:03 +00:00
backendsHealthCheck [ entryPointName + frontend . Backend ] = healthcheck . NewBackendHealthCheck ( * hcOpts , frontend . Backend )
2016-03-29 20:25:32 +00:00
}
2017-07-10 10:11:44 +00:00
lb = middlewares . NewEmptyBackendHandler ( rr , lb )
2017-04-13 14:37:07 +00:00
}
2017-06-30 23:04:18 +00:00
if len ( frontend . Errors ) > 0 {
2018-04-11 11:54:03 +00:00
for errorPageName , errorPage := range frontend . Errors {
if frontend . Backend == errorPage . Backend {
log . Errorf ( "Error when creating error page %q for frontend %q: error pages backend %q is the same as backend for the frontend (infinite call risk)." ,
errorPageName , frontendName , errorPage . Backend )
} else if config . Backends [ errorPage . Backend ] == nil {
log . Errorf ( "Error when creating error page %q for frontend %q: the backend %q doesn't exist." ,
errorPageName , errorPage . Backend )
} else {
errorPagesHandler , err := errorpages . NewHandler ( errorPage , entryPointName + providerName + errorPage . Backend )
2017-06-30 23:04:18 +00:00
if err != nil {
2018-04-11 11:54:03 +00:00
log . Errorf ( "Error creating error pages: %v" , err )
2017-06-30 23:04:18 +00:00
} else {
2018-04-11 11:54:03 +00:00
if errorPageServer , ok := config . Backends [ errorPage . Backend ] . Servers [ "error" ] ; ok {
errorPagesHandler . FallbackURL = errorPageServer . URL
}
errorPageHandlers = append ( errorPageHandlers , errorPagesHandler )
n . Use ( errorPagesHandler )
2017-06-30 23:04:18 +00:00
}
}
}
}
2017-09-09 11:36:03 +00:00
if frontend . RateLimit != nil && len ( frontend . RateLimit . RateSet ) > 0 {
2017-11-24 18:18:03 +00:00
lb , err = s . buildRateLimiter ( lb , frontend . RateLimit )
2017-09-09 11:36:03 +00:00
if err != nil {
log . Errorf ( "Error creating rate limiter: %v" , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
2018-04-11 11:54:03 +00:00
lb = s . wrapHTTPHandlerWithAccessLog ( lb , fmt . Sprintf ( "rate limit for %s" , frontendName ) )
2017-09-09 11:36:03 +00:00
}
2017-08-25 14:10:03 +00:00
maxConns := config . Backends [ frontend . Backend ] . MaxConn
2017-04-13 14:37:07 +00:00
if maxConns != nil && maxConns . Amount != 0 {
extractFunc , err := utils . NewExtractor ( maxConns . ExtractorFunc )
if err != nil {
2018-04-11 11:54:03 +00:00
log . Errorf ( "Error creating connection limit: %v" , err )
2017-04-13 14:37:07 +00:00
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
2018-04-11 11:54:03 +00:00
log . Debugf ( "Creating load-balancer connection limit" )
2017-11-22 17:20:03 +00:00
lb , err = connlimit . New ( lb , extractFunc , maxConns . Amount )
2017-04-13 14:37:07 +00:00
if err != nil {
2018-04-11 11:54:03 +00:00
log . Errorf ( "Error creating connection limit: %v" , err )
2017-04-13 14:37:07 +00:00
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
2018-04-11 11:54:03 +00:00
lb = s . wrapHTTPHandlerWithAccessLog ( lb , fmt . Sprintf ( "connection limit for %s" , frontendName ) )
2017-04-13 14:37:07 +00:00
}
2017-04-18 06:22:06 +00:00
2017-04-13 14:37:07 +00:00
if globalConfiguration . Retry != nil {
2017-08-28 10:50:02 +00:00
countServers := len ( config . Backends [ frontend . Backend ] . Servers )
2017-11-24 18:18:03 +00:00
lb = s . buildRetryMiddleware ( lb , globalConfiguration , countServers , frontend . Backend )
2017-04-13 14:37:07 +00:00
}
2017-08-28 10:50:02 +00:00
2017-11-24 18:18:03 +00:00
if s . metricsRegistry . IsEnabled ( ) {
2018-01-26 10:58:03 +00:00
n . Use ( middlewares . NewBackendMetricsMiddleware ( s . metricsRegistry , frontend . Backend ) )
2017-04-13 14:37:07 +00:00
}
2017-05-26 15:03:14 +00:00
2018-03-23 16:40:04 +00:00
ipWhitelistMiddleware , err := buildIPWhiteLister ( frontend . WhiteList , frontend . WhitelistSourceRange )
2017-04-30 09:22:07 +00:00
if err != nil {
2017-12-13 16:02:04 +00:00
log . Errorf ( "Error creating IP Whitelister: %s" , err )
2017-04-30 09:22:07 +00:00
} else if ipWhitelistMiddleware != nil {
2018-03-23 16:40:04 +00:00
n . Use (
s . tracingMiddleware . NewNegroniHandlerWrapper (
"IP whitelist" ,
s . wrapNegroniHandlerWithAccessLog ( ipWhitelistMiddleware , fmt . Sprintf ( "ipwhitelister for %s" , frontendName ) ) ,
false ) )
log . Debugf ( "Configured IP Whitelists: %s" , frontend . WhitelistSourceRange )
2017-04-30 09:22:07 +00:00
}
2018-02-26 08:34:03 +00:00
if frontend . Redirect != nil && entryPointName != frontend . Redirect . EntryPoint {
2017-12-15 10:48:03 +00:00
rewrite , err := s . buildRedirectHandler ( entryPointName , frontend . Redirect )
2017-12-04 19:04:08 +00:00
if err != nil {
2017-12-13 16:02:04 +00:00
log . Errorf ( "Error creating Frontend Redirect: %v" , err )
2018-01-31 18:10:04 +00:00
} else {
n . Use ( s . wrapNegroniHandlerWithAccessLog ( rewrite , fmt . Sprintf ( "frontend redirect for %s" , frontendName ) ) )
log . Debugf ( "Frontend %s redirect created" , frontendName )
2017-11-18 12:50:03 +00:00
}
}
2017-04-13 14:37:07 +00:00
if len ( frontend . BasicAuth ) > 0 {
users := types . Users { }
for _ , user := range frontend . BasicAuth {
users = append ( users , user )
2017-01-12 13:34:54 +00:00
}
2017-04-19 09:14:05 +00:00
2017-04-13 14:37:07 +00:00
auth := & types . Auth { }
auth . Basic = & types . Basic {
Users : users ,
2016-01-13 21:45:49 +00:00
}
2018-01-10 16:48:04 +00:00
authMiddleware , err := mauth . NewAuthenticator ( auth , s . tracingMiddleware )
2017-04-13 14:37:07 +00:00
if err != nil {
2017-05-15 07:02:32 +00:00
log . Errorf ( "Error creating Auth: %s" , err )
} else {
2018-01-11 09:04:03 +00:00
n . Use ( s . wrapNegroniHandlerWithAccessLog ( authMiddleware , fmt . Sprintf ( "Auth for %s" , frontendName ) ) )
2017-04-13 14:37:07 +00:00
}
2016-01-13 21:45:49 +00:00
}
2017-05-26 15:03:14 +00:00
2017-11-23 16:40:03 +00:00
if headerMiddleware != nil {
2017-06-13 00:48:21 +00:00
log . Debugf ( "Adding header middleware for frontend %s" , frontendName )
2018-01-10 16:48:04 +00:00
n . Use ( s . tracingMiddleware . NewNegroniHandlerWrapper ( "Header" , headerMiddleware , false ) )
2017-06-13 00:48:21 +00:00
}
2018-01-02 09:10:04 +00:00
if secureMiddleware != nil {
2017-06-13 00:48:21 +00:00
log . Debugf ( "Adding secure middleware for frontend %s" , frontendName )
2018-03-01 15:42:04 +00:00
n . UseFunc ( secureMiddleware . HandlerFuncWithNextForRequestOnly )
2017-06-13 00:48:21 +00:00
}
2018-01-31 14:32:04 +00:00
if config . Backends [ frontend . Backend ] . Buffering != nil {
bufferedLb , err := s . buildBufferingMiddleware ( lb , config . Backends [ frontend . Backend ] . Buffering )
if err != nil {
log . Errorf ( "Error setting up buffering middleware: %s" , err )
} else {
lb = bufferedLb
}
}
2017-08-25 14:10:03 +00:00
if config . Backends [ frontend . Backend ] . CircuitBreaker != nil {
log . Debugf ( "Creating circuit breaker %s" , config . Backends [ frontend . Backend ] . CircuitBreaker . Expression )
2018-01-10 16:48:04 +00:00
expression := config . Backends [ frontend . Backend ] . CircuitBreaker . Expression
circuitBreaker , err := middlewares . NewCircuitBreaker ( lb , expression , middlewares . NewCircuitBreakerOptions ( expression ) )
2017-04-13 14:37:07 +00:00
if err != nil {
log . Errorf ( "Error creating circuit breaker: %v" , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
2018-01-10 16:48:04 +00:00
n . Use ( s . tracingMiddleware . NewNegroniHandlerWrapper ( "Circuit breaker" , circuitBreaker , false ) )
2017-04-13 14:37:07 +00:00
} else {
2017-08-25 14:10:03 +00:00
n . UseHandler ( lb )
2016-06-03 15:58:33 +00:00
}
2018-02-16 10:04:04 +00:00
backends [ entryPointName + providerName + frontend . Backend ] = n
2017-04-13 14:37:07 +00:00
} else {
log . Debugf ( "Reusing backend %s" , frontend . Backend )
}
if frontend . Priority > 0 {
2018-02-26 14:34:04 +00:00
newServerRoute . Route . Priority ( frontend . Priority )
2017-04-13 14:37:07 +00:00
}
2018-02-16 10:04:04 +00:00
s . wireFrontendBackend ( newServerRoute , backends [ entryPointName + providerName + frontend . Backend ] )
2017-03-10 18:43:20 +00:00
2018-02-26 14:34:04 +00:00
err := newServerRoute . Route . GetError ( )
2016-01-29 19:34:17 +00:00
if err != nil {
log . Errorf ( "Error building route: %s" , err )
2016-01-13 21:45:49 +00:00
}
}
2016-01-29 19:34:17 +00:00
}
}
2018-04-11 11:54:03 +00:00
for _ , errorPageHandler := range errorPageHandlers {
if handler , ok := backends [ errorPageHandler . BackendName ] ; ok {
errorPageHandler . PostLoad ( handler )
} else {
errorPageHandler . PostLoad ( nil )
}
}
2018-01-26 10:58:03 +00:00
healthcheck . GetHealthCheck ( s . metricsRegistry ) . SetBackendsConfiguration ( s . routinesPool . Ctx ( ) , backendsHealthCheck )
2018-04-11 11:54:03 +00:00
2017-11-09 11:16:03 +00:00
// Get new certificates list sorted per entrypoints
// Update certificates
2017-12-08 10:02:03 +00:00
entryPointsCertificates , err := s . loadHTTPSConfiguration ( configurations , globalConfiguration . DefaultEntryPoints )
2018-04-11 11:54:03 +00:00
2018-03-05 19:54:04 +00:00
// Sort routes and update certificates
2017-11-09 11:16:03 +00:00
for serverEntryPointName , serverEntryPoint := range serverEntryPoints {
2016-06-03 15:58:33 +00:00
serverEntryPoint . httpRouter . GetHandler ( ) . SortRoutes ( )
2018-04-11 11:54:03 +00:00
if _ , exists := entryPointsCertificates [ serverEntryPointName ] ; exists {
2017-11-09 11:16:03 +00:00
serverEntryPoint . certs . Set ( entryPointsCertificates [ serverEntryPointName ] )
}
2016-06-03 15:58:33 +00:00
}
2017-11-09 11:16:03 +00:00
return serverEntryPoints , err
2016-01-29 19:34:17 +00:00
}
2016-01-13 21:45:49 +00:00
2018-01-26 10:58:03 +00:00
func ( s * Server ) configureLBServers ( lb healthcheck . LoadBalancer , config * types . Configuration , frontend * types . Frontend ) error {
for name , srv := range config . Backends [ frontend . Backend ] . Servers {
u , err := url . Parse ( srv . URL )
2017-07-08 08:33:17 +00:00
if err != nil {
2018-01-26 10:58:03 +00:00
log . Errorf ( "Error parsing server URL %s: %v" , srv . URL , err )
2017-07-08 08:33:17 +00:00
return err
}
2018-01-26 10:58:03 +00:00
log . Debugf ( "Creating server %s at %s with weight %d" , name , u , srv . Weight )
if err := lb . UpsertServer ( u , roundrobin . Weight ( srv . Weight ) ) ; err != nil {
log . Errorf ( "Error adding server %s to load balancer: %v" , srv . URL , err )
2017-07-08 08:33:17 +00:00
return err
}
2018-01-26 10:58:03 +00:00
s . metricsRegistry . BackendServerUpGauge ( ) . With ( "backend" , frontend . Backend , "url" , srv . URL ) . Set ( 1 )
2017-07-08 08:33:17 +00:00
}
return nil
}
2018-03-23 16:40:04 +00:00
func buildIPWhiteLister ( whiteList * types . WhiteList , wlRange [ ] string ) ( * middlewares . IPWhiteLister , error ) {
if whiteList != nil &&
len ( whiteList . SourceRange ) > 0 {
return middlewares . NewIPWhiteLister ( whiteList . SourceRange , whiteList . UseXForwardedFor )
} else if len ( wlRange ) > 0 {
return middlewares . NewIPWhiteLister ( wlRange , false )
2017-04-30 09:22:07 +00:00
}
return nil , nil
}
2018-02-26 14:34:04 +00:00
func ( s * Server ) wireFrontendBackend ( serverRoute * types . ServerRoute , handler http . Handler ) {
2017-05-09 20:31:16 +00:00
// path replace - This needs to always be the very last on the handler chain (first in the order in this function)
// -- Replacing Path should happen at the very end of the Modifier chain, after all the Matcher+Modifiers ran
2018-02-26 14:34:04 +00:00
if len ( serverRoute . ReplacePath ) > 0 {
2017-05-09 20:31:16 +00:00
handler = & middlewares . ReplacePath {
2018-02-26 14:34:04 +00:00
Path : serverRoute . ReplacePath ,
2017-05-09 20:31:16 +00:00
Handler : handler ,
}
}
2018-02-26 14:34:04 +00:00
if len ( serverRoute . ReplacePathRegex ) > 0 {
sp := strings . Split ( serverRoute . ReplacePathRegex , " " )
2017-10-30 11:54:03 +00:00
if len ( sp ) == 2 {
handler = middlewares . NewReplacePathRegexHandler ( sp [ 0 ] , sp [ 1 ] , handler )
} else {
2018-02-26 14:34:04 +00:00
log . Warnf ( "Invalid syntax for ReplacePathRegex: %s. Separate the regular expression and the replacement by a space." , serverRoute . ReplacePathRegex )
2017-10-30 11:54:03 +00:00
}
}
2017-05-09 20:31:16 +00:00
// add prefix - This needs to always be right before ReplacePath on the chain (second in order in this function)
// -- Adding Path Prefix should happen after all *Strip Matcher+Modifiers ran, but before Replace (in case it's configured)
2018-02-26 14:34:04 +00:00
if len ( serverRoute . AddPrefix ) > 0 {
2016-12-02 12:40:18 +00:00
handler = & middlewares . AddPrefix {
2018-02-26 14:34:04 +00:00
Prefix : serverRoute . AddPrefix ,
2016-12-02 12:40:18 +00:00
Handler : handler ,
}
}
2016-02-26 14:29:53 +00:00
// strip prefix
2018-02-26 14:34:04 +00:00
if len ( serverRoute . StripPrefixes ) > 0 {
2016-12-02 12:40:18 +00:00
handler = & middlewares . StripPrefix {
2018-02-26 14:34:04 +00:00
Prefixes : serverRoute . StripPrefixes ,
2016-04-06 11:06:31 +00:00
Handler : handler ,
2016-12-02 12:40:18 +00:00
}
2017-04-25 18:13:39 +00:00
}
2017-03-24 11:07:59 +00:00
// strip prefix with regex
2018-02-26 14:34:04 +00:00
if len ( serverRoute . StripPrefixesRegex ) > 0 {
handler = middlewares . NewStripPrefixRegex ( handler , serverRoute . StripPrefixesRegex )
2017-03-24 11:07:59 +00:00
}
2018-02-26 14:34:04 +00:00
serverRoute . Route . Handler ( handler )
2016-02-26 14:29:53 +00:00
}
2018-01-31 18:10:04 +00:00
func ( s * Server ) buildRedirectHandler ( srcEntryPointName string , opt * types . Redirect ) ( negroni . Handler , error ) {
2017-12-15 10:48:03 +00:00
// entry point redirect
2018-01-31 18:10:04 +00:00
if len ( opt . EntryPoint ) > 0 {
entryPoint := s . globalConfiguration . EntryPoints [ opt . EntryPoint ]
if entryPoint == nil {
return nil , fmt . Errorf ( "unknown target entrypoint %q" , srcEntryPointName )
}
log . Debugf ( "Creating entry point redirect %s -> %s" , srcEntryPointName , opt . EntryPoint )
return redirect . NewEntryPointHandler ( entryPoint , opt . Permanent )
2016-01-13 21:45:49 +00:00
}
2017-12-13 16:02:04 +00:00
2017-12-15 10:48:03 +00:00
// regex redirect
2018-01-31 18:10:04 +00:00
redirection , err := redirect . NewRegexHandler ( opt . Regex , opt . Replacement , opt . Permanent )
2016-01-29 19:34:17 +00:00
if err != nil {
return nil , err
}
2018-01-31 18:10:04 +00:00
log . Debugf ( "Creating regex redirect %s -> %s -> %s" , srcEntryPointName , opt . Regex , opt . Replacement )
2017-12-13 16:02:04 +00:00
2018-01-31 18:10:04 +00:00
return redirection , nil
2017-11-18 12:50:03 +00:00
}
2017-11-24 18:18:03 +00:00
func ( s * Server ) buildDefaultHTTPRouter ( ) * mux . Router {
2016-01-29 19:34:17 +00:00
router := mux . NewRouter ( )
2018-01-11 09:04:03 +00:00
router . NotFoundHandler = s . wrapHTTPHandlerWithAccessLog ( http . HandlerFunc ( notFoundHandler ) , "backend not found" )
2016-02-26 14:29:53 +00:00
router . StrictSlash ( true )
2016-09-20 14:43:09 +00:00
router . SkipClean ( true )
2016-01-29 19:34:17 +00:00
return router
2016-01-13 21:45:49 +00:00
}
2016-02-19 22:55:23 +00:00
2017-08-25 14:10:03 +00:00
func parseHealthCheckOptions ( lb healthcheck . LoadBalancer , backend string , hc * types . HealthCheck , hcConfig * configuration . HealthCheckConfig ) * healthcheck . Options {
2017-05-15 07:02:32 +00:00
if hc == nil || hc . Path == "" || hcConfig == nil {
2017-03-15 18:16:06 +00:00
return nil
}
2017-03-24 08:36:33 +00:00
interval := time . Duration ( hcConfig . Interval )
2017-03-15 18:16:06 +00:00
if hc . Interval != "" {
intervalOverride , err := time . ParseDuration ( hc . Interval )
switch {
case err != nil :
log . Errorf ( "Illegal healthcheck interval for backend '%s': %s" , backend , err )
case intervalOverride <= 0 :
log . Errorf ( "Healthcheck interval smaller than zero for backend '%s', backend" , backend )
default :
interval = intervalOverride
}
}
return & healthcheck . Options {
Path : hc . Path ,
2017-09-18 13:50:03 +00:00
Port : hc . Port ,
2017-03-15 18:16:06 +00:00
Interval : interval ,
LB : lb ,
}
}
2018-02-26 14:34:04 +00:00
func getRoute ( serverRoute * types . ServerRoute , route * types . Route ) error {
rules := rules . Rules { Route : serverRoute }
2016-03-27 00:05:17 +00:00
newRoute , err := rules . Parse ( route . Rule )
if err != nil {
return err
2016-02-26 14:29:53 +00:00
}
2018-02-26 14:34:04 +00:00
newRoute . Priority ( serverRoute . Route . GetPriority ( ) + len ( route . Rule ) )
serverRoute . Route = newRoute
2016-03-27 00:05:17 +00:00
return nil
2016-02-26 14:29:53 +00:00
}
2016-02-19 22:55:23 +00:00
func sortedFrontendNamesForConfig ( configuration * types . Configuration ) [ ] string {
2018-01-10 16:48:04 +00:00
var keys [ ] string
2016-02-19 22:55:23 +00:00
for key := range configuration . Frontends {
keys = append ( keys , key )
}
sort . Strings ( keys )
return keys
}
2017-05-10 22:34:47 +00:00
2018-01-11 09:04:03 +00:00
func configureFrontends ( frontends map [ string ] * types . Frontend , defaultEntrypoints [ ] string ) {
2017-05-10 22:34:47 +00:00
for _ , frontend := range frontends {
// default endpoints if not defined in frontends
if len ( frontend . EntryPoints ) == 0 {
2018-01-11 09:04:03 +00:00
frontend . EntryPoints = defaultEntrypoints
2017-05-10 22:34:47 +00:00
}
}
}
2018-01-11 09:04:03 +00:00
func configureBackends ( backends map [ string ] * types . Backend ) {
2017-10-16 15:38:03 +00:00
for backendName := range backends {
backend := backends [ backendName ]
2017-10-10 09:10:02 +00:00
if backend . LoadBalancer != nil && backend . LoadBalancer . Sticky {
2017-10-12 15:50:03 +00:00
log . Warnf ( "Deprecated configuration found: %s. Please use %s." , "backend.LoadBalancer.Sticky" , "backend.LoadBalancer.Stickiness" )
2017-10-10 09:10:02 +00:00
}
2017-05-10 22:34:47 +00:00
_ , err := types . NewLoadBalancerMethod ( backend . LoadBalancer )
2017-10-10 09:10:02 +00:00
if err == nil {
if backend . LoadBalancer != nil && backend . LoadBalancer . Stickiness == nil && backend . LoadBalancer . Sticky {
2017-10-12 15:50:03 +00:00
backend . LoadBalancer . Stickiness = & types . Stickiness {
CookieName : "_TRAEFIK_BACKEND" ,
}
2017-10-10 09:10:02 +00:00
}
} else {
2017-05-10 22:34:47 +00:00
log . Debugf ( "Validation of load balancer method for backend %s failed: %s. Using default method wrr." , backendName , err )
2017-10-10 09:10:02 +00:00
var stickiness * types . Stickiness
2017-05-10 22:34:47 +00:00
if backend . LoadBalancer != nil {
2017-10-12 15:50:03 +00:00
if backend . LoadBalancer . Stickiness == nil {
if backend . LoadBalancer . Sticky {
stickiness = & types . Stickiness {
CookieName : "_TRAEFIK_BACKEND" ,
}
2017-10-10 09:10:02 +00:00
}
2017-10-12 15:50:03 +00:00
} else {
stickiness = backend . LoadBalancer . Stickiness
2017-10-10 09:10:02 +00:00
}
2017-05-10 22:34:47 +00:00
}
backend . LoadBalancer = & types . LoadBalancer {
2017-10-10 09:10:02 +00:00
Method : "wrr" ,
Stickiness : stickiness ,
2017-05-10 22:34:47 +00:00
}
}
}
}
2017-04-18 06:22:06 +00:00
2018-01-26 10:58:03 +00:00
func registerMetricClients ( metricsConfig * types . Metrics ) metrics . Registry {
if metricsConfig == nil {
return metrics . NewVoidRegistry ( )
}
2017-07-20 22:26:43 +00:00
2018-01-31 18:10:04 +00:00
var registries [ ] metrics . Registry
2017-08-23 18:46:03 +00:00
if metricsConfig . Prometheus != nil {
registries = append ( registries , metrics . RegisterPrometheus ( metricsConfig . Prometheus ) )
log . Debug ( "Configured Prometheus metrics" )
}
if metricsConfig . Datadog != nil {
registries = append ( registries , metrics . RegisterDatadog ( metricsConfig . Datadog ) )
log . Debugf ( "Configured DataDog metrics pushing to %s once every %s" , metricsConfig . Datadog . Address , metricsConfig . Datadog . PushInterval )
}
if metricsConfig . StatsD != nil {
registries = append ( registries , metrics . RegisterStatsd ( metricsConfig . StatsD ) )
log . Debugf ( "Configured StatsD metrics pushing to %s once every %s" , metricsConfig . StatsD . Address , metricsConfig . StatsD . PushInterval )
2017-04-18 06:22:06 +00:00
}
2017-11-08 14:14:03 +00:00
if metricsConfig . InfluxDB != nil {
registries = append ( registries , metrics . RegisterInfluxDB ( metricsConfig . InfluxDB ) )
log . Debugf ( "Configured InfluxDB metrics pushing to %s once every %s" , metricsConfig . InfluxDB . Address , metricsConfig . InfluxDB . PushInterval )
}
2017-04-18 06:22:06 +00:00
2018-01-26 10:58:03 +00:00
return metrics . NewMultiRegistry ( registries )
2017-07-20 22:26:43 +00:00
}
2017-08-23 18:46:03 +00:00
func stopMetricsClients ( ) {
metrics . StopDatadog ( )
metrics . StopStatsd ( )
2017-11-08 14:14:03 +00:00
metrics . StopInfluxDB ( )
2017-07-20 22:26:43 +00:00
}
2017-11-24 18:18:03 +00:00
func ( s * Server ) buildRateLimiter ( handler http . Handler , rlConfig * types . RateLimit ) ( http . Handler , error ) {
2017-09-09 11:36:03 +00:00
extractFunc , err := utils . NewExtractor ( rlConfig . ExtractorFunc )
if err != nil {
return nil , err
}
log . Debugf ( "Creating load-balancer rate limiter" )
rateSet := ratelimit . NewRateSet ( )
for _ , rate := range rlConfig . RateSet {
if err := rateSet . Add ( time . Duration ( rate . Period ) , rate . Average , rate . Burst ) ; err != nil {
return nil , err
}
}
2018-01-10 16:48:04 +00:00
rateLimiter , err := ratelimit . New ( handler , extractFunc , rateSet )
return s . tracingMiddleware . NewHTTPHandlerWrapper ( "Rate limit" , rateLimiter , false ) , err
2017-09-09 11:36:03 +00:00
}
2017-11-24 18:18:03 +00:00
func ( s * Server ) buildRetryMiddleware ( handler http . Handler , globalConfig configuration . GlobalConfiguration , countServers int , backendName string ) http . Handler {
2017-08-28 10:50:02 +00:00
retryListeners := middlewares . RetryListeners { }
2017-11-24 18:18:03 +00:00
if s . metricsRegistry . IsEnabled ( ) {
retryListeners = append ( retryListeners , middlewares . NewMetricsRetryListener ( s . metricsRegistry , backendName ) )
2017-08-28 10:50:02 +00:00
}
2017-11-24 18:18:03 +00:00
if s . accessLoggerMiddleware != nil {
2017-08-28 10:50:02 +00:00
retryListeners = append ( retryListeners , & accesslog . SaveRetries { } )
}
retryAttempts := countServers
2017-04-18 06:22:06 +00:00
if globalConfig . Retry . Attempts > 0 {
2017-08-28 10:50:02 +00:00
retryAttempts = globalConfig . Retry . Attempts
2017-04-18 06:22:06 +00:00
}
2017-08-28 10:50:02 +00:00
log . Debugf ( "Creating retries max attempts %d" , retryAttempts )
2017-04-18 06:22:06 +00:00
2018-01-10 16:48:04 +00:00
return s . tracingMiddleware . NewHTTPHandlerWrapper ( "Retry" , middlewares . NewRetry ( retryAttempts , handler , retryListeners ) , false )
2017-04-18 06:22:06 +00:00
}
2018-01-11 09:04:03 +00:00
func ( s * Server ) wrapNegroniHandlerWithAccessLog ( handler negroni . Handler , frontendName string ) negroni . Handler {
if s . accessLoggerMiddleware != nil {
saveBackend := accesslog . NewSaveNegroniBackend ( handler , "Træfik" )
saveFrontend := accesslog . NewSaveNegroniFrontend ( saveBackend , frontendName )
return saveFrontend
}
return handler
}
func ( s * Server ) wrapHTTPHandlerWithAccessLog ( handler http . Handler , frontendName string ) http . Handler {
if s . accessLoggerMiddleware != nil {
saveBackend := accesslog . NewSaveBackend ( handler , "Træfik" )
saveFrontend := accesslog . NewSaveFrontend ( saveBackend , frontendName )
return saveFrontend
}
return handler
}
2018-01-31 14:32:04 +00:00
func ( s * Server ) buildBufferingMiddleware ( handler http . Handler , config * types . Buffering ) ( http . Handler , error ) {
log . Debugf ( "Setting up buffering: request limits: %d (mem), %d (max), response limits: %d (mem), %d (max) with retry: '%s'" ,
config . MemRequestBodyBytes , config . MaxRequestBodyBytes , config . MemResponseBodyBytes ,
config . MaxResponseBodyBytes , config . RetryExpression )
return buffer . New (
handler ,
buffer . MemRequestBodyBytes ( config . MemRequestBodyBytes ) ,
buffer . MaxRequestBodyBytes ( config . MaxRequestBodyBytes ) ,
buffer . MemResponseBodyBytes ( config . MemResponseBodyBytes ) ,
buffer . MaxResponseBodyBytes ( config . MaxResponseBodyBytes ) ,
2018-02-12 16:24:03 +00:00
buffer . CondSetter ( len ( config . RetryExpression ) > 0 , buffer . Retry ( config . RetryExpression ) ) ,
2018-01-31 14:32:04 +00:00
)
}
2018-03-01 15:42:04 +00:00
func buildModifyResponse ( secure * secure . Secure , header * middlewares . HeaderStruct ) func ( res * http . Response ) error {
return func ( res * http . Response ) error {
if secure != nil {
err := secure . ModifyResponseHeaders ( res )
if err != nil {
return err
}
}
if header != nil {
err := header . ModifyResponseHeaders ( res )
if err != nil {
return err
}
}
return nil
}
}