diff --git a/caddyhttp/fastcgi/fastcgi.go b/caddyhttp/fastcgi/fastcgi.go index c4dd9b32c..0833274f6 100644 --- a/caddyhttp/fastcgi/fastcgi.go +++ b/caddyhttp/fastcgi/fastcgi.go @@ -23,7 +23,6 @@ type Handler struct { Next httpserver.Handler Rules []Rule Root string - AbsRoot string // same as root, but absolute path FileSys http.FileSystem // These are sent to CGI scripts in env variables @@ -184,7 +183,7 @@ func (h Handler) buildEnv(r *http.Request, rule Rule, fpath string) (map[string] var env map[string]string // Get absolute path of requested resource - absPath := filepath.Join(h.AbsRoot, fpath) + absPath := filepath.Join(rule.Root, fpath) // Separate remote IP and port; more lenient than net.SplitHostPort var ip, port string @@ -244,7 +243,7 @@ func (h Handler) buildEnv(r *http.Request, rule Rule, fpath string) (map[string] "SERVER_SOFTWARE": h.SoftwareName + "/" + h.SoftwareVersion, // Other variables - "DOCUMENT_ROOT": h.AbsRoot, + "DOCUMENT_ROOT": rule.Root, "DOCUMENT_URI": docURI, "HTTP_HOST": r.Host, // added here, since not always part of headers "REQUEST_URI": reqURI, @@ -256,7 +255,7 @@ func (h Handler) buildEnv(r *http.Request, rule Rule, fpath string) (map[string] // should only exist if PATH_INFO is defined. // Info: https://www.ietf.org/rfc/rfc3875 Page 14 if env["PATH_INFO"] != "" { - env["PATH_TRANSLATED"] = filepath.Join(h.AbsRoot, pathInfo) // Info: http://www.oreilly.com/openbook/cgi/ch02_04.html + env["PATH_TRANSLATED"] = filepath.Join(rule.Root, pathInfo) // Info: http://www.oreilly.com/openbook/cgi/ch02_04.html } // Some web apps rely on knowing HTTPS or not @@ -295,6 +294,10 @@ type Rule struct { // Always process files with this extension with fastcgi. Ext string + // Use this directory as the fastcgi root directory. Defaults to the root + // directory of the parent virtual host. + Root string + // The path in the URL will be split into two, with the first piece ending // with the value of SplitPath. The first piece will be assumed as the // actual resource (CGI script) name, and the second piece will be set to diff --git a/caddyhttp/fastcgi/setup.go b/caddyhttp/fastcgi/setup.go index e3615afef..9e49baf33 100644 --- a/caddyhttp/fastcgi/setup.go +++ b/caddyhttp/fastcgi/setup.go @@ -22,10 +22,6 @@ func init() { // setup configures a new FastCGI middleware instance. func setup(c *caddy.Controller) error { cfg := httpserver.GetConfig(c) - absRoot, err := filepath.Abs(cfg.Root) - if err != nil { - return err - } rules, err := fastcgiParse(c) if err != nil { @@ -37,7 +33,6 @@ func setup(c *caddy.Controller) error { Next: next, Rules: rules, Root: cfg.Root, - AbsRoot: absRoot, FileSys: http.Dir(cfg.Root), SoftwareName: caddy.AppName, SoftwareVersion: caddy.AppVersion, @@ -52,6 +47,12 @@ func setup(c *caddy.Controller) error { func fastcgiParse(c *caddy.Controller) ([]Rule, error) { var rules []Rule + cfg := httpserver.GetConfig(c) + absRoot, err := filepath.Abs(cfg.Root) + if err != nil { + return nil, err + } + for c.Next() { args := c.RemainingArgs() @@ -59,7 +60,12 @@ func fastcgiParse(c *caddy.Controller) ([]Rule, error) { return rules, c.ArgErr() } - rule := Rule{Path: args[0], ReadTimeout: 60 * time.Second, SendTimeout: 60 * time.Second} + rule := Rule{ + Root: absRoot, + Path: args[0], + ReadTimeout: 60 * time.Second, + SendTimeout: 60 * time.Second, + } upstreams := []string{args[1]} if len(args) == 3 { @@ -76,6 +82,12 @@ func fastcgiParse(c *caddy.Controller) ([]Rule, error) { for c.NextBlock() { switch c.Val() { + case "root": + if !c.NextArg() { + return rules, c.ArgErr() + } + rule.Root = c.Val() + case "ext": if !c.NextArg() { return rules, c.ArgErr() diff --git a/caddyhttp/fastcgi/setup_test.go b/caddyhttp/fastcgi/setup_test.go index 488011b69..72d231d71 100644 --- a/caddyhttp/fastcgi/setup_test.go +++ b/caddyhttp/fastcgi/setup_test.go @@ -2,6 +2,7 @@ package fastcgi import ( "fmt" + "os" "reflect" "testing" "time" @@ -61,6 +62,11 @@ func (p *persistentDialer) Equals(q *persistentDialer) bool { } func TestFastcgiParse(t *testing.T) { + rootPath, err := os.Getwd() + if err != nil { + t.Errorf("Can't determine current working directory; got '%v'", err) + } + defaultAddress := "127.0.0.1:9001" network, address := parseAddress(defaultAddress) t.Logf("Address '%v' was parsed to network '%v' and address '%v'", defaultAddress, network, address) @@ -73,6 +79,21 @@ func TestFastcgiParse(t *testing.T) { {`fastcgi /blog 127.0.0.1:9000 php`, false, []Rule{{ + Root: rootPath, + Path: "/blog", + Address: "127.0.0.1:9000", + Ext: ".php", + SplitPath: ".php", + dialer: &loadBalancingDialer{dialers: []dialer{basicDialer{network: "tcp", address: "127.0.0.1:9000", timeout: 60 * time.Second}}}, + IndexFiles: []string{"index.php"}, + ReadTimeout: 60 * time.Second, + SendTimeout: 60 * time.Second, + }}}, + {`fastcgi /blog 127.0.0.1:9000 php { + root /tmp + }`, + false, []Rule{{ + Root: "/tmp", Path: "/blog", Address: "127.0.0.1:9000", Ext: ".php", @@ -86,6 +107,7 @@ func TestFastcgiParse(t *testing.T) { upstream 127.0.0.1:9001 }`, false, []Rule{{ + Root: rootPath, Path: "/blog", Address: "127.0.0.1:9000,127.0.0.1:9001", Ext: ".php", @@ -99,6 +121,7 @@ func TestFastcgiParse(t *testing.T) { upstream 127.0.0.1:9001 }`, false, []Rule{{ + Root: rootPath, Path: "/blog", Address: "127.0.0.1:9000,127.0.0.1:9001", Ext: "", @@ -112,6 +135,7 @@ func TestFastcgiParse(t *testing.T) { split .html }`, false, []Rule{{ + Root: rootPath, Path: "/", Address: defaultAddress, Ext: "", @@ -126,6 +150,7 @@ func TestFastcgiParse(t *testing.T) { except /admin /user }`, false, []Rule{{ + Root: rootPath, Path: "/", Address: "127.0.0.1:9001", Ext: "", @@ -140,6 +165,7 @@ func TestFastcgiParse(t *testing.T) { pool 0 }`, false, []Rule{{ + Root: rootPath, Path: "/", Address: defaultAddress, Ext: "", @@ -154,6 +180,7 @@ func TestFastcgiParse(t *testing.T) { pool 5 }`, false, []Rule{{ + Root: rootPath, Path: "/", Address: "127.0.0.1:8080,127.0.0.1:9000", Ext: "", @@ -167,6 +194,7 @@ func TestFastcgiParse(t *testing.T) { split .php }`, false, []Rule{{ + Root: rootPath, Path: "/", Address: defaultAddress, Ext: "", @@ -180,6 +208,7 @@ func TestFastcgiParse(t *testing.T) { connect_timeout 5s }`, false, []Rule{{ + Root: rootPath, Path: "/", Address: defaultAddress, Ext: "", @@ -198,6 +227,7 @@ func TestFastcgiParse(t *testing.T) { read_timeout 5s }`, false, []Rule{{ + Root: rootPath, Path: "/", Address: defaultAddress, Ext: "", @@ -216,6 +246,7 @@ func TestFastcgiParse(t *testing.T) { send_timeout 5s }`, false, []Rule{{ + Root: rootPath, Path: "/", Address: defaultAddress, Ext: "", @@ -250,6 +281,11 @@ func TestFastcgiParse(t *testing.T) { } for j, actualFastcgiConfig := range actualFastcgiConfigs { + if actualFastcgiConfig.Root != test.expectedFastcgiConfig[j].Root { + t.Errorf("Test %d expected %dth FastCGI Root to be %s , but got %s", + i, j, test.expectedFastcgiConfig[j].Root, actualFastcgiConfig.Root) + } + if actualFastcgiConfig.Path != test.expectedFastcgiConfig[j].Path { t.Errorf("Test %d expected %dth FastCGI Path to be %s , but got %s", i, j, test.expectedFastcgiConfig[j].Path, actualFastcgiConfig.Path)