From 95ff65d625cca33c926b1b3d2376855b15309e72 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 21 Apr 2023 17:06:06 +0800 Subject: [PATCH] [change] optimizing static http server. --- service/fs/fs.go | 120 ++++++++++++++++++++++++++++++++++++++++++ service/guard.go | 14 +++++ service/middleware.go | 10 ++-- service/static.go | 80 ++-------------------------- 4 files changed, 144 insertions(+), 80 deletions(-) create mode 100644 service/fs/fs.go diff --git a/service/fs/fs.go b/service/fs/fs.go new file mode 100644 index 0000000000..a3b6642385 --- /dev/null +++ b/service/fs/fs.go @@ -0,0 +1,120 @@ +package fs + +import ( + "errors" + "io/fs" + "net/http" + "os" + "path" + "path/filepath" + "strings" + + "github.com/yaoapp/gou/application" +) + +// Dir http root path +type Dir string + +// DirPWA is the PWA path +type DirPWA string + +// Open implements FileSystem using os.Open, opening files for reading rooted +// and relative to the directory d. +func (d Dir) Open(name string) (http.File, error) { + if filepath.Separator != '/' && strings.ContainsRune(name, filepath.Separator) { + return nil, errors.New("http: invalid character in file path") + } + + dir := string(d) + if dir == "" { + dir = "." + } + + name = filepath.FromSlash(path.Clean("/" + name)) + relName := filepath.Join(dir, name) + + // Close dir views Disable directory listing + absName := filepath.Join(application.App.Root(), relName) + stat, err := os.Stat(absName) + if err != nil { + return nil, mapOpenError(err, relName, filepath.Separator, os.Stat) + } + + if stat.IsDir() { + if _, err := os.Stat(filepath.Join(absName, "index.html")); os.IsNotExist(err) { + return nil, mapOpenError(fs.ErrNotExist, relName, filepath.Separator, os.Stat) + } + } + + f, err := application.App.FS(string(d)).Open(name) + if err != nil { + return nil, mapOpenError(err, relName, filepath.Separator, os.Stat) + } + + return f, nil +} + +// Open implements FileSystem using os.Open, opening files for reading rooted +// and relative to the directory d. +func (d DirPWA) Open(name string) (http.File, error) { + if filepath.Separator != '/' && strings.ContainsRune(name, filepath.Separator) { + return nil, errors.New("http: invalid character in file path") + } + + dir := string(d) + if dir == "" { + dir = "." + } + + name = filepath.FromSlash(path.Clean("/" + name)) + relName := filepath.Join(dir, name) + + if filepath.Ext(relName) == "" && relName != dir { + relName = filepath.Join(dir, "index.html") + name = filepath.Join(string(os.PathSeparator), "index.html") + } + + // Close dir views Disable directory listing + absName := filepath.Join(application.App.Root(), relName) + stat, err := os.Stat(absName) + if err != nil { + return nil, mapOpenError(err, relName, filepath.Separator, os.Stat) + } + + if stat.IsDir() { + if _, err := os.Stat(filepath.Join(absName, "index.html")); os.IsNotExist(err) { + return nil, mapOpenError(fs.ErrNotExist, relName, filepath.Separator, os.Stat) + } + } + + f, err := application.App.FS(string(d)).Open(name) + if err != nil { + return nil, mapOpenError(err, relName, filepath.Separator, os.Stat) + } + + return f, nil +} + +// mapOpenError maps the provided non-nil error from opening name +// to a possibly better non-nil error. In particular, it turns OS-specific errors +// about opening files in non-directories into fs.ErrNotExist. See Issues 18984 and 49552. +func mapOpenError(originalErr error, name string, sep rune, stat func(string) (fs.FileInfo, error)) error { + if errors.Is(originalErr, fs.ErrNotExist) || errors.Is(originalErr, fs.ErrPermission) { + return originalErr + } + + parts := strings.Split(name, string(sep)) + for i := range parts { + if parts[i] == "" { + continue + } + fi, err := stat(strings.Join(parts[:i+1], string(sep))) + if err != nil { + return originalErr + } + if !fi.IsDir() { + return fs.ErrNotExist + } + } + return originalErr +} diff --git a/service/guard.go b/service/guard.go index e7d3e8c3ec..8c8c730678 100644 --- a/service/guard.go +++ b/service/guard.go @@ -16,6 +16,7 @@ import ( // Guards middlewares var Guards = map[string]gin.HandlerFunc{ "bearer-jwt": guardBearerJWT, // Bearer JWT + "query-jwt": guardQueryJWT, // Get JWT Token from query string "__tk" "cross-origin": guardCrossOrigin, // Cross-Origin Resource Sharing "widget-table": table.Guard, // Widget Table Guard "widget-list": list.Guard, // Widget List Guard @@ -38,6 +39,19 @@ func guardBearerJWT(c *gin.Context) { c.Set("__sid", claims.SID) } +// JWT Bearer JWT +func guardQueryJWT(c *gin.Context) { + tokenString := c.Query("__tk") + if tokenString == "" { + c.JSON(403, gin.H{"code": 403, "message": "No permission"}) + c.Abort() + return + } + + claims := helper.JwtValidate(tokenString) + c.Set("__sid", claims.SID) +} + // CORS Cross Origin func guardCrossOrigin(c *gin.Context) { c.Writer.Header().Set("Access-Control-Allow-Origin", "*") diff --git a/service/middleware.go b/service/middleware.go index 1554d061f8..22af568a56 100644 --- a/service/middleware.go +++ b/service/middleware.go @@ -6,13 +6,13 @@ import ( "github.com/gin-gonic/gin" ) -// Middlewares 服务中间件 +// Middlewares the middlewares var Middlewares = []func(c *gin.Context){ - BinStatic, + withStaticFileServer, } -// BinStatic 静态文件服务 -func BinStatic(c *gin.Context) { +// withStaticFileServer static file server +func withStaticFileServer(c *gin.Context) { length := len(c.Request.URL.Path) @@ -38,7 +38,7 @@ func BinStatic(c *gin.Context) { return } - // 应用内静态文件目录(/ui or public) + // static file server AppFileServer.ServeHTTP(c.Writer, c.Request) c.Abort() } diff --git a/service/static.go b/service/static.go index 1944304187..415ba44f1a 100644 --- a/service/static.go +++ b/service/static.go @@ -1,17 +1,12 @@ package service import ( - "errors" "fmt" - "io/fs" "net/http" - "os" - "path" - "path/filepath" "strings" - "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/data" + "github.com/yaoapp/yao/service/fs" "github.com/yaoapp/yao/share" ) @@ -27,84 +22,19 @@ var AdminRoot = "" // AdminRootLen cache var AdminRootLen = 0 -// Dir files -type Dir string - // SetupStatic setup static file server func SetupStatic() error { // SetAdmin Root adminRoot() - // Static file server - AppFileServer = http.FileServer(Dir(filepath.Join(config.Conf.Root, "public"))) - - return nil -} - -// Open implements FileSystem using os.Open, opening files for reading rooted -// and relative to the directory d. -func (d Dir) Open(name string) (http.File, error) { - if filepath.Separator != '/' && strings.ContainsRune(name, filepath.Separator) { - return nil, errors.New("http: invalid character in file path") - } - - dir := string(d) - if dir == "" { - dir = "." - } - - name = path.Clean("/" + name) - fullName := filepath.Join(dir, filepath.FromSlash(name)) if isPWA() { - if filepath.Ext(fullName) == "" && fullName != dir { - fullName = filepath.Join(dir, "index.html") - } - } - - // Close dir views Disable directory listing - stat, err := os.Stat(fullName) - if err != nil { - return nil, mapOpenError(err, fullName, filepath.Separator, os.Stat) + AppFileServer = http.FileServer(fs.DirPWA("public")) + return nil } - if stat.IsDir() { - indexFile := filepath.Join(fullName, "index.html") - if _, err := os.Stat(indexFile); os.IsNotExist(err) { - return nil, mapOpenError(fs.ErrNotExist, fullName, filepath.Separator, os.Stat) - } - } - - f, err := os.Open(fullName) - if err != nil { - return nil, mapOpenError(err, fullName, filepath.Separator, os.Stat) - } - - return f, nil -} - -// mapOpenError maps the provided non-nil error from opening name -// to a possibly better non-nil error. In particular, it turns OS-specific errors -// about opening files in non-directories into fs.ErrNotExist. See Issues 18984 and 49552. -func mapOpenError(originalErr error, name string, sep rune, stat func(string) (fs.FileInfo, error)) error { - if errors.Is(originalErr, fs.ErrNotExist) || errors.Is(originalErr, fs.ErrPermission) { - return originalErr - } - - parts := strings.Split(name, string(sep)) - for i := range parts { - if parts[i] == "" { - continue - } - fi, err := stat(strings.Join(parts[:i+1], string(sep))) - if err != nil { - return originalErr - } - if !fi.IsDir() { - return fs.ErrNotExist - } - } - return originalErr + AppFileServer = http.FileServer(fs.Dir("public")) + return nil } // rewrite path