reactor.ipc.netty.http.server.HttpServerTests.java Source code

Java tutorial

Introduction

Here is the source code for reactor.ipc.netty.http.server.HttpServerTests.java

Source

/*
 * Copyright (c) 2011-2017 Pivotal Software Inc, All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package reactor.ipc.netty.http.server;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.cert.CertificateException;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.net.ssl.SSLException;

import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.DefaultHttpContent;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import org.junit.Test;
import org.testng.Assert;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.ipc.netty.ByteBufFlux;
import reactor.ipc.netty.NettyContext;
import reactor.ipc.netty.http.HttpResources;
import reactor.ipc.netty.http.client.HttpClient;
import reactor.ipc.netty.http.client.HttpClientResponse;
import reactor.ipc.netty.resources.PoolResources;
import reactor.ipc.netty.tcp.TcpClient;
import reactor.test.StepVerifier;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * @author Stephane Maldini
 */
public class HttpServerTests {

    @Test
    public void secureSendFile() throws CertificateException, SSLException, InterruptedException {
        Path largeFile = Paths.get(getClass().getResource("/largeFile.txt").getFile());
        SelfSignedCertificate ssc = new SelfSignedCertificate();
        SslContext sslServer = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
        SslContext sslClient = SslContextBuilder.forClient().trustManager(ssc.cert()).build();

        NettyContext context = HttpServer.create(opt -> opt.sslContext(sslServer))
                .newHandler((req, resp) -> resp.sendFile(largeFile)).block();

        HttpClientResponse response = HttpClient
                .create(opt -> opt.port(context.address().getPort()).sslContext(sslClient)).get("/foo")
                .block(Duration.ofSeconds(120));

        context.dispose();
        context.onClose().block();

        String body = response.receive().aggregate().asString().block();

        assertThat(body)
                .startsWith(
                        "This is an UTF-8 file that is larger than 1024 bytes.\n" + "It contains accents like .")
                .contains("1024 mark here -><- 1024 mark here").endsWith("End of File");
    }

    @Test
    public void chunkedSendFile() throws InterruptedException, IOException {
        Path largeFile = Paths.get(getClass().getResource("/largeFile.txt").getFile());
        long fileSize = Files.size(largeFile);

        NettyContext context = HttpServer.create(opt -> opt.host("localhost"))
                .newHandler((req, resp) -> resp.sendFileChunked(largeFile, 0, fileSize)).block();

        HttpClientResponse response = HttpClient.create(opt -> opt.port(context.address().getPort())).get("/foo")
                .block(Duration.ofSeconds(120));

        context.dispose();
        context.onClose().block();

        String body = response.receive().aggregate().asString().block();

        assertThat(body)
                .startsWith(
                        "This is an UTF-8 file that is larger than 1024 bytes.\n" + "It contains accents like .")
                .contains("1024 mark here -><- 1024 mark here").endsWith("End of File");
    }

    //from https://github.com/reactor/reactor-netty/issues/90
    @Test
    public void testRestart() {
        // start a first server with a handler that answers HTTP 200 OK
        NettyContext context = HttpServer.create(8080).newHandler((req, resp) -> resp.status(200).send().log())
                .block();

        HttpClientResponse response = HttpClient.create(8080).get("/").block();

        // checking the response status, OK
        assertThat(response.status().code()).isEqualTo(200);
        // dispose the Netty context and wait for the channel close
        context.dispose();
        context.onClose().block();

        //REQUIRED - bug pool does not detect/translate properly lifecycle
        HttpResources.reset();

        // create a totally new server instance, with a different handler that answers HTTP 201
        context = HttpServer.create(8080).newHandler((req, resp) -> resp.status(201).send()).block();

        response = HttpClient.create(8080).get("/").block();

        // fails, response status is 200 and debugging shows the the previous handler is called
        assertThat(response.status().code()).isEqualTo(201);
        context.dispose();
        context.onClose().block();
    }

    @Test
    public void errorResponseAndReturn() throws Exception {
        NettyContext c = HttpServer.create(0).newHandler((req, resp) -> Mono.error(new Exception("returnError")))
                .block();

        assertThat(HttpClient.create(c.address().getPort()).get("/return", r -> r.failOnServerError(false)).block()
                .status().code()).isEqualTo(500);

        c.dispose();

    }

