Moonshark/runner/httpContext.go
2025-06-06 18:57:47 -05:00

166 lines
3.8 KiB
Go

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