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