package router import ( "context" "time" "github.com/xtls/xray-core/app/dns" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/geodata" "github.com/xtls/xray-core/common/log" luamgr "github.com/xtls/xray-core/common/lua" "github.com/xtls/xray-core/features/routing" lua "github.com/yuin/gopher-lua" ) const scriptExecutionTimeout = 10 * time.Second type scriptEngine struct { router *Router pool *luamgr.Pool } func newScriptEngine(path string, router *Router) (*scriptEngine, error) { program, err := luamgr.CompileFile(path) if err != nil { return nil, err } e := &scriptEngine{router: router} e.pool, err = luamgr.NewPool(router.ctx, program.NewStateFactory( scriptExecutionTimeout, func(L *lua.LState) { geodata.RegisterLua(L) log.RegisterLua(L) router.RegisterLua(L) dns.RegisterLua(L, router.dns) }, func(L *lua.LState) error { if L.GetGlobal("HandleRoute").Type() != lua.LTFunction { return errors.New("routing script must define HandleRoute(...)") } return nil })) if err != nil { return nil, err } errors.LogInfo(router.ctx, "routing script initialized from ", path) return e, nil } func (e *scriptEngine) close() { e.pool.Close() } func (e *scriptEngine) pickRoute(ctx routing.Context) (routing.Route, error) { var tag, ruleTag string err := e.pool.WithState(func(L *lua.LState) error { luaCtx, cancel := context.WithTimeout(e.pool.Context(), scriptExecutionTimeout) defer cancel() var luaErr error tag, ruleTag, luaErr = e.router.CallLuaHook(L, luaCtx, ctx) return luaErr }) if err != nil { return nil, err } if tag == "" { return nil, common.ErrNoClue } return &Route{Context: ctx, outboundTag: tag, ruleTag: ruleTag}, nil }