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

reverseproxy: Incorporate latest proxy changes from stdlib (#4266)

I went through the commits that touched stdlib's `reverseproxy.go` file, and copied over all the changes that are to code that was copied into Caddy. The commits I pulled changes from: - https://github.com/golang/go/commit/2cc347382f4df3fb40d8d81ec9331f0748b1c394 - https://github.com/golang/go/commit/a5cea062b305c8502bdc959c0eec279dbcd4391f - https://github.com/golang/go/commit/ecdbffd4ec68b509998792f120868fec319de59b - https://github.com/golang/go/commit/21898524f66c075d7cfb64a38f17684140e57675 -https://github.com/golang/go/commit/ca3c0df1f8e07337ba4048b191bf905118ebe251 - https://github.com/golang/go/commit/9c017ff30dd21bbdcdb11f39458d3944db530d7e This may also fix https://github.com/caddyserver/caddy/issues/4247 because of the change to `copyResponse` to set `mlw.flushPending = true` right away.

Francis Lavoie committed Aug 12, 2021 at 12:48 UTC e6c29ce081673d85e527d59f3afb7ace034573df
2 files changed +37 -15
modules/caddyhttp/reverseproxy/reverseproxy.go
+21 -11
@@ -23,6 +23,7 @@ import (
23 "io"
24 "net"
25 "net/http"
26 + "net/textproto"
27 "net/url"
28 "regexp"
29 "strconv"
@@ -80,10 +81,13 @@ type Handler struct {
81 // Upstreams is the list of backends to proxy to.
82 Upstreams UpstreamPool `json:"upstreams,omitempty"`
83
83 - // Adjusts how often to flush the response buffer. A
84 - // negative value disables response buffering.
85 - // TODO: figure out good defaults and write docs for this
86 - // (see https://github.com/caddyserver/caddy/issues/1460)
84 + // Adjusts how often to flush the response buffer. By default,
85 + // no periodic flushing is done. A negative value disables
86 + // response buffering, and flushes immediately after each
87 + // write to the client. This option is ignored when the upstream's
88 + // response is recognized as a streaming response, or if its
89 + // content length is -1; for such responses, writes are flushed
90 + // to the client immediately.
91 FlushInterval caddy.Duration `json:"flush_interval,omitempty"`
92
93 // Headers manipulates headers between Caddy and the backend.
@@ -528,13 +532,19 @@ func (h Handler) prepareRequest(req *http.Request) error {
532 // If we aren't the first proxy retain prior
533 // X-Forwarded-For information as a comma+space
534 // separated list and fold multiple headers into one.
531 - if prior, ok := req.Header["X-Forwarded-For"]; ok {
535 + prior, ok := req.Header["X-Forwarded-For"]
536 + omit := ok && prior == nil // Issue 38079: nil now means don't populate the header
537 + if len(prior) > 0 {
538 clientIP = strings.Join(prior, ", ") + ", " + clientIP
539 }
534 - req.Header.Set("X-Forwarded-For", clientIP)
540 + if !omit {
541 + req.Header.Set("X-Forwarded-For", clientIP)
542 + }
543 }
544
537 - if req.Header.Get("X-Forwarded-Proto") == "" {
545 + prior, ok := req.Header["X-Forwarded-Proto"]
546 + omit := ok && prior == nil
547 + if len(prior) == 0 && !omit {
548 // set X-Forwarded-Proto; many backend apps expect this too
549 proto := "https"
550 if req.TLS == nil {
@@ -827,10 +837,10 @@ func upgradeType(h http.Header) string {
837 // removeConnectionHeaders removes hop-by-hop headers listed in the "Connection" header of h.
838 // See RFC 7230, section 6.1
839 func removeConnectionHeaders(h http.Header) {
830 - if c := h.Get("Connection"); c != "" {
831 - for _, f := range strings.Split(c, ",") {
832 - if f = strings.TrimSpace(f); f != "" {
833 - h.Del(f)
840 + for _, f := range h["Connection"] {
841 + for _, sf := range strings.Split(f, ",") {
842 + if sf = textproto.TrimString(sf); sf != "" {
843 + h.Del(sf)
844 }
845 }
846 }
modules/caddyhttp/reverseproxy/streaming.go
+16 -4
@@ -32,6 +32,9 @@ import (
32 func (h Handler) handleUpgradeResponse(logger *zap.Logger, rw http.ResponseWriter, req *http.Request, res *http.Response) {
33 reqUpType := upgradeType(req.Header)
34 resUpType := upgradeType(res.Header)
35 + // TODO: Update to use "net/http/internal/ascii" once we bumped
36 + // the minimum Go version to 1.17.
37 + // See https://github.com/golang/go/commit/5c489514bc5e61ad9b5b07bd7d8ec65d66a0512a
38 if reqUpType != resUpType {
39 h.logger.Debug("backend tried to switch to unexpected protocol via Upgrade header",
40 zap.String("backend_upgrade", resUpType),
@@ -39,8 +42,6 @@ func (h Handler) handleUpgradeResponse(logger *zap.Logger, rw http.ResponseWrite
42 return
43 }
44
42 - copyHeader(res.Header, rw.Header())
43 -
45 hj, ok := rw.(http.Hijacker)
46 if !ok {
47 h.logger.Sugar().Errorf("can't switch protocols using non-Hijacker ResponseWriter type %T", rw)
@@ -78,6 +79,9 @@ func (h Handler) handleUpgradeResponse(logger *zap.Logger, rw http.ResponseWrite
79 logger.Debug("connection closed", zap.Duration("duration", time.Since(start)))
80 }()
81
82 + copyHeader(rw.Header(), res.Header)
83 +
84 + res.Header = rw.Header()
85 res.Body = nil // so res.Write only writes the headers; we have res.Body in backConn above
86 if err := res.Write(brw); err != nil {
87 h.logger.Debug("response write", zap.Error(err))
@@ -107,13 +111,16 @@ func (h Handler) flushInterval(req *http.Request, res *http.Response) time.Durat
111 return -1 // negative means immediately
112 }
113
114 + // We might have the case of streaming for which Content-Length might be unset.
115 + if res.ContentLength == -1 {
116 + return -1
117 + }
118 +
119 // for h2 and h2c upstream streaming data to client (issues #3556 and #3606)
120 if h.isBidirectionalStream(req, res) {
121 return -1
122 }
123
115 - // TODO: more specific cases? e.g. res.ContentLength == -1? (this TODO is from the std lib, but
116 - // strangely similar to our isBidirectionalStream function that we implemented ourselves)
124 return time.Duration(h.FlushInterval)
125 }
126
@@ -142,6 +149,11 @@ func (h Handler) copyResponse(dst io.Writer, src io.Reader, flushInterval time.D
149 latency: flushInterval,
150 }
151 defer mlw.stop()
152 +
153 + // set up initial timer so headers get flushed even if body writes are delayed
154 + mlw.flushPending = true
155 + mlw.t = time.AfterFunc(flushInterval, mlw.delayedFlush)
156 +
157 dst = mlw
158 }
159 }