Skip to content

Commit e526ac9

Browse files
committed
Add http_auth_random example and related tests.
Signed-off-by: Hiram Chirino <hiram@hiramchirino.com>
1 parent 6527bf6 commit e526ac9

File tree

11 files changed

+286
-8
lines changed

11 files changed

+286
-8
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
public class HttpContext extends Context {
66

77
private final Handler handler;
8+
Action action;
89

910
HttpContext(ProxyWasm proxyWasm, Handler handler) {
1011
super(proxyWasm);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ public static ProxyWasm.Builder builder() {
257257
return new ProxyWasm.Builder();
258258
}
259259

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

263263
this.httpCallResponseHeaders = headers;
945 Bytes
Binary file not shown.
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/http_auth_random
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/http_auth_random
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: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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+
"hash/fnv"
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 clusterName = "httpbin"
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+
}
49+
50+
// NewHttpContext implements types.PluginContext.
51+
func (*pluginContext) NewHttpContext(contextID uint32) types.HttpContext {
52+
return &httpAuthRandom{contextID: contextID}
53+
}
54+
55+
// httpAuthRandom implements types.HttpContext.
56+
type httpAuthRandom struct {
57+
// Embed the default http context here,
58+
// so that we don't need to reimplement all the methods.
59+
types.DefaultHttpContext
60+
contextID uint32
61+
}
62+
63+
// OnHttpRequestHeaders implements types.HttpContext.
64+
func (ctx *httpAuthRandom) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
65+
hs, err := proxywasm.GetHttpRequestHeaders()
66+
if err != nil {
67+
proxywasm.LogCriticalf("failed to get request headers: %v", err)
68+
return types.ActionContinue
69+
}
70+
for _, h := range hs {
71+
proxywasm.LogInfof("request header: %s: %s", h[0], h[1])
72+
}
73+
74+
if _, err := proxywasm.DispatchHttpCall(clusterName, hs, nil, nil,
75+
50000, httpCallResponseCallback); err != nil {
76+
proxywasm.LogCriticalf("dipatch httpcall failed: %v", err)
77+
return types.ActionContinue
78+
}
79+
80+
proxywasm.LogInfof("http call dispatched to %s", clusterName)
81+
return types.ActionPause
82+
}
83+
84+
// httpCallResponseCallback is a callback function when the http call response is received after dispatching.
85+
func httpCallResponseCallback(numHeaders, bodySize, numTrailers int) {
86+
hs, err := proxywasm.GetHttpCallResponseHeaders()
87+
if err != nil {
88+
proxywasm.LogCriticalf("failed to get response body: %v", err)
89+
return
90+
}
91+
92+
for _, h := range hs {
93+
proxywasm.LogInfof("response header from %s: %s: %s", clusterName, h[0], h[1])
94+
}
95+
96+
b, err := proxywasm.GetHttpCallResponseBody(0, bodySize)
97+
if err != nil {
98+
proxywasm.LogCriticalf("failed to get response body: %v", err)
99+
_ = proxywasm.ResumeHttpRequest()
100+
return
101+
}
102+
103+
s := fnv.New32a()
104+
if _, err := s.Write(b); err != nil {
105+
proxywasm.LogCriticalf("failed to calculate hash: %v", err)
106+
_ = proxywasm.ResumeHttpRequest()
107+
return
108+
}
109+
110+
if s.Sum32()%2 == 0 {
111+
proxywasm.LogInfo("access granted")
112+
_ = proxywasm.ResumeHttpRequest()
113+
return
114+
}
115+
116+
body := "access forbidden"
117+
proxywasm.LogInfo(body)
118+
if err := proxywasm.SendHttpResponse(403, [][2]string{
119+
{"powered-by", "proxy-wasm-go-sdk!!"},
120+
}, []byte(body), -1); err != nil {
121+
proxywasm.LogErrorf("failed to send local response: %v", err)
122+
_ = proxywasm.ResumeHttpRequest()
123+
}
124+
}
2.4 MB
Binary file not shown.

