82 lines
2.1 KiB
Go
82 lines
2.1 KiB
Go
|
package plugins
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"net/http"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"reflect"
|
||
|
|
||
|
"github.com/http-wasm/http-wasm-host-go/handler"
|
||
|
wasm "github.com/http-wasm/http-wasm-host-go/handler/nethttp"
|
||
|
"github.com/tetratelabs/wazero"
|
||
|
"github.com/traefik/traefik/v3/pkg/logs"
|
||
|
"github.com/traefik/traefik/v3/pkg/middlewares"
|
||
|
)
|
||
|
|
||
|
type wasmMiddlewareBuilder struct {
|
||
|
path string
|
||
|
}
|
||
|
|
||
|
func newWasmMiddlewareBuilder(goPath string, moduleName, wasmPath string) *wasmMiddlewareBuilder {
|
||
|
return &wasmMiddlewareBuilder{path: filepath.Join(goPath, "src", moduleName, wasmPath)}
|
||
|
}
|
||
|
|
||
|
func (b wasmMiddlewareBuilder) newMiddleware(config map[string]interface{}, middlewareName string) (pluginMiddleware, error) {
|
||
|
return &WasmMiddleware{
|
||
|
middlewareName: middlewareName,
|
||
|
config: reflect.ValueOf(config),
|
||
|
builder: b,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
func (b wasmMiddlewareBuilder) newHandler(ctx context.Context, next http.Handler, cfg reflect.Value, middlewareName string) (http.Handler, error) {
|
||
|
code, err := os.ReadFile(b.path)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("loading Wasm binary: %w", err)
|
||
|
}
|
||
|
|
||
|
logger := middlewares.GetLogger(ctx, middlewareName, "wasm")
|
||
|
|
||
|
opts := []handler.Option{
|
||
|
handler.ModuleConfig(wazero.NewModuleConfig().WithSysWalltime()),
|
||
|
handler.Logger(logs.NewWasmLogger(logger)),
|
||
|
}
|
||
|
|
||
|
i := cfg.Interface()
|
||
|
if i != nil {
|
||
|
config, ok := i.(map[string]interface{})
|
||
|
if !ok {
|
||
|
return nil, fmt.Errorf("could not type assert config: %T", i)
|
||
|
}
|
||
|
|
||
|
data, err := json.Marshal(config)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("marshaling config: %w", err)
|
||
|
}
|
||
|
|
||
|
opts = append(opts, handler.GuestConfig(data))
|
||
|
}
|
||
|
|
||
|
mw, err := wasm.NewMiddleware(context.Background(), code, opts...)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return mw.NewHandler(ctx, next), nil
|
||
|
}
|
||
|
|
||
|
// WasmMiddleware is an HTTP handler plugin wrapper.
|
||
|
type WasmMiddleware struct {
|
||
|
middlewareName string
|
||
|
config reflect.Value
|
||
|
builder wasmMiddlewareBuilder
|
||
|
}
|
||
|
|
||
|
// NewHandler creates a new HTTP handler.
|
||
|
func (m WasmMiddleware) NewHandler(ctx context.Context, next http.Handler) (http.Handler, error) {
|
||
|
return m.builder.newHandler(ctx, next, m.config, m.middlewareName)
|
||
|
}
|