From dc8d5ef744115ebf7908b230aca0965c8c8e5d0b Mon Sep 17 00:00:00 2001 From: Tom Moulard Date: Thu, 29 Apr 2021 17:56:03 +0200 Subject: [PATCH] Add a mechanism to format the sticky cookie value Co-authored-by: Jean-Baptiste Doumenjou <925513+jbdoumenjou@users.noreply.github.com> --- docs/content/routing/services/index.md | 4 +-- go.mod | 2 +- go.sum | 6 +++-- pkg/server/service/service.go | 9 ++++++- pkg/server/service/service_test.go | 34 ++++++++++++++++++++++++++ 5 files changed, 49 insertions(+), 6 deletions(-) diff --git a/docs/content/routing/services/index.md b/docs/content/routing/services/index.md index 5add1bbbd..95f79b4fb 100644 --- a/docs/content/routing/services/index.md +++ b/docs/content/routing/services/index.md @@ -167,8 +167,8 @@ For now, only round robin load balancing is supported: #### Sticky sessions -When sticky sessions are enabled, a cookie is set on the initial request and response to let the client know which server handles the first response. -On subsequent requests, to keep the session alive with the same server, the client should resend the same cookie. +When sticky sessions are enabled, a `Set-Cookie` header is set on the initial response to let the client know which server handles the first response. +On subsequent requests, to keep the session alive with the same server, the client should send the cookie with the value set. !!! info "Stickiness on multiple levels" diff --git a/go.mod b/go.mod index f53580f17..88837e10a 100644 --- a/go.mod +++ b/go.mod @@ -77,7 +77,7 @@ require ( github.com/unrolled/render v1.0.2 github.com/unrolled/secure v1.0.7 github.com/vdemeester/shakers v0.1.0 - github.com/vulcand/oxy v1.2.0 + github.com/vulcand/oxy v1.3.0 github.com/vulcand/predicate v1.1.0 go.elastic.co/apm v1.7.0 go.elastic.co/apm/module/apmot v1.7.0 diff --git a/go.sum b/go.sum index e02163ffe..ea44b81a7 100644 --- a/go.sum +++ b/go.sum @@ -912,6 +912,8 @@ github.com/santhosh-tekuri/jsonschema v1.2.4 h1:hNhW8e7t+H1vgY+1QeEQpveR6D4+OwKP github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM= +github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -1039,8 +1041,8 @@ github.com/vdemeester/shakers v0.1.0/go.mod h1:IZ1HHynUOQt32iQ3rvAeVddXLd19h/6LW github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= -github.com/vulcand/oxy v1.2.0 h1:Y2Wt1EgQddA/qMnp1+YXjehPsyw2Gy8CAfavkFF+fQk= -github.com/vulcand/oxy v1.2.0/go.mod h1:nGeNTWfyYQj3ghi3W8ok7vLSkw7Gkvr0x+G/v8Wk7vM= +github.com/vulcand/oxy v1.3.0 h1:358BVHmJNLjhOrhbjq2EVJX5NQ3HxrP0d5OyHLRliX0= +github.com/vulcand/oxy v1.3.0/go.mod h1:hN/gw/jg+GH4A+bqvznsW26Izd4jNGV6h1z3s7drRzs= github.com/vulcand/predicate v1.1.0 h1:Gq/uWopa4rx/tnZu2opOSBqHK63Yqlou/SzrbwdJiNg= github.com/vulcand/predicate v1.1.0/go.mod h1:mlccC5IRBoc2cIFmCB8ZM62I3VDb6p2GXESMHa3CnZg= github.com/vultr/govultr/v2 v2.4.0 h1:6ySGGAsoOann0lmVNkS8grLvbAT2iYWnO4R1RVYFg0A= diff --git a/pkg/server/service/service.go b/pkg/server/service/service.go index 33a019423..c9e238b3f 100644 --- a/pkg/server/service/service.go +++ b/pkg/server/service/service.go @@ -26,6 +26,7 @@ import ( "github.com/traefik/traefik/v2/pkg/server/service/loadbalancer/mirror" "github.com/traefik/traefik/v2/pkg/server/service/loadbalancer/wrr" "github.com/vulcand/oxy/roundrobin" + "github.com/vulcand/oxy/roundrobin/stickycookie" ) const ( @@ -310,7 +311,13 @@ func (m *Manager) getLoadBalancer(ctx context.Context, serviceName string, servi SameSite: convertSameSite(service.Sticky.Cookie.SameSite), } - options = append(options, roundrobin.EnableStickySession(roundrobin.NewStickySessionWithOptions(cookieName, opts))) + // Sticky Cookie Value + cv, err := stickycookie.NewFallbackValue(&stickycookie.RawValue{}, &stickycookie.HashValue{}) + if err != nil { + return nil, err + } + + options = append(options, roundrobin.EnableStickySession(roundrobin.NewStickySessionWithOptions(cookieName, opts).SetCookieValue(cv))) logger.Debugf("Sticky session cookie name: %v", cookieName) } diff --git a/pkg/server/service/service_test.go b/pkg/server/service/service_test.go index cf2459078..49fd7a1be 100644 --- a/pkg/server/service/service_test.go +++ b/pkg/server/service/service_test.go @@ -120,6 +120,7 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) { serviceName string service *dynamic.ServersLoadBalancer responseModifier func(*http.Response) error + cookieRawValue string expected []ExpectedResult }{ @@ -258,6 +259,34 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) { }, }, }, + { + desc: "Cookie value is backward compatible", + serviceName: "test", + service: &dynamic.ServersLoadBalancer{ + Sticky: &dynamic.Sticky{ + Cookie: &dynamic.Cookie{}, + }, + Servers: []dynamic.Server{ + { + URL: server1.URL, + }, + { + URL: server2.URL, + }, + }, + }, + cookieRawValue: "_6f743=" + server1.URL, + expected: []ExpectedResult{ + { + StatusCode: http.StatusOK, + XFrom: "first", + }, + { + StatusCode: http.StatusOK, + XFrom: "first", + }, + }, + }, } for _, test := range testCases { @@ -269,6 +298,10 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) { assert.NotNil(t, handler) req := testhelpers.MustNewRequest(http.MethodGet, "http://callme", nil) + if test.cookieRawValue != "" { + req.Header.Set("Cookie", test.cookieRawValue) + } + for _, expected := range test.expected { recorder := httptest.NewRecorder() @@ -282,6 +315,7 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) { req.Header.Set("Cookie", cookieHeader) assert.Equal(t, expected.SecureCookie, strings.Contains(cookieHeader, "Secure")) assert.Equal(t, expected.HTTPOnlyCookie, strings.Contains(cookieHeader, "HttpOnly")) + assert.NotContains(t, cookieHeader, "://") } } })