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
50 changes: 38 additions & 12 deletions src/main/java/io/roastedroot/proxywasm/impl/Imports.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.dylibso.chicory.experimental.hostmodule.annotations.WasmExport;
import com.dylibso.chicory.runtime.Instance;
import com.dylibso.chicory.runtime.WasmRuntimeException;
import io.roastedroot.proxywasm.v1.Action;
import io.roastedroot.proxywasm.v1.BufferType;
import io.roastedroot.proxywasm.v1.Handler;
import io.roastedroot.proxywasm.v1.LogLevel;
Expand Down Expand Up @@ -784,24 +785,49 @@ int proxyGetCurrentTimeNanoseconds(int returnTime) {
}
}

/**
* Resumes processing of paused stream_type.
*
* see: https://github.com/proxy-wasm/spec/tree/main/abi-versions/v0.2.1#proxy_continue_stream
*/
@WasmExport
int proxyContinueStream(int arg) {
var streamType = StreamType.fromInt(arg);
if (streamType == null) {
return WasmResult.BAD_ARGUMENT.getValue();
}
switch (streamType) {
case REQUEST:
return handler.continueRequest().getValue();
case RESPONSE:
return handler.continueResponse().getValue();
case DOWNSTREAM:
return handler.continueDownstream().getValue();
case UPSTREAM:
return handler.continueUpstream().getValue();
}
// should never reach here
return WasmResult.INTERNAL_FAILURE.getValue();
WasmResult result = handler.setAction(streamType, Action.CONTINUE);
return result.getValue();
}

/**
* Resumes processing of paused HTTP request.
*
* see: https://github.com/proxy-wasm/spec/blob/main/abi-versions/v0.1.0/README.md#proxy_continue_request
*/
@WasmExport
void proxyContinueRequest() {
handler.setAction(StreamType.REQUEST, Action.CONTINUE);
}

/**
* Resumes processing of paused HTTP response.
*
* see: https://github.com/proxy-wasm/spec/blob/main/abi-versions/v0.1.0/README.md#proxy_continue_response
*/
@WasmExport
void proxyContinueResponse() {
handler.setAction(StreamType.RESPONSE, Action.CONTINUE);
}

/**
* Clears cached HTTP route.
*
* see: https://github.com/proxy-wasm/spec/blob/main/abi-versions/v0.1.0/README.md#proxy_clear_route_cache
*/
@WasmExport
void proxyClearRouteCache() {
handler.clearRouteCache();
}

@WasmExport
Expand Down
18 changes: 4 additions & 14 deletions src/main/java/io/roastedroot/proxywasm/v1/ChainedHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -236,23 +236,13 @@ public WasmResult setHttpRequestBody(byte[] body) {
}

