Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,20 @@ import (

func main() {}
func init() {
proxywasm.SetVMContext(&vmContext{})
// Plugin authors can use any one of four entrypoints, such as
// `proxywasm.SetVMContext`, `proxywasm.SetPluginContext`, or
// `proxywasm.SetTcpContext`.
proxywasm.SetHttpContext(func(contextID uint32) types.HttpContext {
return &httpContext{}
})
}

type vmContext struct {
types.DefaultVMContext
}
func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext {
return &pluginContext{}
type httpContext struct {
types.DefaultHttpContext
}

type pluginContext struct {
types.DefaultPluginContext
func (*httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
proxywasm.LogInfo("Hello, world!")
return types.ActionContinue
}
// pluginContext should implement OnPluginStart, NewHttpContext, NewTcpContext, etc
```

Compile the plugin as follows:
Expand Down
16 changes: 3 additions & 13 deletions examples/helloworld/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,9 @@ const tickMilliseconds uint32 = 1000

func main() {}
func init() {
proxywasm.SetVMContext(&vmContext{})
}

// vmContext implements types.VMContext.
type vmContext struct {
// Embed the default VM context here,
// so that we don't need to reimplement all the methods.
types.DefaultVMContext
}

// NewPluginContext implements types.VMContext.
func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext {
return &helloWorld{}
proxywasm.SetPluginContext(func(contextID uint32) types.PluginContext {
return &helloWorld{}
})
}

// helloWorld implements types.PluginContext.
Expand Down
21 changes: 11 additions & 10 deletions examples/helloworld/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import (
)

func TestHelloWorld_OnTick(t *testing.T) {
vmTest(t, func(t *testing.T, vm types.VMContext) {
opt := proxytest.NewEmulatorOption().WithVMContext(vm)
vmTest(t, func(t *testing.T, pc types.PluginContextFactory) {
opt := proxytest.NewEmulatorOption().WithPluginContext(pc)
host, reset := proxytest.NewHostEmulator(opt)
defer reset()

Expand All @@ -33,8 +33,8 @@ func TestHelloWorld_OnTick(t *testing.T) {
}

func TestHelloWorld_OnPluginStart(t *testing.T) {
vmTest(t, func(t *testing.T, vm types.VMContext) {
opt := proxytest.NewEmulatorOption().WithVMContext(vm)
vmTest(t, func(t *testing.T, pc types.PluginContextFactory) {
opt := proxytest.NewEmulatorOption().WithPluginContext(pc)
host, reset := proxytest.NewHostEmulator(opt)
defer reset()

Expand All @@ -48,14 +48,15 @@ func TestHelloWorld_OnPluginStart(t *testing.T) {
})
}

// vmTest executes f twice, once with a types.VMContext that executes plugin code directly
// in the host, and again by executing the plugin code within the compiled main.wasm binary.
// Execution with main.wasm will be skipped if the file cannot be found.
func vmTest(t *testing.T, f func(*testing.T, types.VMContext)) {
// vmTest executes f twice, once with a types.PluginContextFactory that
// executes plugin code directly in the host, and again by executing the plugin
// code within the compiled main.wasm binary. Execution with main.wasm will be
// skipped if the file cannot be found.
func vmTest(t *testing.T, f func(*testing.T, types.PluginContextFactory)) {
t.Helper()

t.Run("go", func(t *testing.T) {
f(t, &vmContext{})
f(t, func(uint32) types.PluginContext { return &helloWorld{} })
})

t.Run("wasm", func(t *testing.T) {
Expand All @@ -66,6 +67,6 @@ func vmTest(t *testing.T, f func(*testing.T, types.VMContext)) {
v, err := proxytest.NewWasmVMContext(wasm)
require.NoError(t, err)
defer v.Close()
f(t, v)
f(t, v.NewPluginContext)
})
}
27 changes: 3 additions & 24 deletions examples/properties/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,30 +21,9 @@ import (

func main() {}
func init() {
proxywasm.SetVMContext(&vmContext{})
}

// vmContext implements types.VMContext.
type vmContext struct {
// Embed the default VM context here,
// so that we don't need to reimplement all the methods.
types.DefaultVMContext
}

// NewPluginContext implements types.VMContext.
func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext {
return &pluginContext{}
}

type pluginContext struct {
// Embed the default plugin context here,
// so that we don't need to reimplement all the methods.
types.DefaultPluginContext
}

// NewHttpContext implements types.PluginContext.
func (*pluginContext) NewHttpContext(contextID uint32) types.HttpContext {
return &properties{contextID: contextID}
proxywasm.SetHttpContext(func(contextID uint32) types.HttpContext {
return &properties{contextID: contextID}
})
}

// properties implements types.HttpContext.
Expand Down
20 changes: 12 additions & 8 deletions examples/properties/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import (
)

func TestProperties_OnHttpRequestHeaders(t *testing.T) {
vmTest(t, func(t *testing.T, vm types.VMContext) {
opt := proxytest.NewEmulatorOption().WithVMContext(vm)
vmTest(t, func(t *testing.T, h types.HttpContextFactory) {
opt := proxytest.NewEmulatorOption().WithHttpContext(h)
host, reset := proxytest.NewHostEmulator(opt)
defer reset()

Expand Down Expand Up @@ -87,14 +87,17 @@ func TestProperties_OnHttpRequestHeaders(t *testing.T) {
})
}

// vmTest executes f twice, once with a types.VMContext that executes plugin code directly
// in the host, and again by executing the plugin code within the compiled main.wasm binary.
// Execution with main.wasm will be skipped if the file cannot be found.
func vmTest(t *testing.T, f func(*testing.T, types.VMContext)) {
// vmTest executes f twice, once with a types.HttpContextFactory that executes
// plugin code directly in the host, and again by executing the plugin code
// within the compiled main.wasm binary. Execution with main.wasm will be
// skipped if the file cannot be found.
func vmTest(t *testing.T, f func(*testing.T, types.HttpContextFactory)) {
t.Helper()

t.Run("go", func(t *testing.T) {
f(t, &vmContext{})
f(t, func(contextID uint32) types.HttpContext {
return &properties{contextID: contextID}
})
})

t.Run("wasm", func(t *testing.T) {
Expand All @@ -103,8 +106,9 @@ func vmTest(t *testing.T, f func(*testing.T, types.VMContext)) {
t.Skip("wasm not found")
}
v, err := proxytest.NewWasmVMContext(wasm)
p := v.NewPluginContext(1)
require.NoError(t, err)
defer v.Close()
f(t, v)
f(t, p.NewHttpContext)
})
}
54 changes: 51 additions & 3 deletions proxywasm/entrypoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,57 @@ import (
"github.com/proxy-wasm/proxy-wasm-go-sdk/proxywasm/types"
)

// SetVMContext is the entrypoint for setting up the entire Wasm VM.
// Please make sure to call this entrypoint during "main()" function;
// otherwise, the VM fails.
// SetVMContext is one possible entrypoint for setting up the entire Wasm VM.
//
// Subsequent calls to any entrypoint overwrite previous calls to any
// entrypoint. Be sure to call exactly one entrypoint during `init()`,
// otherwise the VM fails.
func SetVMContext(ctx types.VMContext) {
internal.SetVMContext(ctx)
}

// SetPluginContext is one possible entrypoint for setting up the Wasm VM.
//
// Subsequent calls to any entrypoint overwrite previous calls to any
// entrypoint. Be sure to call exactly one entrypoint during `init()`,
// otherwise the VM fails.
//
// Using SetPluginContext instead of SetVmContext is suitable iff the plugin
// does not make use of the VM configuration provided during `VmContext`'s
// `OnVmStart` call (plugin configuration data is still provided during
// `PluginContext`'s `OnPluginStart` call).
func SetPluginContext(newPluginContext func(contextID uint32) types.PluginContext) {
internal.SetPluginContext(newPluginContext)
}

// SetHttpContext is one possible entrypoint for setting up the Wasm VM. It
// allows plugin authors to provide an Http context implementation without
// writing a VmContext or PluginContext.
//
// Subsequent calls to any entrypoint overwrite previous calls to any
// entrypoint. Be sure to call exactly one entrypoint during `init()`,
// otherwise the VM fails.
//
// SetHttpContext is suitable for stateless plugins that share no state between
// HTTP requests, do not process TCP streams, have no expensive shared setup
// requiring execution during `OnPluginStart`, and do not access the plugin
// configuration data.
func SetHttpContext(newHttpContext func(contextID uint32) types.HttpContext) {
internal.SetHttpContext(newHttpContext)
}

// SetTcpContext is one possible entrypoint for setting up the Wasm VM. It
// allows plugin authors to provide a TCP context implementation without
// writing a VmContext or PluginContext.
//
// Subsequent calls to any entrypoint overwrite previous calls to any
// entrypoint. Be sure to call exactly one entrypoint during `init()`,
// otherwise the VM fails.
//
// SetTcpContext is suitable for stateless plugins that share no state between
// TCP streams, do not process HTTP requests, have no expensive shared setup
// requiring execution during `OnPluginStart`, and do not access the plugin
// configuration data.
func SetTcpContext(newTcpContext func(contextID uint32) types.TcpContext) {
internal.SetTcpContext(newTcpContext)
}
72 changes: 72 additions & 0 deletions proxywasm/internal/entrypoints.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package internal

import "github.com/proxy-wasm/proxy-wasm-go-sdk/proxywasm/types"

// elidedVmContext is registered when the plugin author uses a
// SetPluginContext, SetHttpContext, or SetTcpContext entrypoint. It indicates
// the author did not register a VmContext.
//
// elidedVmContext's primary responsibility is calling the author-provided (or
// elided) thunk to create a new PluginContext.
type elidedVmContext struct {
types.DefaultVMContext
newPluginContext types.PluginContextFactory
}

func (ctx *elidedVmContext) NewPluginContext(contextID uint32) types.PluginContext {
return ctx.newPluginContext(contextID)
}

// elidedPluginContext is registered when the plugin author uses the
// SetHttpContext or SetTcpContext entrypoints. It indicates the author did not
// register a VmContext or PluginContext.
//
// elidedVmContext's primary responsibility is calling the author-provided (or
// elided) thunk to create a new HttpContext or TcpContext.
type elidedPluginContext struct {
types.DefaultPluginContext
newHttpContext types.HttpContextFactory
newTcpContext types.TcpContextFactory
}

func (ctx *elidedPluginContext) NewHttpContext(contextID uint32) types.HttpContext {
return ctx.newHttpContext(contextID)
}

func (ctx *elidedPluginContext) NewTcpContext(contextID uint32) types.TcpContext {
return ctx.newTcpContext(contextID)
}

func SetPluginContext(newPluginContext types.PluginContextFactory) {
SetVMContext(&elidedVmContext{newPluginContext: newPluginContext})
}

func SetHttpContext(newHttpContext types.HttpContextFactory) {
SetVMContext(&elidedVmContext{
newPluginContext: func(uint32) types.PluginContext {
return &elidedPluginContext{newHttpContext: newHttpContext}
},
})
}

func SetTcpContext(newTcpContext types.TcpContextFactory) {
SetVMContext(&elidedVmContext{
newPluginContext: func(uint32) types.PluginContext {
return &elidedPluginContext{newTcpContext: newTcpContext}
},
})
}
Loading
Loading