diff --git a/app.go b/app.go index c2f30f9..465b8d1 100644 --- a/app.go +++ b/app.go @@ -5,13 +5,15 @@ import ( "crypto/tls" "errors" "fmt" - "github.com/loveuer/nf/internal/bytesconv" "io" "log" "net" "net/http" "path" "regexp" + "sync" + + "github.com/loveuer/nf/internal/bytesconv" ) var ( @@ -29,6 +31,8 @@ type App struct { trees methodTrees + pool *sync.Pool + maxParams uint16 maxSections uint16 @@ -40,13 +44,34 @@ type App struct { removeExtraSlash bool // false } +func (a *App) allocateContext() *Ctx { + var ( + skippedNodes = make([]skippedNode, 0, a.maxSections) + v = make(Params, 0, a.maxParams) + ) + + ctx := Ctx{ + lock: sync.Mutex{}, + app: a, + index: -1, + locals: make(map[string]any), + handlers: make([]HandlerFunc, 0), + skippedNodes: &skippedNodes, + params: &v, + } + + return &ctx +} + func (a *App) ServeHTTP(writer http.ResponseWriter, request *http.Request) { var ( err error - c = newContext(a, writer, request) + c = a.pool.Get().(*Ctx) nfe = new(Err) ) + c.reset(writer, request) + if err = c.verify(); err != nil { if errors.As(err, nfe) { _ = c.Status(nfe.Status).SendString(nfe.Msg) @@ -58,6 +83,8 @@ func (a *App) ServeHTTP(writer http.ResponseWriter, request *http.Request) { } a.handleHTTPRequest(c) + + a.pool.Put(c) } func (a *App) run(ln net.Listener) error { @@ -137,9 +164,7 @@ func (a *App) addRoute(method, path string, handlers ...HandlerFunc) { } func (a *App) handleHTTPRequest(c *Ctx) { - var ( - err error - ) + var err error httpMethod := c.Request.Method rPath := c.Request.URL.Path @@ -263,7 +288,7 @@ func redirectFixedPath(c *Ctx, root *node, trailingSlash bool) bool { func redirectRequest(c *Ctx) { req := c.Request - //rPath := req.URL.Path + // rPath := req.URL.Path rURL := req.URL.String() code := http.StatusMovedPermanently // Permanent redirect, request with GET method @@ -271,7 +296,7 @@ func redirectRequest(c *Ctx) { code = http.StatusTemporaryRedirect } - //debugPrint("redirecting request %d: %s --> %s", code, rPath, rURL) + // debugPrint("redirecting request %d: %s --> %s", code, rPath, rURL) http.Redirect(c.Writer, req, rURL, code) c.writermem.WriteHeaderNow() diff --git a/ctx.go b/ctx.go index 6133b76..2d40b81 100644 --- a/ctx.go +++ b/ctx.go @@ -6,8 +6,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/google/uuid" - "github.com/loveuer/nf/internal/sse" "html/template" "io" "mime/multipart" @@ -15,11 +13,12 @@ import ( "net/http" "strings" "sync" + + "github.com/google/uuid" + "github.com/loveuer/nf/internal/sse" ) -var ( - forwardHeaders = []string{"CF-Connecting-IP", "X-Forwarded-For", "X-Real-Ip"} -) +var forwardHeaders = []string{"CF-Connecting-IP", "X-Forwarded-For", "X-Real-Ip"} type Ctx struct { lock sync.Mutex @@ -39,45 +38,29 @@ type Ctx struct { fullPath string } -func newContext(app *App, writer http.ResponseWriter, request *http.Request) *Ctx { - - var ( - traceId string - skippedNodes = make([]skippedNode, 0, app.maxSections) - v = make(Params, 0, app.maxParams) - ) - - if traceId = request.Header.Get(TraceKey); traceId == "" { +func (c *Ctx) reset(w http.ResponseWriter, r *http.Request) { + traceId := r.Header.Get(TraceKey) + if traceId == "" { traceId = uuid.Must(uuid.NewV7()).String() } - c := context.WithValue(request.Context(), TraceKey, traceId) + c.writermem.reset(w) - ctx := &Ctx{ - lock: sync.Mutex{}, - Request: request.WithContext(c), - path: request.URL.Path, - method: request.Method, - StatusCode: 200, + c.Request = r.WithContext(context.WithValue(r.Context(), TraceKey, traceId)) + c.Writer = &c.writermem + c.handlers = nil + c.index = -1 + c.path = r.URL.Path + c.method = r.Method + c.StatusCode = 200 - app: app, - index: -1, - locals: map[string]interface{}{}, - handlers: make([]HandlerFunc, 0), - skippedNodes: &skippedNodes, - params: &v, + c.fullPath = "" + *c.params = (*c.params)[:0] + *c.skippedNodes = (*c.skippedNodes)[:0] + for key := range c.locals { + delete(c.locals, key) } - - ctx.writermem = responseWriter{ - ResponseWriter: writer, - size: -1, - status: 0, - } - - ctx.Writer = &ctx.writermem - ctx.writermem.Header().Set(TraceKey, traceId) - - return ctx + c.writermem.Header().Set(TraceKey, traceId) } func (c *Ctx) Locals(key string, value ...interface{}) interface{} { @@ -109,9 +92,7 @@ func (c *Ctx) Path(overWrite ...string) string { } func (c *Ctx) Cookies(key string, defaultValue ...string) string { - var ( - dv = "" - ) + dv := "" if len(defaultValue) > 0 { dv = defaultValue[0] diff --git a/nf.go b/nf.go index 3f701d5..2dfff39 100644 --- a/nf.go +++ b/nf.go @@ -1,5 +1,7 @@ package nf +import "sync" + const ( banner = " _ _ _ ___ _ \n | \\| |___| |_ | __|__ _ _ _ _ __| |\n | .` / _ \\ _| | _/ _ \\ || | ' \\/ _` |\n |_|\\_\\___/\\__| |_|\\___/\\_,_|_||_\\__,_|\n " _404 = "Not Found" @@ -23,26 +25,24 @@ type Config struct { DisableRecover bool `json:"-"` DisableHttpErrorLog bool `json:"-"` - //EnableNotImplementHandler bool `json:"-"` + // EnableNotImplementHandler bool `json:"-"` NotFoundHandler HandlerFunc `json:"-"` MethodNotAllowedHandler HandlerFunc `json:"-"` } -var ( - defaultConfig = &Config{ - BodyLimit: 4 * 1024 * 1024, - NotFoundHandler: func(c *Ctx) error { - c.Set("Content-Type", MIMETextHTML) - _, err := c.Status(404).Write([]byte(_404)) - return err - }, - MethodNotAllowedHandler: func(c *Ctx) error { - c.Set("Content-Type", MIMETextPlain) - _, err := c.Status(405).Write([]byte(_405)) - return err - }, - } -) +var defaultConfig = &Config{ + BodyLimit: 4 * 1024 * 1024, + NotFoundHandler: func(c *Ctx) error { + c.Set("Content-Type", MIMETextHTML) + _, err := c.Status(404).Write([]byte(_404)) + return err + }, + MethodNotAllowedHandler: func(c *Ctx) error { + c.Set("Content-Type", MIMETextPlain) + _, err := c.Status(405).Write([]byte(_405)) + return err + }, +} func New(config ...Config) *App { app := &App{ @@ -52,6 +52,8 @@ func New(config ...Config) *App { root: true, }, + pool: &sync.Pool{}, + redirectTrailingSlash: true, // true redirectFixedPath: false, // false handleMethodNotAllowed: true, // false @@ -89,5 +91,9 @@ func New(config ...Config) *App { app.Use(NewRecover(true)) } + app.pool.New = func() any { + return app.allocateContext() + } + return app } diff --git a/nft/nfctl/cmd/new.go b/nft/nfctl/cmd/new.go index a0b8dec..506a716 100644 --- a/nft/nfctl/cmd/new.go +++ b/nft/nfctl/cmd/new.go @@ -30,12 +30,12 @@ nfctl new {project} --template http://username:token@my.gitlab.com/my-zone/my-re disableInit bool preTemplateMap = map[string]string{ - "ultone": "https://gitcode.com/loveuer/ultone.git", + "ultone": "https://gitea.loveuer.com/loveuer/ultone.git", } ) func initNew() { - cmdNew.Flags().StringVarP(&template, "template", "t", "", "template name/url[example:ultone, https://github.com/xxx/yyy.git]") + cmdNew.Flags().StringVarP(&template, "template", "t", "ultone", "template name/url[example:ultone, https://github.com/xxx/yyy.git]") cmdNew.Flags().BoolVar(&disableInit, "without-init", false, "don't run template init script") cmdNew.RunE = func(cmd *cobra.Command, args []string) error { @@ -67,7 +67,7 @@ func initNew() { return fmt.Errorf("project folder already exist") } - if err = os.MkdirAll(projectDir, 0750); err != nil { + if err = os.MkdirAll(projectDir, 0o750); err != nil { return fmt.Errorf("create project dir err: %v", err) } diff --git a/nft/nfctl/main.go b/nft/nfctl/main.go index 9ad2ad1..656ddd1 100644 --- a/nft/nfctl/main.go +++ b/nft/nfctl/main.go @@ -2,18 +2,15 @@ package main import ( "context" - "github.com/loveuer/nf/nft/nfctl/cmd" "os/signal" "syscall" + + "github.com/loveuer/nf/nft/nfctl/cmd" ) func main() { ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) defer cancel() - //if !(len(os.Args) >= 2 && os.Args[1] == "version") { - // version.Check(5) - //} - _ = cmd.Root.ExecuteContext(ctx) }