git » gofer » commit 51b9448

http: Fix how we join paths ending in "/"

author Alberto Bertogli
2017-08-07 14:25:24 UTC
committer Alberto Bertogli
2017-08-07 14:48:00 UTC
parent c15b6ad46399a1837ad0436f4865922b22178804

http: Fix how we join paths ending in "/"

Paths ending in "/" may have special meaning for http servers, but
path.Join will always strip them.

So this patch implements our own function to join paths, which does not
mess with ending slashes.

proxy/http.go +10 -2
proxy/http_test.go +20 -0

diff --git a/proxy/http.go b/proxy/http.go
index e3c68b9..540ce7d 100644
--- a/proxy/http.go
+++ b/proxy/http.go
@@ -5,7 +5,6 @@ import (
 	"net/http"
 	"net/http/httputil"
 	"net/url"
-	"path"
 	"strings"
 
 	"blitiri.com.ar/go/gofer/config"
@@ -69,7 +68,7 @@ func makeProxy(from string, to *url.URL) http.Handler {
 		req.URL.Scheme = to.Scheme
 		req.URL.Host = to.Host
 		req.URL.RawQuery = req.URL.RawQuery
-		req.URL.Path = path.Join(to.Path, strings.TrimPrefix(req.URL.Path, from))
+		req.URL.Path = joinPath(to.Path, strings.TrimPrefix(req.URL.Path, from))
 		if req.URL.Path == "" || req.URL.Path[0] != '/' {
 			req.URL.Path = "/" + req.URL.Path
 		}
@@ -88,6 +87,15 @@ func makeProxy(from string, to *url.URL) http.Handler {
 	return proxy
 }
 
+// joinPath joins to HTTP paths. We can't use path.Join because it strips the
+// final "/", which may have meaning in URLs.
+func joinPath(a, b string) string {
+	if !strings.HasSuffix(a, "/") && !strings.HasPrefix(b, "/") {
+		a = a + "/"
+	}
+	return a + b
+}
+
 type loggingTransport struct{}
 
 func (t *loggingTransport) RoundTrip(req *http.Request) (*http.Response, error) {
diff --git a/proxy/http_test.go b/proxy/http_test.go
index f4ffd8c..8df0d40 100644
--- a/proxy/http_test.go
+++ b/proxy/http_test.go
@@ -110,3 +110,23 @@ func getFreePort() string {
 	defer l.Close()
 	return l.Addr().String()
 }
+
+func TestJoinPath(t *testing.T) {
+	cases := []struct{ a, b, expected string }{
+		{"/a/", "", "/a/"},
+		{"/a/", "b", "/a/b"},
+		{"/a/", "b/", "/a/b/"},
+		{"a/", "", "a/"},
+		{"a/", "b", "a/b"},
+		{"a/", "b/", "a/b/"},
+		{"/", "", "/"},
+		{"", "", "/"},
+		{"/", "/", "//"}, // Not sure if we want this, but ok for now.
+	}
+	for _, c := range cases {
+		got := joinPath(c.a, c.b)
+		if got != c.expected {
+			t.Errorf("join %q, %q = %q, expected %q", c.a, c.b, got, c.expected)
+		}
+	}
+}