Commit 9e0b1b42 authored by Matt Holt's avatar Matt Holt

Merge pull request #687 from abiosoft/fastcgi-except

fastcgi: Add `except` to FastCGI. Minor refactor in proxy.
parents 4d9741dd e7001e65
...@@ -86,6 +86,12 @@ func fastcgiParse(c *Controller) ([]fastcgi.Rule, error) { ...@@ -86,6 +86,12 @@ func fastcgiParse(c *Controller) ([]fastcgi.Rule, error) {
return rules, c.ArgErr() return rules, c.ArgErr()
} }
rule.EnvVars = append(rule.EnvVars, [2]string{envArgs[0], envArgs[1]}) 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
} }
} }
......
...@@ -2,8 +2,9 @@ package setup ...@@ -2,8 +2,9 @@ package setup
import ( import (
"fmt" "fmt"
"github.com/mholt/caddy/middleware/fastcgi"
"testing" "testing"
"github.com/mholt/caddy/middleware/fastcgi"
) )
func TestFastCGI(t *testing.T) { func TestFastCGI(t *testing.T) {
...@@ -61,6 +62,18 @@ func TestFastcgiParse(t *testing.T) { ...@@ -61,6 +62,18 @@ func TestFastcgiParse(t *testing.T) {
SplitPath: ".html", SplitPath: ".html",
IndexFiles: []string{}, 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 { for i, test := range tests {
c := NewTestController(test.inputFastcgiConfig) c := NewTestController(test.inputFastcgiConfig)
...@@ -101,6 +114,11 @@ func TestFastcgiParse(t *testing.T) { ...@@ -101,6 +114,11 @@ func TestFastcgiParse(t *testing.T) {
t.Errorf("Test %d expected %dth FastCGI IndexFiles to be %s , but got %s", t.Errorf("Test %d expected %dth FastCGI IndexFiles to be %s , but got %s",
i, j, test.expectedFastcgiConfig[j].IndexFiles, actualFastcgiConfig.IndexFiles) 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)
}
} }
} }
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"io" "io"
"net/http" "net/http"
"os" "os"
"path"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
...@@ -34,8 +35,8 @@ type Handler struct { ...@@ -34,8 +35,8 @@ type Handler struct {
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
for _, rule := range h.Rules { for _, rule := range h.Rules {
// First requirement: Base path must match // First requirement: Base path must match and the path must be allowed.
if !middleware.Path(r.URL.Path).Matches(rule.Path) { if !middleware.Path(r.URL.Path).Matches(rule.Path) || !rule.AllowedPath(r.URL.Path) {
continue continue
} }
...@@ -287,6 +288,9 @@ type Rule struct { ...@@ -287,6 +288,9 @@ type Rule struct {
// Environment Variables // Environment Variables
EnvVars [][2]string EnvVars [][2]string
// Ignored paths
IgnoredSubPaths []string
} }
// canSplit checks if path can split into two based on rule.SplitPath. // canSplit checks if path can split into two based on rule.SplitPath.
...@@ -303,6 +307,16 @@ func (r Rule) splitPos(path string) int { ...@@ -303,6 +307,16 @@ func (r Rule) splitPos(path string) int {
return strings.Index(strings.ToLower(path), strings.ToLower(r.SplitPath)) 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 ( var (
headerNameReplacer = strings.NewReplacer(" ", "_", "-", "_") headerNameReplacer = strings.NewReplacer(" ", "_", "-", "_")
// ErrIndexMissingSplit describes an index configuration error. // ErrIndexMissingSplit describes an index configuration error.
......
...@@ -73,6 +73,36 @@ func TestRuleParseAddress(t *testing.T) { ...@@ -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) { func TestBuildEnv(t *testing.T) {
testBuildEnv := func(r *http.Request, rule Rule, fpath string, envExpected map[string]string) { testBuildEnv := func(r *http.Request, rule Rule, fpath string, envExpected map[string]string) {
var h Handler var h Handler
......
...@@ -27,7 +27,7 @@ type Upstream interface { ...@@ -27,7 +27,7 @@ type Upstream interface {
// Selects an upstream host to be routed to. // Selects an upstream host to be routed to.
Select() *UpstreamHost Select() *UpstreamHost
// Checks if subpath is not an ignored path // Checks if subpath is not an ignored path
IsAllowedPath(string) bool AllowedPath(string) bool
} }
// UpstreamHostDownFunc can be used to customize how Down behaves. // UpstreamHostDownFunc can be used to customize how Down behaves.
...@@ -75,7 +75,7 @@ var tryDuration = 60 * time.Second ...@@ -75,7 +75,7 @@ var tryDuration = 60 * time.Second
// ServeHTTP satisfies the middleware.Handler interface. // ServeHTTP satisfies the middleware.Handler interface.
func (p Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { func (p Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
for _, upstream := range p.Upstreams { 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 var replacer middleware.Replacer
start := time.Now() start := time.Now()
requestHost := r.Host requestHost := r.Host
......
...@@ -254,7 +254,7 @@ func (u *fakeUpstream) Select() *UpstreamHost { ...@@ -254,7 +254,7 @@ func (u *fakeUpstream) Select() *UpstreamHost {
return u.host return u.host
} }
func (u *fakeUpstream) IsAllowedPath(requestPath string) bool { func (u *fakeUpstream) AllowedPath(requestPath string) bool {
return true return true
} }
...@@ -287,7 +287,7 @@ func (u *fakeWsUpstream) Select() *UpstreamHost { ...@@ -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 return true
} }
......
...@@ -263,7 +263,7 @@ func (u *staticUpstream) Select() *UpstreamHost { ...@@ -263,7 +263,7 @@ func (u *staticUpstream) Select() *UpstreamHost {
return u.Policy.Select(pool) return u.Policy.Select(pool)
} }
func (u *staticUpstream) IsAllowedPath(requestPath string) bool { func (u *staticUpstream) AllowedPath(requestPath string) bool {
for _, ignoredSubPath := range u.IgnoredSubPaths { for _, ignoredSubPath := range u.IgnoredSubPaths {
if middleware.Path(path.Clean(requestPath)).Matches(path.Join(u.From(), ignoredSubPath)) { if middleware.Path(path.Clean(requestPath)).Matches(path.Join(u.From(), ignoredSubPath)) {
return false return false
......
...@@ -127,9 +127,9 @@ func TestAllowedPaths(t *testing.T) { ...@@ -127,9 +127,9 @@ func TestAllowedPaths(t *testing.T) {
} }
for i, test := range tests { for i, test := range tests {
isAllowed := upstream.IsAllowedPath(test.url) allowed := upstream.AllowedPath(test.url)
if test.expected != isAllowed { if test.expected != allowed {
t.Errorf("Test %d: expected %v found %v", i+1, test.expected, isAllowed) t.Errorf("Test %d: expected %v found %v", i+1, test.expected, allowed)
} }
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment