Skip to content

Commit b8720b4

Browse files
authored
Add entrypoints for Plugin, Http, Tcp contexts (#6)
* Add entrypoints for Plugin, Http, Tcp contexts Fixes #5 Signed-off-by: Matt Leon <[email protected]> * Address PR comments Signed-off-by: Matt Leon <[email protected]> --------- Signed-off-by: Matt Leon <[email protected]>
1 parent 23e5db5 commit b8720b4

File tree

11 files changed

+298
-73
lines changed

11 files changed

+298
-73
lines changed

README.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,20 +59,20 @@ import (
5959

6060
func main() {}
6161
func init() {
62-
proxywasm.SetVMContext(&vmContext{})
62+
// Plugin authors can use any one of four entrypoints, such as
63+
// `proxywasm.SetVMContext`, `proxywasm.SetPluginContext`, or
64+
// `proxywasm.SetTcpContext`.
65+
proxywasm.SetHttpContext(func(contextID uint32) types.HttpContext {
66+
return &httpContext{}
67+
})
6368
}
64-
65-
type vmContext struct {
66-
types.DefaultVMContext
67-
}
68-
func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext {
69-
return &pluginContext{}
69+
type httpContext struct {
70+
types.DefaultHttpContext
7071
}
71-
72-
type pluginContext struct {
73-
types.DefaultPluginContext
72+
func (*httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
73+
proxywasm.LogInfo("Hello, world!")
74+
return types.ActionContinue
7475
}
75-
// pluginContext should implement OnPluginStart, NewHttpContext, NewTcpContext, etc
7676
```
7777

7878
Compile the plugin as follows:

examples/helloworld/main.go

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,9 @@ const tickMilliseconds uint32 = 1000
2626

2727
func main() {}
2828
func init() {
29-
proxywasm.SetVMContext(&vmContext{})
30-
}
31-
32-
// vmContext implements types.VMContext.
33-
type vmContext struct {
34-
// Embed the default VM context here,
35-
// so that we don't need to reimplement all the methods.
36-
types.DefaultVMContext
37-
}
38-
39-
// NewPluginContext implements types.VMContext.
40-
func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext {
41-
return &helloWorld{}
29+
proxywasm.SetPluginContext(func(contextID uint32) types.PluginContext {
30+
return &helloWorld{}
31+
})
4232
}
4333

4434
// helloWorld implements types.PluginContext.

examples/helloworld/main_test.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import (
1414
)
1515

1616
func TestHelloWorld_OnTick(t *testing.T) {
17-
vmTest(t, func(t *testing.T, vm types.VMContext) {
18-
opt := proxytest.NewEmulatorOption().WithVMContext(vm)
17+
vmTest(t, func(t *testing.T, pc types.PluginContextFactory) {
18+
opt := proxytest.NewEmulatorOption().WithPluginContext(pc)
1919
host, reset := proxytest.NewHostEmulator(opt)
2020
defer reset()
2121

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

3535
func TestHelloWorld_OnPluginStart(t *testing.T) {
36-
vmTest(t, func(t *testing.T, vm types.VMContext) {
37-
opt := proxytest.NewEmulatorOption().WithVMContext(vm)
36+
vmTest(t, func(t *testing.T, pc types.PluginContextFactory) {
37+
opt := proxytest.NewEmulatorOption().WithPluginContext(pc)
3838
host, reset := proxytest.NewHostEmulator(opt)
3939
defer reset()
4040

@@ -48,14 +48,15 @@ func TestHelloWorld_OnPluginStart(t *testing.T) {
4848
})
4949
}
5050

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

5758
t.Run("go", func(t *testing.T) {
58-
f(t, &vmContext{})
59+
f(t, func(uint32) types.PluginContext { return &helloWorld{} })
5960
})
6061

6162
t.Run("wasm", func(t *testing.T) {
@@ -66,6 +67,6 @@ func vmTest(t *testing.T, f func(*testing.T, types.VMContext)) {
6667
v, err := proxytest.NewWasmVMContext(wasm)
6768
require.NoError(t, err)
6869
defer v.Close()
69-
f(t, v)
70+
f(t, v.NewPluginContext)
7071
})
7172
}

examples/properties/main.go

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,30 +21,9 @@ import (
2121

2222
func main() {}
2323
func init() {
24-
proxywasm.SetVMContext(&vmContext{})
25-
}
26-
27-
// vmContext implements types.VMContext.
28-
type vmContext struct {
29-
// Embed the default VM context here,
30-
// so that we don't need to reimplement all the methods.
31-
types.DefaultVMContext
32-
}
33-
34-
// NewPluginContext implements types.VMContext.
35-
func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext {
36-
return &pluginContext{}
37-
}
38-
39-
type pluginContext struct {
40-
// Embed the default plugin context here,
41-
// so that we don't need to reimplement all the methods.
42-
types.DefaultPluginContext
43-
}
44-
45-
// NewHttpContext implements types.PluginContext.
46-
func (*pluginContext) NewHttpContext(contextID uint32) types.HttpContext {
47-
return &properties{contextID: contextID}
24+
proxywasm.SetHttpContext(func(contextID uint32) types.HttpContext {
25+
return &properties{contextID: contextID}
26+
})
4827
}
4928

5029
// properties implements types.HttpContext.

examples/properties/main_test.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import (
1515
)
1616

1717
func TestProperties_OnHttpRequestHeaders(t *testing.T) {
18-
vmTest(t, func(t *testing.T, vm types.VMContext) {
19-
opt := proxytest.NewEmulatorOption().WithVMContext(vm)
18+
vmTest(t, func(t *testing.T, h types.HttpContextFactory) {
19+
opt := proxytest.NewEmulatorOption().WithHttpContext(h)
2020
host, reset := proxytest.NewHostEmulator(opt)
2121
defer reset()
2222

@@ -87,14 +87,17 @@ func TestProperties_OnHttpRequestHeaders(t *testing.T) {
8787
})
8888
}
8989

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

9697
t.Run("go", func(t *testing.T) {
97-
f(t, &vmContext{})
98+
f(t, func(contextID uint32) types.HttpContext {
99+
return &properties{contextID: contextID}
100+
})
98101
})
99102

100103
t.Run("wasm", func(t *testing.T) {
@@ -103,8 +106,9 @@ func vmTest(t *testing.T, f func(*testing.T, types.VMContext)) {
103106
t.Skip("wasm not found")
104107
}
105108
v, err := proxytest.NewWasmVMContext(wasm)
109+
p := v.NewPluginContext(1)
106110
require.NoError(t, err)
107111
defer v.Close()
108-
f(t, v)
112+
f(t, p.NewHttpContext)
109113
})
110114
}

proxywasm/entrypoint.go

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,57 @@ import (
1919
"github.com/proxy-wasm/proxy-wasm-go-sdk/proxywasm/types"
2020
)
2121

22-
// SetVMContext is the entrypoint for setting up the entire Wasm VM.
23-
// Please make sure to call this entrypoint during "main()" function;
24-
// otherwise, the VM fails.
22+
// SetVMContext is one possible entrypoint for setting up the entire Wasm VM.
23+
//
24+
// Subsequent calls to any entrypoint overwrite previous calls to any
25+
// entrypoint. Be sure to call exactly one entrypoint during `init()`,
26+
// otherwise the VM fails.
2527
func SetVMContext(ctx types.VMContext) {
2628
internal.SetVMContext(ctx)
2729
}
30+
31+
// SetPluginContext is one possible entrypoint for setting up the Wasm VM.
32+
//
33+
// Subsequent calls to any entrypoint overwrite previous calls to any
34+
// entrypoint. Be sure to call exactly one entrypoint during `init()`,
35+
// otherwise the VM fails.
36+
//
37+
// Using SetPluginContext instead of SetVmContext is suitable iff the plugin
38+
// does not make use of the VM configuration provided during `VmContext`'s
39+
// `OnVmStart` call (plugin configuration data is still provided during
40+
// `PluginContext`'s `OnPluginStart` call).
41+
func SetPluginContext(newPluginContext func(contextID uint32) types.PluginContext) {
42+
internal.SetPluginContext(newPluginContext)
43+
}
44+
45+
// SetHttpContext is one possible entrypoint for setting up the Wasm VM. It
46+
// allows plugin authors to provide an Http context implementation without
47+
// writing a VmContext or PluginContext.
48+
//
49+
// Subsequent calls to any entrypoint overwrite previous calls to any
50+
// entrypoint. Be sure to call exactly one entrypoint during `init()`,
51+
// otherwise the VM fails.
52+
//
53+
// SetHttpContext is suitable for stateless plugins that share no state between
54+
// HTTP requests, do not process TCP streams, have no expensive shared setup
55+
// requiring execution during `OnPluginStart`, and do not access the plugin
56+
// configuration data.
57+
func SetHttpContext(newHttpContext func(contextID uint32) types.HttpContext) {
58+
internal.SetHttpContext(newHttpContext)
59+
}
60+
61+
// SetTcpContext is one possible entrypoint for setting up the Wasm VM. It
62+
// allows plugin authors to provide a TCP context implementation without
63+
// writing a VmContext or PluginContext.
64+
//
65+
// Subsequent calls to any entrypoint overwrite previous calls to any
66+
// entrypoint. Be sure to call exactly one entrypoint during `init()`,
67+
// otherwise the VM fails.
68+
//
69+
// SetTcpContext is suitable for stateless plugins that share no state between
70+
// TCP streams, do not process HTTP requests, have no expensive shared setup
71+
// requiring execution during `OnPluginStart`, and do not access the plugin
72+
// configuration data.
73+
func SetTcpContext(newTcpContext func(contextID uint32) types.TcpContext) {
74+
internal.SetTcpContext(newTcpContext)
75+
}

proxywasm/internal/entrypoints.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package internal
16+
17+
import "github.com/proxy-wasm/proxy-wasm-go-sdk/proxywasm/types"
18+
19+
// elidedVmContext is registered when the plugin author uses a
20+
// SetPluginContext, SetHttpContext, or SetTcpContext entrypoint. It indicates
21+
// the author did not register a VmContext.
22+
//
23+
// elidedVmContext's primary responsibility is calling the author-provided (or
24+
// elided) thunk to create a new PluginContext.
25+
type elidedVmContext struct {
26+
types.DefaultVMContext
27+
newPluginContext types.PluginContextFactory
28+
}
29+
30+
func (ctx *elidedVmContext) NewPluginContext(contextID uint32) types.PluginContext {
31+
return ctx.newPluginContext(contextID)
32+
}
33+
34+
// elidedPluginContext is registered when the plugin author uses the
35+
// SetHttpContext or SetTcpContext entrypoints. It indicates the author did not
36+
// register a VmContext or PluginContext.
37+
//
38+
// elidedVmContext's primary responsibility is calling the author-provided (or
39+
// elided) thunk to create a new HttpContext or TcpContext.
40+
type elidedPluginContext struct {
41+
types.DefaultPluginContext
42+
newHttpContext types.HttpContextFactory
43+
newTcpContext types.TcpContextFactory
44+
}
45+
46+
func (ctx *elidedPluginContext) NewHttpContext(contextID uint32) types.HttpContext {
47+
return ctx.newHttpContext(contextID)
48+
}
49+
50+
func (ctx *elidedPluginContext) NewTcpContext(contextID uint32) types.TcpContext {
51+
return ctx.newTcpContext(contextID)
52+
}
53+
54+
func SetPluginContext(newPluginContext types.PluginContextFactory) {
55+
SetVMContext(&elidedVmContext{newPluginContext: newPluginContext})
56+
}
57+
58+
func SetHttpContext(newHttpContext types.HttpContextFactory) {
59+
SetVMContext(&elidedVmContext{
60+
newPluginContext: func(uint32) types.PluginContext {
61+
return &elidedPluginContext{newHttpContext: newHttpContext}
62+
},
63+
})
64+
}
65+
66+
func SetTcpContext(newTcpContext types.TcpContextFactory) {
67+
SetVMContext(&elidedVmContext{
68+
newPluginContext: func(uint32) types.PluginContext {
69+
return &elidedPluginContext{newTcpContext: newTcpContext}
70+
},
71+
})
72+
}

0 commit comments

Comments
 (0)