    @Test
    public void httpPipelining() throws Exception {

        AtomicInteger i = new AtomicInteger();

        NettyContext c = HttpServer.create(0)
                .newHandler((req, resp) -> resp.header(HttpHeaderNames.CONTENT_LENGTH, "1")
                        .sendString(Mono.just(i.incrementAndGet())
                                .flatMap(d -> Mono.delay(Duration.ofSeconds(4 - d)).map(x -> d + "\n"))))
                .block(Duration.ofSeconds(30));

        DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET,
                "/plaintext");

        CountDownLatch latch = new CountDownLatch(6);

        TcpClient.create(c.address().getPort()).newHandler((in, out) -> {
            in.context().addHandlerFirst(new HttpClientCodec());

            in.receiveObject().ofType(DefaultHttpContent.class).as(ByteBufFlux::fromInbound).asString().log()
                    .map(Integer::parseInt).subscribe(d -> {
                        for (int x = 0; x < d; x++) {
                            latch.countDown();
                        }
                    });

            return out.sendObject(Flux.just(request.retain(), request.retain(), request.retain())).neverComplete();
        }).block(Duration.ofSeconds(30));

        Assert.assertTrue(latch.await(45, TimeUnit.SECONDS));

    }

    @Test
    public void flushOnComplete() {

        Flux<String> test = Flux.range(0, 100).map(n -> String.format("%010d", n));

        NettyContext c = HttpServer.create(0).newHandler((req, resp) -> resp.sendString(test.map(s -> s + "\n")))
                .block(Duration.ofSeconds(30));

        Flux<String> client = HttpClient.create(c.address().getPort()).get("/").block(Duration.ofSeconds(30))
                .addHandler(new LineBasedFrameDecoder(10)).receive().asString();

        StepVerifier.create(client).expectNextSequence(test.toIterable()).expectComplete()
                .verify(Duration.ofSeconds(30));
    }

    @Test
    public void keepAlive() {
        NettyContext c = HttpServer.create(0)
                .newRouter(
                        routes -> routes.directory("/test", Paths.get(getClass().getResource("/public").getFile())))
                .block(Duration.ofSeconds(30));

        HttpResources.set(PoolResources.fixed("http", 1));

        HttpClientResponse response0 = HttpClient.create(c.address().getPort()).get("/test/index.html")
                .block(Duration.ofSeconds(30));

        HttpClientResponse response1 = HttpClient.create(c.address().getPort()).get("/test/test.css")
                .block(Duration.ofSeconds(30));

        HttpClientResponse response2 = HttpClient.create(c.address().getPort()).get("/test/test1.css")
                .block(Duration.ofSeconds(30));

        HttpClientResponse response3 = HttpClient.create(c.address().getPort()).get("/test/test2.css")
                .block(Duration.ofSeconds(30));

        HttpClientResponse response4 = HttpClient.create(c.address().getPort()).get("/test/test3.css")
                .block(Duration.ofSeconds(30));

        HttpClientResponse response5 = HttpClient.create(c.address().getPort()).get("/test/test4.css")
                .block(Duration.ofSeconds(30));

        HttpClientResponse response6 = HttpClient.create(opts -> opts.port(c.address().getPort()).disablePool())
                .get("/test/test5.css").block(Duration.ofSeconds(30));

        Assert.assertEquals(response0.channel(), response1.channel());
        Assert.assertEquals(response0.channel(), response2.channel());
        Assert.assertEquals(response0.channel(), response3.channel());
        Assert.assertEquals(response0.channel(), response4.channel());
        Assert.assertEquals(response0.channel(), response5.channel());
        Assert.assertNotEquals(response0.channel(), response6.channel());

        HttpResources.reset();
    }

    @Test
    public void toStringShowsOptions() {
        HttpServer server = HttpServer.create(opt -> opt.host("foo").port(123).compression(987));

        assertThat(server.toString()).isEqualTo("HttpServer: listening on foo:123, gzip over 987 bytes");
    }

    @Test
    public void gettingOptionsDuplicates() {
        HttpServer server = HttpServer.create(opt -> opt.host("foo").port(123).compression(true));
        assertThat(server.options()).isNotSameAs(server.options).isNotSameAs(server.options());
    }
}