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
1 change: 1 addition & 0 deletions src/main/java/io/roastedroot/proxywasm/v1/HttpContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
public class HttpContext extends Context {

private final Handler handler;
Action action;

HttpContext(ProxyWasm proxyWasm, Handler handler) {
super(proxyWasm);
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/io/roastedroot/proxywasm/v1/ProxyWasm.java
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ public static ProxyWasm.Builder builder() {
return new ProxyWasm.Builder();
}

public void callOnHttpCallResponse(
public void sendHttpCallResponse(
int calloutID, Map<String, String> headers, Map<String, String> trailers, byte[] body) {

this.httpCallResponseHeaders = headers;
Expand Down
Binary file modified src/test/go-examples/foreign_call_on_tick/main.wasm
Binary file not shown.
4 changes: 4 additions & 0 deletions src/test/go-examples/http_auth_random/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/http_auth_random
5 changes: 5 additions & 0 deletions src/test/go-examples/http_auth_random/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/http_auth_random

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

"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 &httpAuthRandom{contextID: contextID}
}

// httpAuthRandom implements types.HttpContext.
type httpAuthRandom struct {
// Embed the default http context here,
// so that we don't need to reimplement all the methods.
types.DefaultHttpContext
contextID uint32
}

// OnHttpRequestHeaders implements types.HttpContext.
func (ctx *httpAuthRandom) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
hs, err := proxywasm.GetHttpRequestHeaders()
if err != nil {
proxywasm.LogCriticalf("failed to get request headers: %v", err)
return types.ActionContinue
}
for _, h := range hs {
proxywasm.LogInfof("request header: %s: %s", h[0], h[1])
}

if _, err := proxywasm.DispatchHttpCall(clusterName, hs, nil, nil,
50000, httpCallResponseCallback); err != nil {
proxywasm.LogCriticalf("dipatch httpcall failed: %v", err)
return types.ActionContinue
}

proxywasm.LogInfof("http call dispatched to %s", clusterName)
return types.ActionPause
}

// httpCallResponseCallback is a callback function when the http call response is received after dispatching.
func httpCallResponseCallback(numHeaders, bodySize, numTrailers int) {
hs, err := proxywasm.GetHttpCallResponseHeaders()
if err != nil {
proxywasm.LogCriticalf("failed to get response body: %v", err)
return
}

for _, h := range hs {
proxywasm.LogInfof("response header from %s: %s: %s", clusterName, h[0], h[1])
}

b, err := proxywasm.GetHttpCallResponseBody(0, bodySize)
if err != nil {
proxywasm.LogCriticalf("failed to get response body: %v", err)
_ = proxywasm.ResumeHttpRequest()
return
}

s := fnv.New32a()
if _, err := s.Write(b); err != nil {
proxywasm.LogCriticalf("failed to calculate hash: %v", err)
_ = proxywasm.ResumeHttpRequest()
return
}

if s.Sum32()%2 == 0 {
proxywasm.LogInfo("access granted")
_ = proxywasm.ResumeHttpRequest()
return
}

body := "access forbidden"
proxywasm.LogInfo(body)
if err := proxywasm.SendHttpResponse(403, [][2]string{
{"powered-by", "proxy-wasm-go-sdk!!"},
}, []byte(body), -1); err != nil {
proxywasm.LogErrorf("failed to send local response: %v", err)
_ = proxywasm.ResumeHttpRequest()
}
}
Binary file added src/test/go-examples/http_auth_random/main.wasm
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public void testOnTick() throws StartException {
handler.getHttpCalls().entrySet().stream()
.forEach(
entry -> {
host.callOnHttpCallResponse(
host.sendHttpCallResponse(
entry.getKey(), Map.of(), Map.of(), new byte[0]);
});

Expand Down
121 changes: 121 additions & 0 deletions src/test/java/io/roastedroot/proxywasm/HttpAuthRandomTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package io.roastedroot.proxywasm;

import static io.roastedroot.proxywasm.v1.Helpers.bytes;
import static io.roastedroot.proxywasm.v1.Helpers.string;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;

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

public class HttpAuthRandomTest {

private static String clusterName = "httpbin";

private MockHandler handler;
private ProxyWasm host;

@BeforeEach
void setUp() throws StartException {
this.handler = new MockHandler();
ProxyWasm.Builder builder = ProxyWasm.builder();
var module = Parser.parse(Path.of("./src/test/go-examples/http_auth_random/main.wasm"));
this.host = builder.build(module);
}

@AfterEach
void tearDown() {
host.close();
}

@Test
public void onHttpRequestHeaders() throws StartException {
try (var context = host.createHttpContext(handler)) {

// Call OnRequestHeaders.
handler.setHttpRequestHeaders(Map.of("key", "value"));
var action = context.callOnRequestHeaders(false);
assertEquals(Action.PAUSE, action);

// Verify DispatchHttpCall is called.
var calls = handler.getHttpCalls();
assertEquals(1, calls.size());
MockHandler.HttpCall call = calls.values().stream().findFirst().get();
assertEquals(clusterName, call.uri);

// Check Envoy logs.
handler.assertLogsContain(
"http call dispatched to " + clusterName, "request header: key: value");
}
}

@Test
public void onHttpCallResponse() throws StartException {
var headers =
Map.of(
"HTTP/1.1", "200 OK",
"Date:", "Thu, 17 Sep 2020 02:47:07 GMT",
"Content-Type", "application/json",
"Content-Length", "53",
"Connection", "keep-alive",
"Server", "gunicorn/19.9.0",
"Access-Control-Allow-Origin", "*",
"Access-Control-Allow-Credentials", "true");

// Access granted case -> Local response must not be sent.
try (var context = host.createHttpContext(handler)) {

// Call OnRequestHeaders.
handler.setHttpRequestHeaders(Map.of());
var action = context.callOnRequestHeaders(false);
assertEquals(Action.PAUSE, action);

// Verify DispatchHttpCall is called.
var calls = handler.getHttpCalls();
assertEquals(1, calls.size());
var body = bytes("\"uuid\": \"7b10a67a-1c67-4199-835b-cbefcd4a63d4\"");
MockHandler.HttpCall call = calls.values().stream().findFirst().get();
host.sendHttpCallResponse(call.id, headers, null, body);
calls.remove(call.id);

// Check local response.
assertNull(handler.getSentHttpResponse());

// CHeck Envoy logs.
handler.assertLogsContain("access granted");
}

// Access denied case -> Local response must be sent.
try (var context = host.createHttpContext(handler)) {

// Call OnRequestHeaders.
handler.setHttpRequestHeaders(Map.of());
var action = context.callOnRequestHeaders(false);
assertEquals(Action.PAUSE, action);

// Verify DispatchHttpCall is called.
var calls = handler.getHttpCalls();
assertEquals(1, calls.size());
var body = bytes("\"uuid\": \"aaaaaaaa-1c67-4199-835b-cbefcd4a63d4\"");
MockHandler.HttpCall call = calls.values().stream().findFirst().get();
host.sendHttpCallResponse(call.id, headers, null, body);
// Check local response.
MockHandler.HttpResponse localResponse = handler.getSentHttpResponse();
assertNotNull(localResponse);
assertEquals(403, localResponse.statusCode);
assertEquals("access forbidden", string(localResponse.body));
assertEquals(Map.of("powered-by", "proxy-wasm-go-sdk!!"), localResponse.headers);

// CHeck Envoy logs.
handler.assertLogsContain("access forbidden");
}
}
}
25 changes: 19 additions & 6 deletions src/test/java/io/roastedroot/proxywasm/MockHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,17 @@ public void assertSortedLogsEqual(String... messages) {
loggedMessages().stream().sorted().collect(Collectors.toList()));
}

public void assertLogsContain(String message) {
assertTrue(
loggedMessages().contains(message), "logged messages does not contain: " + message);
public void assertLogsContain(String... message) {
for (String m : message) {
assertTrue(loggedMessages().contains(m), "logged messages does not contain: " + m);
}
}

public void assertLogsDoNotContain(String message) {
public void assertLogsDoNotContain(String... message) {
for (String log : loggedMessages()) {
assertFalse(log.contains(message), "logged messages contains: " + message);
for (String m : message) {
assertFalse(log.contains(m), "logged messages contains: " + m);
}
}
}

Expand Down Expand Up @@ -271,6 +274,7 @@ public enum Type {
DISPATCH
}

public final int id;
public final Type callType;
public final String uri;
public final Object headers;
Expand All @@ -279,12 +283,14 @@ public enum Type {
public final int timeoutMilliseconds;

public HttpCall(
int id,
Type callType,
String uri,
HashMap<String, String> headers,
byte[] body,
HashMap<String, String> trailers,
int timeoutMilliseconds) {
this.id = id;
this.callType = callType;
this.uri = uri;
this.headers = headers;
Expand Down Expand Up @@ -312,7 +318,13 @@ public int httpCall(
var id = lastCallId.incrementAndGet();
HttpCall value =
new HttpCall(
HttpCall.Type.REGULAR, uri, headers, body, trailers, timeoutMilliseconds);
id,
HttpCall.Type.REGULAR,
uri,
headers,
body,
trailers,
timeoutMilliseconds);
httpCalls.put(id, value);
return id;
}
Expand All @@ -328,6 +340,7 @@ public int dispatchHttpCall(
var id = lastCallId.incrementAndGet();
HttpCall value =
new HttpCall(
id,
HttpCall.Type.DISPATCH,
upstreamName,
headers,
Expand Down