package runner import ( "Moonshark/router" "Moonshark/runner/lualibs" "Moonshark/sessions" "Moonshark/utils" "sync" "github.com/valyala/fasthttp" ) // A prebuilt, ready-to-go context for HTTP requests to the runner. type HTTPContext struct { Method []byte Path []byte Host []byte Headers map[string]any Cookies map[string]string Query map[string]string Params map[string]any Form map[string]any Session map[string]any Env map[string]any Values map[string]any // Extra context vars just in case } // HTTP context pool to reduce allocations var httpContextPool = sync.Pool{ New: func() any { return &HTTPContext{ Headers: make(map[string]any, 16), Cookies: make(map[string]string, 8), Query: make(map[string]string, 8), Params: make(map[string]any, 4), Form: make(map[string]any, 8), Session: make(map[string]any, 4), Env: make(map[string]any, 16), Values: make(map[string]any, 32), } }, } // Get a clean HTTP context from the pool and build it up with an HTTP request, router params and session data func NewHTTPContext(httpCtx *fasthttp.RequestCtx, params *router.Params, session *sessions.Session) *HTTPContext { ctx := httpContextPool.Get().(*HTTPContext) // Extract basic HTTP info ctx.Method = httpCtx.Method() ctx.Path = httpCtx.Path() ctx.Host = httpCtx.Host() // Extract headers httpCtx.Request.Header.VisitAll(func(key, value []byte) { ctx.Headers[string(key)] = string(value) }) // Extract cookies httpCtx.Request.Header.VisitAllCookie(func(key, value []byte) { ctx.Cookies[string(key)] = string(value) }) // Extract query params httpCtx.QueryArgs().VisitAll(func(key, value []byte) { ctx.Query[string(key)] = string(value) }) // Extract route parameters if params != nil { for i := 0; i < min(len(params.Keys), len(params.Values)); i++ { ctx.Params[params.Keys[i]] = params.Values[i] } } // Extract form data if present if httpCtx.IsPost() || httpCtx.IsPut() || httpCtx.IsPatch() { if form, err := utils.ParseForm(httpCtx); err == nil { for k, v := range form { ctx.Form[k] = v } } } // Extract session data session.AdvanceFlash() ctx.Session["id"] = session.ID if session.IsEmpty() { ctx.Session["data"] = emptyMap ctx.Session["flash"] = emptyMap } else { ctx.Session["data"] = session.GetAll() ctx.Session["flash"] = session.GetAllFlash() } // Add environment vars if envMgr := lualibs.GetGlobalEnvManager(); envMgr != nil { for k, v := range envMgr.GetAll() { ctx.Env[k] = v } } return ctx } // Clear out all the request data from the context and give it back to the pool. Keeps the contexts and inner maps // allocated to prevent GC churn. func (c *HTTPContext) Release() { for k := range c.Headers { delete(c.Headers, k) } for k := range c.Cookies { delete(c.Cookies, k) } for k := range c.Query { delete(c.Query, k) } for k := range c.Params { delete(c.Params, k) } for k := range c.Form { delete(c.Form, k) } for k := range c.Session { delete(c.Session, k) } for k := range c.Env { delete(c.Env, k) } for k := range c.Values { delete(c.Values, k) } c.Method = nil c.Path = nil c.Host = nil httpContextPool.Put(c) } // Add a value to the extras map func (c *HTTPContext) Set(key string, value any) { c.Values[key] = value } // Get a value from the extras map func (c *HTTPContext) Get(key string) any { return c.Values[key] } // Returns a representation of the context ready for Lua func (c *HTTPContext) ToMap() map[string]any { return map[string]any{ "method": string(c.Method), "path": string(c.Path), "host": string(c.Host), "headers": c.Headers, "cookies": c.Cookies, "query": c.Query, "params": c.Params, "form": c.Form, "session": c.Session, "env": c.Env, "values": c.Values, } }