src/test/java/io/roastedroot/proxywasm/DispatchCallOnTickTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public void testOnTick() throws StartException {
3333
handler.getHttpCalls().entrySet().stream()
3434
.forEach(
3535
entry -> {
36-
host.callOnHttpCallResponse(
36+
host.sendHttpCallResponse(
3737
entry.getKey(), Map.of(), Map.of(), new byte[0]);
3838
});
3939

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package io.roastedroot.proxywasm;
2+
3+
import static io.roastedroot.proxywasm.v1.Helpers.bytes;
4+
import static io.roastedroot.proxywasm.v1.Helpers.string;
5+
import static org.junit.jupiter.api.Assertions.assertEquals;
6+
import static org.junit.jupiter.api.Assertions.assertNotNull;
7+
import static org.junit.jupiter.api.Assertions.assertNull;
8+
9+
import com.dylibso.chicory.wasm.Parser;
10+
import io.roastedroot.proxywasm.v1.Action;
11+
import io.roastedroot.proxywasm.v1.ProxyWasm;
12+
import io.roastedroot.proxywasm.v1.StartException;
13+
import java.nio.file.Path;
14+
import java.util.Map;
15+
import org.junit.jupiter.api.AfterEach;
16+
import org.junit.jupiter.api.BeforeEach;
17+
import org.junit.jupiter.api.Test;
18+
19+
public class HttpAuthRandomTest {
20+
21+
private static String clusterName = "httpbin";
22+
23+
private MockHandler handler;
24+
private ProxyWasm host;
25+
26+
@BeforeEach
27+
void setUp() throws StartException {
28+
this.handler = new MockHandler();
29+
ProxyWasm.Builder builder = ProxyWasm.builder();
30+
var module = Parser.parse(Path.of("./src/test/go-examples/http_auth_random/main.wasm"));
31+
this.host = builder.build(module);
32+
}
33+
34+
@AfterEach
35+
void tearDown() {
36+
host.close();
37+
}
38+
39+
@Test
40+
public void onHttpRequestHeaders() throws StartException {
41+
try (var context = host.createHttpContext(handler)) {
42+
43+
// Call OnRequestHeaders.
44+
handler.setHttpRequestHeaders(Map.of("key", "value"));
45+
var action = context.callOnRequestHeaders(false);
46+
assertEquals(Action.PAUSE, action);
47+
48+
// Verify DispatchHttpCall is called.
49+
var calls = handler.getHttpCalls();
50+
assertEquals(1, calls.size());
51+
MockHandler.HttpCall call = calls.values().stream().findFirst().get();
52+
assertEquals(clusterName, call.uri);
53+
54+
// Check Envoy logs.
55+
handler.assertLogsContain(
56+
"http call dispatched to " + clusterName, "request header: key: value");
57+
}
58+
}
59+
60+
@Test
61+
public void onHttpCallResponse() throws StartException {
62+
var headers =
63+
Map.of(
64+
"HTTP/1.1", "200 OK",
65+
"Date:", "Thu, 17 Sep 2020 02:47:07 GMT",
66+
"Content-Type", "application/json",
67+
"Content-Length", "53",
68+
"Connection", "keep-alive",
69+
"Server", "gunicorn/19.9.0",
70+
"Access-Control-Allow-Origin", "*",
71+
"Access-Control-Allow-Credentials", "true");
72+
73+
// Access granted case -> Local response must not be sent.
74+
try (var context = host.createHttpContext(handler)) {
75+
76+
// Call OnRequestHeaders.
77+
handler.setHttpRequestHeaders(Map.of());
78+
var action = context.callOnRequestHeaders(false);
79+
assertEquals(Action.PAUSE, action);
80+
81+
// Verify DispatchHttpCall is called.
82+
var calls = handler.getHttpCalls();
83+
assertEquals(1, calls.size());
84+
var body = bytes("\"uuid\": \"7b10a67a-1c67-4199-835b-cbefcd4a63d4\"");
85+
MockHandler.HttpCall call = calls.values().stream().findFirst().get();
86+
host.sendHttpCallResponse(call.id, headers, null, body);
87+
calls.remove(call.id);
88+
89+
// Check local response.
90+
assertNull(handler.getSentHttpResponse());
91+
92+
// CHeck Envoy logs.
93+
handler.assertLogsContain("access granted");
94+
}
95+
96+
// Access denied case -> Local response must be sent.
97+
try (var context = host.createHttpContext(handler)) {
98+
99+
// Call OnRequestHeaders.
100+
handler.setHttpRequestHeaders(Map.of());
101+
var action = context.callOnRequestHeaders(false);
102+
assertEquals(Action.PAUSE, action);
103+
104+
// Verify DispatchHttpCall is called.
105+
var calls = handler.getHttpCalls();
106+
assertEquals(1, calls.size());
107+
var body = bytes("\"uuid\": \"aaaaaaaa-1c67-4199-835b-cbefcd4a63d4\"");
108+
MockHandler.HttpCall call = calls.values().stream().findFirst().get();
109+
host.sendHttpCallResponse(call.id, headers, null, body);
110+
// Check local response.
111+
MockHandler.HttpResponse localResponse = handler.getSentHttpResponse();
112+
assertNotNull(localResponse);
113+
assertEquals(403, localResponse.statusCode);
114+
assertEquals("access forbidden", string(localResponse.body));
115+
assertEquals(Map.of("powered-by", "proxy-wasm-go-sdk!!"), localResponse.headers);
116+
117+
// CHeck Envoy logs.
118+
handler.assertLogsContain("access forbidden");
119+
}
120+
}
121+
}

0 commit comments

Comments
 (0)