From 3eaad219272ad0bc9e58cf37ab331848e169cd1c Mon Sep 17 00:00:00 2001 From: Hiram Chirino Date: Mon, 17 Mar 2025 13:16:59 -0400 Subject: [PATCH] Improve the Hander interface to properties to match the spec better. Adds a rust envoy_filter_metadata example and associated test. Signed-off-by: Hiram Chirino --- .../java/io/roastedroot/proxywasm/ABI.java | 70 ++++++---- .../roastedroot/proxywasm/ChainedHandler.java | 14 +- .../io/roastedroot/proxywasm/Handler.java | 6 +- .../io/roastedroot/proxywasm/Helpers.java | 16 +++ .../io/roastedroot/proxywasm/ProxyWasm.java | 43 ------ .../proxywasm/examples/MockHandler.java | 13 ++ .../examples/OnRequestHeadersTest.java | 8 +- .../proxywasm/examples/PropertiesTest.java | 31 +++-- .../examples/RustEnvoyFilterMetadataTest.java | 51 +++++++ .../examples/RustHelloWorldTest.java | 6 +- .../proxywasm/examples/SharedQueueTest.java | 8 +- .../examples/VmPluginConfigurationTest.java | 4 +- .../envoy_filter_metadata/Cargo.lock | 127 ++++++++++++++++++ .../envoy_filter_metadata/Cargo.toml | 21 +++ .../envoy_filter_metadata/README.md | 5 + .../envoy_filter_metadata/main.wasm | Bin 0 -> 168148 bytes .../envoy_filter_metadata/src/lib.rs | 52 +++++++ 17 files changed, 377 insertions(+), 98 deletions(-) create mode 100644 src/test/java/io/roastedroot/proxywasm/examples/RustEnvoyFilterMetadataTest.java create mode 100644 src/test/rust-examples/envoy_filter_metadata/Cargo.lock create mode 100644 src/test/rust-examples/envoy_filter_metadata/Cargo.toml create mode 100644 src/test/rust-examples/envoy_filter_metadata/README.md create mode 100755 src/test/rust-examples/envoy_filter_metadata/main.wasm create mode 100644 src/test/rust-examples/envoy_filter_metadata/src/lib.rs diff --git a/src/main/java/io/roastedroot/proxywasm/ABI.java b/src/main/java/io/roastedroot/proxywasm/ABI.java index 1f461fa..ff9003e 100644 --- a/src/main/java/io/roastedroot/proxywasm/ABI.java +++ b/src/main/java/io/roastedroot/proxywasm/ABI.java @@ -1,6 +1,8 @@ package io.roastedroot.proxywasm; +import static io.roastedroot.proxywasm.Helpers.bytes; import static io.roastedroot.proxywasm.Helpers.replaceBytes; +import static io.roastedroot.proxywasm.Helpers.split; import static io.roastedroot.proxywasm.Helpers.string; import com.dylibso.chicory.experimental.hostmodule.annotations.HostModule; @@ -11,7 +13,7 @@ import com.dylibso.chicory.runtime.WasmRuntimeException; import com.dylibso.chicory.wasm.InvalidException; import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; +import java.util.List; import java.util.Map; @HostModule("env") @@ -221,15 +223,6 @@ private byte[] readMemory(int address, int len) throws WasmException { } } - private String readString(int address, int len) throws WasmException { - var data = readMemory(address, len); - return new String(data, StandardCharsets.UTF_8); - } - - private void copyIntoInstance(String value, int retPtr, int retSize) throws WasmException { - copyIntoInstance(value.getBytes(), retPtr, retSize); - } - private void copyIntoInstance(byte[] value, int retPtr, int retSize) throws WasmException { try { if (value.length != 0) { @@ -402,7 +395,7 @@ boolean proxyOnConfigure(int arg0, int arg1) { int proxyLog(int logLevel, int messageData, int messageSize) { try { var msg = memory.readBytes(messageData, messageSize); - handler.log(LogLevel.fromInt(logLevel), new String(msg)); + handler.log(LogLevel.fromInt(logLevel), string(msg)); return WasmResult.OK.getValue(); } catch (WasmException e) { return e.result().getValue(); @@ -840,7 +833,7 @@ int proxyGetHeaderMapValue( } // Get key from memory - String key = readString(keyDataPtr, keySize); + String key = string(readMemory(keyDataPtr, keySize)); // Get value from map String value = headerMap.get(key); @@ -849,7 +842,7 @@ int proxyGetHeaderMapValue( } // Copy value into WebAssembly memory - copyIntoInstance(value, valueDataPtr, valueSize); + copyIntoInstance(bytes(value), valueDataPtr, valueSize); return WasmResult.OK.getValue(); } catch (WasmRuntimeException e) { @@ -898,10 +891,10 @@ int proxyReplaceHeaderMapValue( } // Get key from memory - String key = readString(keyDataPtr, keySize); + String key = string(readMemory(keyDataPtr, keySize)); // Get value from memory - String value = readString(valueDataPtr, valueSize); + String value = string(readMemory(valueDataPtr, valueSize)); // Replace value in map var copy = new ArrayProxyMap(headerMap); @@ -936,7 +929,7 @@ int proxyRemoveHeaderMapValue(int mapType, int keyDataPtr, int keySize) { } // Get key from memory - String key = readString(keyDataPtr, keySize); + String key = string(readMemory(keyDataPtr, keySize)); if (key.isEmpty()) { return WasmResult.BAD_ARGUMENT.getValue(); } @@ -1075,11 +1068,11 @@ private ProxyMap decodeMap(int addr, int mem_size) throws WasmException { } // Extract key - String key = readString((int) (addr + dataOffset), (int) keySize); + String key = string(readMemory((int) (addr + dataOffset), (int) keySize)); dataOffset += keySize + 1; // Skip null terminator // Extract value - String value = readString((int) (addr + dataOffset), (int) valueSize); + String value = string(readMemory((int) (addr + dataOffset), (int) valueSize)); dataOffset += valueSize + 1; // Add to result map @@ -1620,7 +1613,18 @@ int proxyGetSharedData( return WasmResult.NOT_FOUND.getValue(); } - copyIntoInstance(value.data, returnValueData, returnValueSize); + try { + if (value.data.length != 0) { + int addr = malloc(value.data.length); + putMemory(addr, value.data); + putUint32(returnValueData, addr); + } else { + putUint32(returnValueData, 0); + } + putUint32(returnValueSize, value.data.length); + } catch (WasmException e) { + throw new WasmException(WasmResult.INVALID_MEMORY_ACCESS); + } putUint32(returnCas, value.cas); return WasmResult.OK.getValue(); @@ -1642,10 +1646,13 @@ int proxyRegisterSharedQueue(int queueNameDataPtr, int queueNameSize, int return // Get queue name from memory String queueName = string(readMemory(queueNameDataPtr, queueNameSize)); - var vmId = handler.getProperty("vm_id"); + var vmId = handler.getProperty(List.of("vm_id")); + if (vmId == null) { + return WasmResult.INTERNAL_FAILURE.getValue(); + } // Register shared queue using handler - int queueId = handler.registerSharedQueue(new QueueName(vmId, queueName)); + int queueId = handler.registerSharedQueue(new QueueName(string(vmId), queueName)); putUint32(returnQueueId, queueId); return WasmResult.OK.getValue(); @@ -1710,7 +1717,18 @@ int proxyDequeueSharedQueue(int queueId, int returnValueData, int returnValueSiz return WasmResult.EMPTY.getValue(); } - copyIntoInstance(value, returnValueData, returnValueSize); + try { + if (value.length != 0) { + int addr = malloc(value.length); + putMemory(addr, value); + putUint32(returnValueData, addr); + } else { + putUint32(returnValueData, 0); + } + putUint32(returnValueSize, value.length); + } catch (WasmException e) { + throw new WasmException(WasmResult.INVALID_MEMORY_ACCESS); + } return WasmResult.OK.getValue(); } catch (WasmException e) { @@ -1811,10 +1829,10 @@ int proxyGetProperty(int keyPtr, int keySize, int returnValueData, int returnVal return WasmResult.BAD_ARGUMENT.getValue(); } - String key = new String(keyBytes); + var path = split(string(keyBytes), '\u0000'); // Get property value using handler - String value = handler.getProperty(key); + byte[] value = handler.getProperty(path); if (value == null) { return WasmResult.NOT_FOUND.getValue(); } @@ -1834,10 +1852,10 @@ int proxyGetProperty(int keyPtr, int keySize, int returnValueData, int returnVal int proxySetProperty(int pathDataPtr, int pathSize, int valueDataPtr, int valueSize) { try { // Get key from memory - String path = string(readMemory(pathDataPtr, pathSize)); + var path = split(string(readMemory(pathDataPtr, pathSize)), '\u0000'); // Get value from memory - String value = string(readMemory(valueDataPtr, valueSize)); + var value = readMemory(valueDataPtr, valueSize); // Set property value using handler WasmResult result = handler.setProperty(path, value); diff --git a/src/main/java/io/roastedroot/proxywasm/ChainedHandler.java b/src/main/java/io/roastedroot/proxywasm/ChainedHandler.java index 255eef2..ddb68da 100644 --- a/src/main/java/io/roastedroot/proxywasm/ChainedHandler.java +++ b/src/main/java/io/roastedroot/proxywasm/ChainedHandler.java @@ -1,5 +1,7 @@ package io.roastedroot.proxywasm; +import java.util.List; + /** * A Handler implementation that chains to another handler if it can't handle the request. */ @@ -103,10 +105,15 @@ public WasmResult setGrpcReceiveTrailerMetaData(ProxyMap metadata) { } @Override - public String getProperty(String key) throws WasmException { + public byte[] getProperty(List key) throws WasmException { return next().getProperty(key); } + @Override + public WasmResult setProperty(List path, byte[] value) { + return next().setProperty(path, value); + } + @Override public byte[] getHttpRequestBody() { return next().getHttpRequestBody(); @@ -324,11 +331,6 @@ public LogLevel getLogLevel() throws WasmException { return next().getLogLevel(); } - @Override - public WasmResult setProperty(String path, String value) { - return next().setProperty(path, value); - } - @Override public int grpcCall( String upstreamName, diff --git a/src/main/java/io/roastedroot/proxywasm/Handler.java b/src/main/java/io/roastedroot/proxywasm/Handler.java index 73594db..1557aec 100644 --- a/src/main/java/io/roastedroot/proxywasm/Handler.java +++ b/src/main/java/io/roastedroot/proxywasm/Handler.java @@ -1,5 +1,7 @@ package io.roastedroot.proxywasm; +import java.util.List; + public interface Handler { default void log(LogLevel level, String message) throws WasmException {} @@ -45,11 +47,11 @@ default ProxyMap getCustomHeaders(int mapType) { return null; } - default String getProperty(String key) throws WasmException { + default byte[] getProperty(List key) throws WasmException { return null; } - default WasmResult setProperty(String path, String value) { + default WasmResult setProperty(List path, byte[] value) { return WasmResult.UNIMPLEMENTED; } diff --git a/src/main/java/io/roastedroot/proxywasm/Helpers.java b/src/main/java/io/roastedroot/proxywasm/Helpers.java index e71f4ba..eeaa3e2 100644 --- a/src/main/java/io/roastedroot/proxywasm/Helpers.java +++ b/src/main/java/io/roastedroot/proxywasm/Helpers.java @@ -2,7 +2,9 @@ import com.dylibso.chicory.runtime.HostFunction; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Map; public final class Helpers { @@ -30,6 +32,20 @@ public static String string(byte[] value) { return new String(value, StandardCharsets.UTF_8); } + public static List split(String str, char separator) { + ArrayList parts = new ArrayList<>(); + int start = 0; + int len = str.length(); + for (int i = 0; i < len; i++) { + if (str.charAt(i) == separator) { + parts.add(str.substring(start, i)); + start = i + 1; + } + } + parts.add(str.substring(start)); // Add the last part + return List.copyOf(parts); + } + public static int len(byte[] value) { if (value == null) { return 0; diff --git a/src/main/java/io/roastedroot/proxywasm/ProxyWasm.java b/src/main/java/io/roastedroot/proxywasm/ProxyWasm.java index dbee2d8..760429f 100644 --- a/src/main/java/io/roastedroot/proxywasm/ProxyWasm.java +++ b/src/main/java/io/roastedroot/proxywasm/ProxyWasm.java @@ -26,7 +26,6 @@ public final class ProxyWasm implements Closeable { private final Handler pluginHandler; private final byte[] pluginConfig; private final byte[] vmConfig; - private final HashMap properties; private final WasiPreview1 wasi; private final AtomicInteger nextContextID = new AtomicInteger(1); @@ -42,7 +41,6 @@ public final class ProxyWasm implements Closeable { private ProxyWasm(Builder other) throws StartException { this.vmConfig = other.vmConfig; this.pluginConfig = other.pluginConfig; - this.properties = Objects.requireNonNullElse(other.properties, new HashMap<>()); this.pluginHandler = Objects.requireNonNullElse(other.pluginHandler, new Handler() {}); this.wasi = other.wasi; this.abi = other.abi; @@ -91,19 +89,6 @@ public byte[] getPluginConfig() { return pluginConfig; } - @Override - public String getProperty(String key) throws WasmException { - if (properties.containsKey(key)) { - return properties.get(key); - } - - var handler = activeContext.handler(); - if (handler != null) { - return handler.getProperty(key); - } - return null; - } - @Override public WasmResult setEffectiveContextID(int contextID) { Context context = contexts.get(contextID); @@ -188,24 +173,6 @@ public void tick() { this.abi.proxyOnTick(pluginContext.id()); } - public void setProperty(String[] path, String data) { - if (len(path) == 0) { - throw new IllegalArgumentException("path must not be empty"); - } - if (len(data) == 0) { - throw new IllegalArgumentException("data must not be empty"); - } - - this.properties.put(String.join("\u0000", path), data); - } - - public String getProperty(String[] path) { - if (len(path) == 0) { - throw new IllegalArgumentException("path must not be empty"); - } - return this.properties.get(String.join("\u0000", path)); - } - @Override public void close() { this.pluginContext.close(); @@ -262,7 +229,6 @@ public static class Builder implements Cloneable { private byte[] vmConfig = new byte[0]; private byte[] pluginConfig = new byte[0]; - private HashMap properties; private Handler pluginHandler; private ImportMemory memory; private WasiOptions wasiOptions; @@ -301,15 +267,6 @@ public ProxyWasm.Builder withPluginConfig(String pluginConfig) { return this; } - public ProxyWasm.Builder withProperties(Map properties) { - if (properties != null) { - this.properties = new HashMap<>(properties); - } else { - this.properties = null; - } - return this; - } - public ProxyWasm.Builder withPluginHandler(Handler vmHandler) { this.pluginHandler = vmHandler; return this; diff --git a/src/test/java/io/roastedroot/proxywasm/examples/MockHandler.java b/src/test/java/io/roastedroot/proxywasm/examples/MockHandler.java index 9205968..a0a1dd8 100644 --- a/src/test/java/io/roastedroot/proxywasm/examples/MockHandler.java +++ b/src/test/java/io/roastedroot/proxywasm/examples/MockHandler.java @@ -461,4 +461,17 @@ public WasmResult setAction(StreamType streamType, Action action) { public Action getAction() { return action; } + + final HashMap, byte[]> properties = new HashMap<>(); + + @Override + public byte[] getProperty(List path) throws WasmException { + return properties.get(path); + } + + @Override + public WasmResult setProperty(List path, byte[] value) { + properties.put(path, value); + return WasmResult.OK; + } } diff --git a/src/test/java/io/roastedroot/proxywasm/examples/OnRequestHeadersTest.java b/src/test/java/io/roastedroot/proxywasm/examples/OnRequestHeadersTest.java index 2b09a29..1cd2f34 100644 --- a/src/test/java/io/roastedroot/proxywasm/examples/OnRequestHeadersTest.java +++ b/src/test/java/io/roastedroot/proxywasm/examples/OnRequestHeadersTest.java @@ -1,5 +1,6 @@ package io.roastedroot.proxywasm.examples; +import static io.roastedroot.proxywasm.Helpers.bytes; import static org.junit.jupiter.api.Assertions.assertEquals; import com.dylibso.chicory.wasm.Parser; @@ -22,10 +23,9 @@ public class OnRequestHeadersTest { public void test() throws StartException { // This module uses the 0_1_0 ABI var handler = new MockHandler(); - ProxyWasm.Builder builder = - ProxyWasm.builder() - .withProperties(Map.of("plugin_root_id", "")) - .withPluginHandler(handler); + handler.setProperty(List.of("plugin_root_id"), bytes("")); + + ProxyWasm.Builder builder = ProxyWasm.builder().withPluginHandler(handler); try (var proxyWasm = builder.build(module)) { diff --git a/src/test/java/io/roastedroot/proxywasm/examples/PropertiesTest.java b/src/test/java/io/roastedroot/proxywasm/examples/PropertiesTest.java index 13be5ba..8c9e594 100644 --- a/src/test/java/io/roastedroot/proxywasm/examples/PropertiesTest.java +++ b/src/test/java/io/roastedroot/proxywasm/examples/PropertiesTest.java @@ -1,6 +1,8 @@ package io.roastedroot.proxywasm.examples; import static io.roastedroot.proxywasm.Helpers.append; +import static io.roastedroot.proxywasm.Helpers.bytes; +import static io.roastedroot.proxywasm.Helpers.string; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -10,7 +12,10 @@ import io.roastedroot.proxywasm.Action; import io.roastedroot.proxywasm.ProxyWasm; import io.roastedroot.proxywasm.StartException; +import io.roastedroot.proxywasm.WasmException; import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; import java.util.Map; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; @@ -33,7 +38,7 @@ public class PropertiesTest { @BeforeEach void setUp() throws StartException { this.handler = new MockHandler(); - ProxyWasm.Builder builder = ProxyWasm.builder(); + ProxyWasm.Builder builder = ProxyWasm.builder().withPluginHandler(this.handler); this.proxyWasm = builder.build(module); } @@ -55,12 +60,14 @@ public void routeIsUnauthenticated() { } @Test - public void userIsAuthenticated() { + public void userIsAuthenticated() throws WasmException { var path = "auth"; - var data = "cookie"; - proxyWasm.setProperty(append(propertyPrefix, path), data); - var actualData = proxyWasm.getProperty(append(propertyPrefix, path)); + var data = bytes("cookie"); + + List key = Arrays.asList(append(propertyPrefix, path)); + handler.setProperty(key, data); + var actualData = handler.getProperty(key); assertEquals(data, actualData); int id = 0; @@ -72,16 +79,20 @@ public void userIsAuthenticated() { } handler.assertLogsEqual( - String.format("auth header is \"%s\"", data), String.format("%d finished", id)); + String.format("auth header is \"%s\"", string(data)), + String.format("%d finished", id)); } @Test - public void userIsUnauthenticated() { + public void userIsUnauthenticated() throws WasmException { var path = "auth"; - var data = "cookie"; - proxyWasm.setProperty(append(propertyPrefix, path), data); - var actualData = proxyWasm.getProperty(append(propertyPrefix, path)); + var data = bytes("cookie"); + + List key = Arrays.asList(append(propertyPrefix, path)); + + handler.setProperty(key, data); + var actualData = handler.getProperty(key); assertEquals(data, actualData); try (var host = proxyWasm.createHttpContext(handler)) { diff --git a/src/test/java/io/roastedroot/proxywasm/examples/RustEnvoyFilterMetadataTest.java b/src/test/java/io/roastedroot/proxywasm/examples/RustEnvoyFilterMetadataTest.java new file mode 100644 index 0000000..39c828a --- /dev/null +++ b/src/test/java/io/roastedroot/proxywasm/examples/RustEnvoyFilterMetadataTest.java @@ -0,0 +1,51 @@ +package io.roastedroot.proxywasm.examples; + +import static io.roastedroot.proxywasm.Helpers.bytes; +import static io.roastedroot.proxywasm.Helpers.string; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.dylibso.chicory.wasm.Parser; +import com.dylibso.chicory.wasm.WasmModule; +import io.roastedroot.proxywasm.ProxyMap; +import io.roastedroot.proxywasm.ProxyWasm; +import io.roastedroot.proxywasm.StartException; +import java.nio.file.Path; +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * Test loading https://github.com/proxy-wasm/proxy-wasm-rust-sdk/tree/c8b2335df66a569a6306c58e346dd0cf9dbc0f3a/examples/envoy_filter_metadata + */ +public class RustEnvoyFilterMetadataTest { + private static final WasmModule module = + Parser.parse(Path.of("./src/test/rust-examples/envoy_filter_metadata/main.wasm")); + + @Test + public void test() throws StartException { + var handler = new MockHandler(); + try (var host = ProxyWasm.builder().withPluginHandler(handler).build(module)) { + + String value = "SOME-VALUE"; + handler.setProperty( + List.of( + "metadata", + "filter_metadata", + "envoy.filters.http.lua", + "uppercased-custom-metadata"), + bytes(value)); + try (var context = host.createHttpContext(handler)) { + context.callOnRequestHeaders(true); + MockHandler.HttpResponse response = handler.getSentHttpResponse(); + assertNotNull(response); + assertEquals(200, response.statusCode); + assertEquals( + ProxyMap.of("Powered-By", "proxy-wasm", "uppercased-metadata", value), + response.headers); + assertEquals( + String.format("Custom response with Envoy metadata: \"%s\"\n", value), + string(response.body)); + } + } + } +} diff --git a/src/test/java/io/roastedroot/proxywasm/examples/RustHelloWorldTest.java b/src/test/java/io/roastedroot/proxywasm/examples/RustHelloWorldTest.java index a2103cf..bb70676 100644 --- a/src/test/java/io/roastedroot/proxywasm/examples/RustHelloWorldTest.java +++ b/src/test/java/io/roastedroot/proxywasm/examples/RustHelloWorldTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import com.dylibso.chicory.wasm.Parser; +import com.dylibso.chicory.wasm.WasmModule; import io.roastedroot.proxywasm.ProxyWasm; import io.roastedroot.proxywasm.StartException; import java.nio.file.Path; @@ -12,11 +13,12 @@ * Test loading https://github.com/proxy-wasm/proxy-wasm-rust-sdk/tree/c8b2335df66a569a6306c58e346dd0cf9dbc0f3a/examples/hello_world */ public class RustHelloWorldTest { + private static final WasmModule module = + Parser.parse(Path.of("./src/test/rust-examples/hello_world/main.wasm")); @Test - public void pauseUntilEOS() throws StartException { + public void test() throws StartException { var handler = new MockHandler(); - var module = Parser.parse(Path.of("./src/test/rust-examples/hello_world/main.wasm")); try (var host = ProxyWasm.builder().withPluginHandler(handler).build(module)) { handler.assertLogsEqual("Hello, World!"); diff --git a/src/test/java/io/roastedroot/proxywasm/examples/SharedQueueTest.java b/src/test/java/io/roastedroot/proxywasm/examples/SharedQueueTest.java index 08452bc..4fb8b73 100644 --- a/src/test/java/io/roastedroot/proxywasm/examples/SharedQueueTest.java +++ b/src/test/java/io/roastedroot/proxywasm/examples/SharedQueueTest.java @@ -1,5 +1,6 @@ package io.roastedroot.proxywasm.examples; +import static io.roastedroot.proxywasm.Helpers.bytes; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -15,6 +16,7 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; +import java.util.List; import java.util.Map; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -55,11 +57,11 @@ public void testOnPluginStart() throws StartException, WasmException { // Create and configure the http_request_headers receiver instance var receiverHandler1 = new MockHandler(sharedData); + receiverHandler1.setProperty(List.of("vm_id"), bytes(receiverVmId)); var receiverHost1 = deferClose( ProxyWasm.builder() .withPluginHandler(receiverHandler1) - .withProperties(Map.of("vm_id", receiverVmId)) .withPluginConfig("http_request_headers") .build(receiverModule)); @@ -74,11 +76,11 @@ public void testOnPluginStart() throws StartException, WasmException { // Create and configure the http_response_headers receiver instance var receiverHandler2 = new MockHandler(sharedData); + receiverHandler2.setProperty(List.of("vm_id"), bytes(receiverVmId)); var receiverHost2 = deferClose( ProxyWasm.builder() .withPluginHandler(receiverHandler2) - .withProperties(Map.of("vm_id", receiverVmId)) .withPluginConfig("http_response_headers") .build(receiverModule)); @@ -96,11 +98,11 @@ public void testOnPluginStart() throws StartException, WasmException { // Create and configure the sender instance var senderHandler = new MockHandler(sharedData); var senderVmId = "sender"; + senderHandler.setProperty(List.of("vm_id"), bytes(senderVmId)); var senderHost = deferClose( ProxyWasm.builder() .withPluginHandler(senderHandler) - .withProperties(Map.of("vm_id", senderVmId)) .withPluginConfig("http") .build(senderModule)); senderHandler.assertLogsContain( diff --git a/src/test/java/io/roastedroot/proxywasm/examples/VmPluginConfigurationTest.java b/src/test/java/io/roastedroot/proxywasm/examples/VmPluginConfigurationTest.java index 28fd4b4..5ea95d3 100644 --- a/src/test/java/io/roastedroot/proxywasm/examples/VmPluginConfigurationTest.java +++ b/src/test/java/io/roastedroot/proxywasm/examples/VmPluginConfigurationTest.java @@ -1,5 +1,6 @@ package io.roastedroot.proxywasm.examples; +import static io.roastedroot.proxywasm.Helpers.bytes; import static org.junit.jupiter.api.Assertions.assertEquals; import com.dylibso.chicory.wasm.Parser; @@ -8,7 +9,6 @@ import io.roastedroot.proxywasm.StartException; import java.nio.file.Path; import java.util.List; -import java.util.Map; import org.junit.jupiter.api.Test; /** @@ -22,11 +22,11 @@ public class VmPluginConfigurationTest { @Test public void test() throws StartException { var handler = new MockHandler(); + handler.setProperty(List.of("plugin_name"), bytes("vm_plugin_configuration")); ProxyWasm.Builder builder = ProxyWasm.builder() .withPluginConfig("plugin_config") .withVmConfig("vm_config") - .withProperties(Map.of("plugin_name", "vm_plugin_configuration")) .withPluginHandler(handler); try (var ignored = builder.build(module)) { diff --git a/src/test/rust-examples/envoy_filter_metadata/Cargo.lock b/src/test/rust-examples/envoy_filter_metadata/Cargo.lock new file mode 100644 index 0000000..6012ca0 --- /dev/null +++ b/src/test/rust-examples/envoy_filter_metadata/Cargo.lock @@ -0,0 +1,127 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "log" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" + +[[package]] +name = "once_cell" +version = "1.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proxy-wasm" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14a5a4df5a1ab77235e36a0a0f638687ee1586d21ee9774037693001e94d4e11" +dependencies = [ + "hashbrown", + "log", +] + +[[package]] +name = "proxy-wasm-example-envoy-filter-metadata" +version = "0.0.1" +dependencies = [ + "proxy-wasm", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/src/test/rust-examples/envoy_filter_metadata/Cargo.toml b/src/test/rust-examples/envoy_filter_metadata/Cargo.toml new file mode 100644 index 0000000..0852ad3 --- /dev/null +++ b/src/test/rust-examples/envoy_filter_metadata/Cargo.toml @@ -0,0 +1,21 @@ +[package] +publish = false +name = "proxy-wasm-example-envoy-filter-metadata" +version = "0.0.1" +authors = ["Martijn Swaagma "] +description = "Proxy-Wasm plugin example: Envoy filter metadata" +license = "Apache-2.0" +edition = "2018" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +proxy-wasm = "0.2.2" + +[profile.release] +lto = true +opt-level = 3 +codegen-units = 1 +panic = "abort" +strip = "debuginfo" diff --git a/src/test/rust-examples/envoy_filter_metadata/README.md b/src/test/rust-examples/envoy_filter_metadata/README.md new file mode 100644 index 0000000..6dc4984 --- /dev/null +++ b/src/test/rust-examples/envoy_filter_metadata/README.md @@ -0,0 +1,5 @@ +## Attribution + +This example originally came from: +https://github.com/proxy-wasm/proxy-wasm-rust-sdk/tree/c8b2335df66a569a6306c58e346dd0cf9dbc0f3a/examples/envoy_filter_metadata +``` diff --git a/src/test/rust-examples/envoy_filter_metadata/main.wasm b/src/test/rust-examples/envoy_filter_metadata/main.wasm new file mode 100755 index 0000000000000000000000000000000000000000..0596707f248ca9b530b89a0c5068f814282780f8 GIT binary patch literal 168148 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 z Box { Box::new(MetadataHttp {}) }); +}} + +struct MetadataHttp {} + +impl Context for MetadataHttp {} + +impl HttpContext for MetadataHttp { + fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action { + // Read data set by the lua filter + match self.get_property(vec![ + "metadata", + "filter_metadata", + "envoy.filters.http.lua", + "uppercased-custom-metadata", + ]) { + Some(metadata) => match String::from_utf8(metadata) { + Ok(data) => { + self.send_http_response( + 200, + vec![("Powered-By", "proxy-wasm"), ("uppercased-metadata", &data)], + Some( + format!("Custom response with Envoy metadata: {:?}\n", data).as_bytes(), + ), + ); + Action::Pause + } + _ => Action::Continue, + }, + _ => Action::Continue, + } + } +}