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
| 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 | "bufio" |
| 19 | "bytes" |
| 20 | "fmt" |
| 21 | "io" |
| 22 | "net" |
| 23 | "net/http" |
| 24 | "reflect" |
| 25 | |
| 26 | "github.com/caddyserver/caddy/v2" |
| 27 | ) |
| 28 | |
| 29 | // ResponseWriterWrapper wraps an underlying ResponseWriter and |
| 30 | // promotes its Pusher method as well. To use this type, embed |
| 31 | // a pointer to it within your own struct type that implements |
| 32 | // the http.ResponseWriter interface, then call methods on the |
| 33 | // embedded value. |
| 34 | type ResponseWriterWrapper struct { |
| 35 | http.ResponseWriter |
| 36 | } |
| 37 | |
| 38 | // Push implements http.Pusher. It simply calls the underlying |
| 39 | // ResponseWriter's Push method if there is one, or returns |
| 40 | // ErrNotImplemented otherwise. |
| 41 | func (rww *ResponseWriterWrapper) Push(target string, opts *http.PushOptions) error { |
| 42 | if pusher, ok := rww.ResponseWriter.(http.Pusher); ok { |
| 43 | return pusher.Push(target, opts) |
| 44 | } |
| 45 | return ErrNotImplemented |
| 46 | } |
| 47 | |
| 48 | // ReadFrom implements io.ReaderFrom. It retries to use io.ReaderFrom if available, |
| 49 | // then fallback to io.Copy. |
| 50 | // see: https://github.com/caddyserver/caddy/issues/6546 |
| 51 | func (rww *ResponseWriterWrapper) ReadFrom(r io.Reader) (n int64, err error) { |
| 52 | if rf, ok := rww.ResponseWriter.(io.ReaderFrom); ok { |
| 53 | return rf.ReadFrom(r) |
| 54 | } |
| 55 | return io.Copy(rww.ResponseWriter, r) |
| 56 | } |
| 57 | |
| 58 | // Unwrap returns the underlying ResponseWriter, necessary for |
| 59 | // http.ResponseController to work correctly. |
| 60 | func (rww *ResponseWriterWrapper) Unwrap() http.ResponseWriter { |
| 61 | return rww.ResponseWriter |
| 62 | } |
| 63 | |
| 64 | // UnwrapResponseWriterAs walks w through its Unwrap() http.ResponseWriter |
| 65 | // chain and returns the first writer that satisfies T (along with true). |
| 66 | // If no writer in the chain satisfies T, it returns the zero value of T |
| 67 | // and false. This mirrors how http.ResponseController traverses wrapped |
| 68 | // writers internally and is useful when code needs to reach interfaces |
| 69 | // implemented only by the raw writer owned by the HTTP server — for |
| 70 | // example, Extended CONNECT or WebTransport helpers that perform their |
| 71 | // own type assertions and cannot see past a wrapper. |
| 72 | func UnwrapResponseWriterAs[T any](w http.ResponseWriter) (T, bool) { |
| 73 | var zero T |
| 74 | seen := make(map[uintptr]struct{}) |
| 75 | // Bound the walk so a cycle of uncomparable (non-pointer) writers |
| 76 | // cannot hang. Production wrapper stacks are a handful of layers. |
| 77 | for range 64 { |
| 78 | if w == nil { |
| 79 | return zero, false |
| 80 | } |
| 81 | if t, ok := any(w).(T); ok { |
| 82 | return t, true |
| 83 | } |
| 84 | if p := responseWriterPointer(w); p != 0 { |
| 85 | if _, dup := seen[p]; dup { |
| 86 | return zero, false |
| 87 | } |
| 88 | seen[p] = struct{}{} |
| 89 | } |
| 90 | u, ok := w.(interface{ Unwrap() http.ResponseWriter }) |
| 91 | if !ok { |
| 92 | return zero, false |
| 93 | } |
| 94 | w = u.Unwrap() |
| 95 | } |
| 96 | return zero, false |
| 97 | } |
| 98 | |
| 99 | func responseWriterPointer(w http.ResponseWriter) uintptr { |
| 100 | v := reflect.ValueOf(w) |
| 101 | // Only pointer-like kinds have stable identity. |
| 102 | //exhaustive:ignore |
| 103 | switch v.Kind() { |
| 104 | case reflect.Pointer, reflect.Map, reflect.Chan, reflect.Func, reflect.Slice, reflect.UnsafePointer: |
| 105 | if v.IsNil() { |
| 106 | return 0 |
| 107 | } |
| 108 | return v.Pointer() |
| 109 | default: |
| 110 | return 0 |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | // RecordHijackedStatus records that a status was already flushed to the |
| 115 | // client by a writer further down the unwrap chain (for example |
| 116 | // webtransport.Server.Upgrade writing through the naked HTTP/3 writer). |
| 117 | // It updates any wrapping responseRecorder so access logs and metrics |
| 118 | // observe the client-visible status, without writing headers again. |
| 119 | func RecordHijackedStatus(w http.ResponseWriter, status int) { |
| 120 | rec, ok := UnwrapResponseWriterAs[*responseRecorder](w) |
| 121 | if !ok || rec == nil { |
| 122 | return |
| 123 | } |
| 124 | if rec.statusCode == 0 { |
| 125 | rec.statusCode = status |
| 126 | } |
| 127 | rec.wroteHeader = true |
| 128 | rec.stream = true |
| 129 | } |
| 130 | |
| 131 | // ErrNotImplemented is returned when an underlying |
| 132 | // ResponseWriter does not implement the required method. |
| 133 | var ErrNotImplemented = fmt.Errorf("method not implemented") |
| 134 | |
| 135 | type responseRecorder struct { |
| 136 | *ResponseWriterWrapper |
| 137 | statusCode int |
| 138 | buf *bytes.Buffer |
| 139 | shouldBuffer ShouldBufferFunc |
| 140 | size int |
| 141 | wroteHeader bool |
| 142 | stream bool |
| 143 | hijacked bool |
| 144 | detached bool |
| 145 | |
| 146 | readSize *int |
| 147 | } |
| 148 | |
| 149 | // NewResponseRecorder returns a new ResponseRecorder that can be |
| 150 | // used instead of a standard http.ResponseWriter. The recorder is |
| 151 | // useful for middlewares which need to buffer a response and |
| 152 | // potentially process its entire body before actually writing the |
| 153 | // response to the underlying writer. Of course, buffering the entire |
| 154 | // body has a memory overhead, but sometimes there is no way to avoid |
| 155 | // buffering the whole response, hence the existence of this type. |
| 156 | // Still, if at all practical, handlers should strive to stream |
| 157 | // responses by wrapping Write and WriteHeader methods instead of |
| 158 | // buffering whole response bodies. |
| 159 | // |
| 160 | // Buffering is actually optional. The shouldBuffer function will |
| 161 | // be called just before the headers are written. If it returns |
| 162 | // true, the headers and body will be buffered by this recorder |
| 163 | // and not written to the underlying writer; if false, the headers |
| 164 | // will be written immediately and the body will be streamed out |
| 165 | // directly to the underlying writer. If shouldBuffer is nil, |
| 166 | // the response will never be buffered and will always be streamed |
| 167 | // directly to the writer. |
| 168 | // |
| 169 | // You can know if shouldBuffer returned true by calling Buffered(). |
| 170 | // |
| 171 | // The provided buffer buf should be obtained from a pool for best |
| 172 | // performance (see the sync.Pool type). |
| 173 | // |
| 174 | // Proper usage of a recorder looks like this: |
| 175 | // |
| 176 | // rec := caddyhttp.NewResponseRecorder(w, buf, shouldBuffer) |
| 177 | // err := next.ServeHTTP(rec, req) |
| 178 | // if err != nil { |
| 179 | // return err |
| 180 | // } |
| 181 | // if !rec.Buffered() { |
| 182 | // return nil |
| 183 | // } |
| 184 | // // process the buffered response here |
| 185 | // |
| 186 | // The header map is not buffered; i.e. the ResponseRecorder's Header() |
| 187 | // method returns the same header map of the underlying ResponseWriter. |
| 188 | // This is a crucial design decision to allow HTTP trailers to be |
| 189 | // flushed properly (https://github.com/caddyserver/caddy/issues/3236). |
| 190 | // |
| 191 | // Once you are ready to write the response, there are two ways you can |
| 192 | // do it. The easier way is to have the recorder do it: |
| 193 | // |
| 194 | // rec.WriteResponse() |
| 195 | // |
| 196 | // This writes the recorded response headers as well as the buffered body. |
| 197 | // Or, you may wish to do it yourself, especially if you manipulated the |
| 198 | // buffered body. First you will need to write the headers with the |
| 199 | // recorded status code, then write the body (this example writes the |
| 200 | // recorder's body buffer, but you might have your own body to write |
| 201 | // instead): |
| 202 | // |
| 203 | // w.WriteHeader(rec.Status()) |
| 204 | // io.Copy(w, rec.Buffer()) |
| 205 | // |
| 206 | // As a special case, 1xx responses are not buffered nor recorded |
| 207 | // because they are not the final response; they are passed through |
| 208 | // directly to the underlying ResponseWriter. |
| 209 | func NewResponseRecorder(w http.ResponseWriter, buf *bytes.Buffer, shouldBuffer ShouldBufferFunc) ResponseRecorder { |
| 210 | return &responseRecorder{ |
| 211 | ResponseWriterWrapper: &ResponseWriterWrapper{ResponseWriter: w}, |
| 212 | buf: buf, |
| 213 | shouldBuffer: shouldBuffer, |
| 214 | } |
| 215 | } |
| 216 | |
| 217 | // WriteHeader writes the headers with statusCode to the wrapped |
| 218 | // ResponseWriter unless the response is to be buffered instead. |
| 219 | // 1xx responses are never buffered, except 101 which is treated |
| 220 | // as a final upgrade response. |
| 221 | func (rr *responseRecorder) WriteHeader(statusCode int) { |
| 222 | if rr.wroteHeader { |
| 223 | return |
| 224 | } |
| 225 | |
| 226 | // save statusCode always, in case HTTP middleware upgrades websocket |
| 227 | // connections by manually setting headers and writing status 101 |
| 228 | rr.statusCode = statusCode |
| 229 | |
| 230 | // decide whether we should buffer the response |
| 231 | if rr.shouldBuffer == nil { |
| 232 | rr.stream = true |
| 233 | } else { |
| 234 | rr.stream = !rr.shouldBuffer(rr.statusCode, rr.ResponseWriterWrapper.Header()) |
| 235 | } |
| 236 | |
| 237 | // 1xx responses except 101 aren't final; just informational |
| 238 | if statusCode < 100 || statusCode > 199 || statusCode == http.StatusSwitchingProtocols { |
| 239 | rr.wroteHeader = true |
| 240 | } |
| 241 | |
| 242 | // if 1xx or not buffered, immediately write header |
| 243 | if rr.stream || (100 <= statusCode && statusCode <= 199) { |
| 244 | rr.ResponseWriterWrapper.WriteHeader(statusCode) |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | func (rr *responseRecorder) Write(data []byte) (int, error) { |
| 249 | rr.WriteHeader(http.StatusOK) |
| 250 | var n int |
| 251 | var err error |
| 252 | if rr.stream { |
| 253 | n, err = rr.ResponseWriterWrapper.Write(data) |
| 254 | } else { |
| 255 | n, err = rr.buf.Write(data) |
| 256 | } |
| 257 | |
| 258 | rr.size += n |
| 259 | return n, err |
| 260 | } |
| 261 | |
| 262 | func (rr *responseRecorder) ReadFrom(r io.Reader) (int64, error) { |
| 263 | rr.WriteHeader(http.StatusOK) |
| 264 | var n int64 |
| 265 | var err error |
| 266 | if rr.stream { |
| 267 | n, err = rr.ResponseWriterWrapper.ReadFrom(r) |
| 268 | } else { |
| 269 | n, err = rr.buf.ReadFrom(r) |
| 270 | } |
| 271 | |
| 272 | rr.size += int(n) |
| 273 | return n, err |
| 274 | } |
| 275 | |
| 276 | // Status returns the status code that was written, if any. |
| 277 | func (rr *responseRecorder) Status() int { |
| 278 | return rr.statusCode |
| 279 | } |
| 280 | |
| 281 | // Size returns the number of bytes written, |
| 282 | // not including the response headers. |
| 283 | func (rr *responseRecorder) Size() int { |
| 284 | return rr.size |
| 285 | } |
| 286 | |
| 287 | // Buffer returns the body buffer that rr was created with. |
| 288 | // You should still have your original pointer, though. |
| 289 | func (rr *responseRecorder) Buffer() *bytes.Buffer { |
| 290 | return rr.buf |
| 291 | } |
| 292 | |
| 293 | // Buffered returns whether rr has decided to buffer the response. |
| 294 | func (rr *responseRecorder) Buffered() bool { |
| 295 | return !rr.stream |
| 296 | } |
| 297 | |
| 298 | func (rr *responseRecorder) DetachAfterHijack(detached bool) bool { |
| 299 | if rr.hijacked { |
| 300 | return false |
| 301 | } |
| 302 | rr.detached = detached |
| 303 | return true |
| 304 | } |
| 305 | |
| 306 | func (rr *responseRecorder) WriteResponse() error { |
| 307 | if rr.hijacked { |
| 308 | return nil |
| 309 | } |
| 310 | if rr.statusCode == 0 { |
| 311 | // could happen if no handlers actually wrote anything, |
| 312 | // and this prevents a panic; status must be > 0 |
| 313 | rr.WriteHeader(http.StatusOK) |
| 314 | } |
| 315 | if rr.stream { |
| 316 | return nil |
| 317 | } |
| 318 | rr.ResponseWriterWrapper.WriteHeader(rr.statusCode) |
| 319 | _, err := io.Copy(rr.ResponseWriterWrapper, rr.buf) |
| 320 | return err |
| 321 | } |
| 322 | |
| 323 | // FlushError will suppress actual flushing if the response is buffered. See: |
| 324 | // https://github.com/caddyserver/caddy/issues/6144 |
| 325 | func (rr *responseRecorder) FlushError() error { |
| 326 | if rr.stream { |
| 327 | //nolint:bodyclose |
| 328 | return http.NewResponseController(rr.ResponseWriterWrapper).Flush() |
| 329 | } |
| 330 | return nil |
| 331 | } |
| 332 | |
| 333 | // Private interface so it can only be used in this package |
| 334 | // #TODO: maybe export it later |
| 335 | func (rr *responseRecorder) setReadSize(size *int) { |
| 336 | rr.readSize = size |
| 337 | } |
| 338 | |
| 339 | func (rr *responseRecorder) Hijack() (net.Conn, *bufio.ReadWriter, error) { |
| 340 | if !rr.wroteHeader { |
| 341 | // hijacking without writing status code first works as long as |
| 342 | // subsequent writes follows http1.1 wire format, but it will |
| 343 | // show up with a status code of 0 in the access log and bytes |
| 344 | // written will include response headers. Response headers won't |
| 345 | // be present in the log if not set on the response writer. |
| 346 | caddy.Log().Warn("hijacking without writing status code first") |
| 347 | } |
| 348 | //nolint:bodyclose |
| 349 | conn, brw, err := http.NewResponseController(rr.ResponseWriterWrapper).Hijack() |
| 350 | if err != nil { |
| 351 | return nil, nil, err |
| 352 | } |
| 353 | rr.hijacked = true |
| 354 | rr.stream = true |
| 355 | rr.wroteHeader = true |
| 356 | if rr.detached { |
| 357 | return conn, brw, nil |
| 358 | } |
| 359 | // Per http documentation, returned bufio.Writer is empty, but bufio.Read maybe not |
| 360 | conn = &hijackedConn{conn, rr} |
| 361 | brw.Writer.Reset(conn) |
| 362 | |
| 363 | buffered := brw.Reader.Buffered() |
| 364 | if buffered != 0 { |
| 365 | conn.(*hijackedConn).updateReadSize(buffered) |
| 366 | data, _ := brw.Peek(buffered) |
| 367 | brw.Reader.Reset(io.MultiReader(bytes.NewReader(data), conn)) |
| 368 | // peek to make buffered data appear, as Reset will make it 0 |
| 369 | _, _ = brw.Peek(buffered) |
| 370 | } else { |
| 371 | brw.Reader.Reset(conn) |
| 372 | } |
| 373 | return conn, brw, nil |
| 374 | } |
| 375 | |
| 376 | // used to track the size of hijacked response writers |
| 377 | type hijackedConn struct { |
| 378 | net.Conn |
| 379 | rr *responseRecorder |
| 380 | } |
| 381 | |
| 382 | func (hc *hijackedConn) updateReadSize(n int) { |
| 383 | if hc.rr.readSize != nil { |
| 384 | *hc.rr.readSize += n |
| 385 | } |
| 386 | } |
| 387 | |
| 388 | func (hc *hijackedConn) Read(p []byte) (int, error) { |
| 389 | n, err := hc.Conn.Read(p) |
| 390 | hc.updateReadSize(n) |
| 391 | return n, err |
| 392 | } |
| 393 | |
| 394 | func (hc *hijackedConn) WriteTo(w io.Writer) (int64, error) { |
| 395 | n, err := io.Copy(w, hc.Conn) |
| 396 | hc.updateReadSize(int(n)) |
| 397 | return n, err |
| 398 | } |
| 399 | |
| 400 | func (hc *hijackedConn) Write(p []byte) (int, error) { |
| 401 | n, err := hc.Conn.Write(p) |
| 402 | hc.rr.size += n |
| 403 | return n, err |
| 404 | } |
| 405 | |
| 406 | func (hc *hijackedConn) ReadFrom(r io.Reader) (int64, error) { |
| 407 | n, err := io.Copy(hc.Conn, r) |
| 408 | hc.rr.size += int(n) |
| 409 | return n, err |
| 410 | } |
| 411 | |
| 412 | // DetachResponseWriterAfterHijack configures w and its wrapped response |
| 413 | // writers to detach when hijacked. It succeeds only if every detaching writer |
| 414 | // accepts and the unwrap chain reaches the server-owned writer recorded on r. |
| 415 | // When detached, bytes read or written stats will not be recorded for the |
| 416 | // hijacked connection, and it's safe to use the connection after HTTP |
| 417 | // middleware returns. |
| 418 | func DetachResponseWriterAfterHijack(w http.ResponseWriter, r *http.Request, detached bool) bool { |
| 419 | if r == nil { |
| 420 | return false |
| 421 | } |
| 422 | boundaryWriter, _ := r.Context().Value(responseWriterBoundaryCtxKey).(http.ResponseWriter) |
| 423 | boundary := responseWriterPointer(boundaryWriter) |
| 424 | if boundary == 0 { |
| 425 | return false |
| 426 | } |
| 427 | seen := make(map[uintptr]struct{}) |
| 428 | for range 64 { |
| 429 | if w == nil { |
| 430 | return false |
| 431 | } |
| 432 | pointer := responseWriterPointer(w) |
| 433 | if pointer != 0 { |
| 434 | if _, duplicate := seen[pointer]; duplicate { |
| 435 | return false |
| 436 | } |
| 437 | seen[pointer] = struct{}{} |
| 438 | } |
| 439 | if detacher, ok := w.(interface{ DetachAfterHijack(bool) bool }); ok { |
| 440 | if !detacher.DetachAfterHijack(detached) { |
| 441 | return false |
| 442 | } |
| 443 | } |
| 444 | if pointer == boundary { |
| 445 | return true |
| 446 | } |
| 447 | unwrapper, ok := w.(interface{ Unwrap() http.ResponseWriter }) |
| 448 | if !ok { |
| 449 | return false |
| 450 | } |
| 451 | w = unwrapper.Unwrap() |
| 452 | } |
| 453 | return false |
| 454 | } |
| 455 | |
| 456 | type responseWriterBoundaryCtxKeyType struct{} |
| 457 | |
| 458 | var responseWriterBoundaryCtxKey = responseWriterBoundaryCtxKeyType{} |
| 459 | |
| 460 | // ResponseRecorder is a http.ResponseWriter that records |
| 461 | // responses instead of writing them to the client. See |
| 462 | // docs for NewResponseRecorder for proper usage. |
| 463 | type ResponseRecorder interface { |
| 464 | http.ResponseWriter |
| 465 | Status() int |
| 466 | Buffer() *bytes.Buffer |
| 467 | Buffered() bool |
| 468 | DetachAfterHijack(bool) bool |
| 469 | Size() int |
| 470 | WriteResponse() error |
| 471 | } |
| 472 | |
| 473 | // ShouldBufferFunc is a function that returns true if the |
| 474 | // response should be buffered, given the pending HTTP status |
| 475 | // code and response headers. |
| 476 | type ShouldBufferFunc func(status int, header http.Header) bool |
| 477 | |
| 478 | // Interface guards |
| 479 | var ( |
| 480 | _ http.ResponseWriter = (*ResponseWriterWrapper)(nil) |
| 481 | _ ResponseRecorder = (*responseRecorder)(nil) |
| 482 | |
| 483 | // Implementing ReaderFrom can be such a significant |
| 484 | // optimization that it should probably be required! |
| 485 | // see PR #5022 (25%-50% speedup) |
| 486 | _ io.ReaderFrom = (*ResponseWriterWrapper)(nil) |
| 487 | _ io.ReaderFrom = (*responseRecorder)(nil) |
| 488 | _ io.ReaderFrom = (*hijackedConn)(nil) |
| 489 | |
| 490 | _ io.WriterTo = (*hijackedConn)(nil) |
| 491 | ) |