From 63fdd210bf2a8f124b3f39c67855f5627176e2d4 Mon Sep 17 00:00:00 2001 From: Hiram Chirino Date: Tue, 18 Mar 2025 17:55:17 -0400 Subject: [PATCH] Implement getting well known properties in the jaxrs filter. Signed-off-by: Hiram Chirino --- .../proxywasm/WellKnownProperties.java | 5 + proxy-wasm-jaxrs/pom.xml | 25 +++- .../proxywasm/jaxrs/HttpHandler.java | 139 +++++++++++++++++- .../proxywasm/jaxrs/ProxyWasmFilter.java | 13 +- .../proxywasm/jaxrs/WasmPluginFeature.java | 5 +- .../jaxrs/servlet/ServletHttpServer.java | 39 +++++ .../proxywasm/jaxrs/spi/HttpServer.java | 15 ++ .../jaxrs/vertx/VertxHttpServer.java | 41 ++++++ 8 files changed, 266 insertions(+), 16 deletions(-) create mode 100644 proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/servlet/ServletHttpServer.java create mode 100644 proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/spi/HttpServer.java create mode 100644 proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/vertx/VertxHttpServer.java diff --git a/proxy-wasm-java-host/src/main/java/io/roastedroot/proxywasm/WellKnownProperties.java b/proxy-wasm-java-host/src/main/java/io/roastedroot/proxywasm/WellKnownProperties.java index 9d479a3..321f5cf 100644 --- a/proxy-wasm-java-host/src/main/java/io/roastedroot/proxywasm/WellKnownProperties.java +++ b/proxy-wasm-java-host/src/main/java/io/roastedroot/proxywasm/WellKnownProperties.java @@ -2,6 +2,11 @@ import java.util.List; +/** + * Holds constants for the well-known properties defined by the Proxy-Wasm ABI. + * + * see: spec + */ public final class WellKnownProperties { private WellKnownProperties() {} diff --git a/proxy-wasm-jaxrs/pom.xml b/proxy-wasm-jaxrs/pom.xml index 938a7a8..610f38d 100644 --- a/proxy-wasm-jaxrs/pom.xml +++ b/proxy-wasm-jaxrs/pom.xml @@ -26,12 +26,30 @@ + + io.quarkus + quarkus-arc + true + io.roastedroot proxy-wasm-java-host 1.0-SNAPSHOT + + + io.vertx + vertx-core + true + + + jakarta.servlet + jakarta.servlet-api + true + + + jakarta.enterprise jakarta.enterprise.cdi-api @@ -42,8 +60,6 @@ jakarta.inject-api provided - - jakarta.ws.rs jakarta.ws.rs-api @@ -51,11 +67,6 @@ - - io.quarkus - quarkus-arc - test - io.quarkus quarkus-junit5 diff --git a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/HttpHandler.java b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/HttpHandler.java index 7a39640..cd7f5d3 100644 --- a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/HttpHandler.java +++ b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/HttpHandler.java @@ -1,6 +1,29 @@ package io.roastedroot.proxywasm.jaxrs; +import static io.roastedroot.proxywasm.Helpers.bytes; import static io.roastedroot.proxywasm.Helpers.string; +import static io.roastedroot.proxywasm.WellKnownProperties.CONNECTION_DNS_SAN_LOCAL_CERTIFICATE; +import static io.roastedroot.proxywasm.WellKnownProperties.CONNECTION_DNS_SAN_PEER_CERTIFICATE; +import static io.roastedroot.proxywasm.WellKnownProperties.CONNECTION_ID; +import static io.roastedroot.proxywasm.WellKnownProperties.CONNECTION_MTLS; +import static io.roastedroot.proxywasm.WellKnownProperties.CONNECTION_REQUESTED_SERVER_NAME; +import static io.roastedroot.proxywasm.WellKnownProperties.CONNECTION_SHA256_PEER_CERTIFICATE_DIGEST; +import static io.roastedroot.proxywasm.WellKnownProperties.CONNECTION_SUBJECT_LOCAL_CERTIFICATE; +import static io.roastedroot.proxywasm.WellKnownProperties.CONNECTION_SUBJECT_PEER_CERTIFICATE; +import static io.roastedroot.proxywasm.WellKnownProperties.CONNECTION_TLS_VERSION; +import static io.roastedroot.proxywasm.WellKnownProperties.CONNECTION_URI_SAN_LOCAL_CERTIFICATE; +import static io.roastedroot.proxywasm.WellKnownProperties.CONNECTION_URI_SAN_PEER_CERTIFICATE; +import static io.roastedroot.proxywasm.WellKnownProperties.DESTINATION_ADDRESS; +import static io.roastedroot.proxywasm.WellKnownProperties.DESTINATION_PORT; +import static io.roastedroot.proxywasm.WellKnownProperties.REQUEST_DURATION; +import static io.roastedroot.proxywasm.WellKnownProperties.REQUEST_PROTOCOL; +import static io.roastedroot.proxywasm.WellKnownProperties.REQUEST_SIZE; +import static io.roastedroot.proxywasm.WellKnownProperties.REQUEST_TIME; +import static io.roastedroot.proxywasm.WellKnownProperties.REQUEST_TOTAL_SIZE; +import static io.roastedroot.proxywasm.WellKnownProperties.RESPONSE_SIZE; +import static io.roastedroot.proxywasm.WellKnownProperties.RESPONSE_TOTAL_SIZE; +import static io.roastedroot.proxywasm.WellKnownProperties.SOURCE_ADDRESS; +import static io.roastedroot.proxywasm.WellKnownProperties.SOURCE_PORT; import io.roastedroot.proxywasm.Action; import io.roastedroot.proxywasm.ChainedHandler; @@ -10,18 +33,24 @@ import io.roastedroot.proxywasm.StreamType; import io.roastedroot.proxywasm.WasmException; import io.roastedroot.proxywasm.WasmResult; +import io.roastedroot.proxywasm.jaxrs.spi.HttpServer; import jakarta.ws.rs.container.ContainerRequestContext; import jakarta.ws.rs.container.ContainerResponseContext; import jakarta.ws.rs.core.Response; +import java.util.Date; import java.util.HashMap; import java.util.List; class HttpHandler extends ChainedHandler { private final PluginHandler next; + private final HttpServer httpServer; + private final long startedAt; - HttpHandler(PluginHandler pluginHandler) { + HttpHandler(PluginHandler pluginHandler, HttpServer httpServer) { this.next = pluginHandler; + this.httpServer = httpServer; + this.startedAt = System.currentTimeMillis(); } @Override @@ -237,11 +266,113 @@ public Action getAction() { @Override public byte[] getProperty(List path) throws WasmException { + + // Check to see if it's a well known property + + // Downstream connection properties + if (CONNECTION_ID.equals(path)) { + // Do we need to generate one? + return null; + } else if (SOURCE_ADDRESS.equals(path)) { + return bytes(httpServer.remoteAddress()); + } else if (SOURCE_PORT.equals(path)) { + return bytes(httpServer.remotePort()); + } else if (DESTINATION_ADDRESS.equals(path)) { + return bytes(httpServer.localAddress()); + } else if (DESTINATION_PORT.equals(path)) { + return bytes(httpServer.localPort()); + } + + // TLS connection properties + else if (CONNECTION_TLS_VERSION.equals(path)) { + return null; + } else if (CONNECTION_REQUESTED_SERVER_NAME.equals(path)) { + return null; + } else if (CONNECTION_MTLS.equals(path)) { + return null; + } else if (CONNECTION_SUBJECT_LOCAL_CERTIFICATE.equals(path)) { + return null; + } else if (CONNECTION_SUBJECT_PEER_CERTIFICATE.equals(path)) { + return null; + } else if (CONNECTION_DNS_SAN_LOCAL_CERTIFICATE.equals(path)) { + return null; + } else if (CONNECTION_DNS_SAN_PEER_CERTIFICATE.equals(path)) { + return null; + } else if (CONNECTION_URI_SAN_LOCAL_CERTIFICATE.equals(path)) { + return null; + } else if (CONNECTION_URI_SAN_PEER_CERTIFICATE.equals(path)) { + return null; + } else if (CONNECTION_SHA256_PEER_CERTIFICATE_DIGEST.equals(path)) { + return null; + } + + // Upstream connection properties: we are not directly connecting to an upstream server, so + // these are not implemented. + // else if (UPSTREAM_ADDRESS.equals(path)) { + // return null; + // } else if (UPSTREAM_PORT.equals(path)) { + // return null; + // } else if (UPSTREAM_LOCAL_ADDRESS.equals(path)) { + // return null; + // } else if (UPSTREAM_LOCAL_PORT.equals(path)) { + // return null; + // } else if (UPSTREAM_TLS_VERSION.equals(path)) { + // return null; + // } else if (UPSTREAM_SUBJECT_LOCAL_CERTIFICATE.equals(path)) { + // return null; + // } else if (UPSTREAM_SUBJECT_PEER_CERTIFICATE.equals(path)) { + // return null; + // } else if (UPSTREAM_DNS_SAN_LOCAL_CERTIFICATE.equals(path)) { + // return null; + // } else if (UPSTREAM_DNS_SAN_PEER_CERTIFICATE.equals(path)) { + // return null; + // } else if (UPSTREAM_URI_SAN_LOCAL_CERTIFICATE.equals(path)) { + // return null; + // } else if (UPSTREAM_URI_SAN_PEER_CERTIFICATE.equals(path)) { + // return null; + // } else if (UPSTREAM_SHA256_PEER_CERTIFICATE_DIGEST.equals(path)) { + // return null; + // } + + // HTTP request properties + else if (REQUEST_PROTOCOL.equals(path)) { + if (requestContext == null) { + return null; + } + return bytes(requestContext.getUriInfo().getRequestUri().getScheme()); + } else if (REQUEST_TIME.equals(path)) { + // TODO: check encoding /w other impls + return bytes(new Date(startedAt).toString()); + } else if (REQUEST_DURATION.equals(path)) { + // TODO: check encoding /w other impls + return bytes("" + (System.currentTimeMillis() - startedAt)); + } else if (REQUEST_SIZE.equals(path)) { + if (httpRequestBody == null) { + return null; + } + // TODO: check encoding /w other impls + return bytes("" + httpRequestBody.length); + } else if (REQUEST_TOTAL_SIZE.equals(path)) { + return null; + } + + // HTTP response properties + else if (RESPONSE_SIZE.equals(path)) { + if (httpResponseBody == null) { + return null; + } + // TODO: check encoding /w other impls + return bytes("" + httpResponseBody.length); + } else if (RESPONSE_TOTAL_SIZE.equals(path)) { + // TODO: how can we do this? + return null; + } + byte[] result = properties.get(path); - if (result == null) { - return next().getProperty(path); + if (result != null) { + return result; } - return result; + return next().getProperty(path); } @Override diff --git a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/ProxyWasmFilter.java b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/ProxyWasmFilter.java index e1bf473..b8223f8 100644 --- a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/ProxyWasmFilter.java +++ b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/ProxyWasmFilter.java @@ -3,6 +3,8 @@ import io.roastedroot.proxywasm.Action; import io.roastedroot.proxywasm.HttpContext; import io.roastedroot.proxywasm.StartException; +import io.roastedroot.proxywasm.jaxrs.spi.HttpServer; +import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.container.ContainerRequestContext; @@ -28,9 +30,12 @@ public class ProxyWasmFilter private final WasmPluginFactory pluginFactory; + Instance httpServer; + @Inject - public ProxyWasmFilter(WasmPluginFactory pluginFactory) { + public ProxyWasmFilter(WasmPluginFactory pluginFactory, Instance httpServer) { this.pluginFactory = pluginFactory; + this.httpServer = httpServer; } // TODO: the HttpContext and ProxyWasm object's should be closed once the request is done. @@ -40,9 +45,9 @@ static class WasmHttpFilterContext { final HttpHandler handler; final HttpContext wasm; - public WasmHttpFilterContext(WasmPlugin plugin) { + public WasmHttpFilterContext(WasmPlugin plugin, HttpServer httpServer) { this.pluginHandler = plugin.pluginHandler(); - this.handler = new HttpHandler(plugin.pluginHandler()); + this.handler = new HttpHandler(plugin.pluginHandler(), httpServer); this.wasm = plugin.proxyWasm().createHttpContext(this.handler); } } @@ -58,7 +63,7 @@ public void filter(ContainerRequestContext requestContext) throws IOException { Response.status(Response.Status.INTERNAL_SERVER_ERROR).build()); } - var wasmHttpFilterContext = new WasmHttpFilterContext(plugin); + var wasmHttpFilterContext = new WasmHttpFilterContext(plugin, this.httpServer.get()); requestContext.setProperty(FILTER_CONTEXT_PROPERTY_NAME, wasmHttpFilterContext); // the plugin may not be interested in the request headers. diff --git a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPluginFeature.java b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPluginFeature.java index 0220db2..703e6ad 100644 --- a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPluginFeature.java +++ b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPluginFeature.java @@ -1,6 +1,7 @@ package io.roastedroot.proxywasm.jaxrs; import io.roastedroot.proxywasm.StartException; +import io.roastedroot.proxywasm.jaxrs.spi.HttpServer; import jakarta.enterprise.inject.Any; import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; @@ -15,6 +16,8 @@ public class WasmPluginFeature implements DynamicFeature { private HashMap plugins = new HashMap<>(); + @Inject @Any Instance requestAdaptor; + @Inject public WasmPluginFeature(@Any Instance factories) throws StartException { for (var factory : factories) { @@ -41,7 +44,7 @@ public void configure(ResourceInfo resourceInfo, FeatureContext context) { if (pluignNameAnnotation != null) { WasmPluginFactory factory = plugins.get(pluignNameAnnotation.value()); if (factory != null) { - context.register(new ProxyWasmFilter(factory)); + context.register(new ProxyWasmFilter(factory, requestAdaptor)); } } } diff --git a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/servlet/ServletHttpServer.java b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/servlet/ServletHttpServer.java new file mode 100644 index 0000000..9b62c43 --- /dev/null +++ b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/servlet/ServletHttpServer.java @@ -0,0 +1,39 @@ +package io.roastedroot.proxywasm.jaxrs.servlet; + +import io.roastedroot.proxywasm.jaxrs.spi.HttpServer; +import jakarta.annotation.Priority; +import jakarta.enterprise.inject.Alternative; +import jakarta.enterprise.inject.Instance; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.Context; + +@Alternative +@Priority(100) +public class ServletHttpServer implements HttpServer { + + private final HttpServletRequest request; + + public ServletHttpServer(@Context Instance request) { + this.request = request.get(); + } + + @Override + public String remoteAddress() { + return request.getRemoteAddr(); + } + + @Override + public String remotePort() { + return "" + request.getRemotePort(); + } + + @Override + public String localAddress() { + return request.getLocalAddr(); + } + + @Override + public String localPort() { + return "" + request.getLocalPort(); + } +} diff --git a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/spi/HttpServer.java b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/spi/HttpServer.java new file mode 100644 index 0000000..720f74e --- /dev/null +++ b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/spi/HttpServer.java @@ -0,0 +1,15 @@ +package io.roastedroot.proxywasm.jaxrs.spi; + +/** + * This interface will help us deal with differences in the http server impl. + */ +public interface HttpServer { + + String remoteAddress(); + + String remotePort(); + + String localAddress(); + + String localPort(); +} diff --git a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/vertx/VertxHttpServer.java b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/vertx/VertxHttpServer.java new file mode 100644 index 0000000..97d3b5a --- /dev/null +++ b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/vertx/VertxHttpServer.java @@ -0,0 +1,41 @@ +package io.roastedroot.proxywasm.jaxrs.vertx; + +import io.roastedroot.proxywasm.jaxrs.spi.HttpServer; +import io.vertx.core.http.HttpServerRequest; +import jakarta.annotation.Priority; +import jakarta.enterprise.context.RequestScoped; +import jakarta.enterprise.inject.Alternative; +import jakarta.enterprise.inject.Instance; +import jakarta.ws.rs.core.Context; + +@Alternative +@Priority(200) +@RequestScoped +public class VertxHttpServer implements HttpServer { + + private final HttpServerRequest request; + + public VertxHttpServer(@Context Instance request) { + this.request = request.get(); + } + + @Override + public String remoteAddress() { + return request.remoteAddress().hostAddress(); + } + + @Override + public String remotePort() { + return "" + request.remoteAddress().port(); + } + + @Override + public String localAddress() { + return request.localAddress().hostAddress(); + } + + @Override + public String localPort() { + return "" + request.localAddress().port(); + } +}