mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-10-31 02:27:19 -04:00 
			
		
		
		
	Proxy: Add keepalive directive to proxy to set MaxIdleConnsPerHost on transport. Fixes #938
This commit is contained in:
		
							parent
							
								
									da5b3cfc50
								
							
						
					
					
						commit
						db4cd8ee2d
					
				| @ -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 { | 		if nameURL, err := url.Parse(host.Name); err == nil { | ||||||
| 			outreq.Host = nameURL.Host | 			outreq.Host = nameURL.Host | ||||||
| 			if proxy == nil { | 			if proxy == nil { | ||||||
| 				proxy = NewSingleHostReverseProxy(nameURL, host.WithoutPathPrefix) | 				proxy = NewSingleHostReverseProxy(nameURL, host.WithoutPathPrefix, 0) | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			// use upstream credentials by default | 			// use upstream credentials by default | ||||||
|  | |||||||
| @ -716,11 +716,11 @@ func newFakeUpstream(name string, insecure bool) *fakeUpstream { | |||||||
| 		from: "/", | 		from: "/", | ||||||
| 		host: &UpstreamHost{ | 		host: &UpstreamHost{ | ||||||
| 			Name:         name, | 			Name:         name, | ||||||
| 			ReverseProxy: NewSingleHostReverseProxy(uri, ""), | 			ReverseProxy: NewSingleHostReverseProxy(uri, "", 0), | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| 	if insecure { | 	if insecure { | ||||||
| 		u.host.ReverseProxy.Transport = InsecureTransport | 		u.host.ReverseProxy.UseInsecureTransport() | ||||||
| 	} | 	} | ||||||
| 	return u | 	return u | ||||||
| } | } | ||||||
| @ -744,7 +744,7 @@ func (u *fakeUpstream) Select() *UpstreamHost { | |||||||
| 		} | 		} | ||||||
| 		u.host = &UpstreamHost{ | 		u.host = &UpstreamHost{ | ||||||
| 			Name:         u.name, | 			Name:         u.name, | ||||||
| 			ReverseProxy: NewSingleHostReverseProxy(uri, u.without), | 			ReverseProxy: NewSingleHostReverseProxy(uri, u.without, 0), | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return u.host | 	return u.host | ||||||
| @ -785,7 +785,7 @@ func (u *fakeWsUpstream) Select() *UpstreamHost { | |||||||
| 	uri, _ := url.Parse(u.name) | 	uri, _ := url.Parse(u.name) | ||||||
| 	return &UpstreamHost{ | 	return &UpstreamHost{ | ||||||
| 		Name:         u.name, | 		Name:         u.name, | ||||||
| 		ReverseProxy: NewSingleHostReverseProxy(uri, u.without), | 		ReverseProxy: NewSingleHostReverseProxy(uri, u.without, 0), | ||||||
| 		UpstreamHeaders: http.Header{ | 		UpstreamHeaders: http.Header{ | ||||||
| 			"Connection": {"{>Connection}"}, | 			"Connection": {"{>Connection}"}, | ||||||
| 			"Upgrade":    {"{>Upgrade}"}}, | 			"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. | // the target request will be for /base/dir. | ||||||
| // Without logic: target's path is "/", incoming is "/api/messages", | // Without logic: target's path is "/", incoming is "/api/messages", | ||||||
| // without is "/api", then the target request will be for /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 | 	targetQuery := target.RawQuery | ||||||
| 	director := func(req *http.Request) { | 	director := func(req *http.Request) { | ||||||
| 		if target.Scheme == "unix" { | 		if target.Scheme == "unix" { | ||||||
| @ -122,10 +122,44 @@ func NewSingleHostReverseProxy(target *url.URL, without string) *ReverseProxy { | |||||||
| 		rp.Transport = &http.Transport{ | 		rp.Transport = &http.Transport{ | ||||||
| 			Dial: socketDial(target.String()), | 			Dial: socketDial(target.String()), | ||||||
| 		} | 		} | ||||||
|  | 	} else if keepalive != 0 { | ||||||
|  | 		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 | 	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) { | func copyHeader(dst, src http.Header) { | ||||||
| 	for k, vv := range src { | 	for k, vv := range src { | ||||||
| 		for _, v := range vv { | 		for _, v := range vv { | ||||||
| @ -147,19 +181,6 @@ var hopHeaders = []string{ | |||||||
| 	"Upgrade", | 	"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) | type respUpdateFn func(resp *http.Response) | ||||||
| 
 | 
 | ||||||
| func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, outreq *http.Request, respUpdateFn respUpdateFn) error { | func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, outreq *http.Request, respUpdateFn respUpdateFn) error { | ||||||
|  | |||||||
| @ -25,6 +25,7 @@ type staticUpstream struct { | |||||||
| 	downstreamHeaders  http.Header | 	downstreamHeaders  http.Header | ||||||
| 	Hosts              HostPool | 	Hosts              HostPool | ||||||
| 	Policy             Policy | 	Policy             Policy | ||||||
|  | 	KeepAlive          int | ||||||
| 	insecureSkipVerify bool | 	insecureSkipVerify bool | ||||||
| 
 | 
 | ||||||
| 	FailTimeout time.Duration | 	FailTimeout time.Duration | ||||||
| @ -154,9 +155,9 @@ func (u *staticUpstream) NewHost(host string) (*UpstreamHost, error) { | |||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	uh.ReverseProxy = NewSingleHostReverseProxy(baseURL, uh.WithoutPathPrefix) | 	uh.ReverseProxy = NewSingleHostReverseProxy(baseURL, uh.WithoutPathPrefix, u.KeepAlive) | ||||||
| 	if u.insecureSkipVerify { | 	if u.insecureSkipVerify { | ||||||
| 		uh.ReverseProxy.Transport = InsecureTransport | 		uh.ReverseProxy.UseInsecureTransport() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return uh, nil | 	return uh, nil | ||||||
| @ -312,6 +313,15 @@ func parseBlock(c *caddyfile.Dispenser, u *staticUpstream) error { | |||||||
| 		u.IgnoredSubPaths = ignoredPaths | 		u.IgnoredSubPaths = ignoredPaths | ||||||
| 	case "insecure_skip_verify": | 	case "insecure_skip_verify": | ||||||
| 		u.insecureSkipVerify = true | 		u.insecureSkipVerify = true | ||||||
|  | 	case "keepalive": | ||||||
|  | 		if !c.NextArg() { | ||||||
|  | 			return c.ArgErr() | ||||||
|  | 		} | ||||||
|  | 		n, err := strconv.Atoi(c.Val()) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		u.KeepAlive = n | ||||||
| 	default: | 	default: | ||||||
| 		return c.Errf("unknown property '%s'", c.Val()) | 		return c.Errf("unknown property '%s'", c.Val()) | ||||||
| 	} | 	} | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user