Skip to content

Commit fa3d459

Browse files
authored
Merge pull request #4 from chirino/main
Add dispatch_call_on_tick example and associated tests.
2 parents 302c54f + 2b3511d commit fa3d459

File tree

18 files changed

+386
-52
lines changed

18 files changed

+386
-52
lines changed

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

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.roastedroot.proxywasm.impl;
22

3+
import static io.roastedroot.proxywasm.v1.Helpers.string;
4+
35
import com.dylibso.chicory.experimental.hostmodule.annotations.HostModule;
46
import com.dylibso.chicory.experimental.hostmodule.annotations.WasmExport;
57
import com.dylibso.chicory.runtime.Instance;
@@ -800,4 +802,64 @@ int proxyContinueStream(int arg) {
800802
// should never reach here
801803
return WasmResult.INTERNAL_FAILURE.getValue();
802804
}
805+
806+
@WasmExport
807+
int proxyHttpCall(
808+
int uriData,
809+
int uriSize,
810+
int headersData,
811+
int headersSize,
812+
int bodyData,
813+
int bodySize,
814+
int trailersData,
815+
int trailersSize,
816+
int timeout,
817+
int returnCalloutID) {
818+
819+
try {
820+
var uri = string(readMemory(uriData, uriSize));
821+
var headers = decodeMap(headersData, headersSize);
822+
var body = readMemory(bodyData, bodySize);
823+
var trailers = decodeMap(trailersData, trailersSize);
824+
825+
int calloutId = handler.httpCall(uri, headers, body, trailers, timeout);
826+
827+
putUint32(returnCalloutID, calloutId);
828+
return WasmResult.OK.getValue();
829+
830+
} catch (WasmException e) {
831+
return e.result().getValue();
832+
}
833+
}
834+
835+
@WasmExport
836+
int proxyDispatchHttpCall(
837+
int upstreamNameData,
838+
int upstreamNameSize,
839+
int headersData,
840+
int headersSize,
841+
int bodyData,
842+
int bodySize,
843+
int trailersData,
844+
int trailersSize,
845+
int timeoutMilliseconds,
846+
int returnCalloutID) {
847+
848+
try {
849+
var upstreamName = string(readMemory(upstreamNameData, upstreamNameSize));
850+
var headers = decodeMap(headersData, headersSize);
851+
var body = readMemory(bodyData, bodySize);
852+
var trailers = decodeMap(trailersData, trailersSize);
853+
854+
int calloutId =
855+
handler.dispatchHttpCall(
856+
upstreamName, headers, body, trailers, timeoutMilliseconds);
857+
858+
putUint32(returnCalloutID, calloutId);
859+
return WasmResult.OK.getValue();
860+
861+
} catch (WasmException e) {
862+
return e.result().getValue();
863+
}
864+
}
803865
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.roastedroot.proxywasm.v1;
22

3+
import java.util.HashMap;
34
import java.util.Map;
45

