Polishing

This commit is contained in:
Brian Clozel 2019-10-21 14:23:34 +02:00 committed by Rossen Stoyanchev
parent 89d053d7f4
commit 69bcdfc17f
3 changed files with 56 additions and 40 deletions

View File

@ -30,6 +30,7 @@ import reactor.test.StepVerifier;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.buffer.AbstractLeakCheckingTests;
import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.LeakAwareDataBufferFactory; import org.springframework.core.io.buffer.LeakAwareDataBufferFactory;
@ -45,18 +46,10 @@ import static org.assertj.core.api.Assertions.assertThat;
* Test cases for {@link ResourceRegionEncoder} class. * Test cases for {@link ResourceRegionEncoder} class.
* @author Brian Clozel * @author Brian Clozel
*/ */
class ResourceRegionEncoderTests { class ResourceRegionEncoderTests extends AbstractLeakCheckingTests {
private ResourceRegionEncoder encoder = new ResourceRegionEncoder(); private ResourceRegionEncoder encoder = new ResourceRegionEncoder();
private LeakAwareDataBufferFactory bufferFactory = new LeakAwareDataBufferFactory();
@AfterEach
void tearDown() throws Exception {
this.bufferFactory.checkForLeaks();
}
@Test @Test
void canEncode() { void canEncode() {
ResolvableType resourceRegion = ResolvableType.forClass(ResourceRegion.class); ResolvableType resourceRegion = ResolvableType.forClass(ResourceRegion.class);

View File

@ -75,10 +75,17 @@ import org.springframework.util.Assert;
*/ */
public class SynchronossPartHttpMessageReader extends LoggingCodecSupport implements HttpMessageReader<Part> { public class SynchronossPartHttpMessageReader extends LoggingCodecSupport implements HttpMessageReader<Part> {
private final DataBufferFactory bufferFactory = new DefaultDataBufferFactory(); private final DataBufferFactory bufferFactory;
private final PartBodyStreamStorageFactory streamStorageFactory = new DefaultPartBodyStreamStorageFactory(); private final PartBodyStreamStorageFactory streamStorageFactory = new DefaultPartBodyStreamStorageFactory();
public SynchronossPartHttpMessageReader() {
this.bufferFactory = new DefaultDataBufferFactory();
}
SynchronossPartHttpMessageReader(DataBufferFactory bufferFactory) {
this.bufferFactory = bufferFactory;
}
@Override @Override
public List<MediaType> getReadableMediaTypes() { public List<MediaType> getReadableMediaTypes() {
@ -155,15 +162,15 @@ public class SynchronossPartHttpMessageReader extends LoggingCodecSupport implem
parser.write(resultBytes); parser.write(resultBytes);
} }
catch (IOException ex) { catch (IOException ex) {
listener.onError("Exception thrown providing input to the parser", ex); listener.onError("Exception thrown while providing input to the parser", ex);
} }
finally { finally {
DataBufferUtils.release(buffer); DataBufferUtils.release(buffer);
} }
}, ex -> { }, ex -> {
try { try {
listener.onError("Request body input error", ex);
parser.close(); parser.close();
listener.onError("Request body input error", ex);
} }
catch (IOException ex2) { catch (IOException ex2) {
listener.onError("Exception thrown while closing the parser", ex2); listener.onError("Exception thrown while closing the parser", ex2);

View File

@ -17,19 +17,23 @@
package org.springframework.http.codec.multipart; package org.springframework.http.codec.multipart;
import java.io.File; import java.io.File;
import java.nio.charset.StandardCharsets;
import java.time.Duration; import java.time.Duration;
import java.util.Map; import java.util.Map;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.reactivestreams.Subscription;
import reactor.core.publisher.BaseSubscriber;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import reactor.test.StepVerifier; import reactor.test.StepVerifier;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.buffer.AbstractLeakCheckingTests;
import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory; import org.springframework.core.io.buffer.support.DataBufferTestUtils;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.client.MultipartBodyBuilder; import org.springframework.http.client.MultipartBodyBuilder;
@ -49,17 +53,20 @@ import static org.springframework.http.MediaType.MULTIPART_FORM_DATA;
* *
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @author Brian Clozel
*/ */
public class SynchronossPartHttpMessageReaderTests { public class SynchronossPartHttpMessageReaderTests extends AbstractLeakCheckingTests {
private final MultipartHttpMessageReader reader = private final MultipartHttpMessageReader reader =
new MultipartHttpMessageReader(new SynchronossPartHttpMessageReader()); new MultipartHttpMessageReader(new SynchronossPartHttpMessageReader(this.bufferFactory));
private static final ResolvableType PARTS_ELEMENT_TYPE =
forClassWithGenerics(MultiValueMap.class, String.class, Part.class);
@Test @Test
public void canRead() { void canRead() {
assertThat(this.reader.canRead( assertThat(this.reader.canRead(
forClassWithGenerics(MultiValueMap.class, String.class, Part.class), PARTS_ELEMENT_TYPE,
MediaType.MULTIPART_FORM_DATA)).isTrue(); MediaType.MULTIPART_FORM_DATA)).isTrue();
assertThat(this.reader.canRead( assertThat(this.reader.canRead(
@ -80,37 +87,30 @@ public class SynchronossPartHttpMessageReaderTests {
} }
@Test @Test
public void resolveParts() { void resolveParts() {
ServerHttpRequest request = generateMultipartRequest(); ServerHttpRequest request = generateMultipartRequest();
ResolvableType elementType = forClassWithGenerics(MultiValueMap.class, String.class, Part.class); MultiValueMap<String, Part> parts = this.reader.readMono(PARTS_ELEMENT_TYPE, request, emptyMap()).block();
MultiValueMap<String, Part> parts = this.reader.readMono(elementType, request, emptyMap()).block();
assertThat(parts.size()).isEqualTo(2); assertThat(parts).containsOnlyKeys("fooPart", "barPart");
assertThat(parts.containsKey("fooPart")).isTrue();
Part part = parts.getFirst("fooPart"); Part part = parts.getFirst("fooPart");
boolean condition1 = part instanceof FilePart; assertThat(part).isInstanceOf(FilePart.class);
assertThat(condition1).isTrue();
assertThat(part.name()).isEqualTo("fooPart"); assertThat(part.name()).isEqualTo("fooPart");
assertThat(((FilePart) part).filename()).isEqualTo("foo.txt"); assertThat(((FilePart) part).filename()).isEqualTo("foo.txt");
DataBuffer buffer = DataBufferUtils.join(part.content()).block(); DataBuffer buffer = DataBufferUtils.join(part.content()).block();
assertThat(buffer.readableByteCount()).isEqualTo(12); assertThat(DataBufferTestUtils.dumpString(buffer, StandardCharsets.UTF_8)).isEqualTo("Lorem Ipsum.");
byte[] byteContent = new byte[12]; DataBufferUtils.release(buffer);
buffer.read(byteContent);
assertThat(new String(byteContent)).isEqualTo("Lorem Ipsum.");
assertThat(parts.containsKey("barPart")).isTrue();
part = parts.getFirst("barPart"); part = parts.getFirst("barPart");
boolean condition = part instanceof FormFieldPart; assertThat(part).isInstanceOf(FormFieldPart.class);
assertThat(condition).isTrue();
assertThat(part.name()).isEqualTo("barPart"); assertThat(part.name()).isEqualTo("barPart");
assertThat(((FormFieldPart) part).value()).isEqualTo("bar"); assertThat(((FormFieldPart) part).value()).isEqualTo("bar");
} }
@Test // SPR-16545 @Test // SPR-16545
public void transferTo() { void transferTo() {
ServerHttpRequest request = generateMultipartRequest(); ServerHttpRequest request = generateMultipartRequest();
ResolvableType elementType = forClassWithGenerics(MultiValueMap.class, String.class, Part.class); MultiValueMap<String, Part> parts = this.reader.readMono(PARTS_ELEMENT_TYPE, request, emptyMap()).block();
MultiValueMap<String, Part> parts = this.reader.readMono(elementType, request, emptyMap()).block();
assertThat(parts).isNotNull(); assertThat(parts).isNotNull();
FilePart part = (FilePart) parts.getFirst("fooPart"); FilePart part = (FilePart) parts.getFirst("fooPart");
@ -125,10 +125,18 @@ public class SynchronossPartHttpMessageReaderTests {
} }
@Test @Test
public void bodyError() { void bodyError() {
ServerHttpRequest request = generateErrorMultipartRequest(); ServerHttpRequest request = generateErrorMultipartRequest();
ResolvableType elementType = forClassWithGenerics(MultiValueMap.class, String.class, Part.class); StepVerifier.create(this.reader.readMono(PARTS_ELEMENT_TYPE, request, emptyMap())).verifyError();
StepVerifier.create(this.reader.readMono(elementType, request, emptyMap())).verifyError(); }
@Test
void readPartsWithoutDemand() {
ServerHttpRequest request = generateMultipartRequest();
Mono<MultiValueMap<String, Part>> parts = this.reader.readMono(PARTS_ELEMENT_TYPE, request, emptyMap());
ZeroDemandSubscriber subscriber = new ZeroDemandSubscriber();
parts.subscribe(subscriber);
subscriber.cancel();
} }
@ -142,16 +150,24 @@ public class SynchronossPartHttpMessageReaderTests {
new MultipartHttpMessageWriter() new MultipartHttpMessageWriter()
.write(Mono.just(partsBuilder.build()), null, MediaType.MULTIPART_FORM_DATA, outputMessage, null) .write(Mono.just(partsBuilder.build()), null, MediaType.MULTIPART_FORM_DATA, outputMessage, null)
.block(Duration.ofSeconds(5)); .block(Duration.ofSeconds(5));
Flux<DataBuffer> requestBody = outputMessage.getBody().map(buffer -> this.bufferFactory.wrap(buffer.asByteBuffer()));
return MockServerHttpRequest.post("/") return MockServerHttpRequest.post("/")
.contentType(outputMessage.getHeaders().getContentType()) .contentType(outputMessage.getHeaders().getContentType())
.body(outputMessage.getBody()); .body(requestBody);
} }
private ServerHttpRequest generateErrorMultipartRequest() { private ServerHttpRequest generateErrorMultipartRequest() {
return MockServerHttpRequest.post("/") return MockServerHttpRequest.post("/")
.header(CONTENT_TYPE, MULTIPART_FORM_DATA.toString()) .header(CONTENT_TYPE, MULTIPART_FORM_DATA.toString())
.body(Flux.just(new DefaultDataBufferFactory().wrap("invalid content".getBytes()))); .body(Flux.just(this.bufferFactory.wrap("invalid content".getBytes())));
}
private static class ZeroDemandSubscriber extends BaseSubscriber<MultiValueMap<String, Part>> {
@Override
protected void hookOnSubscribe(Subscription subscription) {
// Just subscribe without requesting
}
} }
} }