Adds static fileserver middleware
This commit is contained in:
108
static.go
Normal file
108
static.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user