Browse Source

新增文档根目录处理逻辑

fancl 1 year ago
parent
commit
4b346afaec
4 changed files with 83 additions and 10 deletions
  1. 2 1
      cmd/main.go
  2. 1 1
      cmd/web/index.html
  3. 51 2
      entry/http/file.go
  4. 29 6
      entry/http/server.go

+ 2 - 1
cmd/main.go

@@ -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
 }
 }
 
 

+ 1 - 1
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">
           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>

+ 51 - 2
entry/http/file.go

@@ -3,13 +3,19 @@ package http
 import (
 import (
 	"io/fs"
 	"io/fs"
 	"net/http"
 	"net/http"
+	"os"
+	"path"
+	"strings"
 	"time"
 	"time"
 )
 )
 
 
 type (
 type (
 	FS struct {
 	FS struct {
-		fs      http.FileSystem
-		modtime time.Time
+		fs            http.FileSystem
+		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
 }
 }
 
 

+ 29 - 6
entry/http/server.go

@@ -17,12 +17,14 @@ var (
 )
 )
 
 
 type Server struct {
 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 {
 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) {
 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)