@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 342 lines 10.9 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 caddyhttp
16
17 import (
18 "bytes"
19 "encoding/json"
20 "io"
21 "net"
22 "net/http"
23 "path"
24 "path/filepath"
25 "strconv"
26 "strings"
27
28 "github.com/caddyserver/caddy/v2"
29 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
30 )
31
32 func init() {
33 caddy.RegisterModule(tlsPlaceholderWrapper{})
34 }
35
36 // RequestMatcher is a type that can match to a request.
37 // A route matcher MUST NOT modify the request, with the
38 // only exception being its context.
39 //
40 // Deprecated: Matchers should now implement RequestMatcherWithError.
41 // You may remove any interface guards for RequestMatcher
42 // but keep your Match() methods for backwards compatibility.
43 type RequestMatcher interface {
44 Match(*http.Request) bool
45 }
46
47 // RequestMatcherWithError is like RequestMatcher but can return an error.
48 // An error during matching will abort the request middleware chain and
49 // invoke the error middleware chain.
50 //
51 // This will eventually replace RequestMatcher. Matcher modules
52 // should implement both interfaces, and once all modules have
53 // been updated to use RequestMatcherWithError, the RequestMatcher
54 // interface may eventually be dropped.
55 type RequestMatcherWithError interface {
56 MatchWithError(*http.Request) (bool, error)
57 }
58
59 // Handler is like http.Handler except ServeHTTP may return an error.
60 //
61 // If any handler encounters an error, it should be returned for proper
62 // handling. Return values should be propagated down the middleware chain
63 // by returning it unchanged. Returned errors should not be re-wrapped
64 // if they are already HandlerError values.
65 type Handler interface {
66 ServeHTTP(http.ResponseWriter, *http.Request) error
67 }
68
69 // HandlerFunc is a convenience type like http.HandlerFunc.
70 type HandlerFunc func(http.ResponseWriter, *http.Request) error
71
72 // ServeHTTP implements the Handler interface.
73 func (f HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) error {
74 return f(w, r)
75 }
76
77 // Middleware chains one Handler to the next by being passed
78 // the next Handler in the chain.
79 type Middleware func(Handler) Handler
80
81 // MiddlewareHandler is like Handler except it takes as a third
82 // argument the next handler in the chain. The next handler will
83 // never be nil, but may be a no-op handler if this is the last
84 // handler in the chain. Handlers which act as middleware should
85 // call the next handler's ServeHTTP method so as to propagate
86 // the request down the chain properly. Handlers which act as
87 // responders (content origins) need not invoke the next handler,
88 // since the last handler in the chain should be the first to
89 // write the response.
90 type MiddlewareHandler interface {
91 ServeHTTP(http.ResponseWriter, *http.Request, Handler) error
92 }
93
94 // emptyHandler is used as a no-op handler.
95 var emptyHandler Handler = HandlerFunc(func(_ http.ResponseWriter, req *http.Request) error {
96 SetVar(req.Context(), "unhandled", true)
97 return nil
98 })
99
100 // An implicit suffix middleware that, if reached, sets the StatusCode to the
101 // error stored in the ErrorCtxKey. This is to prevent situations where the
102 // Error chain does not actually handle the error (for instance, it matches only
103 // on some errors). See #3053
104 var errorEmptyHandler Handler = HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
105 httpError := r.Context().Value(ErrorCtxKey)
106 if handlerError, ok := httpError.(HandlerError); ok {
107 w.WriteHeader(handlerError.StatusCode)
108 } else {
109 w.WriteHeader(http.StatusInternalServerError)
110 }
111 return nil
112 })
113
114 // ResponseHandler pairs a response matcher with custom handling
115 // logic. Either the status code can be changed to something else
116 // while using the original response body, or, if a status code
117 // is not set, it can execute a custom route list; this is useful
118 // for executing handler routes based on the properties of an HTTP
119 // response that has not been written out to the client yet.
120 //
121 // To use this type, provision it at module load time, then when
122 // ready to use, match the response against its matcher; if it
123 // matches (or doesn't have a matcher), change the status code on
124 // the response if configured; otherwise invoke the routes by
125 // calling `rh.Routes.Compile(next).ServeHTTP(rw, req)` (or similar).
126 type ResponseHandler struct {
127 // The response matcher for this handler. If empty/nil,
128 // it always matches.
129 Match *ResponseMatcher `json:"match,omitempty"`
130
131 // To write the original response body but with a different
132 // status code, set this field to the desired status code.
133 // If set, this takes priority over routes.
134 StatusCode WeakString `json:"status_code,omitempty"`
135
136 // The list of HTTP routes to execute if no status code is
137 // specified. If evaluated, the original response body
138 // will not be written.
139 Routes RouteList `json:"routes,omitempty"`
140 }
141
142 // Provision sets up the routes in rh.
143 func (rh *ResponseHandler) Provision(ctx caddy.Context) error {
144 if rh.Routes != nil {
145 err := rh.Routes.Provision(ctx)
146 if err != nil {
147 return err
148 }
149 }
150 return nil
151 }
152
153 // WeakString is a type that unmarshals any JSON value
154 // as a string literal, with the following exceptions:
155 //
156 // 1. actual string values are decoded as strings; and
157 // 2. null is decoded as empty string;
158 //
159 // and provides methods for getting the value as various
160 // primitive types. However, using this type removes any
161 // type safety as far as deserializing JSON is concerned.
162 type WeakString string
163
164 // UnmarshalJSON satisfies json.Unmarshaler according to
165 // this type's documentation.
166 func (ws *WeakString) UnmarshalJSON(b []byte) error {
167 if len(b) == 0 {
168 return io.EOF
169 }
170 if b[0] == byte('"') && b[len(b)-1] == byte('"') {
171 var s string
172 err := json.Unmarshal(b, &s)
173 if err != nil {
174 return err
175 }
176 *ws = WeakString(s)
177 return nil
178 }
179 if bytes.Equal(b, []byte("null")) {
180 return nil
181 }
182 *ws = WeakString(b)
183 return nil
184 }
185
186 // MarshalJSON marshals was a boolean if true or false,
187 // a number if an integer, or a string otherwise.
188 func (ws WeakString) MarshalJSON() ([]byte, error) {
189 if ws == "true" {
190 return []byte("true"), nil
191 }
192 if ws == "false" {
193 return []byte("false"), nil
194 }
195 if num, err := strconv.Atoi(string(ws)); err == nil {
196 return json.Marshal(num)
197 }
198 return json.Marshal(string(ws))
199 }
200
201 // Int returns ws as an integer. If ws is not an
202 // integer, 0 is returned.
203 func (ws WeakString) Int() int {
204 num, _ := strconv.Atoi(string(ws))
205 return num
206 }
207
208 // Float64 returns ws as a float64. If ws is not a
209 // float value, the zero value is returned.
210 func (ws WeakString) Float64() float64 {
211 num, _ := strconv.ParseFloat(string(ws), 64)
212 return num
213 }
214
215 // Bool returns ws as a boolean. If ws is not a
216 // boolean, false is returned.
217 func (ws WeakString) Bool() bool {
218 return string(ws) == "true"
219 }
220
221 // String returns ws as a string.
222 func (ws WeakString) String() string {
223 return string(ws)
224 }
225
226 // StatusCodeMatches returns true if a real HTTP status code matches
227 // the configured status code, which may be either a real HTTP status
228 // code or an integer representing a class of codes (e.g. 4 for all
229 // 4xx statuses).
230 func StatusCodeMatches(actual, configured int) bool {
231 if actual == configured {
232 return true
233 }
234 if configured < 100 &&
235 actual >= configured*100 &&
236 actual < (configured+1)*100 {
237 return true
238 }
239 return false
240 }
241
242 // SanitizedPathJoin performs filepath.Join(root, reqPath) that
243 // is safe against directory traversal attacks. It uses logic
244 // similar to that in the Go standard library, specifically
245 // in the implementation of http.Dir. The root is assumed to
246 // be a trusted path, but reqPath is not; and the output will
247 // never be outside of root. The resulting path can be used
248 // with the local file system. If root is empty, the current
249 // directory is assumed. If the cleaned request path is deemed
250 // not local according to lexical processing (i.e. ignoring links),
251 // it will be rejected as unsafe and only the root will be returned.
252 func SanitizedPathJoin(root, reqPath string) string {
253 if root == "" {
254 root = "."
255 }
256
257 relPath := path.Clean("/" + reqPath)[1:] // clean path and trim the leading /
258 if relPath != "" && !filepath.IsLocal(relPath) {
259 // path is unsafe (see https://github.com/golang/go/issues/56336#issuecomment-1416214885)
260 return root
261 }
262
263 path := filepath.Join(root, filepath.FromSlash(relPath))
264
265 // filepath.Join also cleans the path, and cleaning strips
266 // the trailing slash, so we need to re-add it afterwards.
267 // if the length is 1, then it's a path to the root,
268 // and that should return ".", so we don't append the separator.
269 if strings.HasSuffix(reqPath, "/") && len(reqPath) > 1 {
270 path += separator
271 }
272
273 return path
274 }
275
276 // CleanPath cleans path p according to path.Clean(), but only
277 // merges repeated slashes if collapseSlashes is true, and always
278 // preserves trailing slashes.
279 func CleanPath(p string, collapseSlashes bool) string {
280 if collapseSlashes {
281 return cleanPath(p)
282 }
283
284 // insert an invalid/impossible URI character into each two consecutive
285 // slashes to expand empty path segments; then clean the path as usual,
286 // and then remove the remaining temporary characters.
287 const tmpCh = 0xff
288 var sb strings.Builder
289 sb.Grow(len(p) + 1)
290 for i, ch := range p {
291 if ch == '/' && i > 0 && p[i-1] == '/' {
292 sb.WriteByte(tmpCh)
293 }
294 sb.WriteRune(ch)
295 }
296 halfCleaned := cleanPath(sb.String())
297 halfCleaned = strings.ReplaceAll(halfCleaned, string([]byte{tmpCh}), "")
298
299 return halfCleaned
300 }
301
302 // cleanPath does path.Clean(p) but preserves any trailing slash.
303 func cleanPath(p string) string {
304 cleaned := path.Clean(p)
305 if cleaned != "/" && strings.HasSuffix(p, "/") {
306 cleaned = cleaned + "/"
307 }
308 return cleaned
309 }
310
311 // tlsPlaceholderWrapper is a no-op listener wrapper that marks
312 // where the TLS listener should be in a chain of listener wrappers.
313 // It should only be used if another listener wrapper must be placed
314 // in front of the TLS handshake.
315 type tlsPlaceholderWrapper struct{}
316
317 func (tlsPlaceholderWrapper) CaddyModule() caddy.ModuleInfo {
318 return caddy.ModuleInfo{
319 ID: "caddy.listeners.tls",
320 New: func() caddy.Module { return new(tlsPlaceholderWrapper) },
321 }
322 }
323
324 func (tlsPlaceholderWrapper) WrapListener(ln net.Listener) net.Listener { return ln }
325
326 func (tlsPlaceholderWrapper) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { return nil }
327
328 const (
329 // DefaultHTTPPort is the default port for HTTP.
330 DefaultHTTPPort = 80
331
332 // DefaultHTTPSPort is the default port for HTTPS.
333 DefaultHTTPSPort = 443
334 )
335
336 const separator = string(filepath.Separator)
337
338 // Interface guard
339 var (
340 _ caddy.ListenerWrapper = (*tlsPlaceholderWrapper)(nil)
341 _ caddyfile.Unmarshaler = (*tlsPlaceholderWrapper)(nil)
342 )