Browse Source

Drain client response body consistenly

Prior to this commit, the `DefaultClientResponse` implementation would
handle HTTP client cases where the server response body would need to be
consumed (pooled resources need to be released) and the body publisher
cancelled right away. This is done to avoid reading the whole response
body and returning the connection to the pool, now that this connection
cannot be reused.

This was done already when the `WebClient` is asked for a `Void` type
for the response body.

SPR-17054 brought a new case for that: whenever no message reader is
able to decode the response body, an `UnsupportedMediaTypeException`
error signal is sent. Prior to this commit, the response body would not
be consumed and cancelled for that case.

This commit refactors all those cases from the `DefaultClientResponse`
directly into the `BodyExtractors`, since most of the logic and
knowledge for that belongs there. We only need to only apply this
behavior when the HTTP client is involved, as the server does not want
this to happen.

Issue: SPR-17054
pull/1890/head
Brian Clozel 8 years ago
parent
commit
a410d90439
  1. 63
      spring-webflux/src/main/java/org/springframework/web/reactive/function/BodyExtractors.java
  2. 59
      spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java
  3. 79
      spring-webflux/src/test/java/org/springframework/web/reactive/function/BodyExtractorsTests.java
  4. 125
      spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultClientResponseTests.java

63
spring-webflux/src/main/java/org/springframework/web/reactive/function/BodyExtractors.java

