diff --git a/cmd/main.go b/cmd/main.go
index 6f89b49..1008d96 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -4,6 +4,7 @@ import (
"context"
"embed"
"flag"
+ "net/http"
"git.nspix.com/golang/kos"
)
@@ -15,7 +16,7 @@ type subServer struct {
}
func (s *subServer) Start(ctx context.Context) (err error) {
- kos.Http().Embed("/ui/web", "web", webDir)
+ kos.Http().Root("/web", http.FS(webDir))
return
}
diff --git a/cmd/web/index.html b/cmd/web/index.html
index d1518e7..8bfc9e4 100644
--- a/cmd/web/index.html
+++ b/cmd/web/index.html
@@ -6,7 +6,7 @@
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
Document
-
+
Hello
diff --git a/entry/http/file.go b/entry/http/file.go
index 34d0f24..4faed74 100644
--- a/entry/http/file.go
+++ b/entry/http/file.go
@@ -3,13 +3,19 @@ package http
import (
"io/fs"
"net/http"
+ "os"
+ "path"
+ "strings"
"time"
)
type (
FS struct {
- fs http.FileSystem
- modtime time.Time
+ fs http.FileSystem
+ modtime time.Time
+ prefix string
+ indexFile string
+ denyDirectory bool
}
File struct {
@@ -74,11 +80,54 @@ func (file *File) Stat() (fs.FileInfo, error) {
return newFileInfo(fi, file.modtime), nil
}
+func (fs *FS) DenyAccessDirectory() {
+ fs.denyDirectory = true
+}
+
+func (fs *FS) SetPrefix(prefix string) {
+ if prefix != "" {
+ if prefix[0] != '/' {
+ prefix = "/" + prefix
+ }
+ prefix = strings.TrimRight(prefix, "/")
+ fs.prefix = prefix
+ }
+}
+
+func (fs *FS) SetIndexFile(indexFile string) {
+ fs.indexFile = indexFile
+}
+
func (fs *FS) Open(name string) (http.File, error) {
+ var (
+ needRetry bool
+ )
+ if name == "" || name == "/" {
+ needRetry = true
+ }
+ if fs.prefix != "" {
+ if !strings.HasPrefix(name, fs.prefix) {
+ name = path.Join(fs.prefix, name)
+ }
+ }
fp, err := fs.fs.Open(name)
if err != nil {
return nil, err
}
+ if fs.denyDirectory {
+ state, err := fp.Stat()
+ if err != nil {
+ return nil, err
+ }
+ if state.IsDir() {
+ if needRetry {
+ if fs.indexFile != "" {
+ return fs.Open(path.Join(name, fs.indexFile))
+ }
+ }
+ return nil, os.ErrPermission
+ }
+ }
return &File{fp: fp, modtime: fs.modtime}, nil
}
diff --git a/entry/http/server.go b/entry/http/server.go
index 6568de1..656b8f8 100644
--- a/entry/http/server.go
+++ b/entry/http/server.go
@@ -17,12 +17,14 @@ var (
)
type Server struct {
- ctx context.Context
- serve *http.Server
- router *router.Router
- middleware []Middleware
- uptime time.Time
- anyRequests map[string]http.Handler
+ ctx context.Context
+ serve *http.Server
+ router *router.Router
+ middleware []Middleware
+ uptime time.Time
+ enableDocumentRoot bool
+ fileSystem http.FileSystem
+ anyRequests map[string]http.Handler
}
func (svr *Server) applyContext() *Context {
@@ -91,6 +93,15 @@ func (svr *Server) Group(prefix string, routes []Route, middleware ...Middleware
}
}
+func (svr *Server) Root(prefix string, fs http.FileSystem) {
+ svr.enableDocumentRoot = true
+ s := newFS(svr.uptime, fs)
+ s.SetPrefix(prefix)
+ s.DenyAccessDirectory()
+ s.SetIndexFile("/index.html")
+ svr.fileSystem = s
+}
+
func (svr *Server) Embed(prefix string, root string, embedFs embed.FS) {
routePath := prefix
if !strings.HasSuffix(routePath, "/*filepath") {
@@ -159,12 +170,24 @@ func (svr *Server) handleRequest(res http.ResponseWriter, req *http.Request) {
}
func (svr *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
+ var (
+ err error
+ file http.File
+ )
for prefix, handle := range svr.anyRequests {
if strings.HasPrefix(request.URL.Path, prefix) {
handle.ServeHTTP(writer, request)
return
}
}
+ if svr.enableDocumentRoot && request.Method == http.MethodGet {
+ uri := path.Clean(request.URL.Path)
+ if file, err = svr.fileSystem.Open(uri); err == nil {
+ http.ServeContent(writer, request, path.Base(uri), svr.uptime, file)
+ err = file.Close()
+ return
+ }
+ }
switch request.Method {
case http.MethodOptions:
svr.handleOption(writer, request)