add plugins
All checks were successful
Build Multi-Platform Binaries / build (push) Successful in 11m9s

This commit is contained in:
Flik
2025-12-26 11:24:23 +08:00
parent d56fdafc1e
commit 4623a7f031
27 changed files with 2090 additions and 97 deletions

146
pkg/plugin/wasm/host.go Normal file
View File

@@ -0,0 +1,146 @@
package wasm
import (
"errors"
"log"
"net"
"sync"
"time"
"github.com/gotunnel/pkg/plugin"
)
// ErrInvalidHandle 无效的连接句柄
var ErrInvalidHandle = errors.New("invalid connection handle")
// HostContextImpl 实现 HostContext 接口
type HostContextImpl struct {
dialer plugin.Dialer
clientConn net.Conn
config map[string]string
// 连接管理
conns map[plugin.ConnHandle]net.Conn
nextHandle plugin.ConnHandle
mu sync.Mutex
}
// NewHostContext 创建 host context
func NewHostContext(dialer plugin.Dialer, clientConn net.Conn, config map[string]string) *HostContextImpl {
return &HostContextImpl{
dialer: dialer,
clientConn: clientConn,
config: config,
conns: make(map[plugin.ConnHandle]net.Conn),
nextHandle: 1,
}
}
// Dial 通过隧道建立连接
func (h *HostContextImpl) Dial(network, address string) (plugin.ConnHandle, error) {
conn, err := h.dialer.Dial(network, address)
if err != nil {
return 0, err
}
h.mu.Lock()
handle := h.nextHandle
h.nextHandle++
h.conns[handle] = conn
h.mu.Unlock()
return handle, nil
}
// Read 从连接读取数据
func (h *HostContextImpl) Read(handle plugin.ConnHandle, buf []byte) (int, error) {
h.mu.Lock()
conn, ok := h.conns[handle]
h.mu.Unlock()
if !ok {
return 0, ErrInvalidHandle
}
return conn.Read(buf)
}
// Write 向连接写入数据
func (h *HostContextImpl) Write(handle plugin.ConnHandle, buf []byte) (int, error) {
h.mu.Lock()
conn, ok := h.conns[handle]
h.mu.Unlock()
if !ok {
return 0, ErrInvalidHandle
}
return conn.Write(buf)
}
// CloseConn 关闭连接
func (h *HostContextImpl) CloseConn(handle plugin.ConnHandle) error {
h.mu.Lock()
conn, ok := h.conns[handle]
if ok {
delete(h.conns, handle)
}
h.mu.Unlock()
if !ok {
return ErrInvalidHandle
}
return conn.Close()
}
// ClientRead 从客户端连接读取数据
func (h *HostContextImpl) ClientRead(buf []byte) (int, error) {
return h.clientConn.Read(buf)
}
// ClientWrite 向客户端连接写入数据
func (h *HostContextImpl) ClientWrite(buf []byte) (int, error) {
return h.clientConn.Write(buf)
}
// Log 记录日志
func (h *HostContextImpl) Log(level plugin.LogLevel, message string) {
prefix := "[WASM]"
switch 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]"
}
log.Printf("%s %s", prefix, message)
}
// Now 返回当前 Unix 时间戳
func (h *HostContextImpl) Now() int64 {
return time.Now().Unix()
}
// GetConfig 获取配置值
func (h *HostContextImpl) GetConfig(key string) string {
if h.config == nil {
return ""
}
return h.config[key]
}
// Close 关闭所有连接
func (h *HostContextImpl) Close() error {
h.mu.Lock()
defer h.mu.Unlock()
for handle, conn := range h.conns {
conn.Close()
delete(h.conns, handle)
}
return nil
}

29
pkg/plugin/wasm/memory.go Normal file
View File

@@ -0,0 +1,29 @@
package wasm
import (
"github.com/tetratelabs/wazero/api"
)
// ReadString 从 WASM 内存读取字符串
func ReadString(mem api.Memory, ptr, len uint32) (string, bool) {
data, ok := mem.Read(ptr, len)
if !ok {
return "", false
}
return string(data), true
}
// WriteString 向 WASM 内存写入字符串
func WriteString(mem api.Memory, ptr uint32, s string) bool {
return mem.Write(ptr, []byte(s))
}
// ReadBytes 从 WASM 内存读取字节
func ReadBytes(mem api.Memory, ptr, len uint32) ([]byte, bool) {
return mem.Read(ptr, len)
}
// WriteBytes 向 WASM 内存写入字节
func WriteBytes(mem api.Memory, ptr uint32, data []byte) bool {
return mem.Write(ptr, data)
}

148
pkg/plugin/wasm/module.go Normal file
View File

@@ -0,0 +1,148 @@
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)()
}

116
pkg/plugin/wasm/runtime.go Normal file
View File

@@ -0,0 +1,116 @@
package wasm
import (
"context"
"fmt"
"sync"
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api"
)
// Runtime 管理 wazero WASM 运行时
type Runtime struct {
runtime wazero.Runtime
modules map[string]*Module
mu sync.RWMutex
}
// NewRuntime 创建新的 WASM 运行时
func NewRuntime(ctx context.Context) (*Runtime, error) {
r := wazero.NewRuntime(ctx)
return &Runtime{
runtime: r,
modules: make(map[string]*Module),
}, nil
}
// GetWazeroRuntime 返回底层 wazero 运行时
func (r *Runtime) GetWazeroRuntime() wazero.Runtime {
return r.runtime
}
// LoadModule 从字节加载 WASM 模块
func (r *Runtime) LoadModule(ctx context.Context, name string, wasmBytes []byte) (*Module, error) {
r.mu.Lock()
defer r.mu.Unlock()
if _, exists := r.modules[name]; exists {
return nil, fmt.Errorf("module %s already loaded", name)
}
compiled, err := r.runtime.CompileModule(ctx, wasmBytes)
if err != nil {
return nil, fmt.Errorf("failed to compile module: %w", err)
}
module := &Module{
name: name,
compiled: compiled,
}
r.modules[name] = module
return module, nil
}
// GetModule 获取已加载的模块
func (r *Runtime) GetModule(name string) (*Module, bool) {
r.mu.RLock()
defer r.mu.RUnlock()
m, ok := r.modules[name]
return m, ok
}
// UnloadModule 卸载 WASM 模块
func (r *Runtime) UnloadModule(ctx context.Context, name string) error {
r.mu.Lock()
defer r.mu.Unlock()
module, exists := r.modules[name]
if !exists {
return fmt.Errorf("module %s not found", name)
}
if err := module.Close(ctx); err != nil {
return err
}
delete(r.modules, name)
return nil
}
// Close 关闭运行时
func (r *Runtime) Close(ctx context.Context) error {
r.mu.Lock()
defer r.mu.Unlock()
for name, module := range r.modules {
if err := module.Close(ctx); err != nil {
return fmt.Errorf("failed to close module %s: %w", name, err)
}
}
return r.runtime.Close(ctx)
}
// Module WASM 模块封装
type Module struct {
name string
compiled wazero.CompiledModule
instance api.Module
}
// Name 返回模块名称
func (m *Module) Name() string {
return m.name
}
// Close 关闭模块
func (m *Module) Close(ctx context.Context) error {
if m.instance != nil {
if err := m.instance.Close(ctx); err != nil {
return err
}
}
return m.compiled.Close(ctx)
}