@ -29,8 +29,10 @@ import reactor.core.publisher.Mono; @@ -29,8 +29,10 @@ import reactor.core.publisher.Mono;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.MediaType;
import org.springframework.http.ReactiveHttpInputMessage;
import org.springframework.http.client.reactive.ClientHttpResponse;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.multipart.Part;
import org.springframework.http.server.reactive.ServerHttpRequest;
@ -42,6 +44,7 @@ import org.springframework.util.MultiValueMap; @@ -42,6 +44,7 @@ import org.springframework.util.MultiValueMap;
* @author Arjen Poutsma
* @author Sebastien Deleuze
* @author Rossen Stoyanchev
* @author Brian Clozel
* @since 5.0
*/
public abstract class BodyExtractors {
@ -81,8 +84,8 @@ public abstract class BodyExtractors { @@ -81,8 +84,8 @@ public abstract class BodyExtractors {
return (inputMessage, context) ->
readWithMessageReaders(inputMessage, context, elementType,
(HttpMessageReader<T> reader) -> readToMono(inputMessage, context, elementType, reader),
ex -> Mono.from(unsupportedErrorHandler(inputMessage, ex)),
Mono::empty);
ex -> Mono.from(unsupportedErrorHandler(inputMessage, context, ex)),
skipBodyAsMono(inputMessage, context));
}
/**
@ -110,8 +113,8 @@ public abstract class BodyExtractors { @@ -110,8 +113,8 @@ public abstract class BodyExtractors {
return (inputMessage, context) ->
readWithMessageReaders(inputMessage, context, elementType,
(HttpMessageReader<T> reader) -> readToFlux(inputMessage, context, elementType, reader),
ex -> unsupportedErrorHandler(inputMessage, ex),
Flux::empty);
ex -> unsupportedErrorHandler(inputMessage, context, ex),
skipBodyAsFlux(inputMessage, context));
}
@ -183,7 +186,6 @@ public abstract class BodyExtractors { @@ -183,7 +186,6 @@ public abstract class BodyExtractors {
if (VOID_TYPE.equals(elementType)) {
return emptySupplier.get();
}
MediaType contentType = Optional.ofNullable(message.getHeaders().getContentType())
.orElse(MediaType.APPLICATION_OCTET_STREAM);
@ -195,6 +197,28 @@ public abstract class BodyExtractors { @@ -195,6 +197,28 @@ public abstract class BodyExtractors {
.orElseGet(() -> errorFunction.apply(unsupportedError(context, elementType, contentType)));
}
private static <T> Supplier<Flux<T>> skipBodyAsFlux(ReactiveHttpInputMessage message,
BodyExtractor.Context context) {
if (isExtractingForClient(message, context)) {
return () -> consumeAndCancel(message).thenMany(Flux.empty());
}
else {
return Flux::empty;
}
}
private static <T> Supplier<Mono<T>> skipBodyAsMono(ReactiveHttpInputMessage message,
BodyExtractor.Context context) {
if (isExtractingForClient(message, context)) {
return () -> consumeAndCancel(message).then(Mono.empty());
}
else {
return Mono::empty;
}
}
private static UnsupportedMediaTypeException unsupportedError(BodyExtractor.Context context,
ResolvableType elementType, MediaType contentType) {
@ -222,17 +246,21 @@ public abstract class BodyExtractors { @@ -222,17 +246,21 @@ public abstract class BodyExtractors {
}
private static <T> Flux<T> unsupportedErrorHandler(
ReactiveHttpInputMessage inputMessage, UnsupportedMediaTypeException ex) {
ReactiveHttpInputMessage inputMessage, BodyExtractor.Context context,
UnsupportedMediaTypeException ex) {
Flux<T> result;
if (inputMessage.getHeaders().getContentType() == null) {
// Empty body with no content type is ok
return inputMessage.getBody().map(o -> {
result = inputMessage.getBody().map(o -> {
throw ex;
});
}
else {
return Flux.error(ex);
result = Flux.error(ex);
}
return isExtractingForClient(inputMessage, context) ?
consumeAndCancel(inputMessage).thenMany(result) : result;
}
private static <T> HttpMessageReader<T> findReader(
@ -251,4 +279,23 @@ public abstract class BodyExtractors { @@ -251,4 +279,23 @@ public abstract class BodyExtractors {
return (HttpMessageReader<T>) reader;
}
private static boolean isExtractingForClient(ReactiveHttpInputMessage message,
BodyExtractor.Context context) {
return !context.serverResponse().isPresent()
&& message instanceof ClientHttpResponse;
}
private static Mono<Void> consumeAndCancel(ReactiveHttpInputMessage message) {
return message.getBody()
.map(buffer -> {
DataBufferUtils.release(buffer);
throw new ReadCancellationException();
})
.onErrorResume(ReadCancellationException.class, ex -> Mono.empty())
.then();
}
@SuppressWarnings("serial")
private static class ReadCancellationException extends RuntimeException {
}
}

59
spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java

@ -27,7 +27,6 @@ import reactor.core.publisher.Mono; @@ -27,7 +27,6 @@ import reactor.core.publisher.Mono;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.codec.Hints;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@ -111,73 +110,32 @@ class DefaultClientResponse implements ClientResponse { @@ -111,73 +110,32 @@ class DefaultClientResponse implements ClientResponse {
@Override
public <T> Mono<T> bodyToMono(Class<? extends T> elementClass) {
if (Void.class.isAssignableFrom(elementClass)) {
return consumeAndCancel();
}
else {
return body(BodyExtractors.toMono(elementClass));
}
}
@SuppressWarnings("unchecked")
private <T> Mono<T> consumeAndCancel() {
return (Mono<T>) this.response.getBody()
.map(buffer -> {
DataBufferUtils.release(buffer);
throw new ReadCancellationException();
})
.onErrorResume(ReadCancellationException.class, ex -> Mono.empty())
.then();
return body(BodyExtractors.toMono(elementClass));
}
@Override
public <T> Mono<T> bodyToMono(ParameterizedTypeReference<T> typeReference) {
if (Void.class.isAssignableFrom(typeReference.getType().getClass())) {
return consumeAndCancel();
}
else {
return body(BodyExtractors.toMono(typeReference));
}
return body(BodyExtractors.toMono(typeReference));
}
@Override
public <T> Flux<T> bodyToFlux(Class<? extends T> elementClass) {
if (Void.class.isAssignableFrom(elementClass)) {
return Flux.from(consumeAndCancel());
}
else {
return body(BodyExtractors.toFlux(elementClass));
}
return body(BodyExtractors.toFlux(elementClass));
}
@Override
public <T> Flux<T> bodyToFlux(ParameterizedTypeReference<T> typeReference) {
if (Void.class.isAssignableFrom(typeReference.getType().getClass())) {
return Flux.from(consumeAndCancel());
}
else {
return body(BodyExtractors.toFlux(typeReference));
}
return body(BodyExtractors.toFlux(typeReference));
}
@Override
public <T> Mono<ResponseEntity<T>> toEntity(Class<T> bodyType) {
if (Void.class.isAssignableFrom(bodyType)) {
return toEntityInternal(consumeAndCancel());
}
else {
return toEntityInternal(bodyToMono(bodyType));
}
return toEntityInternal(bodyToMono(bodyType));
}
@Override
public <T> Mono<ResponseEntity<T>> toEntity(ParameterizedTypeReference<T> typeReference) {
if (Void.class.isAssignableFrom(typeReference.getType().getClass())) {
return toEntityInternal(consumeAndCancel());
}
else {
return toEntityInternal(bodyToMono(typeReference));
}
return toEntityInternal(bodyToMono(typeReference));
}
private <T> Mono<ResponseEntity<T>> toEntityInternal(Mono<T> bodyMono) {
@ -254,9 +212,4 @@ class DefaultClientResponse implements ClientResponse { @@ -254,9 +212,4 @@ class DefaultClientResponse implements ClientResponse {
}
}
@SuppressWarnings("serial")
private static class ReadCancellationException extends RuntimeException {
}
}

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

@ -27,11 +27,15 @@ import java.util.Map; @@ -27,11 +27,15 @@ import java.util.Map;
import java.util.Optional;
import com.fasterxml.jackson.annotation.JsonView;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.util.IllegalReferenceCountException;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import reactor.test.publisher.TestPublisher;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.codec.ByteBufferDecoder;
@ -39,6 +43,9 @@ import org.springframework.core.codec.StringDecoder; @@ -39,6 +43,9 @@ import org.springframework.core.codec.StringDecoder;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DefaultDataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.core.io.buffer.NettyDataBuffer;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ReactiveHttpInputMessage;
import org.springframework.http.codec.DecoderHttpMessageReader;
@ -53,15 +60,18 @@ import org.springframework.http.codec.multipart.SynchronossPartHttpMessageReader @@ -53,15 +60,18 @@ import org.springframework.http.codec.multipart.SynchronossPartHttpMessageReader
import org.springframework.http.codec.xml.Jaxb2XmlDecoder;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.mock.http.client.reactive.test.MockClientHttpResponse;
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
import org.springframework.util.MultiValueMap;
import static org.junit.Assert.*;
import static org.mockito.Mockito.when;
import static org.springframework.http.codec.json.Jackson2CodecSupport.*;
/**
* @author Arjen Poutsma
* @author Sebastien Deleuze
* @author Brian Clozel
*/
public class BodyExtractorsTests {
@ -69,6 +79,8 @@ public class BodyExtractorsTests { @@ -69,6 +79,8 @@ public class BodyExtractorsTests {
private Map<String, Object> hints;
private Optional<ServerHttpResponse> serverResponse = Optional.empty();
@Before
public void createContext() {
@ -92,7 +104,7 @@ public class BodyExtractorsTests { @@ -92,7 +104,7 @@ public class BodyExtractorsTests {
@Override
public Optional<ServerHttpResponse> serverResponse() {
return Optional.empty();
return serverResponse;
}
@Override
@ -180,6 +192,43 @@ public class BodyExtractorsTests { @@ -180,6 +192,43 @@ public class BodyExtractorsTests {
StepVerifier.create(result).expectComplete().verify();
}
@Test
public void toMonoVoidAsClientShouldConsumeAndCancel() {
DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
DefaultDataBuffer dataBuffer =
factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));
TestPublisher<DataBuffer> body = TestPublisher.create();
BodyExtractor<Mono<Void>, ReactiveHttpInputMessage> extractor = BodyExtractors.toMono(Void.class);
MockClientHttpResponse response = new MockClientHttpResponse(HttpStatus.OK);
response.setBody(body.flux());
StepVerifier.create(extractor.extract(response, this.context))
.then(() -> {
body.assertWasSubscribed();
body.emit(dataBuffer);
})
.verifyComplete();
body.assertCancelled();
}
@Test
public void toMonoVoidAsClientWithEmptyBody() {
TestPublisher<DataBuffer> body = TestPublisher.create();
BodyExtractor<Mono<Void>, ReactiveHttpInputMessage> extractor = BodyExtractors.toMono(Void.class);
MockClientHttpResponse response = new MockClientHttpResponse(HttpStatus.OK);
response.setBody(body.flux());
StepVerifier.create(extractor.extract(response, this.context))
.then(() -> {
body.assertWasSubscribed();
body.complete();
})
.verifyComplete();
}
@Test
public void toFlux() {
BodyExtractor<Flux<String>, ReactiveHttpInputMessage> extractor = BodyExtractors.toFlux(String.class);
@ -366,6 +415,34 @@ public class BodyExtractorsTests { @@ -366,6 +415,34 @@ public class BodyExtractorsTests {
.verify();
}
@Test // SPR-17054
public void unsupportedMediaTypeShouldConsumeAndCancel() {
NettyDataBufferFactory factory = new NettyDataBufferFactory(new PooledByteBufAllocator(true));
NettyDataBuffer buffer = factory.wrap(ByteBuffer.wrap("spring".getBytes(StandardCharsets.UTF_8)));
TestPublisher<DataBuffer> body = TestPublisher.create();
MockClientHttpResponse response = new MockClientHttpResponse(HttpStatus.OK);
response.getHeaders().setContentType(MediaType.APPLICATION_PDF);
response.setBody(body.flux());
BodyExtractor<Mono<User>, ReactiveHttpInputMessage> extractor = BodyExtractors.toMono(User.class);
StepVerifier.create(extractor.extract(response, this.context))
.then(() -> {
body.assertWasSubscribed();
body.emit(buffer);
})
.expectErrorSatisfies(throwable -> {
assertTrue(throwable instanceof UnsupportedMediaTypeException);
try {
buffer.release();
Assert.fail("releasing the buffer should have failed");
} catch (IllegalReferenceCountException exc) {
}
body.assertCancelled();
}).verify();
}
interface SafeToDeserialize {}

125
spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultClientResponseTests.java

@ -28,8 +28,6 @@ import org.junit.Before; @@ -28,8 +28,6 @@ import org.junit.Before;
import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import reactor.test.publisher.TestPublisher;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.codec.StringDecoder;
@ -48,9 +46,12 @@ import org.springframework.http.codec.HttpMessageReader; @@ -48,9 +46,12 @@ import org.springframework.http.codec.HttpMessageReader;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import static org.springframework.web.reactive.function.BodyExtractors.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.web.reactive.function.BodyExtractors.toMono;
/**
* @author Arjen Poutsma
@ -127,11 +128,7 @@ public class DefaultClientResponseTests { @@ -127,11 +128,7 @@ public class DefaultClientResponseTests {
DefaultDataBuffer dataBuffer =
factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));
Flux<DataBuffer> body = Flux.just(dataBuffer);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
when(mockResponse.getHeaders()).thenReturn(httpHeaders);
when(mockResponse.getBody()).thenReturn(body);
mockTextPlainResponse(body);
List<HttpMessageReader<?>> messageReaders = Collections
.singletonList(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes()));
@ -147,13 +144,7 @@ public class DefaultClientResponseTests { @@ -147,13 +144,7 @@ public class DefaultClientResponseTests {
DefaultDataBuffer dataBuffer =
factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));
Flux<DataBuffer> body = Flux.just(dataBuffer);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
when(mockResponse.getHeaders()).thenReturn(httpHeaders);
when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK);
when(mockResponse.getRawStatusCode()).thenReturn(HttpStatus.OK.value());
when(mockResponse.getBody()).thenReturn(body);
mockTextPlainResponse(body);
List<HttpMessageReader<?>> messageReaders = Collections
.singletonList(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes()));
@ -169,13 +160,7 @@ public class DefaultClientResponseTests { @@ -169,13 +160,7 @@ public class DefaultClientResponseTests {
DefaultDataBuffer dataBuffer =
factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));
Flux<DataBuffer> body = Flux.just(dataBuffer);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
when(mockResponse.getHeaders()).thenReturn(httpHeaders);
when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK);
when(mockResponse.getRawStatusCode()).thenReturn(HttpStatus.OK.value());
when(mockResponse.getBody()).thenReturn(body);
mockTextPlainResponse(body);
List<HttpMessageReader<?>> messageReaders = Collections
.singletonList(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes()));
@ -193,13 +178,7 @@ public class DefaultClientResponseTests { @@ -193,13 +178,7 @@ public class DefaultClientResponseTests {
DefaultDataBuffer dataBuffer =
factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));
Flux<DataBuffer> body = Flux.just(dataBuffer);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
when(mockResponse.getHeaders()).thenReturn(httpHeaders);
when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK);
when(mockResponse.getRawStatusCode()).thenReturn(HttpStatus.OK.value());
when(mockResponse.getBody()).thenReturn(body);
mockTextPlainResponse(body);
List<HttpMessageReader<?>> messageReaders = Collections
.singletonList(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes()));
@ -216,13 +195,7 @@ public class DefaultClientResponseTests { @@ -216,13 +195,7 @@ public class DefaultClientResponseTests {
DefaultDataBuffer dataBuffer =
factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));
Flux<DataBuffer> body = Flux.just(dataBuffer);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
when(mockResponse.getHeaders()).thenReturn(httpHeaders);
when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK);
when(mockResponse.getRawStatusCode()).thenReturn(HttpStatus.OK.value());
when(mockResponse.getBody()).thenReturn(body);
mockTextPlainResponse(body);
List<HttpMessageReader<?>> messageReaders = Collections
.singletonList(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes()));
@ -241,13 +214,7 @@ public class DefaultClientResponseTests { @@ -241,13 +214,7 @@ public class DefaultClientResponseTests {
DefaultDataBuffer dataBuffer =
factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));
Flux<DataBuffer> body = Flux.just(dataBuffer);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
when(mockResponse.getHeaders()).thenReturn(httpHeaders);
when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK);
when(mockResponse.getRawStatusCode()).thenReturn(HttpStatus.OK.value());
when(mockResponse.getBody()).thenReturn(body);
mockTextPlainResponse(body);
List<HttpMessageReader<?>> messageReaders = Collections
.singletonList(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes()));
@ -296,13 +263,7 @@ public class DefaultClientResponseTests { @@ -296,13 +263,7 @@ public class DefaultClientResponseTests {
DefaultDataBuffer dataBuffer =
factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));
Flux<DataBuffer> body = Flux.just(dataBuffer);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
when(mockResponse.getHeaders()).thenReturn(httpHeaders);
when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK);
when(mockResponse.getRawStatusCode()).thenReturn(HttpStatus.OK.value());
when(mockResponse.getBody()).thenReturn(body);
mockTextPlainResponse(body);
List<HttpMessageReader<?>> messageReaders = Collections
.singletonList(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes()));
@ -323,13 +284,7 @@ public class DefaultClientResponseTests { @@ -323,13 +284,7 @@ public class DefaultClientResponseTests {
DefaultDataBuffer dataBuffer =
factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));
Flux<DataBuffer> body = Flux.just(dataBuffer);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
when(mockResponse.getHeaders()).thenReturn(httpHeaders);
when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK);
when(mockResponse.getRawStatusCode()).thenReturn(HttpStatus.OK.value());
when(mockResponse.getBody()).thenReturn(body);
mockTextPlainResponse(body);
List<HttpMessageReader<?>> messageReaders = Collections
.singletonList(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes()));
@ -379,12 +334,7 @@ public class DefaultClientResponseTests { @@ -379,12 +334,7 @@ public class DefaultClientResponseTests {
factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));
Flux<DataBuffer> body = Flux.just(dataBuffer);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
when(mockResponse.getHeaders()).thenReturn(httpHeaders);
when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK);
when(mockResponse.getRawStatusCode()).thenReturn(HttpStatus.OK.value());
when(mockResponse.getBody()).thenReturn(body);
mockTextPlainResponse(body);
List<HttpMessageReader<?>> messageReaders = Collections
.singletonList(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes()));
@ -399,55 +349,14 @@ public class DefaultClientResponseTests { @@ -399,55 +349,14 @@ public class DefaultClientResponseTests {
assertEquals(MediaType.TEXT_PLAIN, result.getHeaders().getContentType());
}
@Test
public void toMonoVoid() {
TestPublisher<DataBuffer> body = TestPublisher.create();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
when(mockResponse.getHeaders()).thenReturn(httpHeaders);
when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK);
when(mockResponse.getRawStatusCode()).thenReturn(HttpStatus.OK.value());
when(mockResponse.getBody()).thenReturn(body.flux());
List<HttpMessageReader<?>> messageReaders = Collections
.singletonList(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes()));
when(mockExchangeStrategies.messageReaders()).thenReturn(messageReaders);
StepVerifier.create(defaultClientResponse.bodyToMono(Void.class))
.then(() -> {
body.assertWasSubscribed();
body.complete();
})
.verifyComplete();
}
@Test
public void toMonoVoidNonEmptyBody() {
DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
DefaultDataBuffer dataBuffer =
factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));
TestPublisher<DataBuffer> body = TestPublisher.create();
private void mockTextPlainResponse(Flux<DataBuffer> body) {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
when(mockResponse.getHeaders()).thenReturn(httpHeaders);
when(mockResponse.getStatusCode()).thenReturn(HttpStatus.OK);
when(mockResponse.getRawStatusCode()).thenReturn(HttpStatus.OK.value());
when(mockResponse.getBody()).thenReturn(body.flux());
List<HttpMessageReader<?>> messageReaders = Collections
.singletonList(new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes()));
when(mockExchangeStrategies.messageReaders()).thenReturn(messageReaders);
StepVerifier.create(defaultClientResponse.bodyToMono(Void.class))
.then(() -> {
body.assertWasSubscribed();
body.emit(dataBuffer);
})
.verifyComplete();
body.assertCancelled();
when(mockResponse.getBody()).thenReturn(body);
}
}

Loading…
Cancel
Save