mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-10-26 08:12:43 -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 | ||||||
|  | |||||||
| @ -80,6 +80,7 @@ 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) | ||||||
| 			} | 			} | ||||||
|  | |||||||
| @ -394,6 +394,7 @@ func TestMiddlewareShouldPushIndexFile(t *testing.T) { | |||||||
| 			}}, | 			}}, | ||||||
| 		}, | 		}, | ||||||
| 		Root:       http.Dir(root), | 		Root:       http.Dir(root), | ||||||
|  | 		indexPages: []string{indexFile}, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	indexFilePath := filepath.Join(root, indexFile) | 	indexFilePath := filepath.Join(root, indexFile) | ||||||
|  | |||||||
| @ -39,6 +39,7 @@ type ( | |||||||
| 		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", | ||||||
|  | |||||||
| @ -38,6 +38,7 @@ func TestServeHTTP(t *testing.T) { | |||||||
| 	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