@marcel91200 / caddy Mirror · read-only / commits / 7a99835d
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 14, 05:15 UTC

reverseproxy: Enable changing only the status code (close #2920)

Matthew Holt committed Jun 4, 2020 at 12:06 UTC 7a99835dab64f7864186185761bbf5194216f8b6
3 files changed +60 -33
modules/caddyhttp/caddyhttp.go
+39
@@ -92,6 +92,45 @@ var errorEmptyHandler Handler = HandlerFunc(func(w http.ResponseWriter, r *http.
92 return nil
93 })
94
95 +// ResponseHandler pairs a response matcher with custom handling
96 +// logic. Either the status code can be changed to something else
97 +// while using the original response body, or, if a status code
98 +// is not set, it can execute a custom route list; this is useful
99 +// for executing handler routes based on the properties of an HTTP
100 +// response that has not been written out to the client yet.
101 +//
102 +// To use this type, provision it at module load time, then when
103 +// ready to use, match the response against its matcher; if it
104 +// matches (or doesn't have a matcher), change the status code on
105 +// the response if configured; otherwise invoke the routes by
106 +// calling `rh.Routes.Compile(next).ServeHTTP(rw, req)` (or similar).
107 +type ResponseHandler struct {
108 + // The response matcher for this handler. If empty/nil,
109 + // it always matches.
110 + Match *ResponseMatcher `json:"match,omitempty"`
111 +
112 + // To write the original response body but with a different
113 + // status code, set this field to the desired status code.
114 + // If set, this takes priority over routes.
115 + StatusCode WeakString `json:"status_code,omitempty"`
116 +
117 + // The list of HTTP routes to execute if no status code is
118 + // specified. If evaluated, the original response body
119 + // will not be written.
120 + Routes RouteList `json:"routes,omitempty"`
121 +}
122 +
123 +// Provision sets up the routse in rh.
124 +func (rh *ResponseHandler) Provision(ctx caddy.Context) error {
125 + if rh.Routes != nil {
126 + err := rh.Routes.Provision(ctx)
127 + if err != nil {
128 + return err
129 + }
130 + }
131 + return nil
132 +}
133 +
134 // WeakString is a type that unmarshals any JSON value
135 // as a string literal, with the following exceptions:
136 //
modules/caddyhttp/reverseproxy/reverseproxy.go
+21 -3
@@ -24,6 +24,7 @@ import (
24 "net"
25 "net/http"
26 "regexp"
27 + "strconv"
28 "strings"
29 "sync"
30 "time"
@@ -531,15 +532,32 @@ func (h *Handler) reverseProxy(rw http.ResponseWriter, req *http.Request, di Dia
532 }
533 }
534
535 + // see if any response handler is configured for this response from the backend
536 for i, rh := range h.HandleResponse {
535 - if len(rh.Routes) == 0 {
537 + if rh.Match != nil && !rh.Match.Match(res.StatusCode, res.Header) {
538 continue
539 }
538 - if rh.Match != nil && !rh.Match.Match(res.StatusCode, res.Header) {
540 +
541 + repl := req.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
542 +
543 + // if configured to only change the status code, do that then continue regular proxy response
544 + if statusCodeStr := rh.StatusCode.String(); statusCodeStr != "" {
545 + statusCode, err := strconv.Atoi(repl.ReplaceAll(statusCodeStr, ""))
546 + if err != nil {
547 + return caddyhttp.Error(http.StatusInternalServerError, err)
548 + }
549 + if statusCode != 0 {
550 + res.StatusCode = statusCode
551 + }
552 + break
553 + }
554 +
555 + // otherwise, if there are any routes configured, execute those as the
556 + // actual response instead of what we got from the proxy backend
557 + if len(rh.Routes) == 0 {
558 continue
559 }
560 res.Body.Close()
542 - repl := req.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
561 repl.Set("http.reverse_proxy.status_code", res.StatusCode)
562 repl.Set("http.reverse_proxy.status_text", res.Status)
563 h.logger.Debug("handling response", zap.Int("handler", i))
modules/caddyhttp/subroute.go
-30
@@ -80,36 +80,6 @@ func (sr *Subroute) ServeHTTP(w http.ResponseWriter, r *http.Request, next Handl
80 return err
81 }
82
83 -// ResponseHandler pairs a response matcher with a route list.
84 -// It is useful for executing handler routes based on the
85 -// properties of an HTTP response that has not been written
86 -// out to the client yet.
87 -//
88 -// To use this type, provision it at module load time, then
89 -// when ready to use, match the response against its matcher;
90 -// if it matches (or doesn't have a matcher), invoke the routes
91 -// by calling `rh.Routes.Compile(next).ServeHTTP(rw, req)` (or
92 -// similar).
93 -type ResponseHandler struct {
94 - // The response matcher for this handler. If empty/nil,
95 - // it always matches.
96 - Match *ResponseMatcher `json:"match,omitempty"`
97 -
98 - // The list of HTTP routes to execute.
99 - Routes RouteList `json:"routes,omitempty"`
100 -}
101 -
102 -// Provision sets up the routse in rh.
103 -func (rh *ResponseHandler) Provision(ctx caddy.Context) error {
104 - if rh.Routes != nil {
105 - err := rh.Routes.Provision(ctx)
106 - if err != nil {
107 - return err
108 - }
109 - }
110 - return nil
111 -}
112 -
83 // Interface guards
84 var (
85 _ caddy.Provisioner = (*Subroute)(nil)