From 01c5f5ae3b9bb7608540d363df8925cf12292356 Mon Sep 17 00:00:00 2001 From: Steve Arch Date: Thu, 31 Jan 2019 14:02:15 +0000 Subject: [PATCH] Implemented flushing interval (#23) * Implemented flushing interval When proxying streaming responses, it would not flush the response writer buffer until some seemingly random point (maybe the number of bytes?). This makes it flush every 1 second by default, but with a configurable interval. * flushing CHANGELOG * gofmt and goimports --- CHANGELOG.md | 2 ++ logging_handler.go | 6 ++++++ main.go | 1 + oauthproxy.go | 8 +++++--- oauthproxy_test.go | 4 ++-- options.go | 27 ++++++++++++++------------- 6 files changed, 30 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51086d0..07fffcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ - [#21](https://github.com/pusher/oauth2_proxy/pull/21) Docker Improvement (@yaegashi) - Move Docker base image from debian to alpine - Install ca-certificates in docker image +- [#23](https://github.com/pusher/oauth2_proxy/pull/21) Flushed streaming responses + - Long-running upstream responses will get flushed every (1 second by default) - [#24](https://github.com/pusher/oauth2_proxy/pull/24) Redirect fix (@agentgonzo) - After a successful login, you will be redirected to your original URL rather than / diff --git a/logging_handler.go b/logging_handler.go index 8db2d3e..d47ae58 100644 --- a/logging_handler.go +++ b/logging_handler.go @@ -76,6 +76,12 @@ func (l *responseLogger) Size() int { return l.size } +func (l *responseLogger) Flush() { + if flusher, ok := l.w.(http.Flusher); ok { + flusher.Flush() + } +} + // logMessageData is the container for all values that are available as variables in the request logging format. // All values are pre-formatted strings so it is easy to use them in the format string. type logMessageData struct { diff --git a/main.go b/main.go index 5cfa1f5..ceb7aad 100644 --- a/main.go +++ b/main.go @@ -43,6 +43,7 @@ func main() { flagSet.Bool("skip-provider-button", false, "will skip sign-in-page to directly reach the next step: oauth/start") flagSet.Bool("skip-auth-preflight", false, "will skip authentication for OPTIONS requests") flagSet.Bool("ssl-insecure-skip-verify", false, "skip validation of certificates presented when using HTTPS") + flagSet.Duration("flush-interval", time.Duration(1)*time.Second, "period between response flushing when streaming responses") flagSet.Var(&emailDomains, "email-domain", "authenticate emails with the specified domain (may be given multiple times). Use * to authenticate any email") flagSet.String("azure-tenant", "common", "go to a tenant-specific or common (tenant-independent) endpoint.") diff --git a/oauthproxy.go b/oauthproxy.go index 64503cb..971005d 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -110,8 +110,10 @@ func (u *UpstreamProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { // NewReverseProxy creates a new reverse proxy for proxying requests to upstream // servers -func NewReverseProxy(target *url.URL) (proxy *httputil.ReverseProxy) { - return httputil.NewSingleHostReverseProxy(target) +func NewReverseProxy(target *url.URL, flushInterval time.Duration) (proxy *httputil.ReverseProxy) { + proxy = httputil.NewSingleHostReverseProxy(target) + proxy.FlushInterval = flushInterval + return proxy } func setProxyUpstreamHostHeader(proxy *httputil.ReverseProxy, target *url.URL) { @@ -154,7 +156,7 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy { case httpScheme, httpsScheme: u.Path = "" log.Printf("mapping path %q => upstream %q", path, u) - proxy := NewReverseProxy(u) + proxy := NewReverseProxy(u, opts.FlushInterval) if !opts.PassHostHeader { setProxyUpstreamHostHeader(proxy, u) } else { diff --git a/oauthproxy_test.go b/oauthproxy_test.go index adc3cfb..a5dd545 100644 --- a/oauthproxy_test.go +++ b/oauthproxy_test.go @@ -39,7 +39,7 @@ func TestNewReverseProxy(t *testing.T) { backendHost := net.JoinHostPort(backendHostname, backendPort) proxyURL, _ := url.Parse(backendURL.Scheme + "://" + backendHost + "/") - proxyHandler := NewReverseProxy(proxyURL) + proxyHandler := NewReverseProxy(proxyURL, time.Second) setProxyUpstreamHostHeader(proxyHandler, proxyURL) frontend := httptest.NewServer(proxyHandler) defer frontend.Close() @@ -61,7 +61,7 @@ func TestEncodedSlashes(t *testing.T) { defer backend.Close() b, _ := url.Parse(backend.URL) - proxyHandler := NewReverseProxy(b) + proxyHandler := NewReverseProxy(b, time.Second) setProxyDirector(proxyHandler) frontend := httptest.NewServer(proxyHandler) defer frontend.Close() diff --git a/options.go b/options.go index 9696144..62464e0 100644 --- a/options.go +++ b/options.go @@ -51,19 +51,20 @@ type Options struct { CookieSecure bool `flag:"cookie-secure" cfg:"cookie_secure"` CookieHTTPOnly bool `flag:"cookie-httponly" cfg:"cookie_httponly"` - Upstreams []string `flag:"upstream" cfg:"upstreams"` - SkipAuthRegex []string `flag:"skip-auth-regex" cfg:"skip_auth_regex"` - PassBasicAuth bool `flag:"pass-basic-auth" cfg:"pass_basic_auth"` - BasicAuthPassword string `flag:"basic-auth-password" cfg:"basic_auth_password"` - PassAccessToken bool `flag:"pass-access-token" cfg:"pass_access_token"` - PassHostHeader bool `flag:"pass-host-header" cfg:"pass_host_header"` - SkipProviderButton bool `flag:"skip-provider-button" cfg:"skip_provider_button"` - PassUserHeaders bool `flag:"pass-user-headers" cfg:"pass_user_headers"` - SSLInsecureSkipVerify bool `flag:"ssl-insecure-skip-verify" cfg:"ssl_insecure_skip_verify"` - SetXAuthRequest bool `flag:"set-xauthrequest" cfg:"set_xauthrequest"` - SetAuthorization bool `flag:"set-authorization-header" cfg:"set_authorization_header"` - PassAuthorization bool `flag:"pass-authorization-header" cfg:"pass_authorization_header"` - SkipAuthPreflight bool `flag:"skip-auth-preflight" cfg:"skip_auth_preflight"` + Upstreams []string `flag:"upstream" cfg:"upstreams"` + SkipAuthRegex []string `flag:"skip-auth-regex" cfg:"skip_auth_regex"` + PassBasicAuth bool `flag:"pass-basic-auth" cfg:"pass_basic_auth"` + BasicAuthPassword string `flag:"basic-auth-password" cfg:"basic_auth_password"` + PassAccessToken bool `flag:"pass-access-token" cfg:"pass_access_token"` + PassHostHeader bool `flag:"pass-host-header" cfg:"pass_host_header"` + SkipProviderButton bool `flag:"skip-provider-button" cfg:"skip_provider_button"` + PassUserHeaders bool `flag:"pass-user-headers" cfg:"pass_user_headers"` + SSLInsecureSkipVerify bool `flag:"ssl-insecure-skip-verify" cfg:"ssl_insecure_skip_verify"` + SetXAuthRequest bool `flag:"set-xauthrequest" cfg:"set_xauthrequest"` + SetAuthorization bool `flag:"set-authorization-header" cfg:"set_authorization_header"` + PassAuthorization bool `flag:"pass-authorization-header" cfg:"pass_authorization_header"` + SkipAuthPreflight bool `flag:"skip-auth-preflight" cfg:"skip_auth_preflight"` + FlushInterval time.Duration `flag:"flush-interval" cfg:"flush_interval"` // These options allow for other providers besides Google, with // potential overrides.