diff --git a/pkg/plugins/fixtures/withoutsocket/demo.go b/pkg/plugins/fixtures/withoutsocket/demo.go new file mode 100644 index 000000000..92b1a6ddb --- /dev/null +++ b/pkg/plugins/fixtures/withoutsocket/demo.go @@ -0,0 +1,52 @@ +package main + +import ( + "encoding/json" + "fmt" + "net/http" + "os" + + "github.com/http-wasm/http-wasm-guest-tinygo/handler" + "github.com/http-wasm/http-wasm-guest-tinygo/handler/api" +) + +type config struct { + File string `json:"file"` + Envs []string `json:"envs"` +} + +var cfg config + +// Built with +// tinygo build -o plugin.wasm -scheduler=none --no-debug -target=wasi ./demo.go +func main() { + err := json.Unmarshal(handler.Host.GetConfig(), &cfg) + if err != nil { + handler.Host.Log(api.LogLevelError, fmt.Sprintf("Could not load config %v", err)) + os.Exit(1) + } + + handler.HandleRequestFn = handleRequest +} + +func handleRequest(req api.Request, resp api.Response) (next bool, reqCtx uint32) { + var bodyContent []byte + if cfg.File != "" { + var err error + bodyContent, err = os.ReadFile(cfg.File) + if err != nil { + resp.SetStatusCode(http.StatusInternalServerError) + resp.Body().Write([]byte(fmt.Sprintf("error reading file %q: %w", cfg.File, err.Error()))) + return false, 0 + } + } + + if len(cfg.Envs) > 0 { + for _, env := range cfg.Envs { + bodyContent = append(bodyContent, []byte(os.Getenv(env)+"\n")...) + } + } + + resp.Body().Write(bodyContent) + return false, 0 +} diff --git a/pkg/plugins/fixtures/withoutsocket/go.mod b/pkg/plugins/fixtures/withoutsocket/go.mod new file mode 100644 index 000000000..f684c5a2c --- /dev/null +++ b/pkg/plugins/fixtures/withoutsocket/go.mod @@ -0,0 +1,5 @@ +module withoutsocket + +go 1.23.0 + +require github.com/http-wasm/http-wasm-guest-tinygo v0.4.0 diff --git a/pkg/plugins/fixtures/withoutsocket/go.sum b/pkg/plugins/fixtures/withoutsocket/go.sum new file mode 100644 index 000000000..fab3d3577 --- /dev/null +++ b/pkg/plugins/fixtures/withoutsocket/go.sum @@ -0,0 +1,2 @@ +github.com/http-wasm/http-wasm-guest-tinygo v0.4.0 h1:sWd1hqOL8LF3DVRPXloVELTQItibKtDCtVSA4UfMf4Y= +github.com/http-wasm/http-wasm-guest-tinygo v0.4.0/go.mod h1:zcKr7h/t5ha2ZWIMwV4iOqhfC/qno/tNPYgybVkn/MQ= diff --git a/pkg/plugins/fixtures/withoutsocket/plugin.wasm b/pkg/plugins/fixtures/withoutsocket/plugin.wasm new file mode 100644 index 000000000..23df7b8da Binary files /dev/null and b/pkg/plugins/fixtures/withoutsocket/plugin.wasm differ diff --git a/pkg/plugins/middlewarewasm.go b/pkg/plugins/middlewarewasm.go index 8b182a1dc..c33858a9d 100644 --- a/pkg/plugins/middlewarewasm.go +++ b/pkg/plugins/middlewarewasm.go @@ -83,7 +83,7 @@ func (b *wasmMiddlewareBuilder) buildMiddleware(ctx context.Context, next http.H config := wazero.NewModuleConfig().WithSysWalltime().WithStartFunctions("_start", "_initialize") for _, env := range b.settings.Envs { - config.WithEnv(env, os.Getenv(env)) + config = config.WithEnv(env, os.Getenv(env)) } if len(b.settings.Mounts) > 0 { @@ -97,14 +97,14 @@ func (b *wasmMiddlewareBuilder) buildMiddleware(ctx context.Context, next http.H parts := strings.Split(prefix, ":") switch { case len(parts) == 1: - withDir(parts[0], parts[0]) + fsConfig = withDir(parts[0], parts[0]) case len(parts) == 2: - withDir(parts[0], parts[1]) + fsConfig = withDir(parts[0], parts[1]) default: return nil, nil, fmt.Errorf("invalid directory %q", mount) } } - config.WithFSConfig(fsConfig) + config = config.WithFSConfig(fsConfig) } opts := []handler.Option{ diff --git a/pkg/plugins/middlewarewasm_test.go b/pkg/plugins/middlewarewasm_test.go new file mode 100644 index 000000000..38fee4bbe --- /dev/null +++ b/pkg/plugins/middlewarewasm_test.go @@ -0,0 +1,120 @@ +package plugins + +import ( + "context" + "net/http" + "net/http/httptest" + "os" + "path" + "reflect" + "testing" + + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tetratelabs/wazero" +) + +func TestSettingsWithoutSocket(t *testing.T) { + cache := wazero.NewCompilationCache() + + zerolog.SetGlobalLevel(zerolog.DebugLevel) + + ctx := log.Logger.WithContext(context.Background()) + + t.Setenv("PLUGIN_TEST", "MY-TEST") + t.Setenv("PLUGIN_TEST_B", "MY-TEST_B") + + testCases := []struct { + desc string + getSettings func(t *testing.T) (Settings, map[string]interface{}) + expected string + }{ + { + desc: "mounts path", + getSettings: func(t *testing.T) (Settings, map[string]interface{}) { + t.Helper() + + tempDir := t.TempDir() + filePath := path.Join(tempDir, "hello.txt") + err := os.WriteFile(filePath, []byte("content_test"), 0o644) + require.NoError(t, err) + + return Settings{Mounts: []string{ + tempDir, + }}, map[string]interface{}{ + "file": filePath, + } + }, + expected: "content_test", + }, + { + desc: "mounts src to dest", + getSettings: func(t *testing.T) (Settings, map[string]interface{}) { + t.Helper() + + tempDir := t.TempDir() + filePath := path.Join(tempDir, "hello.txt") + err := os.WriteFile(filePath, []byte("content_test"), 0o644) + require.NoError(t, err) + + return Settings{Mounts: []string{ + tempDir + ":/tmp", + }}, map[string]interface{}{ + "file": "/tmp/hello.txt", + } + }, + expected: "content_test", + }, + { + desc: "one env", + getSettings: func(t *testing.T) (Settings, map[string]interface{}) { + t.Helper() + + envs := []string{"PLUGIN_TEST"} + return Settings{Envs: envs}, map[string]interface{}{ + "envs": envs, + } + }, + expected: "MY-TEST\n", + }, + { + desc: "two env", + getSettings: func(t *testing.T) (Settings, map[string]interface{}) { + t.Helper() + + envs := []string{"PLUGIN_TEST", "PLUGIN_TEST_B"} + return Settings{Envs: envs}, map[string]interface{}{ + "envs": envs, + } + }, + expected: "MY-TEST\nMY-TEST_B\n", + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + settings, config := test.getSettings(t) + + builder := &wasmMiddlewareBuilder{path: "./fixtures/withoutsocket/plugin.wasm", cache: cache, settings: settings} + + cfg := reflect.ValueOf(config) + + m, applyCtx, err := builder.buildMiddleware(ctx, http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.WriteHeader(http.StatusTeapot) + }), cfg, "test") + require.NoError(t, err) + + rw := httptest.NewRecorder() + req := httptest.NewRequestWithContext(applyCtx(ctx), "GET", "/", http.NoBody) + + m.ServeHTTP(rw, req) + + assert.Equal(t, http.StatusOK, rw.Code) + assert.Equal(t, test.expected, rw.Body.String()) + }) + } +}