4
4
5
5
import io .roastedroot .proxywasm .StartException ;
6
6
import io .roastedroot .proxywasm .internal .Action ;
7
+ import io .roastedroot .proxywasm .internal .HttpRequestBody ;
7
8
import io .roastedroot .proxywasm .internal .Plugin ;
8
9
import io .roastedroot .proxywasm .internal .PluginHttpContext ;
9
10
import io .roastedroot .proxywasm .internal .Pool ;
25
26
import java .util .logging .Logger ;
26
27
27
28
/**
28
- * Implements the JAX-RS {@link ContainerRequestFilter}, {@link ContainerResponseFilter},
29
- * and {@link WriterInterceptor} interfaces to intercept HTTP requests and responses,
29
+ * Implements the JAX-RS {@link ContainerRequestFilter},
30
+ * {@link ContainerResponseFilter},
31
+ * and {@link WriterInterceptor} interfaces to intercept HTTP requests and
32
+ * responses,
30
33
* allowing Proxy-Wasm plugins to process them.
31
34
*
32
- * <p>This filter is registered by the {@link ProxyWasmFeature}. It interacts with
33
- * {@link Plugin} instances obtained from configured {@link Pool}s to execute the
35
+ * <p>
36
+ * This filter is registered by the {@link ProxyWasmFeature}. It interacts with
37
+ * {@link Plugin} instances obtained from configured {@link Pool}s to execute
38
+ * the
34
39
* appropriate Proxy-Wasm ABI functions (e.g., {@code on_http_request_headers},
35
- * {@code on_http_response_body}) at different stages of the JAX-RS request/response lifecycle.
40
+ * {@code on_http_response_body}) at different stages of the JAX-RS
41
+ * request/response lifecycle.
36
42
*
37
43
* @see ProxyWasmFeature
38
44
* @see ProxyWasm
@@ -50,7 +56,8 @@ public class ProxyWasmFilter
50
56
/**
51
57
* Constructs a ProxyWasmFilter.
52
58
*
53
- * @param pluginPools A list of {@link Pool} instances, each managing a pool of {@link Plugin}
59
+ * @param pluginPools A list of {@link Pool} instances, each managing a pool of
60
+ * {@link Plugin}
54
61
* instances for a specific Wasm module.
55
62
*/
56
63
public ProxyWasmFilter (List <Pool > pluginPools ) {
@@ -76,10 +83,15 @@ public void release() {
76
83
/**
77
84
* Intercepts incoming JAX-RS requests before they reach the resource method.
78
85
*
79
- * <p>This method iterates through the configured plugin pools, borrows a {@link Plugin}
80
- * instance from each, creates a {@link PluginHttpContext}, and calls the plugin's
81
- * {@code on_http_request_headers} and potentially {@code on_http_request_body} functions.
82
- * It handles potential early responses or modifications dictated by the plugins.
86
+ * <p>
87
+ * This method iterates through the configured plugin pools, borrows a
88
+ * {@link Plugin}
89
+ * instance from each, creates a {@link PluginHttpContext}, and calls the
90
+ * plugin's
91
+ * {@code on_http_request_headers} and potentially {@code on_http_request_body}
92
+ * functions.
93
+ * It handles potential early responses or modifications dictated by the
94
+ * plugins.
83
95
*
84
96
* @param requestContext The JAX-RS request context.
85
97
* @throws IOException If an I/O error occurs, typically during body processing.
@@ -89,6 +101,7 @@ public void filter(ContainerRequestContext requestContext) throws IOException {
89
101
90
102
ArrayList <FilterContext > filterContexts = new ArrayList <>();
91
103
requestContext .setProperty (FILTER_CONTEXT , filterContexts );
104
+
92
105
for (var pluginPool : pluginPools ) {
93
106
try {
94
107
Plugin plugin = pluginPool .borrow ();
@@ -100,6 +113,7 @@ public void filter(ContainerRequestContext requestContext) throws IOException {
100
113
serverAdaptor .httpRequestAdaptor (requestContext );
101
114
requestAdaptor .setRequestContext (requestContext );
102
115
var httpContext = plugin .createHttpContext (requestAdaptor );
116
+
103
117
filterContexts .add (new FilterContext (pluginPool , plugin , httpContext ));
104
118
} finally {
105
119
plugin .unlock ();
@@ -116,6 +130,17 @@ public void filter(ContainerRequestContext requestContext) throws IOException {
116
130
return ;
117
131
}
118
132
}
133
+
134
+ // Create a shared lazy body supplier for all plugins
135
+ HttpRequestBody bodySupplier = new HttpRequestBody (() -> requestContext .getEntityStream ());
136
+
137
+ // Set up lazy providers for all plugins that need the body
138
+ for (var filterContext : filterContexts ) {
139
+ if (filterContext .httpContext .context ().hasOnRequestBody ()) {
140
+ filterContext .httpContext .setHttpRequestBodyState (bodySupplier );
141
+ }
142
+ }
143
+
119
144
for (var filterContext : filterContexts ) {
120
145
filter (requestContext , filterContext );
121
146
}
@@ -147,29 +172,30 @@ private void filter(ContainerRequestContext requestContext, FilterContext filter
147
172
// the plugin may not be interested in the request body.
148
173
if (httpContext .context ().hasOnRequestBody ()) {
149
174
150
- // TODO: find out if it's more efficient to read the body in chunks and do multiple
151
- // callOnRequestBody calls.
152
- byte [] bytes = requestContext .getEntityStream ().readAllBytes ();
175
+ HttpRequestBody httpRequestBodyState = httpContext .getHttpRequestBodyState ();
153
176
154
- httpContext .setHttpRequestBody (bytes );
155
- var action = httpContext .context ().callOnRequestBody (true );
156
- bytes = httpContext .getHttpRequestBody ();
157
- if (action == Action .CONTINUE ) {
158
- // continue means plugin is done reading the body.
159
- httpContext .setHttpRequestBody (null );
160
- } else {
177
+ while (true ) {
178
+ // if we streamed body updates, then endOfStream would be initially false
179
+ var action = httpContext .context ().callOnRequestBody (true );
180
+
181
+ // does the plugin want to respond early?
182
+ var sendResponse = httpContext .consumeSentHttpResponse ();
183
+ if (sendResponse != null ) {
184
+ requestContext .abortWith (toResponse (sendResponse ));
185
+ return ;
186
+ }
187
+
188
+ if (action == Action .CONTINUE ) {
189
+ break ;
190
+ }
161
191
httpContext .maybePause ();
162
192
}
163
193
164
- // does the plugin want to respond early?
165
- var sendResponse = httpContext .consumeSentHttpResponse ();
166
- if (sendResponse != null ) {
167
- requestContext .abortWith (toResponse (sendResponse ));
168
- return ;
194
+ // Body was accessed and potentially modified, update the request stream
195
+ if (httpRequestBodyState .isLoaded ()) {
196
+ byte [] bytes = httpRequestBodyState .getBodyIfLoaded ();
197
+ requestContext .setEntityStream (new java .io .ByteArrayInputStream (bytes ));
169
198
}
170
-
171
- // plugin may have modified the body
172
- requestContext .setEntityStream (new java .io .ByteArrayInputStream (bytes ));
173
199
}
174
200
175
201
} finally {
@@ -184,10 +210,14 @@ private static Response internalServerError() {
184
210
/**
185
211
* Intercepts outgoing JAX-RS responses before the entity is written.
186
212
*
187
- * <p>This method iterates through the configured plugin pools, retrieves the
188
- * {@link PluginHttpContext} created during the request phase, and calls the plugin's
189
- * {@code on_http_response_headers} function. It handles potential modifications to the
190
- * response headers dictated by the plugins. If the response has no entity but the plugin
213
+ * <p>
214
+ * This method iterates through the configured plugin pools, retrieves the
215
+ * {@link PluginHttpContext} created during the request phase, and calls the
216
+ * plugin's
217
+ * {@code on_http_response_headers} function. It handles potential modifications
218
+ * to the
219
+ * response headers dictated by the plugins. If the response has no entity but
220
+ * the plugin
191
221
* implements {@code on_http_response_body}, it invokes that callback as well.
192
222
*
193
223
* @param requestContext The JAX-RS request context.
@@ -274,15 +304,20 @@ private void filter(
274
304
/**
275
305
* Intercepts the response body writing process.
276
306
*
277
- * <p>This method is called when the JAX-RS framework is about to serialize and write
307
+ * <p>
308
+ * This method is called when the JAX-RS framework is about to serialize and
309
+ * write
278
310
* the response entity. It captures the original response body, allows plugins
279
- * (via {@code on_http_response_body}) to inspect or modify it, and then writes the
311
+ * (via {@code on_http_response_body}) to inspect or modify it, and then writes
312
+ * the
280
313
* potentially modified body to the original output stream. It handles potential
281
314
* early responses dictated by the plugins during body processing.
282
315
*
283
316
* @param ctx The JAX-RS writer interceptor context.
284
- * @throws IOException If an I/O error occurs during stream processing.
285
- * @throws WebApplicationException If a plugin decides to abort processing and send an
317
+ * @throws IOException If an I/O error occurs during stream
318
+ * processing.
319
+ * @throws WebApplicationException If a plugin decides to abort processing and
320
+ * send an
286
321
* alternative response during body filtering.
287
322
*/
288
323
@ Override
0 commit comments