Commit 51fde888 authored by Darren Shepherd's avatar Darren Shepherd

Check for 101 response before starting copy loop

If a 101 response is not seen the connection has not been fully upgraded. In this situation do not start the copy loop and only write the HTTP response and exit.
parent ede5f39a
...@@ -264,7 +264,7 @@ func (s *SpdyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) ...@@ -264,7 +264,7 @@ func (s *SpdyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)
) )
if s.followRedirects { if s.followRedirects {
conn, rawResponse, err = utilnet.ConnectWithRedirects(req.Method, req.URL, header, req.Body, s, s.requireSameHostRedirects) conn, rawResponse, _, err = utilnet.ConnectWithRedirects(true, req.Method, req.URL, header, req.Body, s, s.requireSameHostRedirects)
} else { } else {
clone := utilnet.CloneRequest(req) clone := utilnet.CloneRequest(req)
clone.Header = header clone.Header = header
......
...@@ -320,9 +320,9 @@ type Dialer interface { ...@@ -320,9 +320,9 @@ type Dialer interface {
} }
// ConnectWithRedirects uses dialer to send req, following up to 10 redirects (relative to // ConnectWithRedirects uses dialer to send req, following up to 10 redirects (relative to
// originalLocation). It returns the opened net.Conn and the raw response bytes. // originalLocation). It returns the opened net.Conn, the raw response bytes, and the raw response bytes.
// If requireSameHostRedirects is true, only redirects to the same host are permitted. // If requireSameHostRedirects is true, only redirects to the same host are permitted.
func ConnectWithRedirects(originalMethod string, originalLocation *url.URL, header http.Header, originalBody io.Reader, dialer Dialer, requireSameHostRedirects bool) (net.Conn, []byte, error) { func ConnectWithRedirects(processRedirect bool, originalMethod string, originalLocation *url.URL, header http.Header, originalBody io.Reader, dialer Dialer, requireSameHostRedirects bool) (net.Conn, []byte, int, error) {
const ( const (
maxRedirects = 9 // Fail on the 10th redirect maxRedirects = 9 // Fail on the 10th redirect
maxResponseSize = 16384 // play it safe to allow the potential for lots of / large headers maxResponseSize = 16384 // play it safe to allow the potential for lots of / large headers
...@@ -334,6 +334,7 @@ func ConnectWithRedirects(originalMethod string, originalLocation *url.URL, head ...@@ -334,6 +334,7 @@ func ConnectWithRedirects(originalMethod string, originalLocation *url.URL, head
intermediateConn net.Conn intermediateConn net.Conn
rawResponse = bytes.NewBuffer(make([]byte, 0, 256)) rawResponse = bytes.NewBuffer(make([]byte, 0, 256))
body = originalBody body = originalBody
respCode int
) )
defer func() { defer func() {
...@@ -345,19 +346,19 @@ func ConnectWithRedirects(originalMethod string, originalLocation *url.URL, head ...@@ -345,19 +346,19 @@ func ConnectWithRedirects(originalMethod string, originalLocation *url.URL, head
redirectLoop: redirectLoop:
for redirects := 0; ; redirects++ { for redirects := 0; ; redirects++ {
if redirects > maxRedirects { if redirects > maxRedirects {
return nil, nil, fmt.Errorf("too many redirects (%d)", redirects) return nil, nil, 0, fmt.Errorf("too many redirects (%d)", redirects)
} }
req, err := http.NewRequest(method, location.String(), body) req, err := http.NewRequest(method, location.String(), body)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, 0, err
} }
req.Header = header req.Header = header
intermediateConn, err = dialer.Dial(req) intermediateConn, err = dialer.Dial(req)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, 0, err
} }
// Peek at the backend response. // Peek at the backend response.
...@@ -372,6 +373,12 @@ redirectLoop: ...@@ -372,6 +373,12 @@ redirectLoop:
break redirectLoop break redirectLoop
} }
respCode = resp.StatusCode
if !processRedirect {
break redirectLoop
}
switch resp.StatusCode { switch resp.StatusCode {
case http.StatusFound: case http.StatusFound:
// Redirect, continue. // Redirect, continue.
...@@ -391,7 +398,7 @@ redirectLoop: ...@@ -391,7 +398,7 @@ redirectLoop:
// Prepare to follow the redirect. // Prepare to follow the redirect.
redirectStr := resp.Header.Get("Location") redirectStr := resp.Header.Get("Location")
if redirectStr == "" { if redirectStr == "" {
return nil, nil, fmt.Errorf("%d response missing Location header", resp.StatusCode) return nil, nil, 0, fmt.Errorf("%d response missing Location header", resp.StatusCode)
} }
// We have to parse relative to the current location, NOT originalLocation. For example, // We have to parse relative to the current location, NOT originalLocation. For example,
// if we request http://foo.com/a and get back "http://bar.com/b", the result should be // if we request http://foo.com/a and get back "http://bar.com/b", the result should be
...@@ -399,7 +406,7 @@ redirectLoop: ...@@ -399,7 +406,7 @@ redirectLoop:
// should be http://bar.com/c, not http://foo.com/c. // should be http://bar.com/c, not http://foo.com/c.
location, err = location.Parse(redirectStr) location, err = location.Parse(redirectStr)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("malformed Location header: %v", err) return nil, nil, 0, fmt.Errorf("malformed Location header: %v", err)
} }
// Only follow redirects to the same host. Otherwise, propagate the redirect response back. // Only follow redirects to the same host. Otherwise, propagate the redirect response back.
...@@ -414,7 +421,7 @@ redirectLoop: ...@@ -414,7 +421,7 @@ redirectLoop:
connToReturn := intermediateConn connToReturn := intermediateConn
intermediateConn = nil // Don't close the connection when we return it. intermediateConn = nil // Don't close the connection when we return it.
return connToReturn, rawResponse.Bytes(), nil return connToReturn, rawResponse.Bytes(), respCode, nil
} }
// CloneRequest creates a shallow copy of the request along with a deep copy of the Headers. // CloneRequest creates a shallow copy of the request along with a deep copy of the Headers.
......
...@@ -256,14 +256,8 @@ func (h *UpgradeAwareHandler) tryUpgrade(w http.ResponseWriter, req *http.Reques ...@@ -256,14 +256,8 @@ func (h *UpgradeAwareHandler) tryUpgrade(w http.ResponseWriter, req *http.Reques
// Only append X-Forwarded-For in the upgrade path, since httputil.NewSingleHostReverseProxy // Only append X-Forwarded-For in the upgrade path, since httputil.NewSingleHostReverseProxy
// handles this in the non-upgrade path. // handles this in the non-upgrade path.
utilnet.AppendForwardedForHeader(clone) utilnet.AppendForwardedForHeader(clone)
if h.InterceptRedirects { glog.V(6).Infof("Connecting to backend proxy (intercepting redirects) %s\n Headers: %v", &location, clone.Header)
glog.V(6).Infof("Connecting to backend proxy (intercepting redirects) %s\n Headers: %v", &location, clone.Header) backendConn, rawResponse, rawResponseCode, err := utilnet.ConnectWithRedirects(h.InterceptRedirects, req.Method, &location, clone.Header, req.Body, utilnet.DialerFunc(h.DialForUpgrade), h.RequireSameHostRedirects)
backendConn, rawResponse, err = utilnet.ConnectWithRedirects(req.Method, &location, clone.Header, req.Body, utilnet.DialerFunc(h.DialForUpgrade), h.RequireSameHostRedirects)
} else {
glog.V(6).Infof("Connecting to backend proxy (direct dial) %s\n Headers: %v", &location, clone.Header)
clone.URL = &location
backendConn, err = h.DialForUpgrade(clone)
}
if err != nil { if err != nil {
glog.V(6).Infof("Proxy connection error: %v", err) glog.V(6).Infof("Proxy connection error: %v", err)
h.Responder.Error(w, req, err) h.Responder.Error(w, req, err)
...@@ -295,6 +289,10 @@ func (h *UpgradeAwareHandler) tryUpgrade(w http.ResponseWriter, req *http.Reques ...@@ -295,6 +289,10 @@ func (h *UpgradeAwareHandler) tryUpgrade(w http.ResponseWriter, req *http.Reques
} }
} }
if rawResponseCode != http.StatusSwitchingProtocols {
return true
}
// Proxy the connection. This is bidirectional, so we need a goroutine // Proxy the connection. This is bidirectional, so we need a goroutine
// to copy in each direction. Once one side of the connection exits, we // to copy in each direction. Once one side of the connection exits, we
// exit the function which performs cleanup and in the process closes // exit the function which performs cleanup and in the process closes
......
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