Commit fffc1bed authored by Nimi Wariboko Jr's avatar Nimi Wariboko Jr Committed by GitHub

Merge pull request #984 from nemothekid/proxy/keepalive-directive

Proxy: Add keepalive directive to proxy to set MaxIdleConnsPerHost on transport
parents c6c221b8 824ec6cb
...@@ -108,7 +108,7 @@ func (p Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 ...@@ -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 { ...@@ -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{ ...@@ -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 { ...@@ -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) { ...@@ -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) { ...@@ -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 { ...@@ -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())
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment