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. See gh-30953 Closes gh-31766
This commit is contained in:
@@ -540,7 +540,7 @@ final class MultipartParser extends BaseSubscriber<DataBuffer> {
|
|||||||
while ((prev = this.queue.pollLast()) != null) {
|
while ((prev = this.queue.pollLast()) != null) {
|
||||||
int prevByteCount = prev.readableByteCount();
|
int prevByteCount = prev.readableByteCount();
|
||||||
int prevLen = prevByteCount + len;
|
int prevLen = prevByteCount + len;
|
||||||
if (prevLen > 0) {
|
if (prevLen >= 0) {
|
||||||
// slice body part of previous buffer, and flush it
|
// slice body part of previous buffer, and flush it
|
||||||
DataBuffer body = prev.split(prevLen + prev.readPosition());
|
DataBuffer body = prev.split(prevLen + prev.readPosition());
|
||||||
DataBufferUtils.release(prev);
|
DataBufferUtils.release(prev);
|
||||||
|
|||||||
@@ -301,6 +301,23 @@ class DefaultPartHttpMessageReaderTests {
|
|||||||
latch.await();
|
latch.await();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ParameterizedDefaultPartHttpMessageReaderTest
|
||||||
|
void emptyLastPart(DefaultPartHttpMessageReader reader) throws InterruptedException {
|
||||||
|
MockServerHttpRequest request = createRequest(
|
||||||
|
new ClassPathResource("empty-part.multipart", getClass()), "LiG0chJ0k7YtLt-FzTklYFgz50i88xJCW5jD");
|
||||||
|
|
||||||
|
Flux<Part> result = reader.read(forClass(Part.class), request, emptyMap());
|
||||||
|
|
||||||
|
CountDownLatch latch = new CountDownLatch(2);
|
||||||
|
StepVerifier.create(result)
|
||||||
|
.consumeNextWith(part -> testPart(part, null, "", latch))
|
||||||
|
.consumeNextWith(part -> testPart(part, null, "", latch))
|
||||||
|
.verifyComplete();
|
||||||
|
|
||||||
|
latch.await();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void testBrowser(DefaultPartHttpMessageReader reader, Resource resource, String boundary)
|
private void testBrowser(DefaultPartHttpMessageReader reader, Resource resource, String boundary)
|
||||||
throws InterruptedException {
|
throws InterruptedException {
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
--LiG0chJ0k7YtLt-FzTklYFgz50i88xJCW5jD
|
||||||
|
Content-Disposition: form-data; name="files"; filename="file17312898095703516893.tmp"
|
||||||
|
Content-Type: application/octet-stream
|
||||||
|
Content-Length: 0
|
||||||
|
|
||||||
|
|
||||||
|
--LiG0chJ0k7YtLt-FzTklYFgz50i88xJCW5jD
|
||||||
|
Content-Disposition: form-data; name="files"; filename="file14790463448453253614.tmp"
|
||||||
|
Content-Type: application/octet-stream
|
||||||
|
Content-Length: 0
|
||||||
|
|
||||||
|
|
||||||
|
--LiG0chJ0k7YtLt-FzTklYFgz50i88xJCW5jD--
|
||||||
@@ -259,13 +259,19 @@ class MultipartRouterFunctionIntegrationTests extends AbstractRouterFunctionInte
|
|||||||
assertThat(data).hasSize(2);
|
assertThat(data).hasSize(2);
|
||||||
|
|
||||||
List<PartEvent> fileData = data.get(0);
|
List<PartEvent> fileData = data.get(0);
|
||||||
assertThat(fileData).hasSize(1);
|
assertThat(fileData).hasSize(2);
|
||||||
assertThat(fileData.get(0)).isInstanceOf(FilePartEvent.class);
|
assertThat(fileData.get(0)).isInstanceOf(FilePartEvent.class);
|
||||||
FilePartEvent filePartEvent = (FilePartEvent) fileData.get(0);
|
FilePartEvent filePartEvent = (FilePartEvent) fileData.get(0);
|
||||||
assertThat(filePartEvent.name()).isEqualTo("fooPart");
|
assertThat(filePartEvent.name()).isEqualTo("fooPart");
|
||||||
assertThat(filePartEvent.filename()).isEqualTo("foo.txt");
|
assertThat(filePartEvent.filename()).isEqualTo("foo.txt");
|
||||||
DataBufferUtils.release(filePartEvent.content());
|
DataBufferUtils.release(filePartEvent.content());
|
||||||
|
|
||||||
|
assertThat(fileData.get(1)).isInstanceOf(FilePartEvent.class);
|
||||||
|
filePartEvent = (FilePartEvent) fileData.get(1);
|
||||||
|
assertThat(filePartEvent.name()).isEqualTo("fooPart");
|
||||||
|
assertThat(filePartEvent.filename()).isEqualTo("foo.txt");
|
||||||
|
DataBufferUtils.release(filePartEvent.content());
|
||||||
|
|
||||||
List<PartEvent> fieldData = data.get(1);
|
List<PartEvent> fieldData = data.get(1);
|
||||||
assertThat(fieldData).hasSize(1);
|
assertThat(fieldData).hasSize(1);
|
||||||
assertThat(fieldData.get(0)).isInstanceOf(FormPartEvent.class);
|
assertThat(fieldData.get(0)).isInstanceOf(FormPartEvent.class);
|
||||||
|
|||||||
Reference in New Issue
Block a user