diff --git a/app/dns/lua.go b/app/dns/lua.go index 1a429319b..ec1cff555 100644 --- a/app/dns/lua.go +++ b/app/dns/lua.go @@ -46,8 +46,8 @@ func (s *DNS) RegisterLua(L *lua.LState) { } else { ips, ttl, err = client.QueryIP(ctx, string(domain), option) } - result := L.NewTable() - addresses := L.NewTable() + result := L.CreateTable(0, 3) + addresses := L.CreateTable(len(ips), 0) for j, ip := range ips { address := L.NewUserData() address.Value = ip @@ -76,7 +76,7 @@ func (s *DNS) RegisterLua(L *lua.LState) { // must already have passed DNS normalization, hosts, and address-family handling. // The caller serializes access to its state; ctx cancels Lua execution and upstream calls. func (s *DNS) CallLuaHook(L *lua.LState, ctx context.Context, domain string, option featureDNS.IPOption) ([]net.IP, uint32, error) { - q := L.NewTable() + q := L.CreateTable(0, 4) q.RawSetString("domain", lua.LString(strings.ToLower(domain))) q.RawSetString("ipv4", lua.LBool(option.IPv4Enable)) q.RawSetString("ipv6", lua.LBool(option.IPv6Enable)) diff --git a/app/dns/lua_test.go b/app/dns/lua_test.go index 46e9e3f5f..5c8936a95 100644 --- a/app/dns/lua_test.go +++ b/app/dns/lua_test.go @@ -139,3 +139,61 @@ func TestCallLuaHookNormalizesDomain(t *testing.T) { t.Fatal(err) } } + +type benchmarkLuaNameServer struct { + ips []net.IP +} + +func (*benchmarkLuaNameServer) Name() string { return "benchmark" } +func (*benchmarkLuaNameServer) IsDisableCache() bool { return true } +func (s *benchmarkLuaNameServer) QueryIP(context.Context, string, featureDNS.IPOption) ([]net.IP, uint32, error) { + return s.ips, 60, nil +} + +// BenchmarkLuaDNSHookCall isolates a preloaded Lua hook and its server:query bridge. +// The direct case measures the same DNS client without Lua. +func BenchmarkLuaDNSHookCall(b *testing.B) { + option := featureDNS.IPOption{IPv4Enable: true} + ip := net.ParseIP("127.0.0.1") + upstream := &benchmarkLuaNameServer{ips: []net.IP{ip}} + client := &Client{server: upstream, ipOption: &option, timeoutMs: time.Second} + server := &DNS{clients: []*Client{client}} + L := lua.NewState() + defer L.Close() + server.RegisterLua(L) + if err := L.DoString(` +local server = require("xray.dns").servers[1] +function handleDNSQuery(q) + return server:query(q) +end +`); err != nil { + b.Fatal(err) + } + + ctx := context.Background() + for _, bench := range []struct { + name string + query func() ([]net.IP, uint32, error) + }{ + {"direct", func() ([]net.IP, uint32, error) { return client.QueryIP(ctx, "example.com", option) }}, + {"lua_hook", func() ([]net.IP, uint32, error) { return server.CallLuaHook(L, ctx, "example.com", option) }}, + } { + b.Run(bench.name, func(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + var ips []net.IP + var ttl uint32 + var err error + for i := 0; i < b.N; i++ { + ips, ttl, err = bench.query() + if err != nil { + b.Fatal(err) + } + } + b.StopTimer() + if ttl != 60 || len(ips) != 1 || !ips[0].Equal(ip) { + b.Fatalf("query() = %v, TTL %d; want %v, TTL 60", ips, ttl, ip) + } + }) + } +}