Files
oxy/roundrobin/stickysessions.go

87 lines
2.2 KiB
Go

package roundrobin
import (
"errors"
"net/http"
"net/url"
"time"
"github.com/vulcand/oxy/v2/roundrobin/stickycookie"
)
// CookieOptions has all the options one would like to set on the affinity cookie.
type CookieOptions struct {
HTTPOnly bool
Secure bool
Path string
Domain string
Expires time.Time
MaxAge int
SameSite http.SameSite
}
// StickySession is a mixin for load balancers that implements layer 7 (http cookie) session affinity.
type StickySession struct {
cookieName string
cookieValue stickycookie.CookieValue
options CookieOptions
}
// NewStickySession creates a new StickySession.
func NewStickySession(cookieName string) *StickySession {
return &StickySession{cookieName: cookieName, cookieValue: &stickycookie.RawValue{}}
}
// NewStickySessionWithOptions creates a new StickySession whilst allowing for options to
// shape its affinity cookie such as "httpOnly" or "secure".
func NewStickySessionWithOptions(cookieName string, options CookieOptions) *StickySession {
return &StickySession{cookieName: cookieName, options: options, cookieValue: &stickycookie.RawValue{}}
}
// SetCookieValue set the CookieValue for the StickySession.
func (s *StickySession) SetCookieValue(value stickycookie.CookieValue) *StickySession {
s.cookieValue = value
return s
}
// GetBackend returns the backend URL stored in the sticky cookie, iff the backend is still in the valid list of servers.
func (s *StickySession) GetBackend(req *http.Request, servers []*url.URL) (*url.URL, bool, error) {
cookie, err := req.Cookie(s.cookieName)
if err != nil {
if errors.Is(err, http.ErrNoCookie) {
return nil, false, nil
}
return nil, false, err
}
server, err := s.cookieValue.FindURL(cookie.Value, servers)
return server, server != nil, err
}
// StickBackend creates and sets the cookie.
func (s *StickySession) StickBackend(backend *url.URL, w http.ResponseWriter) {
opt := s.options
cp := "/"
if opt.Path != "" {
cp = opt.Path
}
cookie := &http.Cookie{
Name: s.cookieName,
Value: s.cookieValue.Get(backend),
Path: cp,
Domain: opt.Domain,
Expires: opt.Expires,
MaxAge: opt.MaxAge,
Secure: opt.Secure,
HttpOnly: opt.HTTPOnly,
SameSite: opt.SameSite,
}
http.SetCookie(w, cookie)
}