Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wasm middleware: add ability to pass config to guest #2918

Merged
merged 14 commits into from
Aug 7, 2023
6 changes: 3 additions & 3 deletions middleware/http/wasm/benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,19 @@ func BenchmarkNative(b *testing.B) {
}

func BenchmarkTinygo(b *testing.B) {
path := "./internal/e2e-guests/rewrite/main.wasm"
path := "file://internal/e2e-guests/rewrite/main.wasm"
Taction marked this conversation as resolved.
Show resolved Hide resolved
benchmarkMiddleware(b, path)
}

// BenchmarkWat gives baseline performance for the same handler by
// writing it directly in WebAssembly Text Format.
func BenchmarkWat(b *testing.B) {
path := "./internal/testdata/rewrite.wasm"
path := "file://internal/testdata/rewrite.wasm"
benchmarkMiddleware(b, path)
}

func benchmarkMiddleware(b *testing.B, path string) {
md := metadata.Base{Properties: map[string]string{"path": path}}
md := metadata.Base{Properties: map[string]string{"url": path}}

l := logger.NewLogger(b.Name())
l.SetOutput(io.Discard)
Expand Down
Binary file modified middleware/http/wasm/example/router.wasm
Binary file not shown.
7 changes: 6 additions & 1 deletion middleware/http/wasm/httpwasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"github.com/dapr/kit/logger"
)

const metadataConfig = "config"
ItalyPaleAle marked this conversation as resolved.
Show resolved Hide resolved

type middleware struct {
logger logger.Logger
}
Expand All @@ -43,6 +45,8 @@ func (m *middleware) getHandler(ctx context.Context, metadata dapr.Metadata) (*r
return nil, fmt.Errorf("wasm: failed to parse metadata: %w", err)
}

wasmConfig := []byte(metadata.Properties[metadataConfig])

var stdout, stderr bytes.Buffer
mw, err := wasmnethttp.NewMiddleware(ctx, meta.Guest,
handler.Logger(m),
Expand All @@ -54,7 +58,8 @@ func (m *middleware) getHandler(ctx context.Context, metadata dapr.Metadata) (*r
WithRandSource(rand.Reader).
WithSysNanosleep().
WithSysWalltime().
WithSysNanosleep()))
WithSysNanosleep()),
handler.GuestConfig(wasmConfig))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you raise a PR upstream to coerce empty to nil?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When writing into memory, the upstream has checked the len of config bytes, does it necessary to coerce empty to nil?

// getConfig implements the WebAssembly host function handler.FuncGetConfig.
func (m *middleware) getConfig(_ context.Context, mod wazeroapi.Module, stack []uint64) {
	buf := uint32(stack[0])
	bufLimit := handler.BufLimit(stack[1])

	configLen := writeIfUnderLimit(mod.Memory(), buf, bufLimit, m.guestConfig)

	stack[0] = uint64(configLen)
}

func writeIfUnderLimit(mem wazeroapi.Memory, offset, limit handler.BufLimit, v []byte) (vLen uint32) {
	vLen = uint32(len(v))
	if vLen > limit {
		return // caller can retry with a larger limit
	} else if vLen == 0 {
		return // nothing to write
	}
	mem.Write(offset, v)
	return
}

if err != nil {
return nil, err
}
Expand Down
12 changes: 12 additions & 0 deletions middleware/http/wasm/internal/e2e-guests/config/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Package main ensures tests can prove logging or stdio isn't missed, both
ItalyPaleAle marked this conversation as resolved.
Show resolved Hide resolved
// during initialization (main) and request (rewrite).
package main

import (
"github.com/http-wasm/http-wasm-guest-tinygo/handler"
"github.com/http-wasm/http-wasm-guest-tinygo/handler/api"
)

func main() {
handler.Host.Log(api.LogLevelInfo, string(handler.Host.GetConfig()))
}
Binary file not shown.
Binary file modified middleware/http/wasm/internal/e2e-guests/output/main.wasm
Binary file not shown.
Binary file modified middleware/http/wasm/internal/e2e-guests/rewrite/main.wasm
ItalyPaleAle marked this conversation as resolved.
Show resolved Hide resolved
Binary file not shown.
23 changes: 19 additions & 4 deletions middleware/http/wasm/internal/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ var guestWasm map[string][]byte
const (
guestWasmOutput = "output"
guestWasmRewrite = "rewrite"
guestWasmConfig = "config"
)

// TestMain ensures we can read the test wasm prior to running e2e tests.
func TestMain(m *testing.M) {
wasms := []string{guestWasmOutput, guestWasmRewrite}
wasms := []string{guestWasmOutput, guestWasmRewrite, guestWasmConfig}
guestWasm = make(map[string][]byte, len(wasms))
for _, name := range wasms {
if wasm, err := os.ReadFile(path.Join("e2e-guests", name, "main.wasm")); err != nil {
Expand All @@ -47,9 +48,10 @@ func Test_EndToEnd(t *testing.T) {
l.SetOutput(&buf)

type testCase struct {
name string
guest []byte
test func(t *testing.T, handler http.Handler, log *bytes.Buffer)
name string
guest []byte
property map[string]string
test func(t *testing.T, handler http.Handler, log *bytes.Buffer)
}

tests := []testCase{
Expand Down Expand Up @@ -126,6 +128,14 @@ func Test_EndToEnd(t *testing.T) {
require.Equal(t, "/v1.0/hello?name=teddy", r.URL.RequestURI())
},
},
{
name: "consoleLog config",
ItalyPaleAle marked this conversation as resolved.
Show resolved Hide resolved
guest: guestWasm[guestWasmConfig],
property: map[string]string{"config": "config bytes in any format"},
test: func(t *testing.T, handler http.Handler, log *bytes.Buffer) {
require.Contains(t, log.String(), "config bytes in any format")
},
},
}

for _, tt := range tests {
Expand All @@ -137,6 +147,11 @@ func Test_EndToEnd(t *testing.T) {
require.NoError(t, os.WriteFile(wasmPath, tc.guest, 0o600))

meta := metadata.Base{Properties: map[string]string{"url": "file://" + wasmPath}}
if len(tc.property) > 0 {
for k, v := range tc.property {
meta.Properties[k] = v
}
}
handlerFn, err := wasm.NewMiddleware(l).GetHandler(context.Background(), middleware.Metadata{Base: meta})
require.NoError(t, err)
handler := handlerFn(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
Expand Down