mirror of
https://github.com/caddyserver/caddy.git
synced 2024-11-29 12:16:16 +08:00
Added the Context to the browse directive
Moved the Context type to middleware and exported it. Users can use .Include and others in browse directive templating Created test for the templates directive.
This commit is contained in:
parent
fcf2622c26
commit
f536bc94b2
|
@ -2,8 +2,8 @@ package setup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
"github.com/mholt/caddy/middleware"
|
"github.com/mholt/caddy/middleware"
|
||||||
"github.com/mholt/caddy/middleware/browse"
|
"github.com/mholt/caddy/middleware/browse"
|
||||||
|
|
|
@ -5,13 +5,13 @@ package browse
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"html/template"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
|
@ -51,6 +51,8 @@ type Listing struct {
|
||||||
|
|
||||||
// And which order
|
// And which order
|
||||||
Order string
|
Order string
|
||||||
|
|
||||||
|
middleware.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileInfo is the info about a particular file or directory
|
// FileInfo is the info about a particular file or directory
|
||||||
|
@ -135,8 +137,9 @@ var IndexPages = []string{
|
||||||
"default.htm",
|
"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 fileinfos []FileInfo
|
||||||
|
var urlPath = r.URL.Path
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
name := f.Name()
|
name := f.Name()
|
||||||
|
|
||||||
|
@ -168,6 +171,11 @@ func directoryListing(files []os.FileInfo, urlPath string, canGoUp bool) (Listin
|
||||||
Path: urlPath,
|
Path: urlPath,
|
||||||
CanGoUp: canGoUp,
|
CanGoUp: canGoUp,
|
||||||
Items: fileinfos,
|
Items: fileinfos,
|
||||||
|
Context: middleware.Context{
|
||||||
|
Root: http.Dir(root),
|
||||||
|
Req: r,
|
||||||
|
URL: r.URL,
|
||||||
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +230,7 @@ func (b Browse) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Assemble listing of directory contents
|
// 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
|
if err != nil { // directory isn't browsable
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package templates
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -8,23 +8,22 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mholt/caddy/middleware"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// This file contains the context and functions available for
|
// This file contains the context and functions available for
|
||||||
// use in the templates.
|
// use in the templates.
|
||||||
|
|
||||||
// context is the context with which templates are executed.
|
// context is the context with which templates are executed.
|
||||||
type context struct {
|
type Context struct {
|
||||||
root http.FileSystem
|
Root http.FileSystem
|
||||||
req *http.Request
|
Req *http.Request
|
||||||
URL *url.URL
|
// This is used to access information about the URL.
|
||||||
|
URL *url.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
// Include returns the contents of filename relative to the site root
|
// Include returns the contents of filename relative to the site root
|
||||||
func (c context) Include(filename string) (string, error) {
|
func (c Context) Include(filename string) (string, error) {
|
||||||
file, err := c.root.Open(filename)
|
file, err := c.Root.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -50,13 +49,13 @@ func (c context) Include(filename string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Date returns the current timestamp in the specified format
|
// 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)
|
return time.Now().Format(format)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cookie gets the value of a cookie with name name.
|
// Cookie gets the value of a cookie with name name.
|
||||||
func (c context) Cookie(name string) string {
|
func (c Context) Cookie(name string) string {
|
||||||
cookies := c.req.Cookies()
|
cookies := c.Req.Cookies()
|
||||||
for _, cookie := range cookies {
|
for _, cookie := range cookies {
|
||||||
if cookie.Name == name {
|
if cookie.Name == name {
|
||||||
return cookie.Value
|
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.
|
// Header gets the value of a request header with field name.
|
||||||
func (c context) Header(name string) string {
|
func (c Context) Header(name string) string {
|
||||||
return c.req.Header.Get(name)
|
return c.Req.Header.Get(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IP gets the (remote) IP address of the client making the request.
|
// IP gets the (remote) IP address of the client making the request.
|
||||||
func (c context) IP() string {
|
func (c Context) IP() string {
|
||||||
ip, _, err := net.SplitHostPort(c.req.RemoteAddr)
|
ip, _, err := net.SplitHostPort(c.Req.RemoteAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.req.RemoteAddr
|
return c.Req.RemoteAddr
|
||||||
}
|
}
|
||||||
return ip
|
return ip
|
||||||
}
|
}
|
||||||
|
@ -82,14 +81,14 @@ func (c context) IP() string {
|
||||||
// URI returns the raw, unprocessed request URI (including query
|
// URI returns the raw, unprocessed request URI (including query
|
||||||
// string and hash) obtained directly from the Request-Line of
|
// string and hash) obtained directly from the Request-Line of
|
||||||
// the HTTP request.
|
// the HTTP request.
|
||||||
func (c context) URI() string {
|
func (c Context) URI() string {
|
||||||
return c.req.RequestURI
|
return c.Req.RequestURI
|
||||||
}
|
}
|
||||||
|
|
||||||
// Host returns the hostname portion of the Host header
|
// Host returns the hostname portion of the Host header
|
||||||
// from the HTTP request.
|
// from the HTTP request.
|
||||||
func (c context) Host() (string, error) {
|
func (c Context) Host() (string, error) {
|
||||||
host, _, err := net.SplitHostPort(c.req.Host)
|
host, _, err := net.SplitHostPort(c.Req.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -97,8 +96,8 @@ func (c context) Host() (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Port returns the port portion of the Host header if specified.
|
// Port returns the port portion of the Host header if specified.
|
||||||
func (c context) Port() (string, error) {
|
func (c Context) Port() (string, error) {
|
||||||
_, port, err := net.SplitHostPort(c.req.Host)
|
_, port, err := net.SplitHostPort(c.Req.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -106,12 +105,12 @@ func (c context) Port() (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method returns the method (GET, POST, etc.) of the request.
|
// Method returns the method (GET, POST, etc.) of the request.
|
||||||
func (c context) Method() string {
|
func (c Context) Method() string {
|
||||||
return c.req.Method
|
return c.Req.Method
|
||||||
}
|
}
|
||||||
|
|
||||||
// PathMatches returns true if the path portion of the request
|
// PathMatches returns true if the path portion of the request
|
||||||
// URL matches pattern.
|
// URL matches pattern.
|
||||||
func (c context) PathMatches(pattern string) bool {
|
func (c Context) PathMatches(pattern string) bool {
|
||||||
return middleware.Path(c.req.URL.Path).Matches(pattern)
|
return Path(c.Req.URL.Path).Matches(pattern)
|
||||||
}
|
}
|
1
middleware/templates/header.html
Normal file
1
middleware/templates/header.html
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<h1>Header title</h1>
|
1
middleware/templates/images/header.html
Normal file
1
middleware/templates/images/header.html
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<h1>Header title</h1>
|
1
middleware/templates/images/img.htm
Normal file
1
middleware/templates/images/img.htm
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<!DOCTYPE html><html><head><title>img</title></head><body>{{.Include "header.html"}}</body></html>
|
1
middleware/templates/photos/test.html
Normal file
1
middleware/templates/photos/test.html
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<!DOCTYPE html><html><head><title>test page</title></head><body>{{.Include "../header.html"}}</body></html>
|
1
middleware/templates/root.html
Normal file
1
middleware/templates/root.html
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<!DOCTYPE html><html><head><title>root</title></head><body>{{.Include "header.html"}}</body></html>
|
|
@ -31,7 +31,7 @@ func (t Templates) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
|
||||||
for _, ext := range rule.Extensions {
|
for _, ext := range rule.Extensions {
|
||||||
if reqExt == ext {
|
if reqExt == ext {
|
||||||
// Create execution context
|
// 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
|
// Build the template
|
||||||
tpl, err := template.ParseFiles(filepath.Join(t.Root, fpath))
|
tpl, err := template.ParseFiles(filepath.Join(t.Root, fpath))
|
||||||
|
|
121
middleware/templates/templates_test.go
Normal file
121
middleware/templates/templates_test.go
Normal file
|
@ -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 := `<!DOCTYPE html><html><head><title>test page</title></head><body><h1>Header title</h1>
|
||||||
|
</body></html>
|
||||||
|
`
|
||||||
|
|
||||||
|
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 = `<!DOCTYPE html><html><head><title>img</title></head><body><h1>Header title</h1>
|
||||||
|
</body></html>
|
||||||
|
`
|
||||||
|
|
||||||
|
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 = `<!DOCTYPE html><html><head><title>root</title></head><body><h1>Header title</h1>
|
||||||
|
</body></html>
|
||||||
|
`
|
||||||
|
|
||||||
|
if respBody != expectedBody {
|
||||||
|
t.Fatalf("Test: the expected body %v is different from the response one: %v", expectedBody, respBody)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user