add plugins
All checks were successful
Build Multi-Platform Binaries / build (push) Successful in 11m9s
All checks were successful
Build Multi-Platform Binaries / build (push) Successful in 11m9s
This commit is contained in:
146
pkg/plugin/wasm/host.go
Normal file
146
pkg/plugin/wasm/host.go
Normal 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
29
pkg/plugin/wasm/memory.go
Normal 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
148
pkg/plugin/wasm/module.go
Normal 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
116
pkg/plugin/wasm/runtime.go
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user