@marcel91200 / caddy Mirror · read-only
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 11, 01:00 UTC

master
go 197 lines 6.42 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package reverseproxy
16
17 import (
18 "fmt"
19 "net/http"
20 "strconv"
21
22 "github.com/caddyserver/caddy/v2"
23 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
24 "github.com/caddyserver/caddy/v2/modules/caddyhttp"
25 )
26
27 func init() {
28 caddy.RegisterModule(CopyResponseHandler{})
29 caddy.RegisterModule(CopyResponseHeadersHandler{})
30 }
31
32 // CopyResponseHandler is a special HTTP handler which may
33 // only be used within reverse_proxy's handle_response routes,
34 // to copy the proxy response. EXPERIMENTAL, subject to change.
35 type CopyResponseHandler struct {
36 // To write the upstream response's body but with a different
37 // status code, set this field to the desired status code.
38 StatusCode caddyhttp.WeakString `json:"status_code,omitempty"`
39
40 ctx caddy.Context
41 }
42
43 // CaddyModule returns the Caddy module information.
44 func (CopyResponseHandler) CaddyModule() caddy.ModuleInfo {
45 return caddy.ModuleInfo{
46 ID: "http.handlers.copy_response",
47 New: func() caddy.Module { return new(CopyResponseHandler) },
48 }
49 }
50
51 // Provision ensures that h is set up properly before use.
52 func (h *CopyResponseHandler) Provision(ctx caddy.Context) error {
53 h.ctx = ctx
54 return nil
55 }
56
57 // ServeHTTP implements the Handler interface.
58 func (h CopyResponseHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request, _ caddyhttp.Handler) error {
59 repl := req.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
60 hrc, ok := req.Context().Value(proxyHandleResponseContextCtxKey).(*handleResponseContext)
61
62 // don't allow this to be used outside of handle_response routes
63 if !ok {
64 return caddyhttp.Error(http.StatusInternalServerError,
65 fmt.Errorf("cannot use 'copy_response' outside of reverse_proxy's handle_response routes"))
66 }
67
68 // allow a custom status code to be written; otherwise the
69 // status code from the upstream response is written
70 if codeStr := h.StatusCode.String(); codeStr != "" {
71 intVal, err := strconv.Atoi(repl.ReplaceAll(codeStr, ""))
72 if err != nil {
73 return caddyhttp.Error(http.StatusInternalServerError, err)
74 }
75 hrc.response.StatusCode = intVal
76 }
77
78 // make sure the reverse_proxy handler doesn't try to call
79 // finalizeResponse again after we've already done it here.
80 hrc.isFinalized = true
81
82 // write the response
83 return hrc.handler.finalizeResponse(rw, req, hrc.response, repl, hrc.start, hrc.logger, hrc.upstreamAddr)
84 }
85
86 // CopyResponseHeadersHandler is a special HTTP handler which may
87 // only be used within reverse_proxy's handle_response routes,
88 // to copy headers from the proxy response. EXPERIMENTAL;
89 // subject to change.
90 type CopyResponseHeadersHandler struct {
91 // A list of header fields to copy from the response.
92 // Cannot be defined at the same time as Exclude.
93 Include []string `json:"include,omitempty"`
94
95 // A list of header fields to skip copying from the response.
96 // Cannot be defined at the same time as Include.
97 Exclude []string `json:"exclude,omitempty"`
98
99 includeMap map[string]struct{}
100 excludeMap map[string]struct{}
101 ctx caddy.Context
102 }
103
104 // CaddyModule returns the Caddy module information.
105 func (CopyResponseHeadersHandler) CaddyModule() caddy.ModuleInfo {
106 return caddy.ModuleInfo{
107 ID: "http.handlers.copy_response_headers",
108 New: func() caddy.Module { return new(CopyResponseHeadersHandler) },
109 }
110 }
111
112 // Validate ensures the h's configuration is valid.
113 func (h *CopyResponseHeadersHandler) Validate() error {
114 if len(h.Exclude) > 0 && len(h.Include) > 0 {
115 return fmt.Errorf("cannot define both 'exclude' and 'include' lists at the same time")
116 }
117
118 return nil
119 }
120
121 // Provision ensures that h is set up properly before use.
122 func (h *CopyResponseHeadersHandler) Provision(ctx caddy.Context) error {
123 h.ctx = ctx
124
125 // Optimize the include list by converting it to a map
126 if len(h.Include) > 0 {
127 h.includeMap = map[string]struct{}{}
128 }
129 for _, field := range h.Include {
130 h.includeMap[http.CanonicalHeaderKey(field)] = struct{}{}
131 }
132
133 // Optimize the exclude list by converting it to a map
134 if len(h.Exclude) > 0 {
135 h.excludeMap = map[string]struct{}{}
136 }
137 for _, field := range h.Exclude {
138 h.excludeMap[http.CanonicalHeaderKey(field)] = struct{}{}
139 }
140
141 return nil
142 }
143
144 // ServeHTTP implements the Handler interface.
145 func (h CopyResponseHeadersHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next caddyhttp.Handler) error {
146 hrc, ok := req.Context().Value(proxyHandleResponseContextCtxKey).(*handleResponseContext)
147
148 // don't allow this to be used outside of handle_response routes
149 if !ok {
150 return caddyhttp.Error(http.StatusInternalServerError,
151 fmt.Errorf("cannot use 'copy_response_headers' outside of reverse_proxy's handle_response routes"))
152 }
153
154 // Hop-by-hop fields belong to the upstream connection. Strip them
155 // before include and exclude, so naming one in include does not
156 // copy it. Clone first: later handlers in this handle_response
157 // route still need the original upstream headers.
158 src := hrc.response.Header.Clone()
159 stripHopByHopHeaders(src)
160
161 for field, values := range src {
162 // Check the include list first, skip
163 // the header if it's _not_ in this list.
164 if len(h.includeMap) > 0 {
165 if _, ok := h.includeMap[field]; !ok {
166 continue
167 }
168 }
169
170 // Then, check the exclude list, skip
171 // the header if it _is_ in this list.
172 if len(h.excludeMap) > 0 {
173 if _, ok := h.excludeMap[field]; ok {
174 continue
175 }
176 }
177
178 // Copy all the values for the header.
179 for _, value := range values {
180 rw.Header().Add(field, value)
181 }
182 }
183
184 return next.ServeHTTP(rw, req)
185 }
186
187 // Interface guards
188 var (
189 _ caddyhttp.MiddlewareHandler = (*CopyResponseHandler)(nil)
190 _ caddyfile.Unmarshaler = (*CopyResponseHandler)(nil)
191 _ caddy.Provisioner = (*CopyResponseHandler)(nil)
192
193 _ caddyhttp.MiddlewareHandler = (*CopyResponseHeadersHandler)(nil)
194 _ caddyfile.Unmarshaler = (*CopyResponseHeadersHandler)(nil)
195 _ caddy.Provisioner = (*CopyResponseHeadersHandler)(nil)
196 _ caddy.Validator = (*CopyResponseHeadersHandler)(nil)
197 )