Rework servers load-balancer to use the WRR
Co-authored-by: Kevin Pollet <pollet.kevin@gmail.com>
This commit is contained in:
parent
67d9c8da0b
commit
fadee5e87b
70 changed files with 2085 additions and 2211 deletions
|
@ -21,9 +21,8 @@ import (
|
|||
|
||||
// Compile time validation that the response recorder implements http interfaces correctly.
|
||||
var (
|
||||
// TODO: maybe remove at least for codeModifierWithCloseNotify.
|
||||
_ middlewares.Stateful = &codeModifierWithCloseNotify{}
|
||||
_ middlewares.Stateful = &codeCatcherWithCloseNotify{}
|
||||
_ middlewares.Stateful = &codeModifier{}
|
||||
_ middlewares.Stateful = &codeCatcher{}
|
||||
)
|
||||
|
||||
const typeName = "customError"
|
||||
|
@ -124,13 +123,6 @@ func newRequest(baseURL string) (*http.Request, error) {
|
|||
return req, nil
|
||||
}
|
||||
|
||||
type responseInterceptor interface {
|
||||
http.ResponseWriter
|
||||
http.Flusher
|
||||
getCode() int
|
||||
isFilteredCode() bool
|
||||
}
|
||||
|
||||
// codeCatcher is a response writer that detects as soon as possible whether the
|
||||
// response is a code within the ranges of codes it watches for. If it is, it
|
||||
// simply drops the data from the response. Otherwise, it forwards it directly to
|
||||
|
@ -144,27 +136,13 @@ type codeCatcher struct {
|
|||
headersSent bool
|
||||
}
|
||||
|
||||
type codeCatcherWithCloseNotify struct {
|
||||
*codeCatcher
|
||||
}
|
||||
|
||||
// CloseNotify returns a channel that receives at most a
|
||||
// single value (true) when the client connection has gone away.
|
||||
func (cc *codeCatcherWithCloseNotify) CloseNotify() <-chan bool {
|
||||
return cc.responseWriter.(http.CloseNotifier).CloseNotify()
|
||||
}
|
||||
|
||||
func newCodeCatcher(rw http.ResponseWriter, httpCodeRanges types.HTTPCodeRanges) responseInterceptor {
|
||||
catcher := &codeCatcher{
|
||||
func newCodeCatcher(rw http.ResponseWriter, httpCodeRanges types.HTTPCodeRanges) *codeCatcher {
|
||||
return &codeCatcher{
|
||||
headerMap: make(http.Header),
|
||||
code: http.StatusOK, // If backend does not call WriteHeader on us, we consider it's a 200.
|
||||
responseWriter: rw,
|
||||
httpCodeRanges: httpCodeRanges,
|
||||
}
|
||||
if _, ok := rw.(http.CloseNotifier); ok {
|
||||
return &codeCatcherWithCloseNotify{catcher}
|
||||
}
|
||||
return catcher
|
||||
}
|
||||
|
||||
func (cc *codeCatcher) Header() http.Header {
|
||||
|
@ -240,24 +218,7 @@ func (cc *codeCatcher) Flush() {
|
|||
|
||||
// codeModifier forwards a response back to the client,
|
||||
// while enforcing a given response code.
|
||||
type codeModifier interface {
|
||||
http.ResponseWriter
|
||||
}
|
||||
|
||||
// newCodeModifier returns a codeModifier that enforces the given code.
|
||||
func newCodeModifier(rw http.ResponseWriter, code int) codeModifier {
|
||||
codeMod := &codeModifierWithoutCloseNotify{
|
||||
headerMap: make(http.Header),
|
||||
code: code,
|
||||
responseWriter: rw,
|
||||
}
|
||||
if _, ok := rw.(http.CloseNotifier); ok {
|
||||
return &codeModifierWithCloseNotify{codeMod}
|
||||
}
|
||||
return codeMod
|
||||
}
|
||||
|
||||
type codeModifierWithoutCloseNotify struct {
|
||||
type codeModifier struct {
|
||||
code int // the code enforced in the response.
|
||||
|
||||
// headerSent is whether the headers have already been sent,
|
||||
|
@ -268,18 +229,17 @@ type codeModifierWithoutCloseNotify struct {
|
|||
responseWriter http.ResponseWriter
|
||||
}
|
||||
|
||||
type codeModifierWithCloseNotify struct {
|
||||
*codeModifierWithoutCloseNotify
|
||||
}
|
||||
|
||||
// CloseNotify returns a channel that receives at most a
|
||||
// single value (true) when the client connection has gone away.
|
||||
func (r *codeModifierWithCloseNotify) CloseNotify() <-chan bool {
|
||||
return r.responseWriter.(http.CloseNotifier).CloseNotify()
|
||||
// newCodeModifier returns a codeModifier that enforces the given code.
|
||||
func newCodeModifier(rw http.ResponseWriter, code int) *codeModifier {
|
||||
return &codeModifier{
|
||||
headerMap: make(http.Header),
|
||||
code: code,
|
||||
responseWriter: rw,
|
||||
}
|
||||
}
|
||||
|
||||
// Header returns the response headers.
|
||||
func (r *codeModifierWithoutCloseNotify) Header() http.Header {
|
||||
func (r *codeModifier) Header() http.Header {
|
||||
if r.headerMap == nil {
|
||||
r.headerMap = make(http.Header)
|
||||
}
|
||||
|
@ -289,14 +249,14 @@ func (r *codeModifierWithoutCloseNotify) Header() http.Header {
|
|||
|
||||
// Write calls WriteHeader to send the enforced code,
|
||||
// then writes the data directly to r.responseWriter.
|
||||
func (r *codeModifierWithoutCloseNotify) Write(buf []byte) (int, error) {
|
||||
func (r *codeModifier) Write(buf []byte) (int, error) {
|
||||
r.WriteHeader(r.code)
|
||||
return r.responseWriter.Write(buf)
|
||||
}
|
||||
|
||||
// WriteHeader sends the headers, with the enforced code (the code in argument
|
||||
// is always ignored), if it hasn't already been done.
|
||||
func (r *codeModifierWithoutCloseNotify) WriteHeader(_ int) {
|
||||
func (r *codeModifier) WriteHeader(_ int) {
|
||||
if r.headerSent {
|
||||
return
|
||||
}
|
||||
|
@ -307,7 +267,7 @@ func (r *codeModifierWithoutCloseNotify) WriteHeader(_ int) {
|
|||
}
|
||||
|
||||
// Hijack hijacks the connection.
|
||||
func (r *codeModifierWithoutCloseNotify) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
func (r *codeModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
hijacker, ok := r.responseWriter.(http.Hijacker)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("%T is not a http.Hijacker", r.responseWriter)
|
||||
|
@ -316,7 +276,7 @@ func (r *codeModifierWithoutCloseNotify) Hijack() (net.Conn, *bufio.ReadWriter,
|
|||
}
|
||||
|
||||
// Flush sends any buffered data to the client.
|
||||
func (r *codeModifierWithoutCloseNotify) Flush() {
|
||||
func (r *codeModifier) Flush() {
|
||||
r.WriteHeader(r.code)
|
||||
|
||||
if flusher, ok := r.responseWriter.(http.Flusher); ok {
|
||||
|
|
|
@ -188,50 +188,3 @@ type mockServiceBuilder struct {
|
|||
func (m *mockServiceBuilder) BuildHTTP(_ context.Context, _ string) (http.Handler, error) {
|
||||
return m.handler, nil
|
||||
}
|
||||
|
||||
func TestNewResponseRecorder(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
rw http.ResponseWriter
|
||||
expected http.ResponseWriter
|
||||
}{
|
||||
{
|
||||
desc: "Without Close Notify",
|
||||
rw: httptest.NewRecorder(),
|
||||
expected: &codeModifierWithoutCloseNotify{},
|
||||
},
|
||||
{
|
||||
desc: "With Close Notify",
|
||||
rw: &mockRWCloseNotify{},
|
||||
expected: &codeModifierWithCloseNotify{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
rec := newCodeModifier(test.rw, 0)
|
||||
assert.IsType(t, rec, test.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type mockRWCloseNotify struct{}
|
||||
|
||||
func (m *mockRWCloseNotify) CloseNotify() <-chan bool {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (m *mockRWCloseNotify) Header() http.Header {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (m *mockRWCloseNotify) Write([]byte) (int, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (m *mockRWCloseNotify) WriteHeader(int) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue