mirror of
https://github.com/caddyserver/caddy.git
synced 2025-01-23 18:55:49 +03: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
4 changed files with 59 additions and 21 deletions
|
@ -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…
Reference in a new issue