mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-10-31 02:27:19 -04:00 
			
		
		
		
	Merge pull request #984 from nemothekid/proxy/keepalive-directive
Proxy: Add keepalive directive to proxy to set MaxIdleConnsPerHost on transport
This commit is contained in:
		
						commit
						fffc1bed73
					
				| @ -108,7 +108,7 @@ func (p Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { | ||||
| 		if nameURL, err := url.Parse(host.Name); err == nil { | ||||
| 			outreq.Host = nameURL.Host | ||||
| 			if proxy == nil { | ||||
| 				proxy = NewSingleHostReverseProxy(nameURL, host.WithoutPathPrefix) | ||||
| 				proxy = NewSingleHostReverseProxy(nameURL, host.WithoutPathPrefix, http.DefaultMaxIdleConnsPerHost) | ||||
| 			} | ||||
| 
 | ||||
| 			// use upstream credentials by default | ||||
|  | ||||
| @ -725,11 +725,11 @@ func newFakeUpstream(name string, insecure bool) *fakeUpstream { | ||||
| 		from: "/", | ||||
| 		host: &UpstreamHost{ | ||||
| 			Name:         name, | ||||
| 			ReverseProxy: NewSingleHostReverseProxy(uri, ""), | ||||
| 			ReverseProxy: NewSingleHostReverseProxy(uri, "", http.DefaultMaxIdleConnsPerHost), | ||||
| 		}, | ||||
| 	} | ||||
| 	if insecure { | ||||
| 		u.host.ReverseProxy.Transport = InsecureTransport | ||||
| 		u.host.ReverseProxy.UseInsecureTransport() | ||||
| 	} | ||||
| 	return u | ||||
| } | ||||
| @ -753,7 +753,7 @@ func (u *fakeUpstream) Select(r *http.Request) *UpstreamHost { | ||||
| 		} | ||||
| 		u.host = &UpstreamHost{ | ||||
| 			Name:         u.name, | ||||
| 			ReverseProxy: NewSingleHostReverseProxy(uri, u.without), | ||||
| 			ReverseProxy: NewSingleHostReverseProxy(uri, u.without, http.DefaultMaxIdleConnsPerHost), | ||||
| 		} | ||||
| 	} | ||||
| 	return u.host | ||||
| @ -794,7 +794,7 @@ func (u *fakeWsUpstream) Select(r *http.Request) *UpstreamHost { | ||||
| 	uri, _ := url.Parse(u.name) | ||||
| 	return &UpstreamHost{ | ||||
| 		Name:         u.name, | ||||
| 		ReverseProxy: NewSingleHostReverseProxy(uri, u.without), | ||||
| 		ReverseProxy: NewSingleHostReverseProxy(uri, u.without, http.DefaultMaxIdleConnsPerHost), | ||||
| 		UpstreamHeaders: http.Header{ | ||||
| 			"Connection": {"{>Connection}"}, | ||||
| 			"Upgrade":    {"{>Upgrade}"}}, | ||||
|  | ||||
| @ -83,7 +83,7 @@ func socketDial(hostName string) func(network, addr string) (conn net.Conn, err | ||||
| // the target request will be for /base/dir. | ||||
| // Without logic: target's path is "/", incoming is "/api/messages", | ||||
| // without is "/api", then the target request will be for /messages. | ||||
| func NewSingleHostReverseProxy(target *url.URL, without string) *ReverseProxy { | ||||
| func NewSingleHostReverseProxy(target *url.URL, without string, keepalive int) *ReverseProxy { | ||||
| 	targetQuery := target.RawQuery | ||||
| 	director := func(req *http.Request) { | ||||
| 		if target.Scheme == "unix" { | ||||
| @ -122,10 +122,47 @@ func NewSingleHostReverseProxy(target *url.URL, without string) *ReverseProxy { | ||||
| 		rp.Transport = &http.Transport{ | ||||
| 			Dial: socketDial(target.String()), | ||||
| 		} | ||||
| 	} else if keepalive != http.DefaultMaxIdleConnsPerHost { | ||||
| 		// if keepalive is equal to the default, | ||||
| 		// just use default transport, to avoid creating | ||||
| 		// a brand new transport | ||||
| 		rp.Transport = &http.Transport{ | ||||
| 			Proxy: http.ProxyFromEnvironment, | ||||
| 			Dial: (&net.Dialer{ | ||||
| 				Timeout:   30 * time.Second, | ||||
| 				KeepAlive: 30 * time.Second, | ||||
| 			}).Dial, | ||||
| 			TLSHandshakeTimeout:   10 * time.Second, | ||||
| 			ExpectContinueTimeout: 1 * time.Second, | ||||
| 		} | ||||
| 		if keepalive == 0 { | ||||
| 			rp.Transport.(*http.Transport).DisableKeepAlives = true | ||||
| 		} else { | ||||
| 			rp.Transport.(*http.Transport).MaxIdleConnsPerHost = keepalive | ||||
| 		} | ||||
| 	} | ||||
| 	return rp | ||||
| } | ||||
| 
 | ||||
| // InsecureTransport is used to facilitate HTTPS proxying | ||||
| // when it is OK for upstream to be using a bad certificate, | ||||
| // since this transport skips verification. | ||||
| func (rp *ReverseProxy) UseInsecureTransport() { | ||||
| 	if rp.Transport == nil { | ||||
| 		rp.Transport = &http.Transport{ | ||||
| 			Proxy: http.ProxyFromEnvironment, | ||||
| 			Dial: (&net.Dialer{ | ||||
| 				Timeout:   30 * time.Second, | ||||
| 				KeepAlive: 30 * time.Second, | ||||
| 			}).Dial, | ||||
| 			TLSHandshakeTimeout: 10 * time.Second, | ||||
| 			TLSClientConfig:     &tls.Config{InsecureSkipVerify: true}, | ||||
| 		} | ||||
| 	} else if transport, ok := rp.Transport.(*http.Transport); ok { | ||||
| 		transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func copyHeader(dst, src http.Header) { | ||||
| 	for k, vv := range src { | ||||
| 		for _, v := range vv { | ||||
| @ -147,19 +184,6 @@ var hopHeaders = []string{ | ||||
| 	"Upgrade", | ||||
| } | ||||
| 
 | ||||
| // InsecureTransport is used to facilitate HTTPS proxying | ||||
| // when it is OK for upstream to be using a bad certificate, | ||||
| // since this transport skips verification. | ||||
| var InsecureTransport http.RoundTripper = &http.Transport{ | ||||
| 	Proxy: http.ProxyFromEnvironment, | ||||
| 	Dial: (&net.Dialer{ | ||||
| 		Timeout:   30 * time.Second, | ||||
| 		KeepAlive: 30 * time.Second, | ||||
| 	}).Dial, | ||||
| 	TLSHandshakeTimeout: 10 * time.Second, | ||||
| 	TLSClientConfig:     &tls.Config{InsecureSkipVerify: true}, | ||||
| } | ||||
| 
 | ||||
| type respUpdateFn func(resp *http.Response) | ||||
| 
 | ||||
| func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, outreq *http.Request, respUpdateFn respUpdateFn) error { | ||||
|  | ||||
| @ -25,6 +25,7 @@ type staticUpstream struct { | ||||
| 	downstreamHeaders  http.Header | ||||
| 	Hosts              HostPool | ||||
| 	Policy             Policy | ||||
| 	KeepAlive          int | ||||
| 	insecureSkipVerify bool | ||||
| 
 | ||||
| 	FailTimeout time.Duration | ||||
| @ -54,6 +55,7 @@ func NewStaticUpstreams(c caddyfile.Dispenser) ([]Upstream, error) { | ||||
| 			FailTimeout:       10 * time.Second, | ||||
| 			MaxFails:          1, | ||||
| 			MaxConns:          0, | ||||
| 			KeepAlive:         http.DefaultMaxIdleConnsPerHost, | ||||
| 		} | ||||
| 
 | ||||
| 		if !c.Args(&upstream.from) { | ||||
| @ -154,9 +156,9 @@ func (u *staticUpstream) NewHost(host string) (*UpstreamHost, error) { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	uh.ReverseProxy = NewSingleHostReverseProxy(baseURL, uh.WithoutPathPrefix) | ||||
| 	uh.ReverseProxy = NewSingleHostReverseProxy(baseURL, uh.WithoutPathPrefix, u.KeepAlive) | ||||
| 	if u.insecureSkipVerify { | ||||
| 		uh.ReverseProxy.Transport = InsecureTransport | ||||
| 		uh.ReverseProxy.UseInsecureTransport() | ||||
| 	} | ||||
| 
 | ||||
| 	return uh, nil | ||||
| @ -312,6 +314,18 @@ func parseBlock(c *caddyfile.Dispenser, u *staticUpstream) error { | ||||
| 		u.IgnoredSubPaths = ignoredPaths | ||||
| 	case "insecure_skip_verify": | ||||
| 		u.insecureSkipVerify = true | ||||
| 	case "keepalive": | ||||
| 		if !c.NextArg() { | ||||
| 			return c.ArgErr() | ||||
| 		} | ||||
| 		n, err := strconv.Atoi(c.Val()) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if n < 0 { | ||||
| 			return c.ArgErr() | ||||
| 		} | ||||
| 		u.KeepAlive = n | ||||
| 	default: | ||||
| 		return c.Errf("unknown property '%s'", c.Val()) | ||||
| 	} | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user