add ServersTransport on services
This commit is contained in:
parent
6075f7e8fd
commit
76f42a3013
64 changed files with 2359 additions and 242 deletions
|
@ -10,10 +10,11 @@ import (
|
|||
func mergeConfiguration(configurations dynamic.Configurations, defaultEntryPoints []string) dynamic.Configuration {
|
||||
conf := dynamic.Configuration{
|
||||
HTTP: &dynamic.HTTPConfiguration{
|
||||
Routers: make(map[string]*dynamic.Router),
|
||||
Middlewares: make(map[string]*dynamic.Middleware),
|
||||
Services: make(map[string]*dynamic.Service),
|
||||
Models: make(map[string]*dynamic.Model),
|
||||
Routers: make(map[string]*dynamic.Router),
|
||||
Middlewares: make(map[string]*dynamic.Middleware),
|
||||
Services: make(map[string]*dynamic.Service),
|
||||
Models: make(map[string]*dynamic.Model),
|
||||
ServersTransports: make(map[string]*dynamic.ServersTransport),
|
||||
},
|
||||
TCP: &dynamic.TCPConfiguration{
|
||||
Routers: make(map[string]*dynamic.TCPRouter),
|
||||
|
@ -52,6 +53,9 @@ func mergeConfiguration(configurations dynamic.Configurations, defaultEntryPoint
|
|||
for modelName, model := range configuration.HTTP.Models {
|
||||
conf.HTTP.Models[provider.MakeQualifiedName(pvd, modelName)] = model
|
||||
}
|
||||
for serversTransportName, serversTransport := range configuration.HTTP.ServersTransports {
|
||||
conf.HTTP.ServersTransports[provider.MakeQualifiedName(pvd, serversTransportName)] = serversTransport
|
||||
}
|
||||
}
|
||||
|
||||
if configuration.TCP != nil {
|
||||
|
|
|
@ -18,10 +18,11 @@ func Test_mergeConfiguration(t *testing.T) {
|
|||
desc: "Nil returns an empty configuration",
|
||||
given: nil,
|
||||
expected: &dynamic.HTTPConfiguration{
|
||||
Routers: make(map[string]*dynamic.Router),
|
||||
Middlewares: make(map[string]*dynamic.Middleware),
|
||||
Services: make(map[string]*dynamic.Service),
|
||||
Models: make(map[string]*dynamic.Model),
|
||||
Routers: make(map[string]*dynamic.Router),
|
||||
Middlewares: make(map[string]*dynamic.Middleware),
|
||||
Services: make(map[string]*dynamic.Service),
|
||||
Models: make(map[string]*dynamic.Model),
|
||||
ServersTransports: make(map[string]*dynamic.ServersTransport),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -53,7 +54,8 @@ func Test_mergeConfiguration(t *testing.T) {
|
|||
Services: map[string]*dynamic.Service{
|
||||
"service-1@provider-1": {},
|
||||
},
|
||||
Models: make(map[string]*dynamic.Model),
|
||||
Models: make(map[string]*dynamic.Model),
|
||||
ServersTransports: make(map[string]*dynamic.ServersTransport),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -103,7 +105,8 @@ func Test_mergeConfiguration(t *testing.T) {
|
|||
"service-1@provider-1": {},
|
||||
"service-1@provider-2": {},
|
||||
},
|
||||
Models: make(map[string]*dynamic.Model),
|
||||
Models: make(map[string]*dynamic.Model),
|
||||
ServersTransports: make(map[string]*dynamic.ServersTransport),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -287,7 +287,9 @@ func TestRouterManager_Get(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport, nil, nil)
|
||||
roundTripperManager := service.NewRoundTripperManager()
|
||||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(static.Configuration{}, nil, nil)
|
||||
|
||||
|
@ -391,7 +393,9 @@ func TestAccessLog(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport, nil, nil)
|
||||
roundTripperManager := service.NewRoundTripperManager()
|
||||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(static.Configuration{}, nil, nil)
|
||||
|
||||
|
@ -678,7 +682,9 @@ func TestRuntimeConfiguration(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport, nil, nil)
|
||||
roundTripperManager := service.NewRoundTripperManager()
|
||||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(static.Configuration{}, nil, nil)
|
||||
|
||||
|
@ -759,7 +765,9 @@ func TestProviderOnMiddlewares(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport, nil, nil)
|
||||
roundTripperManager := service.NewRoundTripperManager()
|
||||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(staticCfg, nil, nil)
|
||||
|
||||
|
@ -773,6 +781,14 @@ func TestProviderOnMiddlewares(t *testing.T) {
|
|||
assert.Equal(t, []string{"m1@docker", "m2@docker", "m1@file"}, rtConf.Middlewares["chain@docker"].Chain.Middlewares)
|
||||
}
|
||||
|
||||
type staticRoundTripperGetter struct {
|
||||
res *http.Response
|
||||
}
|
||||
|
||||
func (s staticRoundTripperGetter) Get(name string) (http.RoundTripper, error) {
|
||||
return &staticTransport{res: s.res}, nil
|
||||
}
|
||||
|
||||
type staticTransport struct {
|
||||
res *http.Response
|
||||
}
|
||||
|
@ -819,7 +835,7 @@ func BenchmarkRouterServe(b *testing.B) {
|
|||
},
|
||||
})
|
||||
|
||||
serviceManager := service.NewManager(rtConf.Services, &staticTransport{res}, nil, nil)
|
||||
serviceManager := service.NewManager(rtConf.Services, nil, nil, staticRoundTripperGetter{res})
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(static.Configuration{}, nil, nil)
|
||||
|
||||
|
@ -861,7 +877,7 @@ func BenchmarkService(b *testing.B) {
|
|||
},
|
||||
})
|
||||
|
||||
serviceManager := service.NewManager(rtConf.Services, &staticTransport{res}, nil, nil)
|
||||
serviceManager := service.NewManager(rtConf.Services, nil, nil, staticRoundTripperGetter{res})
|
||||
w := httptest.NewRecorder()
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, "http://foo.bar/", nil)
|
||||
|
||||
|
|
|
@ -48,7 +48,9 @@ func TestReuseService(t *testing.T) {
|
|||
),
|
||||
)
|
||||
|
||||
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry())
|
||||
roundTripperManager := service.NewRoundTripperManager()
|
||||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry(), roundTripperManager)
|
||||
tlsManager := tls.NewManager()
|
||||
|
||||
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(staticConfig, metrics.NewVoidRegistry(), nil), nil)
|
||||
|
@ -182,7 +184,9 @@ func TestServerResponseEmptyBackend(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry())
|
||||
roundTripperManager := service.NewRoundTripperManager()
|
||||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry(), roundTripperManager)
|
||||
tlsManager := tls.NewManager()
|
||||
|
||||
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(staticConfig, metrics.NewVoidRegistry(), nil), nil)
|
||||
|
@ -221,7 +225,9 @@ func TestInternalServices(t *testing.T) {
|
|||
),
|
||||
)
|
||||
|
||||
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry())
|
||||
roundTripperManager := service.NewRoundTripperManager()
|
||||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry(), roundTripperManager)
|
||||
tlsManager := tls.NewManager()
|
||||
|
||||
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(staticConfig, metrics.NewVoidRegistry(), nil), nil)
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
type ManagerFactory struct {
|
||||
metricsRegistry metrics.Registry
|
||||
|
||||
defaultRoundTripper http.RoundTripper
|
||||
roundTripperManager *RoundTripperManager
|
||||
|
||||
api func(configuration *runtime.Configuration) http.Handler
|
||||
restHandler http.Handler
|
||||
|
@ -26,11 +26,11 @@ type ManagerFactory struct {
|
|||
}
|
||||
|
||||
// NewManagerFactory creates a new ManagerFactory.
|
||||
func NewManagerFactory(staticConfiguration static.Configuration, routinesPool *safe.Pool, metricsRegistry metrics.Registry) *ManagerFactory {
|
||||
func NewManagerFactory(staticConfiguration static.Configuration, routinesPool *safe.Pool, metricsRegistry metrics.Registry, roundTripperManager *RoundTripperManager) *ManagerFactory {
|
||||
factory := &ManagerFactory{
|
||||
metricsRegistry: metricsRegistry,
|
||||
defaultRoundTripper: setupDefaultRoundTripper(staticConfiguration.ServersTransport),
|
||||
routinesPool: routinesPool,
|
||||
roundTripperManager: roundTripperManager,
|
||||
}
|
||||
|
||||
if staticConfiguration.API != nil {
|
||||
|
@ -61,6 +61,6 @@ func NewManagerFactory(staticConfiguration static.Configuration, routinesPool *s
|
|||
|
||||
// Build creates a service manager.
|
||||
func (f *ManagerFactory) Build(configuration *runtime.Configuration) *InternalHandlers {
|
||||
svcManager := NewManager(configuration.Services, f.defaultRoundTripper, f.metricsRegistry, f.routinesPool)
|
||||
svcManager := NewManager(configuration.Services, f.metricsRegistry, f.routinesPool, f.roundTripperManager)
|
||||
return NewInternalHandlers(f.api, configuration, f.restHandler, f.metricsHandler, f.pingHandler, f.dashboardHandler, svcManager)
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ const StatusClientClosedRequest = 499
|
|||
// StatusClientClosedRequestText non-standard HTTP status for client disconnection.
|
||||
const StatusClientClosedRequestText = "Client Closed Request"
|
||||
|
||||
func buildProxy(passHostHeader *bool, responseForwarding *dynamic.ResponseForwarding, defaultRoundTripper http.RoundTripper, bufferPool httputil.BufferPool) (http.Handler, error) {
|
||||
func buildProxy(passHostHeader *bool, responseForwarding *dynamic.ResponseForwarding, roundTripper http.RoundTripper, bufferPool httputil.BufferPool) (http.Handler, error) {
|
||||
var flushInterval ptypes.Duration
|
||||
if responseForwarding != nil {
|
||||
err := flushInterval.Set(responseForwarding.FlushInterval)
|
||||
|
@ -76,7 +76,7 @@ func buildProxy(passHostHeader *bool, responseForwarding *dynamic.ResponseForwar
|
|||
delete(outReq.Header, "Sec-Websocket-Protocol")
|
||||
delete(outReq.Header, "Sec-Websocket-Version")
|
||||
},
|
||||
Transport: defaultRoundTripper,
|
||||
Transport: roundTripper,
|
||||
FlushInterval: time.Duration(flushInterval),
|
||||
BufferPool: bufferPool,
|
||||
ErrorHandler: func(w http.ResponseWriter, request *http.Request, err error) {
|
||||
|
|
|
@ -4,11 +4,14 @@ import (
|
|||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/v2/pkg/config/static"
|
||||
"github.com/containous/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/containous/traefik/v2/pkg/log"
|
||||
traefiktls "github.com/containous/traefik/v2/pkg/tls"
|
||||
"golang.org/x/net/http2"
|
||||
|
@ -23,30 +26,100 @@ func (t *h2cTransportWrapper) RoundTrip(req *http.Request) (*http.Response, erro
|
|||
return t.Transport.RoundTrip(req)
|
||||
}
|
||||
|
||||
// createRoundtripper creates an http.Roundtripper configured with the Transport configuration settings.
|
||||
// NewRoundTripperManager creates a new RoundTripperManager.
|
||||
func NewRoundTripperManager() *RoundTripperManager {
|
||||
return &RoundTripperManager{
|
||||
roundTrippers: make(map[string]http.RoundTripper),
|
||||
configs: make(map[string]*dynamic.ServersTransport),
|
||||
}
|
||||
}
|
||||
|
||||
// RoundTripperManager handles roundtripper for the reverse proxy.
|
||||
type RoundTripperManager struct {
|
||||
rtLock sync.RWMutex
|
||||
roundTrippers map[string]http.RoundTripper
|
||||
configs map[string]*dynamic.ServersTransport
|
||||
}
|
||||
|
||||
// Update updates the roundtrippers configurations.
|
||||
func (r *RoundTripperManager) Update(newConfigs map[string]*dynamic.ServersTransport) {
|
||||
r.rtLock.Lock()
|
||||
defer r.rtLock.Unlock()
|
||||
|
||||
for configName, config := range r.configs {
|
||||
newConfig, ok := newConfigs[configName]
|
||||
if !ok {
|
||||
delete(r.configs, configName)
|
||||
delete(r.roundTrippers, configName)
|
||||
continue
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(newConfig, config) {
|
||||
continue
|
||||
}
|
||||
|
||||
var err error
|
||||
r.roundTrippers[configName], err = createRoundTripper(newConfig)
|
||||
if err != nil {
|
||||
log.WithoutContext().Errorf("Could not configure HTTP Transport %s, fallback on default transport: %v", configName, err)
|
||||
r.roundTrippers[configName] = http.DefaultTransport
|
||||
}
|
||||
}
|
||||
|
||||
for newConfigName, newConfig := range newConfigs {
|
||||
if _, ok := r.configs[newConfigName]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
var err error
|
||||
r.roundTrippers[newConfigName], err = createRoundTripper(newConfig)
|
||||
if err != nil {
|
||||
log.WithoutContext().Errorf("Could not configure HTTP Transport %s, fallback on default transport: %v", newConfigName, err)
|
||||
r.roundTrippers[newConfigName] = http.DefaultTransport
|
||||
}
|
||||
}
|
||||
|
||||
r.configs = newConfigs
|
||||
}
|
||||
|
||||
// Get get a roundtripper by name.
|
||||
func (r *RoundTripperManager) Get(name string) (http.RoundTripper, error) {
|
||||
if len(name) == 0 {
|
||||
name = "default@internal"
|
||||
}
|
||||
|
||||
r.rtLock.RLock()
|
||||
defer r.rtLock.RUnlock()
|
||||
|
||||
if rt, ok := r.roundTrippers[name]; ok {
|
||||
return rt, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("servers transport not found %s", name)
|
||||
}
|
||||
|
||||
// createRoundTripper creates an http.RoundTripper configured with the Transport configuration 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
|
||||
// behavior and backwards compatibility issues.
|
||||
func createRoundtripper(transportConfiguration *static.ServersTransport) (http.RoundTripper, error) {
|
||||
if transportConfiguration == nil {
|
||||
// An exception to this is the MaxIdleConns setting as we only provide the option MaxIdleConnsPerHostin Traefik at this point in time.
|
||||
// Setting this value to the default of 100 could lead to confusing behavior and backwards compatibility issues.
|
||||
func createRoundTripper(cfg *dynamic.ServersTransport) (http.RoundTripper, error) {
|
||||
if cfg == nil {
|
||||
return nil, errors.New("no transport configuration given")
|
||||
}
|
||||
|
||||
dialer := &net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
DualStack: true,
|
||||
}
|
||||
|
||||
if transportConfiguration.ForwardingTimeouts != nil {
|
||||
dialer.Timeout = time.Duration(transportConfiguration.ForwardingTimeouts.DialTimeout)
|
||||
if cfg.ForwardingTimeouts != nil {
|
||||
dialer.Timeout = time.Duration(cfg.ForwardingTimeouts.DialTimeout)
|
||||
}
|
||||
|
||||
transport := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: dialer.DialContext,
|
||||
MaxIdleConnsPerHost: transportConfiguration.MaxIdleConnsPerHost,
|
||||
MaxIdleConnsPerHost: cfg.MaxIdleConnsPerHost,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
|
@ -61,24 +134,21 @@ func createRoundtripper(transportConfiguration *static.ServersTransport) (http.R
|
|||
},
|
||||
})
|
||||
|
||||
if transportConfiguration.ForwardingTimeouts != nil {
|
||||
transport.ResponseHeaderTimeout = time.Duration(transportConfiguration.ForwardingTimeouts.ResponseHeaderTimeout)
|
||||
transport.IdleConnTimeout = time.Duration(transportConfiguration.ForwardingTimeouts.IdleConnTimeout)
|
||||
if cfg.ForwardingTimeouts != nil {
|
||||
transport.ResponseHeaderTimeout = time.Duration(cfg.ForwardingTimeouts.ResponseHeaderTimeout)
|
||||
transport.IdleConnTimeout = time.Duration(cfg.ForwardingTimeouts.IdleConnTimeout)
|
||||
}
|
||||
|
||||
if transportConfiguration.InsecureSkipVerify || len(transportConfiguration.RootCAs) > 0 {
|
||||
if cfg.InsecureSkipVerify || len(cfg.RootCAs) > 0 || len(cfg.ServerName) > 0 || len(cfg.Certificates) > 0 {
|
||||
transport.TLSClientConfig = &tls.Config{
|
||||
InsecureSkipVerify: transportConfiguration.InsecureSkipVerify,
|
||||
RootCAs: createRootCACertPool(transportConfiguration.RootCAs),
|
||||
ServerName: cfg.ServerName,
|
||||
InsecureSkipVerify: cfg.InsecureSkipVerify,
|
||||
RootCAs: createRootCACertPool(cfg.RootCAs),
|
||||
Certificates: cfg.Certificates.GetCertificates(),
|
||||
}
|
||||
}
|
||||
|
||||
smartTransport, err := newSmartRoundTripper(transport)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return smartTransport, nil
|
||||
return newSmartRoundTripper(transport)
|
||||
}
|
||||
|
||||
func createRootCACertPool(rootCAs []traefiktls.FileOrContent) *x509.CertPool {
|
||||
|
@ -99,13 +169,3 @@ func createRootCACertPool(rootCAs []traefiktls.FileOrContent) *x509.CertPool {
|
|||
|
||||
return roots
|
||||
}
|
||||
|
||||
func setupDefaultRoundTripper(conf *static.ServersTransport) http.RoundTripper {
|
||||
transport, err := createRoundtripper(conf)
|
||||
if err != nil {
|
||||
log.WithoutContext().Errorf("Could not configure HTTP Transport, fallbacking on default transport: %v", err)
|
||||
return http.DefaultTransport
|
||||
}
|
||||
|
||||
return transport
|
||||
}
|
||||
|
|
229
pkg/server/service/roundtripper_test.go
Normal file
229
pkg/server/service/roundtripper_test.go
Normal file
|
@ -0,0 +1,229 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/v2/pkg/config/dynamic"
|
||||
traefiktls "github.com/containous/traefik/v2/pkg/tls"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Int32(i int32) *int32 {
|
||||
return &i
|
||||
}
|
||||
|
||||
// LocalhostCert is a PEM-encoded TLS cert
|
||||
// for host example.com, www.example.com
|
||||
// expiring at Jan 29 16:00:00 2084 GMT.
|
||||
// go run $GOROOT/src/crypto/tls/generate_cert.go --rsa-bits 1024 --host example.com,www.example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
|
||||
var LocalhostCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIICDDCCAXWgAwIBAgIQH20JmcOlcRWHNuf62SYwszANBgkqhkiG9w0BAQsFADAS
|
||||
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
|
||||
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
|
||||
iQKBgQC0qINy3F4oq6viDnlpDDE5J08iSRGggg6EylJKBKZfphEG2ufgK78Dufl3
|
||||
+7b0LlEY2AeZHwviHODqC9a6ihj1ZYQk0/djAh+OeOhFEWu+9T/VP8gVFarFqT8D
|
||||
Opy+hrG7YJivUIzwb4fmJQRI7FajzsnGyM6LiXLU+0qzb7ZO/QIDAQABo2EwXzAO
|
||||
BgNVHQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUw
|
||||
AwEB/zAnBgNVHREEIDAeggtleGFtcGxlLmNvbYIPd3d3LmV4YW1wbGUuY29tMA0G
|
||||
CSqGSIb3DQEBCwUAA4GBAB+eluoQYzyyMfeEEAOtlldevx5MtDENT05NB0WI+91R
|
||||
we7mX8lv763u0XuCWPxbHszhclI6FFjoQef0Z1NYLRm8ZRq58QqWDFZ3E6wdDK+B
|
||||
+OWvkW+hRavo6R9LzIZPfbv8yBo4M9PK/DXw8hLqH7VkkI+Gh793iH7Ugd4A7wvT
|
||||
-----END CERTIFICATE-----`)
|
||||
|
||||
// LocalhostKey is the private key for localhostCert.
|
||||
var LocalhostKey = []byte(`-----BEGIN PRIVATE KEY-----
|
||||
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALSog3LcXiirq+IO
|
||||
eWkMMTknTyJJEaCCDoTKUkoEpl+mEQba5+ArvwO5+Xf7tvQuURjYB5kfC+Ic4OoL
|
||||
1rqKGPVlhCTT92MCH4546EURa771P9U/yBUVqsWpPwM6nL6GsbtgmK9QjPBvh+Yl
|
||||
BEjsVqPOycbIzouJctT7SrNvtk79AgMBAAECgYB1wMT1MBgbkFIXpXGTfAP1id61
|
||||
rUTVBxCpkypx3ngHLjo46qRq5Hi72BN4FlTY8fugIudI8giP2FztkMvkiLDc4m0p
|
||||
Gn+QMJzjlBjjTuNLvLy4aSmNRLIC3mtbx9PdU71DQswEpJHFj/vmsxbuSrG1I1YE
|
||||
r1reuSo2ow6fOAjXLQJBANpz+RkOiPSPuvl+gi1sp2pLuynUJVDVqWZi386YRpfg
|
||||
DiKCLpqwqYDkOozm/fwFALvwXKGmsyyL43HO8eI+2NsCQQDTtY32V+02GPecdsyq
|
||||
msK06EPVTSaYwj9Mm+q709KsmYFHLXDqXjcKV4UgKYKRPz7my1fXodMmGmfuh1a3
|
||||
/HMHAkEAmOQKN0tA90mRJwUvvvMIyRBv0fq0kzq28P3KfiF9ZtZdjjFmxMVYHOmf
|
||||
QPZ6VGR7+w1jB5BQXqEZcpHQIPSzeQJBAIy9tZJ/AYNlNbcegxEnsSjy/6VdlLsY
|
||||
51vWi0Yym2uC4R6gZuBnoc+OP0ISVmqY0Qg9RjhjrCs4gr9f2ZaWjSECQCxqZMq1
|
||||
3viJ8BGCC0m/5jv1EHur3YgwphYCkf4Li6DKwIdMLk1WXkTcPIY3V2Jqj8rPEB5V
|
||||
rqPRSAtd/h6oZbs=
|
||||
-----END PRIVATE KEY-----`)
|
||||
|
||||
// openssl req -newkey rsa:2048 \
|
||||
// -new -nodes -x509 \
|
||||
// -days 3650 \
|
||||
// -out cert.pem \
|
||||
// -keyout key.pem \
|
||||
// -subj "/CN=example.com"
|
||||
// -addext "subjectAltName = DNS:example.com"
|
||||
var mTLSCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIDJTCCAg2gAwIBAgIUYKnGcLnmMosOSKqTn4ydAMURE4gwDQYJKoZIhvcNAQEL
|
||||
BQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMjAwODEzMDkyNzIwWhcNMzAw
|
||||
ODExMDkyNzIwWjAWMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN
|
||||
AQEBBQADggEPADCCAQoCggEBAOAe+QM1c9lZ2TPRgoiuPAq2A3Pfu+i82lmqrTJ0
|
||||
PR2Cx1fPbccCUTFJPlxSDiaMrwtvqw1yP9L2Pu/vJK5BY4YDVDtFGKjpRBau1otJ
|
||||
iY50O5qMo3sfLqR4/1VsQGlLVZYLD3dyc4ZTmOp8+7tJ2SyGorojbIKfimZT7XD7
|
||||
dzrVr4h4Gn+SzzOnoKyx29uaNRP+XuMYHmHyQcJE03pUGhkTOvMwBlF96QdQ9WG0
|
||||
D+1CxRciEsZXE+imKBHoaTgrTkpnFHzsrIEw+OHQYf30zuT/k/lkgv1vqEwINHjz
|
||||
W2VeTur5eqVvA7zZdoEXMRy7BUvh/nZk5AXkXAmZLn0eUg8CAwEAAaNrMGkwHQYD
|
||||
VR0OBBYEFEDrbhPDt+hi3ZOzk6S/CFAVHwk0MB8GA1UdIwQYMBaAFEDrbhPDt+hi
|
||||
3ZOzk6S/CFAVHwk0MA8GA1UdEwEB/wQFMAMBAf8wFgYDVR0RBA8wDYILZXhhbXBs
|
||||
ZS5jb20wDQYJKoZIhvcNAQELBQADggEBAG/JRJWeUNx2mDJAk8W7Syq3gmQB7s9f
|
||||
+yY/XVRJZGahOPilABqFpC6GVn2HWuvuOqy8/RGk9ja5abKVXqE6YKrljqo3XfzB
|
||||
KQcOz4SFirpkHvNCiEcK3kggN3wJWqL2QyXAxWldBBBCO9yx7a3cux31C//sTUOG
|
||||
xq4JZDg171U1UOpfN1t0BFMdt05XZFEM247N7Dcf7HoXwAa7eyLKgtKWqPDqGrFa
|
||||
fvGDDKK9X/KVsU2x9V3pG+LsJg7ogUnSyD2r5G1F3Y8OVs2T/783PaN0M35fDL38
|
||||
09VbsxA2GasOHZrghUzT4UvZWWZbWEmG975hFYvdj6DlK9K0s5TdKIs=
|
||||
-----END CERTIFICATE-----`)
|
||||
|
||||
var mTLSKey = []byte(`-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDgHvkDNXPZWdkz
|
||||
0YKIrjwKtgNz37vovNpZqq0ydD0dgsdXz23HAlExST5cUg4mjK8Lb6sNcj/S9j7v
|
||||
7ySuQWOGA1Q7RRio6UQWrtaLSYmOdDuajKN7Hy6keP9VbEBpS1WWCw93cnOGU5jq
|
||||
fPu7SdkshqK6I2yCn4pmU+1w+3c61a+IeBp/ks8zp6CssdvbmjUT/l7jGB5h8kHC
|
||||
RNN6VBoZEzrzMAZRfekHUPVhtA/tQsUXIhLGVxPopigR6Gk4K05KZxR87KyBMPjh
|
||||
0GH99M7k/5P5ZIL9b6hMCDR481tlXk7q+XqlbwO82XaBFzEcuwVL4f52ZOQF5FwJ
|
||||
mS59HlIPAgMBAAECggEAAKLV3hZ2v7UrkqQTlMO50+X0WI3YAK8Yh4yedTgzPDQ0
|
||||
0KD8FMaC6HrmvGhXNfDMRmIIwD8Ew1qDjzbEieIRoD2+LXTivwf6c34HidmplEfs
|
||||
K2IezKin/zuArgNio2ndUlGxt4sRnN373x5/sGZjQWcYayLSmgRN5kByuhFco0Qa
|
||||
oSrXcXNUlb+KgRQXPDU4+M35tPHvLdyg+tko/m/5uK9dc9MNvGZHOMBKg0VNURJb
|
||||
V1l3dR+evwvpqHzBvWiqN/YOiUUvIxlFKA35hJkfCl7ivFs4CLqqFNCKDao95fWe
|
||||
s0UR9iMakT48jXV76IfwZnyX10OhIWzKls5trjDL8QKBgQD3thQJ8e0FL9y1W+Ph
|
||||
mCdEaoffSPkgSn64wIsQ9bMmv4y+KYBK5AhqaHgYm4LgW4x1+CURNFu+YFEyaNNA
|
||||
kNCXFyRX3Em3vxcShP5jIqg+f07mtXPKntWP/zBeKQWgdHX371oFTfaAlNuKX/7S
|
||||
n0jBYjr4Iof1bnquMQvUoHCYWwKBgQDnntFU9/AQGaQIvhfeU1XKFkQ/BfhIsd27
|
||||
RlmiCi0ee9Ce74cMAhWr/9yg0XUxzrh+Ui1xnkMVTZ5P8tWIxROokznLUTGJA5rs
|
||||
zB+ovCPFZcquTwNzn7SBnpHTR0OqJd8sd89P5ST2SqufeSF/gGi5sTs4EocOLCpZ
|
||||
EPVIfm47XQKBgB4d5RHQeCDJUOw739jtxthqm1pqZN+oLwAHaOEG/mEXqOT15sM0
|
||||
NlG5oeBcB+1/M/Sj1t3gn8blrvmSBR00fifgiGqmPdA5S3TU9pjW/d2bXNxv80QP
|
||||
S6fWPusz0ZtQjYc3cppygCXh808/nJu/AfmBF+pTSHRumjvTery/RPFBAoGBAMi/
|
||||
zCta4cTylEvHhqR5kiefePMu120aTFYeuV1KeKStJ7o5XNE5lVMIZk80e+D5jMpf
|
||||
q2eIhhgWuBoPHKh4N3uqbzMbYlWgvEx09xOmTVKv0SWW8iTqzOZza2y1nZ4BSRcf
|
||||
mJ1ku86EFZAYysHZp+saA3usA0ZzXRjpK87zVdM5AoGBAOSqI+t48PnPtaUDFdpd
|
||||
taNNVDbcecJatm3w8VDWnarahfWe66FIqc9wUkqekqAgwZLa0AGdUalvXfGrHfNs
|
||||
PtvuNc5EImfSkuPBYLBslNxtjbBvAYgacEdY+gRhn2TeIUApnND58lCWsKbNHLFZ
|
||||
ajIPbTY+Fe9OTOFTN48ujXNn
|
||||
-----END PRIVATE KEY-----`)
|
||||
|
||||
func TestKeepConnectionWhenSameConfiguration(t *testing.T) {
|
||||
srv := httptest.NewUnstartedServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
connCount := Int32(0)
|
||||
srv.Config.ConnState = func(conn net.Conn, state http.ConnState) {
|
||||
if state == http.StateNew {
|
||||
atomic.AddInt32(connCount, 1)
|
||||
}
|
||||
}
|
||||
|
||||
cert, err := tls.X509KeyPair(LocalhostCert, LocalhostKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
srv.TLS = &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||
srv.StartTLS()
|
||||
|
||||
rtManager := NewRoundTripperManager()
|
||||
|
||||
dynamicConf := map[string]*dynamic.ServersTransport{
|
||||
"test": {
|
||||
ServerName: "example.com",
|
||||
RootCAs: []traefiktls.FileOrContent{traefiktls.FileOrContent(LocalhostCert)},
|
||||
},
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
rtManager.Update(dynamicConf)
|
||||
|
||||
tr, err := rtManager.Get("test")
|
||||
require.NoError(t, err)
|
||||
|
||||
client := http.Client{Transport: tr}
|
||||
|
||||
resp, err := client.Get(srv.URL)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
}
|
||||
|
||||
count := atomic.LoadInt32(connCount)
|
||||
require.EqualValues(t, 1, count)
|
||||
|
||||
dynamicConf = map[string]*dynamic.ServersTransport{
|
||||
"test": {
|
||||
ServerName: "www.example.com",
|
||||
RootCAs: []traefiktls.FileOrContent{traefiktls.FileOrContent(LocalhostCert)},
|
||||
},
|
||||
}
|
||||
|
||||
rtManager.Update(dynamicConf)
|
||||
|
||||
tr, err := rtManager.Get("test")
|
||||
require.NoError(t, err)
|
||||
|
||||
client := http.Client{Transport: tr}
|
||||
|
||||
resp, err := client.Get(srv.URL)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
count = atomic.LoadInt32(connCount)
|
||||
assert.EqualValues(t, 2, count)
|
||||
}
|
||||
|
||||
func TestMTLS(t *testing.T) {
|
||||
srv := httptest.NewUnstartedServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
cert, err := tls.X509KeyPair(LocalhostCert, LocalhostKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
clientPool := x509.NewCertPool()
|
||||
clientPool.AppendCertsFromPEM(mTLSCert)
|
||||
|
||||
srv.TLS = &tls.Config{
|
||||
// For TLS
|
||||
Certificates: []tls.Certificate{cert},
|
||||
|
||||
// For mTLS
|
||||
ClientAuth: tls.RequireAndVerifyClientCert,
|
||||
ClientCAs: clientPool,
|
||||
}
|
||||
srv.StartTLS()
|
||||
|
||||
rtManager := NewRoundTripperManager()
|
||||
|
||||
dynamicConf := map[string]*dynamic.ServersTransport{
|
||||
"test": {
|
||||
ServerName: "example.com",
|
||||
// For TLS
|
||||
RootCAs: []traefiktls.FileOrContent{traefiktls.FileOrContent(LocalhostCert)},
|
||||
|
||||
// For mTLS
|
||||
Certificates: traefiktls.Certificates{
|
||||
traefiktls.Certificate{
|
||||
CertFile: traefiktls.FileOrContent(mTLSCert),
|
||||
KeyFile: traefiktls.FileOrContent(mTLSKey),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
rtManager.Update(dynamicConf)
|
||||
|
||||
tr, err := rtManager.Get("test")
|
||||
require.NoError(t, err)
|
||||
|
||||
client := http.Client{Transport: tr}
|
||||
|
||||
resp, err := client.Get(srv.URL)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
}
|
|
@ -35,13 +35,18 @@ const (
|
|||
|
||||
const defaultMaxBodySize int64 = -1
|
||||
|
||||
// RoundTripperGetter is a roundtripper getter interface.
|
||||
type RoundTripperGetter interface {
|
||||
Get(name string) (http.RoundTripper, error)
|
||||
}
|
||||
|
||||
// NewManager creates a new Manager.
|
||||
func NewManager(configs map[string]*runtime.ServiceInfo, defaultRoundTripper http.RoundTripper, metricsRegistry metrics.Registry, routinePool *safe.Pool) *Manager {
|
||||
func NewManager(configs map[string]*runtime.ServiceInfo, metricsRegistry metrics.Registry, routinePool *safe.Pool, roundTripperManager RoundTripperGetter) *Manager {
|
||||
return &Manager{
|
||||
routinePool: routinePool,
|
||||
metricsRegistry: metricsRegistry,
|
||||
bufferPool: newBufferPool(),
|
||||
defaultRoundTripper: defaultRoundTripper,
|
||||
roundTripperManager: roundTripperManager,
|
||||
balancers: make(map[string]healthcheck.Balancers),
|
||||
configs: configs,
|
||||
}
|
||||
|
@ -52,7 +57,7 @@ type Manager struct {
|
|||
routinePool *safe.Pool
|
||||
metricsRegistry metrics.Registry
|
||||
bufferPool httputil.BufferPool
|
||||
defaultRoundTripper http.RoundTripper
|
||||
roundTripperManager RoundTripperGetter
|
||||
// balancers is the map of all Balancers, keyed by service name.
|
||||
// There is one Balancer per service handler, and there is one service handler per reference to a service
|
||||
// (e.g. if 2 routers refer to the same service name, 2 service handlers are created),
|
||||
|
@ -168,7 +173,16 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName
|
|||
service.PassHostHeader = &defaultPassHostHeader
|
||||
}
|
||||
|
||||
fwd, err := buildProxy(service.PassHostHeader, service.ResponseForwarding, m.defaultRoundTripper, m.bufferPool)
|
||||
if len(service.ServersTransport) > 0 {
|
||||
service.ServersTransport = provider.GetQualifiedName(ctx, service.ServersTransport)
|
||||
}
|
||||
|
||||
roundTripper, err := m.roundTripperManager.Get(service.ServersTransport)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fwd, err := buildProxy(service.PassHostHeader, service.ResponseForwarding, roundTripper, m.bufferPool)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -213,7 +227,7 @@ func (m *Manager) LaunchHealthCheck() {
|
|||
if hcOpts := buildHealthCheckOptions(ctx, balancers, serviceName, service.HealthCheck); hcOpts != nil {
|
||||
log.FromContext(ctx).Debugf("Setting up healthcheck for service %s with %s", serviceName, *hcOpts)
|
||||
|
||||
hcOpts.Transport = m.defaultRoundTripper
|
||||
hcOpts.Transport, _ = m.roundTripperManager.Get(service.ServersTransport)
|
||||
backendHealthCheck = healthcheck.NewBackendConfig(*hcOpts, serviceName)
|
||||
}
|
||||
|
||||
|
|
|
@ -80,7 +80,11 @@ func TestGetLoadBalancer(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetLoadBalancerServiceHandler(t *testing.T) {
|
||||
sm := NewManager(nil, http.DefaultTransport, nil, nil)
|
||||
sm := NewManager(nil, nil, nil, &RoundTripperManager{
|
||||
roundTrippers: map[string]http.RoundTripper{
|
||||
"default@internal": http.DefaultTransport,
|
||||
},
|
||||
})
|
||||
|
||||
server1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("X-From", "first")
|
||||
|
@ -332,7 +336,11 @@ func TestManager_Build(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
manager := NewManager(test.configs, http.DefaultTransport, nil, nil)
|
||||
manager := NewManager(test.configs, nil, nil, &RoundTripperManager{
|
||||
roundTrippers: map[string]http.RoundTripper{
|
||||
"default@internal": http.DefaultTransport,
|
||||
},
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
if len(test.providerName) > 0 {
|
||||
|
@ -355,7 +363,11 @@ func TestMultipleTypeOnBuildHTTP(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
manager := NewManager(services, http.DefaultTransport, nil, nil)
|
||||
manager := NewManager(services, nil, nil, &RoundTripperManager{
|
||||
roundTrippers: map[string]http.RoundTripper{
|
||||
"default@internal": http.DefaultTransport,
|
||||
},
|
||||
})
|
||||
|
||||
_, err := manager.BuildHTTP(context.Background(), "test@file")
|
||||
assert.Error(t, err, "cannot create service: multi-types service not supported, consider declaring two different pieces of service instead")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue