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
This commit is contained in:
Steve Arch 2019-01-31 14:02:15 +00:00 committed by Joel Speed
parent 787d3da9d2
commit 01c5f5ae3b
6 changed files with 30 additions and 18 deletions

View File

@ -10,6 +10,8 @@
- [#21](https://github.com/pusher/oauth2_proxy/pull/21) Docker Improvement (@yaegashi) - [#21](https://github.com/pusher/oauth2_proxy/pull/21) Docker Improvement (@yaegashi)
- Move Docker base image from debian to alpine - Move Docker base image from debian to alpine
- Install ca-certificates in docker image - 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 <timeperiod> (1 second by default)
- [#24](https://github.com/pusher/oauth2_proxy/pull/24) Redirect fix (@agentgonzo) - [#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 / - After a successful login, you will be redirected to your original URL rather than /

View File

@ -76,6 +76,12 @@ func (l *responseLogger) Size() int {
return l.size 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. // 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. // All values are pre-formatted strings so it is easy to use them in the format string.
type logMessageData struct { type logMessageData struct {

View File

@ -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-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("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.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.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.") flagSet.String("azure-tenant", "common", "go to a tenant-specific or common (tenant-independent) endpoint.")

View File

@ -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 // NewReverseProxy creates a new reverse proxy for proxying requests to upstream
// servers // servers
func NewReverseProxy(target *url.URL) (proxy *httputil.ReverseProxy) { func NewReverseProxy(target *url.URL, flushInterval time.Duration) (proxy *httputil.ReverseProxy) {
return httputil.NewSingleHostReverseProxy(target) proxy = httputil.NewSingleHostReverseProxy(target)
proxy.FlushInterval = flushInterval
return proxy
} }
func setProxyUpstreamHostHeader(proxy *httputil.ReverseProxy, target *url.URL) { func setProxyUpstreamHostHeader(proxy *httputil.ReverseProxy, target *url.URL) {
@ -154,7 +156,7 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy {
case httpScheme, httpsScheme: case httpScheme, httpsScheme:
u.Path = "" u.Path = ""
log.Printf("mapping path %q => upstream %q", path, u) log.Printf("mapping path %q => upstream %q", path, u)
proxy := NewReverseProxy(u) proxy := NewReverseProxy(u, opts.FlushInterval)
if !opts.PassHostHeader { if !opts.PassHostHeader {
setProxyUpstreamHostHeader(proxy, u) setProxyUpstreamHostHeader(proxy, u)
} else { } else {

View File

@ -39,7 +39,7 @@ func TestNewReverseProxy(t *testing.T) {
backendHost := net.JoinHostPort(backendHostname, backendPort) backendHost := net.JoinHostPort(backendHostname, backendPort)
proxyURL, _ := url.Parse(backendURL.Scheme + "://" + backendHost + "/") proxyURL, _ := url.Parse(backendURL.Scheme + "://" + backendHost + "/")
proxyHandler := NewReverseProxy(proxyURL) proxyHandler := NewReverseProxy(proxyURL, time.Second)
setProxyUpstreamHostHeader(proxyHandler, proxyURL) setProxyUpstreamHostHeader(proxyHandler, proxyURL)
frontend := httptest.NewServer(proxyHandler) frontend := httptest.NewServer(proxyHandler)
defer frontend.Close() defer frontend.Close()
@ -61,7 +61,7 @@ func TestEncodedSlashes(t *testing.T) {
defer backend.Close() defer backend.Close()
b, _ := url.Parse(backend.URL) b, _ := url.Parse(backend.URL)
proxyHandler := NewReverseProxy(b) proxyHandler := NewReverseProxy(b, time.Second)
setProxyDirector(proxyHandler) setProxyDirector(proxyHandler)
frontend := httptest.NewServer(proxyHandler) frontend := httptest.NewServer(proxyHandler)
defer frontend.Close() defer frontend.Close()

View File

@ -64,6 +64,7 @@ type Options struct {
SetAuthorization bool `flag:"set-authorization-header" cfg:"set_authorization_header"` SetAuthorization bool `flag:"set-authorization-header" cfg:"set_authorization_header"`
PassAuthorization bool `flag:"pass-authorization-header" cfg:"pass_authorization_header"` PassAuthorization bool `flag:"pass-authorization-header" cfg:"pass_authorization_header"`
SkipAuthPreflight bool `flag:"skip-auth-preflight" cfg:"skip_auth_preflight"` 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 // These options allow for other providers besides Google, with
// potential overrides. // potential overrides.