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)