diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartRequestContent.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartRequestContent.java index 0068a5692e7..eb5ddaede32 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartRequestContent.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/util/MultiPartRequestContent.java @@ -70,7 +70,7 @@ public class MultiPartRequestContent extends MultiPartFormData.ContentSource imp if (headers.contains(HttpHeader.CONTENT_TYPE)) return headers; - Content.Source partContent = part.getNewContent(); + Content.Source partContent = part.newContentSource(); if (partContent instanceof Request.Content requestContent) { String contentType = requestContent.getContentType(); diff --git a/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartRequestContentTest.java b/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartRequestContentTest.java index 8e20301db14..6ca29407eb9 100644 --- a/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartRequestContentTest.java +++ b/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/util/MultiPartRequestContentTest.java @@ -129,7 +129,7 @@ public class MultiPartRequestContentTest extends AbstractHttpClientServerTest int equal = contentType.lastIndexOf('='); Charset charset = Charset.forName(contentType.substring(equal + 1)); assertEquals(encoding, charset); - assertEquals(value, Content.Source.asString(part.getNewContent(), charset)); + assertEquals(value, Content.Source.asString(part.newContentSource(), charset)); } }); @@ -163,7 +163,7 @@ public class MultiPartRequestContentTest extends AbstractHttpClientServerTest MultiPart.Part part = parts.iterator().next(); assertEquals(name, part.getName()); assertEquals("text/plain", part.getHeaders().get(HttpHeader.CONTENT_TYPE)); - assertArrayEquals(data, Content.Source.asByteBuffer(part.getNewContent()).array()); + assertArrayEquals(data, Content.Source.asByteBuffer(part.newContentSource()).array()); } }); @@ -215,8 +215,8 @@ public class MultiPartRequestContentTest extends AbstractHttpClientServerTest assertEquals(name, part.getName()); assertEquals(contentType, part.getHeaders().get(HttpHeader.CONTENT_TYPE)); assertEquals(fileName, part.getFileName()); - assertEquals(data.length, part.getNewContent().getLength()); - assertArrayEquals(data, Content.Source.asByteBuffer(part.getNewContent()).array()); + assertEquals(data.length, part.newContentSource().getLength()); + assertArrayEquals(data, Content.Source.asByteBuffer(part.newContentSource()).array()); } }); @@ -271,8 +271,8 @@ public class MultiPartRequestContentTest extends AbstractHttpClientServerTest assertEquals(name, part.getName()); assertEquals(contentType, part.getHeaders().get(HttpHeader.CONTENT_TYPE)); assertEquals(tmpPath.getFileName().toString(), part.getFileName()); - assertEquals(Files.size(tmpPath), part.getNewContent().getLength()); - assertEquals(data, Content.Source.asString(part.getNewContent(), encoding)); + assertEquals(Files.size(tmpPath), part.newContentSource().getLength()); + assertEquals(data, Content.Source.asString(part.newContentSource(), encoding)); } }); @@ -323,14 +323,14 @@ public class MultiPartRequestContentTest extends AbstractHttpClientServerTest assertEquals(field, fieldPart.getName()); assertEquals(contentType, fieldPart.getHeaders().get(HttpHeader.CONTENT_TYPE)); - assertEquals(value, Content.Source.asString(fieldPart.getNewContent(), encoding)); + assertEquals(value, Content.Source.asString(fieldPart.newContentSource(), encoding)); assertEquals(headerValue, fieldPart.getHeaders().get(headerName)); assertEquals(fileField, filePart.getName()); assertEquals("application/octet-stream", filePart.getHeaders().get(HttpHeader.CONTENT_TYPE)); assertEquals(tmpPath.getFileName().toString(), filePart.getFileName()); - assertEquals(Files.size(tmpPath), filePart.getNewContent().getLength()); - assertArrayEquals(data, Content.Source.asByteBuffer(filePart.getNewContent()).array()); + assertEquals(Files.size(tmpPath), filePart.newContentSource().getLength()); + assertArrayEquals(data, Content.Source.asByteBuffer(filePart.newContentSource()).array()); } }); @@ -367,11 +367,11 @@ public class MultiPartRequestContentTest extends AbstractHttpClientServerTest MultiPart.Part fieldPart = parts.get(0); MultiPart.Part filePart = parts.get(1); - assertEquals(value, Content.Source.asString(fieldPart.getNewContent(), encoding)); + assertEquals(value, Content.Source.asString(fieldPart.newContentSource(), encoding)); assertEquals("file", filePart.getName()); assertEquals("application/octet-stream", filePart.getHeaders().get(HttpHeader.CONTENT_TYPE)); assertEquals("fileName", filePart.getFileName()); - assertArrayEquals(fileData, Content.Source.asByteBuffer(filePart.getNewContent()).array()); + assertArrayEquals(fileData, Content.Source.asByteBuffer(filePart.newContentSource()).array()); } }); diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MultiPart.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MultiPart.java index b39d3475312..4455af9e140 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MultiPart.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MultiPart.java @@ -176,7 +176,7 @@ public class MultiPart public Content.Source getContentSource() { if (content == null) - content = getNewContent(); + content = newContentSource(); return content; } @@ -192,11 +192,11 @@ public class MultiPart * * @return the content of this part as a new {@link Content.Source} */ - public abstract Content.Source getNewContent(); + public abstract Content.Source newContentSource(); public long getLength() { - return getNewContent().getLength(); + return newContentSource().getLength(); } /** @@ -221,7 +221,7 @@ public class MultiPart Charset charset = defaultCharset != null ? defaultCharset : UTF_8; if (charsetName != null) charset = Charset.forName(charsetName); - return Content.Source.asString(getNewContent(), charset); + return Content.Source.asString(newContentSource(), charset); } catch (IOException x) { @@ -237,11 +237,6 @@ public class MultiPart return fields; } - public Path getPath() - { - return path; - } - /** *

Writes the content of this part to the given path.

* @@ -254,7 +249,7 @@ public class MultiPart { try (OutputStream out = Files.newOutputStream(path)) { - IO.copy(Content.Source.asInputStream(getNewContent()), out); + IO.copy(Content.Source.asInputStream(newContentSource()), out); } this.path = path; } @@ -306,7 +301,7 @@ public class MultiPart } @Override - public Content.Source getNewContent() + public Content.Source newContentSource() { return new ByteBufferContentSource(content); } @@ -346,7 +341,7 @@ public class MultiPart } @Override - public Content.Source getNewContent() + public Content.Source newContentSource() { return new ChunksContentSource(content.stream().map(Content.Chunk::slice).toList()); } @@ -395,8 +390,13 @@ public class MultiPart } } + public Path getPath() + { + return path; + } + @Override - public Content.Source getNewContent() + public Content.Source newContentSource() { return new PathContentSource(path); } @@ -409,7 +409,7 @@ public class MultiPart hashCode(), getName(), getFileName(), - getPath() + path ); } } @@ -428,7 +428,7 @@ public class MultiPart } @Override - public Content.Source getNewContent() + public Content.Source newContentSource() { Content.Source c = content; content = null; @@ -630,7 +630,7 @@ public class MultiPart else { part = parts.poll(); - partContent = part.getNewContent(); + partContent = part.newContentSource(); state = State.HEADERS; yield Content.Chunk.from(firstBoundary.slice(), false); } @@ -657,7 +657,7 @@ public class MultiPart else { part = parts.poll(); - partContent = part.getNewContent(); + partContent = part.newContentSource(); state = State.HEADERS; yield Content.Chunk.from(middleBoundary.slice(), false); } @@ -743,7 +743,7 @@ public class MultiPart if (state == State.CONTENT) { - partContent.demand(() -> + part.getContentSource().demand(() -> { try (AutoLock ignoredAgain = lock.lock()) { @@ -850,6 +850,8 @@ public class MultiPart private int trailingWhiteSpaces; private String fieldName; private String fieldValue; + private long maxParts; + private int numParts = 0; public Parser(String boundary, Listener listener) { @@ -881,6 +883,22 @@ public class MultiPart this.partHeadersMaxLength = partHeadersMaxLength; } + /** + * @return the maximum number of parts that can be parsed from the multipart content. + */ + public long getMaxParts() + { + return maxParts; + } + + /** + * @param maxParts the maximum number of parts that can be parsed from the multipart content. + */ + public void setMaxParts(long maxParts) + { + this.maxParts = maxParts; + } + /** *

Resets this parser to make it ready to parse again a multipart/form-data content.

*/ @@ -939,6 +957,10 @@ public class MultiPart } else if (type == HttpTokens.Type.LF) { + numParts++; + if (numParts >= maxParts) + throw new IllegalStateException(String.format("Form with too many keys [%d > %d]", numParts, maxParts)); + notifyPartBegin(); state = State.HEADER_START; trailingWhiteSpaces = 0; @@ -1343,37 +1365,93 @@ public class MultiPart private void notifyPartBegin() { - listener.onPartBegin(); + try + { + listener.onPartBegin(); + } + catch (Throwable x) + { + if (LOG.isDebugEnabled()) + LOG.debug("failure while notifying listener {}", listener, x); + } } private void notifyPartHeader(String name, String value) { - listener.onPartHeader(name, value); + try + { + listener.onPartHeader(name, value); + } + catch (Throwable x) + { + if (LOG.isDebugEnabled()) + LOG.debug("failure while notifying listener {}", listener, x); + } } private void notifyPartHeaders() { - listener.onPartHeaders(); + try + { + listener.onPartHeaders(); + } + catch (Throwable x) + { + if (LOG.isDebugEnabled()) + LOG.debug("failure while notifying listener {}", listener, x); + } } private void notifyPartContent(Content.Chunk chunk) { - listener.onPartContent(chunk); + try + { + listener.onPartContent(chunk); + } + catch (Throwable x) + { + if (LOG.isDebugEnabled()) + LOG.debug("failure while notifying listener {}", listener, x); + } } private void notifyPartEnd() { - listener.onPartEnd(); + try + { + listener.onPartEnd(); + } + catch (Throwable x) + { + if (LOG.isDebugEnabled()) + LOG.debug("failure while notifying listener {}", listener, x); + } } private void notifyComplete() { - listener.onComplete(); + try + { + listener.onComplete(); + } + catch (Throwable x) + { + if (LOG.isDebugEnabled()) + LOG.debug("failure while notifying listener {}", listener, x); + } } private void notifyFailure(Throwable failure) { - listener.onFailure(failure); + try + { + listener.onFailure(failure); + } + catch (Throwable x) + { + if (LOG.isDebugEnabled()) + LOG.debug("failure while notifying listener {}", listener, x); + } } /** diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MultiPartByteRanges.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MultiPartByteRanges.java index 80117431dc5..4f4feebd1d4 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MultiPartByteRanges.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MultiPartByteRanges.java @@ -265,7 +265,7 @@ public class MultiPartByteRanges extends CompletableFuture implements Closeable +public class MultiPartFormData extends CompletableFuture { private static final Logger LOG = LoggerFactory.getLogger(MultiPartFormData.class); @@ -80,8 +80,6 @@ public class MultiPartFormData extends CompletableFutureAn ordered list of {@link MultiPart.Part}s that can * be accessed by index or by name, or iterated over.

*/ - public static class Parts implements Iterable + public class Parts implements Iterable { - private final String boundary; private final List parts; - private Parts(String boundary, List parts) + private Parts(List parts) { - this.boundary = boundary; this.parts = parts; } - /** - * @return the boundary string - */ - public String getBoundary() + public MultiPartFormData getMultiPartFormData() { - return boundary; + return MultiPartFormData.this; } /** @@ -383,6 +376,21 @@ public class MultiPartFormData extends CompletableFuture= maxParts) - throw new IllegalStateException(String.format("Form with too many keys [%d > %d]", numParts, maxParts)); - } - @Override public void onPart(String name, String fileName, HttpFields headers) { @@ -536,7 +535,7 @@ public class MultiPartFormData extends CompletableFuture getParts() @@ -629,25 +628,4 @@ public class MultiPartFormData extends CompletableFuture charSetParts = allParts.get("_charset_"); if (charSetParts != null) { - defaultCharset = Promise.Completable.with(p -> Content.Source.asString(charSetParts.get(0).getNewContent(), StandardCharsets.US_ASCII, p)) + defaultCharset = Promise.Completable.with(p -> Content.Source.asString(charSetParts.get(0).newContentSource(), StandardCharsets.US_ASCII, p)) .get(); } @@ -255,8 +255,8 @@ public class MultiPartCaptureTest assertThat("Part[" + expected.name + "]", parts, is(notNullValue())); MultiPart.Part part = parts.get(0); String charset = getCharsetFromContentType(part.getHeaders().get(HttpHeader.CONTENT_TYPE), defaultCharset); - assertTrue(part.getNewContent().rewind()); - String partContent = Content.Source.asString(part.getNewContent(), Charset.forName(charset)); + assertTrue(part.newContentSource().rewind()); + String partContent = Content.Source.asString(part.newContentSource(), Charset.forName(charset)); assertThat("Part[" + expected.name + "].contents", partContent, containsString(expected.value)); } @@ -276,8 +276,8 @@ public class MultiPartCaptureTest assertThat("Part[" + expected.name + "]", parts, is(notNullValue())); MultiPart.Part part = parts.get(0); MessageDigest digest = MessageDigest.getInstance("SHA1"); - assertTrue(part.getNewContent().rewind()); - try (InputStream partInputStream = Content.Source.asInputStream(part.getNewContent()); + assertTrue(part.newContentSource().rewind()); + try (InputStream partInputStream = Content.Source.asInputStream(part.newContentSource()); DigestOutputStream digester = new DigestOutputStream(OutputStream.nullOutputStream(), digest)) { IO.copy(partInputStream, digester); diff --git a/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/MultiPartFormDataTest.java b/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/MultiPartFormDataTest.java index 80c2ef614b7..6eeff622a34 100644 --- a/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/MultiPartFormDataTest.java +++ b/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/MultiPartFormDataTest.java @@ -189,25 +189,25 @@ public class MultiPartFormDataTest MultiPart.Part fileName = parts.getFirst("fileName"); assertThat(fileName, notNullValue()); - Content.Source partContent = fileName.getNewContent(); + Content.Source partContent = fileName.newContentSource(); assertThat(partContent.getLength(), is(3L)); assertThat(Content.Source.asString(partContent), is("abc")); MultiPart.Part desc = parts.getFirst("desc"); assertThat(desc, notNullValue()); - partContent = desc.getNewContent(); + partContent = desc.newContentSource(); assertThat(partContent.getLength(), is(3L)); assertThat(Content.Source.asString(partContent), is("123")); MultiPart.Part title = parts.getFirst("title"); assertThat(title, notNullValue()); - partContent = title.getNewContent(); + partContent = title.newContentSource(); assertThat(partContent.getLength(), is(3L)); assertThat(Content.Source.asString(partContent), is("ttt")); MultiPart.Part datafile = parts.getFirst("datafile5239138112980980385.txt"); assertThat(datafile, notNullValue()); - partContent = datafile.getNewContent(); + partContent = datafile.newContentSource(); assertThat(partContent.getLength(), is(3L)); assertThat(Content.Source.asString(partContent), is("000")); } @@ -275,11 +275,11 @@ public class MultiPartFormDataTest assertThat(parts.size(), is(2)); MultiPart.Part part1 = parts.getFirst("field1"); assertThat(part1, notNullValue()); - Content.Source partContent = part1.getNewContent(); + Content.Source partContent = part1.newContentSource(); assertThat(Content.Source.asString(partContent), is("Joe Blow")); MultiPart.Part part2 = parts.getFirst("stuff"); assertThat(part2, notNullValue()); - partContent = part2.getNewContent(); + partContent = part2.newContentSource(); assertThat(Content.Source.asString(partContent), is("aaaabbbbb")); } @@ -312,7 +312,7 @@ public class MultiPartFormDataTest assertThat(parts.size(), is(1)); MultiPart.Part part2 = parts.getFirst("stuff"); assertThat(part2, notNullValue()); - Content.Source partContent = part2.getNewContent(); + Content.Source partContent = part2.newContentSource(); assertThat(Content.Source.asString(partContent), is("aaaabbbbb")); } @@ -340,7 +340,7 @@ public class MultiPartFormDataTest assertThat(part, instanceOf(MultiPart.PathPart.class)); MultiPart.PathPart pathPart = (MultiPart.PathPart)part; assertTrue(Files.exists(pathPart.getPath())); - assertEquals("ABCDEFGHIJKLMNOPQRSTUVWXYZ", Content.Source.asString(part.getNewContent())); + assertEquals("ABCDEFGHIJKLMNOPQRSTUVWXYZ", Content.Source.asString(part.newContentSource())); } @Test @@ -422,13 +422,13 @@ public class MultiPartFormDataTest MultiPart.Part part1 = parts.get(0); assertThat(part1, instanceOf(MultiPart.ChunksPart.class)); - assertEquals(chunk, Content.Source.asString(part1.getNewContent())); + assertEquals(chunk, Content.Source.asString(part1.newContentSource())); MultiPart.Part part2 = parts.get(1); assertThat(part2, instanceOf(MultiPart.PathPart.class)); MultiPart.PathPart pathPart2 = (MultiPart.PathPart)part2; assertTrue(Files.exists(pathPart2.getPath())); - assertEquals(chunk.repeat(4), Content.Source.asString(part2.getNewContent())); + assertEquals(chunk.repeat(4), Content.Source.asString(part2.newContentSource())); } @Test diff --git a/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/MultiPartTest.java b/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/MultiPartTest.java index 18b1fd45ad1..5560a495e8c 100644 --- a/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/MultiPartTest.java +++ b/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/MultiPartTest.java @@ -361,11 +361,11 @@ public class MultiPartTest MultiPart.Part part1 = listener.parts.get(0); assertEquals("value", part1.getHeaders().get("name")); - assertEquals("Hello", Content.Source.asString(part1.getNewContent())); + assertEquals("Hello", Content.Source.asString(part1.newContentSource())); MultiPart.Part part2 = listener.parts.get(1); assertEquals("9001", part2.getHeaders().get("powerLevel")); - assertEquals("secondary\r\ncontent", Content.Source.asString(part2.getNewContent())); + assertEquals("secondary\r\ncontent", Content.Source.asString(part2.newContentSource())); assertEquals(0, data.remaining()); } @@ -397,11 +397,11 @@ public class MultiPartTest MultiPart.Part part1 = listener.parts.get(0); assertEquals("value", part1.getHeaders().get("name")); - assertEquals("Hello", Content.Source.asString(part1.getNewContent())); + assertEquals("Hello", Content.Source.asString(part1.newContentSource())); MultiPart.Part part2 = listener.parts.get(1); assertEquals("9001", part2.getHeaders().get("powerLevel")); - assertEquals("secondary\ncontent", Content.Source.asString(part2.getNewContent())); + assertEquals("secondary\ncontent", Content.Source.asString(part2.newContentSource())); assertEquals(0, data.remaining()); } @@ -457,7 +457,7 @@ public class MultiPartTest assertEquals(1, listener.parts.size()); MultiPart.Part part = listener.parts.get(0); assertEquals("value", part.getHeaders().get("name")); - assertEquals("", Content.Source.asString(part.getNewContent())); + assertEquals("", Content.Source.asString(part.newContentSource())); } @Test @@ -477,7 +477,7 @@ public class MultiPartTest assertEquals(1, listener.parts.size()); MultiPart.Part part = listener.parts.get(0); assertEquals("value", part.getHeaders().get("name")); - assertEquals("", Content.Source.asString(part.getNewContent())); + assertEquals("", Content.Source.asString(part.newContentSource())); } @Test @@ -508,7 +508,7 @@ public class MultiPartTest assertEquals(1, listener.parts.size()); MultiPart.Part part = listener.parts.get(0); assertEquals("value", part.getHeaders().get("name")); - assertThat(Content.Source.asString(part.getNewContent()), is(""" + assertThat(Content.Source.asString(part.newContentSource()), is(""" Hello\r this is not a --BOUNDARY\r that's a boundary""")); @@ -532,7 +532,7 @@ public class MultiPartTest assertThat(epilogueBuffer.remaining(), is(0)); assertEquals(1, listener.parts.size()); MultiPart.Part part = listener.parts.get(0); - assertThat(Content.Source.asByteBuffer(part.getNewContent()), is(ByteBuffer.wrap(random))); + assertThat(Content.Source.asByteBuffer(part.newContentSource()), is(ByteBuffer.wrap(random))); } @Test @@ -556,7 +556,7 @@ public class MultiPartTest assertEquals(1, listener.parts.size()); MultiPart.Part part = listener.parts.get(0); assertEquals("value", part.getHeaders().get("name")); - assertEquals("Hello", Content.Source.asString(part.getNewContent())); + assertEquals("Hello", Content.Source.asString(part.newContentSource())); } @Test diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DelayedHandler.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DelayedHandler.java index e862905048a..5680b82e4a7 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DelayedHandler.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DelayedHandler.java @@ -269,13 +269,13 @@ public class DelayedHandler extends Handler.Wrapper super(handler, wrapped, response, callback); String boundary = MultiPart.extractBoundary(contentType); _formData = boundary == null ? null : new MultiPartFormData(boundary); - getRequest().setAttribute(MultiPartFormData.class.getName(), _formData); } private void process(MultiPartFormData.Parts parts, Throwable x) { if (x == null) { + getRequest().setAttribute(MultiPartFormData.Parts.class.getName(), parts); super.process(); } else diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java index ca5a944153d..17384c26355 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java @@ -34,7 +34,7 @@ import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.MetaData; -import org.eclipse.jetty.http.MultiPartFormData; +import org.eclipse.jetty.http.MultiPartFormData.Parts; import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.http.Trailers; import org.eclipse.jetty.http.UriCompliance; @@ -649,9 +649,9 @@ public class HttpChannelState implements HttpChannel, Components } // Clean up any multipart tmp files and release any associated resources. - MultiPartFormData multiParts = (MultiPartFormData)_request.getAttribute(MultiPartFormData.class.getName()); - if (multiParts != null) - multiParts.close(); + Parts parts = (Parts)_request.getAttribute(Parts.class.getName()); + if (parts != null) + parts.close(); } finally { diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/MultiPartByteRangesTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/MultiPartByteRangesTest.java index f955f5ea909..204c552381b 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/MultiPartByteRangesTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/MultiPartByteRangesTest.java @@ -119,11 +119,11 @@ public class MultiPartByteRangesTest assertEquals(3, parts.size()); MultiPart.Part part1 = parts.get(0); - assertEquals("12", Content.Source.asString(part1.getNewContent())); + assertEquals("12", Content.Source.asString(part1.newContentSource())); MultiPart.Part part2 = parts.get(1); - assertEquals("456", Content.Source.asString(part2.getNewContent())); + assertEquals("456", Content.Source.asString(part2.newContentSource())); MultiPart.Part part3 = parts.get(2); - assertEquals("CDEF", Content.Source.asString(part3.getNewContent())); + assertEquals("CDEF", Content.Source.asString(part3.newContentSource())); } } } diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/MultiPartFormDataHandlerTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/MultiPartFormDataHandlerTest.java index a365abed169..cf8cba7064b 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/MultiPartFormDataHandlerTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/MultiPartFormDataHandlerTest.java @@ -80,7 +80,7 @@ public class MultiPartFormDataHandlerTest .whenComplete((parts, failure) -> { if (parts != null) - Content.copy(parts.get(0).getNewContent(), response, callback); + Content.copy(parts.get(0).newContentSource(), response, callback); else Response.writeError(request, response, callback, failure); }); @@ -126,10 +126,10 @@ public class MultiPartFormDataHandlerTest public boolean process(Request request, Response response, Callback callback) throws Exception { processLatch.countDown(); - MultiPartFormData formData = (MultiPartFormData)request.getAttribute(MultiPartFormData.class.getName()); - assertNotNull(formData); - MultiPart.Part part = formData.get().get(0); - Content.copy(part.getNewContent(), response, callback); + MultiPartFormData.Parts parts = (MultiPartFormData.Parts)request.getAttribute(MultiPartFormData.Parts.class.getName()); + assertNotNull(parts); + MultiPart.Part part = parts.get(0); + Content.copy(part.newContentSource(), response, callback); return true; } }); @@ -195,8 +195,8 @@ public class MultiPartFormDataHandlerTest { if (parts != null) { - response.getHeaders().put(HttpHeader.CONTENT_TYPE, "multipart/form-data; boundary=\"%s\"".formatted(parts.getBoundary())); - MultiPartFormData.ContentSource source = new MultiPartFormData.ContentSource(parts.getBoundary()); + response.getHeaders().put(HttpHeader.CONTENT_TYPE, "multipart/form-data; boundary=\"%s\"".formatted(parts.getMultiPartFormData().getBoundary())); + MultiPartFormData.ContentSource source = new MultiPartFormData.ContentSource(parts.getMultiPartFormData().getBoundary()); source.setPartHeadersMaxLength(1024); parts.forEach(source::addPart); source.close(); @@ -321,7 +321,7 @@ public class MultiPartFormDataHandlerTest HttpFields headers2 = part2.getHeaders(); assertEquals(2, headers2.size()); assertEquals("application/octet-stream", headers2.get(HttpHeader.CONTENT_TYPE)); - assertEquals(32, part2.getNewContent().getLength()); + assertEquals(32, part2.newContentSource().getLength()); } } } diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerByteRangesTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerByteRangesTest.java index 8247eff178a..0495089f89f 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerByteRangesTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerByteRangesTest.java @@ -181,10 +181,10 @@ public class ResourceHandlerByteRangesTest assertEquals(2, parts.size()); MultiPart.Part part1 = parts.get(0); assertEquals("text/plain", part1.getHeaders().get(HttpHeader.CONTENT_TYPE)); - assertEquals("234", Content.Source.asString(part1.getNewContent())); + assertEquals("234", Content.Source.asString(part1.newContentSource())); MultiPart.Part part2 = parts.get(1); assertEquals("text/plain", part2.getHeaders().get(HttpHeader.CONTENT_TYPE)); - assertEquals("xyz", Content.Source.asString(part2.getNewContent())); + assertEquals("xyz", Content.Source.asString(part2.newContentSource())); } } } diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletMultiPartFormData.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletMultiPartFormData.java index 28ad413be6f..22680abb252 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletMultiPartFormData.java +++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletMultiPartFormData.java @@ -77,9 +77,9 @@ public class ServletMultiPartFormData try { // Look for a previously read and parsed MultiPartFormData from the DelayedHandler. - MultiPartFormData formData = (MultiPartFormData)request.getAttribute(MultiPartFormData.class.getName()); - if (formData != null) - return new Parts(formData); + MultiPartFormData.Parts parts = (MultiPartFormData.Parts)request.getAttribute(MultiPartFormData.Parts.class.getName()); + if (parts != null) + return new Parts(parts); // TODO set the files directory return new ServletMultiPartFormData().parse(request, maxParts); @@ -102,7 +102,6 @@ public class ServletMultiPartFormData // Store MultiPartFormData as attribute on request so it is released by the HttpChannel. MultiPartFormData formData = new MultiPartFormData(boundary); - request.setAttribute(MultiPartFormData.class.getName(), formData); formData.setMaxParts(maxParts); File tmpDirFile = (File)request.getServletContext().getAttribute(ServletContext.TEMPDIR); @@ -123,7 +122,7 @@ public class ServletMultiPartFormData Connection connection = connectionMetaData.getConnection(); int bufferSize = connection instanceof AbstractConnection c ? c.getInputBufferSize() : 2048; InputStream input = request.getInputStream(); - while (true) + while (!formData.isDone()) { ByteBuffer buffer = byteBufferPool.newByteBuffer(bufferSize, false); boolean readEof = false; @@ -145,7 +144,9 @@ public class ServletMultiPartFormData } } - return new Parts(formData); + Parts parts = new Parts(formData.join()); + request.setAttribute(Parts.class.getName(), parts); + return parts; } /** @@ -155,9 +156,9 @@ public class ServletMultiPartFormData { private final List parts = new ArrayList<>(); - public Parts(MultiPartFormData formData) + public Parts(MultiPartFormData.Parts parts) { - formData.join().forEach(part -> parts.add(new ServletPart(formData, part))); + parts.forEach(part -> this.parts.add(new ServletPart(parts.getMultiPartFormData(), part))); } public Part getPart(String name) @@ -190,7 +191,7 @@ public class ServletMultiPartFormData @Override public InputStream getInputStream() throws IOException { - return Content.Source.asInputStream(_part.getNewContent()); + return Content.Source.asInputStream(_part.newContentSource()); } @Override diff --git a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/MultiPartServletTest.java b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/MultiPartServletTest.java index 69230d1827a..1850e9dfd75 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/MultiPartServletTest.java +++ b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/MultiPartServletTest.java @@ -129,10 +129,6 @@ public class MultiPartServletTest protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getParameterMap(); - req.getParts(); - resp.setStatus(200); - resp.getWriter().print("success"); - resp.getWriter().close(); } }, new MultipartConfigElement(tmpDirString)); @@ -174,10 +170,6 @@ public class MultiPartServletTest protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getParameterMap(); - req.getParts(); - resp.setStatus(200); - resp.getWriter().print("success"); - resp.getWriter().close(); } }, new MultipartConfigElement(tmpDirString)); @@ -216,10 +208,6 @@ public class MultiPartServletTest protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getParameterMap(); - req.getParts(); - resp.setStatus(200); - resp.getWriter().print("success"); - resp.getWriter().close(); } }, new MultipartConfigElement(tmpDirString, -1, 1024, 1024 * 1024 * 8));