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:
parent
9d11990cbb
commit
d932e2a863
@ -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
79
echo.go
@ -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
|
||||||
|
}
|
||||||
|
@ -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)`
|
||||||
|
Loading…
Reference in New Issue
Block a user