56
/**
@@ -253,4 +254,15 @@ public WasmResult continueDownstream() {
253254
public WasmResult continueUpstream() {
254255
return next().continueUpstream();
255256
}
257+
258+
@Override
259+
public int httpCall(
260+
String uri,
261+
HashMap<String, String> headers,
262+
byte[] body,
263+
HashMap<String, String> trailers,
264+
int timeout)
265+
throws WasmException {
266+
return next().httpCall(uri, headers, body, trailers, timeout);
267+
}
256268
}

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ public void close() {
4242
}
4343

4444
// plugin is indicating it wants to finish closing
45-
// TODO: need a unit test for this. We likely can't implement the test until we provide tick
46-
// callbacks to the plugin.
4745
WasmResult done() {
4846
if (!closeStarted) {
4947
// spec says: return NOT_FOUND when active context was not pending finalization.

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

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.roastedroot.proxywasm.v1;
22

3+
import java.util.HashMap;
34
import java.util.Map;
45

56
public interface Handler {
@@ -160,11 +161,11 @@ default WasmResult done() {
160161
/**
161162
* Sets a low-resolution timer period (tick_period).
162163
*
163-
* When set, the host will call proxy_on_tick every tick_period milliseconds. Setting tick_period to 0 disables the timer.
164+
* When set, the host will call proxy_on_tick every tickPeriodMilliseconds milliseconds. Setting tickPeriodMilliseconds to 0 disables the timer.
164165
*
165166
* @return The current time in nanoseconds
166167
*/
167-
default WasmResult setTickPeriodMilliseconds(int tick_period) {
168+
default WasmResult setTickPeriodMilliseconds(int tickPeriodMilliseconds) {
168169
return WasmResult.UNIMPLEMENTED;
169170
}
170171

@@ -385,4 +386,24 @@ default WasmResult continueDownstream() {
385386
default WasmResult continueUpstream() {
386387
return WasmResult.UNIMPLEMENTED;
387388
}
389+
390+
default int httpCall(
391+
String uri,
392+
HashMap<String, String> headers,
393+
byte[] body,
394+
HashMap<String, String> trailers,
395+
int timeoutMilliseconds)
396+
throws WasmException {
397+
throw new WasmException(WasmResult.UNIMPLEMENTED);
398+
}
399+
400+
default int dispatchHttpCall(
401+
String upstreamName,
402+
HashMap<String, String> headers,
403+
byte[] body,
404+
HashMap<String, String> trailers,
405+
int timeoutMilliseconds)
406+
throws WasmException {
407+
throw new WasmException(WasmResult.UNIMPLEMENTED);
408+
}
388409
}

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ public final class ProxyWasm implements Closeable {
4040
private Context activeContext;
4141

4242
private HashMap<Integer, Context> contexts = new HashMap<>();
43+
private Map<String, String> httpCallResponseHeaders;
44+
private Map<String, String> httpCallResponseTrailers;
45+
private byte[] httpCallResponseBody;
4346

4447
private ProxyWasm(Builder other) throws StartException {
4548
this.vmConfig = other.vmConfig;
@@ -151,6 +154,21 @@ public WasmResult setEffectiveContextID(int contextID) {
151154
public WasmResult done() {
152155
return activeContext.done();
153156
}
157+
158+
@Override
159+
public Map<String, String> getHttpCallResponseHeaders() {
160+
return httpCallResponseHeaders;
161+
}
162+
163+
@Override
164+
public Map<String, String> getHttpCallResponseTrailers() {
165+
return httpCallResponseTrailers;
166+
}
167+
168+
@Override
169+
public byte[] getHttpCallResponseBody() {
170+
return httpCallResponseBody;
171+
}
154172
};
155173
}
156174

@@ -229,6 +247,25 @@ public static ProxyWasm.Builder builder() {
229247
return new ProxyWasm.Builder();
230248
}
231249

250+
public void callOnHttpCallResponse(
251+
int calloutID, Map<String, String> headers, Map<String, String> trailers, byte[] body) {
252+
253+
this.httpCallResponseHeaders = headers;
254+
this.httpCallResponseTrailers = trailers;
255+
this.httpCallResponseBody = body;
256+
257+
this.exports.proxyOnHttpCallResponse(
258+
pluginContext.id(), calloutID, len(headers), len(body), len(trailers));
259+
260+
this.httpCallResponseHeaders = null;
261+
this.httpCallResponseTrailers = null;
262+
this.httpCallResponseBody = null;
263+
}
264+
265+
public int contextId() {
266+
return pluginContext.id();
267+
}
268+
232269
public static class Builder implements Cloneable {
233270

234271
private Exports exports;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
## Attribution
2+
3+
This example originally came from:
4+
https://github.com/proxy-wasm/proxy-wasm-go-sdk/tree/main/examples/dispatch_call_on_tick
5+
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/dispatch_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: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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+
"crypto/rand"
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 = 100
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{contextID: contextID}
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+
contextID uint32
49+
callBack func(numHeaders, bodySize, numTrailers int)
50+
cnt int
51+
}
52+
53+
// OnPluginStart implements types.PluginContext.
54+
func (ctx *pluginContext) OnPluginStart(pluginConfigurationSize int) types.OnPluginStartStatus {
55+
if err := proxywasm.SetTickPeriodMilliSeconds(tickMilliseconds); err != nil {
56+
proxywasm.LogCriticalf("failed to set tick period: %v", err)
57+
return types.OnPluginStartStatusFailed
58+
}
59+
proxywasm.LogInfof("set tick period milliseconds: %d", tickMilliseconds)
60+
ctx.callBack = func(numHeaders, bodySize, numTrailers int) {
61+
ctx.cnt++
62+
proxywasm.LogInfof("called %d for contextID=%d", ctx.cnt, ctx.contextID)
63+
headers, err := proxywasm.GetHttpCallResponseHeaders()
64+
if err != nil && err != types.ErrorStatusNotFound {
65+
panic(err)
66+
}
67+
for _, h := range headers {
68+
proxywasm.LogInfof("response header for the dispatched call: %s: %s", h[0], h[1])
69+
}
70+
headers, err = proxywasm.GetHttpCallResponseTrailers()
71+
if err != nil && err != types.ErrorStatusNotFound {
72+
panic(err)
73+
}
74+
for _, h := range headers {
75+
proxywasm.LogInfof("response trailer for the dispatched call: %s: %s", h[0], h[1])
76+
}
77+
}
78+
return types.OnPluginStartStatusOK
79+
}
80+
81+
// OnTick implements types.PluginContext.
82+
func (ctx *pluginContext) OnTick() {
83+
headers := [][2]string{
84+
{":method", "GET"}, {":authority", "some_authority"}, {"accept", "*/*"},
85+
}
86+
// Pick random value to select the request path.
87+
buf := make([]byte, 1)
88+
_, _ = rand.Read(buf)
89+
if buf[0]%2 == 0 {
90+
headers = append(headers, [2]string{":path", "/ok"})
91+
} else {
92+
headers = append(headers, [2]string{":path", "/fail"})
93+
}
94+
if _, err := proxywasm.DispatchHttpCall("web_service", headers, nil, nil, 5000, ctx.callBack); err != nil {
95+
proxywasm.LogCriticalf("dispatch httpcall failed: %v", err)
96+
}
97+
}
2.57 MB
Binary file not shown.

0 commit comments

Comments
 (0)