mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-10-31 10:37:24 -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 { | 		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, http.DefaultMaxIdleConnsPerHost) | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			// use upstream credentials by default | 			// use upstream credentials by default | ||||||
|  | |||||||
| @ -725,11 +725,11 @@ func newFakeUpstream(name string, insecure bool) *fakeUpstream { | |||||||
| 		from: "/", | 		from: "/", | ||||||
| 		host: &UpstreamHost{ | 		host: &UpstreamHost{ | ||||||
| 			Name:         name, | 			Name:         name, | ||||||
| 			ReverseProxy: NewSingleHostReverseProxy(uri, ""), | 			ReverseProxy: NewSingleHostReverseProxy(uri, "", http.DefaultMaxIdleConnsPerHost), | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| 	if insecure { | 	if insecure { | ||||||
| 		u.host.ReverseProxy.Transport = InsecureTransport | 		u.host.ReverseProxy.UseInsecureTransport() | ||||||
| 	} | 	} | ||||||
| 	return u | 	return u | ||||||
| } | } | ||||||
| @ -753,7 +753,7 @@ func (u *fakeUpstream) Select(r *http.Request) *UpstreamHost { | |||||||
| 		} | 		} | ||||||
| 		u.host = &UpstreamHost{ | 		u.host = &UpstreamHost{ | ||||||
| 			Name:         u.name, | 			Name:         u.name, | ||||||
| 			ReverseProxy: NewSingleHostReverseProxy(uri, u.without), | 			ReverseProxy: NewSingleHostReverseProxy(uri, u.without, http.DefaultMaxIdleConnsPerHost), | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return u.host | 	return u.host | ||||||
| @ -794,7 +794,7 @@ func (u *fakeWsUpstream) Select(r *http.Request) *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, http.DefaultMaxIdleConnsPerHost), | ||||||
| 		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,47 @@ 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 != 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 | 	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 +184,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 | ||||||
| @ -54,6 +55,7 @@ func NewStaticUpstreams(c caddyfile.Dispenser) ([]Upstream, error) { | |||||||
| 			FailTimeout:       10 * time.Second, | 			FailTimeout:       10 * time.Second, | ||||||
| 			MaxFails:          1, | 			MaxFails:          1, | ||||||
| 			MaxConns:          0, | 			MaxConns:          0, | ||||||
|  | 			KeepAlive:         http.DefaultMaxIdleConnsPerHost, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if !c.Args(&upstream.from) { | 		if !c.Args(&upstream.from) { | ||||||
| @ -154,9 +156,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 +314,18 @@ 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 | ||||||
|  | 		} | ||||||
|  | 		if n < 0 { | ||||||
|  | 			return c.ArgErr() | ||||||
|  | 		} | ||||||
|  | 		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