Merge branch '5.1.x'

This commit is contained in:
Rossen Stoyanchev
2019-04-16 21:08:54 -04:00
15 changed files with 112 additions and 141 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -81,8 +81,16 @@ class ReactorClientHttpRequest extends AbstractClientHttpRequest implements Zero
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
return doCommit(() -> {
Flux<ByteBuf> byteBufFlux = Flux.from(body).map(NettyDataBufferFactory::toByteBuf);
return this.outbound.send(byteBufFlux).then();
// Send as Mono if possible as an optimization hint to Reactor Netty
if (body instanceof Mono) {
Mono<ByteBuf> byteBufMono = Mono.from(body).map(NettyDataBufferFactory::toByteBuf);
return this.outbound.send(byteBufMono).then();
}
else {
Flux<ByteBuf> byteBufFlux = Flux.from(body).map(NettyDataBufferFactory::toByteBuf);
return this.outbound.send(byteBufFlux).then();
}
});
}

View File

@@ -125,16 +125,14 @@ public class EncoderHttpMessageWriter<T> implements HttpMessageWriter<T> {
}))
.flatMap(buffer -> {
headers.setContentLength(buffer.readableByteCount());
return message.writeWith(
Mono.fromCallable(() -> buffer)
.doOnDiscard(PooledDataBuffer.class, PooledDataBuffer::release));
return message.writeWith(Mono.just(buffer)
.doOnDiscard(PooledDataBuffer.class, PooledDataBuffer::release));
});
}
if (isStreamingMediaType(contentType)) {
return message.writeAndFlushWith(body.map(buffer ->
Mono.fromCallable(() -> buffer)
.doOnDiscard(PooledDataBuffer.class, PooledDataBuffer::release)));
Mono.just(buffer).doOnDiscard(PooledDataBuffer.class, PooledDataBuffer::release)));
}
return message.writeWith(body);

View File

@@ -28,6 +28,8 @@ import reactor.core.publisher.Mono;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.PooledDataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpLogging;
import org.springframework.http.HttpStatus;
@@ -172,9 +174,16 @@ public abstract class AbstractServerHttpResponse implements ServerHttpResponse {
}
@Override
@SuppressWarnings("unchecked")
public final Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
return new ChannelSendOperator<>(body,
writePublisher -> doCommit(() -> writeWithInternal(writePublisher)))
// Write as Mono if possible as an optimization hint to Reactor Netty
// ChannelSendOperator not necessary for Mono
if (body instanceof Mono) {
return ((Mono<? extends DataBuffer>) body).flatMap(buffer ->
doCommit(() -> writeWithInternal(Mono.just(buffer)))
.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release));
}
return new ChannelSendOperator<>(body, inner -> doCommit(() -> writeWithInternal(inner)))
.doOnError(t -> removeContentLength());
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -85,8 +85,7 @@ public class FormHttpMessageWriterTests extends AbstractLeakCheckingTestCase {
String expected = "name+1=value+1&name+2=value+2%2B1&name+2=value+2%2B2&name+3";
StepVerifier.create(response.getBody())
.consumeNextWith(stringConsumer(
expected))
.consumeNextWith(stringConsumer(expected))
.expectComplete()
.verify();
HttpHeaders headers = response.getHeaders();
@@ -96,8 +95,7 @@ public class FormHttpMessageWriterTests extends AbstractLeakCheckingTestCase {
private Consumer<DataBuffer> stringConsumer(String expected) {
return dataBuffer -> {
String value =
DataBufferTestUtils.dumpString(dataBuffer, StandardCharsets.UTF_8);
String value = DataBufferTestUtils.dumpString(dataBuffer, StandardCharsets.UTF_8);
DataBufferUtils.release(dataBuffer);
assertEquals(expected, value);
};

View File

@@ -24,7 +24,6 @@ import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import io.netty.buffer.ByteBufAllocator;
import org.junit.Before;
import org.junit.Test;
import org.reactivestreams.Publisher;
@@ -38,7 +37,6 @@ import reactor.test.StepVerifier;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.LeakAwareDataBufferFactory;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import static org.junit.Assert.*;
@@ -135,8 +133,7 @@ public class ChannelSendOperatorTests {
@Test // gh-22720
public void cancelWhileItemCached() {
NettyDataBufferFactory delegate = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
LeakAwareDataBufferFactory bufferFactory = new LeakAwareDataBufferFactory(delegate);
LeakAwareDataBufferFactory bufferFactory = new LeakAwareDataBufferFactory();
ChannelSendOperator<DataBuffer> operator = new ChannelSendOperator<>(
Mono.fromCallable(() -> {
@@ -164,8 +161,7 @@ public class ChannelSendOperatorTests {
// 2. writeFunction applied and writeCompletionBarrier subscribed to it
// 3. Write Publisher fails right after that and before request(n) from server
NettyDataBufferFactory delegate = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
LeakAwareDataBufferFactory bufferFactory = new LeakAwareDataBufferFactory(delegate);
LeakAwareDataBufferFactory bufferFactory = new LeakAwareDataBufferFactory();
ZeroDemandSubscriber writeSubscriber = new ZeroDemandSubscriber();
ChannelSendOperator<DataBuffer> operator = new ChannelSendOperator<>(
@@ -200,8 +196,7 @@ public class ChannelSendOperatorTests {
// 2. writeFunction applied and writeCompletionBarrier subscribed to it
// 3. writeFunction fails, e.g. to flush status and headers, before request(n) from server
NettyDataBufferFactory delegate = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
LeakAwareDataBufferFactory bufferFactory = new LeakAwareDataBufferFactory(delegate);
LeakAwareDataBufferFactory bufferFactory = new LeakAwareDataBufferFactory();
ChannelSendOperator<DataBuffer> operator = new ChannelSendOperator<>(
Flux.create(sink -> {

View File

@@ -25,6 +25,7 @@ import java.util.function.Function;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.publisher.MonoProcessor;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
@@ -61,8 +62,11 @@ public class MockServerHttpResponse extends AbstractServerHttpResponse {
public MockServerHttpResponse(DataBufferFactory dataBufferFactory) {
super(dataBufferFactory);
this.writeHandler = body -> {
this.body = body.cache();
return this.body.then();
// Avoid .then() which causes data buffers to be released
MonoProcessor<Void> completion = MonoProcessor.create();
this.body = body.doOnComplete(completion::onComplete).doOnError(completion::onError).cache();
this.body.subscribe();
return completion;
};
}