From 8494d3fdc10cbf6a8518ef3089298e783c38156c Mon Sep 17 00:00:00 2001 From: Peter Schultz Date: Tue, 16 Jul 2019 17:05:03 +0200 Subject: [PATCH] Fix matching priority for host:port tuples (#675) e566aaab reversed DNS names since they have their most significant part at the front. Unfortunately it didn't consider cases where the name is followed by a :port, making names without port always take precedence over names with a port. This patch changes the reverse function to ignores such port suffixes. Signed-off-by: Peter Schultz --- route/table.go | 27 +++++++++++++++++++-------- route/table_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/route/table.go b/route/table.go index f1d50eec3..67a5ca603 100644 --- a/route/table.go +++ b/route/table.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "log" + "net" "net/http" "net/url" "sort" @@ -341,11 +342,11 @@ func (t Table) matchingHosts(req *http.Request) (hosts []string) { // the correct result we need to reverse the strings, sort them and then // reverse them again. for i, h := range hosts { - hosts[i] = Reverse(h) + hosts[i] = ReverseHostPort(h) } sort.Sort(sort.Reverse(sort.StringSlice(hosts))) for i, h := range hosts { - hosts[i] = Reverse(h) + hosts[i] = ReverseHostPort(h) } return } @@ -368,15 +369,25 @@ func (t Table) matchingHostNoGlob(req *http.Request) (hosts []string) { return } -// Reverse returns its argument string reversed rune-wise left to right. -// -// taken from https://github.com/golang/example/blob/master/stringutil/reverse.go -func Reverse(s string) string { - r := []rune(s) +// ReverseHostPort returns its argument string reversed rune-wise left to +// right. If s includes a port, only the host part is reversed. +func ReverseHostPort(s string) string { + h, p, _ := net.SplitHostPort(s) + if h == "" { + h = s + } + + // Taken from https://github.com/golang/example/blob/master/stringutil/reverse.go + r := []rune(h) for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 { r[i], r[j] = r[j], r[i] } - return string(r) + + if p == "" { + return string(r) + } else { + return net.JoinHostPort(string(r), p) + } } // Lookup finds a target url based on the current matcher and picker diff --git a/route/table_test.go b/route/table_test.go index fb621c8c0..86b260927 100644 --- a/route/table_test.go +++ b/route/table_test.go @@ -6,6 +6,7 @@ import ( "fmt" "math" "net/http" + "net/http/httptest" "reflect" "strconv" "strings" @@ -641,6 +642,32 @@ func TestTableLookup(t *testing.T) { } } +func TestTableLookup_656(t *testing.T) { + // A typical HTTPS redirect + s := ` + route add my-service example.com:80/ https://example.com$path opts "redirect=301" + route add my-service example.com/ http://127.0.0.1:3000/ + ` + + tbl, err := NewTable(bytes.NewBufferString(s)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("GET", "http://example.com/foo", nil) + target := tbl.Lookup(req, "redirect", rrPicker, prefixMatcher, false) + + if target == nil { + t.Fatal("No route match") + } + if got, want := target.RedirectCode, 301; got != want { + t.Errorf("target.RedirectCode = %d, want %d", got, want) + } + if got, want := fmt.Sprint(target.RedirectURL), "https://example.com/foo"; got != want { + t.Errorf("target.RedirectURL = %s, want %s", got, want) + } +} + func TestNewTableCustom(t *testing.T) { var routes []RouteDef