新增文档根目录处理逻辑

This commit is contained in:
fancl 2023-08-25 09:51:03 +08:00
parent 6693cfe68f
commit 4b346afaec
4 changed files with 83 additions and 10 deletions

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"embed" "embed"
"flag" "flag"
"net/http"
"git.nspix.com/golang/kos" "git.nspix.com/golang/kos"
) )
@ -15,7 +16,7 @@ type subServer struct {
} }
func (s *subServer) Start(ctx context.Context) (err error) { func (s *subServer) Start(ctx context.Context) (err error) {
kos.Http().Embed("/ui/web", "web", webDir) kos.Http().Root("/web", http.FS(webDir))
return return
} }

View File

@ -6,7 +6,7 @@
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title> <title>Document</title>
<link rel="stylesheet" href="css/index.css"> <link rel="stylesheet" href="/css/index.css">
</head> </head>
<body> <body>
<h1>Hello</h1> <h1>Hello</h1>

View File

@ -3,6 +3,9 @@ package http
import ( import (
"io/fs" "io/fs"
"net/http" "net/http"
"os"
"path"
"strings"
"time" "time"
) )
@ -10,6 +13,9 @@ type (
FS struct { FS struct {
fs http.FileSystem fs http.FileSystem
modtime time.Time modtime time.Time
prefix string
indexFile string
denyDirectory bool
} }
File struct { File struct {
@ -74,11 +80,54 @@ func (file *File) Stat() (fs.FileInfo, error) {
return newFileInfo(fi, file.modtime), nil 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) { 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) fp, err := fs.fs.Open(name)
if err != nil { if err != nil {
return nil, err 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 return &File{fp: fp, modtime: fs.modtime}, nil
} }

View File

@ -22,6 +22,8 @@ type Server struct {
router *router.Router router *router.Router
middleware []Middleware middleware []Middleware
uptime time.Time uptime time.Time
enableDocumentRoot bool
fileSystem http.FileSystem
anyRequests map[string]http.Handler anyRequests map[string]http.Handler
} }
@ -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) { func (svr *Server) Embed(prefix string, root string, embedFs embed.FS) {
routePath := prefix routePath := prefix
if !strings.HasSuffix(routePath, "/*filepath") { 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) { func (svr *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
var (
err error
file http.File
)
for prefix, handle := range svr.anyRequests { for prefix, handle := range svr.anyRequests {
if strings.HasPrefix(request.URL.Path, prefix) { if strings.HasPrefix(request.URL.Path, prefix) {
handle.ServeHTTP(writer, request) handle.ServeHTTP(writer, request)
return 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 { switch request.Method {
case http.MethodOptions: case http.MethodOptions:
svr.handleOption(writer, request) svr.handleOption(writer, request)