mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-16 21:56:40 -05:00
reverseproxy: Enable changing only the status code (close #2920)
This commit is contained in:
parent
7b0962ba4d
commit
7a99835dab
3 changed files with 61 additions and 34 deletions
|
@ -92,6 +92,45 @@ var errorEmptyHandler Handler = HandlerFunc(func(w http.ResponseWriter, r *http.
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ResponseHandler pairs a response matcher with custom handling
|
||||||
|
// logic. Either the status code can be changed to something else
|
||||||
|
// while using the original response body, or, if a status code
|
||||||
|
// is not set, it can execute a custom route list; this is useful
|
||||||
|
// for executing handler routes based on the properties of an HTTP
|
||||||
|
// response that has not been written out to the client yet.
|
||||||
|
//
|
||||||
|
// To use this type, provision it at module load time, then when
|
||||||
|
// ready to use, match the response against its matcher; if it
|
||||||
|
// matches (or doesn't have a matcher), change the status code on
|
||||||
|
// the response if configured; otherwise invoke the routes by
|
||||||
|
// calling `rh.Routes.Compile(next).ServeHTTP(rw, req)` (or similar).
|
||||||
|
type ResponseHandler struct {
|
||||||
|
// The response matcher for this handler. If empty/nil,
|
||||||
|
// it always matches.
|
||||||
|
Match *ResponseMatcher `json:"match,omitempty"`
|
||||||
|
|
||||||
|
// To write the original response body but with a different
|
||||||
|
// status code, set this field to the desired status code.
|
||||||
|
// If set, this takes priority over routes.
|
||||||
|
StatusCode WeakString `json:"status_code,omitempty"`
|
||||||
|
|
||||||
|
// The list of HTTP routes to execute if no status code is
|
||||||
|
// specified. If evaluated, the original response body
|
||||||
|
// will not be written.
|
||||||
|
Routes RouteList `json:"routes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provision sets up the routse in rh.
|
||||||
|
func (rh *ResponseHandler) Provision(ctx caddy.Context) error {
|
||||||
|
if rh.Routes != nil {
|
||||||
|
err := rh.Routes.Provision(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// WeakString is a type that unmarshals any JSON value
|
// WeakString is a type that unmarshals any JSON value
|
||||||
// as a string literal, with the following exceptions:
|
// as a string literal, with the following exceptions:
|
||||||
//
|
//
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -531,15 +532,32 @@ func (h *Handler) reverseProxy(rw http.ResponseWriter, req *http.Request, di Dia
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// see if any response handler is configured for this response from the backend
|
||||||
for i, rh := range h.HandleResponse {
|
for i, rh := range h.HandleResponse {
|
||||||
if len(rh.Routes) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if rh.Match != nil && !rh.Match.Match(res.StatusCode, res.Header) {
|
if rh.Match != nil && !rh.Match.Match(res.StatusCode, res.Header) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
res.Body.Close()
|
|
||||||
repl := req.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
|
repl := req.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
|
||||||
|
|
||||||
|
// if configured to only change the status code, do that then continue regular proxy response
|
||||||
|
if statusCodeStr := rh.StatusCode.String(); statusCodeStr != "" {
|
||||||
|
statusCode, err := strconv.Atoi(repl.ReplaceAll(statusCodeStr, ""))
|
||||||
|
if err != nil {
|
||||||
|
return caddyhttp.Error(http.StatusInternalServerError, err)
|
||||||
|
}
|
||||||
|
if statusCode != 0 {
|
||||||
|
res.StatusCode = statusCode
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, if there are any routes configured, execute those as the
|
||||||
|
// actual response instead of what we got from the proxy backend
|
||||||
|
if len(rh.Routes) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
res.Body.Close()
|
||||||
repl.Set("http.reverse_proxy.status_code", res.StatusCode)
|
repl.Set("http.reverse_proxy.status_code", res.StatusCode)
|
||||||
repl.Set("http.reverse_proxy.status_text", res.Status)
|
repl.Set("http.reverse_proxy.status_text", res.Status)
|
||||||
h.logger.Debug("handling response", zap.Int("handler", i))
|
h.logger.Debug("handling response", zap.Int("handler", i))
|
||||||
|
|
|
@ -80,36 +80,6 @@ func (sr *Subroute) ServeHTTP(w http.ResponseWriter, r *http.Request, next Handl
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResponseHandler pairs a response matcher with a route list.
|
|
||||||
// It is useful for executing handler routes based on the
|
|
||||||
// properties of an HTTP response that has not been written
|
|
||||||
// out to the client yet.
|
|
||||||
//
|
|
||||||
// To use this type, provision it at module load time, then
|
|
||||||
// when ready to use, match the response against its matcher;
|
|
||||||
// if it matches (or doesn't have a matcher), invoke the routes
|
|
||||||
// by calling `rh.Routes.Compile(next).ServeHTTP(rw, req)` (or
|
|
||||||
// similar).
|
|
||||||
type ResponseHandler struct {
|
|
||||||
// The response matcher for this handler. If empty/nil,
|
|
||||||
// it always matches.
|
|
||||||
Match *ResponseMatcher `json:"match,omitempty"`
|
|
||||||
|
|
||||||
// The list of HTTP routes to execute.
|
|
||||||
Routes RouteList `json:"routes,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Provision sets up the routse in rh.
|
|
||||||
func (rh *ResponseHandler) Provision(ctx caddy.Context) error {
|
|
||||||
if rh.Routes != nil {
|
|
||||||
err := rh.Routes.Provision(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface guards
|
// Interface guards
|
||||||
var (
|
var (
|
||||||
_ caddy.Provisioner = (*Subroute)(nil)
|
_ caddy.Provisioner = (*Subroute)(nil)
|
||||||
|
|
Loading…
Reference in a new issue