Implement cookie utilities
This commit is contained in:
parent
e37e798377
commit
727ce89da7
|
@ -156,7 +156,7 @@ func (s *Server) handleLuaRoute(w http.ResponseWriter, r *http.Request, bytecode
|
||||||
ctx.Set("path", r.URL.Path)
|
ctx.Set("path", r.URL.Path)
|
||||||
ctx.Set("host", r.Host)
|
ctx.Set("host", r.Host)
|
||||||
|
|
||||||
// Inline the header conversion (previously makeHeaderMap)
|
// Add headers to context
|
||||||
headerMap := make(map[string]any, len(r.Header))
|
headerMap := make(map[string]any, len(r.Header))
|
||||||
for name, values := range r.Header {
|
for name, values := range r.Header {
|
||||||
if len(values) == 1 {
|
if len(values) == 1 {
|
||||||
|
@ -167,11 +167,20 @@ func (s *Server) handleLuaRoute(w http.ResponseWriter, r *http.Request, bytecode
|
||||||
}
|
}
|
||||||
ctx.Set("headers", headerMap)
|
ctx.Set("headers", headerMap)
|
||||||
|
|
||||||
|
// Add cookies to context
|
||||||
|
if cookies := r.Cookies(); len(cookies) > 0 {
|
||||||
|
cookieMap := make(map[string]any, len(cookies))
|
||||||
|
for _, cookie := range cookies {
|
||||||
|
cookieMap[cookie.Name] = cookie.Value
|
||||||
|
}
|
||||||
|
ctx.Set("cookies", cookieMap)
|
||||||
|
}
|
||||||
|
|
||||||
// Add URL parameters
|
// Add URL parameters
|
||||||
if params.Count > 0 {
|
if params.Count > 0 {
|
||||||
paramMap := make(map[string]any, params.Count)
|
paramMap := make(map[string]any, params.Count)
|
||||||
for i := 0; i < params.Count; i++ {
|
for i, key := range params.Keys {
|
||||||
paramMap[params.Keys[i]] = params.Values[i]
|
paramMap[key] = params.Values[i]
|
||||||
}
|
}
|
||||||
ctx.Set("params", paramMap)
|
ctx.Set("params", paramMap)
|
||||||
}
|
}
|
||||||
|
@ -230,6 +239,11 @@ func writeResponse(w http.ResponseWriter, result any, log *logger.Logger) {
|
||||||
w.Header().Set(name, value)
|
w.Header().Set(name, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set cookies
|
||||||
|
for _, cookie := range httpResp.Cookies {
|
||||||
|
http.SetCookie(w, cookie)
|
||||||
|
}
|
||||||
|
|
||||||
// Set status code
|
// Set status code
|
||||||
w.WriteHeader(httpResp.Status)
|
w.WriteHeader(httpResp.Status)
|
||||||
|
|
||||||
|
@ -243,7 +257,7 @@ func writeResponse(w http.ResponseWriter, result any, log *logger.Logger) {
|
||||||
|
|
||||||
switch res := result.(type) {
|
switch res := result.(type) {
|
||||||
case string:
|
case string:
|
||||||
// String result - plain text
|
// String result - default to plain text
|
||||||
setContentTypeIfMissing(w, contentTypePlain)
|
setContentTypeIfMissing(w, contentTypePlain)
|
||||||
w.Write([]byte(res))
|
w.Write([]byte(res))
|
||||||
default:
|
default:
|
||||||
|
|
172
core/runner/cookies.go
Normal file
172
core/runner/cookies.go
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
package runner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// extractCookie grabs cookies from the Lua state
|
||||||
|
func extractCookie(state *luajit.State) *http.Cookie {
|
||||||
|
cookie := &http.Cookie{
|
||||||
|
Path: "/", // Default path
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get name
|
||||||
|
state.GetField(-1, "name")
|
||||||
|
if !state.IsString(-1) {
|
||||||
|
state.Pop(1)
|
||||||
|
return nil // Name is required
|
||||||
|
}
|
||||||
|
cookie.Name = state.ToString(-1)
|
||||||
|
state.Pop(1)
|
||||||
|
|
||||||
|
// Get value
|
||||||
|
state.GetField(-1, "value")
|
||||||
|
if state.IsString(-1) {
|
||||||
|
cookie.Value = state.ToString(-1)
|
||||||
|
}
|
||||||
|
state.Pop(1)
|
||||||
|
|
||||||
|
// Get path
|
||||||
|
state.GetField(-1, "path")
|
||||||
|
if state.IsString(-1) {
|
||||||
|
cookie.Path = state.ToString(-1)
|
||||||
|
}
|
||||||
|
state.Pop(1)
|
||||||
|
|
||||||
|
// Get domain
|
||||||
|
state.GetField(-1, "domain")
|
||||||
|
if state.IsString(-1) {
|
||||||
|
cookie.Domain = state.ToString(-1)
|
||||||
|
}
|
||||||
|
state.Pop(1)
|
||||||
|
|
||||||
|
// Get expires
|
||||||
|
state.GetField(-1, "expires")
|
||||||
|
if state.IsNumber(-1) {
|
||||||
|
expiry := int64(state.ToNumber(-1))
|
||||||
|
cookie.Expires = time.Unix(expiry, 0)
|
||||||
|
}
|
||||||
|
state.Pop(1)
|
||||||
|
|
||||||
|
// Get max age
|
||||||
|
state.GetField(-1, "max_age")
|
||||||
|
if state.IsNumber(-1) {
|
||||||
|
cookie.MaxAge = int(state.ToNumber(-1))
|
||||||
|
}
|
||||||
|
state.Pop(1)
|
||||||
|
|
||||||
|
// Get secure
|
||||||
|
state.GetField(-1, "secure")
|
||||||
|
if state.IsBoolean(-1) {
|
||||||
|
cookie.Secure = state.ToBoolean(-1)
|
||||||
|
}
|
||||||
|
state.Pop(1)
|
||||||
|
|
||||||
|
// Get http only
|
||||||
|
state.GetField(-1, "http_only")
|
||||||
|
if state.IsBoolean(-1) {
|
||||||
|
cookie.HttpOnly = state.ToBoolean(-1)
|
||||||
|
}
|
||||||
|
state.Pop(1)
|
||||||
|
|
||||||
|
return cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
// LuaCookieModule provides cookie functionality to Lua scripts
|
||||||
|
const LuaCookieModule = `
|
||||||
|
-- Cookie module implementation
|
||||||
|
local cookie = {
|
||||||
|
-- Set a cookie
|
||||||
|
set = function(name, value, expires, path, domain, secure, http_only)
|
||||||
|
if type(name) ~= "string" then
|
||||||
|
error("cookie.set: name must be a string", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get or create response
|
||||||
|
local resp = __http_responses[1] or {}
|
||||||
|
resp.cookies = resp.cookies or {}
|
||||||
|
__http_responses[1] = resp
|
||||||
|
|
||||||
|
-- Create cookie table
|
||||||
|
local cookie = {
|
||||||
|
name = name,
|
||||||
|
value = value or "",
|
||||||
|
path = path or "/",
|
||||||
|
domain = domain
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Handle expiry
|
||||||
|
if expires then
|
||||||
|
if type(expires) == "number" then
|
||||||
|
if expires > 0 then
|
||||||
|
-- Add seconds to current time
|
||||||
|
cookie.max_age = expires
|
||||||
|
local now = os.time()
|
||||||
|
cookie.expires = now + expires
|
||||||
|
elseif expires < 0 then
|
||||||
|
-- Session cookie (default)
|
||||||
|
else
|
||||||
|
-- Expire immediately
|
||||||
|
cookie.expires = 0
|
||||||
|
cookie.max_age = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set flags
|
||||||
|
cookie.secure = secure or false
|
||||||
|
cookie.http_only = http_only or false
|
||||||
|
|
||||||
|
-- Store in cookies table
|
||||||
|
local n = #resp.cookies + 1
|
||||||
|
resp.cookies[n] = cookie
|
||||||
|
|
||||||
|
return true
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Get a cookie value
|
||||||
|
get = function(name)
|
||||||
|
if type(name) ~= "string" then
|
||||||
|
error("cookie.get: name must be a string", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Access values directly from current environment
|
||||||
|
local env = getfenv(1)
|
||||||
|
|
||||||
|
-- Check if context exists and has cookies
|
||||||
|
if env.ctx and env.ctx.cookies and env.ctx.cookies[name] then
|
||||||
|
return tostring(env.ctx.cookies[name])
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Remove a cookie
|
||||||
|
remove = function(name, path, domain)
|
||||||
|
if type(name) ~= "string" then
|
||||||
|
error("cookie.remove: name must be a string", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Create an expired cookie
|
||||||
|
return cookie.set(name, "", 0, path or "/", domain, false, false)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Install cookie module
|
||||||
|
_G.cookie = cookie
|
||||||
|
|
||||||
|
-- Make sure the cookie module is accessible in sandbox
|
||||||
|
if __env_system and __env_system.base_env then
|
||||||
|
__env_system.base_env.cookie = cookie
|
||||||
|
end
|
||||||
|
`
|
||||||
|
|
||||||
|
// CookieModuleInitFunc returns an initializer for the cookie module
|
||||||
|
func CookieModuleInitFunc() StateInitFunc {
|
||||||
|
return func(state *luajit.State) error {
|
||||||
|
return state.DoString(LuaCookieModule)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
package runner
|
package runner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -9,6 +11,7 @@ type HTTPResponse struct {
|
||||||
Status int `json:"status"`
|
Status int `json:"status"`
|
||||||
Headers map[string]string `json:"headers"`
|
Headers map[string]string `json:"headers"`
|
||||||
Body any `json:"body"`
|
Body any `json:"body"`
|
||||||
|
Cookies []*http.Cookie `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHTTPResponse creates a default HTTP response
|
// NewHTTPResponse creates a default HTTP response
|
||||||
|
@ -16,6 +19,7 @@ func NewHTTPResponse() *HTTPResponse {
|
||||||
return &HTTPResponse{
|
return &HTTPResponse{
|
||||||
Status: 200,
|
Status: 200,
|
||||||
Headers: make(map[string]string),
|
Headers: make(map[string]string),
|
||||||
|
Cookies: []*http.Cookie{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,6 +132,26 @@ func GetHTTPResponse(state *luajit.State) (*HTTPResponse, bool) {
|
||||||
}
|
}
|
||||||
state.Pop(1)
|
state.Pop(1)
|
||||||
|
|
||||||
|
// Get cookies
|
||||||
|
state.GetField(-1, "cookies")
|
||||||
|
if state.IsTable(-1) {
|
||||||
|
// Iterate through cookies array
|
||||||
|
length := state.GetTableLength(-1)
|
||||||
|
for i := 1; i <= length; i++ {
|
||||||
|
state.PushNumber(float64(i))
|
||||||
|
state.GetTable(-2)
|
||||||
|
|
||||||
|
if state.IsTable(-1) {
|
||||||
|
cookie := extractCookie(state)
|
||||||
|
if cookie != nil {
|
||||||
|
response.Cookies = append(response.Cookies, cookie)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.Pop(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.Pop(1)
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
state.Pop(2) // Pop response table and __http_responses
|
state.Pop(2) // Pop response table and __http_responses
|
||||||
|
|
||||||
|
|
|
@ -103,14 +103,14 @@ func NewRunner(options ...RunnerOption) (*LuaRunner, error) {
|
||||||
return nil, ErrInitFailed
|
return nil, ErrInitFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize HTTP module BEFORE sandbox setup
|
// Preload core modules
|
||||||
httpInit := HTTPModuleInitFunc()
|
moduleInits := CombineInitFuncs(HTTPModuleInitFunc(), CookieModuleInitFunc())
|
||||||
if err := httpInit(state); err != nil {
|
if err := moduleInits(state); err != nil {
|
||||||
state.Close()
|
state.Close()
|
||||||
return nil, ErrInitFailed
|
return nil, ErrInitFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up sandbox AFTER HTTP module is initialized
|
// Set up sandbox after core modules are initialized
|
||||||
if err := runner.sandbox.Setup(state); err != nil {
|
if err := runner.sandbox.Setup(state); err != nil {
|
||||||
state.Close()
|
state.Close()
|
||||||
return nil, ErrInitFailed
|
return nil, ErrInitFailed
|
||||||
|
|
|
@ -81,8 +81,8 @@ func (s *Sandbox) Setup(state *luajit.State) error {
|
||||||
preload = package.preload
|
preload = package.preload
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Add HTTP module explicitly to the base environment
|
|
||||||
base.http = http
|
base.http = http
|
||||||
|
base.cookie = cookie
|
||||||
|
|
||||||
-- Add registered custom modules
|
-- Add registered custom modules
|
||||||
if __sandbox_modules then
|
if __sandbox_modules then
|
||||||
|
@ -127,6 +127,24 @@ func (s *Sandbox) Setup(state *luajit.State) error {
|
||||||
env.require = function(modname)
|
env.require = function(modname)
|
||||||
return __fast_require(env, modname)
|
return __fast_require(env, modname)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Install cookie module methods directly into environment
|
||||||
|
env.cookie = {
|
||||||
|
get = function(name)
|
||||||
|
if type(name) ~= "string" then
|
||||||
|
error("cookie.get: name must be a string", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if env.ctx and env.ctx.cookies and env.ctx.cookies[name] then
|
||||||
|
return tostring(env.ctx.cookies[name])
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end,
|
||||||
|
|
||||||
|
set = cookie.set,
|
||||||
|
remove = cookie.remove
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Store reference to current environment
|
-- Store reference to current environment
|
||||||
|
|
Loading…
Reference in New Issue
Block a user