mirror of
https://github.com/caddyserver/caddy.git
synced 2025-05-24 02:02:26 -04:00
Implement per-site index (#1906)
This commit is contained in:
parent
fc75527eb5
commit
f7a70266ed
@ -252,7 +252,7 @@ func directoryListing(files []os.FileInfo, canGoUp bool, urlPath string, config
|
|||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
name := f.Name()
|
name := f.Name()
|
||||||
|
|
||||||
for _, indexName := range staticfiles.IndexPages {
|
for _, indexName := range config.Fs.IndexPages {
|
||||||
if name == indexName {
|
if name == indexName {
|
||||||
hasIndexFile = true
|
hasIndexFile = true
|
||||||
break
|
break
|
||||||
|
@ -78,8 +78,9 @@ func browseParse(c *caddy.Controller) ([]Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bc.Fs = staticfiles.FileServer{
|
bc.Fs = staticfiles.FileServer{
|
||||||
Root: http.Dir(cfg.Root),
|
Root: http.Dir(cfg.Root),
|
||||||
Hide: cfg.HiddenFiles,
|
Hide: cfg.HiddenFiles,
|
||||||
|
IndexPages: cfg.IndexPages,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second argument would be the template file to use
|
// Second argument would be the template file to use
|
||||||
|
@ -27,6 +27,7 @@ import (
|
|||||||
|
|
||||||
"github.com/mholt/caddy"
|
"github.com/mholt/caddy"
|
||||||
"github.com/mholt/caddy/caddyfile"
|
"github.com/mholt/caddy/caddyfile"
|
||||||
|
"github.com/mholt/caddy/caddyhttp/staticfiles"
|
||||||
"github.com/mholt/caddy/caddytls"
|
"github.com/mholt/caddy/caddytls"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -155,6 +156,7 @@ func (h *httpContext) InspectServerBlocks(sourceFile string, serverBlocks []cadd
|
|||||||
AltTLSSNIPort: altTLSSNIPort,
|
AltTLSSNIPort: altTLSSNIPort,
|
||||||
},
|
},
|
||||||
originCaddyfile: sourceFile,
|
originCaddyfile: sourceFile,
|
||||||
|
IndexPages: staticfiles.DefaultIndexPages,
|
||||||
}
|
}
|
||||||
h.saveConfig(key, cfg)
|
h.saveConfig(key, cfg)
|
||||||
}
|
}
|
||||||
@ -234,7 +236,7 @@ func GetConfig(c *caddy.Controller) *SiteConfig {
|
|||||||
// we should only get here during tests because directive
|
// we should only get here during tests because directive
|
||||||
// actions typically skip the server blocks where we make
|
// actions typically skip the server blocks where we make
|
||||||
// the configs
|
// 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)
|
ctx.saveConfig(key, cfg)
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
@ -142,7 +142,7 @@ func NewServer(addr string, group []*SiteConfig) (*Server, error) {
|
|||||||
|
|
||||||
// Compile custom middleware for every site (enables virtual hosting)
|
// Compile custom middleware for every site (enables virtual hosting)
|
||||||
for _, site := range group {
|
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-- {
|
for i := len(site.middleware) - 1; i >= 0; i-- {
|
||||||
stack = site.middleware[i](stack)
|
stack = site.middleware[i](stack)
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,9 @@ type SiteConfig struct {
|
|||||||
// The address of the site
|
// The address of the site
|
||||||
Addr Address
|
Addr Address
|
||||||
|
|
||||||
|
// The list of viable index page names of the site
|
||||||
|
IndexPages []string
|
||||||
|
|
||||||
// The hostname to bind listener to;
|
// The hostname to bind listener to;
|
||||||
// defaults to Addr.Host
|
// defaults to Addr.Host
|
||||||
ListenHost string
|
ListenHost string
|
||||||
|
@ -16,7 +16,7 @@ package index
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/mholt/caddy"
|
"github.com/mholt/caddy"
|
||||||
"github.com/mholt/caddy/caddyhttp/staticfiles"
|
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -29,6 +29,8 @@ func init() {
|
|||||||
func setupIndex(c *caddy.Controller) error {
|
func setupIndex(c *caddy.Controller) error {
|
||||||
var index []string
|
var index []string
|
||||||
|
|
||||||
|
cfg := httpserver.GetConfig(c)
|
||||||
|
|
||||||
for c.Next() {
|
for c.Next() {
|
||||||
args := c.RemainingArgs()
|
args := c.RemainingArgs()
|
||||||
|
|
||||||
@ -40,7 +42,7 @@ func setupIndex(c *caddy.Controller) error {
|
|||||||
index = append(index, in)
|
index = append(index, in)
|
||||||
}
|
}
|
||||||
|
|
||||||
staticfiles.IndexPages = index
|
cfg.IndexPages = index
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/mholt/caddy"
|
"github.com/mholt/caddy"
|
||||||
|
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||||
"github.com/mholt/caddy/caddyhttp/staticfiles"
|
"github.com/mholt/caddy/caddyhttp/staticfiles"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -31,7 +32,7 @@ func TestIndexIncompleteParams(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIndex(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)
|
err := setupIndex(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -40,14 +41,85 @@ func TestIndex(t *testing.T) {
|
|||||||
|
|
||||||
expectedIndex := []string{"a.html", "b.html", "c.html"}
|
expectedIndex := []string{"a.html", "b.html", "c.html"}
|
||||||
|
|
||||||
if len(staticfiles.IndexPages) != 3 {
|
siteConfig := httpserver.GetConfig(c)
|
||||||
t.Errorf("Expected 3 values, got %v", len(staticfiles.IndexPages))
|
|
||||||
|
if len(siteConfig.IndexPages) != len(expectedIndex) {
|
||||||
|
t.Errorf("Expected 3 values, got %v", len(siteConfig.IndexPages))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure ordering is correct
|
// Ensure ordering is correct
|
||||||
for i, actual := range staticfiles.IndexPages {
|
for i, actual := range siteConfig.IndexPages {
|
||||||
if actual != expectedIndex[i] {
|
if actual != expectedIndex[i] {
|
||||||
t.Errorf("Expected value in position %d to be %v, got %v", i, expectedIndex[i], actual)
|
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"
|
"strings"
|
||||||
|
|
||||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||||
"github.com/mholt/caddy/caddyhttp/staticfiles"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
func (h Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||||
@ -44,7 +43,7 @@ outer:
|
|||||||
matches := httpserver.Path(urlPath).Matches(rule.Path)
|
matches := httpserver.Path(urlPath).Matches(rule.Path)
|
||||||
// Also check IndexPages when requesting a directory
|
// Also check IndexPages when requesting a directory
|
||||||
if !matches {
|
if !matches {
|
||||||
indexFile, isIndexFile := httpserver.IndexFile(h.Root, urlPath, staticfiles.IndexPages)
|
indexFile, isIndexFile := httpserver.IndexFile(h.Root, urlPath, h.indexPages)
|
||||||
if isIndexFile {
|
if isIndexFile {
|
||||||
matches = httpserver.Path(indexFile).Matches(rule.Path)
|
matches = httpserver.Path(indexFile).Matches(rule.Path)
|
||||||
}
|
}
|
||||||
|
@ -393,7 +393,8 @@ func TestMiddlewareShouldPushIndexFile(t *testing.T) {
|
|||||||
{Path: "/index.css", Method: http.MethodGet},
|
{Path: "/index.css", Method: http.MethodGet},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
Root: http.Dir(root),
|
Root: http.Dir(root),
|
||||||
|
indexPages: []string{indexFile},
|
||||||
}
|
}
|
||||||
|
|
||||||
indexFilePath := filepath.Join(root, indexFile)
|
indexFilePath := filepath.Join(root, indexFile)
|
||||||
|
@ -36,9 +36,10 @@ type (
|
|||||||
|
|
||||||
// Middleware supports pushing resources to clients
|
// Middleware supports pushing resources to clients
|
||||||
Middleware struct {
|
Middleware struct {
|
||||||
Next httpserver.Handler
|
Next httpserver.Handler
|
||||||
Rules []Rule
|
Rules []Rule
|
||||||
Root http.FileSystem
|
Root http.FileSystem
|
||||||
|
indexPages []string // will be injected from SiteConfig on setup
|
||||||
}
|
}
|
||||||
|
|
||||||
ruleOp func([]Resource)
|
ruleOp func([]Resource)
|
||||||
|
@ -50,7 +50,7 @@ func setup(c *caddy.Controller) error {
|
|||||||
|
|
||||||
cfg := httpserver.GetConfig(c)
|
cfg := httpserver.GetConfig(c)
|
||||||
cfg.AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
|
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
|
return nil
|
||||||
|
@ -45,6 +45,10 @@ import (
|
|||||||
type FileServer struct {
|
type FileServer struct {
|
||||||
Root http.FileSystem // jailed access to the file system
|
Root http.FileSystem // jailed access to the file system
|
||||||
Hide []string // list of files for which to respond with "Not Found"
|
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.
|
// 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
|
// if an index file was explicitly requested, strip file name from the request
|
||||||
// ("/foo/index.html" -> "/foo/")
|
// ("/foo/index.html" -> "/foo/")
|
||||||
var requestPage = path.Base(urlCopy.Path)
|
var requestPage = path.Base(urlCopy.Path)
|
||||||
for _, indexPage := range IndexPages {
|
for _, indexPage := range fs.IndexPages {
|
||||||
if requestPage == indexPage {
|
if requestPage == indexPage {
|
||||||
urlCopy.Path = urlCopy.Path[:len(urlCopy.Path)-len(indexPage)]
|
urlCopy.Path = urlCopy.Path[:len(urlCopy.Path)-len(indexPage)]
|
||||||
redir = true
|
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
|
// use contents of an index file, if present, for directory requests
|
||||||
if d.IsDir() {
|
if d.IsDir() {
|
||||||
for _, indexPage := range IndexPages {
|
for _, indexPage := range fs.IndexPages {
|
||||||
indexPath := path.Join(reqPath, indexPage)
|
indexPath := path.Join(reqPath, indexPage)
|
||||||
indexFile, err := fs.Root.Open(indexPath)
|
indexFile, err := fs.Root.Open(indexPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -253,9 +257,9 @@ func calculateEtag(d os.FileInfo) string {
|
|||||||
return `"` + t + s + `"`
|
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.
|
// the "index" files to directories.
|
||||||
var IndexPages = []string{
|
var DefaultIndexPages = []string{
|
||||||
"index.html",
|
"index.html",
|
||||||
"index.htm",
|
"index.htm",
|
||||||
"index.txt",
|
"index.txt",
|
||||||
|
@ -36,8 +36,9 @@ func TestServeHTTP(t *testing.T) {
|
|||||||
defer afterServeHTTPTest(t, tmpWebRootDir)
|
defer afterServeHTTPTest(t, tmpWebRootDir)
|
||||||
|
|
||||||
fileserver := FileServer{
|
fileserver := FileServer{
|
||||||
Root: http.Dir(filepath.Join(tmpWebRootDir, webrootName)),
|
Root: http.Dir(filepath.Join(tmpWebRootDir, webrootName)),
|
||||||
Hide: []string{"dir/hidden.html"},
|
Hide: []string{"dir/hidden.html"},
|
||||||
|
IndexPages: DefaultIndexPages,
|
||||||
}
|
}
|
||||||
|
|
||||||
movedPermanently := "Moved Permanently"
|
movedPermanently := "Moved Permanently"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user