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
27 changes: 27 additions & 0 deletions src/main/java/io/roastedroot/proxywasm/impl/Imports.java
Original file line number Diff line number Diff line change
Expand Up @@ -862,4 +862,31 @@ int proxyDispatchHttpCall(
return e.result().getValue();
}
}

@WasmExport
int proxyCallForeignFunction(
int nameDataPtr,
int nameSize,
int argumentDataPtr,
int argumentSize,
int returnResultsPtr,
int returnResultsSizePtr) {
try {
var name = string(readMemory(nameDataPtr, nameSize));
var argument = readMemory(argumentDataPtr, argumentSize);
var result = handler.callForeignFunction(name, argument);

// Allocate memory in the WebAssembly instance
int addr = malloc(result.length);
putMemory(addr, result);
// Write the address to the return pointer
putUint32(returnResultsPtr, addr);
// Write the length to the return size pointer
putUint32(returnResultsSizePtr, result.length);
return WasmResult.OK.getValue();

} catch (WasmException e) {
return e.result().getValue();
}
}
}
16 changes: 16 additions & 0 deletions src/main/java/io/roastedroot/proxywasm/v1/ChainedHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -265,4 +265,20 @@ public int httpCall(
throws WasmException {
return next().httpCall(uri, headers, body, trailers, timeout);
}

@Override
public int dispatchHttpCall(
String upstreamName,
HashMap<String, String> headers,
byte[] body,
HashMap<String, String> trailers,
int timeoutMilliseconds)
throws WasmException {
return next().dispatchHttpCall(upstreamName, headers, body, trailers, timeoutMilliseconds);
}

@Override
public byte[] callForeignFunction(String name, byte[] bytes) throws WasmException {
return next().callForeignFunction(name, bytes);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.roastedroot.proxywasm.v1;

public interface ForeignFunction {
byte[] apply(byte[] data);
}
4 changes: 4 additions & 0 deletions src/main/java/io/roastedroot/proxywasm/v1/Handler.java
Original file line number Diff line number Diff line change
Expand Up @@ -406,4 +406,8 @@ default int dispatchHttpCall(
throws WasmException {
throw new WasmException(WasmResult.UNIMPLEMENTED);
}

default byte[] callForeignFunction(String name, byte[] bytes) throws WasmException {
throw new WasmException(WasmResult.NOT_FOUND);
}
}
14 changes: 14 additions & 0 deletions src/main/java/io/roastedroot/proxywasm/v1/ProxyWasm.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public final class ProxyWasm implements Closeable {
private Map<String, String> httpCallResponseHeaders;
private Map<String, String> httpCallResponseTrailers;
private byte[] httpCallResponseBody;
private HashMap<String, ForeignFunction> foreignFunctions = new HashMap<>();

private ProxyWasm(Builder other) throws StartException {
this.vmConfig = other.vmConfig;
Expand Down Expand Up @@ -169,6 +170,15 @@ public Map<String, String> getHttpCallResponseTrailers() {
public byte[] getHttpCallResponseBody() {
return httpCallResponseBody;
}

@Override
public byte[] callForeignFunction(String name, byte[] bytes) throws WasmException {
ForeignFunction func = foreignFunctions.get(name);
if (func == null) {
throw new WasmException(WasmResult.NOT_FOUND);
}
return func.apply(bytes);
}
};
}

Expand Down Expand Up @@ -266,6 +276,10 @@ public int contextId() {
return pluginContext.id();
}

public void registerForeignFunction(String name, ForeignFunction func) {
foreignFunctions.put(name, func);
}

public static class Builder implements Cloneable {

private Exports exports;
Expand Down
4 changes: 4 additions & 0 deletions src/test/go-examples/foreign_call_on_tick/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## Attribution

This example originally came from:
https://github.com/proxy-wasm/proxy-wasm-go-sdk/tree/main/examples/foreign_call_on_tick
5 changes: 5 additions & 0 deletions src/test/go-examples/foreign_call_on_tick/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/proxy-wasm/proxy-wasm-go-sdk/examples/foreign_call_on_tick

go 1.24

require github.com/proxy-wasm/proxy-wasm-go-sdk v0.0.0-20250212164326-ab4161dcf924
10 changes: 10 additions & 0 deletions src/test/go-examples/foreign_call_on_tick/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/proxy-wasm/proxy-wasm-go-sdk v0.0.0-20250212164326-ab4161dcf924 h1:wTcK6gcyTKJMeDka69AMjZYvisdI8CBXzTEfZ+2pOxI=
github.com/proxy-wasm/proxy-wasm-go-sdk v0.0.0-20250212164326-ab4161dcf924/go.mod h1:9mBRvh8I6Td6sg3CwEY+zGFE4DKaIoieCaca1kQnDBE=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
69 changes: 69 additions & 0 deletions src/test/go-examples/foreign_call_on_tick/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2020-2024 Tetrate
//
// 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 main

import (
"encoding/hex"

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

const tickMilliseconds uint32 = 1

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{}
}

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

// OnPluginStart implements types.PluginContext.
func (ctx *pluginContext) OnPluginStart(pluginConfigurationSize int) types.OnPluginStartStatus {
if err := proxywasm.SetTickPeriodMilliSeconds(tickMilliseconds); err != nil {
proxywasm.LogCriticalf("failed to set tick period: %v", err)
return types.OnPluginStartStatusFailed
}
proxywasm.LogInfof("set tick period milliseconds: %d", tickMilliseconds)
return types.OnPluginStartStatusOK
}

// OnTick implements types.PluginContext.
func (ctx *pluginContext) OnTick() {
ctx.callNum++
ret, err := proxywasm.CallForeignFunction("compress", []byte("hello world!"))
if err != nil {
proxywasm.LogCriticalf("foreign function (compress) failed: %v", err)
}
proxywasm.LogInfof("foreign function (compress) called: %d, result: %s", ctx.callNum, hex.EncodeToString(ret))
}
Binary file not shown.
37 changes: 37 additions & 0 deletions src/test/java/io/roastedroot/proxywasm/ForeignCallOnTickTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.roastedroot.proxywasm;

import static org.junit.jupiter.api.Assertions.assertEquals;

import com.dylibso.chicory.wasm.Parser;
import io.roastedroot.proxywasm.v1.ProxyWasm;
import io.roastedroot.proxywasm.v1.StartException;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;

/**
*/
public class ForeignCallOnTickTest {

static int tickMilliseconds = 1;

@Test
public void testOnTick() throws StartException {

var handler = new MockHandler();
var module = Parser.parse(Path.of("./src/test/go-examples/foreign_call_on_tick/main.wasm"));
ProxyWasm.Builder builder = ProxyWasm.builder().withPluginHandler(handler);
try (var host = builder.build(module)) {
assertEquals(tickMilliseconds, handler.getTickPeriodMilliseconds());

host.registerForeignFunction("compress", data -> data);

for (int i = 1; i <= 10; i++) {
host.tick(); // call OnTick
handler.assertLogsContain(
String.format(
"foreign function (compress) called: %d, result: %s",
i, "68656c6c6f20776f726c6421"));
}
}
}
}