git.iko.soy/typeable/oauth2-proxy
on master help sign in
Implement configurable timeout for upstream connections
7a27cb0Jack Henschel
CHANGELOG.md+1
@@ -39,6 +39,7 @@ If you are using an architecture specific tag (ex: v7.2.1-arm64) you should move
39- [#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)
39- [#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)
40- [#1361](https://github.com/oauth2-proxy/oauth2-proxy/pull/1541) PKCE Code Challenge Support - RFC-7636 (@braunsonm)
40- [#1361](https://github.com/oauth2-proxy/oauth2-proxy/pull/1541) PKCE Code Challenge Support - RFC-7636 (@braunsonm)
41- [#1594](https://github.com/oauth2-proxy/oauth2-proxy/pull/1594) Release ARMv8 docker images (@braunsonm)
41- [#1594](https://github.com/oauth2-proxy/oauth2-proxy/pull/1594) Release ARMv8 docker images (@braunsonm)
42+- [#1638](https://github.com/oauth2-proxy/oauth2-proxy/pull/1638) Implement configurable upstream timeout (@jacksgt)
42
43
43# V7.2.1
44# V7.2.1
44
45
docs/docs/configuration/alpha_config.md+1
@@ -512,6 +512,7 @@ Requests will be proxied to this upstream if the path matches the request path.
512| `flushInterval` | _[Duration](#duration)_ | FlushInterval is the period between flushing the response buffer when<br/>streaming response from the upstream.<br/>Defaults to 1 second. |
512| `flushInterval` | _[Duration](#duration)_ | FlushInterval is the period between flushing the response buffer when<br/>streaming response from the upstream.<br/>Defaults to 1 second. |
513| `passHostHeader` | _bool_ | PassHostHeader determines whether the request host header should be proxied<br/>to the upstream server.<br/>Defaults to true. |
513| `passHostHeader` | _bool_ | PassHostHeader determines whether the request host header should be proxied<br/>to the upstream server.<br/>Defaults to true. |
514| `proxyWebSockets` | _bool_ | ProxyWebSockets enables proxying of websockets to upstream servers<br/>Defaults to true. |
514| `proxyWebSockets` | _bool_ | ProxyWebSockets enables proxying of websockets to upstream servers<br/>Defaults to true. |
515+| `timeout` | _[Duration](#duration)_ | Timeout is the maximum duration the server will wait for a response from the upstream server.<br/>Defaults to 30 seconds. |
515
516
516### UpstreamConfig
517### UpstreamConfig
517
518
docs/docs/configuration/overview.md+1
@@ -196,6 +196,7 @@ An example [oauth2-proxy.cfg](https://github.com/oauth2-proxy/oauth2-proxy/blob/
196| `--tls-key-file` | string | path to private key file | |
196| `--tls-key-file` | string | path to private key file | |
197| `--tls-min-version` | string | minimum TLS version that is acceptable, either `"TLS1.2"` or `"TLS1.3"` | `"TLS1.2"` |
197| `--tls-min-version` | string | minimum TLS version that is acceptable, either `"TLS1.2"` or `"TLS1.3"` | `"TLS1.2"` |
198| `--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 | |
198| `--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 | |
199+| `--upstream-timeout` | duration | maximum amount of time the server will wait for a response from the upstream | 30s |
199| `--allowed-group` | string \| list | restrict logins to members of this group (may be given multiple times) | |
200| `--allowed-group` | string \| list | restrict logins to members of this group (may be given multiple times) | |
200| `--allowed-role` | string \| list | restrict logins to users with this role (may be given multiple times). Only works with the keycloak-oidc provider. | |
201| `--allowed-role` | string \| list | restrict logins to users with this role (may be given multiple times). Only works with the keycloak-oidc provider. | |
201| `--validate-url` | string | Access token validation endpoint | |
202| `--validate-url` | string | Access token validation endpoint | |
main_test.go+2
@@ -35,6 +35,7 @@ upstreamConfig:
35 flushInterval: 1s
35 flushInterval: 1s
36 passHostHeader: true
36 passHostHeader: true
37 proxyWebSockets: true
37 proxyWebSockets: true
38+ timeout: 30s
38injectRequestHeaders:
39injectRequestHeaders:
39- name: Authorization
40- name: Authorization
40 values:
41 values:
@@ -118,6 +119,7 @@ redirect_url="http://localhost:4180/oauth2/callback"
118 FlushInterval: durationPtr(options.DefaultUpstreamFlushInterval),
119 FlushInterval: durationPtr(options.DefaultUpstreamFlushInterval),
119 PassHostHeader: boolPtr(true),
120 PassHostHeader: boolPtr(true),
120 ProxyWebSockets: boolPtr(true),
121 ProxyWebSockets: boolPtr(true),
122+ Timeout: durationPtr(options.DefaultUpstreamTimeout),
121 },
123 },
122 },
124 },
123 }
125 }
pkg/apis/options/legacy_options.go+6
@@ -33,6 +33,7 @@ func NewLegacyOptions() *LegacyOptions {
33 PassHostHeader: true,
33 PassHostHeader: true,
34 ProxyWebSockets: true,
34 ProxyWebSockets: true,
35 FlushInterval: DefaultUpstreamFlushInterval,
35 FlushInterval: DefaultUpstreamFlushInterval,
36+ Timeout: DefaultUpstreamTimeout,
36 },
37 },
37
38
38 LegacyHeaders: LegacyHeaders{
39 LegacyHeaders: LegacyHeaders{
@@ -101,6 +102,7 @@ type LegacyUpstreams struct {
101 ProxyWebSockets bool `flag:"proxy-websockets" cfg:"proxy_websockets"`
102 ProxyWebSockets bool `flag:"proxy-websockets" cfg:"proxy_websockets"`
102 SSLUpstreamInsecureSkipVerify bool `flag:"ssl-upstream-insecure-skip-verify" cfg:"ssl_upstream_insecure_skip_verify"`
103 SSLUpstreamInsecureSkipVerify bool `flag:"ssl-upstream-insecure-skip-verify" cfg:"ssl_upstream_insecure_skip_verify"`
103 Upstreams []string `flag:"upstream" cfg:"upstreams"`
104 Upstreams []string `flag:"upstream" cfg:"upstreams"`
105+ Timeout time.Duration `flag:"upstream-timeout" cfg:"upstream_timeout"`
104}
106}
105
107
106func legacyUpstreamsFlagSet() *pflag.FlagSet {
108func legacyUpstreamsFlagSet() *pflag.FlagSet {
@@ -111,6 +113,7 @@ func legacyUpstreamsFlagSet() *pflag.FlagSet {
111 flagSet.Bool("proxy-websockets", true, "enables WebSocket proxying")
113 flagSet.Bool("proxy-websockets", true, "enables WebSocket proxying")
112 flagSet.Bool("ssl-upstream-insecure-skip-verify", false, "skip validation of certificates presented when using HTTPS upstreams")
114 flagSet.Bool("ssl-upstream-insecure-skip-verify", false, "skip validation of certificates presented when using HTTPS upstreams")
113 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")
115 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")
116+ flagSet.Duration("upstream-timeout", DefaultUpstreamTimeout, "maximum amount of time the server will wait for a response from the upstream")
114
117
115 return flagSet
118 return flagSet
116}
119}
@@ -129,6 +132,7 @@ func (l *LegacyUpstreams) convert() (UpstreamConfig, error) {
129 }
132 }
130
133
131 flushInterval := Duration(l.FlushInterval)
134 flushInterval := Duration(l.FlushInterval)
135+ timeout := Duration(l.Timeout)
132 upstream := Upstream{
136 upstream := Upstream{
133 ID: u.Path,
137 ID: u.Path,
134 Path: u.Path,
138 Path: u.Path,
@@ -137,6 +141,7 @@ func (l *LegacyUpstreams) convert() (UpstreamConfig, error) {
137 PassHostHeader: &l.PassHostHeader,
141 PassHostHeader: &l.PassHostHeader,
138 ProxyWebSockets: &l.ProxyWebSockets,
142 ProxyWebSockets: &l.ProxyWebSockets,
139 FlushInterval: &flushInterval,
143 FlushInterval: &flushInterval,
144+ Timeout: &timeout,
140 }
145 }
141
146
142 switch u.Scheme {
147 switch u.Scheme {
@@ -168,6 +173,7 @@ func (l *LegacyUpstreams) convert() (UpstreamConfig, error) {
168 upstream.PassHostHeader = nil
173 upstream.PassHostHeader = nil
169 upstream.ProxyWebSockets = nil
174 upstream.ProxyWebSockets = nil
170 upstream.FlushInterval = nil
175 upstream.FlushInterval = nil
176+ upstream.Timeout = nil
171 }
177 }
172
178
173 upstreams.Upstreams = append(upstreams.Upstreams, upstream)
179 upstreams.Upstreams = append(upstreams.Upstreams, upstream)
pkg/apis/options/legacy_options_test.go+12
@@ -17,7 +17,9 @@ var _ = Describe("Legacy Options", func() {
17
17
18 // Set upstreams and related options to test their conversion
18 // Set upstreams and related options to test their conversion
19 flushInterval := Duration(5 * time.Second)
19 flushInterval := Duration(5 * time.Second)
20+ timeout := Duration(5 * time.Second)
20 legacyOpts.LegacyUpstreams.FlushInterval = time.Duration(flushInterval)
21 legacyOpts.LegacyUpstreams.FlushInterval = time.Duration(flushInterval)
22+ legacyOpts.LegacyUpstreams.Timeout = time.Duration(timeout)
21 legacyOpts.LegacyUpstreams.PassHostHeader = true
23 legacyOpts.LegacyUpstreams.PassHostHeader = true
22 legacyOpts.LegacyUpstreams.ProxyWebSockets = true
24 legacyOpts.LegacyUpstreams.ProxyWebSockets = true
23 legacyOpts.LegacyUpstreams.SSLUpstreamInsecureSkipVerify = true
25 legacyOpts.LegacyUpstreams.SSLUpstreamInsecureSkipVerify = true
@@ -36,6 +38,7 @@ var _ = Describe("Legacy Options", func() {
36 InsecureSkipTLSVerify: true,
38 InsecureSkipTLSVerify: true,
37 PassHostHeader: &truth,
39 PassHostHeader: &truth,
38 ProxyWebSockets: &truth,
40 ProxyWebSockets: &truth,
41+ Timeout: &timeout,
39 },
42 },
40 {
43 {
41 ID: "/bar",
44 ID: "/bar",
@@ -45,6 +48,7 @@ var _ = Describe("Legacy Options", func() {
45 InsecureSkipTLSVerify: true,
48 InsecureSkipTLSVerify: true,
46 PassHostHeader: &truth,
49 PassHostHeader: &truth,
47 ProxyWebSockets: &truth,
50 ProxyWebSockets: &truth,
51+ Timeout: &timeout,
48 },
52 },
49 {
53 {
50 ID: "static://204",
54 ID: "static://204",
@@ -56,6 +60,7 @@ var _ = Describe("Legacy Options", func() {
56 InsecureSkipTLSVerify: false,
60 InsecureSkipTLSVerify: false,
57 PassHostHeader: nil,
61 PassHostHeader: nil,
58 ProxyWebSockets: nil,
62 ProxyWebSockets: nil,
63+ Timeout: nil,
59 },
64 },
60 },
65 },
61 }
66 }
@@ -140,6 +145,7 @@ var _ = Describe("Legacy Options", func() {
140 passHostHeader := false
145 passHostHeader := false
141 proxyWebSockets := true
146 proxyWebSockets := true
142 flushInterval := Duration(5 * time.Second)
147 flushInterval := Duration(5 * time.Second)
148+ timeout := Duration(5 * time.Second)
143
149
144 // Test cases and expected outcomes
150 // Test cases and expected outcomes
145 validHTTP := "http://foo.bar/baz"
151 validHTTP := "http://foo.bar/baz"
@@ -151,6 +157,7 @@ var _ = Describe("Legacy Options", func() {
151 PassHostHeader: &passHostHeader,
157 PassHostHeader: &passHostHeader,
152 ProxyWebSockets: &proxyWebSockets,
158 ProxyWebSockets: &proxyWebSockets,
153 FlushInterval: &flushInterval,
159 FlushInterval: &flushInterval,
160+ Timeout: &timeout,
154 }
161 }
155
162
156 // Test cases and expected outcomes
163 // Test cases and expected outcomes
@@ -163,6 +170,7 @@ var _ = Describe("Legacy Options", func() {
163 PassHostHeader: &passHostHeader,
170 PassHostHeader: &passHostHeader,
164 ProxyWebSockets: &proxyWebSockets,
171 ProxyWebSockets: &proxyWebSockets,
165 FlushInterval: &flushInterval,
172 FlushInterval: &flushInterval,
173+ Timeout: &timeout,
166 }
174 }
167
175
168 validFileWithFragment := "file:///var/lib/website#/bar"
176 validFileWithFragment := "file:///var/lib/website#/bar"
@@ -174,6 +182,7 @@ var _ = Describe("Legacy Options", func() {
174 PassHostHeader: &passHostHeader,
182 PassHostHeader: &passHostHeader,
175 ProxyWebSockets: &proxyWebSockets,
183 ProxyWebSockets: &proxyWebSockets,
176 FlushInterval: &flushInterval,
184 FlushInterval: &flushInterval,
185+ Timeout: &timeout,
177 }
186 }
178
187
179 validStatic := "static://204"
188 validStatic := "static://204"
@@ -188,6 +197,7 @@ var _ = Describe("Legacy Options", func() {
188 PassHostHeader: nil,
197 PassHostHeader: nil,
189 ProxyWebSockets: nil,
198 ProxyWebSockets: nil,
190 FlushInterval: nil,
199 FlushInterval: nil,
200+ Timeout: nil,
191 }
201 }
192
202
193 invalidStatic := "static://abc"
203 invalidStatic := "static://abc"
@@ -202,6 +212,7 @@ var _ = Describe("Legacy Options", func() {
202 PassHostHeader: nil,
212 PassHostHeader: nil,
203 ProxyWebSockets: nil,
213 ProxyWebSockets: nil,
204 FlushInterval: nil,
214 FlushInterval: nil,
215+ Timeout: nil,
205 }
216 }
206
217
207 invalidHTTP := ":foo"
218 invalidHTTP := ":foo"
@@ -215,6 +226,7 @@ var _ = Describe("Legacy Options", func() {
215 PassHostHeader: passHostHeader,
226 PassHostHeader: passHostHeader,
216 ProxyWebSockets: proxyWebSockets,
227 ProxyWebSockets: proxyWebSockets,
217 FlushInterval: time.Duration(flushInterval),
228 FlushInterval: time.Duration(flushInterval),
229+ Timeout: time.Duration(timeout),
218 }
230 }
219
231
220 upstreams, err := legacyUpstreams.convert()
232 upstreams, err := legacyUpstreams.convert()
pkg/apis/options/load_test.go+1
@@ -22,6 +22,7 @@ var _ = Describe("Load", func() {
22 PassHostHeader: true,
22 PassHostHeader: true,
23 ProxyWebSockets: true,
23 ProxyWebSockets: true,
24 FlushInterval: DefaultUpstreamFlushInterval,
24 FlushInterval: DefaultUpstreamFlushInterval,
25+ Timeout: DefaultUpstreamTimeout,
25 },
26 },
26
27
27 LegacyHeaders: LegacyHeaders{
28 LegacyHeaders: LegacyHeaders{
pkg/apis/options/upstreams.go+7
@@ -5,6 +5,9 @@ import "time"
5const (
5const (
6 // DefaultUpstreamFlushInterval is the default value for the Upstream FlushInterval.
6 // DefaultUpstreamFlushInterval is the default value for the Upstream FlushInterval.
7 DefaultUpstreamFlushInterval = 1 * time.Second
7 DefaultUpstreamFlushInterval = 1 * time.Second
8+
9+ // DefaultUpstreamTimeout is the maximum duration a network dial to a upstream server for a response.
10+ DefaultUpstreamTimeout = 30 * time.Second
8)
11)
9
12
10// UpstreamConfig is a collection of definitions for upstream servers.
13// UpstreamConfig is a collection of definitions for upstream servers.
@@ -84,4 +87,8 @@ type Upstream struct {
84 // ProxyWebSockets enables proxying of websockets to upstream servers
87 // ProxyWebSockets enables proxying of websockets to upstream servers
85 // Defaults to true.
88 // Defaults to true.
86 ProxyWebSockets *bool `json:"proxyWebSockets,omitempty"`
89 ProxyWebSockets *bool `json:"proxyWebSockets,omitempty"`
90+
91+ // Timeout is the maximum duration the server will wait for a response from the upstream server.
92+ // Defaults to 30 seconds.
93+ Timeout *Duration `json:"timeout,omitempty"`
87}
94}
pkg/upstream/http.go+22−7
@@ -1,7 +1,6 @@
1package upstream
1package upstream
2
2
3import (
3import (
4− "crypto/tls"
5 "net/http"
4 "net/http"
6 "net/http/httputil"
5 "net/http/httputil"
7 "net/url"
6 "net/url"
@@ -100,6 +99,14 @@ func (h *httpUpstreamProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request)
100func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler ProxyErrorHandler) http.Handler {
99func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler ProxyErrorHandler) http.Handler {
101 proxy := httputil.NewSingleHostReverseProxy(target)
100 proxy := httputil.NewSingleHostReverseProxy(target)
102
101
102+ // Inherit default transport options from Go's stdlib
103+ transport := http.DefaultTransport.(*http.Transport).Clone()
104+
105+ // Change default duration for waiting for an upstream response
106+ if upstream.Timeout != nil {
107+ transport.ResponseHeaderTimeout = upstream.Timeout.Duration()
108+ }
109+
103 // Configure options on the SingleHostReverseProxy
110 // Configure options on the SingleHostReverseProxy
104 if upstream.FlushInterval != nil {
111 if upstream.FlushInterval != nil {
105 proxy.FlushInterval = upstream.FlushInterval.Duration()
112 proxy.FlushInterval = upstream.FlushInterval.Duration()
@@ -110,9 +117,7 @@ func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler Pr
110 // InsecureSkipVerify is a configurable option we allow
117 // InsecureSkipVerify is a configurable option we allow
111 /* #nosec G402 */
118 /* #nosec G402 */
112 if upstream.InsecureSkipTLSVerify {
119 if upstream.InsecureSkipTLSVerify {
113− proxy.Transport = &http.Transport{
114− TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
120+ transport.TLSClientConfig.InsecureSkipVerify = true
115− }
116 }
121 }
117
122
118 // Ensure we always pass the original request path
123 // Ensure we always pass the original request path
@@ -127,6 +132,10 @@ func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler Pr
127 if errorHandler != nil {
132 if errorHandler != nil {
128 proxy.ErrorHandler = errorHandler
133 proxy.ErrorHandler = errorHandler
129 }
134 }
135+
136+ // Apply the customized transport to our proxy before returning it
137+ proxy.Transport = transport
138+
130 return proxy
139 return proxy
131}
140}
132
141
@@ -156,11 +165,17 @@ func setProxyDirector(proxy *httputil.ReverseProxy) {
156// newWebSocketReverseProxy creates a new reverse proxy for proxying websocket connections.
165// newWebSocketReverseProxy creates a new reverse proxy for proxying websocket connections.
157func newWebSocketReverseProxy(u *url.URL, skipTLSVerify bool) http.Handler {
166func newWebSocketReverseProxy(u *url.URL, skipTLSVerify bool) http.Handler {
158 wsProxy := httputil.NewSingleHostReverseProxy(u)
167 wsProxy := httputil.NewSingleHostReverseProxy(u)
168+
169+ // Inherit default transport options from Go's stdlib
170+ transport := http.DefaultTransport.(*http.Transport).Clone()
171+
159 /* #nosec G402 */
172 /* #nosec G402 */
160 if skipTLSVerify {
173 if skipTLSVerify {
161− wsProxy.Transport = &http.Transport{
162− TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
174+ transport.TLSClientConfig.InsecureSkipVerify = true
163− }
164 }
175 }
176+
177+ // Apply the customized transport to our proxy before returning it
178+ wsProxy.Transport = transport
179+
165 return wsProxy
180 return wsProxy
166}
181}
pkg/upstream/http_test.go+33−14
@@ -3,7 +3,6 @@ package upstream
3import (
3import (
4 "bytes"
4 "bytes"
5 "crypto"
5 "crypto"
6− "crypto/tls"
7 "encoding/json"
6 "encoding/json"
8 "fmt"
7 "fmt"
9 "net/http"
8 "net/http"
@@ -23,9 +22,8 @@ import (
23)
22)
24
23
25var _ = Describe("HTTP Upstream Suite", func() {
24var _ = Describe("HTTP Upstream Suite", func() {
26−
25+ defaultFlushInterval := options.Duration(options.DefaultUpstreamFlushInterval)
27− const flushInterval5s = options.Duration(5 * time.Second)
26+ defaultTimeout := options.Duration(options.DefaultUpstreamTimeout)
28− const flushInterval1s = options.Duration(1 * time.Second)
29 truth := true
27 truth := true
30 falsum := false
28 falsum := false
31
29
@@ -62,12 +60,15 @@ var _ = Describe("HTTP Upstream Suite", func() {
62
60
63 flush := options.Duration(1 * time.Second)
61 flush := options.Duration(1 * time.Second)
64
62
63+ timeout := options.Duration(options.DefaultUpstreamTimeout)
64+
65 upstream := options.Upstream{
65 upstream := options.Upstream{
66 ID: in.id,
66 ID: in.id,
67 PassHostHeader: &in.passUpstreamHostHeader,
67 PassHostHeader: &in.passUpstreamHostHeader,
68 ProxyWebSockets: &falsum,
68 ProxyWebSockets: &falsum,
69 InsecureSkipTLSVerify: false,
69 InsecureSkipTLSVerify: false,
70 FlushInterval: &flush,
70 FlushInterval: &flush,
71+ Timeout: &timeout,
71 }
72 }
72
73
73 Expect(in.serverAddr).ToNot(BeNil())
74 Expect(in.serverAddr).ToNot(BeNil())
@@ -318,13 +319,13 @@ var _ = Describe("HTTP Upstream Suite", func() {
318 req = middlewareapi.AddRequestScope(req, &middlewareapi.RequestScope{})
319 req = middlewareapi.AddRequestScope(req, &middlewareapi.RequestScope{})
319 rw := httptest.NewRecorder()
320 rw := httptest.NewRecorder()
320
321
321− flush := options.Duration(1 * time.Second)
322 upstream := options.Upstream{
322 upstream := options.Upstream{
323 ID: "noPassHost",
323 ID: "noPassHost",
324 PassHostHeader: &falsum,
324 PassHostHeader: &falsum,
325 ProxyWebSockets: &falsum,
325 ProxyWebSockets: &falsum,
326 InsecureSkipTLSVerify: false,
326 InsecureSkipTLSVerify: false,
327− FlushInterval: &flush,
327+ FlushInterval: &defaultFlushInterval,
328+ Timeout: &defaultTimeout,
328 }
329 }
329
330
330 u, err := url.Parse(serverAddr)
331 u, err := url.Parse(serverAddr)
@@ -354,6 +355,7 @@ var _ = Describe("HTTP Upstream Suite", func() {
354 skipVerify bool
355 skipVerify bool
355 sigData *options.SignatureData
356 sigData *options.SignatureData
356 errorHandler func(http.ResponseWriter, *http.Request, error)
357 errorHandler func(http.ResponseWriter, *http.Request, error)
358+ timeout options.Duration
357 }
359 }
358
360
359 DescribeTable("newHTTPUpstreamProxy",
361 DescribeTable("newHTTPUpstreamProxy",
@@ -366,6 +368,7 @@ var _ = Describe("HTTP Upstream Suite", func() {
366 FlushInterval: &in.flushInterval,
368 FlushInterval: &in.flushInterval,
367 InsecureSkipTLSVerify: in.skipVerify,
369 InsecureSkipTLSVerify: in.skipVerify,
368 ProxyWebSockets: &in.proxyWebSockets,
370 ProxyWebSockets: &in.proxyWebSockets,
371+ Timeout: &in.timeout,
369 }
372 }
370
373
371 handler := newHTTPUpstreamProxy(upstream, u, in.sigData, in.errorHandler)
374 handler := newHTTPUpstreamProxy(upstream, u, in.sigData, in.errorHandler)
@@ -380,49 +383,63 @@ var _ = Describe("HTTP Upstream Suite", func() {
380 proxy, ok := upstreamProxy.handler.(*httputil.ReverseProxy)
383 proxy, ok := upstreamProxy.handler.(*httputil.ReverseProxy)
381 Expect(ok).To(BeTrue())
384 Expect(ok).To(BeTrue())
382 Expect(proxy.FlushInterval).To(Equal(in.flushInterval.Duration()))
385 Expect(proxy.FlushInterval).To(Equal(in.flushInterval.Duration()))
386+ transport, ok := proxy.Transport.(*http.Transport)
387+ Expect(ok).To(BeTrue())
388+ Expect(transport.ResponseHeaderTimeout).To(Equal(in.timeout.Duration()))
383 Expect(proxy.ErrorHandler != nil).To(Equal(in.errorHandler != nil))
389 Expect(proxy.ErrorHandler != nil).To(Equal(in.errorHandler != nil))
384 if in.skipVerify {
390 if in.skipVerify {
385− Expect(proxy.Transport).To(Equal(&http.Transport{
391+ Expect(transport.TLSClientConfig.InsecureSkipVerify).To(Equal(true))
386− TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
387− }))
388 }
392 }
389 },
393 },
390 Entry("with proxy websockets", &newUpstreamTableInput{
394 Entry("with proxy websockets", &newUpstreamTableInput{
391 proxyWebSockets: true,
395 proxyWebSockets: true,
392− flushInterval: flushInterval1s,
396+ flushInterval: defaultFlushInterval,
393 skipVerify: false,
397 skipVerify: false,
394 sigData: nil,
398 sigData: nil,
395 errorHandler: nil,
399 errorHandler: nil,
400+ timeout: defaultTimeout,
396 }),
401 }),
397 Entry("with a non standard flush interval", &newUpstreamTableInput{
402 Entry("with a non standard flush interval", &newUpstreamTableInput{
398 proxyWebSockets: false,
403 proxyWebSockets: false,
399− flushInterval: flushInterval5s,
404+ flushInterval: options.Duration(5 * time.Second),
400 skipVerify: false,
405 skipVerify: false,
401 sigData: nil,
406 sigData: nil,
402 errorHandler: nil,
407 errorHandler: nil,
408+ timeout: defaultTimeout,
403 }),
409 }),
404 Entry("with a InsecureSkipTLSVerify", &newUpstreamTableInput{
410 Entry("with a InsecureSkipTLSVerify", &newUpstreamTableInput{
405 proxyWebSockets: false,
411 proxyWebSockets: false,
406− flushInterval: flushInterval1s,
412+ flushInterval: defaultFlushInterval,
407 skipVerify: true,
413 skipVerify: true,
408 sigData: nil,
414 sigData: nil,
409 errorHandler: nil,
415 errorHandler: nil,
416+ timeout: defaultTimeout,
410 }),
417 }),
411 Entry("with a SignatureData", &newUpstreamTableInput{
418 Entry("with a SignatureData", &newUpstreamTableInput{
412 proxyWebSockets: false,
419 proxyWebSockets: false,
413− flushInterval: flushInterval1s,
420+ flushInterval: defaultFlushInterval,
414 skipVerify: false,
421 skipVerify: false,
415 sigData: &options.SignatureData{Hash: crypto.SHA256, Key: "secret"},
422 sigData: &options.SignatureData{Hash: crypto.SHA256, Key: "secret"},
416 errorHandler: nil,
423 errorHandler: nil,
424+ timeout: defaultTimeout,
417 }),
425 }),
418 Entry("with an error handler", &newUpstreamTableInput{
426 Entry("with an error handler", &newUpstreamTableInput{
419 proxyWebSockets: false,
427 proxyWebSockets: false,
420− flushInterval: flushInterval1s,
428+ flushInterval: defaultFlushInterval,
421 skipVerify: false,
429 skipVerify: false,
422 sigData: nil,
430 sigData: nil,
423 errorHandler: func(rw http.ResponseWriter, req *http.Request, arg3 error) {
431 errorHandler: func(rw http.ResponseWriter, req *http.Request, arg3 error) {
424 rw.WriteHeader(502)
432 rw.WriteHeader(502)
425 },
433 },
434+ timeout: defaultTimeout,
435+ }),
436+ Entry("with a non-default timeout", &newUpstreamTableInput{
437+ proxyWebSockets: false,
438+ flushInterval: defaultFlushInterval,
439+ skipVerify: false,
440+ sigData: nil,
441+ errorHandler: nil,
442+ timeout: options.Duration(5 * time.Second),
426 }),
443 }),
427 )
444 )
428
445
@@ -431,12 +448,14 @@ var _ = Describe("HTTP Upstream Suite", func() {
431
448
432 BeforeEach(func() {
449 BeforeEach(func() {
433 flush := options.Duration(1 * time.Second)
450 flush := options.Duration(1 * time.Second)
451+ timeout := options.Duration(options.DefaultUpstreamTimeout)
434 upstream := options.Upstream{
452 upstream := options.Upstream{
435 ID: "websocketProxy",
453 ID: "websocketProxy",
436 PassHostHeader: &truth,
454 PassHostHeader: &truth,
437 ProxyWebSockets: &truth,
455 ProxyWebSockets: &truth,
438 InsecureSkipTLSVerify: false,
456 InsecureSkipTLSVerify: false,
439 FlushInterval: &flush,
457 FlushInterval: &flush,
458+ Timeout: &timeout,
440 }
459 }
441
460
442 u, err := url.Parse(serverAddr)
461 u, err := url.Parse(serverAddr)