tango/static.go
2015-04-03 14:21:21 +08:00

162 lines
3.6 KiB
Go

package tango
import (
"os"
"path"
"path/filepath"
"strings"
)
type StaticOptions struct {
RootPath string
Prefix string
IndexFiles []string
ListDir bool
FilterExts []string
}
func prepareStaticOptions(options []StaticOptions) StaticOptions {
var opt StaticOptions
if len(options) > 0 {
opt = options[0]
}
// Defaults
if len(opt.RootPath) == 0 {
opt.RootPath = "./public"
}
if len(opt.Prefix) > 0 {
if opt.Prefix[0] != '/' {
opt.Prefix = "/" + opt.Prefix
}
}
if len(opt.IndexFiles) == 0 {
opt.IndexFiles = []string{"index.html", "index.htm"}
}
return opt
}
func Static(opts ...StaticOptions) HandlerFunc {
return func(ctx *Context) {
if ctx.Req().Method != "GET" && ctx.Req().Method != "HEAD" {
ctx.Next()
return
}
opt := prepareStaticOptions(opts)
var rPath = ctx.Req().URL.Path
// if defined prefix, then only check prefix
if opt.Prefix != "" {
if !strings.HasPrefix(ctx.Req().URL.Path, opt.Prefix) {
ctx.Next()
return
} else {
if len(opt.Prefix) == len(ctx.Req().URL.Path) {
rPath = ""
} else {
rPath = ctx.Req().URL.Path[len(opt.Prefix):]
}
}
}
fPath, _ := filepath.Abs(filepath.Join(opt.RootPath, rPath))
finfo, err := os.Stat(fPath)
if err != nil {
if !os.IsNotExist(err) {
ctx.Result = InternalServerError(err.Error())
ctx.HandleError()
return
}
} else if !finfo.IsDir() {
if len(opt.FilterExts) > 0 {
var matched bool
for _, ext := range opt.FilterExts {
if filepath.Ext(fPath) == ext {
matched = true
break
}
}
if !matched {
ctx.Next()
return
}
}
err := ctx.ServeFile(fPath)
if err != nil {
ctx.Result = InternalServerError(err.Error())
ctx.HandleError()
}
return
} else {
// try serving index.html or index.htm
if len(opt.IndexFiles) > 0 {
for _, index := range opt.IndexFiles {
nPath := filepath.Join(fPath, index)
finfo, err = os.Stat(nPath)
if err != nil {
if !os.IsNotExist(err) {
ctx.Result = InternalServerError(err.Error())
ctx.HandleError()
return
}
} else if !finfo.IsDir() {
err = ctx.ServeFile(nPath)
if err != nil {
ctx.Result = InternalServerError(err.Error())
ctx.HandleError()
}
return
}
}
}
// list dir files
if opt.ListDir {
ctx.Header().Set("Content-Type", "text/html")
ctx.Write([]byte(`<ul style="list-style-type:none;line-height:32px;">`))
rootPath, _ := filepath.Abs(opt.RootPath)
rPath, _ := filepath.Rel(rootPath, fPath)
if fPath != rootPath {
ctx.Write([]byte(`<li>&nbsp; &nbsp; <a href="/` + path.Join(opt.Prefix, filepath.Dir(rPath)) + `">..</a></li>`))
}
err = filepath.Walk(fPath, func(p string, fi os.FileInfo, err error) error {
rPath, _ := filepath.Rel(fPath, p)
if rPath == "." || len(strings.Split(rPath, string(filepath.Separator))) > 1 {
return nil
}
rPath, _ = filepath.Rel(rootPath, p)
ps, _ := os.Stat(p)
if ps.IsDir() {
ctx.Write([]byte(`<li>┖ <a href="/` + path.Join(opt.Prefix, rPath) + `">` + filepath.Base(p) + `</a></li>`))
} else {
if len(opt.FilterExts) > 0 {
var matched bool
for _, ext := range opt.FilterExts {
if filepath.Ext(p) == ext {
matched = true
break
}
}
if !matched {
return nil
}
}
ctx.Write([]byte(`<li>&nbsp; &nbsp; <a href="/` + path.Join(opt.Prefix, rPath) + `">` + filepath.Base(p) + `</a></li>`))
}
return nil
})
ctx.Write([]byte("</ul>"))
return
}
}
ctx.Next()
}
}