Skip to content

Commit ef4ffa0

Browse files
committed
Support empty part in DefaultPartHttpMessageReader
This commit fixes a bug in DefaultPartHttpMessageReader's MultipartParser, due to which the last token in a part window was not properly indicated. Closes gh-30953
1 parent 2e3d133 commit ef4ffa0

File tree

4 files changed

+38
-2
lines changed

4 files changed

+38
-2
lines changed

spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartParser.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,7 @@ else if (len < 0) {
540540
while ((prev = this.queue.pollLast()) != null) {
541541
int prevByteCount = prev.readableByteCount();
542542
int prevLen = prevByteCount + len;
543-
if (prevLen > 0) {
543+
if (prevLen >= 0) {
544544
// slice body part of previous buffer, and flush it
545545
DataBuffer body = prev.split(prevLen + prev.readPosition());
546546
DataBufferUtils.release(prev);

spring-web/src/test/java/org/springframework/http/codec/multipart/DefaultPartHttpMessageReaderTests.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,23 @@ void exceedHeaderLimit() throws InterruptedException {
301301
latch.await();
302302
}
303303

304+
@ParameterizedDefaultPartHttpMessageReaderTest
305+
void emptyLastPart(DefaultPartHttpMessageReader reader) throws InterruptedException {
306+
MockServerHttpRequest request = createRequest(
307+
new ClassPathResource("empty-part.multipart", getClass()), "LiG0chJ0k7YtLt-FzTklYFgz50i88xJCW5jD");
308+
309+
Flux<Part> result = reader.read(forClass(Part.class), request, emptyMap());
310+
311+
CountDownLatch latch = new CountDownLatch(2);
312+
StepVerifier.create(result)
313+
.consumeNextWith(part -> testPart(part, null, "", latch))
314+
.consumeNextWith(part -> testPart(part, null, "", latch))
315+
.verifyComplete();
316+
317+
latch.await();
318+
}
319+
320+
304321
private void testBrowser(DefaultPartHttpMessageReader reader, Resource resource, String boundary)
305322
throws InterruptedException {
306323

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--LiG0chJ0k7YtLt-FzTklYFgz50i88xJCW5jD
2+
Content-Disposition: form-data; name="files"; filename="file17312898095703516893.tmp"
3+
Content-Type: application/octet-stream
4+
Content-Length: 0
5+
6+
7+
--LiG0chJ0k7YtLt-FzTklYFgz50i88xJCW5jD
8+
Content-Disposition: form-data; name="files"; filename="file14790463448453253614.tmp"
9+
Content-Type: application/octet-stream
10+
Content-Length: 0
11+
12+
13+
--LiG0chJ0k7YtLt-FzTklYFgz50i88xJCW5jD--

spring-webflux/src/test/java/org/springframework/web/reactive/function/MultipartRouterFunctionIntegrationTests.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,13 +257,19 @@ public Mono<ServerResponse> partData(ServerRequest request) {
257257
assertThat(data).hasSize(2);
258258

259259
List<PartEvent> fileData = data.get(0);
260-
assertThat(fileData).hasSize(1);
260+
assertThat(fileData).hasSize(2);
261261
assertThat(fileData).element(0).isInstanceOf(FilePartEvent.class);
262262
FilePartEvent filePartEvent = (FilePartEvent) fileData.get(0);
263263
assertThat(filePartEvent.name()).isEqualTo("fooPart");
264264
assertThat(filePartEvent.filename()).isEqualTo("foo.txt");
265265
DataBufferUtils.release(filePartEvent.content());
266266

267+
assertThat(fileData).element(1).isInstanceOf(FilePartEvent.class);
268+
filePartEvent = (FilePartEvent) fileData.get(1);
269+
assertThat(filePartEvent.name()).isEqualTo("fooPart");
270+
assertThat(filePartEvent.filename()).isEqualTo("foo.txt");
271+
DataBufferUtils.release(filePartEvent.content());
272+
267273
List<PartEvent> fieldData = data.get(1);
268274
assertThat(fieldData).hasSize(1);
269275
assertThat(fieldData).element(0).isInstanceOf(FormPartEvent.class);

0 commit comments

Comments
 (0)