2016-01-14 00:45:49 +03:00
/ *
Copyright
* /
package main
import (
2016-08-16 18:26:10 +03:00
"context"
2016-01-14 00:45:49 +03:00
"crypto/tls"
2016-06-15 23:38:40 +03:00
"crypto/x509"
2016-01-29 22:34:17 +03:00
"encoding/json"
2016-01-14 00:45:49 +03:00
"errors"
2016-06-15 23:38:40 +03:00
"io/ioutil"
2016-02-26 17:29:53 +03:00
"net/http"
"net/url"
"os"
"os/signal"
"reflect"
"regexp"
"sort"
"syscall"
"time"
2016-02-20 01:55:23 +03:00
2016-01-14 00:45:49 +03:00
"github.com/codegangsta/negroni"
2016-06-03 18:58:33 +03:00
"github.com/containous/mux"
2016-08-18 14:03:10 +03:00
"github.com/containous/traefik/cluster"
2016-08-18 15:20:11 +03:00
"github.com/containous/traefik/log"
2016-02-24 18:43:39 +03:00
"github.com/containous/traefik/middlewares"
"github.com/containous/traefik/provider"
2016-03-31 19:57:08 +03:00
"github.com/containous/traefik/safe"
2016-02-24 18:43:39 +03:00
"github.com/containous/traefik/types"
2016-01-14 00:45:49 +03:00
"github.com/mailgun/manners"
2016-04-13 21:36:23 +03:00
"github.com/streamrail/concurrent-map"
2016-06-15 20:07:33 +03:00
"github.com/vulcand/oxy/cbreaker"
"github.com/vulcand/oxy/connlimit"
"github.com/vulcand/oxy/forward"
"github.com/vulcand/oxy/roundrobin"
"github.com/vulcand/oxy/utils"
2016-01-14 00:45:49 +03:00
)
2016-04-20 21:54:57 +03:00
var oxyLogger = & OxyLogger { }
2016-01-14 00:46:44 +03:00
2016-01-14 00:45:49 +03:00
// Server is the reverse-proxy/load-balancer engine
type Server struct {
2016-02-25 20:30:13 +03:00
serverEntryPoints serverEntryPoints
2016-01-14 00:45:49 +03:00
configurationChan chan types . ConfigMessage
2016-01-29 22:34:17 +03:00
configurationValidatedChan chan types . ConfigMessage
signals chan os . Signal
2016-01-14 00:45:49 +03:00
stopChan chan bool
providers [ ] provider . Provider
2016-04-13 21:36:23 +03:00
currentConfigurations safe . Safe
2016-01-14 00:45:49 +03:00
globalConfiguration GlobalConfiguration
loggerMiddleware * middlewares . Logger
2016-08-18 14:03:10 +03:00
routinesPool * safe . Pool
leadership * cluster . Leadership
2016-01-14 00:45:49 +03:00
}
2016-02-25 20:30:13 +03:00
type serverEntryPoints map [ string ] * serverEntryPoint
2016-01-29 22:34:17 +03:00
type serverEntryPoint struct {
httpServer * manners . GracefulServer
2016-03-04 13:32:23 +03:00
httpRouter * middlewares . HandlerSwitcher
2016-01-29 22:34:17 +03:00
}
2016-03-27 03:05:17 +03:00
type serverRoute struct {
2016-04-06 14:06:31 +03:00
route * mux . Route
stripPrefixes [ ] string
2016-12-02 15:40:18 +03:00
addPrefix string
2016-03-27 03:05:17 +03:00
}
2016-01-14 00:45:49 +03:00
// NewServer returns an initialized Server.
func NewServer ( globalConfiguration GlobalConfiguration ) * Server {
server := new ( Server )
2016-02-25 20:30:13 +03:00
server . serverEntryPoints = make ( map [ string ] * serverEntryPoint )
2016-05-19 21:09:01 +03:00
server . configurationChan = make ( chan types . ConfigMessage , 100 )
server . configurationValidatedChan = make ( chan types . ConfigMessage , 100 )
2016-01-29 22:34:17 +03:00
server . signals = make ( chan os . Signal , 1 )
2016-04-13 21:36:23 +03:00
server . stopChan = make ( chan bool , 1 )
2016-01-14 00:45:49 +03:00
server . providers = [ ] provider . Provider { }
2016-01-29 22:34:17 +03:00
signal . Notify ( server . signals , syscall . SIGINT , syscall . SIGTERM )
2016-04-13 21:36:23 +03:00
currentConfigurations := make ( configs )
server . currentConfigurations . Set ( currentConfigurations )
2016-01-14 00:45:49 +03:00
server . globalConfiguration = globalConfiguration
server . loggerMiddleware = middlewares . NewLogger ( globalConfiguration . AccessLogsFile )
2016-08-18 14:03:10 +03:00
server . routinesPool = safe . NewPool ( context . Background ( ) )
if globalConfiguration . Cluster != nil {
// leadership creation if cluster mode
server . leadership = cluster . NewLeadership ( server . routinesPool . Ctx ( ) , globalConfiguration . Cluster )
}
2016-01-14 00:45:49 +03:00
return server
}
2016-10-25 18:59:39 +03:00
// Start starts the server.
2016-01-14 00:45:49 +03:00
func ( server * Server ) Start ( ) {
2016-02-25 20:30:13 +03:00
server . startHTTPServers ( )
2016-08-18 15:20:11 +03:00
server . startLeadership ( )
2016-04-13 21:36:23 +03:00
server . routinesPool . Go ( func ( stop chan bool ) {
server . listenProviders ( stop )
2016-03-31 19:57:08 +03:00
} )
2016-04-13 21:36:23 +03:00
server . routinesPool . Go ( func ( stop chan bool ) {
server . listenConfigurations ( stop )
2016-03-31 19:57:08 +03:00
} )
2016-01-14 00:45:49 +03:00
server . configureProviders ( )
server . startProviders ( )
go server . listenSignals ( )
2016-10-25 18:59:39 +03:00
}
// Wait blocks until server is shutted down.
func ( server * Server ) Wait ( ) {
2016-01-14 00:45:49 +03:00
<- server . stopChan
}
// Stop stops the server
func ( server * Server ) Stop ( ) {
2016-07-13 18:50:57 +03:00
for serverEntryPointName , serverEntryPoint := range server . serverEntryPoints {
ctx , cancel := context . WithTimeout ( context . Background ( ) , time . Duration ( server . globalConfiguration . GraceTimeOut ) * time . Second )
go func ( ) {
log . Debugf ( "Waiting %d seconds before killing connections on entrypoint %s..." , 30 , serverEntryPointName )
serverEntryPoint . httpServer . BlockingClose ( )
cancel ( )
} ( )
<- ctx . Done ( )
2016-01-29 22:34:17 +03:00
}
2016-01-14 00:45:49 +03:00
server . stopChan <- true
}
// Close destroys the server
func ( server * Server ) Close ( ) {
2016-07-13 18:50:57 +03:00
ctx , cancel := context . WithTimeout ( context . Background ( ) , time . Duration ( server . globalConfiguration . GraceTimeOut ) * time . Second )
go func ( ctx context . Context ) {
<- ctx . Done ( )
if ctx . Err ( ) == context . Canceled {
return
} else if ctx . Err ( ) == context . DeadlineExceeded {
2016-08-18 15:20:11 +03:00
log . Warnf ( "Timeout while stopping traefik, killing instance ✝" )
2016-07-13 18:50:57 +03:00
os . Exit ( 1 )
}
} ( ctx )
2016-08-18 14:03:10 +03:00
server . stopLeadership ( )
2016-11-15 22:14:11 +03:00
server . routinesPool . Cleanup ( )
2016-01-14 00:46:44 +03:00
close ( server . configurationChan )
2016-01-29 22:34:17 +03:00
close ( server . configurationValidatedChan )
2016-07-14 11:22:18 +03:00
signal . Stop ( server . signals )
2016-01-29 22:34:17 +03:00
close ( server . signals )
2016-01-14 00:46:44 +03:00
close ( server . stopChan )
server . loggerMiddleware . Close ( )
2016-07-13 18:50:57 +03:00
cancel ( )
2016-01-14 00:45:49 +03:00
}
2016-08-18 14:03:10 +03:00
func ( server * Server ) startLeadership ( ) {
if server . leadership != nil {
server . leadership . Participate ( server . routinesPool )
2016-08-18 15:20:11 +03:00
// server.leadership.AddGoCtx(func(ctx context.Context) {
// log.Debugf("Started test routine")
// <-ctx.Done()
// log.Debugf("Stopped test routine")
// })
2016-08-18 14:03:10 +03:00
}
}
func ( server * Server ) stopLeadership ( ) {
if server . leadership != nil {
2016-08-18 15:20:11 +03:00
server . leadership . Stop ( )
2016-08-18 14:03:10 +03:00
}
}
2016-02-25 20:30:13 +03:00
func ( server * Server ) startHTTPServers ( ) {
server . serverEntryPoints = server . buildEntryPoints ( server . globalConfiguration )
for newServerEntryPointName , newServerEntryPoint := range server . serverEntryPoints {
2016-07-21 01:29:00 +03:00
serverMiddlewares := [ ] negroni . Handler { server . loggerMiddleware , metrics }
2016-10-21 11:36:07 +03:00
if server . globalConfiguration . Web != nil && server . globalConfiguration . Web . Statistics != nil {
2016-12-06 17:44:25 +03:00
statsRecorder = middlewares . NewStatsRecorder ( server . globalConfiguration . Web . Statistics . RecentErrors )
2016-10-21 11:36:07 +03:00
serverMiddlewares = append ( serverMiddlewares , statsRecorder )
}
2016-07-21 01:29:00 +03:00
if server . globalConfiguration . EntryPoints [ newServerEntryPointName ] . Auth != nil {
authMiddleware , err := middlewares . NewAuthenticator ( server . globalConfiguration . EntryPoints [ newServerEntryPointName ] . Auth )
if err != nil {
log . Fatal ( "Error starting server: " , err )
}
serverMiddlewares = append ( serverMiddlewares , authMiddleware )
}
2016-09-29 00:07:06 +03:00
if server . globalConfiguration . EntryPoints [ newServerEntryPointName ] . Compress {
serverMiddlewares = append ( serverMiddlewares , & middlewares . Compress { } )
}
2016-07-21 01:29:00 +03:00
newsrv , err := server . prepareServer ( newServerEntryPointName , newServerEntryPoint . httpRouter , server . globalConfiguration . EntryPoints [ newServerEntryPointName ] , nil , serverMiddlewares ... )
2016-02-25 20:30:13 +03:00
if err != nil {
log . Fatal ( "Error preparing server: " , err )
}
serverEntryPoint := server . serverEntryPoints [ newServerEntryPointName ]
serverEntryPoint . httpServer = newsrv
go server . startServer ( serverEntryPoint . httpServer , server . globalConfiguration )
}
}
2016-04-13 21:36:23 +03:00
func ( server * Server ) listenProviders ( stop chan bool ) {
lastReceivedConfiguration := safe . New ( time . Unix ( 0 , 0 ) )
lastConfigs := cmap . New ( )
2016-01-14 00:45:49 +03:00
for {
2016-04-13 21:36:23 +03:00
select {
case <- stop :
return
case configMsg , ok := <- server . configurationChan :
if ! ok {
return
}
2016-06-22 19:31:14 +03:00
server . defaultConfigurationValues ( configMsg . Configuration )
currentConfigurations := server . currentConfigurations . Get ( ) . ( configs )
2016-04-13 21:36:23 +03:00
jsonConf , _ := json . Marshal ( configMsg . Configuration )
log . Debugf ( "Configuration received from provider %s: %s" , configMsg . ProviderName , string ( jsonConf ) )
2016-06-22 19:31:14 +03:00
if configMsg . Configuration == nil || configMsg . Configuration . Backends == nil && configMsg . Configuration . Frontends == nil {
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 )
2016-04-13 21:36:23 +03:00
} else {
2016-06-22 19:31:14 +03:00
lastConfigs . Set ( configMsg . ProviderName , & configMsg )
lastReceivedConfigurationValue := lastReceivedConfiguration . Get ( ) . ( time . Time )
if time . Now ( ) . After ( lastReceivedConfigurationValue . Add ( time . Duration ( server . globalConfiguration . ProvidersThrottleDuration ) ) ) {
2016-10-21 17:02:18 +03:00
log . Debugf ( "Last %s config received more than %s, OK" , configMsg . ProviderName , server . globalConfiguration . ProvidersThrottleDuration . String ( ) )
2016-06-22 19:31:14 +03:00
// last config received more than n s ago
server . configurationValidatedChan <- configMsg
} else {
2016-10-21 17:02:18 +03:00
log . Debugf ( "Last %s config received less than %s, waiting..." , configMsg . ProviderName , server . globalConfiguration . ProvidersThrottleDuration . String ( ) )
2016-06-22 19:31:14 +03:00
safe . Go ( func ( ) {
<- time . After ( server . globalConfiguration . ProvidersThrottleDuration )
lastReceivedConfigurationValue := lastReceivedConfiguration . Get ( ) . ( time . Time )
if time . Now ( ) . After ( lastReceivedConfigurationValue . Add ( time . Duration ( server . globalConfiguration . ProvidersThrottleDuration ) ) ) {
log . Debugf ( "Waited for %s config, OK" , configMsg . ProviderName )
if lastConfig , ok := lastConfigs . Get ( configMsg . ProviderName ) ; ok {
server . configurationValidatedChan <- * lastConfig . ( * types . ConfigMessage )
}
2016-04-13 21:36:23 +03:00
}
2016-06-22 19:31:14 +03:00
} )
}
lastReceivedConfiguration . Set ( time . Now ( ) )
2016-04-13 21:36:23 +03:00
}
2016-06-22 19:31:14 +03:00
}
}
}
func ( server * Server ) defaultConfigurationValues ( configuration * types . Configuration ) {
if configuration == nil || configuration . Frontends == nil {
return
}
for _ , frontend := range configuration . Frontends {
// default endpoints if not defined in frontends
if len ( frontend . EntryPoints ) == 0 {
frontend . EntryPoints = server . globalConfiguration . DefaultEntryPoints
2016-01-14 00:45:49 +03:00
}
}
2016-07-04 20:30:32 +03:00
for backendName , backend := range configuration . Backends {
_ , err := types . NewLoadBalancerMethod ( backend . LoadBalancer )
if err != nil {
2016-09-23 19:27:01 +03:00
log . Debugf ( "Load balancer method '%+v' for backend %s: %v. Using default wrr." , backend . LoadBalancer , backendName , err )
2016-07-04 20:30:32 +03:00
backend . LoadBalancer = & types . LoadBalancer { Method : "wrr" }
}
}
2016-01-14 00:45:49 +03:00
}
2016-04-13 21:36:23 +03:00
func ( server * Server ) listenConfigurations ( stop chan bool ) {
2016-01-14 00:45:49 +03:00
for {
2016-04-13 21:36:23 +03:00
select {
case <- stop :
return
case configMsg , ok := <- server . configurationValidatedChan :
if ! ok {
return
2016-01-14 00:45:49 +03:00
}
2016-04-13 21:36:23 +03:00
currentConfigurations := server . currentConfigurations . Get ( ) . ( configs )
2016-06-22 19:31:14 +03:00
// Copy configurations to new map so we don't change current if LoadConfig fails
newConfigurations := make ( configs )
for k , v := range currentConfigurations {
newConfigurations [ k ] = v
}
newConfigurations [ configMsg . ProviderName ] = configMsg . Configuration
newServerEntryPoints , err := server . loadConfig ( newConfigurations , server . globalConfiguration )
if err == nil {
for newServerEntryPointName , newServerEntryPoint := range newServerEntryPoints {
server . serverEntryPoints [ newServerEntryPointName ] . httpRouter . UpdateHandler ( newServerEntryPoint . httpRouter . GetHandler ( ) )
log . Infof ( "Server configuration reloaded on %s" , server . serverEntryPoints [ newServerEntryPointName ] . httpServer . Addr )
2016-04-13 21:36:23 +03:00
}
2016-06-22 19:31:14 +03:00
server . currentConfigurations . Set ( newConfigurations )
2016-08-05 21:42:45 +03:00
server . postLoadConfig ( )
2016-06-22 19:31:14 +03:00
} else {
log . Error ( "Error loading new configuration, aborted " , err )
2016-01-14 00:45:49 +03:00
}
}
}
}
2016-08-05 21:42:45 +03:00
func ( server * Server ) postLoadConfig ( ) {
2016-08-18 15:20:11 +03:00
if server . globalConfiguration . ACME == nil {
return
}
if server . leadership != nil && ! server . leadership . IsLeader ( ) {
return
}
if server . globalConfiguration . ACME . OnHostRule {
2016-08-05 21:42:45 +03:00
currentConfigurations := server . currentConfigurations . Get ( ) . ( configs )
for _ , configuration := range currentConfigurations {
for _ , frontend := range configuration . Frontends {
2017-01-05 14:32:56 +03:00
// check if one of the frontend entrypoints is configured with TLS
TLSEnabled := false
for _ , entrypoint := range frontend . EntryPoints {
if server . globalConfiguration . EntryPoints [ entrypoint ] . TLS != nil {
TLSEnabled = true
break
2016-08-05 21:42:45 +03:00
}
}
2017-01-05 14:32:56 +03:00
if TLSEnabled {
for _ , route := range frontend . Routes {
rules := Rules { }
domains , err := rules . ParseDomains ( route . Rule )
if err != nil {
log . Errorf ( "Error parsing domains: %v" , err )
} else {
server . globalConfiguration . ACME . LoadCertificateForDomains ( domains )
}
}
}
2016-08-05 21:42:45 +03:00
}
}
}
}
2016-01-14 00:45:49 +03:00
func ( server * Server ) configureProviders ( ) {
// configure providers
if server . globalConfiguration . Docker != nil {
server . providers = append ( server . providers , server . globalConfiguration . Docker )
}
if server . globalConfiguration . Marathon != nil {
server . providers = append ( server . providers , server . globalConfiguration . Marathon )
}
if server . globalConfiguration . File != nil {
server . providers = append ( server . providers , server . globalConfiguration . File )
}
if server . globalConfiguration . Web != nil {
server . globalConfiguration . Web . server = server
server . providers = append ( server . providers , server . globalConfiguration . Web )
}
if server . globalConfiguration . Consul != nil {
server . providers = append ( server . providers , server . globalConfiguration . Consul )
}
2016-02-02 20:03:40 +03:00
if server . globalConfiguration . ConsulCatalog != nil {
server . providers = append ( server . providers , server . globalConfiguration . ConsulCatalog )
}
2016-01-14 00:45:49 +03:00
if server . globalConfiguration . Etcd != nil {
server . providers = append ( server . providers , server . globalConfiguration . Etcd )
}
if server . globalConfiguration . Zookeeper != nil {
server . providers = append ( server . providers , server . globalConfiguration . Zookeeper )
}
if server . globalConfiguration . Boltdb != nil {
server . providers = append ( server . providers , server . globalConfiguration . Boltdb )
}
2016-02-08 23:57:32 +03:00
if server . globalConfiguration . Kubernetes != nil {
server . providers = append ( server . providers , server . globalConfiguration . Kubernetes )
}
2016-07-20 12:56:14 +03:00
if server . globalConfiguration . Mesos != nil {
server . providers = append ( server . providers , server . globalConfiguration . Mesos )
}
2016-08-31 23:43:05 +03:00
if server . globalConfiguration . Eureka != nil {
server . providers = append ( server . providers , server . globalConfiguration . Eureka )
}
2016-01-14 00:45:49 +03:00
}
func ( server * Server ) startProviders ( ) {
// start providers
for _ , provider := range server . providers {
2016-01-29 22:34:17 +03:00
jsonConf , _ := json . Marshal ( provider )
log . Infof ( "Starting provider %v %s" , reflect . TypeOf ( provider ) , jsonConf )
2016-01-14 00:45:49 +03:00
currentProvider := provider
2016-03-31 19:57:08 +03:00
safe . Go ( func ( ) {
2016-08-18 14:03:10 +03:00
err := currentProvider . Provide ( server . configurationChan , server . routinesPool , server . globalConfiguration . Constraints )
2016-01-14 00:45:49 +03:00
if err != nil {
log . Errorf ( "Error starting provider %s" , err )
}
2016-03-31 19:57:08 +03:00
} )
2016-01-14 00:45:49 +03:00
}
}
func ( server * Server ) listenSignals ( ) {
2016-01-29 22:34:17 +03:00
sig := <- server . signals
2016-01-14 00:45:49 +03:00
log . Infof ( "I have to go... %+v" , sig )
log . Info ( "Stopping server" )
server . Stop ( )
}
// creates a TLS config that allows terminating HTTPS for multiple domains using SNI
2016-03-21 13:10:18 +03:00
func ( server * Server ) createTLSConfig ( entryPointName string , tlsOption * TLS , router * middlewares . HandlerSwitcher ) ( * tls . Config , error ) {
2016-01-29 22:34:17 +03:00
if tlsOption == nil {
return nil , nil
}
2016-01-14 00:45:49 +03:00
2016-06-27 13:19:14 +03:00
config , err := tlsOption . Certificates . CreateTLSConfig ( )
if err != nil {
return nil , err
2016-03-21 13:10:18 +03:00
}
2016-11-09 19:56:41 +03:00
// ensure http2 enabled
config . NextProtos = [ ] string { "h2" , "http/1.1" }
2016-06-15 23:38:40 +03:00
if len ( tlsOption . ClientCAFiles ) > 0 {
pool := x509 . NewCertPool ( )
for _ , caFile := range tlsOption . ClientCAFiles {
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
config . ClientAuth = tls . RequireAndVerifyClientCert
}
2016-03-21 13:10:18 +03:00
if server . globalConfiguration . ACME != nil {
if _ , ok := server . serverEntryPoints [ server . globalConfiguration . ACME . EntryPoint ] ; ok {
if entryPointName == server . globalConfiguration . ACME . EntryPoint {
checkOnDemandDomain := func ( domain string ) bool {
2016-07-11 17:43:23 +03:00
routeMatch := & mux . RouteMatch { }
router := router . GetHandler ( )
match := router . Match ( & http . Request { URL : & url . URL { } , Host : domain } , routeMatch )
if match && routeMatch . Route != nil {
2016-03-21 13:10:18 +03:00
return true
}
return false
}
2016-08-18 15:20:11 +03:00
if server . leadership == nil {
err := server . globalConfiguration . ACME . CreateLocalConfig ( config , checkOnDemandDomain )
if err != nil {
return nil , err
}
} else {
err := server . globalConfiguration . ACME . CreateClusterConfig ( server . leadership , config , checkOnDemandDomain )
if err != nil {
return nil , err
}
2016-03-21 13:10:18 +03:00
}
}
} else {
return nil , errors . New ( "Unknown entrypoint " + server . globalConfiguration . ACME . EntryPoint + " for ACME configuration" )
}
}
if len ( config . Certificates ) == 0 {
return nil , errors . New ( "No certificates found for TLS entrypoint " + entryPointName )
2016-01-14 00:45:49 +03:00
}
// BuildNameToCertificate parses the CommonName and SubjectAlternateName fields
// in each certificate and populates the config.NameToCertificate map.
config . BuildNameToCertificate ( )
2016-09-20 09:06:06 +03:00
//Set the minimum TLS version if set in the config TOML
if minConst , exists := minVersion [ server . globalConfiguration . EntryPoints [ entryPointName ] . TLS . MinVersion ] ; exists {
config . PreferServerCipherSuites = true
config . MinVersion = minConst
}
//Set the list of CipherSuites if set in the config TOML
if server . globalConfiguration . EntryPoints [ entryPointName ] . TLS . CipherSuites != nil {
//if our list of CipherSuites is defined in the entrypoint config, we can re-initilize the suites list as empty
config . CipherSuites = make ( [ ] uint16 , 0 )
for _ , cipher := range server . globalConfiguration . EntryPoints [ entryPointName ] . TLS . CipherSuites {
if cipherConst , exists := cipherSuites [ cipher ] ; exists {
config . CipherSuites = append ( config . CipherSuites , cipherConst )
} else {
//CipherSuite listed in the toml does not exist in our listed
return nil , errors . New ( "Invalid CipherSuite: " + cipher )
}
}
}
2016-01-14 00:45:49 +03:00
return config , nil
}
func ( server * Server ) startServer ( srv * manners . GracefulServer , globalConfiguration GlobalConfiguration ) {
2016-02-25 20:30:13 +03:00
log . Infof ( "Starting server on %s" , srv . Addr )
2016-01-14 00:45:49 +03:00
if srv . TLSConfig != nil {
2016-02-25 20:30:13 +03:00
if err := srv . ListenAndServeTLSWithConfig ( srv . TLSConfig ) ; err != nil {
2016-01-14 00:45:49 +03:00
log . Fatal ( "Error creating server: " , err )
}
} else {
2016-02-25 20:30:13 +03:00
if err := srv . ListenAndServe ( ) ; err != nil {
2016-01-14 00:45:49 +03:00
log . Fatal ( "Error creating server: " , err )
}
}
log . Info ( "Server stopped" )
}
2016-03-21 13:10:18 +03:00
func ( server * Server ) prepareServer ( entryPointName string , router * middlewares . HandlerSwitcher , entryPoint * EntryPoint , oldServer * manners . GracefulServer , middlewares ... negroni . Handler ) ( * manners . GracefulServer , error ) {
log . Infof ( "Preparing server %s %+v" , entryPointName , entryPoint )
2016-01-14 00:45:49 +03:00
// middlewares
var negroni = negroni . New ( )
for _ , middleware := range middlewares {
negroni . Use ( middleware )
}
negroni . UseHandler ( router )
2016-03-21 13:10:18 +03:00
tlsConfig , err := server . createTLSConfig ( entryPointName , entryPoint . TLS , router )
2016-01-14 00:45:49 +03:00
if err != nil {
2017-01-12 13:04:11 +03:00
log . Errorf ( "Error creating TLS config: %s" , err )
2016-01-14 00:45:49 +03:00
return nil , err
}
if oldServer == nil {
return manners . NewWithServer (
& http . Server {
2016-01-29 22:34:17 +03:00
Addr : entryPoint . Address ,
2016-01-14 00:45:49 +03:00
Handler : negroni ,
TLSConfig : tlsConfig ,
} ) , nil
}
gracefulServer , err := oldServer . HijackListener ( & http . Server {
2016-01-29 22:34:17 +03:00
Addr : entryPoint . Address ,
2016-01-14 00:45:49 +03:00
Handler : negroni ,
TLSConfig : tlsConfig ,
} , tlsConfig )
if err != nil {
2017-01-12 13:04:11 +03:00
log . Errorf ( "Error hijacking server: %s" , err )
2016-01-14 00:45:49 +03:00
return nil , err
}
return gracefulServer , nil
}
2016-02-25 20:30:13 +03:00
func ( server * Server ) buildEntryPoints ( globalConfiguration GlobalConfiguration ) map [ string ] * serverEntryPoint {
serverEntryPoints := make ( map [ string ] * serverEntryPoint )
2016-01-29 22:34:17 +03:00
for entryPointName := range globalConfiguration . EntryPoints {
router := server . buildDefaultHTTPRouter ( )
2016-02-25 20:30:13 +03:00
serverEntryPoints [ entryPointName ] = & serverEntryPoint {
2016-03-04 13:32:23 +03:00
httpRouter : middlewares . NewHandlerSwitcher ( router ) ,
2016-01-29 22:34:17 +03:00
}
}
return serverEntryPoints
}
2016-01-14 00:45:49 +03:00
// LoadConfig returns a new gorilla.mux Route from the specified global configuration and the dynamic
// provider configurations.
2016-02-25 20:30:13 +03:00
func ( server * Server ) loadConfig ( configurations configs , globalConfiguration GlobalConfiguration ) ( map [ string ] * serverEntryPoint , error ) {
2016-01-29 22:34:17 +03:00
serverEntryPoints := server . buildEntryPoints ( globalConfiguration )
redirectHandlers := make ( map [ string ] http . Handler )
2016-01-14 00:45:49 +03:00
backends := map [ string ] http . Handler { }
2016-04-20 02:45:59 +03:00
backend2FrontendMap := map [ string ] string { }
2016-01-14 00:45:49 +03:00
for _ , configuration := range configurations {
2016-02-20 01:55:23 +03:00
frontendNames := sortedFrontendNamesForConfig ( configuration )
2016-06-20 16:19:52 +03:00
frontend :
2016-02-20 01:55:23 +03:00
for _ , frontendName := range frontendNames {
frontend := configuration . Frontends [ frontendName ]
2016-01-29 22:34:17 +03:00
log . Debugf ( "Creating frontend %s" , frontendName )
2016-07-04 20:30:32 +03:00
fwd , err := forward . New ( forward . Logger ( oxyLogger ) , forward . PassHostHeader ( frontend . PassHostHeader ) )
if err != nil {
log . Errorf ( "Error creating forwarder for frontend %s: %v" , frontendName , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
2016-04-20 02:45:59 +03:00
saveBackend := middlewares . NewSaveBackend ( fwd )
2016-03-22 03:32:02 +03:00
if len ( frontend . EntryPoints ) == 0 {
2016-06-20 16:19:52 +03:00
log . Errorf ( "No entrypoint defined for frontend %s, defaultEntryPoints:%s" , frontendName , globalConfiguration . DefaultEntryPoints )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-03-22 03:32:02 +03:00
}
2016-01-29 22:34:17 +03:00
for _ , entryPointName := range frontend . EntryPoints {
log . Debugf ( "Wiring frontend %s to entryPoint %s" , frontendName , entryPointName )
if _ , ok := serverEntryPoints [ entryPointName ] ; ! ok {
2016-06-20 16:19:52 +03:00
log . Errorf ( "Undefined entrypoint '%s' for frontend %s" , entryPointName , frontendName )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-01-14 00:45:49 +03:00
}
2016-03-27 03:05:17 +03:00
newServerRoute := & serverRoute { route : serverEntryPoints [ entryPointName ] . httpRouter . GetHandler ( ) . NewRoute ( ) . Name ( frontendName ) }
2016-01-29 22:34:17 +03:00
for routeName , route := range frontend . Routes {
2016-03-30 20:05:43 +03:00
err := getRoute ( newServerRoute , & route )
2016-01-29 22:34:17 +03:00
if err != nil {
2016-06-20 16:19:52 +03:00
log . Errorf ( "Error creating route for frontend %s: %v" , frontendName , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-01-29 22:34:17 +03:00
}
2016-03-30 20:05:43 +03:00
log . Debugf ( "Creating route %s %s" , routeName , route . Rule )
2016-01-14 00:45:49 +03:00
}
2016-01-29 22:34:17 +03:00
entryPoint := globalConfiguration . EntryPoints [ entryPointName ]
if entryPoint . Redirect != nil {
if redirectHandlers [ entryPointName ] != nil {
2016-03-27 03:05:17 +03:00
newServerRoute . route . Handler ( redirectHandlers [ entryPointName ] )
2016-01-29 22:34:17 +03:00
} else if handler , err := server . loadEntryPointConfig ( entryPointName , entryPoint ) ; err != nil {
2016-06-20 16:19:52 +03:00
log . Errorf ( "Error loading entrypoint configuration for frontend %s: %v" , frontendName , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-01-29 22:34:17 +03:00
} else {
2016-03-27 03:05:17 +03:00
newServerRoute . route . Handler ( handler )
2016-01-29 22:34:17 +03:00
redirectHandlers [ entryPointName ] = handler
2016-01-14 00:45:49 +03:00
}
2016-01-29 22:34:17 +03:00
} else {
if backends [ frontend . Backend ] == nil {
log . Debugf ( "Creating backend %s" , frontend . Backend )
var lb http . Handler
2016-04-20 02:45:59 +03:00
rr , _ := roundrobin . New ( saveBackend )
2016-01-29 22:34:17 +03:00
if configuration . Backends [ frontend . Backend ] == nil {
2016-06-20 16:19:52 +03:00
log . Errorf ( "Undefined backend '%s' for frontend %s" , frontend . Backend , frontendName )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-01-29 22:34:17 +03:00
}
2016-05-13 17:22:11 +03:00
2016-01-29 22:34:17 +03:00
lbMethod , err := types . NewLoadBalancerMethod ( configuration . Backends [ frontend . Backend ] . LoadBalancer )
2016-01-14 00:45:49 +03:00
if err != nil {
2016-07-04 20:30:32 +03:00
log . Errorf ( "Error loading load balancer method '%+v' for frontend %s: %v" , configuration . Backends [ frontend . Backend ] . LoadBalancer , frontendName , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-01-29 22:34:17 +03:00
}
2016-05-13 17:22:11 +03:00
stickysession := configuration . Backends [ frontend . Backend ] . LoadBalancer . Sticky
2016-09-28 17:29:19 +03:00
cookiename := "_TRAEFIK_BACKEND"
2016-08-10 19:19:02 +03:00
var sticky * roundrobin . StickySession
2016-05-26 20:38:55 +03:00
2016-08-10 19:19:02 +03:00
if stickysession {
sticky = roundrobin . NewStickySession ( cookiename )
2016-05-26 20:38:55 +03:00
}
2016-05-13 17:22:11 +03:00
2016-01-29 22:34:17 +03:00
switch lbMethod {
case types . Drr :
log . Debugf ( "Creating load-balancer drr" )
2016-08-10 19:19:02 +03:00
rebalancer , _ := roundrobin . NewRebalancer ( rr , roundrobin . RebalancerLogger ( oxyLogger ) )
2016-05-26 20:38:55 +03:00
if stickysession {
2016-09-28 14:28:20 +03:00
log . Debugf ( "Sticky session with cookie %v" , cookiename )
2016-08-10 19:19:02 +03:00
rebalancer , _ = roundrobin . NewRebalancer ( rr , roundrobin . RebalancerLogger ( oxyLogger ) , roundrobin . RebalancerStickySession ( sticky ) )
2016-05-26 20:38:55 +03:00
}
2016-01-29 22:34:17 +03:00
lb = rebalancer
for serverName , server := range configuration . Backends [ frontend . Backend ] . Servers {
url , err := url . Parse ( server . URL )
if err != nil {
2016-06-20 16:19:52 +03:00
log . Errorf ( "Error parsing server URL %s: %v" , server . URL , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-01-29 22:34:17 +03:00
}
2016-04-20 02:45:59 +03:00
backend2FrontendMap [ url . String ( ) ] = frontendName
2016-01-29 22:34:17 +03:00
log . Debugf ( "Creating server %s at %s with weight %d" , serverName , url . String ( ) , server . Weight )
2016-03-22 03:32:02 +03:00
if err := rebalancer . UpsertServer ( url , roundrobin . Weight ( server . Weight ) ) ; err != nil {
2016-06-20 16:19:52 +03:00
log . Errorf ( "Error adding server %s to load balancer: %v" , server . URL , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-03-22 03:32:02 +03:00
}
2016-01-29 22:34:17 +03:00
}
case types . Wrr :
log . Debugf ( "Creating load-balancer wrr" )
2016-05-13 17:22:11 +03:00
if stickysession {
2016-09-28 14:28:20 +03:00
log . Debugf ( "Sticky session with cookie %v" , cookiename )
2016-05-13 17:22:11 +03:00
rr , _ = roundrobin . New ( saveBackend , roundrobin . EnableStickySession ( sticky ) )
}
2016-03-16 01:27:07 +03:00
lb = rr
2016-01-29 22:34:17 +03:00
for serverName , server := range configuration . Backends [ frontend . Backend ] . Servers {
url , err := url . Parse ( server . URL )
if err != nil {
2016-06-20 16:19:52 +03:00
log . Errorf ( "Error parsing server URL %s: %v" , server . URL , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-01-29 22:34:17 +03:00
}
2016-04-20 02:45:59 +03:00
backend2FrontendMap [ url . String ( ) ] = frontendName
2016-01-29 22:34:17 +03:00
log . Debugf ( "Creating server %s at %s with weight %d" , serverName , url . String ( ) , server . Weight )
2016-03-22 03:32:02 +03:00
if err := rr . UpsertServer ( url , roundrobin . Weight ( server . Weight ) ) ; err != nil {
2016-06-20 16:19:52 +03:00
log . Errorf ( "Error adding server %s to load balancer: %v" , server . URL , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-03-22 03:32:02 +03:00
}
2016-01-29 22:34:17 +03:00
}
}
2016-04-13 11:11:36 +03:00
maxConns := configuration . Backends [ frontend . Backend ] . MaxConn
if maxConns != nil && maxConns . Amount != 0 {
extractFunc , err := utils . NewExtractor ( maxConns . ExtractorFunc )
if err != nil {
2016-06-20 16:19:52 +03:00
log . Errorf ( "Error creating connlimit: %v" , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-04-13 11:11:36 +03:00
}
log . Debugf ( "Creating loadd-balancer connlimit" )
lb , err = connlimit . New ( lb , extractFunc , maxConns . Amount , connlimit . Logger ( oxyLogger ) )
if err != nil {
2016-06-20 16:19:52 +03:00
log . Errorf ( "Error creating connlimit: %v" , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-04-13 11:11:36 +03:00
}
}
2016-03-29 23:25:32 +03:00
// retry ?
if globalConfiguration . Retry != nil {
2016-04-06 15:07:51 +03:00
retries := len ( configuration . Backends [ frontend . Backend ] . Servers )
2016-03-29 23:25:32 +03:00
if globalConfiguration . Retry . Attempts > 0 {
retries = globalConfiguration . Retry . Attempts
}
2016-06-15 20:07:33 +03:00
lb = middlewares . NewRetry ( retries , lb )
2016-03-29 23:25:32 +03:00
log . Debugf ( "Creating retries max attempts %d" , retries )
}
2016-01-29 22:34:17 +03:00
var negroni = negroni . New ( )
if configuration . Backends [ frontend . Backend ] . CircuitBreaker != nil {
log . Debugf ( "Creating circuit breaker %s" , configuration . Backends [ frontend . Backend ] . CircuitBreaker . Expression )
2016-06-22 23:14:40 +03:00
cbreaker , err := middlewares . NewCircuitBreaker ( lb , configuration . Backends [ frontend . Backend ] . CircuitBreaker . Expression , cbreaker . Logger ( oxyLogger ) )
if err != nil {
log . Errorf ( "Error creating circuit breaker: %v" , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
negroni . Use ( cbreaker )
2016-01-29 22:34:17 +03:00
} else {
negroni . UseHandler ( lb )
2016-01-14 00:45:49 +03:00
}
2016-01-29 22:34:17 +03:00
backends [ frontend . Backend ] = negroni
} else {
log . Debugf ( "Reusing backend %s" , frontend . Backend )
2016-01-14 00:45:49 +03:00
}
2016-06-03 18:58:33 +03:00
if frontend . Priority > 0 {
newServerRoute . route . Priority ( frontend . Priority )
}
2016-03-27 03:05:17 +03:00
server . wireFrontendBackend ( newServerRoute , backends [ frontend . Backend ] )
2016-01-14 00:45:49 +03:00
}
2016-03-27 03:05:17 +03:00
err := newServerRoute . route . GetError ( )
2016-01-29 22:34:17 +03:00
if err != nil {
log . Errorf ( "Error building route: %s" , err )
2016-01-14 00:45:49 +03:00
}
}
2016-01-29 22:34:17 +03:00
}
}
2016-04-20 02:45:59 +03:00
middlewares . SetBackend2FrontendMap ( & backend2FrontendMap )
2016-06-03 18:58:33 +03:00
//sort routes
for _ , serverEntryPoint := range serverEntryPoints {
serverEntryPoint . httpRouter . GetHandler ( ) . SortRoutes ( )
}
2016-01-29 22:34:17 +03:00
return serverEntryPoints , nil
}
2016-01-14 00:45:49 +03:00
2016-03-27 03:05:17 +03:00
func ( server * Server ) wireFrontendBackend ( serverRoute * serverRoute , handler http . Handler ) {
2016-12-02 15:40:18 +03:00
// add prefix
if len ( serverRoute . addPrefix ) > 0 {
handler = & middlewares . AddPrefix {
Prefix : serverRoute . addPrefix ,
Handler : handler ,
}
}
2016-02-26 17:29:53 +03:00
// strip prefix
2016-04-06 14:06:31 +03:00
if len ( serverRoute . stripPrefixes ) > 0 {
2016-12-02 15:40:18 +03:00
handler = & middlewares . StripPrefix {
2016-04-06 14:06:31 +03:00
Prefixes : serverRoute . stripPrefixes ,
Handler : handler ,
2016-12-02 15:40:18 +03:00
}
2016-02-26 17:29:53 +03:00
}
2016-12-02 15:40:18 +03:00
serverRoute . route . Handler ( handler )
2016-02-26 17:29:53 +03:00
}
2016-01-29 22:34:17 +03:00
func ( server * Server ) loadEntryPointConfig ( entryPointName string , entryPoint * EntryPoint ) ( http . Handler , error ) {
regex := entryPoint . Redirect . Regex
replacement := entryPoint . Redirect . Replacement
if len ( entryPoint . Redirect . EntryPoint ) > 0 {
2016-03-09 20:59:08 +03:00
regex = "^(?:https?:\\/\\/)?([\\da-z\\.-]+)(?::\\d+)?(.*)$"
2016-01-29 22:34:17 +03:00
if server . globalConfiguration . EntryPoints [ entryPoint . Redirect . EntryPoint ] == nil {
2016-03-03 23:29:52 +03:00
return nil , errors . New ( "Unknown entrypoint " + entryPoint . Redirect . EntryPoint )
2016-01-29 22:34:17 +03:00
}
protocol := "http"
if server . globalConfiguration . EntryPoints [ entryPoint . Redirect . EntryPoint ] . TLS != nil {
protocol = "https"
2016-01-14 00:45:49 +03:00
}
2016-01-29 22:34:17 +03:00
r , _ := regexp . Compile ( "(:\\d+)" )
match := r . FindStringSubmatch ( server . globalConfiguration . EntryPoints [ entryPoint . Redirect . EntryPoint ] . Address )
if len ( match ) == 0 {
return nil , errors . New ( "Bad Address format: " + server . globalConfiguration . EntryPoints [ entryPoint . Redirect . EntryPoint ] . Address )
}
replacement = protocol + "://$1" + match [ 0 ] + "$2"
2016-01-14 00:45:49 +03:00
}
2016-01-29 22:34:17 +03:00
rewrite , err := middlewares . NewRewrite ( regex , replacement , true )
if err != nil {
return nil , err
}
log . Debugf ( "Creating entryPoint redirect %s -> %s : %s -> %s" , entryPointName , entryPoint . Redirect . EntryPoint , regex , replacement )
negroni := negroni . New ( )
negroni . Use ( rewrite )
return negroni , nil
}
func ( server * Server ) buildDefaultHTTPRouter ( ) * mux . Router {
router := mux . NewRouter ( )
router . NotFoundHandler = http . HandlerFunc ( notFoundHandler )
2016-02-26 17:29:53 +03:00
router . StrictSlash ( true )
2016-09-20 17:43:09 +03:00
router . SkipClean ( true )
2016-01-29 22:34:17 +03:00
return router
2016-01-14 00:45:49 +03:00
}
2016-02-20 01:55:23 +03:00
2016-03-30 20:05:43 +03:00
func getRoute ( serverRoute * serverRoute , route * types . Route ) error {
2016-03-27 03:05:17 +03:00
rules := Rules { route : serverRoute }
newRoute , err := rules . Parse ( route . Rule )
if err != nil {
return err
2016-02-26 17:29:53 +03:00
}
2016-06-03 18:58:33 +03:00
newRoute . Priority ( serverRoute . route . GetPriority ( ) + len ( route . Rule ) )
2016-03-27 03:05:17 +03:00
serverRoute . route = newRoute
return nil
2016-02-26 17:29:53 +03:00
}
2016-02-20 01:55:23 +03:00
func sortedFrontendNamesForConfig ( configuration * types . Configuration ) [ ] string {
keys := [ ] string { }
for key := range configuration . Frontends {
keys = append ( keys , key )
}
sort . Strings ( keys )
return keys
}