Files
GoTunnel/pkg/plugin/wasm/module.go
Flik 4623a7f031
All checks were successful
Build Multi-Platform Binaries / build (push) Successful in 11m9s
add plugins
2025-12-26 11:24:23 +08:00

149 lines
3.5 KiB
Go

package wasm
import (
"context"
"encoding/json"
"fmt"
"github.com/gotunnel/pkg/plugin"
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api"
)
// WASMPlugin 封装 WASM 模块作为 ProxyHandler
type WASMPlugin struct {
name string
metadata plugin.PluginMetadata
runtime *Runtime
compiled wazero.CompiledModule
config map[string]string
}
// NewWASMPlugin 从 WASM 字节创建 plugin
func NewWASMPlugin(ctx context.Context, rt *Runtime, name string, wasmBytes []byte) (*WASMPlugin, error) {
compiled, err := rt.runtime.CompileModule(ctx, wasmBytes)
if err != nil {
return nil, fmt.Errorf("compile module: %w", err)
}
p := &WASMPlugin{
name: name,
runtime: rt,
compiled: compiled,
}
// 尝试获取元数据
if err := p.loadMetadata(ctx); err != nil {
// 使用默认元数据
p.metadata = plugin.PluginMetadata{
Name: name,
Type: plugin.PluginTypeProxy,
Source: plugin.PluginSourceWASM,
}
}
return p, nil
}
// loadMetadata 从 WASM 模块加载元数据
func (p *WASMPlugin) loadMetadata(ctx context.Context) error {
// 创建临时实例获取元数据
inst, err := p.runtime.runtime.InstantiateModule(ctx, p.compiled, wazero.NewModuleConfig())
if err != nil {
return err
}
defer inst.Close(ctx)
metadataFn := inst.ExportedFunction("metadata")
if metadataFn == nil {
return fmt.Errorf("metadata function not exported")
}
allocFn := inst.ExportedFunction("alloc")
if allocFn == nil {
return fmt.Errorf("alloc function not exported")
}
// 分配缓冲区
results, err := allocFn.Call(ctx, 1024)
if err != nil {
return err
}
bufPtr := uint32(results[0])
// 调用 metadata 函数
results, err = metadataFn.Call(ctx, uint64(bufPtr), 1024)
if err != nil {
return err
}
actualLen := uint32(results[0])
// 读取元数据
mem := inst.Memory()
data, ok := mem.Read(bufPtr, actualLen)
if !ok {
return fmt.Errorf("failed to read metadata")
}
return json.Unmarshal(data, &p.metadata)
}
// Metadata 返回 plugin 信息
func (p *WASMPlugin) Metadata() plugin.PluginMetadata {
return p.metadata
}
// Init 初始化 plugin
func (p *WASMPlugin) Init(config map[string]string) error {
p.config = config
return nil
}
// HandleConn 处理连接
func (p *WASMPlugin) HandleConn(conn interface{}, dialer plugin.Dialer) error {
// WASM plugin 的连接处理需要更复杂的实现
// 这里提供基础框架,实际实现需要注册 host functions
return fmt.Errorf("WASM plugin HandleConn not fully implemented")
}
// Close 关闭 plugin
func (p *WASMPlugin) Close() error {
return p.compiled.Close(context.Background())
}
// RegisterHostFunctions 注册 host functions 到 wazero 运行时
func RegisterHostFunctions(ctx context.Context, r wazero.Runtime) (wazero.CompiledModule, error) {
return r.NewHostModuleBuilder("env").
NewFunctionBuilder().
WithFunc(hostLog).
Export("log").
NewFunctionBuilder().
WithFunc(hostNow).
Export("now").
Compile(ctx)
}
// host function 实现
func hostLog(ctx context.Context, m api.Module, level uint32, msgPtr, msgLen uint32) {
data, ok := m.Memory().Read(msgPtr, msgLen)
if !ok {
return
}
prefix := "[WASM]"
switch plugin.LogLevel(level) {
case plugin.LogDebug:
prefix = "[WASM DEBUG]"
case plugin.LogInfo:
prefix = "[WASM INFO]"
case plugin.LogWarn:
prefix = "[WASM WARN]"
case plugin.LogError:
prefix = "[WASM ERROR]"
}
fmt.Printf("%s %s\n", prefix, string(data))
}
func hostNow(ctx context.Context) int64 {
return ctx.Value("now").(func() int64)()
}