@Override
public WasmResult continueRequest() {
return next().continueRequest();
public WasmResult setAction(StreamType streamType, Action action) {
return next().setAction(streamType, action);
}

@Override
public WasmResult continueResponse() {
return next().continueResponse();
}

@Override
public WasmResult continueDownstream() {
return next().continueDownstream();
}

@Override
public WasmResult continueUpstream() {
return next().continueUpstream();
public WasmResult clearRouteCache() {
return next().clearRouteCache();
}

@Override
Expand Down
12 changes: 2 additions & 10 deletions src/main/java/io/roastedroot/proxywasm/v1/Handler.java
Original file line number Diff line number Diff line change
Expand Up @@ -371,19 +371,11 @@ default WasmResult setGrpcReceiveTrailerMetaData(Map<String, String> metadata) {
return WasmResult.UNIMPLEMENTED;
}

default WasmResult continueRequest() {
default WasmResult setAction(StreamType streamType, Action action) {
return WasmResult.UNIMPLEMENTED;
}

default WasmResult continueResponse() {
return WasmResult.UNIMPLEMENTED;
}

default WasmResult continueDownstream() {
return WasmResult.UNIMPLEMENTED;
}

default WasmResult continueUpstream() {
default WasmResult clearRouteCache() {
return WasmResult.UNIMPLEMENTED;
}

Expand Down
16 changes: 12 additions & 4 deletions src/main/java/io/roastedroot/proxywasm/v1/HttpContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,18 @@ public Action callOnRequestHeaders(boolean endOfStream) {
var headers = handler.getHttpRequestHeaders();
int result =
proxyWasm.exports().proxyOnRequestHeaders(id, len(headers), endOfStream ? 1 : 0);
return Action.fromInt(result);
Action action = Action.fromInt(result);
handler.setAction(StreamType.REQUEST, action);
return action;
}

public Action callOnResponseHeaders(boolean endOfStream) {
var headers = handler.getHttpResponseHeaders();
int result =
proxyWasm.exports().proxyOnResponseHeaders(id, len(headers), endOfStream ? 1 : 0);
return Action.fromInt(result);
Action action = Action.fromInt(result);
handler.setAction(StreamType.RESPONSE, action);
return action;
}

public Action callOnRequestBody(boolean endOfStream) {
Expand All @@ -36,7 +40,9 @@ public Action callOnRequestBody(boolean endOfStream) {
proxyWasm
.exports()
.proxyOnRequestBody(id, Helpers.len(requestBody), endOfStream ? 1 : 0);
return Action.fromInt(result);
Action action = Action.fromInt(result);
handler.setAction(StreamType.REQUEST, action);
return action;
}

public Action callOnResponseBody(boolean endOfStream) {
Expand All @@ -45,6 +51,8 @@ public Action callOnResponseBody(boolean endOfStream) {
proxyWasm
.exports()
.proxyOnResponseBody(id, Helpers.len(responseBody), endOfStream ? 1 : 0);
return Action.fromInt(result);
Action action = Action.fromInt(result);
handler.setAction(StreamType.RESPONSE, action);
return action;
}
}
5 changes: 5 additions & 0 deletions src/test/go-examples/multiple_dispatches/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Attribution

This example originally came from:
https://github.com/proxy-wasm/proxy-wasm-go-sdk/blob/ab4161dcf9246a828008b539a82a1556cf0f2e24/examples/multiple_dispatches
```
5 changes: 5 additions & 0 deletions src/test/go-examples/multiple_dispatches/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/multiple_dispatches

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/multiple_dispatches/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=
100 changes: 100 additions & 0 deletions src/test/go-examples/multiple_dispatches/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// 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 (
"strconv"

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

const clusterName = "httpbin"

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
}

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

// httpContext implements types.HttpContext.
type httpContext struct {
// Embed the default http context here,
// so that we don't need to reimplement all the methods.
types.DefaultHttpContext
// contextID is the unique identifier assigned to each httpContext.
contextID uint32
// pendingDispatchedRequest is the number of pending dispatched requests.
pendingDispatchedRequest int
}

const totalDispatchNum = 10

// OnHttpResponseHeaders implements types.HttpContext.
func (ctx *httpContext) OnHttpResponseHeaders(numHeaders int, endOfStream bool) types.Action {
// On each request response, we dispatch the http calls `totalDispatchNum` times.
// Note: DispatchHttpCall is asynchronously processed, so each loop is non-blocking.
for i := 0; i < totalDispatchNum; i++ {
if _, err := proxywasm.DispatchHttpCall(clusterName, [][2]string{
{":path", "/"},
{":method", "GET"},
{":authority", ""}},
nil, nil, 50000, ctx.dispatchCallback); err != nil {
panic(err)
}
// Now we have made a dispatched request, so we record it.
ctx.pendingDispatchedRequest++
}
return types.ActionPause
}

// dispatchCallback is the callback function called in response to the response arrival from the dispatched request.
func (ctx *httpContext) dispatchCallback(numHeaders, bodySize, numTrailers int) {
// Decrement the pending request counter.
ctx.pendingDispatchedRequest--
if ctx.pendingDispatchedRequest == 0 {
// This case, all the dispatched request was processed.
// Adds a response header to the original response.
_ = proxywasm.AddHttpResponseHeader("total-dispatched", strconv.Itoa(totalDispatchNum))
// And then contniue the original reponse.
_ = proxywasm.ResumeHttpResponse()
proxywasm.LogInfof("response resumed after processed %d dispatched request", totalDispatchNum)
} else {
proxywasm.LogInfof("pending dispatched requests: %d", ctx.pendingDispatchedRequest)
}
}
Binary file not shown.
77 changes: 77 additions & 0 deletions src/test/java/io/roastedroot/proxywasm/JsonValidationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,83 @@
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

// This file what the go code was:
//
// func TestOnHTTPRequestHeaders(t *testing.T) {
// type testCase struct {
// contentType string
// expectedAction types.Action
// }
//
// vmTest(t, func(t *testing.T, vm types.VMContext) {
// for name, tCase := range map[string]testCase{
// "fails due to unsupported content type": {
// contentType: "text/html",
// expectedAction: types.ActionPause,
// },
// "success for JSON": {
// contentType: "application/json",
// expectedAction: types.ActionContinue,
// },
// } {
// t.Run(name, func(t *testing.T) {
// opt := proxytest.NewEmulatorOption().WithVMContext(vm)
// host, reset := proxytest.NewHostEmulator(opt)
// defer reset()
//
// require.Equal(t, types.OnPluginStartStatusOK, host.StartPlugin())
//
// id := host.InitializeHttpContext()
//
// hs := [][2]string{{"content-type", tCase.contentType}}
//
// action := host.CallOnRequestHeaders(id, hs, false)
// assert.Equal(t, tCase.expectedAction, action)
// })
// }
// })
// }
//
// func TestOnHTTPRequestBody(t *testing.T) {
// type testCase struct {
// body string
// expectedAction types.Action
// }
//
// vmTest(t, func(t *testing.T, vm types.VMContext) {
//
// for name, tCase := range map[string]testCase{
// "pauses due to invalid payload": {
// body: "invalid_payload",
// expectedAction: types.ActionPause,
// },
// "pauses due to unknown keys": {
// body: `{"unknown_key":"unknown_value"}`,
// expectedAction: types.ActionPause,
// },
// "success": {
// body: "{\"my_key\":\"my_value\"}",
// expectedAction: types.ActionContinue,
// },
// } {
// t.Run(name, func(t *testing.T) {
// opt := proxytest.
// NewEmulatorOption().
// WithPluginConfiguration([]byte(`{"requiredKeys": ["my_key"]}`)).
// WithVMContext(vm)
// host, reset := proxytest.NewHostEmulator(opt)
// defer reset()
//
// require.Equal(t, types.OnPluginStartStatusOK, host.StartPlugin())
//
// id := host.InitializeHttpContext()
//
// action := host.CallOnRequestBody(id, []byte(tCase.body), true)
// assert.Equal(t, tCase.expectedAction, action)
// })
// }
// })
// }
/**
* Java port of https://github.com/proxy-wasm/proxy-wasm-go-sdk/blob/ab4161dcf9246a828008b539a82a1556cf0f2e24/examples/json_validation/main_test.go
*/
Expand Down
14 changes: 14 additions & 0 deletions src/test/java/io/roastedroot/proxywasm/MockHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import io.roastedroot.proxywasm.v1.Action;
import io.roastedroot.proxywasm.v1.Handler;
import io.roastedroot.proxywasm.v1.Helpers;
import io.roastedroot.proxywasm.v1.LogLevel;
import io.roastedroot.proxywasm.v1.MetricType;
import io.roastedroot.proxywasm.v1.StreamType;
import io.roastedroot.proxywasm.v1.WasmException;
import io.roastedroot.proxywasm.v1.WasmResult;
import java.util.ArrayList;
Expand Down Expand Up @@ -421,4 +423,16 @@ public WasmResult removeMetric(int metricId) {
metricsByName.remove(metric.name);
return WasmResult.OK;
}

private Action action;

@Override
public WasmResult setAction(StreamType streamType, Action action) {
this.action = action;
return WasmResult.OK;
}

public Action getAction() {
return action;
}
}
Loading