Implement per-site index (#1906)

This commit is contained in:
Mohammed Al Sahaf 2017-10-30 00:13:10 +03:00 committed by Toby Allen
parent fc75527eb5
commit f7a70266ed
13 changed files with 110 additions and 24 deletions

View file

@ -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

View file

@ -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

View file

@ -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
}

View file

@ -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)
}

View file

@ -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

View file

@ -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

View file

@ -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])
}
}

View file

@ -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)
}

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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",

View file

@ -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"