@marcel91200 / caddy Mirror · read-only / commits / 1228dd7d
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

reverse_proxy: Allow buffering of client requests

This is a bad idea, but some backends apparently require it. See discussion in #176.

Matthew Holt committed Nov 15, 2019 at 17:15 UTC 1228dd7d93b59409fd844400b9c114be267df3a3
2 files changed +39 -13
modules/caddyhttp/reverseproxy/reverseproxy.go
+38 -13
@@ -15,14 +15,18 @@
15 package reverseproxy
16
17 import (
18 + "bytes"
19 "context"
20 "encoding/json"
21 "fmt"
22 + "io"
23 + "io/ioutil"
24 "net"
25 "net/http"
26 "regexp"
27 "strconv"
28 "strings"
29 + "sync"
30 "time"
31
32 "github.com/caddyserver/caddy/v2"
@@ -38,13 +42,14 @@ func init() {
42
43 // Handler implements a highly configurable and production-ready reverse proxy.
44 type Handler struct {
41 - TransportRaw json.RawMessage `json:"transport,omitempty"`
42 - CBRaw json.RawMessage `json:"circuit_breaker,omitempty"`
43 - LoadBalancing *LoadBalancing `json:"load_balancing,omitempty"`
44 - HealthChecks *HealthChecks `json:"health_checks,omitempty"`
45 - Upstreams UpstreamPool `json:"upstreams,omitempty"`
46 - FlushInterval caddy.Duration `json:"flush_interval,omitempty"`
47 - Headers *headers.Handler `json:"headers,omitempty"`
45 + TransportRaw json.RawMessage `json:"transport,omitempty"`
46 + CBRaw json.RawMessage `json:"circuit_breaker,omitempty"`
47 + LoadBalancing *LoadBalancing `json:"load_balancing,omitempty"`
48 + HealthChecks *HealthChecks `json:"health_checks,omitempty"`
49 + Upstreams UpstreamPool `json:"upstreams,omitempty"`
50 + FlushInterval caddy.Duration `json:"flush_interval,omitempty"`
51 + Headers *headers.Handler `json:"headers,omitempty"`
52 + BufferRequests bool `json:"buffer_requests,omitempty"`
53
54 Transport http.RoundTripper `json:"-"`
55 CB CircuitBreaker `json:"-"`
@@ -222,6 +227,27 @@ func (h *Handler) Cleanup() error {
227 func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
228 repl := r.Context().Value(caddy.ReplacerCtxKey).(caddy.Replacer)
229
230 + // if enabled, buffer client request;
231 + // this should only be enabled if the
232 + // upstream requires it and does not
233 + // work with "slow clients" (gunicorn,
234 + // etc.) - this obviously has a perf
235 + // overhead and makes the proxy at
236 + // risk of exhausting memory and more
237 + // suseptible to slowloris attacks,
238 + // so it is strongly recommended to
239 + // only use this feature if absolutely
240 + // required, if read timeouts are set,
241 + // and if body size is limited
242 + if h.BufferRequests {
243 + buf := bufPool.Get().(*bytes.Buffer)
244 + buf.Reset()
245 + defer bufPool.Put(buf)
246 + io.Copy(buf, r.Body)
247 + r.Body.Close()
248 + r.Body = ioutil.NopCloser(buf)
249 + }
250 +
251 // prepare the request for proxying; this is needed only once
252 err := h.prepareRequest(r)
253 if err != nil {
@@ -662,12 +688,11 @@ type DialError struct {
688 error
689 }
690
665 -// TODO: see if we can use this
666 -// var bufPool = sync.Pool{
667 -// New: func() interface{} {
668 -// return new(bytes.Buffer)
669 -// },
670 -// }
691 +var bufPool = sync.Pool{
692 + New: func() interface{} {
693 + return new(bytes.Buffer)
694 + },
695 +}
696
697 // Interface guards
698 var (
modules/caddyhttp/reverseproxy/streaming.go
+1
@@ -110,6 +110,7 @@ func (h Handler) copyResponse(dst io.Writer, src io.Reader, flushInterval time.D
110 // buf = h.BufferPool.Get()
111 // defer h.BufferPool.Put(buf)
112 // }
113 + // // we could also see about a pool that returns values like this: make([]byte, 32*1024)
114 _, err := h.copyBuffer(dst, src, buf)
115 return err
116 }