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
}