From caa6da83315e86df23bab77c2599f089d0e64010 Mon Sep 17 00:00:00 2001 From: baranowb Date: Fri, 18 Oct 2024 11:27:45 +0200 Subject: [PATCH] [UNDERTOW-2509] Add 413 response code to overflowing multipart in default handler --- .../form/MultiPartParserDefinition.java | 4 + .../form/MultipartFormDataParserTestCase.java | 94 +++++++++++++++---- 2 files changed, 82 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/io/undertow/server/handlers/form/MultiPartParserDefinition.java b/core/src/main/java/io/undertow/server/handlers/form/MultiPartParserDefinition.java index 2a78e203b6..66269c64fd 100644 --- a/core/src/main/java/io/undertow/server/handlers/form/MultiPartParserDefinition.java +++ b/core/src/main/java/io/undertow/server/handlers/form/MultiPartParserDefinition.java @@ -480,6 +480,10 @@ public void handleEvent(StreamSourceChannel channel) { pooled.close(); } + } catch(FileTooLargeException e) { + UndertowLogger.REQUEST_IO_LOGGER.debug("Exception parsing data", e); + exchange.setStatusCode(StatusCodes.REQUEST_ENTITY_TOO_LARGE); + exchange.endExchange(); } catch (Throwable e) { UndertowLogger.REQUEST_IO_LOGGER.debug("Exception parsing data", e); exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR); diff --git a/core/src/test/java/io/undertow/server/handlers/form/MultipartFormDataParserTestCase.java b/core/src/test/java/io/undertow/server/handlers/form/MultipartFormDataParserTestCase.java index c6ab4c792b..7d6d02bce4 100644 --- a/core/src/test/java/io/undertow/server/handlers/form/MultipartFormDataParserTestCase.java +++ b/core/src/test/java/io/undertow/server/handlers/form/MultipartFormDataParserTestCase.java @@ -49,6 +49,7 @@ import io.undertow.server.HttpHandler; import io.undertow.server.HttpServerExchange; import io.undertow.server.handlers.BlockingHandler; +import io.undertow.server.handlers.form.MultiPartParserDefinition.FileTooLargeException; import io.undertow.testutils.DefaultServer; import io.undertow.testutils.HttpClientUtils; import io.undertow.testutils.TestHttpClient; @@ -198,33 +199,44 @@ public void testFileUploadWithEagerParsingAndNonASCIIFilename() throws Exception client.getConnectionManager().shutdown(); } } - private static HttpHandler createInMemoryReadingHandler(final long fileSizeThreshold) { + return createInMemoryReadingHandler(fileSizeThreshold, -1, null); + } + + private static HttpHandler createInMemoryReadingHandler(final long fileSizeThreshold, final long maxInvidualFileThreshold, final HttpHandler async) { return new HttpHandler() { @Override public void handleRequest(final HttpServerExchange exchange) throws Exception { MultiPartParserDefinition multiPartParserDefinition = new MultiPartParserDefinition(); multiPartParserDefinition.setFileSizeThreshold(fileSizeThreshold); + multiPartParserDefinition.setMaxIndividualFileSize(maxInvidualFileThreshold); final FormDataParser parser = FormParserFactory.builder(false) .addParsers(new FormEncodedDataDefinition(), multiPartParserDefinition) .build().createParser(exchange); - try { - FormData data = parser.parseBlocking(); - exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR); - if (data.getFirst("formValue").getValue().equals("myValue")) { - FormData.FormValue file = data.getFirst("file"); - if (file.isFileItem()) { - exchange.setStatusCode(StatusCodes.OK); - logResult(exchange, file.getFileItem().isInMemory(), file.getFileName(), stream2String(file)); + if (async == null) { + try { + FormData data = parser.parseBlocking(); + exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR); + if (data.getFirst("formValue").getValue().equals("myValue")) { + FormData.FormValue file = data.getFirst("file"); + if (file.isFileItem()) { + exchange.setStatusCode(StatusCodes.OK); + logResult(exchange, file.getFileItem().isInMemory(), file.getFileName(), stream2String(file)); + } } + exchange.endExchange(); + } catch (FileTooLargeException e) { + exchange.setStatusCode(StatusCodes.REQUEST_ENTITY_TOO_LARGE); + exchange.endExchange(); + } catch (Throwable e) { + e.printStackTrace(); + exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR); + exchange.endExchange(); + } finally { + IoUtils.safeClose(parser); } - exchange.endExchange(); - } catch (Throwable e) { - e.printStackTrace(); - exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR); - exchange.endExchange(); - } finally { - IoUtils.safeClose(parser); + } else { + parser.parse(async); } } @@ -373,6 +385,56 @@ public void testLargeContentWithoutFileNameWithSmallFileSizeThreshold() throws E } } + @Test + public void testFileUploadWithFileSizeThresholdOverflow_Sync() throws Exception { + DefaultServer.setRootHandler(new BlockingHandler(createInMemoryReadingHandler(10, 1, null))); + + TestHttpClient client = new TestHttpClient(); + try { + + HttpPost post = new HttpPost(DefaultServer.getDefaultServerURL() + "/path"); + MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE); + + entity.addPart("formValue", new StringBody("myValue", "text/plain", StandardCharsets.UTF_8)); + entity.addPart("file", new FileBody(new File(MultipartFormDataParserTestCase.class.getResource("uploadfile.txt").getFile()))); + + post.setEntity(entity); + HttpResponse result = client.execute(post); + Assert.assertEquals(StatusCodes.REQUEST_ENTITY_TOO_LARGE, result.getStatusLine().getStatusCode()); + + } finally { + client.getConnectionManager().shutdown(); + } + } + + @Test + public void testFileUploadWithFileSizeThresholdOverflow_ASync() throws Exception { + DefaultServer.setRootHandler(new BlockingHandler(createInMemoryReadingHandler(10, 1, new HttpHandler() { + + @Override + public void handleRequest(HttpServerExchange exchange) throws Exception { + throw new Exception(); + } + }))); + + TestHttpClient client = new TestHttpClient(); + try { + + HttpPost post = new HttpPost(DefaultServer.getDefaultServerURL() + "/path"); + MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE); + + entity.addPart("formValue", new StringBody("myValue", "text/plain", StandardCharsets.UTF_8)); + entity.addPart("file", new FileBody(new File(MultipartFormDataParserTestCase.class.getResource("uploadfile.txt").getFile()))); + + post.setEntity(entity); + HttpResponse result = client.execute(post); + Assert.assertEquals(StatusCodes.REQUEST_ENTITY_TOO_LARGE, result.getStatusLine().getStatusCode()); + + } finally { + client.getConnectionManager().shutdown(); + } + } + private void writeLargeFileContent(File file, int size) throws IOException { int textLength = "content".getBytes().length; FileOutputStream fos = new FileOutputStream(file);