From e7001e653841811ca752989c519551a4f1bf6871 Mon Sep 17 00:00:00 2001 From: Abiola Ibrahim Date: Sun, 20 Mar 2016 08:02:17 +0100 Subject: [PATCH] Add `except` to FastCGI. Minor refactor in proxy. --- caddy/setup/fastcgi.go | 6 ++++++ caddy/setup/fastcgi_test.go | 20 +++++++++++++++++++- middleware/fastcgi/fastcgi.go | 18 ++++++++++++++++-- middleware/fastcgi/fastcgi_test.go | 30 ++++++++++++++++++++++++++++++ middleware/proxy/proxy.go | 4 ++-- middleware/proxy/proxy_test.go | 4 ++-- middleware/proxy/upstream.go | 2 +- middleware/proxy/upstream_test.go | 6 +++--- 8 files changed, 79 insertions(+), 11 deletions(-) diff --git a/caddy/setup/fastcgi.go b/caddy/setup/fastcgi.go index a2a7e879..d1e53d15 100644 --- a/caddy/setup/fastcgi.go +++ b/caddy/setup/fastcgi.go @@ -86,6 +86,12 @@ func fastcgiParse(c *Controller) ([]fastcgi.Rule, error) { return rules, c.ArgErr() } rule.EnvVars = append(rule.EnvVars, [2]string{envArgs[0], envArgs[1]}) + case "except": + ignoredPaths := c.RemainingArgs() + if len(ignoredPaths) == 0 { + return rules, c.ArgErr() + } + rule.IgnoredSubPaths = ignoredPaths } } diff --git a/caddy/setup/fastcgi_test.go b/caddy/setup/fastcgi_test.go index 661b92e5..366446de 100644 --- a/caddy/setup/fastcgi_test.go +++ b/caddy/setup/fastcgi_test.go @@ -2,8 +2,9 @@ package setup import ( "fmt" - "github.com/mholt/caddy/middleware/fastcgi" "testing" + + "github.com/mholt/caddy/middleware/fastcgi" ) func TestFastCGI(t *testing.T) { @@ -61,6 +62,18 @@ func TestFastcgiParse(t *testing.T) { SplitPath: ".html", IndexFiles: []string{}, }}}, + {`fastcgi / 127.0.0.1:9001 { + split .html + except /admin /user + }`, + false, []fastcgi.Rule{{ + Path: "/", + Address: "127.0.0.1:9001", + Ext: "", + SplitPath: ".html", + IndexFiles: []string{}, + IgnoredSubPaths: []string{"/admin", "/user"}, + }}}, } for i, test := range tests { c := NewTestController(test.inputFastcgiConfig) @@ -101,6 +114,11 @@ func TestFastcgiParse(t *testing.T) { t.Errorf("Test %d expected %dth FastCGI IndexFiles to be %s , but got %s", i, j, test.expectedFastcgiConfig[j].IndexFiles, actualFastcgiConfig.IndexFiles) } + + if fmt.Sprint(actualFastcgiConfig.IgnoredSubPaths) != fmt.Sprint(test.expectedFastcgiConfig[j].IgnoredSubPaths) { + t.Errorf("Test %d expected %dth FastCGI IgnoredSubPaths to be %s , but got %s", + i, j, test.expectedFastcgiConfig[j].IgnoredSubPaths, actualFastcgiConfig.IgnoredSubPaths) + } } } diff --git a/middleware/fastcgi/fastcgi.go b/middleware/fastcgi/fastcgi.go index d3600b67..e481698c 100755 --- a/middleware/fastcgi/fastcgi.go +++ b/middleware/fastcgi/fastcgi.go @@ -8,6 +8,7 @@ import ( "io" "net/http" "os" + "path" "path/filepath" "strconv" "strings" @@ -34,8 +35,8 @@ type Handler struct { func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { for _, rule := range h.Rules { - // First requirement: Base path must match - if !middleware.Path(r.URL.Path).Matches(rule.Path) { + // First requirement: Base path must match and the path must be allowed. + if !middleware.Path(r.URL.Path).Matches(rule.Path) || !rule.AllowedPath(r.URL.Path) { continue } @@ -287,6 +288,9 @@ type Rule struct { // Environment Variables EnvVars [][2]string + + // Ignored paths + IgnoredSubPaths []string } // canSplit checks if path can split into two based on rule.SplitPath. @@ -303,6 +307,16 @@ func (r Rule) splitPos(path string) int { return strings.Index(strings.ToLower(path), strings.ToLower(r.SplitPath)) } +// AllowedPath checks if requestPath is not an ignored path. +func (r Rule) AllowedPath(requestPath string) bool { + for _, ignoredSubPath := range r.IgnoredSubPaths { + if middleware.Path(path.Clean(requestPath)).Matches(path.Join(r.Path, ignoredSubPath)) { + return false + } + } + return true +} + var ( headerNameReplacer = strings.NewReplacer(" ", "_", "-", "_") // ErrIndexMissingSplit describes an index configuration error. diff --git a/middleware/fastcgi/fastcgi_test.go b/middleware/fastcgi/fastcgi_test.go index 001f3872..3d93e8ae 100644 --- a/middleware/fastcgi/fastcgi_test.go +++ b/middleware/fastcgi/fastcgi_test.go @@ -73,6 +73,36 @@ func TestRuleParseAddress(t *testing.T) { } } +func TestRuleIgnoredPath(t *testing.T) { + rule := &Rule{ + Path: "/fastcgi", + IgnoredSubPaths: []string{"/download", "/static"}, + } + tests := []struct { + url string + expected bool + }{ + {"/fastcgi", true}, + {"/fastcgi/dl", true}, + {"/fastcgi/download", false}, + {"/fastcgi/download/static", false}, + {"/fastcgi/static", false}, + {"/fastcgi/static/download", false}, + {"/fastcgi/something/download", true}, + {"/fastcgi/something/static", true}, + {"/fastcgi//static", false}, + {"/fastcgi//static//download", false}, + {"/fastcgi//download", false}, + } + + for i, test := range tests { + allowed := rule.AllowedPath(test.url) + if test.expected != allowed { + t.Errorf("Test %d: expected %v found %v", i, test.expected, allowed) + } + } +} + func TestBuildEnv(t *testing.T) { testBuildEnv := func(r *http.Request, rule Rule, fpath string, envExpected map[string]string) { var h Handler diff --git a/middleware/proxy/proxy.go b/middleware/proxy/proxy.go index 583f4a63..ed466bae 100644 --- a/middleware/proxy/proxy.go +++ b/middleware/proxy/proxy.go @@ -27,7 +27,7 @@ type Upstream interface { // Selects an upstream host to be routed to. Select() *UpstreamHost // Checks if subpath is not an ignored path - IsAllowedPath(string) bool + AllowedPath(string) bool } // UpstreamHostDownFunc can be used to customize how Down behaves. @@ -75,7 +75,7 @@ var tryDuration = 60 * time.Second // ServeHTTP satisfies the middleware.Handler interface. 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()) && upstream.IsAllowedPath(r.URL.Path) { + if middleware.Path(r.URL.Path).Matches(upstream.From()) && upstream.AllowedPath(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 8066874d..6bd17eae 100644 --- a/middleware/proxy/proxy_test.go +++ b/middleware/proxy/proxy_test.go @@ -254,7 +254,7 @@ func (u *fakeUpstream) Select() *UpstreamHost { return u.host } -func (u *fakeUpstream) IsAllowedPath(requestPath string) bool { +func (u *fakeUpstream) AllowedPath(requestPath string) bool { return true } @@ -287,7 +287,7 @@ func (u *fakeWsUpstream) Select() *UpstreamHost { } } -func (u *fakeWsUpstream) IsAllowedPath(requestPath string) bool { +func (u *fakeWsUpstream) AllowedPath(requestPath string) bool { return true } diff --git a/middleware/proxy/upstream.go b/middleware/proxy/upstream.go index 29b3d2d8..f98f1482 100644 --- a/middleware/proxy/upstream.go +++ b/middleware/proxy/upstream.go @@ -263,7 +263,7 @@ func (u *staticUpstream) Select() *UpstreamHost { return u.Policy.Select(pool) } -func (u *staticUpstream) IsAllowedPath(requestPath string) bool { +func (u *staticUpstream) AllowedPath(requestPath string) bool { for _, ignoredSubPath := range u.IgnoredSubPaths { if middleware.Path(path.Clean(requestPath)).Matches(path.Join(u.From(), ignoredSubPath)) { return false diff --git a/middleware/proxy/upstream_test.go b/middleware/proxy/upstream_test.go index 0370b6a4..9d38b785 100644 --- a/middleware/proxy/upstream_test.go +++ b/middleware/proxy/upstream_test.go @@ -127,9 +127,9 @@ func TestAllowedPaths(t *testing.T) { } 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) + allowed := upstream.AllowedPath(test.url) + if test.expected != allowed { + t.Errorf("Test %d: expected %v found %v", i+1, test.expected, allowed) } } }