diff --git a/proxy-wasm-java-host/src/main/java/io/roastedroot/proxywasm/ProxyWasm.java b/proxy-wasm-java-host/src/main/java/io/roastedroot/proxywasm/ProxyWasm.java index 087a65e..630c1b9 100644 --- a/proxy-wasm-java-host/src/main/java/io/roastedroot/proxywasm/ProxyWasm.java +++ b/proxy-wasm-java-host/src/main/java/io/roastedroot/proxywasm/ProxyWasm.java @@ -59,7 +59,7 @@ private ProxyWasm(Builder other) throws StartException { public void start() throws StartException { if (pluginContext != null) { - throw new IllegalStateException("already started"); + return; } this.pluginContext = new PluginContext(this, pluginHandler); diff --git a/proxy-wasm-jaxrs/pom.xml b/proxy-wasm-jaxrs/pom.xml index 610f38d..0c8a4c0 100644 --- a/proxy-wasm-jaxrs/pom.xml +++ b/proxy-wasm-jaxrs/pom.xml @@ -114,10 +114,8 @@ - build generate-code generate-code-tests - native-image-agent 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 6af101c..69542b1 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 @@ -50,7 +50,7 @@ import io.roastedroot.proxywasm.StreamType; import io.roastedroot.proxywasm.WasmException; import io.roastedroot.proxywasm.WasmResult; -import io.roastedroot.proxywasm.jaxrs.spi.HttpServer; +import io.roastedroot.proxywasm.jaxrs.spi.HttpServerRequest; import jakarta.ws.rs.container.ContainerRequestContext; import jakarta.ws.rs.container.ContainerResponseContext; import jakarta.ws.rs.core.Response; @@ -64,10 +64,10 @@ class HttpHandler extends ChainedHandler { private final PluginHandler next; - private final HttpServer httpServer; + private final HttpServerRequest httpServer; private final long startedAt; - HttpHandler(PluginHandler pluginHandler, HttpServer httpServer) { + HttpHandler(PluginHandler pluginHandler, HttpServerRequest httpServer) { this.next = pluginHandler; this.httpServer = httpServer; this.startedAt = System.currentTimeMillis(); diff --git a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/PluginHandler.java b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/PluginHandler.java index 3d80f14..0e76cb7 100644 --- a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/PluginHandler.java +++ b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/PluginHandler.java @@ -23,6 +23,7 @@ class PluginHandler extends ChainedHandler { // Filter Chain Methods // ////////////////////////////////////////////////////////////////////// private Handler next; + WasmPlugin plugin; PluginHandler() { this(new Handler() {}); @@ -37,6 +38,16 @@ protected Handler next() { return next; } + // ////////////////////////////////////////////////////////////////////// + // Cleanup + // ////////////////////////////////////////////////////////////////////// + public void close() { + if (cancelTick != null) { + cancelTick.run(); + cancelTick = null; + } + } + // ////////////////////////////////////////////////////////////////////// // Properties // ////////////////////////////////////////////////////////////////////// @@ -103,7 +114,9 @@ public LogLevel getLogLevel() throws WasmException { // Timers // ////////////////////////////////////////////////////////////////////// + int minTickPeriodMilliseconds; private int tickPeriodMilliseconds; + private Runnable cancelTick; public int getTickPeriodMilliseconds() { return tickPeriodMilliseconds; @@ -111,7 +124,36 @@ public int getTickPeriodMilliseconds() { @Override public WasmResult setTickPeriodMilliseconds(int tickPeriodMilliseconds) { + + // check for no change + if (tickPeriodMilliseconds == this.tickPeriodMilliseconds) { + return WasmResult.OK; + } + + // cancel the current tick, if any + if (cancelTick != null) { + cancelTick.run(); + cancelTick = null; + } + + // set the new tick period, if any this.tickPeriodMilliseconds = tickPeriodMilliseconds; + if (this.tickPeriodMilliseconds == 0) { + return WasmResult.OK; + } + + // schedule the new tick + this.cancelTick = + this.plugin.httpServer.scheduleTick( + Math.max(minTickPeriodMilliseconds, this.tickPeriodMilliseconds), + () -> { + this.plugin.lock(); + try { + this.plugin.wasm.tick(); + } finally { + this.plugin.unlock(); + } + }); return WasmResult.OK; } @@ -132,6 +174,10 @@ public WasmResult setFuncCallData(byte[] data) { return WasmResult.OK; } + public void setPlugin(WasmPlugin plugin) { + this.plugin = plugin; + } + // ////////////////////////////////////////////////////////////////////// // HTTP calls // ////////////////////////////////////////////////////////////////////// @@ -296,6 +342,9 @@ public WasmResult removeMetric(int metricId) { @Override public ForeignFunction getForeignFunction(String name) { - return super.getForeignFunction(name); + if (foreignFunctions == null) { + return null; + } + return foreignFunctions.get(name); } } 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 82eb319..6c548f9 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,15 +3,13 @@ import io.roastedroot.proxywasm.Action; import io.roastedroot.proxywasm.HttpContext; import io.roastedroot.proxywasm.StartException; -import io.roastedroot.proxywasm.jaxrs.spi.HttpServer; +import io.roastedroot.proxywasm.jaxrs.spi.HttpServerRequest; import jakarta.enterprise.inject.Instance; -import jakarta.inject.Inject; import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.container.ContainerRequestContext; import jakarta.ws.rs.container.ContainerRequestFilter; import jakarta.ws.rs.container.ContainerResponseContext; import jakarta.ws.rs.container.ContainerResponseFilter; -import jakarta.ws.rs.container.PreMatching; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.ext.ReaderInterceptor; import jakarta.ws.rs.ext.ReaderInterceptorContext; @@ -20,7 +18,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; -@PreMatching public class ProxyWasmFilter implements ContainerRequestFilter, ReaderInterceptor, @@ -30,14 +27,17 @@ public class ProxyWasmFilter private final WasmPluginPool pluginPool; - Instance httpServer; + Instance httpServer; - @Inject - public ProxyWasmFilter(WasmPluginPool pluginPool, Instance httpServer) { + public ProxyWasmFilter(WasmPluginPool pluginPool, Instance httpServer) { this.pluginPool = pluginPool; this.httpServer = httpServer; } + String name() { + return pluginPool.name(); + } + // TODO: the HttpContext and ProxyWasm object's should be closed once the request is done. // is there an easy way to hook up cleanup code for this? static class WasmHttpFilterContext { @@ -46,22 +46,27 @@ static class WasmHttpFilterContext { final HttpHandler httpHandler; final HttpContext httpContext; - public WasmHttpFilterContext(WasmPlugin plugin, HttpServer httpServer) { + public WasmHttpFilterContext(WasmPlugin plugin, HttpServerRequest httpServer) { this.plugin = plugin; - this.pluginHandler = plugin.pluginHandler(); - this.httpHandler = new HttpHandler(plugin.pluginHandler(), httpServer); - this.httpContext = plugin.proxyWasm().createHttpContext(this.httpHandler); + this.pluginHandler = plugin.handler; + this.httpHandler = new HttpHandler(plugin.handler, httpServer); + this.httpContext = plugin.wasm.createHttpContext(this.httpHandler); } } @Override public void filter(ContainerRequestContext requestContext) throws IOException { - WasmPlugin plugin = null; + WasmPlugin plugin; try { plugin = pluginPool.borrow(); - plugin.lock(); + } catch (StartException e) { + requestContext.abortWith(interalServerError()); + return; + } + plugin.lock(); + try { var ctx = new WasmHttpFilterContext(plugin, this.httpServer.get()); requestContext.setProperty(FILTER_CONTEXT_PROPERTY_NAME, ctx); @@ -81,15 +86,15 @@ public void filter(ContainerRequestContext requestContext) throws IOException { requestContext.abortWith(sendResponse.toResponse()); } } - - } catch (StartException e) { - requestContext.abortWith( - Response.status(Response.Status.INTERNAL_SERVER_ERROR).build()); } finally { plugin.unlock(); // allow another request to use the plugin. } } + private static Response interalServerError() { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } + @Override public Object aroundReadFrom(ReaderInterceptorContext ctx) throws IOException, WebApplicationException { @@ -97,8 +102,7 @@ public Object aroundReadFrom(ReaderInterceptorContext ctx) var wasmHttpFilterContext = (WasmHttpFilterContext) ctx.getProperty(FILTER_CONTEXT_PROPERTY_NAME); if (wasmHttpFilterContext == null) { - throw new WebApplicationException( - Response.status(Response.Status.INTERNAL_SERVER_ERROR).build()); + throw new WebApplicationException(interalServerError()); } // the plugin may not be interested in the request body. @@ -147,8 +151,7 @@ public void filter( var wasmHttpFilterContext = (WasmHttpFilterContext) requestContext.getProperty(FILTER_CONTEXT_PROPERTY_NAME); if (wasmHttpFilterContext == null) { - throw new WebApplicationException( - Response.status(Response.Status.INTERNAL_SERVER_ERROR).build()); + throw new WebApplicationException(interalServerError()); } // the plugin may not be interested in the request headers. @@ -182,8 +185,7 @@ public void aroundWriteTo(WriterInterceptorContext ctx) var wasmHttpFilterContext = (WasmHttpFilterContext) ctx.getProperty(FILTER_CONTEXT_PROPERTY_NAME); if (wasmHttpFilterContext == null) { - throw new WebApplicationException( - Response.status(Response.Status.INTERNAL_SERVER_ERROR).build()); + throw new WebApplicationException(interalServerError()); } try { diff --git a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPlugin.java b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPlugin.java index 5de5bcc..cad5ed8 100644 --- a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPlugin.java +++ b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPlugin.java @@ -6,6 +6,7 @@ import io.roastedroot.proxywasm.ForeignFunction; import io.roastedroot.proxywasm.ProxyWasm; import io.roastedroot.proxywasm.StartException; +import io.roastedroot.proxywasm.jaxrs.spi.HttpServer; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -13,30 +14,24 @@ public class WasmPlugin { - private final ProxyWasm proxyWasm; - private final PluginHandler handler; + final PluginHandler handler; private final ReentrantLock lock; + final ProxyWasm wasm; + HttpServer httpServer; private WasmPlugin(ProxyWasm proxyWasm, PluginHandler handler, boolean shared) { Objects.requireNonNull(proxyWasm); Objects.requireNonNull(handler); - this.proxyWasm = proxyWasm; + this.wasm = proxyWasm; this.handler = handler; this.lock = shared ? new ReentrantLock() : null; + this.handler.setPlugin(this); } public String name() { return handler.getName(); } - ProxyWasm proxyWasm() { - return proxyWasm; - } - - PluginHandler pluginHandler() { - return handler; - } - public static WasmPlugin.Builder builder() { return new WasmPlugin.Builder(); } @@ -59,10 +54,25 @@ public boolean isShared() { return lock != null; } + public void setHttpServer(HttpServer httpServer) { + this.httpServer = httpServer; + } + + public void close() { + lock(); + try { + wasm.close(); + handler.close(); + } finally { + unlock(); + } + } + public static class Builder implements Cloneable { private PluginHandler handler = new PluginHandler(); - private ProxyWasm.Builder proxyWasmBuilder = ProxyWasm.builder().withPluginHandler(handler); + private ProxyWasm.Builder proxyWasmBuilder = + ProxyWasm.builder().withPluginHandler(handler).withStart(false); private boolean shared = true; public WasmPlugin.Builder withName(String name) { @@ -75,6 +85,11 @@ public Builder withForeignFunctions(Map functions) { return this; } + public Builder withMinTickPeriodMilliseconds(int minTickPeriodMilliseconds) { + this.handler.minTickPeriodMilliseconds = minTickPeriodMilliseconds; + return this; + } + public Builder withLogger(Logger logger) { this.handler.logger = logger; return this; 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 6204d43..579cb54 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 @@ -2,6 +2,8 @@ import io.roastedroot.proxywasm.StartException; import io.roastedroot.proxywasm.jaxrs.spi.HttpServer; +import io.roastedroot.proxywasm.jaxrs.spi.HttpServerRequest; +import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Any; import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; @@ -9,31 +11,44 @@ import jakarta.ws.rs.container.ResourceInfo; import jakarta.ws.rs.core.FeatureContext; import jakarta.ws.rs.ext.Provider; +import java.util.Collection; import java.util.HashMap; @Provider +@ApplicationScoped public class WasmPluginFeature implements DynamicFeature { - private HashMap plugins = new HashMap<>(); + private final HashMap pluginPools = new HashMap<>(); - @Inject @Any Instance requestAdaptor; + @Inject @Any Instance httpServerRequest; @Inject - public WasmPluginFeature(@Any Instance factories) throws StartException { + public WasmPluginFeature(Instance factories, @Any HttpServer httpServer) + throws StartException { for (var factory : factories) { - var plugin = factory.create(); + WasmPlugin plugin = null; + plugin = factory.create(); + plugin.setHttpServer(httpServer); String name = plugin.name(); - if (this.plugins.containsKey(name)) { + if (this.pluginPools.containsKey(name)) { throw new IllegalArgumentException("Duplicate wasm plugin name: " + name); } WasmPluginPool pool = plugin.isShared() ? new WasmPluginPool.AppScoped(plugin) : new WasmPluginPool.RequestScoped(factory, plugin); - this.plugins.put(name, pool); + this.pluginPools.put(name, pool); } } + public Collection getPluginPools() { + return pluginPools.values(); + } + + WasmPluginPool pool(String name) { + return pluginPools.get(name); + } + @Override public void configure(ResourceInfo resourceInfo, FeatureContext context) { @@ -47,9 +62,9 @@ public void configure(ResourceInfo resourceInfo, FeatureContext context) { resourceInfo.getResourceClass().getAnnotation(NamedWasmPlugin.class); } if (pluignNameAnnotation != null) { - WasmPluginPool factory = plugins.get(pluignNameAnnotation.value()); + WasmPluginPool factory = pluginPools.get(pluignNameAnnotation.value()); if (factory != null) { - context.register(new ProxyWasmFilter(factory, requestAdaptor)); + context.register(new ProxyWasmFilter(factory, httpServerRequest)); } } } diff --git a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPluginPool.java b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPluginPool.java index a32b86a..7f60bd1 100644 --- a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPluginPool.java +++ b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPluginPool.java @@ -1,20 +1,39 @@ package io.roastedroot.proxywasm.jaxrs; import io.roastedroot.proxywasm.StartException; +import jakarta.annotation.PreDestroy; +import java.util.Collection; +import java.util.List; -public interface WasmPluginPool { +interface WasmPluginPool { WasmPlugin borrow() throws StartException; + String name(); + void release(WasmPlugin plugin); class AppScoped implements WasmPluginPool { private final WasmPlugin plugin; - public AppScoped(WasmPlugin plugin) { + public AppScoped(WasmPlugin plugin) throws StartException { this.plugin = plugin; } + @PreDestroy + public void close() { + plugin.wasm.close(); + } + + public Collection getPluginPools() { + return List.of(plugin); + } + + @Override + public String name() { + return plugin.name(); + } + @Override public void release(WasmPlugin plugin) { if (plugin != this.plugin) { @@ -24,6 +43,7 @@ public void release(WasmPlugin plugin) { @Override public WasmPlugin borrow() throws StartException { + plugin.wasm.start(); return plugin; } } @@ -31,15 +51,24 @@ public WasmPlugin borrow() throws StartException { class RequestScoped implements WasmPluginPool { final WasmPluginFactory factory; + private final String name; public RequestScoped(WasmPluginFactory factory, WasmPlugin plugin) { this.factory = factory; + this.name = plugin.name(); release(plugin); } + @Override + public String name() { + return this.name; + } + @Override public WasmPlugin borrow() throws StartException { - return factory.create(); + WasmPlugin plugin = factory.create(); + plugin.wasm.start(); + return plugin; } // Return the plugin to the pool @@ -47,7 +76,7 @@ public WasmPlugin borrow() throws StartException { public void release(WasmPlugin plugin) { // TODO: maybe implementing pooling in the future to reduce GC pressure // but for now, we just close the plugin - plugin.proxyWasm().close(); + plugin.close(); } } } 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 index 9b62c43..6c363b2 100644 --- 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 @@ -2,38 +2,25 @@ import io.roastedroot.proxywasm.jaxrs.spi.HttpServer; import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Alternative; -import jakarta.enterprise.inject.Instance; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.ws.rs.core.Context; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; @Alternative @Priority(100) +@ApplicationScoped 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(); - } + ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); @Override - public String localPort() { - return "" + request.getLocalPort(); + public Runnable scheduleTick(long delay, Runnable task) { + var f = executorService.scheduleAtFixedRate(task, delay, delay, TimeUnit.MILLISECONDS); + return () -> { + ; + f.cancel(false); + }; } } diff --git a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/servlet/ServletHttpServerRequest.java b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/servlet/ServletHttpServerRequest.java new file mode 100644 index 0000000..6d56106 --- /dev/null +++ b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/servlet/ServletHttpServerRequest.java @@ -0,0 +1,42 @@ +package io.roastedroot.proxywasm.jaxrs.servlet; + +import io.roastedroot.proxywasm.jaxrs.spi.HttpServerRequest; +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; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +@Alternative +@Priority(100) +public class ServletHttpServerRequest implements HttpServerRequest { + + private final HttpServletRequest request; + ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); + + public ServletHttpServerRequest(@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 index 720f74e..5d34bb5 100644 --- 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 @@ -5,11 +5,5 @@ */ public interface HttpServer { - String remoteAddress(); - - String remotePort(); - - String localAddress(); - - String localPort(); + Runnable scheduleTick(long delay, Runnable task); } diff --git a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/spi/HttpServerRequest.java b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/spi/HttpServerRequest.java new file mode 100644 index 0000000..65c1ff7 --- /dev/null +++ b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/spi/HttpServerRequest.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 HttpServerRequest { + + 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 index 97d3b5a..f5e610b 100644 --- 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 @@ -1,41 +1,24 @@ package io.roastedroot.proxywasm.jaxrs.vertx; import io.roastedroot.proxywasm.jaxrs.spi.HttpServer; -import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.Vertx; import jakarta.annotation.Priority; -import jakarta.enterprise.context.RequestScoped; +import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Alternative; -import jakarta.enterprise.inject.Instance; -import jakarta.ws.rs.core.Context; +import jakarta.inject.Inject; @Alternative @Priority(200) -@RequestScoped +@ApplicationScoped 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(); - } + @Inject Vertx vertx; @Override - public String localPort() { - return "" + request.localAddress().port(); + public Runnable scheduleTick(long delay, Runnable task) { + var id = vertx.setPeriodic(delay, x -> task.run()); + return () -> { + vertx.cancelTimer(id); + }; } } diff --git a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/vertx/VertxHttpServerRequest.java b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/vertx/VertxHttpServerRequest.java new file mode 100644 index 0000000..3ca426e --- /dev/null +++ b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/vertx/VertxHttpServerRequest.java @@ -0,0 +1,42 @@ +package io.roastedroot.proxywasm.jaxrs.vertx; + +import io.roastedroot.proxywasm.jaxrs.spi.HttpServerRequest; +import jakarta.annotation.Priority; +import jakarta.enterprise.context.RequestScoped; +import jakarta.enterprise.inject.Alternative; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; +import jakarta.ws.rs.core.Context; + +@Alternative +@Priority(200) +@RequestScoped +public class VertxHttpServerRequest implements HttpServerRequest { + + private final io.vertx.core.http.HttpServerRequest request; + + @Inject + public VertxHttpServerRequest(@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(); + } +} diff --git a/proxy-wasm-jaxrs/src/test/java/io/roastedroot/proxywasm/jaxrs/ForeignCallOnTickTest.java b/proxy-wasm-jaxrs/src/test/java/io/roastedroot/proxywasm/jaxrs/ForeignCallOnTickTest.java new file mode 100644 index 0000000..3962461 --- /dev/null +++ b/proxy-wasm-jaxrs/src/test/java/io/roastedroot/proxywasm/jaxrs/ForeignCallOnTickTest.java @@ -0,0 +1,46 @@ +package io.roastedroot.proxywasm.jaxrs; + +import static io.roastedroot.proxywasm.jaxrs.TestHelpers.parseTestModule; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import io.quarkus.test.junit.QuarkusTest; +import io.roastedroot.proxywasm.StartException; +import jakarta.enterprise.inject.Produces; +import jakarta.inject.Inject; +import java.util.Map; +import org.junit.jupiter.api.Test; + +@QuarkusTest +public class ForeignCallOnTickTest { + + @Produces + public WasmPluginFactory create() throws StartException { + return () -> + WasmPlugin.builder() + .withName("foreignCallOnTickTest") + .withLogger(new MockLogger()) + .withMinTickPeriodMilliseconds( + 100) // plugin wants a tick every 1 ms, that's too often + .withForeignFunctions(Map.of("compress", data -> data)) + .build(parseTestModule("/go-examples/foreign_call_on_tick/main.wasm")); + } + + @Inject WasmPluginFeature feature; + + @Test + public void testRequest() throws InterruptedException, StartException { + WasmPlugin plugin = feature.pool("foreignCallOnTickTest").borrow(); + assertNotNull(plugin); + assertEquals(1, plugin.handler.getTickPeriodMilliseconds()); + + var logger = (MockLogger) plugin.handler.logger; + Thread.sleep(200); + logger.assertLogsContain( + String.format( + "foreign function (compress) called: %d, result: %s", + 1, "68656c6c6f20776f726c6421")); + plugin.handler.logger = null; + plugin.close(); // so that the ticks don't keep running in the background. + } +}