|
46 | 46 | import org.junit.jupiter.params.ParameterizedTest;
|
47 | 47 | import org.junit.jupiter.params.provider.MethodSource;
|
48 | 48 | import org.mockito.Mockito;
|
| 49 | +import org.reactivestreams.Publisher; |
49 | 50 | import org.slf4j.LoggerFactory;
|
50 | 51 | import reactor.core.publisher.Flux;
|
51 | 52 | import reactor.core.publisher.Mono;
|
52 | 53 | import reactor.core.scheduler.Schedulers;
|
53 | 54 | import reactor.netty.BaseHttpTest;
|
54 | 55 | import reactor.netty.ByteBufFlux;
|
| 56 | +import reactor.netty.ByteBufMono; |
55 | 57 | import reactor.netty.Connection;
|
56 | 58 | import reactor.netty.LogTracker;
|
57 | 59 | import reactor.netty.NettyPipeline;
|
|
60 | 62 | import reactor.netty.http.client.HttpClientResponse;
|
61 | 63 | import reactor.netty.http.server.HttpServer;
|
62 | 64 | import reactor.netty.http.server.HttpServerConfig;
|
| 65 | +import reactor.netty.http.server.HttpServerRequest; |
| 66 | +import reactor.netty.http.server.HttpServerResponse; |
63 | 67 | import reactor.netty.http.server.logging.AccessLog;
|
64 | 68 | import reactor.netty.resources.ConnectionProvider;
|
65 | 69 | import reactor.test.StepVerifier;
|
|
79 | 83 | import java.util.concurrent.TimeUnit;
|
80 | 84 | import java.util.concurrent.atomic.AtomicBoolean;
|
81 | 85 | import java.util.concurrent.atomic.AtomicLong;
|
| 86 | +import java.util.concurrent.atomic.AtomicReference; |
| 87 | +import java.util.function.BiFunction; |
82 | 88 | import java.util.function.Function;
|
83 | 89 | import java.util.function.Predicate;
|
84 | 90 |
|
@@ -789,6 +795,95 @@ void test100Continue(HttpServer server, HttpClient client) throws Exception {
|
789 | 795 | assertThat(content.getT2()).isEqualTo(200);
|
790 | 796 | }
|
791 | 797 |
|
| 798 | + @ParameterizedCompatibleCombinationsCustomPoolTest |
| 799 | + void test100ContinueConnectionClose(HttpServer server, HttpClient client) throws Exception { |
| 800 | + doTest100ContinueConnection(server, client, |
| 801 | + (req, res) -> res.status(400).sendString(Mono.just("ERROR")), |
| 802 | + ByteBufFlux.fromString(Flux.just("1", "2", "3", "4", "5").delaySubscription(Duration.ofMillis(100))), |
| 803 | + false); |
| 804 | + } |
| 805 | + |
| 806 | + @ParameterizedCompatibleCombinationsCustomPoolTest |
| 807 | + void test100ContinueConnectionKeepAlive(HttpServer server, HttpClient client) throws Exception { |
| 808 | + doTest100ContinueConnection(server, client, |
| 809 | + (req, res) -> res.status(400).sendString(Mono.just("ERROR").delaySubscription(Duration.ofMillis(100))), |
| 810 | + ByteBufMono.fromString(Mono.just("12345")), |
| 811 | + true); |
| 812 | + } |
| 813 | + |
| 814 | + private void doTest100ContinueConnection( |
| 815 | + HttpServer server, |
| 816 | + HttpClient client, |
| 817 | + BiFunction<? super HttpServerRequest, ? super HttpServerResponse, ? extends Publisher<Void>> postHandler, |
| 818 | + Publisher<ByteBuf> sendBody, |
| 819 | + boolean isKeepAlive) throws Exception { |
| 820 | + HttpProtocol[] serverProtocols = server.configuration().protocols(); |
| 821 | + HttpProtocol[] clientProtocols = client.configuration().protocols(); |
| 822 | + |
| 823 | + CountDownLatch latch = new CountDownLatch(2); |
| 824 | + AtomicReference<List<Channel>> channels = new AtomicReference<>(new ArrayList<>(2)); |
| 825 | + disposableServer = |
| 826 | + server.doOnConnection(conn -> { |
| 827 | + channels.get().add(conn.channel()); |
| 828 | + conn.onTerminate().subscribe(null, t -> latch.countDown(), latch::countDown); |
| 829 | + }) |
| 830 | + .route(r -> |
| 831 | + r.post("/post", postHandler) |
| 832 | + .get("/get", (req, res) -> res.sendString(Mono.just("OK")))) |
| 833 | + .bindNow(); |
| 834 | + |
| 835 | + Mono<Tuple2<String, HttpClientResponse>> content1 = |
| 836 | + client.port(disposableServer.port()) |
| 837 | + .headers(h -> h.add(HttpHeaderNames.EXPECT, HttpHeaderValues.CONTINUE)) |
| 838 | + .post() |
| 839 | + .uri("/post") |
| 840 | + .send(sendBody) |
| 841 | + .responseSingle((res, bytes) -> bytes.asString().zipWith(Mono.just(res))); |
| 842 | + |
| 843 | + Mono<Tuple2<String, HttpClientResponse>> content2 = |
| 844 | + client.port(disposableServer.port()) |
| 845 | + .get() |
| 846 | + .uri("/get") |
| 847 | + .responseSingle((res, bytes) -> bytes.asString().zipWith(Mono.just(res))); |
| 848 | + |
| 849 | + List<Tuple2<String, HttpClientResponse>> responses = |
| 850 | + Flux.concat(content1, content2) |
| 851 | + .collectList() |
| 852 | + .block(Duration.ofSeconds(5)); |
| 853 | + |
| 854 | + assertThat(latch.await(30, TimeUnit.SECONDS)).as("latch await").isTrue(); |
| 855 | + |
| 856 | + assertThat(responses).isNotNull(); |
| 857 | + assertThat(responses.size()).isEqualTo(2); |
| 858 | + assertThat(responses.get(0).getT1()).isEqualTo("ERROR"); |
| 859 | + assertThat(responses.get(0).getT2().status().code()).isEqualTo(400); |
| 860 | + assertThat(responses.get(1).getT1()).isEqualTo("OK"); |
| 861 | + assertThat(responses.get(1).getT2().status().code()).isEqualTo(200); |
| 862 | + |
| 863 | + assertThat(channels.get().size()).isEqualTo(2); |
| 864 | + if ((serverProtocols.length == 1 && serverProtocols[0] == HttpProtocol.HTTP11) || |
| 865 | + (clientProtocols.length == 1 && clientProtocols[0] == HttpProtocol.HTTP11)) { |
| 866 | + if (isKeepAlive) { |
| 867 | + assertThat(channels.get().get(0)).isEqualTo(channels.get().get(1)); |
| 868 | + |
| 869 | + assertThat(responses.get(0).getT2().responseHeaders().get(HttpHeaderNames.CONNECTION)) |
| 870 | + .isNull(); |
| 871 | + } |
| 872 | + else { |
| 873 | + assertThat(channels.get()).doesNotHaveDuplicates(); |
| 874 | + |
| 875 | + assertThat(responses.get(0).getT2().responseHeaders().get(HttpHeaderNames.CONNECTION)) |
| 876 | + .isEqualTo(HttpHeaderValues.CLOSE.toString()); |
| 877 | + } |
| 878 | + } |
| 879 | + else { |
| 880 | + assertThat(channels.get()).doesNotHaveDuplicates(); |
| 881 | + |
| 882 | + assertThat(responses.get(0).getT2().responseHeaders().get(HttpHeaderNames.CONNECTION)) |
| 883 | + .isNull(); |
| 884 | + } |
| 885 | + } |
| 886 | + |
792 | 887 | static final class IdleTimeoutTestChannelInboundHandler extends ChannelInboundHandlerAdapter {
|
793 | 888 |
|
794 | 889 | final CountDownLatch latch = new CountDownLatch(1);
|
|
0 commit comments