From 7949388da8afed082064df539076c7043acd2993 Mon Sep 17 00:00:00 2001 From: Abiola Ibrahim Date: Mon, 16 Nov 2015 17:22:06 +0100 Subject: [PATCH] Proxy: Allow ignored subpaths. --- middleware/proxy/proxy.go | 4 +++- middleware/proxy/proxy_test.go | 4 ++++ middleware/proxy/upstream.go | 18 ++++++++++++++++++ middleware/proxy/upstream_test.go | 30 ++++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 1 deletion(-) diff --git a/middleware/proxy/proxy.go b/middleware/proxy/proxy.go index 9b8fed21f..fcb00abea 100644 --- a/middleware/proxy/proxy.go +++ b/middleware/proxy/proxy.go @@ -26,6 +26,8 @@ type Upstream interface { From() string // Selects an upstream host to be routed to. Select() *UpstreamHost + // Checks if subpath is not an ignored path + IsAllowedPath(string) bool } // UpstreamHostDownFunc can be used to customize how Down behaves. @@ -59,7 +61,7 @@ func (uh *UpstreamHost) Down() bool { func (p Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { for _, upstream := range p.Upstreams { - if middleware.Path(r.URL.Path).Matches(upstream.From()) { + if middleware.Path(r.URL.Path).Matches(upstream.From()) && upstream.IsAllowedPath(r.URL.Path) { var replacer middleware.Replacer start := time.Now() requestHost := r.Host diff --git a/middleware/proxy/proxy_test.go b/middleware/proxy/proxy_test.go index 6fb6a899b..aab836bcc 100644 --- a/middleware/proxy/proxy_test.go +++ b/middleware/proxy/proxy_test.go @@ -125,6 +125,10 @@ func (u *fakeUpstream) Select() *UpstreamHost { } } +func (u *fakeUpstream) IsAllowedPath(requestPath string) bool { + return true +} + // recorderHijacker is a ResponseRecorder that can // be hijacked. type recorderHijacker struct { diff --git a/middleware/proxy/upstream.go b/middleware/proxy/upstream.go index f068907ef..7916b3b6c 100644 --- a/middleware/proxy/upstream.go +++ b/middleware/proxy/upstream.go @@ -5,11 +5,13 @@ import ( "io/ioutil" "net/http" "net/url" + "path" "strconv" "strings" "time" "github.com/mholt/caddy/caddy/parse" + "github.com/mholt/caddy/middleware" ) var ( @@ -29,6 +31,7 @@ type staticUpstream struct { Interval time.Duration } WithoutPathPrefix string + IgnoredSubPaths []string } // NewStaticUpstreams parses the configuration input and sets up @@ -165,6 +168,12 @@ func parseBlock(c *parse.Dispenser, u *staticUpstream) error { return c.ArgErr() } u.WithoutPathPrefix = c.Val() + case "except": + ignoredPaths := c.RemainingArgs() + if len(ignoredPaths) == 0 { + return c.ArgErr() + } + u.IgnoredSubPaths = ignoredPaths default: return c.Errf("unknown property '%s'", c.Val()) } @@ -223,3 +232,12 @@ func (u *staticUpstream) Select() *UpstreamHost { } return u.Policy.Select(pool) } + +func (u *staticUpstream) IsAllowedPath(requestPath string) bool { + for _, ignoredSubPath := range u.IgnoredSubPaths { + if middleware.Path(path.Clean(requestPath)).Matches(path.Join(u.From(), ignoredSubPath)) { + return false + } + } + return true +} diff --git a/middleware/proxy/upstream_test.go b/middleware/proxy/upstream_test.go index f3df16136..5b2fdb1da 100644 --- a/middleware/proxy/upstream_test.go +++ b/middleware/proxy/upstream_test.go @@ -51,3 +51,33 @@ func TestRegisterPolicy(t *testing.T) { } } + +func TestAllowedPaths(t *testing.T) { + upstream := &staticUpstream{ + from: "/proxy", + IgnoredSubPaths: []string{"/download", "/static"}, + } + tests := []struct { + url string + expected bool + }{ + {"/proxy", true}, + {"/proxy/dl", true}, + {"/proxy/download", false}, + {"/proxy/download/static", false}, + {"/proxy/static", false}, + {"/proxy/static/download", false}, + {"/proxy/something/download", true}, + {"/proxy/something/static", true}, + {"/proxy//static", false}, + {"/proxy//static//download", false}, + {"/proxy//download", false}, + } + + for i, test := range tests { + isAllowed := upstream.IsAllowedPath(test.url) + if test.expected != isAllowed { + t.Errorf("Test %d: expected %v found %v", i+1, test.expected, isAllowed) + } + } +}