From bfdb77a245927cac71e2440a7df4cb837a469979 Mon Sep 17 00:00:00 2001 From: ale <ale@incal.net> Date: Thu, 30 Apr 2020 10:39:08 +0100 Subject: [PATCH] Run a simple httputil.ReverseProxy with TLS connections We can't do low-level splice() since we don't have access to the raw socket in the TLS case. This should work for clients though (maybe not for sources). --- node/http.go | 4 ++-- node/proxy.go | 25 +++++++++++++++++-------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/node/http.go b/node/http.go index 49559346..1e75ae62 100644 --- a/node/http.go +++ b/node/http.go @@ -291,8 +291,8 @@ func newHTTPServer(name, addr string, h http.Handler) *httpServer { Handler: h, ReadTimeout: 10 * time.Second, ReadHeaderTimeout: 3 * time.Second, - WriteTimeout: 10 * time.Second, - IdleTimeout: 30 * time.Second, + //WriteTimeout: 10 * time.Second, + IdleTimeout: 30 * time.Second, }, name: name, } diff --git a/node/proxy.go b/node/proxy.go index b9176e54..9b94d829 100644 --- a/node/proxy.go +++ b/node/proxy.go @@ -14,6 +14,7 @@ import ( "log" "net" "net/http" + "net/http/httputil" "net/url" "strings" "sync" @@ -102,6 +103,18 @@ func doIcecastProxy(rw http.ResponseWriter, req *http.Request, target *url.URL, outreq.Header.Set("X-Forwarded-For", clientIP) } + // SSL requests can't be hijacked, so we just fire up a normal + // httputil.ReverseProxy (not fully functional for sources, but + // meh). + if req.TLS != nil { + log.Printf("TLS connection, switching to dumb reverse proxy mode") + u := *outreq.URL + u.Path = "/" + rp := httputil.NewSingleHostReverseProxy(&u) + rp.ServeHTTP(rw, outreq) + return + } + // Create the upstream connection and write the HTTP request // to it. upstream, err := dialer.Dial("tcp", outreq.URL.Host) @@ -147,7 +160,7 @@ func doIcecastProxy(rw http.ResponseWriter, req *http.Request, target *url.URL, } if conn == nil { log.Printf("http: proxy error: could not find hijackable connection") - rw.WriteHeader(http.StatusInternalServerError) + http.Error(rw, "could not find hijackable connection", http.StatusInternalServerError) return } defer conn.Close() @@ -156,21 +169,17 @@ func doIcecastProxy(rw http.ResponseWriter, req *http.Request, target *url.URL, } // Run two-way proxying. - handleProxy(conn.(*net.TCPConn), upstream.(*net.TCPConn), streamName) + handleProxy(conn, upstream, streamName) } // Copy data between two network connections. On recent Go versions // (>1.11), this is quite fast as io.CopyBuffer uses the splice() // system call internally (in exchange we lose the ability to figure // out which end of the connection is the source of the error). -func copyStream(tag string, out, in *net.TCPConn, promCounter prometheus.Counter, cntr *uint64) { +func copyStream(tag string, out io.WriteCloser, in io.ReadCloser, promCounter prometheus.Counter, cntr *uint64) { buf := getBuf() defer releaseBuf(buf) - // We used to do this in order to support half-closed connections. - //defer in.CloseRead() //nolint - //defer out.CloseWrite() //nolint - // Instead we do this and shut down the entire connection on error. // We end up calling Close() twice but that's not a huge problem. defer in.Close() //nolint @@ -195,7 +204,7 @@ func isCloseError(err error) bool { // Simple two-way TCP proxy that copies data in both directions and // can shutdown each direction of the connection independently. -func handleProxy(conn *net.TCPConn, upstream *net.TCPConn, streamName string) { +func handleProxy(conn, upstream io.ReadWriteCloser, streamName string) { l := streamListeners.WithLabelValues(streamName) l.Inc() var wg sync.WaitGroup -- GitLab