Skip to content

Commit caa0bbc

Browse files
authored
Merge pull request #5 from chirino/main
featL: implement proxy foreign call.
2 parents 3f6adf4 + 98b9db3 commit caa0bbc

File tree

10 files changed

+191
-0
lines changed

10 files changed

+191
-0
lines changed

src/main/java/io/roastedroot/proxywasm/impl/Imports.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -862,4 +862,31 @@ int proxyDispatchHttpCall(
862862
return e.result().getValue();
863863
}
864864
}
865+
866+
@WasmExport
867+
int proxyCallForeignFunction(
868+
int nameDataPtr,
869+
int nameSize,
870+
int argumentDataPtr,
871+
int argumentSize,
872+
int returnResultsPtr,
873+
int returnResultsSizePtr) {
874+
try {
875+
var name = string(readMemory(nameDataPtr, nameSize));
876+
var argument = readMemory(argumentDataPtr, argumentSize);
877+
var result = handler.callForeignFunction(name, argument);
878+
879+
// Allocate memory in the WebAssembly instance
880+
int addr = malloc(result.length);
881+
putMemory(addr, result);
882+
// Write the address to the return pointer
883+
putUint32(returnResultsPtr, addr);
884+
// Write the length to the return size pointer
885+
putUint32(returnResultsSizePtr, result.length);
886+
return WasmResult.OK.getValue();
887+
888+
} catch (WasmException e) {
889+
return e.result().getValue();
890+
}
891+
}
865892
}

