1
0
mirror of https://github.com/labstack/echo.git synced 2024-11-28 08:38:39 +02:00

Added directory autoindex

Signed-off-by: Vishal Rana <vr@labstack.com>
This commit is contained in:
Vishal Rana 2015-11-23 20:33:13 -08:00
parent 9d11990cbb
commit d932e2a863
3 changed files with 91 additions and 8 deletions

View File

@ -243,7 +243,7 @@ func (c *Context) File(path, name string, attachment bool) (err error) {
if attachment { if attachment {
c.response.Header().Set(ContentDisposition, "attachment; filename="+name) c.response.Header().Set(ContentDisposition, "attachment; filename="+name)
} }
if err = serveFile(dir, file, c); err != nil { if err = c.echo.serveFile(dir, file, c); err != nil {
c.response.Header().Del(ContentDisposition) c.response.Header().Del(ContentDisposition)
} }
return return

79
echo.go
View File

@ -12,6 +12,7 @@ import (
"runtime" "runtime"
"strings" "strings"
"sync" "sync"
"time"
"encoding/xml" "encoding/xml"
@ -34,6 +35,7 @@ type (
pool sync.Pool pool sync.Pool
debug bool debug bool
hook http.HandlerFunc hook http.HandlerFunc
autoIndex bool
router *Router router *Router
} }
@ -142,7 +144,7 @@ const (
WebSocket = "websocket" WebSocket = "websocket"
indexFile = "index.html" indexPage = "index.html"
) )
var ( var (
@ -178,6 +180,8 @@ var (
return NewHTTPError(http.StatusMethodNotAllowed) return NewHTTPError(http.StatusMethodNotAllowed)
} }
unixEpochTime = time.Unix(0, 0)
logger = log.New("echo") logger = log.New("echo")
) )
@ -269,6 +273,12 @@ func (e *Echo) Debug() bool {
return e.debug return e.debug
} }
// AutoIndex enables automatically creates a directory listing if the directory
// doesn't contain an index page.
func (e *Echo) AutoIndex(on bool) {
e.autoIndex = on
}
// Hook registers a callback which is invoked from `Echo#ServerHTTP` as the first // Hook registers a callback which is invoked from `Echo#ServerHTTP` as the first
// statement. Hook is useful if you want to modify response/response objects even // statement. Hook is useful if you want to modify response/response objects even
// before it hits the router or any middleware. // before it hits the router or any middleware.
@ -386,7 +396,7 @@ func (e *Echo) Static(path, dir string) {
// ServeDir serves files from a directory. // ServeDir serves files from a directory.
func (e *Echo) ServeDir(path, dir string) { func (e *Echo) ServeDir(path, dir string) {
e.Get(path+"*", func(c *Context) error { e.Get(path+"*", func(c *Context) error {
return serveFile(dir, c.P(0), c) // Param `_*` return e.serveFile(dir, c.P(0), c) // Param `_*`
}) })
} }
@ -394,11 +404,11 @@ func (e *Echo) ServeDir(path, dir string) {
func (e *Echo) ServeFile(path, file string) { func (e *Echo) ServeFile(path, file string) {
e.Get(path, func(c *Context) error { e.Get(path, func(c *Context) error {
dir, file := filepath.Split(file) dir, file := filepath.Split(file)
return serveFile(dir, file, c) return e.serveFile(dir, file, c)
}) })
} }
func serveFile(dir, file string, c *Context) error { func (e *Echo) serveFile(dir, file string, c *Context) (err error) {
fs := http.Dir(dir) fs := http.Dir(dir)
f, err := fs.Open(file) f, err := fs.Open(file)
if err != nil { if err != nil {
@ -408,16 +418,49 @@ func serveFile(dir, file string, c *Context) error {
fi, _ := f.Stat() fi, _ := f.Stat()
if fi.IsDir() { if fi.IsDir() {
file = filepath.Join(file, indexFile) if checkLastModified(c.response, c.request, fi.ModTime()) {
return
}
d := f
// Index file
file = filepath.Join(file, indexPage)
f, err = fs.Open(file) f, err = fs.Open(file)
if err != nil { if err != nil {
if e.autoIndex {
// Auto index
return listDir(d, c)
}
return NewHTTPError(http.StatusForbidden) return NewHTTPError(http.StatusForbidden)
} }
fi, _ = f.Stat() fi, _ = f.Stat() // Index file stat
} }
http.ServeContent(c.response, c.request, fi.Name(), fi.ModTime(), f) http.ServeContent(c.response, c.request, fi.Name(), fi.ModTime(), f)
return nil return
}
func listDir(d http.File, c *Context) (err error) {
dirs, err := d.Readdir(-1)
if err != nil {
return err
}
// Create directory index
w := c.Response()
w.Header().Set(ContentType, TextHTMLCharsetUTF8)
fmt.Fprintf(w, "<pre>\n")
for _, d := range dirs {
name := d.Name()
color := "#212121"
if d.IsDir() {
color = "#e91e63"
name += "/"
}
fmt.Fprintf(w, "<a href=\"%s\" style=\"color: %s;\">%s</a>\n", name, color, name)
}
fmt.Fprintf(w, "</pre>\n")
return
} }
// Group creates a new sub router with prefix. It inherits all properties from // Group creates a new sub router with prefix. It inherits all properties from
@ -650,3 +693,25 @@ func (binder) Bind(r *http.Request, i interface{}) (err error) {
} }
return return
} }
// Source: net/http/fs.go
func checkLastModified(w http.ResponseWriter, r *http.Request, modtime time.Time) bool {
if modtime.IsZero() || modtime.Equal(unixEpochTime) {
// If the file doesn't have a modtime (IsZero), or the modtime
// is obviously garbage (Unix time == 0), then ignore modtimes
// and don't process the If-Modified-Since header.
return false
}
// The Date-Modified header truncates sub-second precision, so
// use mtime < t+1s instead of mtime <= t to check for unmodified.
if t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && modtime.Before(t.Add(1*time.Second)) {
h := w.Header()
delete(h, "Content-Type")
delete(h, "Content-Length")
w.WriteHeader(http.StatusNotModified)
return true
}
w.Header().Set("Last-Modified", modtime.UTC().Format(http.TimeFormat))
return false
}

View File

@ -37,6 +37,24 @@ SetOutput sets the output destination for the global logger.
SetLogLevel sets the log level for global logger. The default value is `log.INFO`. SetLogLevel sets the log level for global logger. The default value is `log.INFO`.
### Auto index
`Echo#AutoIndex(on bool)`
AutoIndex enables automatically creates a directory listing if the directory doesn't
contain an index page.
*Example*
```go
e := echo.New()
e.AutoIndex(true)
e.ServerDir("/", "/Users/vr/Projects/echo")
e.Run(":1323")
```
Browse to `http://localhost:1323/` to see the directory listing.
### Hook ### Hook
`Echo#Hook(http.HandlerFunc)` `Echo#Hook(http.HandlerFunc)`