mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-04 03:27:23 -05: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