diff --git a/config/setup/browse.go b/config/setup/browse.go index 7db8b40ac..a98ec313d 100644 --- a/config/setup/browse.go +++ b/config/setup/browse.go @@ -2,8 +2,8 @@ package setup import ( "fmt" - "html/template" "io/ioutil" + "text/template" "github.com/mholt/caddy/middleware" "github.com/mholt/caddy/middleware/browse" diff --git a/middleware/browse/browse.go b/middleware/browse/browse.go index c0090acd6..5caeb5ac5 100644 --- a/middleware/browse/browse.go +++ b/middleware/browse/browse.go @@ -5,13 +5,13 @@ package browse import ( "bytes" "errors" - "html/template" "net/http" "net/url" "os" "path" "sort" "strings" + "text/template" "time" "github.com/dustin/go-humanize" @@ -51,6 +51,8 @@ type Listing struct { // And which order Order string + + middleware.Context } // FileInfo is the info about a particular file or directory @@ -135,8 +137,9 @@ var IndexPages = []string{ "default.htm", } -func directoryListing(files []os.FileInfo, urlPath string, canGoUp bool) (Listing, error) { +func directoryListing(files []os.FileInfo, r *http.Request, canGoUp bool, root string) (Listing, error) { var fileinfos []FileInfo + var urlPath = r.URL.Path for _, f := range files { name := f.Name() @@ -168,6 +171,11 @@ func directoryListing(files []os.FileInfo, urlPath string, canGoUp bool) (Listin Path: urlPath, CanGoUp: canGoUp, Items: fileinfos, + Context: middleware.Context{ + Root: http.Dir(root), + Req: r, + URL: r.URL, + }, }, nil } @@ -222,7 +230,7 @@ func (b Browse) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { } } // Assemble listing of directory contents - listing, err := directoryListing(files, r.URL.Path, canGoUp) + listing, err := directoryListing(files, r, canGoUp, b.Root) if err != nil { // directory isn't browsable continue } diff --git a/middleware/templates/context.go b/middleware/context.go similarity index 63% rename from middleware/templates/context.go rename to middleware/context.go index 4f125c991..508ec6042 100644 --- a/middleware/templates/context.go +++ b/middleware/context.go @@ -1,4 +1,4 @@ -package templates +package middleware import ( "bytes" @@ -8,23 +8,22 @@ import ( "net/url" "text/template" "time" - - "github.com/mholt/caddy/middleware" ) // This file contains the context and functions available for // use in the templates. // context is the context with which templates are executed. -type context struct { - root http.FileSystem - req *http.Request - URL *url.URL +type Context struct { + Root http.FileSystem + Req *http.Request + // This is used to access information about the URL. + URL *url.URL } // Include returns the contents of filename relative to the site root -func (c context) Include(filename string) (string, error) { - file, err := c.root.Open(filename) +func (c Context) Include(filename string) (string, error) { + file, err := c.Root.Open(filename) if err != nil { return "", err } @@ -50,13 +49,13 @@ func (c context) Include(filename string) (string, error) { } // Date returns the current timestamp in the specified format -func (c context) Date(format string) string { +func (c Context) Date(format string) string { return time.Now().Format(format) } // Cookie gets the value of a cookie with name name. -func (c context) Cookie(name string) string { - cookies := c.req.Cookies() +func (c Context) Cookie(name string) string { + cookies := c.Req.Cookies() for _, cookie := range cookies { if cookie.Name == name { return cookie.Value @@ -66,15 +65,15 @@ func (c context) Cookie(name string) string { } // Header gets the value of a request header with field name. -func (c context) Header(name string) string { - return c.req.Header.Get(name) +func (c Context) Header(name string) string { + return c.Req.Header.Get(name) } // IP gets the (remote) IP address of the client making the request. -func (c context) IP() string { - ip, _, err := net.SplitHostPort(c.req.RemoteAddr) +func (c Context) IP() string { + ip, _, err := net.SplitHostPort(c.Req.RemoteAddr) if err != nil { - return c.req.RemoteAddr + return c.Req.RemoteAddr } return ip } @@ -82,14 +81,14 @@ func (c context) IP() string { // URI returns the raw, unprocessed request URI (including query // string and hash) obtained directly from the Request-Line of // the HTTP request. -func (c context) URI() string { - return c.req.RequestURI +func (c Context) URI() string { + return c.Req.RequestURI } // Host returns the hostname portion of the Host header // from the HTTP request. -func (c context) Host() (string, error) { - host, _, err := net.SplitHostPort(c.req.Host) +func (c Context) Host() (string, error) { + host, _, err := net.SplitHostPort(c.Req.Host) if err != nil { return "", err } @@ -97,8 +96,8 @@ func (c context) Host() (string, error) { } // Port returns the port portion of the Host header if specified. -func (c context) Port() (string, error) { - _, port, err := net.SplitHostPort(c.req.Host) +func (c Context) Port() (string, error) { + _, port, err := net.SplitHostPort(c.Req.Host) if err != nil { return "", err } @@ -106,12 +105,12 @@ func (c context) Port() (string, error) { } // Method returns the method (GET, POST, etc.) of the request. -func (c context) Method() string { - return c.req.Method +func (c Context) Method() string { + return c.Req.Method } // PathMatches returns true if the path portion of the request // URL matches pattern. -func (c context) PathMatches(pattern string) bool { - return middleware.Path(c.req.URL.Path).Matches(pattern) +func (c Context) PathMatches(pattern string) bool { + return Path(c.Req.URL.Path).Matches(pattern) } diff --git a/middleware/templates/header.html b/middleware/templates/header.html new file mode 100644 index 000000000..9c96e0e37 --- /dev/null +++ b/middleware/templates/header.html @@ -0,0 +1 @@ +

Header title

diff --git a/middleware/templates/images/header.html b/middleware/templates/images/header.html new file mode 100644 index 000000000..9c96e0e37 --- /dev/null +++ b/middleware/templates/images/header.html @@ -0,0 +1 @@ +

Header title

diff --git a/middleware/templates/images/img.htm b/middleware/templates/images/img.htm new file mode 100644 index 000000000..865a73809 --- /dev/null +++ b/middleware/templates/images/img.htm @@ -0,0 +1 @@ +img{{.Include "header.html"}} diff --git a/middleware/templates/photos/test.html b/middleware/templates/photos/test.html new file mode 100644 index 000000000..e2e95e133 --- /dev/null +++ b/middleware/templates/photos/test.html @@ -0,0 +1 @@ +test page{{.Include "../header.html"}} diff --git a/middleware/templates/root.html b/middleware/templates/root.html new file mode 100644 index 000000000..e1720e726 --- /dev/null +++ b/middleware/templates/root.html @@ -0,0 +1 @@ +root{{.Include "header.html"}} diff --git a/middleware/templates/templates.go b/middleware/templates/templates.go index fc4f8242e..a699d0026 100644 --- a/middleware/templates/templates.go +++ b/middleware/templates/templates.go @@ -31,7 +31,7 @@ func (t Templates) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error for _, ext := range rule.Extensions { if reqExt == ext { // Create execution context - ctx := context{root: t.FileSys, req: r, URL: r.URL} + ctx := middleware.Context{Root: t.FileSys, Req: r, URL: r.URL} // Build the template tpl, err := template.ParseFiles(filepath.Join(t.Root, fpath)) diff --git a/middleware/templates/templates_test.go b/middleware/templates/templates_test.go new file mode 100644 index 000000000..a519de99e --- /dev/null +++ b/middleware/templates/templates_test.go @@ -0,0 +1,121 @@ +package templates + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/mholt/caddy/middleware" +) + +func Test(t *testing.T) { + tmpl := Templates{ + Next: middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { + return 0, nil + }), + Rules: []Rule{ + Rule{ + Extensions: []string{".html"}, + IndexFiles: []string{"index.html"}, + Path: "/photos", + }, + Rule{ + Extensions: []string{".html", ".htm"}, + IndexFiles: []string{"index.html", "index.htm"}, + Path: "/images", + }, + }, + Root: ".", + FileSys: http.Dir("."), + } + + tmplroot := Templates{ + Next: middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { + return 0, nil + }), + Rules: []Rule{ + Rule{ + Extensions: []string{".html"}, + IndexFiles: []string{"index.html"}, + Path: "/", + }, + }, + Root: ".", + FileSys: http.Dir("."), + } + + /* + * Test tmpl on /photos/test.html + */ + req, err := http.NewRequest("GET", "/photos/test.html", nil) + if err != nil { + t.Fatalf("Test: Could not create HTTP request: %v", err) + } + + rec := httptest.NewRecorder() + + tmpl.ServeHTTP(rec, req) + + if rec.Code != http.StatusOK { + t.Fatalf("Test: Wrong response code: %d, should be %d", rec.Code, http.StatusOK) + } + + respBody := rec.Body.String() + expectedBody := `test page

Header title

+ +` + + if respBody != expectedBody { + t.Fatalf("Test: the expected body %v is different from the response one: %v", expectedBody, respBody) + } + + /* + * Test tmpl on /images/img.htm + */ + req, err = http.NewRequest("GET", "/images/img.htm", nil) + if err != nil { + t.Fatalf("Could not create HTTP request: %v", err) + } + + rec = httptest.NewRecorder() + + tmpl.ServeHTTP(rec, req) + + if rec.Code != http.StatusOK { + t.Fatalf("Test: Wrong response code: %d, should be %d", rec.Code, http.StatusOK) + } + + respBody = rec.Body.String() + expectedBody = `img

Header title

+ +` + + if respBody != expectedBody { + t.Fatalf("Test: the expected body %v is different from the response one: %v", expectedBody, respBody) + } + + /* + * Test tmplroot on /root.html + */ + req, err = http.NewRequest("GET", "/root.html", nil) + if err != nil { + t.Fatalf("Could not create HTTP request: %v", err) + } + + rec = httptest.NewRecorder() + + tmplroot.ServeHTTP(rec, req) + + if rec.Code != http.StatusOK { + t.Fatalf("Test: Wrong response code: %d, should be %d", rec.Code, http.StatusOK) + } + + respBody = rec.Body.String() + expectedBody = `root

Header title

+ +` + + if respBody != expectedBody { + t.Fatalf("Test: the expected body %v is different from the response one: %v", expectedBody, respBody) + } +}