|
25 | 25 | import org.eclipse.jetty.http.MultiPartConfig; |
26 | 26 | import org.eclipse.jetty.http.MultiPartFormData; |
27 | 27 | import org.eclipse.jetty.http.MultiPartFormData.ContentSource; |
28 | | -import org.eclipse.jetty.http.MultiPartFormData.Parts; |
29 | 28 | import org.eclipse.jetty.io.Content; |
30 | 29 | import org.eclipse.jetty.io.Content.Source; |
31 | 30 | import org.eclipse.jetty.server.Handler; |
@@ -104,15 +103,6 @@ public void start( NGApplication application ) { |
104 | 103 |
|
105 | 104 | public class NGHandler extends Handler.Abstract { |
106 | 105 |
|
107 | | - /** |
108 | | - * The directory used by Jetty to store cached data during processing of multipart requests |
109 | | - * |
110 | | - * FIXME: This should probably be specified in a property // Hugi 2025-06-17 |
111 | | - */ |
112 | | - private static final String multipartTemporaryDirectory() { |
113 | | - return "/tmp/ngmultijet"; |
114 | | - } |
115 | | - |
116 | 106 | @Override |
117 | 107 | public boolean handle( Request request, Response response, Callback callback ) throws Exception { |
118 | 108 | doRequest( request, response, callback ); |
@@ -228,53 +218,53 @@ private static HttpCookie ngCookieToJettyCookie( final NGCookie ngCookie ) { |
228 | 218 | return jettyCookieBuilder.build(); |
229 | 219 | } |
230 | 220 |
|
| 221 | + /** |
| 222 | + * @return Jetty's configuration for handling of multipart request parsing |
| 223 | + * |
| 224 | + * FIXME: MultiPart configuration needs to be configurable on ng's side. Or we need to expose the Jetty configuration (which I'd prefer not to, since that makes configuration implementation dependent) // Hugi 2025-06-17 |
| 225 | + */ |
| 226 | + private static MultiPartConfig multiPartConfig() { |
| 227 | + return new MultiPartConfig.Builder() |
| 228 | + .location( Path.of( "/tmp/ngmultijet" ) ) // Path to a directory used by Jetty to store cached data during processing of multipart requests |
| 229 | + .build(); |
| 230 | + } |
| 231 | + |
231 | 232 | /** |
232 | 233 | * @return the given Request converted to an NGRequest |
233 | 234 | */ |
234 | 235 | private static NGRequest multipartRequestToNGRequest( final Request jettyRequest, final String contentType, final Callback callback ) { |
235 | 236 |
|
236 | | - final MultiPartConfig config = new MultiPartConfig.Builder() |
237 | | - .location( Path.of( multipartTemporaryDirectory() ) ) |
238 | | - .build(); |
239 | | - |
240 | | - // The formValues that will get set on the request |
| 237 | + // Regular formValues to set on the request |
241 | 238 | final Map<String, List<String>> formValues = new HashMap<>(); |
242 | 239 |
|
243 | | - // The uploaded files, if any |
| 240 | + // Uploaded files to set on the request |
244 | 241 | final Map<String, UploadedFile> uploadedFiles = new HashMap<>(); |
245 | 242 |
|
246 | | - final Parts parts = MultiPartFormData.getParts( jettyRequest, jettyRequest, contentType, config ); |
247 | | - |
248 | | - parts.forEach( p -> { |
249 | | - final String partContentType = p.getHeaders().get( HttpHeader.CONTENT_TYPE ); |
250 | | - |
251 | | - final String parameterName = p.getName(); |
252 | | - final String parameterValue; |
253 | | - |
254 | | - // We're assuming that if this part does not have a content type, it's a regular ol' form value, to be added to the requests formValues map as usual. |
255 | | - if( partContentType == null ) { |
256 | | - parameterValue = p.getContentAsString( StandardCharsets.UTF_8 ); // FIXME: Hardcoding the character set is a little presumptuous // Hugi 2025-04-05 |
257 | | - } |
258 | | - else { |
259 | | - // We're generating a unique ID here to store the attachment under in the request. This value will be stored in the request's formValues, and can be used to fetch the uploaded data in the request's uploadedFiles map |
260 | | - final String uniqueID = UUID.randomUUID().toString(); |
261 | | - |
262 | | - parameterValue = uniqueID; |
263 | | - |
264 | | - // Now we add the uploaded file to the request |
265 | | - final UploadedFile file = new UploadedFile( p.getFileName(), partContentType, Content.Source.asInputStream( p.getContentSource() ), p.getLength() ); |
266 | | - uploadedFiles.put( uniqueID, file ); |
267 | | - } |
268 | | - |
269 | | - List<String> list = formValues.get( parameterName ); |
270 | | - |
271 | | - if( list == null ) { |
272 | | - list = new ArrayList<>(); |
273 | | - formValues.put( p.getName(), list ); |
274 | | - } |
275 | | - |
276 | | - list.add( parameterValue ); |
277 | | - } ); |
| 243 | + MultiPartFormData |
| 244 | + .getParts( jettyRequest, jettyRequest, contentType, multiPartConfig() ) |
| 245 | + .forEach( part -> { |
| 246 | + final String partContentType = part.getHeaders().get( HttpHeader.CONTENT_TYPE ); |
| 247 | + |
| 248 | + final String parameterValue; |
| 249 | + |
| 250 | + if( partContentType == null ) { |
| 251 | + // If this part does not have a content type, we treat it like a regular ol' form value, added to the request's formValues map as usual |
| 252 | + parameterValue = part.getContentAsString( StandardCharsets.UTF_8 ); // FIXME: Hardcoding the character set is a little presumptuous // Hugi 2025-04-05 |
| 253 | + } |
| 254 | + else { |
| 255 | + // We generate a unique ID to store the attachment under. The ID will be stored as a value under the part's name in the request's formValues, where it can be used to obtaing the uploaded file from the request's uploadedFiles map |
| 256 | + parameterValue = UUID.randomUUID().toString(); |
| 257 | + |
| 258 | + // Now we add the uploaded file to the request |
| 259 | + final UploadedFile file = new UploadedFile( part.getFileName(), partContentType, Content.Source.asInputStream( part.getContentSource() ), part.getLength() ); |
| 260 | + uploadedFiles.put( parameterValue, file ); |
| 261 | + } |
| 262 | + |
| 263 | + // Finally, we add our "value" to the request's form values |
| 264 | + formValues |
| 265 | + .computeIfAbsent( part.getName(), _unused -> new ArrayList<>() ) |
| 266 | + .add( parameterValue ); |
| 267 | + } ); |
278 | 268 |
|
279 | 269 | final String method = jettyRequest.getMethod(); |
280 | 270 | final String uri = jettyRequest.getHttpURI().getCanonicalPath(); |
|
0 commit comments