influxdb/kit/feature/http_proxy.go

74 lines
2.0 KiB
Go

package feature
import (
"context"
"net/http"
"net/http/httputil"
"net/url"
"go.uber.org/zap"
)
// HTTPProxy is an HTTP proxy that's guided by a feature flag. If the feature flag
// presented to it is enabled, it will perform the proxying behavior. Otherwise
// it will be a no-op.
type HTTPProxy struct {
proxy *httputil.ReverseProxy
logger *zap.Logger
enabler ProxyEnabler
}
// NewHTTPProxy returns a new Proxy.
func NewHTTPProxy(dest *url.URL, logger *zap.Logger, enabler ProxyEnabler) *HTTPProxy {
return &HTTPProxy{
proxy: newReverseProxy(dest, enabler.Key()),
logger: logger,
enabler: enabler,
}
}
// Do performs the proxying. It returns whether or not the request was proxied.
func (p *HTTPProxy) Do(w http.ResponseWriter, r *http.Request) bool {
if p.enabler.Enabled(r.Context()) {
p.proxy.ServeHTTP(w, r)
return true
}
return false
}
const (
// headerProxyFlag is the HTTP header for enriching the request and response
// with the feature flag key that precipitated the proxying behavior.
headerProxyFlag = "X-Platform-Proxy-Flag"
)
// newReverseProxy creates a new single-host reverse proxy.
func newReverseProxy(dest *url.URL, enablerKey string) *httputil.ReverseProxy {
proxy := httputil.NewSingleHostReverseProxy(dest)
defaultDirector := proxy.Director
proxy.Director = func(r *http.Request) {
defaultDirector(r)
r.Header.Set(headerProxyFlag, enablerKey)
// Override r.Host to prevent us sending this request back to ourselves.
// A bug in the stdlib causes this value to be preferred over the
// r.URL.Host (which is set in the default Director) if r.Host isn't
// empty (which it isn't).
// https://github.com/golang/go/issues/28168
r.Host = dest.Host
}
proxy.ModifyResponse = func(r *http.Response) error {
r.Header.Set(headerProxyFlag, enablerKey)
return nil
}
return proxy
}
// ProxyEnabler is a boolean feature flag.
type ProxyEnabler interface {
Key() string
Enabled(ctx context.Context, fs ...Flagger) bool
}