Merge current branch v2.4 into master
This commit is contained in:
commit
702e301990
100 changed files with 561 additions and 557 deletions
|
@ -28,6 +28,7 @@ type ConfigurationWatcher struct {
|
|||
configurationValidatedChan chan dynamic.Message
|
||||
providerConfigUpdateMap map[string]chan dynamic.Message
|
||||
|
||||
requiredProvider string
|
||||
configurationListeners []func(dynamic.Configuration)
|
||||
|
||||
routinesPool *safe.Pool
|
||||
|
@ -39,6 +40,7 @@ func NewConfigurationWatcher(
|
|||
pvd provider.Provider,
|
||||
providersThrottleDuration time.Duration,
|
||||
defaultEntryPoints []string,
|
||||
requiredProvider string,
|
||||
) *ConfigurationWatcher {
|
||||
watcher := &ConfigurationWatcher{
|
||||
provider: pvd,
|
||||
|
@ -48,6 +50,7 @@ func NewConfigurationWatcher(
|
|||
providersThrottleDuration: providersThrottleDuration,
|
||||
routinesPool: routinesPool,
|
||||
defaultEntryPoints: defaultEntryPoints,
|
||||
requiredProvider: requiredProvider,
|
||||
}
|
||||
|
||||
currentConfigurations := make(dynamic.Configurations)
|
||||
|
@ -146,8 +149,11 @@ func (c *ConfigurationWatcher) loadMessage(configMsg dynamic.Message) {
|
|||
conf := mergeConfiguration(newConfigurations, c.defaultEntryPoints)
|
||||
conf = applyModel(conf)
|
||||
|
||||
for _, listener := range c.configurationListeners {
|
||||
listener(conf)
|
||||
// We wait for first configuration of the require provider before applying configurations.
|
||||
if _, ok := newConfigurations[c.requiredProvider]; c.requiredProvider == "" || ok {
|
||||
for _, listener := range c.configurationListeners {
|
||||
listener(conf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ func TestNewConfigurationWatcher(t *testing.T) {
|
|||
}},
|
||||
}
|
||||
|
||||
watcher := NewConfigurationWatcher(routinesPool, pvd, time.Second, []string{})
|
||||
watcher := NewConfigurationWatcher(routinesPool, pvd, time.Second, []string{}, "")
|
||||
|
||||
run := make(chan struct{})
|
||||
|
||||
|
@ -112,7 +112,7 @@ func TestListenProvidersThrottleProviderConfigReload(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
watcher := NewConfigurationWatcher(routinesPool, pvd, 30*time.Millisecond, []string{})
|
||||
watcher := NewConfigurationWatcher(routinesPool, pvd, 30*time.Millisecond, []string{}, "")
|
||||
|
||||
publishedConfigCount := 0
|
||||
watcher.AddListener(func(_ dynamic.Configuration) {
|
||||
|
@ -136,7 +136,7 @@ func TestListenProvidersSkipsEmptyConfigs(t *testing.T) {
|
|||
messages: []dynamic.Message{{ProviderName: "mock"}},
|
||||
}
|
||||
|
||||
watcher := NewConfigurationWatcher(routinesPool, pvd, time.Second, []string{})
|
||||
watcher := NewConfigurationWatcher(routinesPool, pvd, time.Second, []string{}, "")
|
||||
watcher.AddListener(func(_ dynamic.Configuration) {
|
||||
t.Error("An empty configuration was published but it should not")
|
||||
})
|
||||
|
@ -162,7 +162,7 @@ func TestListenProvidersSkipsSameConfigurationForProvider(t *testing.T) {
|
|||
messages: []dynamic.Message{message, message},
|
||||
}
|
||||
|
||||
watcher := NewConfigurationWatcher(routinesPool, pvd, 0, []string{})
|
||||
watcher := NewConfigurationWatcher(routinesPool, pvd, 0, []string{}, "")
|
||||
|
||||
alreadyCalled := false
|
||||
watcher.AddListener(func(_ dynamic.Configuration) {
|
||||
|
@ -205,7 +205,7 @@ func TestListenProvidersDoesNotSkipFlappingConfiguration(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
watcher := NewConfigurationWatcher(routinesPool, pvd, 15*time.Millisecond, []string{"defaultEP"})
|
||||
watcher := NewConfigurationWatcher(routinesPool, pvd, 15*time.Millisecond, []string{"defaultEP"}, "")
|
||||
|
||||
var lastConfig dynamic.Configuration
|
||||
watcher.AddListener(func(conf dynamic.Configuration) {
|
||||
|
@ -216,7 +216,7 @@ func TestListenProvidersDoesNotSkipFlappingConfiguration(t *testing.T) {
|
|||
defer watcher.Stop()
|
||||
|
||||
// give some time so that the configuration can be processed
|
||||
time.Sleep(40 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
expected := dynamic.Configuration{
|
||||
HTTP: th.BuildConfiguration(
|
||||
|
@ -260,7 +260,7 @@ func TestListenProvidersPublishesConfigForEachProvider(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
watcher := NewConfigurationWatcher(routinesPool, pvd, 0, []string{"defaultEP"})
|
||||
watcher := NewConfigurationWatcher(routinesPool, pvd, 0, []string{"defaultEP"}, "")
|
||||
|
||||
var publishedProviderConfig dynamic.Configuration
|
||||
|
||||
|
@ -327,7 +327,7 @@ func TestPublishConfigUpdatedByProvider(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
watcher := NewConfigurationWatcher(routinesPool, pvd, 30*time.Millisecond, []string{})
|
||||
watcher := NewConfigurationWatcher(routinesPool, pvd, 30*time.Millisecond, []string{}, "")
|
||||
|
||||
publishedConfigCount := 0
|
||||
watcher.AddListener(func(configuration dynamic.Configuration) {
|
||||
|
@ -375,7 +375,7 @@ func TestPublishConfigUpdatedByConfigWatcherListener(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
watcher := NewConfigurationWatcher(routinesPool, pvd, 30*time.Millisecond, []string{})
|
||||
watcher := NewConfigurationWatcher(routinesPool, pvd, 30*time.Millisecond, []string{}, "")
|
||||
|
||||
publishedConfigCount := 0
|
||||
watcher.AddListener(func(configuration dynamic.Configuration) {
|
||||
|
|
|
@ -2,7 +2,7 @@ package router
|
|||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
|
@ -827,7 +827,7 @@ func BenchmarkRouterServe(b *testing.B) {
|
|||
|
||||
res := &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(strings.NewReader("")),
|
||||
Body: io.NopCloser(strings.NewReader("")),
|
||||
}
|
||||
|
||||
routersConfig := map[string]*dynamic.Router{
|
||||
|
@ -879,7 +879,7 @@ func BenchmarkRouterServe(b *testing.B) {
|
|||
func BenchmarkService(b *testing.B) {
|
||||
res := &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(strings.NewReader("")),
|
||||
Body: io.NopCloser(strings.NewReader("")),
|
||||
}
|
||||
|
||||
serviceConfig := map[string]*dynamic.Service{
|
||||
|
|
|
@ -362,11 +362,11 @@ func (ln tcpKeepAliveListener) Accept() (net.Conn, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if err = tc.SetKeepAlive(true); err != nil {
|
||||
if err := tc.SetKeepAlive(true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = tc.SetKeepAlivePeriod(3 * time.Minute); err != nil {
|
||||
if err := tc.SetKeepAlivePeriod(3 * time.Minute); err != nil {
|
||||
// Some systems, such as OpenBSD, have no user-settable per-socket TCP
|
||||
// keepalive options.
|
||||
if !errors.Is(err, syscall.ENOPROTOOPT) {
|
||||
|
|
|
@ -48,7 +48,7 @@ func TestShutdownTCP(t *testing.T) {
|
|||
for {
|
||||
_, err := http.ReadRequest(bufio.NewReader(conn))
|
||||
|
||||
if errors.Is(err, io.EOF) || (err != nil && strings.HasSuffix(err.Error(), "use of closed network connection")) {
|
||||
if errors.Is(err, io.EOF) || (err != nil && errors.Is(err, net.ErrClosed)) {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
@ -70,6 +70,8 @@ func testShutdown(t *testing.T, router *tcp.Router) {
|
|||
|
||||
epConfig.LifeCycle.RequestAcceptGraceTimeout = 0
|
||||
epConfig.LifeCycle.GraceTimeOut = ptypes.Duration(5 * time.Second)
|
||||
epConfig.RespondingTimeouts.ReadTimeout = ptypes.Duration(5 * time.Second)
|
||||
epConfig.RespondingTimeouts.WriteTimeout = ptypes.Duration(5 * time.Second)
|
||||
|
||||
entryPoint, err := NewTCPEntryPoint(context.Background(), &static.EntryPoint{
|
||||
// We explicitly use an IPV4 address because on Alpine, with an IPV6 address
|
||||
|
@ -97,6 +99,11 @@ func testShutdown(t *testing.T, router *tcp.Router) {
|
|||
err = request.Write(conn)
|
||||
require.NoError(t, err)
|
||||
|
||||
reader := bufio.NewReader(conn)
|
||||
// Wait for first byte in response.
|
||||
_, err = reader.Peek(1)
|
||||
require.NoError(t, err)
|
||||
|
||||
go entryPoint.Shutdown(context.Background())
|
||||
|
||||
// Make sure that new connections are not permitted anymore.
|
||||
|
@ -123,7 +130,7 @@ func testShutdown(t *testing.T, router *tcp.Router) {
|
|||
|
||||
// And make sure that the connection we had opened before shutting things down is still operational
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(conn), request)
|
||||
resp, err := http.ReadResponse(reader, request)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
}
|
||||
|
@ -133,22 +140,17 @@ func startEntrypoint(entryPoint *TCPEntryPoint, router *tcp.Router) (net.Conn, e
|
|||
|
||||
entryPoint.SwitchRouter(router)
|
||||
|
||||
var conn net.Conn
|
||||
var err error
|
||||
var epStarted bool
|
||||
for i := 0; i < 10; i++ {
|
||||
conn, err = net.Dial("tcp", entryPoint.listener.Addr().String())
|
||||
conn, err := net.Dial("tcp", entryPoint.listener.Addr().String())
|
||||
if err != nil {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
epStarted = true
|
||||
break
|
||||
|
||||
return conn, err
|
||||
}
|
||||
if !epStarted {
|
||||
return nil, errors.New("entry point never started")
|
||||
}
|
||||
return conn, err
|
||||
|
||||
return nil, errors.New("entry point never started")
|
||||
}
|
||||
|
||||
func TestReadTimeoutWithoutFirstByte(t *testing.T) {
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
@ -88,7 +87,7 @@ func (m *Mirroring) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
|
||||
if errors.Is(err, errBodyTooLarge) {
|
||||
req.Body = ioutil.NopCloser(io.MultiReader(bytes.NewReader(bytesRead), req.Body))
|
||||
req.Body = io.NopCloser(io.MultiReader(bytes.NewReader(bytesRead), req.Body))
|
||||
m.handler.ServeHTTP(rw, req)
|
||||
logger.Debugf("no mirroring, request body larger than allowed size")
|
||||
return
|
||||
|
@ -147,8 +146,8 @@ func (b blackHoleResponseWriter) Header() http.Header {
|
|||
return http.Header{}
|
||||
}
|
||||
|
||||
func (b blackHoleResponseWriter) Write(bytes []byte) (int, error) {
|
||||
return len(bytes), nil
|
||||
func (b blackHoleResponseWriter) Write(data []byte) (int, error) {
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
func (b blackHoleResponseWriter) WriteHeader(statusCode int) {}
|
||||
|
@ -182,7 +181,7 @@ func newReusableRequest(req *http.Request, maxBodySize int64) (*reusableRequest,
|
|||
|
||||
// unbounded body size
|
||||
if maxBodySize < 0 {
|
||||
body, err := ioutil.ReadAll(req.Body)
|
||||
body, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -217,7 +216,7 @@ func (rr reusableRequest) clone(ctx context.Context) *http.Request {
|
|||
req := rr.req.Clone(ctx)
|
||||
|
||||
if rr.body != nil {
|
||||
req.Body = ioutil.NopCloser(bytes.NewReader(rr.body))
|
||||
req.Body = io.NopCloser(bytes.NewReader(rr.body))
|
||||
}
|
||||
|
||||
return req
|
||||
|
|
|
@ -3,7 +3,7 @@ package mirror
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync/atomic"
|
||||
|
@ -148,7 +148,7 @@ func TestMirroringWithBody(t *testing.T) {
|
|||
|
||||
handler := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
assert.NotNil(t, r.Body)
|
||||
bb, err := ioutil.ReadAll(r.Body)
|
||||
bb, err := io.ReadAll(r.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, body, bb)
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
|
@ -159,7 +159,7 @@ func TestMirroringWithBody(t *testing.T) {
|
|||
for i := 0; i < numMirrors; i++ {
|
||||
err := mirror.AddMirror(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
assert.NotNil(t, r.Body)
|
||||
bb, err := ioutil.ReadAll(r.Body)
|
||||
bb, err := io.ReadAll(r.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, body, bb)
|
||||
atomic.AddInt32(&countMirror, 1)
|
||||
|
@ -213,13 +213,13 @@ func TestCloneRequest(t *testing.T) {
|
|||
|
||||
// first call
|
||||
cloned := rr.clone(ctx)
|
||||
body, err := ioutil.ReadAll(cloned.Body)
|
||||
body, err := io.ReadAll(cloned.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, bb, body)
|
||||
|
||||
// second call
|
||||
cloned = rr.clone(ctx)
|
||||
body, err = ioutil.ReadAll(cloned.Body)
|
||||
body, err = io.ReadAll(cloned.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, bb, body)
|
||||
})
|
||||
|
|
|
@ -24,6 +24,19 @@ type stickyCookie struct {
|
|||
httpOnly bool
|
||||
}
|
||||
|
||||
// Balancer is a WeightedRoundRobin load balancer based on Earliest Deadline First (EDF).
|
||||
// (https://en.wikipedia.org/wiki/Earliest_deadline_first_scheduling)
|
||||
// Each pick from the schedule has the earliest deadline entry selected.
|
||||
// Entries have deadlines set at currentDeadline + 1 / weight,
|
||||
// providing weighted round robin behavior with floating point weights and an O(log n) pick time.
|
||||
type Balancer struct {
|
||||
stickyCookie *stickyCookie
|
||||
|
||||
mutex sync.RWMutex
|
||||
handlers []*namedHandler
|
||||
curDeadline float64
|
||||
}
|
||||
|
||||
// New creates a new load balancer.
|
||||
func New(sticky *dynamic.Sticky) *Balancer {
|
||||
balancer := &Balancer{}
|
||||
|
@ -68,19 +81,6 @@ func (b *Balancer) Pop() interface{} {
|
|||
return h
|
||||
}
|
||||
|
||||
// Balancer is a WeightedRoundRobin load balancer based on Earliest Deadline First (EDF).
|
||||
// (https://en.wikipedia.org/wiki/Earliest_deadline_first_scheduling)
|
||||
// Each pick from the schedule has the earliest deadline entry selected.
|
||||
// Entries have deadlines set at currentDeadline + 1 / weight,
|
||||
// providing weighted round robin behavior with floating point weights and an O(log n) pick time.
|
||||
type Balancer struct {
|
||||
stickyCookie *stickyCookie
|
||||
|
||||
mutex sync.RWMutex
|
||||
handlers []*namedHandler
|
||||
curDeadline float64
|
||||
}
|
||||
|
||||
func (b *Balancer) nextServer() (*namedHandler, error) {
|
||||
b.mutex.Lock()
|
||||
defer b.mutex.Unlock()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
|
@ -21,7 +21,7 @@ func (t *staticTransport) RoundTrip(r *http.Request) (*http.Response, error) {
|
|||
func BenchmarkProxy(b *testing.B) {
|
||||
res := &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(strings.NewReader("")),
|
||||
Body: io.NopCloser(strings.NewReader("")),
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
|
|
@ -123,6 +123,8 @@ func createRoundTripper(cfg *dynamic.ServersTransport) (http.RoundTripper, error
|
|||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
ReadBufferSize: 64 * 1024,
|
||||
WriteBufferSize: 64 * 1024,
|
||||
}
|
||||
|
||||
transport.RegisterProtocol("h2c", &h2cTransportWrapper{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue