traefik/pkg/plugins/middlewarewasm.go
2023-11-30 21:42:06 +01:00

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)
}