Adds static fileserver middleware

This commit is contained in:
2015-08-12 16:31:28 +02:00
parent 8d3675fcfb
commit e71418fa92

108
static.go Normal file
View File

@@ -0,0 +1,108 @@
package narco
import (
"fmt"
"net/http"
"os"
"strings"
"github.com/codemodus/chain"
"golang.org/x/net/context"
)
type StaticFileServer struct {
dir http.FileSystem
prefix string
}
type StaticFileServerError struct {
error string
status int
}
func NewStaticFileServer(dir http.FileSystem, prefix string) *StaticFileServer {
return &StaticFileServer{
dir: dir,
prefix: prefix,
}
}
func (e StaticFileServerError) Error() string {
return fmt.Sprintf("%d: %s", e.status, e.error)
}
func (fs *StaticFileServer) ServeHTTPError(rw http.ResponseWriter, req *http.Request) error {
if req.Method != "GET" && req.Method != "HEAD" {
return StaticFileServerError{"Invalid " + req.Method + " method", http.StatusMethodNotAllowed}
}
fpath := req.URL.Path
if len(fs.prefix) != 0 {
if strings.HasPrefix(fpath, fs.prefix) == false {
return StaticFileServerError{"Invalid path " + fpath, http.StatusNotFound}
}
fpath = strings.TrimPrefix(fpath, fs.prefix)
}
f, err := fs.dir.Open(fpath)
if err != nil {
if os.IsNotExist(err) == true {
return StaticFileServerError{"Unknown path " + fpath, http.StatusNotFound}
} else {
return StaticFileServerError{"Internal FS error", http.StatusInternalServerError}
}
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
if os.IsNotExist(err) == true {
return StaticFileServerError{"Unknown path " + fpath, http.StatusNotFound}
} else {
return StaticFileServerError{"Internal FS error", http.StatusInternalServerError}
}
}
if fi.IsDir() == true {
if fpath[len(fpath)-1] == '/' {
return StaticFileServerError{"Cannot index directory", http.StatusUnauthorized}
} else {
http.Redirect(rw, req, req.URL.Path+"/", http.StatusFound)
return nil
}
}
http.ServeContent(rw, req, fpath, fi.ModTime(), f)
return nil
}
//http.Handler interface
func (fs *StaticFileServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
err := fs.ServeHTTPError(rw, req)
if err != nil {
fsErr := err.(StaticFileServerError)
http.Error(rw, err.Error(), fsErr.status)
}
}
//chain handler interface
func (fs *StaticFileServer) ServeHTTPContext(ctx context.Context, rw http.ResponseWriter, req *http.Request) {
fs.ServeHTTP(rw, req)
}
// chain wrapper. we just ignore the error, and send the request to
// the next middleware. I would prefer to use as an endpoint, i.e. we
// fail if its not good
func (fs *StaticFileServer) Wrap() func(chain.Handler) chain.Handler {
return func(other chain.Handler) chain.Handler {
return chain.HandlerFunc(func(ctx context.Context, rw http.ResponseWriter, req *http.Request) {
fs.ServeHTTPError(rw, req)
// todo ? put the error in the context ?? otherwise we
// should simply use endpoint, which is good for static
// resources we serve here !
other.ServeHTTPContext(ctx, rw, req)
})
}
}