| 
						
						
							
								
							
						
						
					 | 
					@ -90,13 +90,20 @@ type Handler struct { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
						// to the client immediately.
 | 
					 | 
					 | 
						// to the client immediately.
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
						FlushInterval caddy.Duration `json:"flush_interval,omitempty"` | 
					 | 
					 | 
						FlushInterval caddy.Duration `json:"flush_interval,omitempty"` | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					
 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// A list of IP ranges (supports CIDR notation) from which
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// X-Forwarded-* header values should be trusted. By default,
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// no proxies are trusted, so existing values will be ignored
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// when setting these headers. If the proxy is trusted, then
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// existing values will be used when constructing the final
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// header values.
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						TrustedProxies []string `json:"trusted_proxies,omitempty"` | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
						// Headers manipulates headers between Caddy and the backend.
 | 
					 | 
					 | 
						// Headers manipulates headers between Caddy and the backend.
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
						// By default, all headers are passed-thru without changes,
 | 
					 | 
					 | 
						// By default, all headers are passed-thru without changes,
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
						// with the exceptions of special hop-by-hop headers.
 | 
					 | 
					 | 
						// with the exceptions of special hop-by-hop headers.
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
						//
 | 
					 | 
					 | 
						//
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
						// X-Forwarded-For and X-Forwarded-Proto are also set
 | 
					 | 
					 | 
						// X-Forwarded-For, X-Forwarded-Proto and X-Forwarded-Host
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
						// implicitly, but this may change in the future if the official
 | 
					 | 
					 | 
						// are also set implicitly.
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					 | 
					 | 
						// standardized Forwarded header field gains more adoption.
 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
			
				
					 | 
					 | 
						Headers *headers.Handler `json:"headers,omitempty"` | 
					 | 
					 | 
						Headers *headers.Handler `json:"headers,omitempty"` | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					
 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
						// If true, the entire request body will be read and buffered
 | 
					 | 
					 | 
						// If true, the entire request body will be read and buffered
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					@ -133,6 +140,9 @@ type Handler struct { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
						Transport http.RoundTripper `json:"-"` | 
					 | 
					 | 
						Transport http.RoundTripper `json:"-"` | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
						CB        CircuitBreaker    `json:"-"` | 
					 | 
					 | 
						CB        CircuitBreaker    `json:"-"` | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					
 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// Holds the parsed CIDR ranges from TrustedProxies
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						trustedProxies []*net.IPNet | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
						// Holds the named response matchers from the Caddyfile while adapting
 | 
					 | 
					 | 
						// Holds the named response matchers from the Caddyfile while adapting
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
						responseMatchers map[string]caddyhttp.ResponseMatcher | 
					 | 
					 | 
						responseMatchers map[string]caddyhttp.ResponseMatcher | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					
 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					@ -192,6 +202,30 @@ func (h *Handler) Provision(ctx caddy.Context) error { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
							h.CB = mod.(CircuitBreaker) | 
					 | 
					 | 
							h.CB = mod.(CircuitBreaker) | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
						} | 
					 | 
					 | 
						} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					
 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// parse trusted proxy CIDRs ahead of time
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						for _, str := range h.TrustedProxies { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
							if strings.Contains(str, "/") { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
								_, ipNet, err := net.ParseCIDR(str) | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
								if err != nil { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
									return fmt.Errorf("parsing CIDR expression: %v", err) | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
								} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
								h.trustedProxies = append(h.trustedProxies, ipNet) | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
							} else { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
								ip := net.ParseIP(str) | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
								if ip == nil { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
									return fmt.Errorf("invalid IP address: %s", str) | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
								} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
								if ipv4 := ip.To4(); ipv4 != nil { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
									ip = ipv4 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
								} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
								mask := len(ip) * 8 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
								h.trustedProxies = append(h.trustedProxies, &net.IPNet{ | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
									IP:   ip, | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
									Mask: net.CIDRMask(mask, mask), | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
								}) | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
							} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
						// ensure any embedded headers handler module gets provisioned
 | 
					 | 
					 | 
						// ensure any embedded headers handler module gets provisioned
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
						// (see https://caddy.community/t/set-cookie-manipulation-in-reverse-proxy/7666?u=matt
 | 
					 | 
					 | 
						// (see https://caddy.community/t/set-cookie-manipulation-in-reverse-proxy/7666?u=matt
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
						// for what happens if we forget to provision it)
 | 
					 | 
					 | 
						// for what happens if we forget to provision it)
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					@ -514,32 +548,103 @@ func (h Handler) prepareRequest(req *http.Request) (*http.Request, error) { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
							req.Header.Set("Upgrade", reqUpType) | 
					 | 
					 | 
							req.Header.Set("Upgrade", reqUpType) | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
						} | 
					 | 
					 | 
						} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					
 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
						if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil { | 
					 | 
					 | 
						// Add the supported X-Forwarded-* headers
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
							// If we aren't the first proxy retain prior
 | 
					 | 
					 | 
						err := h.addForwardedHeaders(req) | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
							// X-Forwarded-For information as a comma+space
 | 
					 | 
					 | 
						if err != nil { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
							// separated list and fold multiple headers into one.
 | 
					 | 
					 | 
							return nil, err | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
							prior, ok := req.Header["X-Forwarded-For"] | 
					 | 
					 | 
						} | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
							omit := ok && prior == nil // Issue 38079: nil now means don't populate the header
 | 
					 | 
					 | 
					
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
							if len(prior) > 0 { | 
					 | 
					 | 
						return req, nil | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
								clientIP = strings.Join(prior, ", ") + ", " + clientIP | 
					 | 
					 | 
					} | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
							} | 
					 | 
					 | 
					
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
							if !omit { | 
					 | 
					 | 
					// addForwardedHeaders adds the de-facto standard X-Forwarded-*
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
								req.Header.Set("X-Forwarded-For", clientIP) | 
					 | 
					 | 
					// headers to the request before it is sent upstream.
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
							} | 
					 | 
					 | 
					//
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					// These headers are security sensitive, so care is taken to only
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					// use existing values for these headers from the incoming request
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					// if the client IP is trusted (i.e. coming from a trusted proxy
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					// sitting in front of this server). If the request didn't have
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					// the headers at all, then they will be added with the values
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					// that we can glean from the request.
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					func (h Handler) addForwardedHeaders(req *http.Request) error { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// Parse the remote IP, ignore the error as non-fatal,
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// but the remote IP is required to continue, so we
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// just return early. This should probably never happen
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// though, unless some other module manipulated the request's
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// remote address and used an invalid value.
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						clientIP, _, err := net.SplitHostPort(req.RemoteAddr) | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						if err != nil { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
							// Remove the `X-Forwarded-*` headers to avoid upstreams
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
							// potentially trusting a header that came from the client
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
							req.Header.Del("X-Forwarded-For") | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
							req.Header.Del("X-Forwarded-Proto") | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
							req.Header.Del("X-Forwarded-Host") | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
							return nil | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// Client IP may contain a zone if IPv6, so we need
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// to pull that out before parsing the IP
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						if idx := strings.IndexByte(clientIP, '%'); idx >= 0 { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
							clientIP = clientIP[:idx] | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						ip := net.ParseIP(clientIP) | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						if ip == nil { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
							return fmt.Errorf("invalid client IP address: %s", clientIP) | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
						} | 
					 | 
					 | 
						} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					
 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
						prior, ok := req.Header["X-Forwarded-Proto"] | 
					 | 
					 | 
						// Check if the client is a trusted proxy
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
						omit := ok && prior == nil | 
					 | 
					 | 
						trusted := false | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
						if len(prior) == 0 && !omit { | 
					 | 
					 | 
						for _, ipRange := range h.trustedProxies { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
							// set X-Forwarded-Proto; many backend apps expect this too
 | 
					 | 
					 | 
							if ipRange.Contains(ip) { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
							proto := "https" | 
					 | 
					 | 
								trusted = true | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
							if req.TLS == nil { | 
					 | 
					 | 
								break | 
				
			
			
				
				
			
		
	
		
		
			
				
					 | 
					 | 
								proto = "http" | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					 | 
					 | 
							} | 
					 | 
					 | 
							} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// If we aren't the first proxy, and the proxy is trusted,
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// retain prior X-Forwarded-For information as a comma+space
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// separated list and fold multiple headers into one.
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						clientXFF := clientIP | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						prior, ok, omit := allHeaderValues(req.Header, "X-Forwarded-For") | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						if trusted && ok && prior != "" { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
							clientXFF = prior + ", " + clientXFF | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						if !omit { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
							req.Header.Set("X-Forwarded-For", clientXFF) | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// Set X-Forwarded-Proto; many backend apps expect this,
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// so that they can properly craft URLs with the right
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// scheme to match the original request
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						proto := "https" | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						if req.TLS == nil { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
							proto = "http" | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						prior, ok, omit = lastHeaderValue(req.Header, "X-Forwarded-Proto") | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						if trusted && ok && prior != "" { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
							proto = prior | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						if !omit { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
							req.Header.Set("X-Forwarded-Proto", proto) | 
					 | 
					 | 
							req.Header.Set("X-Forwarded-Proto", proto) | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
						} | 
					 | 
					 | 
						} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					
 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
						return req, nil | 
					 | 
					 | 
						// Set X-Forwarded-Host; often this is redundant because
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// we pass through the request Host as-is, but in situations
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// where we proxy over HTTPS, the user may need to override
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						// Host themselves, so it's helpful to send the original too.
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						host, _, err := net.SplitHostPort(req.Host) | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						if err != nil { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
							host = req.Host // OK; there probably was no port
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						prior, ok, omit = lastHeaderValue(req.Header, "X-Forwarded-Host") | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						if trusted && ok && prior != "" { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
							host = prior | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						if !omit { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
							req.Header.Set("X-Forwarded-Host", host) | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						return nil | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					} | 
					 | 
					 | 
					} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					
 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					// reverseProxy performs a round-trip to the given backend and processes the response with the client.
 | 
					 | 
					 | 
					// reverseProxy performs a round-trip to the given backend and processes the response with the client.
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					@ -868,6 +973,42 @@ func copyHeader(dst, src http.Header) { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
						} | 
					 | 
					 | 
						} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					} | 
					 | 
					 | 
					} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					
 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					// allHeaderValues gets all values for a given header field,
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					// joined by a comma and space if more than one is set. If the
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					// header field is nil, then the omit is true, meaning some
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					// earlier logic in the server wanted to prevent this header from
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					// getting written at all. If the header is empty, then ok is
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					// false. Callers should still check that the value is not empty
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					// (the header field may be set but have an empty value).
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					func allHeaderValues(h http.Header, field string) (value string, ok bool, omit bool) { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						values, ok := h[http.CanonicalHeaderKey(field)] | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						if ok && values == nil { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
							return "", true, true | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						if len(values) == 0 { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
							return "", false, false | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						return strings.Join(values, ", "), true, false | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					// lastHeaderValue gets the last value for a given header field
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					// if more than one is set. If the header field is nil, then
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					// the omit is true, meaning some earlier logic in the server
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					// wanted to prevent this header from getting written at all.
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					// If the header is empty, then ok is false. Callers should
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					// still check that the value is not empty (the header field
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					// may be set but have an empty value).
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					func lastHeaderValue(h http.Header, field string) (value string, ok bool, omit bool) { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						values, ok := h[http.CanonicalHeaderKey(field)] | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						if ok && values == nil { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
							return "", true, true | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						if len(values) == 0 { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
							return "", false, false | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
						return values[len(values)-1], true, false | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					} | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
					func upgradeType(h http.Header) string { | 
					 | 
					 | 
					func upgradeType(h http.Header) string { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
						if !httpguts.HeaderValuesContainsToken(h["Connection"], "Upgrade") { | 
					 | 
					 | 
						if !httpguts.HeaderValuesContainsToken(h["Connection"], "Upgrade") { | 
				
			
			
		
	
		
		
			
				
					 | 
					 | 
							return "" | 
					 | 
					 | 
							return "" | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
					
  |