Implement configurable timeout for upstream connections
Signed-off-by: Jack Henschel <jack.henschel@cern.ch>
This commit is contained in:
		
							parent
							
								
									27f4bb6a0e
								
							
						
					
					
						commit
						7a27cb04df
					
				|  | @ -39,6 +39,7 @@ If you are using an architecture specific tag (ex: v7.2.1-arm64) you should move | ||||||
| - [#1286](https://github.com/oauth2-proxy/oauth2-proxy/pull/1286) Add the `allowed_email_domains` and the `allowed_groups` on the `auth_request` + support standard wildcard char for validation with sub-domain and email-domain. (@w3st3ry @armandpicard) | - [#1286](https://github.com/oauth2-proxy/oauth2-proxy/pull/1286) Add the `allowed_email_domains` and the `allowed_groups` on the `auth_request` + support standard wildcard char for validation with sub-domain and email-domain. (@w3st3ry @armandpicard) | ||||||
| - [#1361](https://github.com/oauth2-proxy/oauth2-proxy/pull/1541) PKCE Code Challenge Support - RFC-7636 (@braunsonm) | - [#1361](https://github.com/oauth2-proxy/oauth2-proxy/pull/1541) PKCE Code Challenge Support - RFC-7636 (@braunsonm) | ||||||
| - [#1594](https://github.com/oauth2-proxy/oauth2-proxy/pull/1594) Release ARMv8 docker images (@braunsonm) | - [#1594](https://github.com/oauth2-proxy/oauth2-proxy/pull/1594) Release ARMv8 docker images (@braunsonm) | ||||||
|  | - [#1638](https://github.com/oauth2-proxy/oauth2-proxy/pull/1638) Implement configurable upstream timeout (@jacksgt) | ||||||
| 
 | 
 | ||||||
| # V7.2.1 | # V7.2.1 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -512,6 +512,7 @@ Requests will be proxied to this upstream if the path matches the request path. | ||||||
| | `flushInterval` | _[Duration](#duration)_ | FlushInterval is the period between flushing the response buffer when<br/>streaming response from the upstream.<br/>Defaults to 1 second. | | | `flushInterval` | _[Duration](#duration)_ | FlushInterval is the period between flushing the response buffer when<br/>streaming response from the upstream.<br/>Defaults to 1 second. | | ||||||
| | `passHostHeader` | _bool_ | PassHostHeader determines whether the request host header should be proxied<br/>to the upstream server.<br/>Defaults to true. | | | `passHostHeader` | _bool_ | PassHostHeader determines whether the request host header should be proxied<br/>to the upstream server.<br/>Defaults to true. | | ||||||
| | `proxyWebSockets` | _bool_ | ProxyWebSockets enables proxying of websockets to upstream servers<br/>Defaults to true. | | | `proxyWebSockets` | _bool_ | ProxyWebSockets enables proxying of websockets to upstream servers<br/>Defaults to true. | | ||||||
|  | | `timeout` | _[Duration](#duration)_ | Timeout is the maximum duration the server will wait for a response from the upstream server.<br/>Defaults to 30 seconds. | | ||||||
| 
 | 
 | ||||||
| ### UpstreamConfig | ### UpstreamConfig | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -196,6 +196,7 @@ An example [oauth2-proxy.cfg](https://github.com/oauth2-proxy/oauth2-proxy/blob/ | ||||||
| | `--tls-key-file` | string | path to private key file | | | | `--tls-key-file` | string | path to private key file | | | ||||||
| | `--tls-min-version` | string | minimum TLS version that is acceptable, either `"TLS1.2"` or `"TLS1.3"` | `"TLS1.2"` | | | `--tls-min-version` | string | minimum TLS version that is acceptable, either `"TLS1.2"` or `"TLS1.3"` | `"TLS1.2"` | | ||||||
| | `--upstream` | string \| list | the http url(s) of the upstream endpoint, file:// paths for static files or `static://<status_code>` for static response. Routing is based on the path | | | | `--upstream` | string \| list | the http url(s) of the upstream endpoint, file:// paths for static files or `static://<status_code>` for static response. Routing is based on the path | | | ||||||
|  | | `--upstream-timeout` | duration | maximum amount of time the server will wait for a response from the upstream | 30s | | ||||||
| | `--allowed-group` | string \| list | restrict logins to members of this group (may be given multiple times) | | | | `--allowed-group` | string \| list | restrict logins to members of this group (may be given multiple times) | | | ||||||
| | `--allowed-role` | string \| list | restrict logins to users with this role (may be given multiple times). Only works with the keycloak-oidc provider. | | | | `--allowed-role` | string \| list | restrict logins to users with this role (may be given multiple times). Only works with the keycloak-oidc provider. | | | ||||||
| | `--validate-url` | string | Access token validation endpoint | | | | `--validate-url` | string | Access token validation endpoint | | | ||||||
|  |  | ||||||
|  | @ -35,6 +35,7 @@ upstreamConfig: | ||||||
|     flushInterval: 1s |     flushInterval: 1s | ||||||
|     passHostHeader: true |     passHostHeader: true | ||||||
|     proxyWebSockets: true |     proxyWebSockets: true | ||||||
|  |     timeout: 30s | ||||||
| injectRequestHeaders: | injectRequestHeaders: | ||||||
| - name: Authorization | - name: Authorization | ||||||
|   values: |   values: | ||||||
|  | @ -118,6 +119,7 @@ redirect_url="http://localhost:4180/oauth2/callback" | ||||||
| 					FlushInterval:   durationPtr(options.DefaultUpstreamFlushInterval), | 					FlushInterval:   durationPtr(options.DefaultUpstreamFlushInterval), | ||||||
| 					PassHostHeader:  boolPtr(true), | 					PassHostHeader:  boolPtr(true), | ||||||
| 					ProxyWebSockets: boolPtr(true), | 					ProxyWebSockets: boolPtr(true), | ||||||
|  | 					Timeout:         durationPtr(options.DefaultUpstreamTimeout), | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -33,6 +33,7 @@ func NewLegacyOptions() *LegacyOptions { | ||||||
| 			PassHostHeader:  true, | 			PassHostHeader:  true, | ||||||
| 			ProxyWebSockets: true, | 			ProxyWebSockets: true, | ||||||
| 			FlushInterval:   DefaultUpstreamFlushInterval, | 			FlushInterval:   DefaultUpstreamFlushInterval, | ||||||
|  | 			Timeout:         DefaultUpstreamTimeout, | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		LegacyHeaders: LegacyHeaders{ | 		LegacyHeaders: LegacyHeaders{ | ||||||
|  | @ -101,6 +102,7 @@ type LegacyUpstreams struct { | ||||||
| 	ProxyWebSockets               bool          `flag:"proxy-websockets" cfg:"proxy_websockets"` | 	ProxyWebSockets               bool          `flag:"proxy-websockets" cfg:"proxy_websockets"` | ||||||
| 	SSLUpstreamInsecureSkipVerify bool          `flag:"ssl-upstream-insecure-skip-verify" cfg:"ssl_upstream_insecure_skip_verify"` | 	SSLUpstreamInsecureSkipVerify bool          `flag:"ssl-upstream-insecure-skip-verify" cfg:"ssl_upstream_insecure_skip_verify"` | ||||||
| 	Upstreams                     []string      `flag:"upstream" cfg:"upstreams"` | 	Upstreams                     []string      `flag:"upstream" cfg:"upstreams"` | ||||||
|  | 	Timeout                       time.Duration `flag:"upstream-timeout" cfg:"upstream_timeout"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func legacyUpstreamsFlagSet() *pflag.FlagSet { | func legacyUpstreamsFlagSet() *pflag.FlagSet { | ||||||
|  | @ -111,6 +113,7 @@ func legacyUpstreamsFlagSet() *pflag.FlagSet { | ||||||
| 	flagSet.Bool("proxy-websockets", true, "enables WebSocket proxying") | 	flagSet.Bool("proxy-websockets", true, "enables WebSocket proxying") | ||||||
| 	flagSet.Bool("ssl-upstream-insecure-skip-verify", false, "skip validation of certificates presented when using HTTPS upstreams") | 	flagSet.Bool("ssl-upstream-insecure-skip-verify", false, "skip validation of certificates presented when using HTTPS upstreams") | ||||||
| 	flagSet.StringSlice("upstream", []string{}, "the http url(s) of the upstream endpoint, file:// paths for static files or static://<status_code> for static response. Routing is based on the path") | 	flagSet.StringSlice("upstream", []string{}, "the http url(s) of the upstream endpoint, file:// paths for static files or static://<status_code> for static response. Routing is based on the path") | ||||||
|  | 	flagSet.Duration("upstream-timeout", DefaultUpstreamTimeout, "maximum amount of time the server will wait for a response from the upstream") | ||||||
| 
 | 
 | ||||||
| 	return flagSet | 	return flagSet | ||||||
| } | } | ||||||
|  | @ -129,6 +132,7 @@ func (l *LegacyUpstreams) convert() (UpstreamConfig, error) { | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		flushInterval := Duration(l.FlushInterval) | 		flushInterval := Duration(l.FlushInterval) | ||||||
|  | 		timeout := Duration(l.Timeout) | ||||||
| 		upstream := Upstream{ | 		upstream := Upstream{ | ||||||
| 			ID:                    u.Path, | 			ID:                    u.Path, | ||||||
| 			Path:                  u.Path, | 			Path:                  u.Path, | ||||||
|  | @ -137,6 +141,7 @@ func (l *LegacyUpstreams) convert() (UpstreamConfig, error) { | ||||||
| 			PassHostHeader:        &l.PassHostHeader, | 			PassHostHeader:        &l.PassHostHeader, | ||||||
| 			ProxyWebSockets:       &l.ProxyWebSockets, | 			ProxyWebSockets:       &l.ProxyWebSockets, | ||||||
| 			FlushInterval:         &flushInterval, | 			FlushInterval:         &flushInterval, | ||||||
|  | 			Timeout:               &timeout, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		switch u.Scheme { | 		switch u.Scheme { | ||||||
|  | @ -168,6 +173,7 @@ func (l *LegacyUpstreams) convert() (UpstreamConfig, error) { | ||||||
| 			upstream.PassHostHeader = nil | 			upstream.PassHostHeader = nil | ||||||
| 			upstream.ProxyWebSockets = nil | 			upstream.ProxyWebSockets = nil | ||||||
| 			upstream.FlushInterval = nil | 			upstream.FlushInterval = nil | ||||||
|  | 			upstream.Timeout = nil | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		upstreams.Upstreams = append(upstreams.Upstreams, upstream) | 		upstreams.Upstreams = append(upstreams.Upstreams, upstream) | ||||||
|  |  | ||||||
|  | @ -17,7 +17,9 @@ var _ = Describe("Legacy Options", func() { | ||||||
| 
 | 
 | ||||||
| 			// Set upstreams and related options to test their conversion
 | 			// Set upstreams and related options to test their conversion
 | ||||||
| 			flushInterval := Duration(5 * time.Second) | 			flushInterval := Duration(5 * time.Second) | ||||||
|  | 			timeout := Duration(5 * time.Second) | ||||||
| 			legacyOpts.LegacyUpstreams.FlushInterval = time.Duration(flushInterval) | 			legacyOpts.LegacyUpstreams.FlushInterval = time.Duration(flushInterval) | ||||||
|  | 			legacyOpts.LegacyUpstreams.Timeout = time.Duration(timeout) | ||||||
| 			legacyOpts.LegacyUpstreams.PassHostHeader = true | 			legacyOpts.LegacyUpstreams.PassHostHeader = true | ||||||
| 			legacyOpts.LegacyUpstreams.ProxyWebSockets = true | 			legacyOpts.LegacyUpstreams.ProxyWebSockets = true | ||||||
| 			legacyOpts.LegacyUpstreams.SSLUpstreamInsecureSkipVerify = true | 			legacyOpts.LegacyUpstreams.SSLUpstreamInsecureSkipVerify = true | ||||||
|  | @ -36,6 +38,7 @@ var _ = Describe("Legacy Options", func() { | ||||||
| 						InsecureSkipTLSVerify: true, | 						InsecureSkipTLSVerify: true, | ||||||
| 						PassHostHeader:        &truth, | 						PassHostHeader:        &truth, | ||||||
| 						ProxyWebSockets:       &truth, | 						ProxyWebSockets:       &truth, | ||||||
|  | 						Timeout:               &timeout, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						ID:                    "/bar", | 						ID:                    "/bar", | ||||||
|  | @ -45,6 +48,7 @@ var _ = Describe("Legacy Options", func() { | ||||||
| 						InsecureSkipTLSVerify: true, | 						InsecureSkipTLSVerify: true, | ||||||
| 						PassHostHeader:        &truth, | 						PassHostHeader:        &truth, | ||||||
| 						ProxyWebSockets:       &truth, | 						ProxyWebSockets:       &truth, | ||||||
|  | 						Timeout:               &timeout, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						ID:                    "static://204", | 						ID:                    "static://204", | ||||||
|  | @ -56,6 +60,7 @@ var _ = Describe("Legacy Options", func() { | ||||||
| 						InsecureSkipTLSVerify: false, | 						InsecureSkipTLSVerify: false, | ||||||
| 						PassHostHeader:        nil, | 						PassHostHeader:        nil, | ||||||
| 						ProxyWebSockets:       nil, | 						ProxyWebSockets:       nil, | ||||||
|  | 						Timeout:               nil, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 			} | 			} | ||||||
|  | @ -140,6 +145,7 @@ var _ = Describe("Legacy Options", func() { | ||||||
| 		passHostHeader := false | 		passHostHeader := false | ||||||
| 		proxyWebSockets := true | 		proxyWebSockets := true | ||||||
| 		flushInterval := Duration(5 * time.Second) | 		flushInterval := Duration(5 * time.Second) | ||||||
|  | 		timeout := Duration(5 * time.Second) | ||||||
| 
 | 
 | ||||||
| 		// Test cases and expected outcomes
 | 		// Test cases and expected outcomes
 | ||||||
| 		validHTTP := "http://foo.bar/baz" | 		validHTTP := "http://foo.bar/baz" | ||||||
|  | @ -151,6 +157,7 @@ var _ = Describe("Legacy Options", func() { | ||||||
| 			PassHostHeader:        &passHostHeader, | 			PassHostHeader:        &passHostHeader, | ||||||
| 			ProxyWebSockets:       &proxyWebSockets, | 			ProxyWebSockets:       &proxyWebSockets, | ||||||
| 			FlushInterval:         &flushInterval, | 			FlushInterval:         &flushInterval, | ||||||
|  | 			Timeout:               &timeout, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// Test cases and expected outcomes
 | 		// Test cases and expected outcomes
 | ||||||
|  | @ -163,6 +170,7 @@ var _ = Describe("Legacy Options", func() { | ||||||
| 			PassHostHeader:        &passHostHeader, | 			PassHostHeader:        &passHostHeader, | ||||||
| 			ProxyWebSockets:       &proxyWebSockets, | 			ProxyWebSockets:       &proxyWebSockets, | ||||||
| 			FlushInterval:         &flushInterval, | 			FlushInterval:         &flushInterval, | ||||||
|  | 			Timeout:               &timeout, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		validFileWithFragment := "file:///var/lib/website#/bar" | 		validFileWithFragment := "file:///var/lib/website#/bar" | ||||||
|  | @ -174,6 +182,7 @@ var _ = Describe("Legacy Options", func() { | ||||||
| 			PassHostHeader:        &passHostHeader, | 			PassHostHeader:        &passHostHeader, | ||||||
| 			ProxyWebSockets:       &proxyWebSockets, | 			ProxyWebSockets:       &proxyWebSockets, | ||||||
| 			FlushInterval:         &flushInterval, | 			FlushInterval:         &flushInterval, | ||||||
|  | 			Timeout:               &timeout, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		validStatic := "static://204" | 		validStatic := "static://204" | ||||||
|  | @ -188,6 +197,7 @@ var _ = Describe("Legacy Options", func() { | ||||||
| 			PassHostHeader:        nil, | 			PassHostHeader:        nil, | ||||||
| 			ProxyWebSockets:       nil, | 			ProxyWebSockets:       nil, | ||||||
| 			FlushInterval:         nil, | 			FlushInterval:         nil, | ||||||
|  | 			Timeout:               nil, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		invalidStatic := "static://abc" | 		invalidStatic := "static://abc" | ||||||
|  | @ -202,6 +212,7 @@ var _ = Describe("Legacy Options", func() { | ||||||
| 			PassHostHeader:        nil, | 			PassHostHeader:        nil, | ||||||
| 			ProxyWebSockets:       nil, | 			ProxyWebSockets:       nil, | ||||||
| 			FlushInterval:         nil, | 			FlushInterval:         nil, | ||||||
|  | 			Timeout:               nil, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		invalidHTTP := ":foo" | 		invalidHTTP := ":foo" | ||||||
|  | @ -215,6 +226,7 @@ var _ = Describe("Legacy Options", func() { | ||||||
| 					PassHostHeader:                passHostHeader, | 					PassHostHeader:                passHostHeader, | ||||||
| 					ProxyWebSockets:               proxyWebSockets, | 					ProxyWebSockets:               proxyWebSockets, | ||||||
| 					FlushInterval:                 time.Duration(flushInterval), | 					FlushInterval:                 time.Duration(flushInterval), | ||||||
|  | 					Timeout:                       time.Duration(timeout), | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				upstreams, err := legacyUpstreams.convert() | 				upstreams, err := legacyUpstreams.convert() | ||||||
|  |  | ||||||
|  | @ -22,6 +22,7 @@ var _ = Describe("Load", func() { | ||||||
| 			PassHostHeader:  true, | 			PassHostHeader:  true, | ||||||
| 			ProxyWebSockets: true, | 			ProxyWebSockets: true, | ||||||
| 			FlushInterval:   DefaultUpstreamFlushInterval, | 			FlushInterval:   DefaultUpstreamFlushInterval, | ||||||
|  | 			Timeout:         DefaultUpstreamTimeout, | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		LegacyHeaders: LegacyHeaders{ | 		LegacyHeaders: LegacyHeaders{ | ||||||
|  |  | ||||||
|  | @ -5,6 +5,9 @@ import "time" | ||||||
| const ( | const ( | ||||||
| 	// DefaultUpstreamFlushInterval is the default value for the Upstream FlushInterval.
 | 	// DefaultUpstreamFlushInterval is the default value for the Upstream FlushInterval.
 | ||||||
| 	DefaultUpstreamFlushInterval = 1 * time.Second | 	DefaultUpstreamFlushInterval = 1 * time.Second | ||||||
|  | 
 | ||||||
|  | 	// DefaultUpstreamTimeout is the maximum duration a network dial to a upstream server for a response.
 | ||||||
|  | 	DefaultUpstreamTimeout = 30 * time.Second | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // UpstreamConfig is a collection of definitions for upstream servers.
 | // UpstreamConfig is a collection of definitions for upstream servers.
 | ||||||
|  | @ -84,4 +87,8 @@ type Upstream struct { | ||||||
| 	// ProxyWebSockets enables proxying of websockets to upstream servers
 | 	// ProxyWebSockets enables proxying of websockets to upstream servers
 | ||||||
| 	// Defaults to true.
 | 	// Defaults to true.
 | ||||||
| 	ProxyWebSockets *bool `json:"proxyWebSockets,omitempty"` | 	ProxyWebSockets *bool `json:"proxyWebSockets,omitempty"` | ||||||
|  | 
 | ||||||
|  | 	// Timeout is the maximum duration the server will wait for a response from the upstream server.
 | ||||||
|  | 	// Defaults to 30 seconds.
 | ||||||
|  | 	Timeout *Duration `json:"timeout,omitempty"` | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,7 +1,6 @@ | ||||||
| package upstream | package upstream | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"crypto/tls" |  | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/http/httputil" | 	"net/http/httputil" | ||||||
| 	"net/url" | 	"net/url" | ||||||
|  | @ -100,6 +99,14 @@ func (h *httpUpstreamProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) | ||||||
| func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler ProxyErrorHandler) http.Handler { | func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler ProxyErrorHandler) http.Handler { | ||||||
| 	proxy := httputil.NewSingleHostReverseProxy(target) | 	proxy := httputil.NewSingleHostReverseProxy(target) | ||||||
| 
 | 
 | ||||||
|  | 	// Inherit default transport options from Go's stdlib
 | ||||||
|  | 	transport := http.DefaultTransport.(*http.Transport).Clone() | ||||||
|  | 
 | ||||||
|  | 	// Change default duration for waiting for an upstream response
 | ||||||
|  | 	if upstream.Timeout != nil { | ||||||
|  | 		transport.ResponseHeaderTimeout = upstream.Timeout.Duration() | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// Configure options on the SingleHostReverseProxy
 | 	// Configure options on the SingleHostReverseProxy
 | ||||||
| 	if upstream.FlushInterval != nil { | 	if upstream.FlushInterval != nil { | ||||||
| 		proxy.FlushInterval = upstream.FlushInterval.Duration() | 		proxy.FlushInterval = upstream.FlushInterval.Duration() | ||||||
|  | @ -110,9 +117,7 @@ func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler Pr | ||||||
| 	// InsecureSkipVerify is a configurable option we allow
 | 	// InsecureSkipVerify is a configurable option we allow
 | ||||||
| 	/* #nosec G402 */ | 	/* #nosec G402 */ | ||||||
| 	if upstream.InsecureSkipTLSVerify { | 	if upstream.InsecureSkipTLSVerify { | ||||||
| 		proxy.Transport = &http.Transport{ | 		transport.TLSClientConfig.InsecureSkipVerify = true | ||||||
| 			TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Ensure we always pass the original request path
 | 	// Ensure we always pass the original request path
 | ||||||
|  | @ -127,6 +132,10 @@ func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler Pr | ||||||
| 	if errorHandler != nil { | 	if errorHandler != nil { | ||||||
| 		proxy.ErrorHandler = errorHandler | 		proxy.ErrorHandler = errorHandler | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	// Apply the customized transport to our proxy before returning it
 | ||||||
|  | 	proxy.Transport = transport | ||||||
|  | 
 | ||||||
| 	return proxy | 	return proxy | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -156,11 +165,17 @@ func setProxyDirector(proxy *httputil.ReverseProxy) { | ||||||
| // newWebSocketReverseProxy creates a new reverse proxy for proxying websocket connections.
 | // newWebSocketReverseProxy creates a new reverse proxy for proxying websocket connections.
 | ||||||
| func newWebSocketReverseProxy(u *url.URL, skipTLSVerify bool) http.Handler { | func newWebSocketReverseProxy(u *url.URL, skipTLSVerify bool) http.Handler { | ||||||
| 	wsProxy := httputil.NewSingleHostReverseProxy(u) | 	wsProxy := httputil.NewSingleHostReverseProxy(u) | ||||||
|  | 
 | ||||||
|  | 	// Inherit default transport options from Go's stdlib
 | ||||||
|  | 	transport := http.DefaultTransport.(*http.Transport).Clone() | ||||||
|  | 
 | ||||||
| 	/* #nosec G402 */ | 	/* #nosec G402 */ | ||||||
| 	if skipTLSVerify { | 	if skipTLSVerify { | ||||||
| 		wsProxy.Transport = &http.Transport{ | 		transport.TLSClientConfig.InsecureSkipVerify = true | ||||||
| 			TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	// Apply the customized transport to our proxy before returning it
 | ||||||
|  | 	wsProxy.Transport = transport | ||||||
|  | 
 | ||||||
| 	return wsProxy | 	return wsProxy | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -3,7 +3,6 @@ package upstream | ||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"crypto" | 	"crypto" | ||||||
| 	"crypto/tls" |  | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | @ -23,9 +22,8 @@ import ( | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var _ = Describe("HTTP Upstream Suite", func() { | var _ = Describe("HTTP Upstream Suite", func() { | ||||||
| 
 | 	defaultFlushInterval := options.Duration(options.DefaultUpstreamFlushInterval) | ||||||
| 	const flushInterval5s = options.Duration(5 * time.Second) | 	defaultTimeout := options.Duration(options.DefaultUpstreamTimeout) | ||||||
| 	const flushInterval1s = options.Duration(1 * time.Second) |  | ||||||
| 	truth := true | 	truth := true | ||||||
| 	falsum := false | 	falsum := false | ||||||
| 
 | 
 | ||||||
|  | @ -62,12 +60,15 @@ var _ = Describe("HTTP Upstream Suite", func() { | ||||||
| 
 | 
 | ||||||
| 			flush := options.Duration(1 * time.Second) | 			flush := options.Duration(1 * time.Second) | ||||||
| 
 | 
 | ||||||
|  | 			timeout := options.Duration(options.DefaultUpstreamTimeout) | ||||||
|  | 
 | ||||||
| 			upstream := options.Upstream{ | 			upstream := options.Upstream{ | ||||||
| 				ID:                    in.id, | 				ID:                    in.id, | ||||||
| 				PassHostHeader:        &in.passUpstreamHostHeader, | 				PassHostHeader:        &in.passUpstreamHostHeader, | ||||||
| 				ProxyWebSockets:       &falsum, | 				ProxyWebSockets:       &falsum, | ||||||
| 				InsecureSkipTLSVerify: false, | 				InsecureSkipTLSVerify: false, | ||||||
| 				FlushInterval:         &flush, | 				FlushInterval:         &flush, | ||||||
|  | 				Timeout:               &timeout, | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			Expect(in.serverAddr).ToNot(BeNil()) | 			Expect(in.serverAddr).ToNot(BeNil()) | ||||||
|  | @ -318,13 +319,13 @@ var _ = Describe("HTTP Upstream Suite", func() { | ||||||
| 		req = middlewareapi.AddRequestScope(req, &middlewareapi.RequestScope{}) | 		req = middlewareapi.AddRequestScope(req, &middlewareapi.RequestScope{}) | ||||||
| 		rw := httptest.NewRecorder() | 		rw := httptest.NewRecorder() | ||||||
| 
 | 
 | ||||||
| 		flush := options.Duration(1 * time.Second) |  | ||||||
| 		upstream := options.Upstream{ | 		upstream := options.Upstream{ | ||||||
| 			ID:                    "noPassHost", | 			ID:                    "noPassHost", | ||||||
| 			PassHostHeader:        &falsum, | 			PassHostHeader:        &falsum, | ||||||
| 			ProxyWebSockets:       &falsum, | 			ProxyWebSockets:       &falsum, | ||||||
| 			InsecureSkipTLSVerify: false, | 			InsecureSkipTLSVerify: false, | ||||||
| 			FlushInterval:         &flush, | 			FlushInterval:         &defaultFlushInterval, | ||||||
|  | 			Timeout:               &defaultTimeout, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		u, err := url.Parse(serverAddr) | 		u, err := url.Parse(serverAddr) | ||||||
|  | @ -354,6 +355,7 @@ var _ = Describe("HTTP Upstream Suite", func() { | ||||||
| 		skipVerify      bool | 		skipVerify      bool | ||||||
| 		sigData         *options.SignatureData | 		sigData         *options.SignatureData | ||||||
| 		errorHandler    func(http.ResponseWriter, *http.Request, error) | 		errorHandler    func(http.ResponseWriter, *http.Request, error) | ||||||
|  | 		timeout         options.Duration | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	DescribeTable("newHTTPUpstreamProxy", | 	DescribeTable("newHTTPUpstreamProxy", | ||||||
|  | @ -366,6 +368,7 @@ var _ = Describe("HTTP Upstream Suite", func() { | ||||||
| 				FlushInterval:         &in.flushInterval, | 				FlushInterval:         &in.flushInterval, | ||||||
| 				InsecureSkipTLSVerify: in.skipVerify, | 				InsecureSkipTLSVerify: in.skipVerify, | ||||||
| 				ProxyWebSockets:       &in.proxyWebSockets, | 				ProxyWebSockets:       &in.proxyWebSockets, | ||||||
|  | 				Timeout:               &in.timeout, | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			handler := newHTTPUpstreamProxy(upstream, u, in.sigData, in.errorHandler) | 			handler := newHTTPUpstreamProxy(upstream, u, in.sigData, in.errorHandler) | ||||||
|  | @ -380,49 +383,63 @@ var _ = Describe("HTTP Upstream Suite", func() { | ||||||
| 			proxy, ok := upstreamProxy.handler.(*httputil.ReverseProxy) | 			proxy, ok := upstreamProxy.handler.(*httputil.ReverseProxy) | ||||||
| 			Expect(ok).To(BeTrue()) | 			Expect(ok).To(BeTrue()) | ||||||
| 			Expect(proxy.FlushInterval).To(Equal(in.flushInterval.Duration())) | 			Expect(proxy.FlushInterval).To(Equal(in.flushInterval.Duration())) | ||||||
|  | 			transport, ok := proxy.Transport.(*http.Transport) | ||||||
|  | 			Expect(ok).To(BeTrue()) | ||||||
|  | 			Expect(transport.ResponseHeaderTimeout).To(Equal(in.timeout.Duration())) | ||||||
| 			Expect(proxy.ErrorHandler != nil).To(Equal(in.errorHandler != nil)) | 			Expect(proxy.ErrorHandler != nil).To(Equal(in.errorHandler != nil)) | ||||||
| 			if in.skipVerify { | 			if in.skipVerify { | ||||||
| 				Expect(proxy.Transport).To(Equal(&http.Transport{ | 				Expect(transport.TLSClientConfig.InsecureSkipVerify).To(Equal(true)) | ||||||
| 					TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, |  | ||||||
| 				})) |  | ||||||
| 			} | 			} | ||||||
| 		}, | 		}, | ||||||
| 		Entry("with proxy websockets", &newUpstreamTableInput{ | 		Entry("with proxy websockets", &newUpstreamTableInput{ | ||||||
| 			proxyWebSockets: true, | 			proxyWebSockets: true, | ||||||
| 			flushInterval:   flushInterval1s, | 			flushInterval:   defaultFlushInterval, | ||||||
| 			skipVerify:      false, | 			skipVerify:      false, | ||||||
| 			sigData:         nil, | 			sigData:         nil, | ||||||
| 			errorHandler:    nil, | 			errorHandler:    nil, | ||||||
|  | 			timeout:         defaultTimeout, | ||||||
| 		}), | 		}), | ||||||
| 		Entry("with a non standard flush interval", &newUpstreamTableInput{ | 		Entry("with a non standard flush interval", &newUpstreamTableInput{ | ||||||
| 			proxyWebSockets: false, | 			proxyWebSockets: false, | ||||||
| 			flushInterval:   flushInterval5s, | 			flushInterval:   options.Duration(5 * time.Second), | ||||||
| 			skipVerify:      false, | 			skipVerify:      false, | ||||||
| 			sigData:         nil, | 			sigData:         nil, | ||||||
| 			errorHandler:    nil, | 			errorHandler:    nil, | ||||||
|  | 			timeout:         defaultTimeout, | ||||||
| 		}), | 		}), | ||||||
| 		Entry("with a InsecureSkipTLSVerify", &newUpstreamTableInput{ | 		Entry("with a InsecureSkipTLSVerify", &newUpstreamTableInput{ | ||||||
| 			proxyWebSockets: false, | 			proxyWebSockets: false, | ||||||
| 			flushInterval:   flushInterval1s, | 			flushInterval:   defaultFlushInterval, | ||||||
| 			skipVerify:      true, | 			skipVerify:      true, | ||||||
| 			sigData:         nil, | 			sigData:         nil, | ||||||
| 			errorHandler:    nil, | 			errorHandler:    nil, | ||||||
|  | 			timeout:         defaultTimeout, | ||||||
| 		}), | 		}), | ||||||
| 		Entry("with a SignatureData", &newUpstreamTableInput{ | 		Entry("with a SignatureData", &newUpstreamTableInput{ | ||||||
| 			proxyWebSockets: false, | 			proxyWebSockets: false, | ||||||
| 			flushInterval:   flushInterval1s, | 			flushInterval:   defaultFlushInterval, | ||||||
| 			skipVerify:      false, | 			skipVerify:      false, | ||||||
| 			sigData:         &options.SignatureData{Hash: crypto.SHA256, Key: "secret"}, | 			sigData:         &options.SignatureData{Hash: crypto.SHA256, Key: "secret"}, | ||||||
| 			errorHandler:    nil, | 			errorHandler:    nil, | ||||||
|  | 			timeout:         defaultTimeout, | ||||||
| 		}), | 		}), | ||||||
| 		Entry("with an error handler", &newUpstreamTableInput{ | 		Entry("with an error handler", &newUpstreamTableInput{ | ||||||
| 			proxyWebSockets: false, | 			proxyWebSockets: false, | ||||||
| 			flushInterval:   flushInterval1s, | 			flushInterval:   defaultFlushInterval, | ||||||
| 			skipVerify:      false, | 			skipVerify:      false, | ||||||
| 			sigData:         nil, | 			sigData:         nil, | ||||||
| 			errorHandler: func(rw http.ResponseWriter, req *http.Request, arg3 error) { | 			errorHandler: func(rw http.ResponseWriter, req *http.Request, arg3 error) { | ||||||
| 				rw.WriteHeader(502) | 				rw.WriteHeader(502) | ||||||
| 			}, | 			}, | ||||||
|  | 			timeout: defaultTimeout, | ||||||
|  | 		}), | ||||||
|  | 		Entry("with a non-default timeout", &newUpstreamTableInput{ | ||||||
|  | 			proxyWebSockets: false, | ||||||
|  | 			flushInterval:   defaultFlushInterval, | ||||||
|  | 			skipVerify:      false, | ||||||
|  | 			sigData:         nil, | ||||||
|  | 			errorHandler:    nil, | ||||||
|  | 			timeout:         options.Duration(5 * time.Second), | ||||||
| 		}), | 		}), | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
|  | @ -431,12 +448,14 @@ var _ = Describe("HTTP Upstream Suite", func() { | ||||||
| 
 | 
 | ||||||
| 		BeforeEach(func() { | 		BeforeEach(func() { | ||||||
| 			flush := options.Duration(1 * time.Second) | 			flush := options.Duration(1 * time.Second) | ||||||
|  | 			timeout := options.Duration(options.DefaultUpstreamTimeout) | ||||||
| 			upstream := options.Upstream{ | 			upstream := options.Upstream{ | ||||||
| 				ID:                    "websocketProxy", | 				ID:                    "websocketProxy", | ||||||
| 				PassHostHeader:        &truth, | 				PassHostHeader:        &truth, | ||||||
| 				ProxyWebSockets:       &truth, | 				ProxyWebSockets:       &truth, | ||||||
| 				InsecureSkipTLSVerify: false, | 				InsecureSkipTLSVerify: false, | ||||||
| 				FlushInterval:         &flush, | 				FlushInterval:         &flush, | ||||||
|  | 				Timeout:               &timeout, | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			u, err := url.Parse(serverAddr) | 			u, err := url.Parse(serverAddr) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue