Skip to content

Commit 915bd54

Browse files
committed
feat(jaxrs): add support for tick callbacks and FFI calls.
Signed-off-by: Hiram Chirino <hiram@hiramchirino.com>
1 parent 40d876e commit 915bd54

File tree

15 files changed

+330
-113
lines changed

15 files changed

+330
-113
lines changed

proxy-wasm-java-host/src/main/java/io/roastedroot/proxywasm/ProxyWasm.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ private ProxyWasm(Builder other) throws StartException {
5959

6060
public void start() throws StartException {
6161
if (pluginContext != null) {
62-
throw new IllegalStateException("already started");
62+
return;
6363
}
6464

6565
this.pluginContext = new PluginContext(this, pluginHandler);

proxy-wasm-jaxrs/pom.xml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,8 @@
114114
<executions>
115115
<execution>
116116
<goals>
117-
<goal>build</goal>
118117
<goal>generate-code</goal>
119118
<goal>generate-code-tests</goal>
120-
<goal>native-image-agent</goal>
121119
</goals>
122120
</execution>
123121
</executions>

proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/HttpHandler.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
import io.roastedroot.proxywasm.StreamType;
5151
import io.roastedroot.proxywasm.WasmException;
5252
import io.roastedroot.proxywasm.WasmResult;
53-
import io.roastedroot.proxywasm.jaxrs.spi.HttpServer;
53+
import io.roastedroot.proxywasm.jaxrs.spi.HttpServerRequest;
5454
import jakarta.ws.rs.container.ContainerRequestContext;
5555
import jakarta.ws.rs.container.ContainerResponseContext;
5656
import jakarta.ws.rs.core.Response;
@@ -64,10 +64,10 @@
6464
class HttpHandler extends ChainedHandler {
6565

6666
private final PluginHandler next;
67-
private final HttpServer httpServer;
67+
private final HttpServerRequest httpServer;
6868
private final long startedAt;
6969

70-
HttpHandler(PluginHandler pluginHandler, HttpServer httpServer) {
70+
HttpHandler(PluginHandler pluginHandler, HttpServerRequest httpServer) {
7171
this.next = pluginHandler;
7272
this.httpServer = httpServer;
7373
this.startedAt = System.currentTimeMillis();

proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/PluginHandler.java

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class PluginHandler extends ChainedHandler {
2323
// Filter Chain Methods
2424
// //////////////////////////////////////////////////////////////////////
2525
private Handler next;
26+
WasmPlugin plugin;
2627

2728
PluginHandler() {
2829
this(new Handler() {});
@@ -37,6 +38,16 @@ protected Handler next() {
3738
return next;
3839
}
3940

41+
// //////////////////////////////////////////////////////////////////////
42+
// Cleanup
43+
// //////////////////////////////////////////////////////////////////////
44+
public void close() {
45+
if (cancelTick != null) {
46+
cancelTick.run();
47+
cancelTick = null;
48+
}
49+
}
50+
4051
// //////////////////////////////////////////////////////////////////////
4152
// Properties
4253
// //////////////////////////////////////////////////////////////////////
@@ -103,15 +114,46 @@ public LogLevel getLogLevel() throws WasmException {
103114
// Timers
104115
// //////////////////////////////////////////////////////////////////////
105116

117+
int minTickPeriodMilliseconds;
106118
private int tickPeriodMilliseconds;
119+
private Runnable cancelTick;
107120

108121
public int getTickPeriodMilliseconds() {
109122
return tickPeriodMilliseconds;
110123
}
111124

112125
@Override
113126
public WasmResult setTickPeriodMilliseconds(int tickPeriodMilliseconds) {
127+
128+
// check for no change
129+
if (tickPeriodMilliseconds == this.tickPeriodMilliseconds) {
130+
return WasmResult.OK;
131+
}
132+
133+
// cancel the current tick, if any
134+
if (cancelTick != null) {
135+
cancelTick.run();
136+
cancelTick = null;
137+
}
138+
139+
// set the new tick period, if any
114140
this.tickPeriodMilliseconds = tickPeriodMilliseconds;
141+
if (this.tickPeriodMilliseconds == 0) {
142+
return WasmResult.OK;
143+
}
144+
145+
// schedule the new tick
146+
this.cancelTick =
147+
this.plugin.httpServer.scheduleTick(
148+
Math.max(minTickPeriodMilliseconds, this.tickPeriodMilliseconds),
149+
() -> {
150+
this.plugin.lock();
151+
try {
152+
this.plugin.wasm.tick();
153+
} finally {
154+
this.plugin.unlock();
155+
}
156+
});
115157
return WasmResult.OK;
116158
}
117159

@@ -132,6 +174,10 @@ public WasmResult setFuncCallData(byte[] data) {
132174
return WasmResult.OK;
133175
}
134176

177+
public void setPlugin(WasmPlugin plugin) {
178+
this.plugin = plugin;
179+
}
180+
135181
// //////////////////////////////////////////////////////////////////////
136182
// HTTP calls
137183
// //////////////////////////////////////////////////////////////////////
@@ -296,6 +342,9 @@ public WasmResult removeMetric(int metricId) {
296342

297343
@Override
298344
public ForeignFunction getForeignFunction(String name) {
299-
return super.getForeignFunction(name);
345+
if (foreignFunctions == null) {
346+
return null;
347+
}
348+
return foreignFunctions.get(name);
300349
}
301350
}

proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/ProxyWasmFilter.java

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@
33
import io.roastedroot.proxywasm.Action;
44
import io.roastedroot.proxywasm.HttpContext;
55
import io.roastedroot.proxywasm.StartException;
6-
import io.roastedroot.proxywasm.jaxrs.spi.HttpServer;
6+
import io.roastedroot.proxywasm.jaxrs.spi.HttpServerRequest;
77
import jakarta.enterprise.inject.Instance;
8-
import jakarta.inject.Inject;
98
import jakarta.ws.rs.WebApplicationException;
109
import jakarta.ws.rs.container.ContainerRequestContext;
1110
import jakarta.ws.rs.container.ContainerRequestFilter;
1211
import jakarta.ws.rs.container.ContainerResponseContext;
1312
import jakarta.ws.rs.container.ContainerResponseFilter;
14-
import jakarta.ws.rs.container.PreMatching;
1513
import jakarta.ws.rs.core.Response;
1614
import jakarta.ws.rs.ext.ReaderInterceptor;
1715
import jakarta.ws.rs.ext.ReaderInterceptorContext;
@@ -20,7 +18,6 @@
2018
import java.io.ByteArrayOutputStream;
2119
import java.io.IOException;
2220

23-
@PreMatching
2421
public class ProxyWasmFilter
2522
implements ContainerRequestFilter,
2623
ReaderInterceptor,
@@ -30,14 +27,17 @@ public class ProxyWasmFilter
3027

3128
private final WasmPluginPool pluginPool;
3229

33-
Instance<HttpServer> httpServer;
30+
Instance<HttpServerRequest> httpServer;
3431

35-
@Inject
36-
public ProxyWasmFilter(WasmPluginPool pluginPool, Instance<HttpServer> httpServer) {
32+
public ProxyWasmFilter(WasmPluginPool pluginPool, Instance<HttpServerRequest> httpServer) {
3733
this.pluginPool = pluginPool;
3834
this.httpServer = httpServer;
3935
}
4036

37+
String name() {
38+
return pluginPool.name();
39+
}
40+
4141
// TODO: the HttpContext and ProxyWasm object's should be closed once the request is done.
4242
// is there an easy way to hook up cleanup code for this?
4343
static class WasmHttpFilterContext {
@@ -46,22 +46,27 @@ static class WasmHttpFilterContext {
4646
final HttpHandler httpHandler;
4747
final HttpContext httpContext;
4848

49-
public WasmHttpFilterContext(WasmPlugin plugin, HttpServer httpServer) {
49+
public WasmHttpFilterContext(WasmPlugin plugin, HttpServerRequest httpServer) {
5050
this.plugin = plugin;
51-
this.pluginHandler = plugin.pluginHandler();
52-
this.httpHandler = new HttpHandler(plugin.pluginHandler(), httpServer);
53-
this.httpContext = plugin.proxyWasm().createHttpContext(this.httpHandler);
51+
this.pluginHandler = plugin.handler;
52+
this.httpHandler = new HttpHandler(plugin.handler, httpServer);
53+
this.httpContext = plugin.wasm.createHttpContext(this.httpHandler);
5454
}
5555
}
5656

5757
@Override
5858
public void filter(ContainerRequestContext requestContext) throws IOException {
5959

60-
WasmPlugin plugin = null;
60+
WasmPlugin plugin;
6161
try {
6262
plugin = pluginPool.borrow();
63-
plugin.lock();
63+
} catch (StartException e) {
64+
requestContext.abortWith(interalServerError());
65+
return;
66+
}
6467

68+
plugin.lock();
69+
try {
6570
var ctx = new WasmHttpFilterContext(plugin, this.httpServer.get());
6671
requestContext.setProperty(FILTER_CONTEXT_PROPERTY_NAME, ctx);
6772

@@ -81,24 +86,23 @@ public void filter(ContainerRequestContext requestContext) throws IOException {
8186
requestContext.abortWith(sendResponse.toResponse());
8287
}
8388
}
84-
85-
} catch (StartException e) {
86-
requestContext.abortWith(
87-
Response.status(Response.Status.INTERNAL_SERVER_ERROR).build());
8889
} finally {
8990
plugin.unlock(); // allow another request to use the plugin.
9091
}
9192
}
9293

94+
private static Response interalServerError() {
95+
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
96+
}
97+
9398
@Override
9499
public Object aroundReadFrom(ReaderInterceptorContext ctx)
95100
throws IOException, WebApplicationException {
96101

97102
var wasmHttpFilterContext =
98103
(WasmHttpFilterContext) ctx.getProperty(FILTER_CONTEXT_PROPERTY_NAME);
99104
if (wasmHttpFilterContext == null) {
100-
throw new WebApplicationException(
101-
Response.status(Response.Status.INTERNAL_SERVER_ERROR).build());
105+
throw new WebApplicationException(interalServerError());
102106
}
103107

104108
// the plugin may not be interested in the request body.
@@ -147,8 +151,7 @@ public void filter(
147151
var wasmHttpFilterContext =
148152
(WasmHttpFilterContext) requestContext.getProperty(FILTER_CONTEXT_PROPERTY_NAME);
149153
if (wasmHttpFilterContext == null) {
150-
throw new WebApplicationException(
151-
Response.status(Response.Status.INTERNAL_SERVER_ERROR).build());
154+
throw new WebApplicationException(interalServerError());
152155
}
153156

154157
// the plugin may not be interested in the request headers.
@@ -182,8 +185,7 @@ public void aroundWriteTo(WriterInterceptorContext ctx)
182185
var wasmHttpFilterContext =
183186
(WasmHttpFilterContext) ctx.getProperty(FILTER_CONTEXT_PROPERTY_NAME);
184187
if (wasmHttpFilterContext == null) {
185-
throw new WebApplicationException(
186-
Response.status(Response.Status.INTERNAL_SERVER_ERROR).build());
188+
throw new WebApplicationException(interalServerError());
187189
}
188190

189191
try {

proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPlugin.java

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,37 +6,32 @@
66
import io.roastedroot.proxywasm.ForeignFunction;
77
import io.roastedroot.proxywasm.ProxyWasm;
88
import io.roastedroot.proxywasm.StartException;
9+
import io.roastedroot.proxywasm.jaxrs.spi.HttpServer;
910
import java.util.HashMap;
1011
import java.util.Map;
1112
import java.util.Objects;
1213
import java.util.concurrent.locks.ReentrantLock;
1314

1415
public class WasmPlugin {
1516

16-
private final ProxyWasm proxyWasm;
17-
private final PluginHandler handler;
17+
final PluginHandler handler;
1818
private final ReentrantLock lock;
19+
final ProxyWasm wasm;
20+
HttpServer httpServer;
1921

2022
private WasmPlugin(ProxyWasm proxyWasm, PluginHandler handler, boolean shared) {
2123
Objects.requireNonNull(proxyWasm);
2224
Objects.requireNonNull(handler);
23-
this.proxyWasm = proxyWasm;
25+
this.wasm = proxyWasm;
2426
this.handler = handler;
2527
this.lock = shared ? new ReentrantLock() : null;
28+
this.handler.setPlugin(this);
2629
}
2730

2831
public String name() {
2932
return handler.getName();
3033
}
3134

32-
ProxyWasm proxyWasm() {
33-
return proxyWasm;
34-
}
35-
36-
PluginHandler pluginHandler() {
37-
return handler;
38-
}
39-
4035
public static WasmPlugin.Builder builder() {
4136
return new WasmPlugin.Builder();
4237
}
@@ -59,10 +54,25 @@ public boolean isShared() {
5954
return lock != null;
6055
}
6156

57+
public void setHttpServer(HttpServer httpServer) {
58+
this.httpServer = httpServer;
59+
}
60+
61+
public void close() {
62+
lock();
63+
try {
64+
wasm.close();
65+
handler.close();
66+
} finally {
67+
unlock();
68+
}
69+
}
70+
6271
public static class Builder implements Cloneable {
6372

6473
private PluginHandler handler = new PluginHandler();
65-
private ProxyWasm.Builder proxyWasmBuilder = ProxyWasm.builder().withPluginHandler(handler);
74+
private ProxyWasm.Builder proxyWasmBuilder =
75+
ProxyWasm.builder().withPluginHandler(handler).withStart(false);
6676
private boolean shared = true;
6777

6878
public WasmPlugin.Builder withName(String name) {
@@ -75,6 +85,11 @@ public Builder withForeignFunctions(Map<String, ForeignFunction> functions) {
7585
return this;
7686
}
7787

88+
public Builder withMinTickPeriodMilliseconds(int minTickPeriodMilliseconds) {
89+
this.handler.minTickPeriodMilliseconds = minTickPeriodMilliseconds;
90+
return this;
91+
}
92+
7893
public Builder withLogger(Logger logger) {
7994
this.handler.logger = logger;
8095
return this;

0 commit comments

Comments
 (0)