diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ddce6a91793263de45b6086f95ddc59545cec1b..4f11cc69a3b611b412bd0895fe8332e3bb957613 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ FEATURES: * Added a `--enable-request-id` option to inject a request id into the upstream request [#PR392](https://github.com/gambol99/keycloak-proxy/pull/392) * Added the ability for the proxy to generate self-signed certificates for use via the `--enable-self-signed-tls` [#PR394](https://github.com/gambol99/keycloak-proxy/pull/394) * Added support for token with multiple audiences in the claims [#PR401](https://github.com/gambol99/keycloak-proxy/pull/401) +* Added `--max-idle-connections` and `--max-idle-connections-per-host` settings to support tuning the http connection pool size for performance needs [#PR405](https://github.com/gambol99/keycloak-proxy/pull/405) BREAK CHANGES * Added the http-cookie-only option as default true [#PR397](https://github.com/gambol99/keycloak-proxy/pull/397) diff --git a/README.md b/README.md index 2bf7ffaf94619aa43be7329c6ca0710f37ba7c7e..c22c0b0f5e0c245c482b3a76099ea14e6ac053c8 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,8 @@ GLOBAL OPTIONS: --upstream-response-header-timeout value the timeout placed on the response header for upstream (default: 10s) --upstream-expect-continue-timeout value the timeout placed on the expect continue for upstream (default: 10s) --verbose switch on debug / verbose logging (default: false) + --max-idle-connections max idle upstream / keycloak connections to keep alive, ready for reuse (default: 100) + --max-idle-connections-per-host limits the number of idle connections maintained per host (default: 50) --enabled-proxy-protocol enable proxy protocol (default: false) --server-read-timeout value the server read timeout on the http server (default: 10s) --server-write-timeout value the server write timeout on the http server (default: 10s) diff --git a/config.go b/config.go index 217524fdf362dcef10c7d507c486e1413c9e4f51..6418b5ad39248388706fafce15f34e9aa739ab44 100644 --- a/config.go +++ b/config.go @@ -46,6 +46,8 @@ func newDefaultConfig() *Config { Headers: make(map[string]string), LetsEncryptCacheDir: "./cache/", MatchClaims: make(map[string]string), + MaxIdleConns: 100, + MaxIdleConnsPerHost: 50, OAuthURI: "/oauth", OpenIDProviderTimeout: 30 * time.Second, PreserveHost: false, @@ -84,6 +86,12 @@ func (r *Config) isValid() error { if r.Listen == "" { return errors.New("you have not specified the listening interface") } + if r.MaxIdleConns <= 0 { + return errors.New("max-idle-connections must be a number > 0") + } + if r.MaxIdleConnsPerHost < 0 || r.MaxIdleConnsPerHost > r.MaxIdleConns { + return errors.New("maxi-idle-connections-per-host must be a number > 0 and <= max-idle-connections") + } if r.TLSCertificate != "" && r.TLSPrivateKey == "" { return errors.New("you have not provided a private key") } diff --git a/config_test.go b/config_test.go index 19704c5e41f4b903eedd5fca850e1053cdb8c0e9..523429183340a33604aeabdeca4a7a9640ad412e 100644 --- a/config_test.go +++ b/config_test.go @@ -63,8 +63,44 @@ func TestIsConfig(t *testing.T) { RedirectionURL: "http://120.0.0.1", Upstream: "http://120.0.0.1", }, + }, + { + Config: &Config{ + Listen: ":8080", + DiscoveryURL: "http://127.0.0.1:8080", + ClientID: "client", + ClientSecret: "client", + RedirectionURL: "http://120.0.0.1", + Upstream: "http://120.0.0.1", + MaxIdleConns: 100, + MaxIdleConnsPerHost: 50, + }, Ok: true, }, + { + Config: &Config{ + Listen: ":8080", + DiscoveryURL: "http://127.0.0.1:8080", + ClientID: "client", + ClientSecret: "client", + RedirectionURL: "http://120.0.0.1", + Upstream: "http://120.0.0.1", + MaxIdleConns: 0, + MaxIdleConnsPerHost: 0, + }, + }, + { + Config: &Config{ + Listen: ":8080", + DiscoveryURL: "http://127.0.0.1:8080", + ClientID: "client", + ClientSecret: "client", + RedirectionURL: "http://120.0.0.1", + Upstream: "http://120.0.0.1", + MaxIdleConns: 100, + MaxIdleConnsPerHost: 200, + }, + }, { Config: &Config{ Listen: ":8080", @@ -76,6 +112,8 @@ func TestIsConfig(t *testing.T) { MatchClaims: map[string]string{ "test": "&&&[", }, + MaxIdleConns: 100, + MaxIdleConnsPerHost: 50, }, }, { @@ -83,57 +121,69 @@ func TestIsConfig(t *testing.T) { Listen: ":8080", SkipTokenVerification: true, Upstream: "http://120.0.0.1", + MaxIdleConns: 100, + MaxIdleConnsPerHost: 50, }, Ok: true, }, { Config: &Config{ - DiscoveryURL: "http://127.0.0.1:8080", - ClientID: "client", - ClientSecret: "client", - RedirectionURL: "http://120.0.0.1", - Upstream: "http://120.0.0.1", + DiscoveryURL: "http://127.0.0.1:8080", + ClientID: "client", + ClientSecret: "client", + RedirectionURL: "http://120.0.0.1", + Upstream: "http://120.0.0.1", + MaxIdleConns: 100, + MaxIdleConnsPerHost: 50, }, }, { Config: &Config{ - Listen: ":8080", - DiscoveryURL: "http://127.0.0.1:8080", - ClientID: "client", - ClientSecret: "client", - RedirectionURL: "http://120.0.0.1", + Listen: ":8080", + DiscoveryURL: "http://127.0.0.1:8080", + ClientID: "client", + ClientSecret: "client", + RedirectionURL: "http://120.0.0.1", + MaxIdleConns: 100, + MaxIdleConnsPerHost: 50, }, }, { Config: &Config{ - Listen: ":8080", - DiscoveryURL: "http://127.0.0.1:8080", - ClientID: "client", - ClientSecret: "client", - RedirectionURL: "http://120.0.0.1", - Upstream: "this should fail", + Listen: ":8080", + DiscoveryURL: "http://127.0.0.1:8080", + ClientID: "client", + ClientSecret: "client", + RedirectionURL: "http://120.0.0.1", + Upstream: "this should fail", + MaxIdleConns: 100, + MaxIdleConnsPerHost: 50, }, }, { Config: &Config{ - Listen: ":8080", - DiscoveryURL: "http://127.0.0.1:8080", - ClientID: "client", - ClientSecret: "client", - RedirectionURL: "http://120.0.0.1", - Upstream: "this should fail", - SecureCookie: true, + Listen: ":8080", + DiscoveryURL: "http://127.0.0.1:8080", + ClientID: "client", + ClientSecret: "client", + RedirectionURL: "http://120.0.0.1", + Upstream: "this should fail", + SecureCookie: true, + MaxIdleConns: 100, + MaxIdleConnsPerHost: 50, }, }, { Config: &Config{ - Listen: ":8080", - DiscoveryURL: "http://127.0.0.1:8080", - ClientID: "client", - ClientSecret: "client", - RedirectionURL: "https://120.0.0.1", - Upstream: "this should fail", - SecureCookie: true, + Listen: ":8080", + DiscoveryURL: "http://127.0.0.1:8080", + ClientID: "client", + ClientSecret: "client", + RedirectionURL: "https://120.0.0.1", + Upstream: "this should fail", + SecureCookie: true, + MaxIdleConns: 100, + MaxIdleConnsPerHost: 50, }, Ok: true, }, diff --git a/doc.go b/doc.go index 9c64ace45d968762c10231cdfba55fbac4d8bd2a..16f27f5a97c1f7bb6b1bb90b741596f5c6fb1b26 100644 --- a/doc.go +++ b/doc.go @@ -312,6 +312,12 @@ type Config struct { Verbose bool `json:"verbose" yaml:"verbose" usage:"switch on debug / verbose logging"` // EnableProxyProtocol controls the proxy protocol EnableProxyProtocol bool `json:"enabled-proxy-protocol" yaml:"enabled-proxy-protocol" usage:"enable proxy protocol"` + + // MaxIdleConns is the max idle connections to keep alive, ready for reuse + MaxIdleConns int `json:"max-idle-connections" yaml:"max-idle-connections" usage:"max idle upstream / keycloak connections to keep alive, ready for reuse"` + // MaxIdleConnsPerHost limits the number of idle connections maintained per host + MaxIdleConnsPerHost int `json:"max-idle-connections-per-host" yaml:"max-idle-connections-per-host" usage:"limits the number of idle connections maintained per host"` + // ServerReadTimeout is the read timeout on the http server ServerReadTimeout time.Duration `json:"server-read-timeout" yaml:"server-read-timeout" usage:"the server read timeout on the http server"` // ServerWriteTimeout is the write timeout on the http server diff --git a/server.go b/server.go index 7f0c1739dabdf83e6110269dc06ee4398e1dbfcd..b6fa409572421a07d09f240abef0e1387945410e 100644 --- a/server.go +++ b/server.go @@ -613,6 +613,8 @@ func (r *oauthProxy) createUpstreamProxy(upstream *url.URL) error { ResponseHeaderTimeout: r.config.UpstreamResponseHeaderTimeout, TLSClientConfig: tlsConfig, TLSHandshakeTimeout: r.config.UpstreamTLSHandshakeTimeout, + MaxIdleConns: r.config.MaxIdleConns, + MaxIdleConnsPerHost: r.config.MaxIdleConnsPerHost, } return nil