lolly/internal/lua/middleware.go
xfy 2734b04d8f refactor: remove 16.8k lines of dead code across all internal packages
- Delete unused files: tempfile subsystem, matcher variants, server/internal
- Remove 200+ unused functions across proxy, ssl, lua, http2/3, stream, variable
- Fix proxy test type errors (backgroundRefresh ctx→Request)
- Move bench/tools mock backend into internal/testutil
- Remove corresponding test functions for all deleted code
2026-06-03 16:15:43 +08:00

350 lines
9.0 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package lua 提供 Lua 中间件实现。
//
// 该文件包含 fasthttp 中间件的 Lua 集成,包括:
// - LuaMiddleware单阶段 Lua 中间件
// - MultiPhaseLuaMiddleware多阶段 Lua 中间件,支持在请求生命周期不同阶段执行不同脚本
//
// 中间件执行顺序(从外到内):
//
// rewrite -> access -> content -> header_filter -> body_filter -> log
//
// 注意事项:
// - 中间件在协程创建失败时记录错误并继续执行下一处理器
// - ngx.exit/ngx.redirect 导致的终止视为正常行为,不返回 500 错误
//
// 作者xfy
package lua
import (
"fmt"
"strings"
"time"
"github.com/valyala/fasthttp"
)
// LuaMiddleware 单阶段 Lua 中间件。
//
// 包装 fasthttp.RequestHandler在执行实际请求处理前运行指定的 Lua 脚本。
// 脚本在独立的协程中执行,拥有自己的沙箱环境。
type LuaMiddleware struct {
// engine Lua 引擎实例
engine *LuaEngine
// scriptPath Lua 脚本文件路径
scriptPath string
// name 中间件名称
name string
// phase 执行阶段
phase Phase
// timeout 执行超时(当前未使用超时控制)
timeout time.Duration
// enabled 是否启用
enabled bool
}
// LuaMiddlewareConfig Lua 中间件配置参数。
type LuaMiddlewareConfig struct {
// ScriptPath Lua 脚本文件路径(必填)
ScriptPath string
// Name 中间件名称(为空时自动生成)
Name string
// Phase 执行阶段(默认为 PhaseContent
Phase Phase
// Timeout 执行超时(默认为 30 秒)
Timeout time.Duration
// Enabled 是否启用(默认 true
Enabled bool
// EnabledSet 是否显式设置了 Enabled用于区分零值和显式 false
EnabledSet bool
}
// NewLuaMiddleware 创建 Lua 中间件实例。
//
// 参数:
// - engine: Lua 引擎实例
// - config: 中间件配置
//
// 返回值:
// - *LuaMiddleware: 中间件实例
// - error: 参数验证失败时返回错误
//
// 注意事项:
// - ScriptPath 不能为空
// - engine 不能为 nil
// - Timeout 为零时自动设置为 30 秒默认值
func NewLuaMiddleware(engine *LuaEngine, config LuaMiddlewareConfig) (*LuaMiddleware, error) {
if engine == nil {
return nil, fmt.Errorf("lua engine is required")
}
if config.ScriptPath == "" {
return nil, fmt.Errorf("script path is required")
}
// 设置默认值
if config.Timeout == 0 {
config.Timeout = 30 * time.Second
}
// Enabled 默认值处理:
// - EnabledSet 为 true 时,使用显式设置的 Enabled 值
// - EnabledSet 为 false 时(零值),默认启用
if !config.EnabledSet {
config.Enabled = true
}
// 生成默认名称
if config.Name == "" {
config.Name = fmt.Sprintf("lua-%s", config.Phase.String())
}
return &LuaMiddleware{
engine: engine,
scriptPath: config.ScriptPath,
phase: config.Phase,
timeout: config.Timeout,
name: config.Name,
enabled: config.Enabled,
}, nil
}
// Name 返回中间件名称。
func (m *LuaMiddleware) Name() string {
return m.name
}
// Process 包装请求处理器,注入 Lua 脚本执行逻辑。
//
// 执行流程:
// 1. 检查中间件是否启用,未启用则直接调用 next
// 2. 创建 Lua 上下文并设置阶段
// 3. 初始化协程(失败时记录错误并继续)
// 4. 执行 Lua 脚本文件
// 5. 处理 ngx.exit/ngx.redirect 导致的终止(视为正常行为)
// 6. 非 ngx.exit 错误时设置 500 响应
// 7. 刷新输出缓冲
// 8. 如果脚本调用了 ngx.exit不继续执行 next
// 9. 否则继续执行后续处理器
//
// 返回值:
// - fasthttp.RequestHandler: 包装后的处理器
func (m *LuaMiddleware) Process(next fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
// 检查是否启用
if !m.enabled {
next(ctx)
return
}
// 创建 Lua 上下文
luaCtx := NewContext(m.engine, ctx)
luaCtx.SetPhase(m.phase)
// 初始化协程
if err := luaCtx.InitCoroutine(); err != nil {
// 协程创建失败,返回 500 错误,不继续执行后续处理器
ctx.Error(fmt.Sprintf("lua coroutine init failed: %v", err), fasthttp.StatusInternalServerError)
luaCtx.Release()
return
}
// 执行脚本
err := luaCtx.ExecuteFile(m.scriptPath)
// 检查是否为 ngx.exit/redirect 导致的终止(正常行为)
// 这些 API 通过 RaiseError 终止执行,错误消息包含 "ngx.exit" 或 "ngx.redirect"
isNgxExit := err != nil && (strings.Contains(err.Error(), "ngx.exit") ||
strings.Contains(err.Error(), "ngx.redirect"))
// 如果是 ngx.exit手动设置 Exited 标记
// 因为 setupNgxAPI 中 ngxLogAPI.luaCtx 为 nilExit() 只设置了 RequestCtx.StatusCode()
if isNgxExit {
luaCtx.Exited = true
}
// 只有非 ngx.exit/redirect 错误才设置错误响应
if err != nil && !isNgxExit && !luaCtx.Exited {
ctx.Error(fmt.Sprintf("lua execution failed: %v", err), fasthttp.StatusInternalServerError)
}
// 刷新输出缓冲
luaCtx.FlushOutput()
// 检查退出状态(在 Release 之前)
exited := luaCtx.Exited
// 释放资源
luaCtx.Release()
// 如果已退出,不再继续执行后续处理器
if exited {
return
}
// 继续执行后续处理器
next(ctx)
}
}
// SetEnabled 设置启用状态
func (m *LuaMiddleware) SetEnabled(enabled bool) {
m.enabled = enabled
}
// SetPhase 设置执行阶段
func (m *LuaMiddleware) SetPhase(phase Phase) {
m.phase = phase
m.name = fmt.Sprintf("lua-%s", phase.String())
}
// SetTimeout 设置超时时间
func (m *LuaMiddleware) SetTimeout(timeout time.Duration) {
m.timeout = timeout
}
// SetScriptPath 设置脚本路径
func (m *LuaMiddleware) SetScriptPath(path string) {
m.scriptPath = path
}
// GetPhase 获取执行阶段
func (m *LuaMiddleware) GetPhase() Phase {
return m.phase
}
// GetScriptPath 获取脚本路径
func (m *LuaMiddleware) GetScriptPath() string {
return m.scriptPath
}
// IsEnabled 检查是否启用
func (m *LuaMiddleware) IsEnabled() bool {
return m.enabled
}
// MultiPhaseLuaMiddleware 多阶段 Lua 中间件。
//
// 支持在不同请求处理阶段执行不同的 Lua 脚本。
// 阶段按逆序包装,确保执行顺序为:
//
// rewrite -> access -> content -> header_filter -> body_filter -> log
type MultiPhaseLuaMiddleware struct {
// engine Lua 引擎实例
engine *LuaEngine
// phases 各阶段对应的单阶段中间件
phases map[Phase]*LuaMiddleware
// name 中间件名称
name string
}
// NewMultiPhaseLuaMiddleware 创建多阶段 Lua 中间件。
//
// 参数:
// - engine: Lua 引擎实例
// - name: 中间件名称
//
// 返回值:
// - *MultiPhaseLuaMiddleware: 初始化的多阶段中间件
func NewMultiPhaseLuaMiddleware(engine *LuaEngine, name string) *MultiPhaseLuaMiddleware {
return &MultiPhaseLuaMiddleware{
engine: engine,
phases: make(map[Phase]*LuaMiddleware),
name: name,
}
}
// Name 返回中间件名称。
func (m *MultiPhaseLuaMiddleware) Name() string {
return m.name
}
// AddPhase 为指定阶段添加 Lua 脚本。
//
// 参数:
// - phase: 处理阶段
// - scriptPath: 脚本文件路径
// - timeout: 执行超时
//
// 返回值:
// - error: 中间件创建失败时返回错误
func (m *MultiPhaseLuaMiddleware) AddPhase(phase Phase, scriptPath string, timeout time.Duration) error {
config := LuaMiddlewareConfig{
ScriptPath: scriptPath,
Phase: phase,
Timeout: timeout,
Name: fmt.Sprintf("%s-%s", m.name, phase.String()),
Enabled: true, // 多阶段配置默认启用
EnabledSet: true, // 显式设置
}
middleware, err := NewLuaMiddleware(m.engine, config)
if err != nil {
return err
}
m.phases[phase] = middleware
return nil
}
// Process 包装请求处理器,按逆序添加各阶段中间件。
//
// 执行顺序(从先到后):
//
// rewrite -> access -> content -> header_filter -> body_filter -> log
//
// 通过在包装链中逆序注册(从 log 开始),确保实际执行时先执行 rewrite。
func (m *MultiPhaseLuaMiddleware) Process(next fasthttp.RequestHandler) fasthttp.RequestHandler {
handler := next
// 按逆序包装,确保执行顺序正确
// log -> body_filter -> header_filter -> content -> access -> rewrite
phaseOrder := []Phase{
PhaseLog,
PhaseBodyFilter,
PhaseHeaderFilter,
PhaseContent,
PhaseAccess,
PhaseRewrite,
}
for _, phase := range phaseOrder {
if middleware, ok := m.phases[phase]; ok {
handler = middleware.Process(handler)
}
}
return handler
}
// GetPhaseMiddleware 获取指定阶段的中间件
func (m *MultiPhaseLuaMiddleware) GetPhaseMiddleware(phase Phase) *LuaMiddleware {
return m.phases[phase]
}
// RemovePhase 移除阶段脚本
func (m *MultiPhaseLuaMiddleware) RemovePhase(phase Phase) {
delete(m.phases, phase)
}
// HasPhase 检查是否有指定阶段的脚本
func (m *MultiPhaseLuaMiddleware) HasPhase(phase Phase) bool {
return m.phases[phase] != nil
}
// PhaseCount 返回阶段数量
func (m *MultiPhaseLuaMiddleware) PhaseCount() int {
return len(m.phases)
}