mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-27 22:23:48 +03:00
Implement per-site index (#1906)
This commit is contained in:
parent
fc75527eb5
commit
f7a70266ed
13 changed files with 110 additions and 24 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue