|
@ -37,12 +37,14 @@ func init() { |
|
|
// Handler implements a highly configurable and production-ready reverse proxy.
|
|
|
// Handler implements a highly configurable and production-ready reverse proxy.
|
|
|
type Handler struct { |
|
|
type Handler struct { |
|
|
TransportRaw json.RawMessage `json:"transport,omitempty"` |
|
|
TransportRaw json.RawMessage `json:"transport,omitempty"` |
|
|
|
|
|
CBRaw json.RawMessage `json:"circuit_breaker,omitempty"` |
|
|
LoadBalancing *LoadBalancing `json:"load_balancing,omitempty"` |
|
|
LoadBalancing *LoadBalancing `json:"load_balancing,omitempty"` |
|
|
HealthChecks *HealthChecks `json:"health_checks,omitempty"` |
|
|
HealthChecks *HealthChecks `json:"health_checks,omitempty"` |
|
|
Upstreams UpstreamPool `json:"upstreams,omitempty"` |
|
|
Upstreams UpstreamPool `json:"upstreams,omitempty"` |
|
|
FlushInterval caddy.Duration `json:"flush_interval,omitempty"` |
|
|
FlushInterval caddy.Duration `json:"flush_interval,omitempty"` |
|
|
|
|
|
|
|
|
Transport http.RoundTripper `json:"-"` |
|
|
Transport http.RoundTripper `json:"-"` |
|
|
|
|
|
CB CircuitBreaker `json:"-"` |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// CaddyModule returns the Caddy module information.
|
|
|
// CaddyModule returns the Caddy module information.
|
|
@ -55,6 +57,7 @@ func (Handler) CaddyModule() caddy.ModuleInfo { |
|
|
|
|
|
|
|
|
// Provision ensures that h is set up properly before use.
|
|
|
// Provision ensures that h is set up properly before use.
|
|
|
func (h *Handler) Provision(ctx caddy.Context) error { |
|
|
func (h *Handler) Provision(ctx caddy.Context) error { |
|
|
|
|
|
// start by loading modules
|
|
|
if h.TransportRaw != nil { |
|
|
if h.TransportRaw != nil { |
|
|
val, err := ctx.LoadModuleInline("protocol", "http.handlers.reverse_proxy.transport", h.TransportRaw) |
|
|
val, err := ctx.LoadModuleInline("protocol", "http.handlers.reverse_proxy.transport", h.TransportRaw) |
|
|
if err != nil { |
|
|
if err != nil { |
|
@ -73,6 +76,14 @@ func (h *Handler) Provision(ctx caddy.Context) error { |
|
|
h.LoadBalancing.SelectionPolicy = val.(Selector) |
|
|
h.LoadBalancing.SelectionPolicy = val.(Selector) |
|
|
h.LoadBalancing.SelectionPolicyRaw = nil // allow GC to deallocate - TODO: Does this help?
|
|
|
h.LoadBalancing.SelectionPolicyRaw = nil // allow GC to deallocate - TODO: Does this help?
|
|
|
} |
|
|
} |
|
|
|
|
|
if h.CBRaw != nil { |
|
|
|
|
|
val, err := ctx.LoadModuleInline("type", "http.handlers.reverse_proxy.circuit_breakers", h.CBRaw) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
return fmt.Errorf("loading circuit breaker module: %s", err) |
|
|
|
|
|
} |
|
|
|
|
|
h.CB = val.(CircuitBreaker) |
|
|
|
|
|
h.CBRaw = nil // allow GC to deallocate - TODO: Does this help?
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if h.Transport == nil { |
|
|
if h.Transport == nil { |
|
|
h.Transport = defaultTransport |
|
|
h.Transport = defaultTransport |
|
@ -123,6 +134,8 @@ func (h *Handler) Provision(ctx caddy.Context) error { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
for _, upstream := range h.Upstreams { |
|
|
for _, upstream := range h.Upstreams { |
|
|
|
|
|
upstream.cb = h.CB |
|
|
|
|
|
|
|
|
// url parser requires a scheme
|
|
|
// url parser requires a scheme
|
|
|
if !strings.Contains(upstream.Address, "://") { |
|
|
if !strings.Contains(upstream.Address, "://") { |
|
|
upstream.Address = "http://" + upstream.Address |
|
|
upstream.Address = "http://" + upstream.Address |
|
@ -307,6 +320,11 @@ func (h *Handler) reverseProxy(rw http.ResponseWriter, req *http.Request, upstre |
|
|
return err |
|
|
return err |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// update circuit breaker on current conditions
|
|
|
|
|
|
if upstream.cb != nil { |
|
|
|
|
|
upstream.cb.RecordMetric(res.StatusCode, latency) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// perform passive health checks (if enabled)
|
|
|
// perform passive health checks (if enabled)
|
|
|
if h.HealthChecks != nil && h.HealthChecks.Passive != nil { |
|
|
if h.HealthChecks != nil && h.HealthChecks.Passive != nil { |
|
|
// strike if the status code matches one that is "bad"
|
|
|
// strike if the status code matches one that is "bad"
|
|
|