src/main/java/io/roastedroot/proxywasm/v1/ChainedHandler.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,4 +265,20 @@ public int httpCall(
265265
throws WasmException {
266266
return next().httpCall(uri, headers, body, trailers, timeout);
267267
}
268+
269+
@Override
270+
public int dispatchHttpCall(
271+
String upstreamName,
272+
HashMap<String, String> headers,
273+
byte[] body,
274+
HashMap<String, String> trailers,
275+
int timeoutMilliseconds)
276+
throws WasmException {
277+
return next().dispatchHttpCall(upstreamName, headers, body, trailers, timeoutMilliseconds);
278+
}
279+
280+
@Override
281+
public byte[] callForeignFunction(String name, byte[] bytes) throws WasmException {
282+
return next().callForeignFunction(name, bytes);
283+
}
268284
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package io.roastedroot.proxywasm.v1;
2+
3+
public interface ForeignFunction {
4+
byte[] apply(byte[] data);
5+
}

src/main/java/io/roastedroot/proxywasm/v1/Handler.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,4 +406,8 @@ default int dispatchHttpCall(
406406
throws WasmException {
407407
throw new WasmException(WasmResult.UNIMPLEMENTED);
408408
}
409+
410+
default byte[] callForeignFunction(String name, byte[] bytes) throws WasmException {
411+
throw new WasmException(WasmResult.NOT_FOUND);
412+
}
409413
}

src/main/java/io/roastedroot/proxywasm/v1/ProxyWasm.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public final class ProxyWasm implements Closeable {
4343
private Map<String, String> httpCallResponseHeaders;
4444
private Map<String, String> httpCallResponseTrailers;
4545
private byte[] httpCallResponseBody;
46+
private HashMap<String, ForeignFunction> foreignFunctions = new HashMap<>();
4647

4748
private ProxyWasm(Builder other) throws StartException {
4849
this.vmConfig = other.vmConfig;
@@ -169,6 +170,15 @@ public Map<String, String> getHttpCallResponseTrailers() {
169170
public byte[] getHttpCallResponseBody() {
170171
return httpCallResponseBody;
171172
}
173+
174+
@Override
175+
public byte[] callForeignFunction(String name, byte[] bytes) throws WasmException {
176+
ForeignFunction func = foreignFunctions.get(name);
177+
if (func == null) {
178+
throw new WasmException(WasmResult.NOT_FOUND);
179+
}
180+
return func.apply(bytes);
181+
}
172182
};
173183
}
174184

@@ -266,6 +276,10 @@ public int contextId() {
266276
return pluginContext.id();
267277
}
268278

279+
public void registerForeignFunction(String name, ForeignFunction func) {
280+
foreignFunctions.put(name, func);
281+
}
282+
269283
public static class Builder implements Cloneable {
270284

271285
private Exports exports;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
## Attribution
2+
3+
This example originally came from:
4+
https://github.com/proxy-wasm/proxy-wasm-go-sdk/tree/main/examples/foreign_call_on_tick
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module github.com/proxy-wasm/proxy-wasm-go-sdk/examples/foreign_call_on_tick
2+
3+
go 1.24
4+
5+
require github.com/proxy-wasm/proxy-wasm-go-sdk v0.0.0-20250212164326-ab4161dcf924
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
4+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
5+
github.com/proxy-wasm/proxy-wasm-go-sdk v0.0.0-20250212164326-ab4161dcf924 h1:wTcK6gcyTKJMeDka69AMjZYvisdI8CBXzTEfZ+2pOxI=
6+
github.com/proxy-wasm/proxy-wasm-go-sdk v0.0.0-20250212164326-ab4161dcf924/go.mod h1:9mBRvh8I6Td6sg3CwEY+zGFE4DKaIoieCaca1kQnDBE=
7+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
8+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
9+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
10+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright 2020-2024 Tetrate
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 main
16+
17+
import (
18+
"encoding/hex"
19+
20+
"github.com/proxy-wasm/proxy-wasm-go-sdk/proxywasm"
21+
"github.com/proxy-wasm/proxy-wasm-go-sdk/proxywasm/types"
22+
)
23+
24+
const tickMilliseconds uint32 = 1
25+
26+
func main() {}
27+
func init() {
28+
proxywasm.SetVMContext(&vmContext{})
29+
}
30+
31+
// vmContext implements types.VMContext.
32+
type vmContext struct {
33+
// Embed the default VM context here,
34+
// so that we don't need to reimplement all the methods.
35+
types.DefaultVMContext
36+
}
37+
38+
// NewPluginContext implements types.VMContext.
39+
func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext {
40+
return &pluginContext{}
41+
}
42+
43+
// pluginContext implements types.PluginContext.
44+
type pluginContext struct {
45+
// Embed the default plugin context here,
46+
// so that we don't need to reimplement all the methods.
47+
types.DefaultPluginContext
48+
callNum uint32
49+
}
50+
51+
// OnPluginStart implements types.PluginContext.
52+
func (ctx *pluginContext) OnPluginStart(pluginConfigurationSize int) types.OnPluginStartStatus {
53+
if err := proxywasm.SetTickPeriodMilliSeconds(tickMilliseconds); err != nil {
54+
proxywasm.LogCriticalf("failed to set tick period: %v", err)
55+
return types.OnPluginStartStatusFailed
56+
}
57+
proxywasm.LogInfof("set tick period milliseconds: %d", tickMilliseconds)
58+
return types.OnPluginStartStatusOK
59+
}
60+
61+
// OnTick implements types.PluginContext.
62+
func (ctx *pluginContext) OnTick() {
63+
ctx.callNum++
64+
ret, err := proxywasm.CallForeignFunction("compress", []byte("hello world!"))
65+
if err != nil {
66+
proxywasm.LogCriticalf("foreign function (compress) failed: %v", err)
67+
}
68+
proxywasm.LogInfof("foreign function (compress) called: %d, result: %s", ctx.callNum, hex.EncodeToString(ret))
69+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package io.roastedroot.proxywasm;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import com.dylibso.chicory.wasm.Parser;
6+
import io.roastedroot.proxywasm.v1.ProxyWasm;
7+
import io.roastedroot.proxywasm.v1.StartException;
8+
import java.nio.file.Path;
9+
import org.junit.jupiter.api.Test;
10+
11+
/**
12+
*/
13+
public class ForeignCallOnTickTest {
14+
15+
static int tickMilliseconds = 1;
16+
17+
@Test
18+
public void testOnTick() throws StartException {
19+
20+
var handler = new MockHandler();
21+
var module = Parser.parse(Path.of("./src/test/go-examples/foreign_call_on_tick/main.wasm"));
22+
ProxyWasm.Builder builder = ProxyWasm.builder().withPluginHandler(handler);
23+
try (var host = builder.build(module)) {
24+
assertEquals(tickMilliseconds, handler.getTickPeriodMilliseconds());
25+
26+
host.registerForeignFunction("compress", data -> data);
27+
28+
for (int i = 1; i <= 10; i++) {
29+
host.tick(); // call OnTick
30+
handler.assertLogsContain(
31+
String.format(
32+
"foreign function (compress) called: %d, result: %s",
33+
i, "68656c6c6f20776f726c6421"));
34+
}
35+
}
36+
}
37+
}

0 commit comments

Comments
 (0)