diff --git a/caddyhttp/browse/browse.go b/caddyhttp/browse/browse.go index 2ac72c03..f265710e 100644 --- a/caddyhttp/browse/browse.go +++ b/caddyhttp/browse/browse.go @@ -252,7 +252,7 @@ func directoryListing(files []os.FileInfo, canGoUp bool, urlPath string, config for _, f := range files { name := f.Name() - for _, indexName := range staticfiles.IndexPages { + for _, indexName := range config.Fs.IndexPages { if name == indexName { hasIndexFile = true break diff --git a/caddyhttp/browse/setup.go b/caddyhttp/browse/setup.go index 949f414a..6979308b 100644 --- a/caddyhttp/browse/setup.go +++ b/caddyhttp/browse/setup.go @@ -78,8 +78,9 @@ func browseParse(c *caddy.Controller) ([]Config, error) { } bc.Fs = staticfiles.FileServer{ - Root: http.Dir(cfg.Root), - Hide: cfg.HiddenFiles, + Root: http.Dir(cfg.Root), + Hide: cfg.HiddenFiles, + IndexPages: cfg.IndexPages, } // Second argument would be the template file to use diff --git a/caddyhttp/httpserver/plugin.go b/caddyhttp/httpserver/plugin.go index ca8f0c33..643eea7f 100644 --- a/caddyhttp/httpserver/plugin.go +++ b/caddyhttp/httpserver/plugin.go @@ -27,6 +27,7 @@ import ( "github.com/mholt/caddy" "github.com/mholt/caddy/caddyfile" + "github.com/mholt/caddy/caddyhttp/staticfiles" "github.com/mholt/caddy/caddytls" ) @@ -155,6 +156,7 @@ func (h *httpContext) InspectServerBlocks(sourceFile string, serverBlocks []cadd AltTLSSNIPort: altTLSSNIPort, }, originCaddyfile: sourceFile, + IndexPages: staticfiles.DefaultIndexPages, } h.saveConfig(key, cfg) } @@ -234,7 +236,7 @@ func GetConfig(c *caddy.Controller) *SiteConfig { // we should only get here during tests because directive // actions typically skip the server blocks where we make // the configs - cfg := &SiteConfig{Root: Root, TLS: new(caddytls.Config)} + cfg := &SiteConfig{Root: Root, TLS: new(caddytls.Config), IndexPages: staticfiles.DefaultIndexPages} ctx.saveConfig(key, cfg) return cfg } diff --git a/caddyhttp/httpserver/server.go b/caddyhttp/httpserver/server.go index eb854d72..b0f85a05 100644 --- a/caddyhttp/httpserver/server.go +++ b/caddyhttp/httpserver/server.go @@ -142,7 +142,7 @@ func NewServer(addr string, group []*SiteConfig) (*Server, error) { // Compile custom middleware for every site (enables virtual hosting) for _, site := range group { - stack := Handler(staticfiles.FileServer{Root: http.Dir(site.Root), Hide: site.HiddenFiles}) + stack := Handler(staticfiles.FileServer{Root: http.Dir(site.Root), Hide: site.HiddenFiles, IndexPages: site.IndexPages}) for i := len(site.middleware) - 1; i >= 0; i-- { stack = site.middleware[i](stack) } diff --git a/caddyhttp/httpserver/siteconfig.go b/caddyhttp/httpserver/siteconfig.go index bd444c75..1cd9a050 100644 --- a/caddyhttp/httpserver/siteconfig.go +++ b/caddyhttp/httpserver/siteconfig.go @@ -26,6 +26,9 @@ type SiteConfig struct { // The address of the site Addr Address + // The list of viable index page names of the site + IndexPages []string + // The hostname to bind listener to; // defaults to Addr.Host ListenHost string diff --git a/caddyhttp/index/index.go b/caddyhttp/index/index.go index 760245c9..fefc0ff7 100644 --- a/caddyhttp/index/index.go +++ b/caddyhttp/index/index.go @@ -16,7 +16,7 @@ package index import ( "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/staticfiles" + "github.com/mholt/caddy/caddyhttp/httpserver" ) func init() { @@ -29,6 +29,8 @@ func init() { func setupIndex(c *caddy.Controller) error { var index []string + cfg := httpserver.GetConfig(c) + for c.Next() { args := c.RemainingArgs() @@ -40,7 +42,7 @@ func setupIndex(c *caddy.Controller) error { index = append(index, in) } - staticfiles.IndexPages = index + cfg.IndexPages = index } return nil diff --git a/caddyhttp/index/index_test.go b/caddyhttp/index/index_test.go index bedb2962..f0b3f41d 100644 --- a/caddyhttp/index/index_test.go +++ b/caddyhttp/index/index_test.go @@ -18,6 +18,7 @@ import ( "testing" "github.com/mholt/caddy" + "github.com/mholt/caddy/caddyhttp/httpserver" "github.com/mholt/caddy/caddyhttp/staticfiles" ) @@ -31,7 +32,7 @@ func TestIndexIncompleteParams(t *testing.T) { } func TestIndex(t *testing.T) { - c := caddy.NewTestController("", "index a.html b.html c.html") + c := caddy.NewTestController("http", "index a.html b.html c.html") err := setupIndex(c) if err != nil { @@ -40,14 +41,85 @@ func TestIndex(t *testing.T) { expectedIndex := []string{"a.html", "b.html", "c.html"} - if len(staticfiles.IndexPages) != 3 { - t.Errorf("Expected 3 values, got %v", len(staticfiles.IndexPages)) + siteConfig := httpserver.GetConfig(c) + + if len(siteConfig.IndexPages) != len(expectedIndex) { + t.Errorf("Expected 3 values, got %v", len(siteConfig.IndexPages)) } // Ensure ordering is correct - for i, actual := range staticfiles.IndexPages { + for i, actual := range siteConfig.IndexPages { if actual != expectedIndex[i] { t.Errorf("Expected value in position %d to be %v, got %v", i, expectedIndex[i], actual) } } } + +func TestMultiSiteIndexWithEitherHasDefault(t *testing.T) { + // TestIndex already covers the correctness of the directive + // when used on a single controller, so no need to verify test setupIndex again. + // This sets the stage for the actual verification. + customIndex := caddy.NewTestController("http", "index a.html b.html") + + // setupIndex against customIdx should not pollute the + // index list for other controllers. + err := setupIndex(customIndex) + if err != nil { + t.Errorf("Expected no errors, got: %v", err) + } + + // Represents a virtual host with no index directive. + defaultIndex := caddy.NewTestController("http", "") + + // Not calling setupIndex because it guards against lack of arguments, + // and we need to ensure the site gets the default set of index pages. + + siteConfig := httpserver.GetConfig(defaultIndex) + + // In case the index directive is not used, the virtual host + // should receive staticfiles.DefaultIndexPages slice. The length, as checked here, + // and the values, as checked in the upcoming loop, should match. + if len(siteConfig.IndexPages) != len(staticfiles.DefaultIndexPages) { + t.Errorf("Expected %d values, got %d", len(staticfiles.DefaultIndexPages), len(siteConfig.IndexPages)) + } + + // Ensure values match the expected default index pages + for i, actual := range siteConfig.IndexPages { + if actual != staticfiles.DefaultIndexPages[i] { + t.Errorf("Expected value in position %d to be %v, got %v", i, staticfiles.DefaultIndexPages[i], actual) + } + } +} + +func TestPerSiteIndexPageIsolation(t *testing.T) { + firstIndex := "first.html" + secondIndex := "second.html" + + // Create two sites with different index page configurations + firstSite := caddy.NewTestController("http", "index first.html") + err := setupIndex(firstSite) + if err != nil { + t.Errorf("Expected no errors, got: %v", err) + } + + secondSite := caddy.NewTestController("http", "index second.html") + err = setupIndex(secondSite) + if err != nil { + t.Errorf("Expected no errors, got: %v", err) + } + + firstSiteConfig := httpserver.GetConfig(firstSite) + if firstSiteConfig.IndexPages[0] != firstIndex { + t.Errorf("Expected index for first site as %s, received %s", firstIndex, firstSiteConfig.IndexPages[0]) + } + + secondSiteConfig := httpserver.GetConfig(secondSite) + if secondSiteConfig.IndexPages[0] != secondIndex { + t.Errorf("Expected index for second site as %s, received %s", secondIndex, secondSiteConfig.IndexPages[0]) + } + + // They should have different index pages, as per the provided config. + if firstSiteConfig.IndexPages[0] == secondSiteConfig.IndexPages[0] { + t.Errorf("Expected different index pages for both sites, got %s for first and %s for second", firstSiteConfig.IndexPages[0], secondSiteConfig.IndexPages[0]) + } +} diff --git a/caddyhttp/push/handler.go b/caddyhttp/push/handler.go index 36d414ff..4a676c27 100644 --- a/caddyhttp/push/handler.go +++ b/caddyhttp/push/handler.go @@ -19,7 +19,6 @@ import ( "strings" "github.com/mholt/caddy/caddyhttp/httpserver" - "github.com/mholt/caddy/caddyhttp/staticfiles" ) func (h Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { @@ -44,7 +43,7 @@ outer: matches := httpserver.Path(urlPath).Matches(rule.Path) // Also check IndexPages when requesting a directory if !matches { - indexFile, isIndexFile := httpserver.IndexFile(h.Root, urlPath, staticfiles.IndexPages) + indexFile, isIndexFile := httpserver.IndexFile(h.Root, urlPath, h.indexPages) if isIndexFile { matches = httpserver.Path(indexFile).Matches(rule.Path) } diff --git a/caddyhttp/push/handler_test.go b/caddyhttp/push/handler_test.go index 0055ae79..99bba416 100644 --- a/caddyhttp/push/handler_test.go +++ b/caddyhttp/push/handler_test.go @@ -393,7 +393,8 @@ func TestMiddlewareShouldPushIndexFile(t *testing.T) { {Path: "/index.css", Method: http.MethodGet}, }}, }, - Root: http.Dir(root), + Root: http.Dir(root), + indexPages: []string{indexFile}, } indexFilePath := filepath.Join(root, indexFile) diff --git a/caddyhttp/push/push.go b/caddyhttp/push/push.go index 9fdd48b8..5dc75c11 100644 --- a/caddyhttp/push/push.go +++ b/caddyhttp/push/push.go @@ -36,9 +36,10 @@ type ( // Middleware supports pushing resources to clients Middleware struct { - Next httpserver.Handler - Rules []Rule - Root http.FileSystem + Next httpserver.Handler + Rules []Rule + Root http.FileSystem + indexPages []string // will be injected from SiteConfig on setup } ruleOp func([]Resource) diff --git a/caddyhttp/push/setup.go b/caddyhttp/push/setup.go index bda4935a..a5037df3 100644 --- a/caddyhttp/push/setup.go +++ b/caddyhttp/push/setup.go @@ -50,7 +50,7 @@ func setup(c *caddy.Controller) error { cfg := httpserver.GetConfig(c) cfg.AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - return Middleware{Next: next, Rules: rules, Root: http.Dir(cfg.Root)} + return Middleware{Next: next, Rules: rules, Root: http.Dir(cfg.Root), indexPages: cfg.IndexPages} }) return nil diff --git a/caddyhttp/staticfiles/fileserver.go b/caddyhttp/staticfiles/fileserver.go index 4f77f1db..2b38212e 100644 --- a/caddyhttp/staticfiles/fileserver.go +++ b/caddyhttp/staticfiles/fileserver.go @@ -45,6 +45,10 @@ import ( type FileServer struct { Root http.FileSystem // jailed access to the file system Hide []string // list of files for which to respond with "Not Found" + + // A list of pages that may be understood as the "index" files to directories. + // Injected from *SiteConfig. + IndexPages []string } // ServeHTTP serves static files for r according to fs's configuration. @@ -118,7 +122,7 @@ func (fs FileServer) serveFile(w http.ResponseWriter, r *http.Request) (int, err // if an index file was explicitly requested, strip file name from the request // ("/foo/index.html" -> "/foo/") var requestPage = path.Base(urlCopy.Path) - for _, indexPage := range IndexPages { + for _, indexPage := range fs.IndexPages { if requestPage == indexPage { urlCopy.Path = urlCopy.Path[:len(urlCopy.Path)-len(indexPage)] redir = true @@ -134,7 +138,7 @@ func (fs FileServer) serveFile(w http.ResponseWriter, r *http.Request) (int, err // use contents of an index file, if present, for directory requests if d.IsDir() { - for _, indexPage := range IndexPages { + for _, indexPage := range fs.IndexPages { indexPath := path.Join(reqPath, indexPage) indexFile, err := fs.Root.Open(indexPath) if err != nil { @@ -253,9 +257,9 @@ func calculateEtag(d os.FileInfo) string { return `"` + t + s + `"` } -// IndexPages is a list of pages that may be understood as +// DefaultIndexPages is a list of pages that may be understood as // the "index" files to directories. -var IndexPages = []string{ +var DefaultIndexPages = []string{ "index.html", "index.htm", "index.txt", diff --git a/caddyhttp/staticfiles/fileserver_test.go b/caddyhttp/staticfiles/fileserver_test.go index 95d315d3..9cce7705 100644 --- a/caddyhttp/staticfiles/fileserver_test.go +++ b/caddyhttp/staticfiles/fileserver_test.go @@ -36,8 +36,9 @@ func TestServeHTTP(t *testing.T) { defer afterServeHTTPTest(t, tmpWebRootDir) fileserver := FileServer{ - Root: http.Dir(filepath.Join(tmpWebRootDir, webrootName)), - Hide: []string{"dir/hidden.html"}, + Root: http.Dir(filepath.Join(tmpWebRootDir, webrootName)), + Hide: []string{"dir/hidden.html"}, + IndexPages: DefaultIndexPages, } movedPermanently := "Moved Permanently"