Commit 803f74c2 authored by Pawel Chojnacki's avatar Pawel Chojnacki

Do not follow when doing api calls.

+ small code cleanup

Make redirects work in go < 1.7
parent e7a352d9
...@@ -81,6 +81,18 @@ func TestPreAuthorizeContentTypeFailure(t *testing.T) { ...@@ -81,6 +81,18 @@ func TestPreAuthorizeContentTypeFailure(t *testing.T) {
200, 200) 200, 200)
} }
func TestPreAuthorizeRedirect(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/", 301)
}))
defer ts.Close()
runPreAuthorizeHandler(t, ts, "/willredirect",
regexp.MustCompile(`/willredirect\z`),
"",
301, 301)
}
func TestPreAuthorizeJWT(t *testing.T) { func TestPreAuthorizeJWT(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token, err := jwt.Parse(r.Header.Get(api.RequestHeader), func(token *jwt.Token) (interface{}, error) { token, err := jwt.Parse(r.Header.Get(api.RequestHeader), func(token *jwt.Token) (interface{}, error) {
......
...@@ -132,7 +132,6 @@ func (api *API) newRequest(r *http.Request, body io.Reader, suffix string) (*htt ...@@ -132,7 +132,6 @@ func (api *API) newRequest(r *http.Request, body io.Reader, suffix string) (*htt
if body != nil { if body != nil {
authReq.Body = ioutil.NopCloser(body) authReq.Body = ioutil.NopCloser(body)
} }
// Clean some headers when issuing a new request without body // Clean some headers when issuing a new request without body
if body == nil { if body == nil {
authReq.Header.Del("Content-Type") authReq.Header.Del("Content-Type")
...@@ -186,7 +185,8 @@ func (api *API) PreAuthorize(suffix string, r *http.Request) (httpResponse *http ...@@ -186,7 +185,8 @@ func (api *API) PreAuthorize(suffix string, r *http.Request) (httpResponse *http
return nil, nil, fmt.Errorf("preAuthorizeHandler newUpstreamRequest: %v", err) return nil, nil, fmt.Errorf("preAuthorizeHandler newUpstreamRequest: %v", err)
} }
httpResponse, err = api.Client.Do(authReq) httpResponse, err = api.clientDoNotFollowRedirects(authReq)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("preAuthorizeHandler: do request: %v", err) return nil, nil, fmt.Errorf("preAuthorizeHandler: do request: %v", err)
} }
...@@ -230,48 +230,62 @@ func (api *API) PreAuthorizeHandler(next HandleFunc, suffix string) http.Handler ...@@ -230,48 +230,62 @@ func (api *API) PreAuthorizeHandler(next HandleFunc, suffix string) http.Handler
// The response couldn't be interpreted as a valid auth response, so // The response couldn't be interpreted as a valid auth response, so
// pass it back (mostly) unmodified // pass it back (mostly) unmodified
if httpResponse != nil && authResponse == nil { if httpResponse != nil && authResponse == nil {
// NGINX response buffering is disabled on this path (with passResponseBack(httpResponse, w, r)
// X-Accel-Buffering: no) but we still want to free up the Unicorn worker
// that generated httpResponse as fast as possible. To do this we buffer
// the entire response body in memory before sending it on.
responseBody, err := bufferResponse(httpResponse.Body)
if err != nil {
helper.Fail500(w, r, err)
}
httpResponse.Body.Close() // Free up the Unicorn worker
bytesTotal.Add(float64(responseBody.Len()))
for k, v := range httpResponse.Header {
// Accomodate broken clients that do case-sensitive header lookup
if k == "Www-Authenticate" {
w.Header()["WWW-Authenticate"] = v
} else {
w.Header()[k] = v
}
}
w.WriteHeader(httpResponse.StatusCode)
if _, err := io.Copy(w, responseBody); err != nil {
helper.LogError(r, err)
}
return return
} }
httpResponse.Body.Close() // Free up the Unicorn worker httpResponse.Body.Close() // Free up the Unicorn worker
// Negotiate authentication (Kerberos) may need to return a WWW-Authenticate copyAuthHeader(httpResponse, w)
// header to the client even in case of success as per RFC4559.
for k, v := range httpResponse.Header {
// Case-insensitive comparison as per RFC7230
if strings.EqualFold(k, "WWW-Authenticate") {
w.Header()[k] = v
}
}
next(w, r, authResponse) next(w, r, authResponse)
}) })
} }
// go < 1.7 compatibility
func (api *API) clientDoNotFollowRedirects(authReq *http.Request) (*http.Response, error) {
return api.Client.Transport.RoundTrip(authReq)
}
func copyAuthHeader(httpResponse *http.Response, w http.ResponseWriter) {
// Negotiate authentication (Kerberos) may need to return a WWW-Authenticate
// header to the client even in case of success as per RFC4559.
for k, v := range httpResponse.Header {
// Case-insensitive comparison as per RFC7230
if strings.EqualFold(k, "WWW-Authenticate") {
w.Header()[k] = v
}
}
}
func passResponseBack(httpResponse *http.Response, w http.ResponseWriter, r *http.Request) {
// NGINX response buffering is disabled on this path (with
// X-Accel-Buffering: no) but we still want to free up the Unicorn worker
// that generated httpResponse as fast as possible. To do this we buffer
// the entire response body in memory before sending it on.
responseBody, err := bufferResponse(httpResponse.Body)
if err != nil {
helper.Fail500(w, r, err)
return
}
httpResponse.Body.Close() // Free up the Unicorn worker
bytesTotal.Add(float64(responseBody.Len()))
for k, v := range httpResponse.Header {
// Accomodate broken clients that do case-sensitive header lookup
if k == "Www-Authenticate" {
w.Header()["WWW-Authenticate"] = v
} else {
w.Header()[k] = v
}
}
w.WriteHeader(httpResponse.StatusCode)
if _, err := io.Copy(w, responseBody); err != nil {
helper.LogError(r, err)
}
}
func bufferResponse(r io.Reader) (*bytes.Buffer, error) { func bufferResponse(r io.Reader) (*bytes.Buffer, error) {
responseBody := &bytes.Buffer{} responseBody := &bytes.Buffer{}
n, err := io.Copy(responseBody, io.LimitReader(r, failureResponseLimit)) n, err := io.Copy(responseBody, io.LimitReader(r, failureResponseLimit))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment