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)