// tryUpgrade returns true if the request was handled. func (r *ProxyHandler) tryUpgrade(w http.ResponseWriter, req, newReq *http.Request, location *url.URL, transport http.RoundTripper) bool { if !httpstream.IsUpgradeRequest(req) { return false } backendConn, err := proxyutil.DialURL(location, transport) if err != nil { status := errToAPIStatus(err) code := int(status.Code) writeJSON(code, r.codec, status, w, true) return true } defer backendConn.Close() // TODO should we use _ (a bufio.ReadWriter) instead of requestHijackedConn // when copying between the client and the backend? Docker doesn't when they // hijack, just for reference... requestHijackedConn, _, err := w.(http.Hijacker).Hijack() if err != nil { status := errToAPIStatus(err) code := int(status.Code) writeJSON(code, r.codec, status, w, true) return true } defer requestHijackedConn.Close() if err = newReq.Write(backendConn); err != nil { status := errToAPIStatus(err) code := int(status.Code) writeJSON(code, r.codec, status, w, true) return true } done := make(chan struct{}, 2) go func() { _, err := io.Copy(backendConn, requestHijackedConn) if err != nil && !strings.Contains(err.Error(), "use of closed network connection") { glog.Errorf("Error proxying data from client to backend: %v", err) } done <- struct{}{} }() go func() { _, err := io.Copy(requestHijackedConn, backendConn) if err != nil && !strings.Contains(err.Error(), "use of closed network connection") { glog.Errorf("Error proxying data from backend to client: %v", err) } done <- struct{}{} }() <-done return true }
// tryUpgrade returns true if the request was handled. func (h *UpgradeAwareProxyHandler) tryUpgrade(w http.ResponseWriter, req *http.Request) bool { if !httpstream.IsUpgradeRequest(req) { return false } backendConn, err := proxy.DialURL(h.Location, h.Transport) if err != nil { h.Responder.Error(err) return true } defer backendConn.Close() requestHijackedConn, _, err := w.(http.Hijacker).Hijack() if err != nil { h.Responder.Error(err) return true } defer requestHijackedConn.Close() newReq, err := http.NewRequest(req.Method, h.Location.String(), req.Body) if err != nil { h.Responder.Error(err) return true } newReq.Header = req.Header if err = newReq.Write(backendConn); err != nil { h.Responder.Error(err) return true } wg := &sync.WaitGroup{} wg.Add(2) go func() { var writer io.WriteCloser if h.MaxBytesPerSec > 0 { writer = flowrate.NewWriter(backendConn, h.MaxBytesPerSec) } else { writer = backendConn } _, err := io.Copy(writer, requestHijackedConn) if err != nil && !strings.Contains(err.Error(), "use of closed network connection") { glog.Errorf("Error proxying data from client to backend: %v", err) } wg.Done() }() go func() { var reader io.ReadCloser if h.MaxBytesPerSec > 0 { reader = flowrate.NewReader(backendConn, h.MaxBytesPerSec) } else { reader = backendConn } _, err := io.Copy(requestHijackedConn, reader) if err != nil && !strings.Contains(err.Error(), "use of closed network connection") { glog.Errorf("Error proxying data from backend to client: %v", err) } wg.Done() }() wg.Wait() return true }
// tryUpgrade returns true if the request was handled. func (h *UpgradeAwareProxyHandler) tryUpgrade(w http.ResponseWriter, req *http.Request) bool { if !httpstream.IsUpgradeRequest(req) { return false } var ( backendConn net.Conn rawResponse []byte err error ) if h.InterceptRedirects && utilconfig.DefaultFeatureGate.StreamingProxyRedirects() { backendConn, rawResponse, err = h.connectBackendWithRedirects(req) } else { backendConn, err = h.connectBackend(req.Method, h.Location, req.Header, req.Body) } if err != nil { h.Responder.Error(err) return true } defer backendConn.Close() // Once the connection is hijacked, the ErrorResponder will no longer work, so // hijacking should be the last step in the upgrade. requestHijacker, ok := w.(http.Hijacker) if !ok { h.Responder.Error(fmt.Errorf("request connection cannot be hijacked: %T", w)) return true } requestHijackedConn, _, err := requestHijacker.Hijack() if err != nil { h.Responder.Error(fmt.Errorf("error hijacking request connection: %v", err)) return true } defer requestHijackedConn.Close() // Forward raw response bytes back to client. if len(rawResponse) > 0 { if _, err = requestHijackedConn.Write(rawResponse); err != nil { utilruntime.HandleError(fmt.Errorf("Error proxying response from backend to client: %v", err)) } } // Proxy the connection. wg := &sync.WaitGroup{} wg.Add(2) go func() { var writer io.WriteCloser if h.MaxBytesPerSec > 0 { writer = flowrate.NewWriter(backendConn, h.MaxBytesPerSec) } else { writer = backendConn } _, err := io.Copy(writer, requestHijackedConn) if err != nil && !strings.Contains(err.Error(), "use of closed network connection") { glog.Errorf("Error proxying data from client to backend: %v", err) } wg.Done() }() go func() { var reader io.ReadCloser if h.MaxBytesPerSec > 0 { reader = flowrate.NewReader(backendConn, h.MaxBytesPerSec) } else { reader = backendConn } _, err := io.Copy(requestHijackedConn, reader) if err != nil && !strings.Contains(err.Error(), "use of closed network connection") { glog.Errorf("Error proxying data from backend to client: %v", err) } wg.Done() }() wg.Wait() return true }