From f4c6c31cf379929be0a16356062b2dbbe6386380 Mon Sep 17 00:00:00 2001 From: Hiram Chirino Date: Sun, 16 Mar 2025 15:17:01 -0400 Subject: [PATCH] Combined Imports/Exports into a single ABI class. group all methods by function in the order defined in the spec and Javadoc links to the spec for each method. Signed-off-by: Hiram Chirino --- .../proxywasm/impl/{Imports.java => ABI.java} | 1378 ++++++++++++----- .../io/roastedroot/proxywasm/impl/Common.java | 157 -- .../roastedroot/proxywasm/impl/Exports.java | 186 --- .../io/roastedroot/proxywasm/v1/Context.java | 13 +- .../io/roastedroot/proxywasm/v1/Handler.java | 4 + .../io/roastedroot/proxywasm/v1/Helpers.java | 37 + .../roastedroot/proxywasm/v1/HttpContext.java | 10 +- .../proxywasm/v1/NetworkContext.java | 8 +- .../roastedroot/proxywasm/v1/ProxyWasm.java | 70 +- 9 files changed, 1060 insertions(+), 803 deletions(-) rename src/main/java/io/roastedroot/proxywasm/impl/{Imports.java => ABI.java} (57%) delete mode 100644 src/main/java/io/roastedroot/proxywasm/impl/Common.java delete mode 100644 src/main/java/io/roastedroot/proxywasm/impl/Exports.java diff --git a/src/main/java/io/roastedroot/proxywasm/impl/Imports.java b/src/main/java/io/roastedroot/proxywasm/impl/ABI.java similarity index 57% rename from src/main/java/io/roastedroot/proxywasm/impl/Imports.java rename to src/main/java/io/roastedroot/proxywasm/impl/ABI.java index 1b14c91..7bd69da 100644 --- a/src/main/java/io/roastedroot/proxywasm/impl/Imports.java +++ b/src/main/java/io/roastedroot/proxywasm/impl/ABI.java @@ -1,11 +1,14 @@ package io.roastedroot.proxywasm.impl; +import static io.roastedroot.proxywasm.v1.Helpers.replaceBytes; import static io.roastedroot.proxywasm.v1.Helpers.string; import com.dylibso.chicory.experimental.hostmodule.annotations.HostModule; import com.dylibso.chicory.experimental.hostmodule.annotations.WasmExport; import com.dylibso.chicory.runtime.Instance; +import com.dylibso.chicory.runtime.Memory; import com.dylibso.chicory.runtime.WasmRuntimeException; +import com.dylibso.chicory.wasm.InvalidException; import io.roastedroot.proxywasm.v1.Action; import io.roastedroot.proxywasm.v1.BufferType; import io.roastedroot.proxywasm.v1.Handler; @@ -17,22 +20,15 @@ import io.roastedroot.proxywasm.v1.WasmException; import io.roastedroot.proxywasm.v1.WasmResult; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; @HostModule("env") -public class Imports extends Common { +public class ABI { private Handler handler; - Exports exports; - - public Exports getExports() { - return exports; - } - - public void setExports(Exports exports) { - this.exports = exports; - } + private Instance instance; public Handler getHandler() { return handler; @@ -42,15 +38,296 @@ public void setHandler(Handler handler) { this.handler = handler; } - @Override - int malloc(int length) throws WasmException { - return exports.malloc(length); + public void setInstance(Instance instance) { + this.instance = instance; + } + + public Instance.Exports exports() { + return instance.exports(); + } + + public Memory memory() { + return instance.memory(); + } + + // ////////////////////////////////////////////////////////////////////// + // Common Helpers + // ////////////////////////////////////////////////////////////////////// + + // Size of a 32-bit integer in bytes + static final int U32_LEN = 4; + + public boolean instanceExportsFunction(String name) { + try { + this.exports().function(name); + return true; + } catch (InvalidException e) { + return false; + } + } + + /** + * Write a 32-bit unsigned integer to memory(). + * + * @param address The address to write to + * @param value The value to write + * @throws WasmException if the memory access is invalid + */ + void putUint32(int address, int value) throws WasmException { + try { + memory().writeI32(address, value); + } catch (RuntimeException e) { + throw new WasmException(WasmResult.INVALID_MEMORY_ACCESS); + } + } + + /** + * Read a 32-bit unsigned integer to memory(). + * + * @param address The address to read from + * @throws WasmException if the memory access is invalid + */ + long getUint32(int address) throws WasmException { + try { + return memory().readU32(address); + } catch (RuntimeException e) { + throw new WasmException(WasmResult.INVALID_MEMORY_ACCESS); + } + } + + /** + * Write a byte to memory(). + * + * @param address The address to write to + * @param value The value to write + * @throws WasmException if the memory access is invalid + */ + void putByte(int address, byte value) throws WasmException { + try { + memory().writeByte(address, value); + } catch (RuntimeException e) { + throw new WasmException(WasmResult.INVALID_MEMORY_ACCESS); + } + } + + /** + * Write bytes to memory(). + * + * @param address The address to write to + * @param data The data to write + * @throws WasmException if the memory access is invalid + */ + void putMemory(int address, byte[] data) throws WasmException { + try { + // TODO: do we need a better writeU32 method? + memory().write(address, data); + } catch (RuntimeException e) { + throw new WasmException(WasmResult.INVALID_MEMORY_ACCESS); + } + } + + /** + * Write bytes to memory(). + * + * @param address The address to write to + * @param data The data to write + * @throws WasmException if the memory access is invalid + */ + void putMemory(int address, ByteBuffer data) throws WasmException { + try { + if (data.hasArray()) { + var array = data.array(); + memory().write(address, array, data.position(), data.remaining()); + } else { + // This could likely be optimized by extending the memory interface to accept + // ByteBuffer + byte[] bytes = new byte[data.remaining()]; + data.get(bytes); + memory().write(address, bytes); + } + } catch (RuntimeException e) { + throw new WasmException(WasmResult.INVALID_MEMORY_ACCESS); + } + } + + /** + * Read bytes from memory(). + * + * @param address The address to read from + * @param len The number of bytes to read + * @return The value read + * @throws WasmException if the memory access is invalid + */ + byte[] readMemory(int address, int len) throws WasmException { + try { + return memory().readBytes(address, len); + } catch (RuntimeException e) { + throw new WasmException(WasmResult.INVALID_MEMORY_ACCESS); + } + } + + String readString(int address, int len) throws WasmException { + var data = readMemory(address, len); + return new String(data, StandardCharsets.UTF_8); + } + + void copyIntoInstance(String value, int retPtr, int retSize) throws WasmException { + copyIntoInstance(value.getBytes(), retPtr, retSize); + } + + void copyIntoInstance(byte[] value, int retPtr, int retSize) throws WasmException { + try { + if (value.length != 0) { + int addr = malloc(value.length); + putMemory(addr, value); + putUint32(retPtr, addr); + } else { + putUint32(retPtr, 0); + } + putUint32(retSize, value.length); + } catch (WasmException e) { + throw new WasmException(WasmResult.INVALID_MEMORY_ACCESS); + } + } + + // ////////////////////////////////////////////////////////////////////// + // Integration + // ////////////////////////////////////////////////////////////////////// + + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#_initialize + */ + public void initialize() { + exports().function("_initialize").apply(); + } + + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#main + */ + public int main(int arg0, int arg1) { + long result = exports().function("main").apply(arg0, arg1)[0]; + return (int) result; + } + + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#_start + */ + public void start() { + exports().function("_start").apply(); + } + + // ////////////////////////////////////////////////////////////////////// + // Memory management + // ////////////////////////////////////////////////////////////////////// + + String mallocFunctionName = "malloc"; + + public String getMallocFunctionName() { + return mallocFunctionName; + } + + public void setMallocFunctionName(String mallocFunctionName) { + this.mallocFunctionName = mallocFunctionName; + } + + /** + * implements: + * * https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#malloc + * * https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_memory_allocate + */ + int malloc(int size) throws WasmException { + // I've noticed guests fail on malloc(0) so lets avoid that + assert size > 0 : "malloc size must be greater than zero"; + long ptr = exports().function(mallocFunctionName).apply(size)[0]; + if (ptr == 0) { + throw new WasmException(WasmResult.INVALID_MEMORY_ACCESS); + } + return (int) ptr; + } + + // ////////////////////////////////////////////////////////////////////// + // Context lifecycle + // ////////////////////////////////////////////////////////////////////// + + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_context_create + */ + public void proxyOnContextCreate(int contextID, int parentContextID) { + exports().function("proxy_on_context_create").apply(contextID, parentContextID); + } + + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_done + */ + public boolean proxyOnDone(int context_id) { + long result = exports().function("proxy_on_done").apply(context_id)[0]; + return result != 0; + } + + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_log + */ + public void proxyOnLog(int context_id) { + exports().function("proxy_on_log").apply(context_id); + } + + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_delete + */ + public void proxyOnDelete(int context_id) { + exports().function("proxy_on_delete").apply(context_id); + } + + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_done + */ + @WasmExport + int proxyDone() { + return handler.done().getValue(); } + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_set_effective_context + */ + @WasmExport + int proxySetEffectiveContext(int contextId) { + return handler.setEffectiveContextID(contextId).getValue(); + } + + // ////////////////////////////////////////////////////////////////////// + // Configuration + // ////////////////////////////////////////////////////////////////////// + + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_vm_start + */ + public boolean proxyOnVmStart(int arg0, int arg1) { + long result = exports().function("proxy_on_vm_start").apply(arg0, arg1)[0]; + return result != 0; + } + + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_configure + */ + public boolean proxyOnConfigure(int arg0, int arg1) { + long result = exports().function("proxy_on_configure").apply(arg0, arg1)[0]; + return result != 0; + } + + // ////////////////////////////////////////////////////////////////////// + // Logging + // ////////////////////////////////////////////////////////////////////// + + // wasi_snapshot_preview1.fd_write : + // https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#wasi_snapshot_preview1fd_write + + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_log + */ @WasmExport public int proxyLog(int logLevel, int messageData, int messageSize) { try { - var msg = instance.memory().readBytes(messageData, messageSize); + var msg = memory().readBytes(messageData, messageSize); handler.log(LogLevel.fromInt(logLevel), new String(msg)); return WasmResult.OK.getValue(); } catch (WasmException e) { @@ -59,83 +336,188 @@ public int proxyLog(int logLevel, int messageData, int messageSize) { } /** - * Get a header map based on the map type. - * - * @param instance The WebAssembly instance - * @param mapType The type of map to get - * @return The header map + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_get_log_level */ - public Map getMap(Instance instance, int mapType) { + @WasmExport + public int proxyGetLogLevel(int returnLogLevel) { + try { + LogLevel level = handler.getLogLevel(); + putUint32(returnLogLevel, level.value()); + return WasmResult.OK.getValue(); + } catch (WasmException e) { + return e.result().getValue(); + } + } - var knownType = MapType.fromInt(mapType); - if (knownType == null) { - return handler.getCustomHeaders(mapType); + // ////////////////////////////////////////////////////////////////////// + // Clocks + // ////////////////////////////////////////////////////////////////////// + + // wasi_snapshot_preview1.clock_time_get + + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_get_current_time_nanoseconds + */ + @WasmExport + int proxyGetCurrentTimeNanoseconds(int returnTime) { + try { + int currentTimeNanoseconds = handler.getCurrentTimeNanoseconds(); + putUint32(returnTime, currentTimeNanoseconds); + return WasmResult.OK.getValue(); + } catch (WasmException e) { + return e.result().getValue(); } + } - switch (knownType) { - case HTTP_REQUEST_HEADERS: - return handler.getHttpRequestHeaders(); - case HTTP_REQUEST_TRAILERS: - return handler.getHttpRequestTrailers(); - case HTTP_RESPONSE_HEADERS: - return handler.getHttpResponseHeaders(); - case HTTP_RESPONSE_TRAILERS: - return handler.getHttpResponseTrailers(); - case HTTP_CALL_RESPONSE_HEADERS: - return handler.getHttpCallResponseHeaders(); - case HTTP_CALL_RESPONSE_TRAILERS: - return handler.getHttpCallResponseTrailers(); - case GRPC_RECEIVE_INITIAL_METADATA: - return handler.getGrpcReceiveInitialMetaData(); - case GRPC_RECEIVE_TRAILING_METADATA: - return handler.getGrpcReceiveTrailerMetaData(); + // ////////////////////////////////////////////////////////////////////// + // Timers + // ////////////////////////////////////////////////////////////////////// + + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_set_tick_period_milliseconds + */ + @WasmExport + int proxySetTickPeriodMilliseconds(int tick_period) { + return handler.setTickPeriodMilliseconds(tick_period).getValue(); + } + + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_tick + */ + public void proxyOnTick(int arg0) { + exports().function("proxy_on_tick").apply(arg0); + } + + // ////////////////////////////////////////////////////////////////////// + // Randomness + // ////////////////////////////////////////////////////////////////////// + + // wasi_snapshot_preview1.random_get + + // ////////////////////////////////////////////////////////////////////// + // Environment variables + // ////////////////////////////////////////////////////////////////////// + + // wasi_snapshot_preview1.environ_sizes_get + // wasi_snapshot_preview1.environ_get + + // ////////////////////////////////////////////////////////////////////// + // Buffers + // ////////////////////////////////////////////////////////////////////// + + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_get_buffer_bytes + * + * @param bufferType The type of buffer to get + * @param start The start index in the buffer + * @param length The number of bytes to get + * @param returnBufferData Pointer to where the buffer data address should be stored + * @param returnBufferSize Pointer to where the buffer size should be stored + * @return WasmResult status code + */ + @WasmExport + public int proxyGetBufferBytes( + int bufferType, int start, int length, int returnBufferData, int returnBufferSize) { + + try { + // Get the buffer based on the buffer type + byte[] b = getBuffer(bufferType); + if (b == null || b.length == 0) { + return WasmResult.NOT_FOUND.getValue(); + } + + if (start > start + length) { + return WasmResult.BAD_ARGUMENT.getValue(); + } + + ByteBuffer buffer = ByteBuffer.wrap(b); + if (start + length > buffer.capacity()) { + length = buffer.capacity() - start; + } + + try { + buffer.position(start); + buffer.limit(start + length); + } catch (IllegalArgumentException e) { + return WasmResult.BAD_ARGUMENT.getValue(); + } + + // Allocate memory in the WebAssembly instance + int addr = malloc(length); + putMemory(addr, buffer); + // Write the address to the return pointer + putUint32(returnBufferData, addr); + // Write the length to the return size pointer + putUint32(returnBufferSize, length); + return WasmResult.OK.getValue(); + + } catch (WasmException e) { + return e.result().getValue(); } - return null; } /** - * Set a header map based on the map type. + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_set_buffer_bytes * - * @param instance The WebAssembly instance - * @param mapType The type of map to set - * @param map The header map to set - * @return WasmResult indicating success or failure + * @param bufferType The type of buffer to modify + * @param start The start index in the buffer + * @param length The number of bytes to set + * @param dataPtr Pointer to the data in WebAssembly memory + * @param dataSize Size of the data + * @return WasmResult status code */ - private WasmResult setMap(Instance instance, int mapType, Map map) { - var knownType = MapType.fromInt(mapType); - if (knownType == null) { - return handler.setCustomHeaders(mapType, map); + @WasmExport + public int proxySetBufferBytes( + int bufferType, int start, int length, int dataPtr, int dataSize) { + try { + + // Get the buffer based on the buffer type + var buf = getBuffer(bufferType); + if (buf == null) { + return WasmResult.NOT_FOUND.getValue(); + } + + // Get content from WebAssembly memory + byte[] content = memory().readBytes(dataPtr, dataSize); + + content = replaceBytes(buf, content, start, length); + + // Set the buffer using the appropriate handler method + WasmResult result = setBuffer(bufferType, content); + return result.getValue(); + + } catch (WasmRuntimeException e) { + return WasmResult.INVALID_MEMORY_ACCESS.getValue(); } + } - switch (knownType) { - case HTTP_REQUEST_HEADERS: - return handler.setHttpRequestHeaders(map); - case HTTP_REQUEST_TRAILERS: - return handler.setHttpRequestTrailers(map); - case HTTP_RESPONSE_HEADERS: - return handler.setHttpResponseHeaders(map); - case HTTP_RESPONSE_TRAILERS: - return handler.setHttpResponseTrailers(map); - case HTTP_CALL_RESPONSE_HEADERS: - return handler.setHttpCallResponseHeaders(map); - case HTTP_CALL_RESPONSE_TRAILERS: - return handler.setHttpCallResponseTrailers(map); - case GRPC_RECEIVE_INITIAL_METADATA: - return handler.setGrpcReceiveInitialMetaData(map); - case GRPC_RECEIVE_TRAILING_METADATA: - return handler.setGrpcReceiveTrailerMetaData(map); + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_get_buffer_status + */ + @WasmExport + public int proxyGetBufferStatus(int bufferType, int returnBufferSize, int returnUnused) { + try { + // Get the buffer based on the buffer type + byte[] b = getBuffer(bufferType); + if (b == null) { + return WasmResult.NOT_FOUND.getValue(); + } + + // Write the buffer size to the return size pointer + putUint32(returnBufferSize, b.length); + return WasmResult.OK.getValue(); + } catch (WasmException e) { + return e.result().getValue(); } - return WasmResult.NOT_FOUND; } /** * Get a buffer based on the buffer type. * - * @param instance The WebAssembly instance * @param bufferType The type of buffer to get * @return The buffer, or null if not found */ - private byte[] getBuffer(Instance instance, int bufferType) { + private byte[] getBuffer(int bufferType) { // Return the appropriate buffer based on the buffer type var knownType = BufferType.fromInt(bufferType); if (knownType == null) { @@ -167,12 +549,11 @@ private byte[] getBuffer(Instance instance, int bufferType) { /** * Set a buffer based on the buffer type. * - * @param instance The WebAssembly instance * @param bufferType The type of buffer to set * @param buffer The buffer to set * @return WasmResult indicating success or failure */ - private WasmResult setBuffer(Instance instance, int bufferType, byte[] buffer) { + private WasmResult setBuffer(int bufferType, byte[] buffer) { // Set the appropriate buffer based on the buffer type var knownType = BufferType.fromInt(bufferType); if (knownType == null) { @@ -199,8 +580,55 @@ private WasmResult setBuffer(Instance instance, int bufferType, byte[] buffer) { return WasmResult.NOT_FOUND; } + // ////////////////////////////////////////////////////////////////////// + // HTTP fields + // ////////////////////////////////////////////////////////////////////// + /** - * Get header map pairs and format them for WebAssembly memory. + * Retrieves serialized size of all key-value pairs from the map mapType + * + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_get_header_map_size + * + * @param mapType The type of map to set + * @param returnSize Pointer to ruturn the size of the map data + * @return WasmResult status code + */ + @WasmExport + public int proxyGetHeaderMapSize(int mapType, int returnSize) { + try { + + // Get the header map based on the map type + Map header = getMap(mapType); + if (header == null) { + return WasmResult.BAD_ARGUMENT.getValue(); + } + + // to clone the headers so that they don't change on while we process them in the loop + final Map cloneMap = new HashMap<>(); + int totalBytesLen = U32_LEN; // Start with space for the count + + for (Map.Entry entry : header.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + cloneMap.put(key, value); + totalBytesLen += U32_LEN + U32_LEN; // keyLen + valueLen + totalBytesLen += key.length() + 1 + value.length() + 1; // key + \0 + value + \0 + } + + // Write the total size to the return size pointer + putUint32(returnSize, totalBytesLen); + + return WasmResult.OK.getValue(); + + } catch (WasmException e) { + return e.result().getValue(); + } + } + + /** + * Get header map pairs and format them for WebAssembly memory(). + * + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_get_header_map_pairs * * @param mapType The type of map to get * @param returnDataPtr Pointer to where the data address should be stored @@ -212,7 +640,7 @@ public int proxyGetHeaderMapPairs(int mapType, int returnDataPtr, int returnData try { // Get the header map based on the map type - Map header = getMap(instance, mapType); + Map header = getMap(mapType); if (header == null) { return WasmResult.NOT_FOUND.getValue(); } @@ -283,7 +711,9 @@ public int proxyGetHeaderMapPairs(int mapType, int returnDataPtr, int returnData } /** - * Set header map pairs from WebAssembly memory. + * Set header map pairs from WebAssembly memory(). + * + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_set_header_map_pairs * * @param mapType The type of map to set * @param ptr Pointer to the map data in WebAssembly memory @@ -295,7 +725,7 @@ public int proxySetHeaderMapPairs(int mapType, int ptr, int size) { try { // Get the header map based on the map type - Map headerMap = getMap(instance, mapType); + Map headerMap = getMap(mapType); if (headerMap == null) { return WasmResult.BAD_ARGUMENT.getValue(); } @@ -314,47 +744,9 @@ public int proxySetHeaderMapPairs(int mapType, int ptr, int size) { } } - /** - * Retrieves serialized size of all key-value pairs from the map mapType - * - * @param mapType The type of map to set - * @param returnSize Pointer to ruturn the size of the map data - * @return WasmResult status code - */ - @WasmExport - public int proxyGetHeaderMapSize(int mapType, int returnSize) { - try { - - // Get the header map based on the map type - Map header = getMap(instance, mapType); - if (header == null) { - return WasmResult.BAD_ARGUMENT.getValue(); - } - - // to clone the headers so that they don't change on while we process them in the loop - final Map cloneMap = new HashMap<>(); - int totalBytesLen = U32_LEN; // Start with space for the count - - for (Map.Entry entry : header.entrySet()) { - String key = entry.getKey(); - String value = entry.getValue(); - cloneMap.put(key, value); - totalBytesLen += U32_LEN + U32_LEN; // keyLen + valueLen - totalBytesLen += key.length() + 1 + value.length() + 1; // key + \0 + value + \0 - } - - // Write the total size to the return size pointer - putUint32(returnSize, totalBytesLen); - - return WasmResult.OK.getValue(); - - } catch (WasmException e) { - return e.result().getValue(); - } - } - /** * Get a value from a header map by key. + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_get_header_map_value * * @param mapType The type of map to get from * @param keyDataPtr Pointer to the key data in WebAssembly memory @@ -368,7 +760,7 @@ public int proxyGetHeaderMapValue( int mapType, int keyDataPtr, int keySize, int valueDataPtr, int valueSize) { try { // Get the header map based on the map type - Map headerMap = getMap(instance, mapType); + Map headerMap = getMap(mapType); if (headerMap == null) { return WasmResult.BAD_ARGUMENT.getValue(); } @@ -394,7 +786,8 @@ public int proxyGetHeaderMapValue( } /** - * Replace a value in a header map. + * Add a value to a header map. + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_add_header_map_value * * @param mapType The type of map to modify * @param keyDataPtr Pointer to the key data in WebAssembly memory @@ -404,37 +797,14 @@ public int proxyGetHeaderMapValue( * @return WasmResult status code */ @WasmExport - public int proxyReplaceHeaderMapValue( + public int proxyAddHeaderMapValue( int mapType, int keyDataPtr, int keySize, int valueDataPtr, int valueSize) { - try { - // Get the header map based on the map type - Map headerMap = getMap(instance, mapType); - if (headerMap == null) { - return WasmResult.BAD_ARGUMENT.getValue(); - } - - // Get key from memory - String key = readString(keyDataPtr, keySize); - - // Get value from memory - String value = readString(valueDataPtr, valueSize); - - // Replace value in map - var copy = new HashMap<>(headerMap); - copy.put(key, value); - setMap(instance, mapType, copy); - - return WasmResult.OK.getValue(); - - } catch (WasmRuntimeException e) { - return WasmResult.INVALID_MEMORY_ACCESS.getValue(); - } catch (WasmException e) { - return e.result().getValue(); - } + return proxyReplaceHeaderMapValue(mapType, keyDataPtr, keySize, valueDataPtr, valueSize); } /** - * Add a value to a header map. + * Replace a value in a header map. + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_replace_header_map_value * * @param mapType The type of map to modify * @param keyDataPtr Pointer to the key data in WebAssembly memory @@ -444,13 +814,38 @@ public int proxyReplaceHeaderMapValue( * @return WasmResult status code */ @WasmExport - public int proxyAddHeaderMapValue( + public int proxyReplaceHeaderMapValue( int mapType, int keyDataPtr, int keySize, int valueDataPtr, int valueSize) { - return proxyReplaceHeaderMapValue(mapType, keyDataPtr, keySize, valueDataPtr, valueSize); + try { + // Get the header map based on the map type + Map headerMap = getMap(mapType); + if (headerMap == null) { + return WasmResult.BAD_ARGUMENT.getValue(); + } + + // Get key from memory + String key = readString(keyDataPtr, keySize); + + // Get value from memory + String value = readString(valueDataPtr, valueSize); + + // Replace value in map + var copy = new HashMap<>(headerMap); + copy.put(key, value); + setMap(mapType, copy); + + return WasmResult.OK.getValue(); + + } catch (WasmRuntimeException e) { + return WasmResult.INVALID_MEMORY_ACCESS.getValue(); + } catch (WasmException e) { + return e.result().getValue(); + } } /** * Remove a value from a header map. + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_remove_header_map_value * * @param mapType The type of map to modify * @param keyDataPtr Pointer to the key data in WebAssembly memory @@ -461,7 +856,7 @@ public int proxyAddHeaderMapValue( public int proxyRemoveHeaderMapValue(int mapType, int keyDataPtr, int keySize) { try { // Get the header map based on the map type - Map headerMap = getMap(instance, mapType); + Map headerMap = getMap(mapType); if (headerMap == null) { return WasmResult.NOT_FOUND.getValue(); } @@ -475,7 +870,7 @@ public int proxyRemoveHeaderMapValue(int mapType, int keyDataPtr, int keySize) { // Remove key from map var copy = new HashMap<>(headerMap); copy.remove(key); - setMap(instance, mapType, copy); + setMap(mapType, copy); return WasmResult.OK.getValue(); @@ -486,6 +881,74 @@ public int proxyRemoveHeaderMapValue(int mapType, int keyDataPtr, int keySize) { } } + /** + * Get a header map based on the map type. + * + * @param mapType The type of map to get + * @return The header map + */ + private Map getMap(int mapType) { + + var knownType = MapType.fromInt(mapType); + if (knownType == null) { + return handler.getCustomHeaders(mapType); + } + + switch (knownType) { + case HTTP_REQUEST_HEADERS: + return handler.getHttpRequestHeaders(); + case HTTP_REQUEST_TRAILERS: + return handler.getHttpRequestTrailers(); + case HTTP_RESPONSE_HEADERS: + return handler.getHttpResponseHeaders(); + case HTTP_RESPONSE_TRAILERS: + return handler.getHttpResponseTrailers(); + case HTTP_CALL_RESPONSE_HEADERS: + return handler.getHttpCallResponseHeaders(); + case HTTP_CALL_RESPONSE_TRAILERS: + return handler.getHttpCallResponseTrailers(); + case GRPC_RECEIVE_INITIAL_METADATA: + return handler.getGrpcReceiveInitialMetaData(); + case GRPC_RECEIVE_TRAILING_METADATA: + return handler.getGrpcReceiveTrailerMetaData(); + } + return null; + } + + /** + * Set a header map based on the map type. + * + * @param mapType The type of map to set + * @param map The header map to set + * @return WasmResult indicating success or failure + */ + private WasmResult setMap(int mapType, Map map) { + var knownType = MapType.fromInt(mapType); + if (knownType == null) { + return handler.setCustomHeaders(mapType, map); + } + + switch (knownType) { + case HTTP_REQUEST_HEADERS: + return handler.setHttpRequestHeaders(map); + case HTTP_REQUEST_TRAILERS: + return handler.setHttpRequestTrailers(map); + case HTTP_RESPONSE_HEADERS: + return handler.setHttpResponseHeaders(map); + case HTTP_RESPONSE_TRAILERS: + return handler.setHttpResponseTrailers(map); + case HTTP_CALL_RESPONSE_HEADERS: + return handler.setHttpCallResponseHeaders(map); + case HTTP_CALL_RESPONSE_TRAILERS: + return handler.setHttpCallResponseTrailers(map); + case GRPC_RECEIVE_INITIAL_METADATA: + return handler.setGrpcReceiveInitialMetaData(map); + case GRPC_RECEIVE_TRAILING_METADATA: + return handler.setGrpcReceiveTrailerMetaData(map); + } + return WasmResult.NOT_FOUND; + } + /** * Decodes a byte array containing map data into a Map of String key-value pairs. *

@@ -552,156 +1015,133 @@ private HashMap decodeMap(int addr, int mem_size) throws WasmExc return result; } - @WasmExport - public int proxyGetProperty(int keyPtr, int keySize, int returnValueData, int returnValueSize) { - try { - // Get key from memory - byte[] keyBytes = readMemory(keyPtr, keySize); - if (keyBytes.length == 0) { - return WasmResult.BAD_ARGUMENT.getValue(); - } - - String key = new String(keyBytes); - - // Get property value using handler - String value = handler.getProperty(key); - if (value == null) { - return WasmResult.NOT_FOUND.getValue(); - } - - copyIntoInstance(value, returnValueData, returnValueSize); - return WasmResult.OK.getValue(); - - } catch (WasmException e) { - return e.result().getValue(); - } - } + // ////////////////////////////////////////////////////////////////////// + // Common HTTP and TCP stream operations + // ////////////////////////////////////////////////////////////////////// /** - * Get bytes from a buffer and make them available to WebAssembly. - * - * @param bufferType The type of buffer to get - * @param start The start index in the buffer - * @param length The number of bytes to get - * @param returnBufferData Pointer to where the buffer data address should be stored - * @param returnBufferSize Pointer to where the buffer size should be stored - * @return WasmResult status code + * Resumes processing of paused stream_type. + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/v0.2.0#proxy_continue_stream */ @WasmExport - public int proxyGetBufferBytes( - int bufferType, int start, int length, int returnBufferData, int returnBufferSize) { - - try { - // Get the buffer based on the buffer type - byte[] b = getBuffer(instance, bufferType); - if (b == null || b.length == 0) { - return WasmResult.NOT_FOUND.getValue(); - } - - if (start > start + length) { - return WasmResult.BAD_ARGUMENT.getValue(); - } - - ByteBuffer buffer = ByteBuffer.wrap(b); - if (start + length > buffer.capacity()) { - length = buffer.capacity() - start; - } + int proxyContinueStream(int arg) { + var streamType = StreamType.fromInt(arg); + if (streamType == null) { + return WasmResult.BAD_ARGUMENT.getValue(); + } + WasmResult result = handler.setAction(streamType, Action.CONTINUE); + return result.getValue(); + } - try { - buffer.position(start); - buffer.limit(start + length); - } catch (IllegalArgumentException e) { - return WasmResult.BAD_ARGUMENT.getValue(); - } + // TODO: implement + // https://github.com/proxy-wasm/spec/tree/main/abi-versions/v0.2.1#proxy_close_stream + // TODO: implement + // https://github.com/proxy-wasm/spec/tree/main/abi-versions/v0.2.1#proxy_get_status - // Allocate memory in the WebAssembly instance - int addr = malloc(length); - putMemory(addr, buffer); - // Write the address to the return pointer - putUint32(returnBufferData, addr); - // Write the length to the return size pointer - putUint32(returnBufferSize, length); - return WasmResult.OK.getValue(); + // ////////////////////////////////////////////////////////////////////// + // TCP streams + // ////////////////////////////////////////////////////////////////////// - } catch (WasmException e) { - return e.result().getValue(); - } + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_new_connection + */ + public int proxyOnNewConnection(int arg0) { + long result = exports().function("proxy_on_new_connection").apply(arg0)[0]; + return (int) result; } /** - * Set bytes in a buffer. - * - * @param bufferType The type of buffer to modify - * @param start The start index in the buffer - * @param length The number of bytes to set - * @param dataPtr Pointer to the data in WebAssembly memory - * @param dataSize Size of the data - * @return WasmResult status code + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_downstream_data */ - @WasmExport - public int proxySetBufferBytes( - int bufferType, int start, int length, int dataPtr, int dataSize) { - try { - - // Get the buffer based on the buffer type - var buf = getBuffer(instance, bufferType); - if (buf == null) { - return WasmResult.NOT_FOUND.getValue(); - } - - // Get content from WebAssembly memory - byte[] content = instance.memory().readBytes(dataPtr, dataSize); - - content = replaceBytes(buf, content, start, length); + public int proxyOnDownstreamData(int contextId, int dataSize, int endOfStream) { + long result = + exports() + .function("proxy_on_downstream_data") + .apply(contextId, dataSize, endOfStream)[0]; + return (int) result; + } - // Set the buffer using the appropriate handler method - WasmResult result = setBuffer(instance, bufferType, content); - return result.getValue(); + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_downstream_connection_close + */ + public void proxyOnDownstreamConnectionClose(int arg0, int arg1) { + exports().function("proxy_on_downstream_connection_close").apply(arg0, arg1); + } - } catch (WasmRuntimeException e) { - return WasmResult.INVALID_MEMORY_ACCESS.getValue(); - } + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_upstream_data + */ + public int proxyOnUpstreamData(int arg0, int arg1, int arg2) { + long result = exports().function("proxy_on_upstream_data").apply(arg0, arg1, arg2)[0]; + return (int) result; } - public static byte[] replaceBytes( - byte[] existing, byte[] change, int replaceStart, int replaceLength) { + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_upstream_connection_close + */ + public void proxyOnUpstreamConnectionClose(int arg0, int arg1) { + exports().function("proxy_on_upstream_connection_close").apply(arg0, arg1); + } - if (replaceStart > existing.length) { - replaceStart = existing.length; - } - if (replaceLength > existing.length) { - replaceLength = existing.length; - } + // ////////////////////////////////////////////////////////////////////// + // HTTP streams + // ////////////////////////////////////////////////////////////////////// - // when we are replacing the whole buffer - if (replaceStart == 0 && replaceLength == existing.length) { - return change; - } + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_request_headers + */ + public int proxyOnRequestHeaders(int contextID, int headers, int endOfStream) { + long result = + exports() + .function("proxy_on_request_headers") + .apply(contextID, headers, endOfStream)[0]; + return (int) result; + } - int newLength = change.length + (existing.length - replaceLength); - byte[] result = new byte[newLength]; + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_request_body + */ + public int proxyOnRequestBody(int contextId, int bodySize, int arg2) { + long result = + exports().function("proxy_on_request_body").apply(contextId, bodySize, arg2)[0]; + return (int) result; + } - // Copy the unchanged part before the start position - System.arraycopy(existing, 0, result, 0, Math.min(replaceStart, existing.length)); + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_request_trailers + */ + public int proxyOnRequestTrailers(int arg0, int arg1) { + long result = exports().function("proxy_on_request_trailers").apply(arg0, arg1)[0]; + return (int) result; + } - // Copy the new change bytes - System.arraycopy(change, 0, result, replaceStart, change.length); + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_response_headers + */ + public int proxyOnResponseHeaders(int arg0, int arg1, int arg2) { + long result = exports().function("proxy_on_response_headers").apply(arg0, arg1, arg2)[0]; + return (int) result; + } - // Copy the remaining unchanged part after replacement - if (replaceStart + replaceLength < existing.length) { - System.arraycopy( - existing, - replaceStart + replaceLength, - result, - replaceStart + change.length, - existing.length - (replaceStart + replaceLength)); - } + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_response_body + */ + public int proxyOnResponseBody(int arg0, int arg1, int arg2) { + long result = exports().function("proxy_on_response_body").apply(arg0, arg1, arg2)[0]; + return (int) result; + } - return result; + /** + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_response_trailers + */ + public int proxyOnResponseTrailers(int arg0, int arg1) { + long result = exports().function("proxy_on_response_trailers").apply(arg0, arg1)[0]; + return (int) result; } /** * Send an HTTP response. + * implements: https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_send_local_response * * @param responseCode The HTTP response code * @param responseCodeDetailsData Pointer to response code details in WebAssembly memory @@ -729,14 +1169,13 @@ public int proxySendLocalResponse( byte[] responseCodeDetails = null; if (responseCodeDetailsSize > 0) { responseCodeDetails = - instance.memory() - .readBytes(responseCodeDetailsData, responseCodeDetailsSize); + memory().readBytes(responseCodeDetailsData, responseCodeDetailsSize); } // Get response body from memory byte[] responseBody = new byte[0]; if (responseBodySize > 0) { - responseBody = instance.memory().readBytes(responseBodyData, responseBodySize); + responseBody = memory().readBytes(responseBodyData, responseBodySize); } // Get and decode additional headers from memory @@ -751,86 +1190,61 @@ public int proxySendLocalResponse( responseBody, additionalHeaders, grpcStatus); - return result.getValue(); - - } catch (WasmRuntimeException e) { - return WasmResult.INVALID_MEMORY_ACCESS.getValue(); - } catch (WasmException e) { - return e.result().getValue(); - } - } - - @WasmExport - int proxySetEffectiveContext(int contextId) { - return handler.setEffectiveContextID(contextId).getValue(); - } - - @WasmExport - int proxyDone() { - return handler.done().getValue(); - } - - @WasmExport - int proxySetTickPeriodMilliseconds(int tick_period) { - return handler.setTickPeriodMilliseconds(tick_period).getValue(); - } - - @WasmExport - int proxyGetCurrentTimeNanoseconds(int returnTime) { - try { - int currentTimeNanoseconds = handler.getCurrentTimeNanoseconds(); - putUint32(returnTime, currentTimeNanoseconds); - return WasmResult.OK.getValue(); - } catch (WasmException e) { - return e.result().getValue(); - } - } - - /** - * 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(); + return result.getValue(); + + } catch (WasmRuntimeException e) { + return WasmResult.INVALID_MEMORY_ACCESS.getValue(); + } catch (WasmException e) { + return e.result().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 + * + * @deprecated in 0.2.0 */ @WasmExport + @Deprecated(since = "0.2.0") 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 + * + * @deprecated in 0.2.0 */ @WasmExport + @Deprecated(since = "0.2.0") 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 + * + * @deprecated in 0.2.0 */ @WasmExport + @Deprecated(since = "0.2.0") void proxyClearRouteCache() { handler.clearRouteCache(); } + // ////////////////////////////////////////////////////////////////////// + // HTTP calls + // ////////////////////////////////////////////////////////////////////// + + /** + * implements https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_http_call + */ @WasmExport int proxyHttpCall( int uriData, @@ -860,6 +1274,9 @@ int proxyHttpCall( } } + /** + * implements https://github.com/api7/wasm-nginx-module/blob/main/proxy_wasm_abi.md#proxy_dispatch_http_call + */ @WasmExport int proxyDispatchHttpCall( int upstreamNameData, @@ -891,79 +1308,86 @@ int proxyDispatchHttpCall( } } - @WasmExport - int proxyCallForeignFunction( - int nameDataPtr, - int nameSize, - int argumentDataPtr, - int argumentSize, - int returnResultsPtr, - int returnResultsSizePtr) { - try { - var name = string(readMemory(nameDataPtr, nameSize)); - var argument = readMemory(argumentDataPtr, argumentSize); - var result = handler.callForeignFunction(name, argument); - - // Allocate memory in the WebAssembly instance - int addr = malloc(result.length); - putMemory(addr, result); - // Write the address to the return pointer - putUint32(returnResultsPtr, addr); - // Write the length to the return size pointer - putUint32(returnResultsSizePtr, result.length); - return WasmResult.OK.getValue(); - - } catch (WasmException e) { - return e.result().getValue(); - } + /** + * implements https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_http_call_response + */ + public void proxyOnHttpCallResponse(int arg0, int arg1, int arg2, int arg3, int arg4) { + exports().function("proxy_on_http_call_response").apply(arg0, arg1, arg2, arg3, arg4); } - @WasmExport - int proxyDefineMetric(int metricType, int nameDataPtr, int nameSize, int returnMetricId) { - try { - MetricType type = MetricType.fromInt(metricType); - if (type == null) { - return WasmResult.BAD_ARGUMENT.getValue(); - } + // ////////////////////////////////////////////////////////////////////// + // gRPC calls + // ////////////////////////////////////////////////////////////////////// + + // TODO: implement + // https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_grpc_call + // TODO: implement + // https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_grpc_stream + // TODO: implement + // https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_grpc_send + // TODO: implement + // https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_grpc_cancel + // TODO: implement + // https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_grpc_close - var name = string(readMemory(nameDataPtr, nameSize)); - int metricId = handler.defineMetric(type, name); - putUint32(returnMetricId, metricId); - return WasmResult.OK.getValue(); - } catch (WasmException e) { - return e.result().getValue(); - } + /** + * implements https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_grpc_receive_initial_metadata + */ + public void proxyOnGrpcReceiveInitialMetadata(int contextId, int callId, int numElements) { + exports() + .function("proxy_on_grpc_receive_initial_metadata") + .apply(contextId, callId, numElements); } - @WasmExport - int proxyRecordMetric(int metricId, long value) { - WasmResult result = handler.recordMetric(metricId, value); - return result.getValue(); + /** + * implements https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_grpc_receive + */ + public void proxyOnGrpcReceive(int contextId, int callId, int messageSize) { + exports().function("proxy_on_grpc_receive").apply(contextId, callId, messageSize); } - @WasmExport - int proxyRemoveMetric(int metricId) { - WasmResult result = handler.removeMetric(metricId); - return result.getValue(); + /** + * implements https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_grpc_receive_trailing_metadata + */ + public void proxyOnGrpcReceiveTrailingMetadata(int arg0, int arg1, int arg2) { + exports().function("proxy_on_grpc_receive_trailing_metadata").apply(arg0, arg1, arg2); } - @WasmExport - int proxyIncrementMetric(int metricId, long value) { - WasmResult result = handler.incrementMetric(metricId, value); - return result.getValue(); + /** + * implements https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_grpc_close + */ + public void proxyOnGrpcClose(int arg0, int arg1, int arg2) { + exports().function("proxy_on_grpc_close").apply(arg0, arg1, arg2); } + // ////////////////////////////////////////////////////////////////////// + // Shared Key-Value Store + // ////////////////////////////////////////////////////////////////////// + + /** + * implements https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_set_shared_data + */ @WasmExport - int proxyGetMetric(int metricId, int returnValuePtr) { + int proxySetSharedData(int keyDataPtr, int keySize, int valueDataPtr, int valueSize, int cas) { try { - var result = handler.getMetric(metricId); - putUint32(returnValuePtr, (int) result); - return WasmResult.OK.getValue(); + // Get key from memory + String key = string(readMemory(keyDataPtr, keySize)); + + // Get value from memory + byte[] value = readMemory(valueDataPtr, valueSize); + + // Set shared data value using handler + WasmResult result = handler.setSharedData(key, value, cas); + return result.getValue(); + } catch (WasmException e) { return e.result().getValue(); } } + /** + * implements https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_get_shared_data + */ @WasmExport int proxyGetSharedData( int keyDataPtr, int keySize, int returnValueData, int returnValueSize, int returnCas) { @@ -986,24 +1410,13 @@ int proxyGetSharedData( } } - @WasmExport - int proxySetSharedData(int keyDataPtr, int keySize, int valueDataPtr, int valueSize, int cas) { - try { - // Get key from memory - String key = string(readMemory(keyDataPtr, keySize)); - - // Get value from memory - byte[] value = readMemory(valueDataPtr, valueSize); - - // Set shared data value using handler - WasmResult result = handler.setSharedData(key, value, cas); - return result.getValue(); - - } catch (WasmException e) { - return e.result().getValue(); - } - } + // ////////////////////////////////////////////////////////////////////// + // Shared Queues + // ////////////////////////////////////////////////////////////////////// + /** + * implements https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_register_shared_queue + */ @WasmExport int proxyRegisterSharedQueue(int queueNameDataPtr, int queueNameSize, int returnQueueId) { try { @@ -1022,6 +1435,9 @@ int proxyRegisterSharedQueue(int queueNameDataPtr, int queueNameSize, int return } } + /** + * implements https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_resolve_shared_queue + */ @WasmExport int proxyResolveSharedQueue( int vmIdDataPtr, @@ -1045,6 +1461,9 @@ int proxyResolveSharedQueue( } } + /** + * implements https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_enqueue_shared_queue + */ @WasmExport int proxyEnqueueSharedQueue(int queueId, int valueDataPtr, int valueSize) { try { @@ -1060,6 +1479,9 @@ int proxyEnqueueSharedQueue(int queueId, int valueDataPtr, int valueSize) { } } + /** + * implements https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_dequeue_shared_queue + */ @WasmExport int proxyDequeueSharedQueue(int queueId, int returnValueData, int returnValueSize) { try { @@ -1076,4 +1498,166 @@ int proxyDequeueSharedQueue(int queueId, int returnValueData, int returnValueSiz return e.result().getValue(); } } + + /** + * implements https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_on_queue_ready + */ + public void proxyOnQueueReady(int arg0, int arg1) { + exports().function("proxy_on_queue_ready").apply(arg0, arg1); + } + + // ////////////////////////////////////////////////////////////////////// + // Metrics + // ////////////////////////////////////////////////////////////////////// + + /** + * implements https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_define_metric + */ + @WasmExport + int proxyDefineMetric(int metricType, int nameDataPtr, int nameSize, int returnMetricId) { + try { + MetricType type = MetricType.fromInt(metricType); + if (type == null) { + return WasmResult.BAD_ARGUMENT.getValue(); + } + + var name = string(readMemory(nameDataPtr, nameSize)); + int metricId = handler.defineMetric(type, name); + putUint32(returnMetricId, metricId); + return WasmResult.OK.getValue(); + } catch (WasmException e) { + return e.result().getValue(); + } + } + + /** + * implements https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_record_metric + */ + @WasmExport + int proxyRecordMetric(int metricId, long value) { + WasmResult result = handler.recordMetric(metricId, value); + return result.getValue(); + } + + /** + * implements https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_increment_metric + */ + @WasmExport + int proxyIncrementMetric(int metricId, long value) { + WasmResult result = handler.incrementMetric(metricId, value); + return result.getValue(); + } + + /** + * implements https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_get_metric + */ + @WasmExport + int proxyGetMetric(int metricId, int returnValuePtr) { + try { + var result = handler.getMetric(metricId); + putUint32(returnValuePtr, (int) result); + return WasmResult.OK.getValue(); + } catch (WasmException e) { + return e.result().getValue(); + } + } + + /** + * Remove a defined metric. + *

+ * Not defined in the spec, but seems useful to have. + */ + @WasmExport + int proxyRemoveMetric(int metricId) { + WasmResult result = handler.removeMetric(metricId); + return result.getValue(); + } + + // ////////////////////////////////////////////////////////////////////// + // Properties + // ////////////////////////////////////////////////////////////////////// + + /** + * implements https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_get_property + */ + @WasmExport + public int proxyGetProperty(int keyPtr, int keySize, int returnValueData, int returnValueSize) { + try { + // Get key from memory + byte[] keyBytes = readMemory(keyPtr, keySize); + if (keyBytes.length == 0) { + return WasmResult.BAD_ARGUMENT.getValue(); + } + + String key = new String(keyBytes); + + // Get property value using handler + String value = handler.getProperty(key); + if (value == null) { + return WasmResult.NOT_FOUND.getValue(); + } + + copyIntoInstance(value, returnValueData, returnValueSize); + return WasmResult.OK.getValue(); + + } catch (WasmException e) { + return e.result().getValue(); + } + } + + // TODO: implement + // https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_set_property + + // ////////////////////////////////////////////////////////////////////// + // Foreign function interface (FFI) + // ////////////////////////////////////////////////////////////////////// + + /** + * implements https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_call_foreign_function + */ + @WasmExport + int proxyCallForeignFunction( + int nameDataPtr, + int nameSize, + int argumentDataPtr, + int argumentSize, + int returnResultsPtr, + int returnResultsSizePtr) { + try { + var name = string(readMemory(nameDataPtr, nameSize)); + var argument = readMemory(argumentDataPtr, argumentSize); + var result = handler.callForeignFunction(name, argument); + + // Allocate memory in the WebAssembly instance + int addr = malloc(result.length); + putMemory(addr, result); + // Write the address to the return pointer + putUint32(returnResultsPtr, addr); + // Write the length to the return size pointer + putUint32(returnResultsSizePtr, result.length); + return WasmResult.OK.getValue(); + + } catch (WasmException e) { + return e.result().getValue(); + } + } + + /** + * implements https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#proxy_call_foreign_function + */ + public void proxyOnForeignFunction(int contextId, int functionId, int argumentsSize) { + exports().function("proxy_on_foreign_function").apply(contextId, functionId, argumentsSize); + } + + // ////////////////////////////////////////////////////////////////////// + // Unimplemented WASI functions + // ////////////////////////////////////////////////////////////////////// + + // wasi_snapshot_preview1.args_sizes_get : + // https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#wasi_snapshot_preview1args_sizes_get + // wasi_snapshot_preview1.args_get : + // https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#wasi_snapshot_preview1args_get + // wasi_snapshot_preview1.proc_exit : + // https://github.com/proxy-wasm/spec/tree/main/abi-versions/vNEXT#wasi_snapshot_preview1proc_exit + } diff --git a/src/main/java/io/roastedroot/proxywasm/impl/Common.java b/src/main/java/io/roastedroot/proxywasm/impl/Common.java deleted file mode 100644 index ae9b4d5..0000000 --- a/src/main/java/io/roastedroot/proxywasm/impl/Common.java +++ /dev/null @@ -1,157 +0,0 @@ -package io.roastedroot.proxywasm.impl; - -import com.dylibso.chicory.runtime.Instance; -import io.roastedroot.proxywasm.v1.WasmException; -import io.roastedroot.proxywasm.v1.WasmResult; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; - -/** - * Common methods used to implement the Wasm modules. - *

- * Some of it may seem not-java idomatic, that's because it's being used to translate - * from a go version - *

- * Once the translation is done, we can refactor it to be more idiomatic. - */ -public abstract class Common { - - // Size of a 32-bit integer in bytes - static final int U32_LEN = 4; - - Instance instance; - - public Instance getInstance() { - return this.instance; - } - - public void setInstance(Instance instance) { - this.instance = instance; - } - - /** - * Write a 32-bit unsigned integer to memory. - * - * @param address The address to write to - * @param value The value to write - * @throws WasmException if the memory access is invalid - */ - void putUint32(int address, int value) throws WasmException { - try { - instance.memory().writeI32(address, value); - } catch (RuntimeException e) { - throw new WasmException(WasmResult.INVALID_MEMORY_ACCESS); - } - } - - /** - * Read a 32-bit unsigned integer to memory. - * - * @param address The address to read from - * @throws WasmException if the memory access is invalid - */ - long getUint32(int address) throws WasmException { - try { - return instance.memory().readU32(address); - } catch (RuntimeException e) { - throw new WasmException(WasmResult.INVALID_MEMORY_ACCESS); - } - } - - /** - * Write a byte to memory. - * - * @param address The address to write to - * @param value The value to write - * @throws WasmException if the memory access is invalid - */ - void putByte(int address, byte value) throws WasmException { - try { - instance.memory().writeByte(address, value); - } catch (RuntimeException e) { - throw new WasmException(WasmResult.INVALID_MEMORY_ACCESS); - } - } - - /** - * Write bytes to memory. - * - * @param address The address to write to - * @param data The data to write - * @throws WasmException if the memory access is invalid - */ - void putMemory(int address, byte[] data) throws WasmException { - try { - // TODO: do we need a better writeU32 method? - instance.memory().write(address, data); - } catch (RuntimeException e) { - throw new WasmException(WasmResult.INVALID_MEMORY_ACCESS); - } - } - - /** - * Write bytes to memory. - * - * @param address The address to write to - * @param data The data to write - * @throws WasmException if the memory access is invalid - */ - void putMemory(int address, ByteBuffer data) throws WasmException { - try { - if (data.hasArray()) { - var array = data.array(); - instance.memory().write(address, array, data.position(), data.remaining()); - } else { - // This could likely be optimized by extending the memory interface to accept - // ByteBuffer - byte[] bytes = new byte[data.remaining()]; - data.get(bytes); - instance.memory().write(address, bytes); - } - } catch (RuntimeException e) { - throw new WasmException(WasmResult.INVALID_MEMORY_ACCESS); - } - } - - /** - * Read bytes from memory. - * - * @param address The address to read from - * @param len The number of bytes to read - * @return The value read - * @throws WasmException if the memory access is invalid - */ - byte[] readMemory(int address, int len) throws WasmException { - try { - return instance.memory().readBytes(address, len); - } catch (RuntimeException e) { - throw new WasmException(WasmResult.INVALID_MEMORY_ACCESS); - } - } - - String readString(int address, int len) throws WasmException { - var data = readMemory(address, len); - return new String(data, StandardCharsets.UTF_8); - } - - void copyIntoInstance(String value, int retPtr, int retSize) throws WasmException { - copyIntoInstance(value.getBytes(), retPtr, retSize); - } - - void copyIntoInstance(byte[] value, int retPtr, int retSize) throws WasmException { - try { - if (value.length != 0) { - int addr = malloc(value.length); - putMemory(addr, value); - putUint32(retPtr, addr); - } else { - putUint32(retPtr, 0); - } - putUint32(retSize, value.length); - } catch (WasmException e) { - throw new WasmException(WasmResult.INVALID_MEMORY_ACCESS); - } - } - - abstract int malloc(int length) throws WasmException; -} diff --git a/src/main/java/io/roastedroot/proxywasm/impl/Exports.java b/src/main/java/io/roastedroot/proxywasm/impl/Exports.java deleted file mode 100644 index bb4c9c3..0000000 --- a/src/main/java/io/roastedroot/proxywasm/impl/Exports.java +++ /dev/null @@ -1,186 +0,0 @@ -package io.roastedroot.proxywasm.impl; - -import com.dylibso.chicory.runtime.Instance; -import io.roastedroot.proxywasm.v1.WasmException; -import io.roastedroot.proxywasm.v1.WasmResult; - -// @WasmModuleInterface("main.wasm") -public class Exports { - - private final Instance.Exports exports; - - public String getMallocFunctionName() { - return mallocFunctionName; - } - - public void setMallocFunctionName(String mallocFunctionName) { - this.mallocFunctionName = mallocFunctionName; - } - - String mallocFunctionName = "malloc"; - - public Exports(Instance.Exports exports) { - this.exports = exports; - } - - int malloc(int size) throws WasmException { - // I've noticed guests fail on malloc(0) so lets avoid that - assert size > 0 : "malloc size must be greater than zero"; - long ptr = exports.function(mallocFunctionName).apply(size)[0]; - if (ptr == 0) { - throw new WasmException(WasmResult.INVALID_MEMORY_ACCESS); - } - return (int) ptr; - } - - public boolean proxyOnVmStart(int arg0, int arg1) { - long result = exports.function("proxy_on_vm_start").apply(arg0, arg1)[0]; - return result != 0; - } - - public int proxyValidateConfiguration(int arg0, int arg1) { - long result = exports.function("proxy_validate_configuration").apply(arg0, arg1)[0]; - return (int) result; - } - - public boolean proxyOnConfigure(int arg0, int arg1) { - long result = exports.function("proxy_on_configure").apply(arg0, arg1)[0]; - return result != 0; - } - - public void proxyOnTick(int arg0) { - exports.function("proxy_on_tick").apply(arg0); - } - - public void proxyOnContextCreate(int contextID, int parentContextID) { - exports.function("proxy_on_context_create").apply(contextID, parentContextID); - } - - // This can be used to change active context, e.g. during proxy_on_http_call_response, - // proxy_on_grpc_receive and/or proxy_on_queue_ready callbacks. - public void proxySetEffectiveContext(int contextID) throws WasmException { - long result = exports.function("proxy_set_effective_context").apply(contextID)[0]; - WasmResult.fromInt((int) result).expect(WasmResult.OK); - } - - public int proxyOnNewConnection(int arg0) { - long result = exports.function("proxy_on_new_connection").apply(arg0)[0]; - return (int) result; - } - - public int proxyOnDownstreamData(int contextId, int dataSize, int endOfStream) { - long result = - exports.function("proxy_on_downstream_data") - .apply(contextId, dataSize, endOfStream)[0]; - return (int) result; - } - - public int proxyOnUpstreamData(int arg0, int arg1, int arg2) { - long result = exports.function("proxy_on_upstream_data").apply(arg0, arg1, arg2)[0]; - return (int) result; - } - - public void proxyOnDownstreamConnectionClose(int arg0, int arg1) { - exports.function("proxy_on_downstream_connection_close").apply(arg0, arg1); - } - - public void proxyOnUpstreamConnectionClose(int arg0, int arg1) { - exports.function("proxy_on_upstream_connection_close").apply(arg0, arg1); - } - - public int proxyOnRequestHeaders(int contextID, int headers, int endOfStream) { - long result = - exports.function("proxy_on_request_headers") - .apply(contextID, headers, endOfStream)[0]; - return (int) result; - } - - public int proxyOnRequestMetadata(int arg0, int arg1) { - long result = exports.function("proxy_on_request_metadata").apply(arg0, arg1)[0]; - return (int) result; - } - - public int proxyOnRequestBody(int contextId, int bodySize, int arg2) { - long result = exports.function("proxy_on_request_body").apply(contextId, bodySize, arg2)[0]; - return (int) result; - } - - public int proxyOnRequestTrailers(int arg0, int arg1) { - long result = exports.function("proxy_on_request_trailers").apply(arg0, arg1)[0]; - return (int) result; - } - - public int proxyOnResponseHeaders(int arg0, int arg1, int arg2) { - long result = exports.function("proxy_on_response_headers").apply(arg0, arg1, arg2)[0]; - return (int) result; - } - - public int proxyOnResponseMetadata(int arg0, int arg1) { - long result = exports.function("proxy_on_response_metadata").apply(arg0, arg1)[0]; - return (int) result; - } - - public int proxyOnResponseBody(int arg0, int arg1, int arg2) { - long result = exports.function("proxy_on_response_body").apply(arg0, arg1, arg2)[0]; - return (int) result; - } - - public int proxyOnResponseTrailers(int arg0, int arg1) { - long result = exports.function("proxy_on_response_trailers").apply(arg0, arg1)[0]; - return (int) result; - } - - public void proxyOnLog(int context_id) { - exports.function("proxy_on_log").apply(context_id); - } - - public boolean proxyOnDone(int context_id) { - long result = exports.function("proxy_on_done").apply(context_id)[0]; - return result != 0; - } - - public void proxyOnDelete(int context_id) { - exports.function("proxy_on_delete").apply(context_id); - } - - public void proxyOnHttpCallResponse(int arg0, int arg1, int arg2, int arg3, int arg4) { - exports.function("proxy_on_http_call_response").apply(arg0, arg1, arg2, arg3, arg4); - } - - public void proxyOnGrpcReceiveInitialMetadata(int arg0, int arg1, int arg2) { - exports.function("proxy_on_grpc_receive_initial_metadata").apply(arg0, arg1, arg2); - } - - public void proxyOnGrpcReceiveTrailingMetadata(int arg0, int arg1, int arg2) { - exports.function("proxy_on_grpc_receive_trailing_metadata").apply(arg0, arg1, arg2); - } - - public void proxyOnGrpcReceive(int arg0, int arg1, int arg2) { - exports.function("proxy_on_grpc_receive").apply(arg0, arg1, arg2); - } - - public void proxyOnGrpcClose(int arg0, int arg1, int arg2) { - exports.function("proxy_on_grpc_close").apply(arg0, arg1, arg2); - } - - public void proxyOnQueueReady(int arg0, int arg1) { - exports.function("proxy_on_queue_ready").apply(arg0, arg1); - } - - public void proxyOnForeignFunction(int arg0, int arg1, int arg2) { - exports.function("proxy_on_foreign_function").apply(arg0, arg1, arg2); - } - - public void initialize() { - exports.function("_initialize").apply(); - } - - public int main(int arg0, int arg1) { - long result = exports.function("main").apply(arg0, arg1)[0]; - return (int) result; - } - - public void start() { - exports.function("_start").apply(); - } -} diff --git a/src/main/java/io/roastedroot/proxywasm/v1/Context.java b/src/main/java/io/roastedroot/proxywasm/v1/Context.java index ecc3cc3..19ea76a 100644 --- a/src/main/java/io/roastedroot/proxywasm/v1/Context.java +++ b/src/main/java/io/roastedroot/proxywasm/v1/Context.java @@ -16,13 +16,6 @@ public abstract class Context implements Closeable { abstract Handler handler(); - void activate() throws WasmException { - if (this != proxyWasm.getActiveContext()) { - proxyWasm.setActiveContext(this); - proxyWasm.exports().proxySetEffectiveContext(id); - } - } - public int id() { return id; } @@ -34,7 +27,7 @@ public void close() { closeStarted = true; if (!closeDone) { // the plugin may want to finish closing later... - if (proxyWasm.exports().proxyOnDone(id)) { + if (proxyWasm.abi().proxyOnDone(id)) { // close now... finishClose(); } @@ -56,7 +49,7 @@ WasmResult done() { protected void finishClose() { closeDone = true; - proxyWasm.exports().proxyOnLog(id); + proxyWasm.abi().proxyOnLog(id); proxyWasm.contexts().remove(id); // todo: we likely need to callback to user code to allow cleaning up resources like http @@ -67,6 +60,6 @@ protected void finishClose() { // unset active context so that callbacks don't try to use us. proxyWasm.setActiveContext(null); - proxyWasm.exports().proxyOnDelete(id); + proxyWasm.abi().proxyOnDelete(id); } } diff --git a/src/main/java/io/roastedroot/proxywasm/v1/Handler.java b/src/main/java/io/roastedroot/proxywasm/v1/Handler.java index 024e94d..2d61bf0 100644 --- a/src/main/java/io/roastedroot/proxywasm/v1/Handler.java +++ b/src/main/java/io/roastedroot/proxywasm/v1/Handler.java @@ -7,6 +7,10 @@ public interface Handler { default void log(LogLevel level, String message) throws WasmException {} + default LogLevel getLogLevel() throws WasmException { + return LogLevel.TRACE; + } + // TODO: use a better type than Map so that we can support repeated headers default Map getHttpRequestHeaders() { return null; diff --git a/src/main/java/io/roastedroot/proxywasm/v1/Helpers.java b/src/main/java/io/roastedroot/proxywasm/v1/Helpers.java index 61acf63..adfcb18 100644 --- a/src/main/java/io/roastedroot/proxywasm/v1/Helpers.java +++ b/src/main/java/io/roastedroot/proxywasm/v1/Helpers.java @@ -83,4 +83,41 @@ public static String[] append(String[] value1, String... value2) { System.arraycopy(value2, 0, result, value1.length, value2.length); return result; } + + public static byte[] replaceBytes( + byte[] existing, byte[] change, int replaceStart, int replaceLength) { + + if (replaceStart > existing.length) { + replaceStart = existing.length; + } + if (replaceLength > existing.length) { + replaceLength = existing.length; + } + + // when we are replacing the whole buffer + if (replaceStart == 0 && replaceLength == existing.length) { + return change; + } + + int newLength = change.length + (existing.length - replaceLength); + byte[] result = new byte[newLength]; + + // Copy the unchanged part before the start position + System.arraycopy(existing, 0, result, 0, Math.min(replaceStart, existing.length)); + + // Copy the new change bytes + System.arraycopy(change, 0, result, replaceStart, change.length); + + // Copy the remaining unchanged part after replacement + if (replaceStart + replaceLength < existing.length) { + System.arraycopy( + existing, + replaceStart + replaceLength, + result, + replaceStart + change.length, + existing.length - (replaceStart + replaceLength)); + } + + return result; + } } diff --git a/src/main/java/io/roastedroot/proxywasm/v1/HttpContext.java b/src/main/java/io/roastedroot/proxywasm/v1/HttpContext.java index 0a09a18..ce65613 100644 --- a/src/main/java/io/roastedroot/proxywasm/v1/HttpContext.java +++ b/src/main/java/io/roastedroot/proxywasm/v1/HttpContext.java @@ -17,8 +17,7 @@ Handler handler() { public Action callOnRequestHeaders(boolean endOfStream) { var headers = handler.getHttpRequestHeaders(); - int result = - proxyWasm.exports().proxyOnRequestHeaders(id, len(headers), endOfStream ? 1 : 0); + int result = proxyWasm.abi().proxyOnRequestHeaders(id, len(headers), endOfStream ? 1 : 0); Action action = Action.fromInt(result); handler.setAction(StreamType.REQUEST, action); return action; @@ -26,8 +25,7 @@ public Action callOnRequestHeaders(boolean endOfStream) { public Action callOnResponseHeaders(boolean endOfStream) { var headers = handler.getHttpResponseHeaders(); - int result = - proxyWasm.exports().proxyOnResponseHeaders(id, len(headers), endOfStream ? 1 : 0); + int result = proxyWasm.abi().proxyOnResponseHeaders(id, len(headers), endOfStream ? 1 : 0); Action action = Action.fromInt(result); handler.setAction(StreamType.RESPONSE, action); return action; @@ -37,7 +35,7 @@ public Action callOnRequestBody(boolean endOfStream) { var requestBody = handler.getHttpRequestBody(); int result = proxyWasm - .exports() + .abi() .proxyOnRequestBody(id, Helpers.len(requestBody), endOfStream ? 1 : 0); Action action = Action.fromInt(result); handler.setAction(StreamType.REQUEST, action); @@ -48,7 +46,7 @@ public Action callOnResponseBody(boolean endOfStream) { var responseBody = handler.getHttpResponseBody(); int result = proxyWasm - .exports() + .abi() .proxyOnResponseBody(id, Helpers.len(responseBody), endOfStream ? 1 : 0); Action action = Action.fromInt(result); handler.setAction(StreamType.RESPONSE, action); diff --git a/src/main/java/io/roastedroot/proxywasm/v1/NetworkContext.java b/src/main/java/io/roastedroot/proxywasm/v1/NetworkContext.java index 435e1c7..c5b5889 100644 --- a/src/main/java/io/roastedroot/proxywasm/v1/NetworkContext.java +++ b/src/main/java/io/roastedroot/proxywasm/v1/NetworkContext.java @@ -16,7 +16,7 @@ Handler handler() { } public Action callOnNewConnection() { - int result = proxyWasm.exports().proxyOnNewConnection(id); + int result = proxyWasm.abi().proxyOnNewConnection(id); Action action = Action.fromInt(result); handler.setAction(StreamType.DOWNSTREAM, action); return action; @@ -24,7 +24,7 @@ public Action callOnNewConnection() { public Action callOnDownstreamData(boolean endOfStream) { var data = handler.getDownStreamData(); - var result = proxyWasm.exports().proxyOnDownstreamData(id, len(data), endOfStream ? 1 : 0); + var result = proxyWasm.abi().proxyOnDownstreamData(id, len(data), endOfStream ? 1 : 0); Action action = Action.fromInt(result); handler.setAction(StreamType.DOWNSTREAM, action); return action; @@ -32,14 +32,14 @@ public Action callOnDownstreamData(boolean endOfStream) { public Action callOnUpstreamData(boolean endOfStream) { var data = handler.getUpstreamData(); - var result = proxyWasm.exports().proxyOnUpstreamData(id, len(data), endOfStream ? 1 : 0); + var result = proxyWasm.abi().proxyOnUpstreamData(id, len(data), endOfStream ? 1 : 0); Action action = Action.fromInt(result); handler.setAction(StreamType.UPSTREAM, action); return action; } public void callOnDownstreamConnectionClose(PeerType type) { - proxyWasm.exports().proxyOnDownstreamConnectionClose(id, type.getValue()); + proxyWasm.abi().proxyOnDownstreamConnectionClose(id, type.getValue()); } public void callOnDownstreamConnectionClose() { diff --git a/src/main/java/io/roastedroot/proxywasm/v1/ProxyWasm.java b/src/main/java/io/roastedroot/proxywasm/v1/ProxyWasm.java index bb2a9fe..32daff8 100644 --- a/src/main/java/io/roastedroot/proxywasm/v1/ProxyWasm.java +++ b/src/main/java/io/roastedroot/proxywasm/v1/ProxyWasm.java @@ -9,13 +9,11 @@ import com.dylibso.chicory.runtime.Instance; import com.dylibso.chicory.wasi.WasiOptions; import com.dylibso.chicory.wasi.WasiPreview1; -import com.dylibso.chicory.wasm.InvalidException; import com.dylibso.chicory.wasm.WasmModule; import com.dylibso.chicory.wasm.types.MemoryLimits; import com.dylibso.chicory.wasm.types.ValueType; -import io.roastedroot.proxywasm.impl.Exports; -import io.roastedroot.proxywasm.impl.Imports; -import io.roastedroot.proxywasm.impl.Imports_ModuleFactory; +import io.roastedroot.proxywasm.impl.ABI; +import io.roastedroot.proxywasm.impl.ABI_ModuleFactory; import java.io.Closeable; import java.nio.charset.StandardCharsets; import java.util.HashMap; @@ -26,8 +24,7 @@ public final class ProxyWasm implements Closeable { - private final Exports exports; - private final Imports imports; + private final ABI abi; private final Handler pluginHandler; private final byte[] pluginConfig; private final byte[] vmConfig; @@ -51,36 +48,35 @@ private ProxyWasm(Builder other) throws StartException { this.properties = Objects.requireNonNullElse(other.properties, new HashMap<>()); this.pluginHandler = Objects.requireNonNullElse(other.pluginHandler, new Handler() {}); this.wasi = other.wasi; - this.exports = other.exports; - this.imports = other.imports; - this.imports.setHandler(createImportsHandler()); + this.abi = other.abi; + this.abi.setHandler(createImportsHandler()); this.abiVersion = findAbiVersion(); // Since 0_2_0, prefer proxy_on_memory_allocate over malloc - if (instanceExportsFunction("proxy_on_memory_allocate")) { - this.exports.setMallocFunctionName("proxy_on_memory_allocate"); + if (this.abi.instanceExportsFunction("proxy_on_memory_allocate")) { + this.abi.setMallocFunctionName("proxy_on_memory_allocate"); } // initialize/start the vm - if (instanceExportsFunction("_initialize")) { - this.exports.initialize(); - if (instanceExportsFunction("main")) { - this.exports.main(0, 0); + if (this.abi.instanceExportsFunction("_initialize")) { + this.abi.initialize(); + if (this.abi.instanceExportsFunction("main")) { + this.abi.main(0, 0); } } else { - if (instanceExportsFunction("_start")) { - this.exports.start(); + if (this.abi.instanceExportsFunction("_start")) { + this.abi.start(); } } // start the vm with the vmHandler, it will receive stuff like log messages. this.pluginContext = new PluginContext(this, pluginHandler); registerContext(pluginContext, 0); - if (!exports.proxyOnVmStart(pluginContext.id(), vmConfig.length)) { + if (!this.abi.proxyOnVmStart(pluginContext.id(), vmConfig.length)) { throw new StartException("proxy_on_vm_start failed"); } - if (!exports.proxyOnConfigure(pluginContext.id(), pluginConfig.length)) { + if (!this.abi.proxyOnConfigure(pluginContext.id(), pluginConfig.length)) { throw new StartException("proxy_on_configure failed"); } } @@ -88,27 +84,18 @@ private ProxyWasm(Builder other) throws StartException { private void registerContext(Context context, int parentContextID) { contexts.put(context.id(), context); activeContext = context; - exports.proxyOnContextCreate(context.id(), parentContextID); + this.abi.proxyOnContextCreate(context.id(), parentContextID); } private ABIVersion findAbiVersion() throws StartException { for (var version : ABIVersion.values()) { - if (instanceExportsFunction(version.getAbiMarkerFunction())) { + if (this.abi.instanceExportsFunction(version.getAbiMarkerFunction())) { return version; } } throw new StartException("wasm module does nto contain a supported proxy-wasm abi version"); } - private boolean instanceExportsFunction(String name) { - try { - this.imports.getInstance().exports().function(name); - return true; - } catch (InvalidException e) { - return false; - } - } - // Let's implement some of the handler functions to make life easier for the user. // The user's handler will be the last handler in the chain. private ChainedHandler createImportsHandler() { @@ -186,10 +173,6 @@ HashMap contexts() { return contexts; } - Exports exports() { - return exports; - } - Context getActiveContext() { return activeContext; } @@ -226,7 +209,7 @@ public NetworkContext createNetworkContext(Handler handler) { * tick() should be called in response to a Handler.setTickPeriodMilliseconds(int tick_period) callback. */ public void tick() { - exports.proxyOnTick(pluginContext.id()); + this.abi.proxyOnTick(pluginContext.id()); } public ABIVersion abiVersion() { @@ -270,7 +253,7 @@ public void sendHttpCallResponse( this.httpCallResponseTrailers = trailers; this.httpCallResponseBody = body; - this.exports.proxyOnHttpCallResponse( + this.abi.proxyOnHttpCallResponse( pluginContext.id(), calloutID, len(headers), len(body), len(trailers)); this.httpCallResponseHeaders = null; @@ -279,7 +262,7 @@ public void sendHttpCallResponse( } public void sendOnQueueReady(int queueId) { - this.exports.proxyOnQueueReady(pluginContext.id(), queueId); + this.abi.proxyOnQueueReady(pluginContext.id(), queueId); } public int contextId() { @@ -290,10 +273,13 @@ public void registerForeignFunction(String name, ForeignFunction func) { foreignFunctions.put(name, func); } + ABI abi() { + return abi; + } + public static class Builder implements Cloneable { - private Exports exports; - private final Imports imports = new Imports(); + private final ABI abi = new ABI(); private WasiPreview1 wasi; private byte[] vmConfig = new byte[0]; @@ -314,7 +300,7 @@ protected Builder clone() { } public HostFunction[] toHostFunctions() { - return Imports_ModuleFactory.toHostFunctions(imports); + return ABI_ModuleFactory.toHostFunctions(abi); } public ProxyWasm.Builder withVmConfig(byte[] vmConfig) { @@ -364,9 +350,7 @@ public ProxyWasm.Builder withWasiOptions(WasiOptions options) { Builder() {} public ProxyWasm build(Instance instance) throws StartException { - exports = new Exports(instance.exports()); - imports.setInstance(instance); - imports.setExports(exports); + abi.setInstance(instance); return new ProxyWasm(this.clone()); }