All checks were successful
Build Multi-Platform Binaries / build (push) Successful in 11m9s
149 lines
3.5 KiB
Go
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)()
|
|
}
|