From 3e1632f7c5c6ea242f3a7e0c734e724957e238ad Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Thu, 29 Sep 2022 23:46:18 +0200 Subject: [PATCH] Removed deprecated tests, disabled pipelining test. (#5010) --- .../webserver/BytesReuseV2ApiTest.java | 395 ------------ .../webserver/CipherSuiteV2ApiTest.java | 117 ---- .../webserver/CloseConnectionV2ApiTest.java | 99 --- .../webserver/CompressionV2ApiTest.java | 163 ----- .../reactive/webserver/EncodingV2ApiTest.java | 156 ----- .../reactive/webserver/FilterV2ApiTest.java | 154 ----- .../reactive/webserver/Gh1893V2ApiTest.java | 183 ------ .../reactive/webserver/Gh377V2ApiTest.java | 59 -- .../reactive/webserver/HttpPipelineTest.java | 2 + .../webserver/HttpPipelineV2ApiTest.java | 124 ---- .../webserver/KeepAliveV2ApiTest.java | 143 ----- .../webserver/MultiPortV2ApiTest.java | 277 --------- .../webserver/NettyWebServerV2ApiTest.java | 277 --------- .../reactive/webserver/PlainV2ApiTest.java | 574 ------------------ .../SocketConfigurationV2ApiTest.java | 158 ----- 15 files changed, 2 insertions(+), 2879 deletions(-) delete mode 100644 reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/BytesReuseV2ApiTest.java delete mode 100644 reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/CipherSuiteV2ApiTest.java delete mode 100644 reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/CloseConnectionV2ApiTest.java delete mode 100644 reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/CompressionV2ApiTest.java delete mode 100644 reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/EncodingV2ApiTest.java delete mode 100644 reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/FilterV2ApiTest.java delete mode 100644 reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/Gh1893V2ApiTest.java delete mode 100644 reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/Gh377V2ApiTest.java delete mode 100644 reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/HttpPipelineV2ApiTest.java delete mode 100644 reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/KeepAliveV2ApiTest.java delete mode 100644 reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/MultiPortV2ApiTest.java delete mode 100644 reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/NettyWebServerV2ApiTest.java delete mode 100644 reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/PlainV2ApiTest.java delete mode 100644 reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/SocketConfigurationV2ApiTest.java diff --git a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/BytesReuseV2ApiTest.java b/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/BytesReuseV2ApiTest.java deleted file mode 100644 index 1e1807dd46e..00000000000 --- a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/BytesReuseV2ApiTest.java +++ /dev/null @@ -1,395 +0,0 @@ -/* - * Copyright (c) 2017, 2022 Oracle and/or its affiliates. - * - * 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 io.helidon.reactive.webserver; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.Executors; -import java.util.concurrent.Flow.Publisher; -import java.util.concurrent.Flow.Subscription; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; - -import io.helidon.common.http.BadRequestException; -import io.helidon.common.http.DataChunk; -import io.helidon.common.http.Http; -import io.helidon.common.reactive.Multi; -import io.helidon.common.testing.http.junit5.SocketHttpClient; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.StringEndsWith.endsWith; -import static org.hamcrest.core.StringStartsWith.startsWith; -import static org.junit.jupiter.api.Assertions.fail; - -/** - * The BytesReuseTest verifies whether the {@link io.helidon.common.http.DataChunk} instances get released properly. - *

- * Note that with {@link io.helidon.common.http.DataChunk#finalize()} which calls {@link io.helidon.common.http.DataChunk#release()}, - * we no longer experience {@link OutOfMemoryError} exceptions in case the chunks aren't freed - * as long as no references to the {@link io.helidon.common.http.DataChunk} instances are kept. - */ -@Deprecated(since = "3.0.0", forRemoval = true) -public class BytesReuseV2ApiTest { - - private static final Logger LOGGER = Logger.getLogger(PlainTest.class.getName()); - - private static WebServer webServer; - - private static Queue chunkReference = new ConcurrentLinkedQueue<>(); - - /** - * Start the Web Server - * - * @param port the port on which to start the server; if less than 1, - * the port is dynamically selected - * @throws Exception in case of an error - */ - private static void startServer(int port) throws Exception { - webServer = WebServer.builder() - .host("localhost") - .port(port) - .routing(Routing.builder() - .any((req, res) -> { - req.content().registerFilter( - (Publisher publisher) -> Multi.create(publisher).map(chunk -> { - if (req.queryParams().first("keep_chunks").map(Boolean::valueOf).orElse(true)) { - chunkReference.add(chunk); - } - return chunk; - })); - res.headers().add(Http.HeaderValues.TRANSFER_ENCODING_CHUNKED); - req.next(); - }) - .post("/subscriber", (req, res) -> { - Multi.create(req.content()).subscribe((DataChunk chunk) -> { - if (req.queryParams().first("release").map(Boolean::valueOf).orElse(true)) { - chunk.release(); - } - }, (Throwable ex) -> { - LOGGER.log(Level.WARNING, - "Encountered an exception!", ex); - res.status(500) - .send("Error: " + ex.getMessage()); - }, () -> { - res.send("Finished"); - }, (Subscription subscription) -> { - subscription.request(Long.MAX_VALUE); - }); - }) - .post("/string", Handler.create(String.class, (req, res, s) -> { - assertAgainstPrefixQueryParam(req, s); - res.send("Finished"); - })) - .post("/bytes", Handler.create(byte[].class, (req, res, b) -> { - assertAgainstPrefixQueryParam(req, new String(b)); - res.send("Finished"); - })) - .post("/bytes_deferred", (req, res) -> { - Executors.newSingleThreadExecutor().submit(() -> { - req.content().as(byte[].class).thenAccept(bytes -> { - assertAgainstPrefixQueryParam(req, new String(bytes)); - res.send("Finished"); - }).exceptionally(t -> { - req.next(t); - return null; - }); - }); - }) - .post("/input_stream", Handler.create(InputStream.class, (req, res, stream) -> { - Executors.newSingleThreadExecutor().submit(() -> { - try { - LOGGER.info("Consuming data from input stream!"); - assertAgainstPrefixQueryParam(req, - new String(stream.readAllBytes())); - res.send("Finished"); - } catch (IOException e) { - req.next(new IllegalStateException("Got an IO error.", e)); - } - }); - })) - .any("/unconsumed", (req, res) -> res.send("Nothing consumed!")) - .build()) - .build() - .start() - .toCompletableFuture() - .get(10, TimeUnit.SECONDS); - - LOGGER.log(Level.INFO, "Started server at: https://localhost:{0}", webServer.port()); - } - - private static void assertAgainstPrefixQueryParam(ServerRequest req, String actual) { - assertThat(actual, - startsWith(req.queryParams() - .first("test") - .orElseThrow(() -> new BadRequestException("Missing 'test' query param")))); - } - - @BeforeAll - public static void startServer() throws Exception { - // start the server at a free port - startServer(0); - } - - @AfterAll - public static void close() throws Exception { - if (webServer != null) { - webServer.shutdown() - .toCompletableFuture() - .get(10, TimeUnit.SECONDS); - } - } - - @AfterEach - public void tearDown() throws Exception { - chunkReference.clear(); - } - - private void doSubscriberPostRequest(boolean release) throws Exception { - try (SocketHttpClient s = SocketHttpClient.create(webServer.port())) { - s.request(Http.Method.POST, "/subscriber?test=myData&release=" + release, "myData" + SocketHttpClient.longData(100_000).toString()); - assertThat(s.receive(), endsWith("\nFinished\n0\n\n")); - } - } - - private void assertChunkReferencesAreReleased() { - LOGGER.log(Level.INFO, "Asserting that {0} request chunks were released.", chunkReference.size()); - for (DataChunk chunk : chunkReference) { - assertThat("The chunk was not released: ID " + chunk.id(), chunk.isReleased(), is(true)); - } - } - - @Test - public void requestChunkDataRemainsWhenNotReleased() throws Exception { - doSubscriberPostRequest(false); - for (DataChunk chunk : chunkReference) { - assertThat("The chunk was released: ID " + chunk.id(), chunk.isReleased(), is(false)); - } - assertThat(new String(chunkReference.peek().bytes()), startsWith("myData")); - } - - @Test - @Disabled("This test takes minutes before it throws OutOfMemoryError") - public void requestChunkDataRemainsWhenNotReleasedOutOfMemoryError() throws Exception { - for (int i = 0; i < 100_000; i++) { - try { - requestChunkDataRemainsWhenNotReleased(); - } finally { - LOGGER.log(Level.INFO, "Iteration reached: {0}", i); - } - } - fail("An assertion was expected: OutOfMemoryError"); - } - - @Test - public void requestChunkDataGetReusedWhenReleased() throws Exception { - doSubscriberPostRequest(true); - assertChunkReferencesAreReleased(); - } - - @Test - @Disabled("The intention of this test is to show that webserver can run indefinitely") - public void requestChunkDataGetReusedWhenReleasedDoesNeverFail() throws Exception { - for (int i = 0; i < 100_000; i++) { - try { - requestChunkDataGetReusedWhenReleased(); - } finally { - LOGGER.log(Level.INFO, "Iteration reached: {0}", i); - } - } - } - - @Test - public void toStringConverterFreesTheRequestChunks() throws Exception { - try (SocketHttpClient s = SocketHttpClient.create(webServer.port())) { - s.request(Http.Method.POST, "/string?test=myData", "myData" + SocketHttpClient.longData(100_000).toString()); - assertThat(s.receive(), endsWith("\nFinished\n0\n\n")); - } - assertChunkReferencesAreReleased(); - } - - @Test - public void toByteArrayConverterFreesTheRequestChunks() throws Exception { - try (SocketHttpClient s = SocketHttpClient.create(webServer.port())) { - s.request(Http.Method.POST, "/bytes?test=myData", "myData" + SocketHttpClient.longData(100_000).toString()); - assertThat(s.receive(), endsWith("\nFinished\n0\n\n")); - } - assertChunkReferencesAreReleased(); - } - - @Test - public void toByteArrayDeferredConverterFreesTheRequestChunks() throws Exception { - try (SocketHttpClient s = SocketHttpClient.create(webServer.port())) { - s.request(Http.Method.POST, "/bytes_deferred?test=myData", "myData" + SocketHttpClient.longData(100_000).toString()); - assertThat(s.receive(), endsWith("\nFinished\n0\n\n")); - } - assertChunkReferencesAreReleased(); - } - - @Test - public void toInputStreamConverterFreesTheRequestChunks() throws Exception { - try (SocketHttpClient s = SocketHttpClient.create(webServer.port())) { - s.request(Http.Method.POST, "/input_stream?test=myData", "myData" + SocketHttpClient.longData(100_000).toString()); - assertThat(s.receive(), endsWith("\nFinished\n0\n\n")); - } - assertChunkReferencesAreReleased(); - } - - @Test - public void notFoundPostRequestPayloadGetsReleased() throws Exception { - try (SocketHttpClient s = SocketHttpClient.create(webServer.port())) { - s.request(Http.Method.POST, "/non_existent?test=myData", "myData" + SocketHttpClient.longData(100_000).toString()); - assertThat(s.receive(), startsWith("HTTP/1.1 404 Not Found\n")); - } - assertChunkReferencesAreReleased(); - } - - @Test - public void unconsumedPostRequestPayloadGetsReleased() throws Exception { - try (SocketHttpClient s = SocketHttpClient.create(webServer.port())) { - s.request(Http.Method.POST, "/unconsumed?test=myData", "myData" + SocketHttpClient.longData(100_000).toString()); - assertThat(s.receive(), endsWith("Nothing consumed!\n0\n\n")); - } - assertChunkReferencesAreReleased(); - } - - /** - * This test causes the WebServer to throw {@link OutOfMemoryError} by not releasing the - * {@link io.helidon.common.http.DataChunk} instances. - * It takes several seconds which is why it's disabled by default. - *

- * Note that since {@link ByteBufRequestChunk} releases the underlying {@link io.netty.buffer.ByteBuf} on - * {@link Object#finalize()} call, the {@link OutOfMemoryError} never occurs in case that the - * {@link #chunkReference} doesn't get filled. - * - * @throws Exception in case of an error - */ - @Test - @Disabled("Ignored because to get the OutOfMemoryError it takes more than 10 seconds.") - public void sendUnlimitedDataWithoutReleasingTheChunksEndsWithOutOfMemoryError() throws Exception { - try { - flood(false, true, 0, true); - } catch (OutOfMemoryError e) { - // OOME is expected either here or anywhere else (Netty threads for instance) - } - - // and yes, the memory where the first chunk is stored is left intact - assertThat(new String(chunkReference.peek().bytes()), startsWith("unlimited")); - } - - /** - * This test shows that even when the {@link io.helidon.common.http.DataChunk#release()} isn't called, we don't get - * {@link OutOfMemoryError} thanks to {@link io.helidon.common.http.DataChunk#finalize()} that calls - * {@link io.helidon.common.http.DataChunk#release()} automatically. This test needs at least 1GB of heap. - *

- * This feature is not guarantied though. - * - * @throws Exception in case of an error - */ - @Test - @Disabled("Ignored because this test should run indefinitely.") - public void sendUnlimitedDataWithoutReleasingTheChunksDoesntEndsWithOutOfMemoryErrorAsLongAsChunkRefsArentKept() - throws Exception { - flood(false, false, 0, true); - } - - /** - * This test shows that when the {@link io.helidon.common.http.DataChunk#release()} is called, WebServer can run - * indefinitely and it performs perfectly while operating with low amount of memory. - * This test needs 128m of heap. - * - * @throws Exception in case of an error - */ - @Test - @Disabled("Ignored because this test should run indefinitely.") - public void sendUnlimitedDataAndReleasingTheChunksAllowToRunIndefinitely() throws Exception { - flood(true, false, 0, true); - } - - /** - * This test shows that with an unlimited number of connections, each sending 1GB of data, no - * memory leak occurs. - * - * @throws Exception in case of a problem - */ - @Test - @Disabled("Ignored because this test should run indefinitely.") - public void sendLimitedDataInAnUnlimitedLoopAndReleasingTheChunksAllowToRunIndefinitely() throws Exception { - while (true) { - flood(true, false, 1_000, false); - } - } - - /** - * This test shows that in case that {@link ByteBufRequestChunk#finalize()} is disabled, there would - * remain unreleased {@link io.netty.buffer.ByteBuf} instances that the {@link HttpInitializer} should - * take care of. - * - * @throws Exception in case of an error - */ - @Test - @Disabled("Ignored because this test should run indefinitely.") - public void sendLimitedDataInAnUnlimitedLoopWithoutReleasingIt() throws Exception { - while (true) { - flood(false, false, 10, false); - } - } - - /** - * This test shows that with a WebServer shutdown, no memory leak occurs. - * - * If {@link ReferenceHoldingQueue#shutdown()} is not called, the {@code DEFAULT} pool arena - * of the {@link io.netty.buffer.PooledByteBufAllocator} would grow without any limits. - * - * @throws Exception in case of an error - */ - @Test - @Disabled("Ignored because this test should run indefinitely.") - public void sendLimitedDataInAnUnlimitedLoopWithoutReleasingItToRecreatedWebServer() throws Exception { - // recreate the WebServer 10x times - for (int i = 0; i < 10; i++) { - flood(false, false, 1_000, false); - webServer.shutdown() - .toCompletableFuture() - .join(); - startServer(); - } - webServer.shutdown().toCompletableFuture().join(); - - Thread.currentThread().join(); - } - - private void flood(boolean release, boolean keepChunks, int limit, boolean doAssert) throws Exception { - try (SocketHttpClient s = new ChunkedSocketHttpClient(BytesReuseV2ApiTest.webServer, limit)) { - s.request(Http.Method.POST, "/subscriber?test=unlimited&release=" + release + "&keep_chunks=" + keepChunks, null); - - // so we got a OutOfMemoryError - if (doAssert) { - assertThat(s.receive(), endsWith("\nError: Java heap space\n0\n\n")); - } - } - } -} diff --git a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/CipherSuiteV2ApiTest.java b/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/CipherSuiteV2ApiTest.java deleted file mode 100644 index df17447ea48..00000000000 --- a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/CipherSuiteV2ApiTest.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2021, 2022 Oracle and/or its affiliates. - * - * 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 io.helidon.reactive.webserver; - -import java.util.List; -import java.util.concurrent.CompletionException; -import java.util.concurrent.TimeUnit; -import java.util.logging.Logger; - -import javax.net.ssl.SSLHandshakeException; - -import io.helidon.config.Config; -import io.helidon.config.ConfigSources; -import io.helidon.reactive.webclient.WebClient; -import io.helidon.reactive.webclient.WebClientTls; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -/** - * The test of SSL Netty layer. - */ -@Deprecated(since = "3.0.0", forRemoval = true) -public class CipherSuiteV2ApiTest { - - private static final Logger LOGGER = Logger.getLogger(CipherSuiteV2ApiTest.class.getName()); - private static final Config CONFIG = Config.just(() -> ConfigSources.classpath("cipherSuiteConfig.yaml").build()); - - private static WebServer webServer; - private static WebClient clientOne; - private static WebClient clientTwo; - - @BeforeAll - public static void startServer() throws Exception { - webServer = WebServer.builder(Routing.builder().get("/", (req, res) -> res.send("It works!"))) - .config(CONFIG.get("server")) - .addNamedRouting("second", Routing.builder().get("/", (req, res) -> res.send("It works! Second!"))) - .build() - .start() - .toCompletableFuture() - .get(10, TimeUnit.SECONDS); - - clientOne = WebClient.builder() - .baseUri("https://localhost:" + webServer.port()) - .config(CONFIG.get("client")) - .build(); - - clientTwo = WebClient.builder() - .baseUri("https://localhost:" + webServer.port()) - .tls(WebClientTls.builder() - .trustAll(true) - .allowedCipherSuite(List.of("TLS_DHE_RSA_WITH_AES_256_GCM_SHA384")) - .build()) - .build(); - - LOGGER.info("Started secured server at: https://localhost:" + webServer.port()); - } - - @AfterAll - public static void close() throws Exception { - if (webServer != null) { - webServer.shutdown() - .toCompletableFuture() - .get(10, TimeUnit.SECONDS); - } - } - - @Test - public void testSupportedAlgorithm() { - String response = clientOne.get() - .request(String.class) - .await(); - assertThat(response, is("It works!")); - - response = clientTwo.get() - .uri("https://localhost:" + webServer.port("second")) - .request(String.class) - .await(); - assertThat(response, is("It works! Second!")); - } - - @Test - public void testUnsupportedAlgorithm() { - CompletionException completionException = assertThrows(CompletionException.class, - () -> clientOne.get() - .uri("https://localhost:" + webServer.port("second")) - .request() - .await()); - assertThat(completionException.getCause(), instanceOf(SSLHandshakeException.class)); - assertThat(completionException.getCause().getMessage(), is("Received fatal alert: handshake_failure")); - - completionException = assertThrows(CompletionException.class, () -> clientTwo.get().request().await()); - assertThat(completionException.getCause(), instanceOf(SSLHandshakeException.class)); - assertThat(completionException.getCause().getMessage(), is("Received fatal alert: handshake_failure")); - } - -} diff --git a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/CloseConnectionV2ApiTest.java b/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/CloseConnectionV2ApiTest.java deleted file mode 100644 index 1346d5bbb82..00000000000 --- a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/CloseConnectionV2ApiTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2021, 2022 Oracle and/or its affiliates. - * - * 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 io.helidon.reactive.webserver; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.time.Duration; -import java.time.temporal.ChronoUnit; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -import io.helidon.common.http.DataChunk; -import io.helidon.common.http.Http; -import io.helidon.common.reactive.Multi; -import io.helidon.common.reactive.Single; -import io.helidon.common.testing.http.junit5.SocketHttpClient; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.not; - -@Deprecated(since = "3.0.0", forRemoval = true) -public class CloseConnectionV2ApiTest { - - private static final Duration TIME_OUT = Duration.of(30, ChronoUnit.SECONDS); - private WebServer webServer; - private CompletableFuture closedConnectionFuture; - private final ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor(); - - @BeforeEach - void setUp() throws UnknownHostException { - closedConnectionFuture = new CompletableFuture<>(); - InetAddress localHost = InetAddress.getLocalHost(); - webServer = WebServer - .builder() - .port(0) - .host("localhost") - .routing(Routing.builder() - .get((req, res) -> { - res.send(Multi - .interval(100, 100, TimeUnit.MILLISECONDS, exec) - .peek(i -> { - if (i > 2) { - req.closeConnection() - .onError(closedConnectionFuture::completeExceptionally) - .forSingle(closedConnectionFuture::complete); - } - }) - .map(i -> "item" + i) - .limit(10) - .map(String::getBytes) - .map(DataChunk::create) - .flatMap(bb -> Multi.just(bb, DataChunk.create(true))) - ); - }) - .build()) - .build() - .start() - .await(TIME_OUT); - } - - @AfterEach - void tearDown() { - webServer.shutdown().await(TIME_OUT); - exec.shutdown(); - } - - @Test - void closeManually() throws Exception { - try (SocketHttpClient c = SocketHttpClient.create(webServer.port())) { - c.request(Http.Method.GET); - String result = c.receive(); - Single.create(closedConnectionFuture, true).await(TIME_OUT); - c.assertConnectionIsClosed(); - assertThat(result, containsString("item0")); - assertThat(result, not(containsString("item9"))); - } - } -} diff --git a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/CompressionV2ApiTest.java b/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/CompressionV2ApiTest.java deleted file mode 100644 index 4f98157a139..00000000000 --- a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/CompressionV2ApiTest.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (c) 2020, 2022 Oracle and/or its affiliates. - * - * 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 io.helidon.reactive.webserver; - -import java.time.Duration; -import java.time.temporal.ChronoUnit; -import java.util.List; -import java.util.logging.Logger; - -import io.helidon.common.http.ClientResponseHeaders; -import io.helidon.common.http.Http; -import io.helidon.common.testing.http.junit5.SocketHttpClient; -import io.helidon.reactive.webclient.WebClient; -import io.helidon.reactive.webclient.WebClientRequestBuilder; -import io.helidon.reactive.webclient.WebClientResponse; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import static io.helidon.common.testing.http.junit5.HttpHeaderMatcher.hasHeader; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.MatcherAssert.assertThat; - -/** - * Tests support for compression in the webserver. - */ -@Deprecated(since = "3.0.0", forRemoval = true) -class CompressionV2ApiTest { - private static final Logger LOGGER = Logger.getLogger(CompressionV2ApiTest.class.getName()); - private static final Duration TIME_OUT = Duration.of(10, ChronoUnit.SECONDS); - - private static WebServer webServer; - - private static WebClient webClient; - - @BeforeAll - static void startServer() throws Exception { - startServer(0); - } - - @AfterAll - static void close() throws Exception { - if (webServer != null) { - webServer.shutdown() - .await(TIME_OUT); - } - } - - /** - * Start the Web Server - * - * @param port the port on which to start the server; if less than 1, - * the port is dynamically selected - */ - private static void startServer(int port) { - webServer = WebServer.builder() - .host("localhost") - .port(port) - .routing(Routing.builder() - .get("/compressed", (req, res) -> { - String payload = "It works!"; - res.send(payload); - }) - .build()) - .enableCompression(true) // compression - .build() - .start() - .await(TIME_OUT); - - webClient = WebClient.builder() - .baseUri("http://localhost:" + webServer.port()) - .validateHeaders(false) - .keepAlive(true) - .build(); - - LOGGER.info("Started server at: https://localhost:" + webServer.port()); - } - - /** - * Test that "content-encoding" header is "gzip". Note that we use a - * {@code SocketHttpClient} as other clients may remove this header after - * processing. - * - * @throws Exception if error occurs. - */ - @Test - void testGzipHeader() throws Exception { - List requestHeaders = List.of("Accept-Encoding: gzip"); - try (SocketHttpClient c = SocketHttpClient.create(webServer.port())) { - String s = c.sendAndReceive("/compressed", - Http.Method.GET, null, - requestHeaders); - - ClientResponseHeaders responseHeaders = SocketHttpClient.headersFromResponse(s); - assertThat(responseHeaders, hasHeader(Http.Header.create(Http.Header.CONTENT_ENCODING, "gzip"))); - } - } - - /** - * Test that "content-encoding" is "deflate". Note that we use a - * {@code SocketHttpClient} as other clients may remove this header after - * processing. - * - * @throws Exception if error occurs. - */ - @Test - void testDeflateHeader() throws Exception { - List requestHeaders = List.of("Accept-Encoding: deflate"); - try (SocketHttpClient c = SocketHttpClient.create(webServer.port())) { - String s = c.sendAndReceive("/compressed", - Http.Method.GET, null, - requestHeaders); - - ClientResponseHeaders responseHeaders = SocketHttpClient.headersFromResponse(s); - assertThat(responseHeaders, hasHeader(Http.Header.create(Http.Header.CONTENT_ENCODING, "deflate"))); - } - } - - /** - * Test that the entity is decompressed using the correct algorithm. - * - * @throws Exception if error occurs. - */ - @Test - void testGzipContent() throws Exception { - WebClientRequestBuilder builder = webClient.get(); - builder.headers().add("Accept-Encoding", "gzip"); - WebClientResponse response = builder.path("/compressed") - .request() - .await(TIME_OUT); - assertThat(response.content().as(String.class).get(), equalTo("It works!")); - } - - /** - * Test that the entity is decompressed using the correct algorithm. - * - * @throws Exception if error occurs. - */ - @Test - void testDeflateContent() throws Exception { - WebClientRequestBuilder builder = webClient.get(); - builder.headers().add("Accept-Encoding", "deflate"); - WebClientResponse response = builder.path("/compressed") - .request() - .await(TIME_OUT); - assertThat(response.content().as(String.class).get(), equalTo("It works!")); - } -} diff --git a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/EncodingV2ApiTest.java b/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/EncodingV2ApiTest.java deleted file mode 100644 index a6b2d6324b9..00000000000 --- a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/EncodingV2ApiTest.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (c) 2019, 2022 Oracle and/or its affiliates. - * - * 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 io.helidon.reactive.webserver; - -import java.time.Duration; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.logging.Logger; - -import io.helidon.common.http.Http; -import io.helidon.common.testing.http.junit5.SocketHttpClient; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.CoreMatchers.startsWith; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.collection.IsMapContaining.hasEntry; - -/** - * The PlainTest. - */ -@Deprecated(since = "3.0.0", forRemoval = true) -public class EncodingV2ApiTest { - - private static final Logger LOGGER = Logger.getLogger(EncodingV2ApiTest.class.getName()); - private static final Duration TIMEOUT = Duration.ofSeconds(10); - - private static WebServer webServer; - private static SocketHttpClient client; - - /** - * Start the Web Server - * - * @param port the port on which to start the server; if less than 1, - * the port is dynamically selected - * @throws Exception in case of an error - */ - private static void startServer(int port) throws Exception { - webServer = WebServer.builder() - .host("localhost") - .port(port) - .routing(Routing.builder() - .get("/foo", (req, res) -> res.send("It works!")) - .get("/foo/{bar}", (req, res) -> res.send(req.path().param("bar"))) - .any(Handler.create(String.class, (req, res, entity) -> res.send("Oops " + entity))) - .build()) - .build() - .start() - .await(TIMEOUT); - client = SocketHttpClient.create(webServer.port()); - - LOGGER.info("Started server at: https://localhost:" + webServer.port()); - } - - /** - * Test path decoding and matching. - * - * @throws Exception If an error occurs. - */ - @Test - void testEncodedUrl() throws Exception { - String s = client.sendAndReceive("/f%6F%6F", Http.Method.GET, null); - assertThat(cutPayloadAndCheckHeadersFormat(s), is("It works!")); - Map headers = cutHeaders(s); - assertThat(headers, hasEntry("connection", "keep-alive")); - } - - /** - * Test path decoding with params and matching. - * - * @throws Exception If an error occurs. - */ - @Test - void testEncodedUrlParams() throws Exception { - String s = client.sendAndReceive("/f%6F%6F/b%61%72", Http.Method.GET, null); - assertThat(cutPayloadAndCheckHeadersFormat(s), is("bar")); - Map headers = cutHeaders(s); - assertThat(headers, hasEntry("connection", "keep-alive")); - } - - private Map cutHeaders(String response) { - assertThat(response, notNullValue()); - int index = response.indexOf("\n\n"); - if (index < 0) { - throw new AssertionError("Missing end of headers in response!"); - } - String hdrsPart = response.substring(0, index); - String[] lines = hdrsPart.split("\\n"); - if (lines.length <= 1) { - return Collections.emptyMap(); - } - Map result = new HashMap<>(lines.length - 1); - boolean first = true; - for (String line : lines) { - if (first) { - first = false; - continue; - } - int i = line.indexOf(':'); - if (i < 0) { - throw new AssertionError("Header without semicolon - " + line); - } - result.put(line.substring(0, i).trim(), line.substring(i + 1).trim()); - } - return result; - } - - private String cutPayloadAndCheckHeadersFormat(String response) { - assertThat(response, notNullValue()); - int index = response.indexOf("\n\n"); - if (index < 0) { - throw new AssertionError("Missing end of headers in response!"); - } - String headers = response.substring(0, index); - String[] lines = headers.split("\\n"); - assertThat(lines[0], startsWith("HTTP/")); - for (int i = 1; i < lines.length; i++) { - assertThat(lines[i], containsString(":")); - } - return response.substring(index + 2); - } - - @BeforeAll - static void startServer() throws Exception { - // start the server at a free port - startServer(0); - } - - @AfterAll - static void close() throws Exception { - if (webServer != null) { - webServer.shutdown() - .await(TIMEOUT); - } - } -} diff --git a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/FilterV2ApiTest.java b/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/FilterV2ApiTest.java deleted file mode 100644 index 07042653324..00000000000 --- a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/FilterV2ApiTest.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (c) 2020, 2022 Oracle and/or its affiliates. - * - * 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 io.helidon.reactive.webserver; - -import java.time.Duration; -import java.util.concurrent.Flow; -import java.util.concurrent.atomic.AtomicLong; -import java.util.logging.Logger; - -import io.helidon.common.GenericType; -import io.helidon.common.http.DataChunk; -import io.helidon.common.http.Http; -import io.helidon.common.reactive.Multi; -import io.helidon.common.reactive.Single; -import io.helidon.common.testing.http.junit5.SocketHttpClient; -import io.helidon.reactive.media.common.MessageBodyWriter; -import io.helidon.reactive.media.common.MessageBodyWriterContext; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; - -@Deprecated(since = "3.0.0", forRemoval = true) -public class FilterV2ApiTest { - - private static final Logger LOGGER = Logger.getLogger(FilterV2ApiTest.class.getName()); - private static final Duration TIMEOUT = Duration.ofSeconds(10); - private static WebServer webServer; - private static final AtomicLong filterItemCounter = new AtomicLong(0); - private static SocketHttpClient client; - - /** - * Start the Web Server - * - * @param port the port on which to start the server; if less than 1, - * the port is dynamically selected - * @throws Exception in case of an error - */ - private static void startServer(int port) { - webServer = WebServer.builder() - .host("localhost") - .port(port) - .routing(Routing.builder().any((req, res) -> { - res.headers().add(Http.Header.TRANSFER_ENCODING, "chunked"); - req.next(); - }) - .get("/dataChunkPublisher", (req, res) -> { - res.registerFilter(pub -> Multi.create(pub) - .peek(chunk -> filterItemCounter.incrementAndGet())); - res.send(Single.just("Test").map(s -> DataChunk.create(s.getBytes()))); - }) - .get("/dataChunkPublisherNoFilters", (req, res) -> { - res.registerFilter(pub -> Multi.create(pub) - .peek(chunk -> filterItemCounter.incrementAndGet())); - res.send(Single.just("Test").map(s -> DataChunk.create(s.getBytes())), false); - }) - .get("/customWriter", (req, res) -> { - res.registerFilter(pub -> Multi.create(pub) - .peek(chunk -> filterItemCounter.incrementAndGet())); - res.send(ctx -> { - return ctx.marshall(Single.just("Test"), new MessageBodyWriter<>() { - - @Override - public PredicateResult accept(final GenericType type, - final MessageBodyWriterContext context) { - return PredicateResult.SUPPORTED; - } - - @Override - public Flow.Publisher write(final Single single, - final GenericType type, - final MessageBodyWriterContext context) { - return single.map(s -> DataChunk.create(s.getBytes())); - } - }, GenericType.create(String.class)); - }); - }) - .build()) - .build() - .start() - .await(TIMEOUT); - - client = SocketHttpClient.create(webServer.port()); - LOGGER.info("Started server at: https://localhost:" + webServer.port()); - } - - @BeforeAll - static void startServer() throws Exception { - // start the server at a free port - startServer(0); - } - - @AfterAll - static void close() throws Exception { - if (webServer != null) { - webServer.shutdown() - .await(TIMEOUT); - } - if (client != null) { - client.close(); - } - } - - @BeforeEach - void resetClient() { - client.disconnect(); - client.connect(); - } - - @Test - void customWriterTest() { - filterItemCounter.set(0); - String response = client.sendAndReceive("/customWriter", Http.Method.GET, null); - assertThat(response, containsString("200 OK")); - assertThat("Filter should been called once.", - filterItemCounter.get(), is(1L)); - } - - @Test - void dataChunkPublisherTest() { - filterItemCounter.set(0); - String response = client.sendAndReceive("/dataChunkPublisher", Http.Method.GET, null); - assertThat(response, containsString("200 OK")); - assertThat("Filter should been called once.", - filterItemCounter.get(), is(1L)); - } - - @Test - void dataChunkPublisherNoFiltersTest() { - filterItemCounter.set(0); - client.sendAndReceive("/dataChunkPublisherNoFilters", Http.Method.GET, null); - assertThat("Filter shouldn't been called.", - filterItemCounter.get(), is(0L)); - } -} diff --git a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/Gh1893V2ApiTest.java b/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/Gh1893V2ApiTest.java deleted file mode 100644 index 8ed2757632d..00000000000 --- a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/Gh1893V2ApiTest.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (c) 2022 Oracle and/or its affiliates. - * - * 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 io.helidon.reactive.webserver; - -import java.time.Duration; -import java.util.List; - -import io.helidon.common.http.ClientResponseHeaders; -import io.helidon.common.http.DirectHandler; -import io.helidon.common.http.DirectHandler.TransportResponse; -import io.helidon.common.http.Http; -import io.helidon.common.http.ServerResponseHeaders; -import io.helidon.common.testing.http.junit5.HttpHeaderMatcher; -import io.helidon.common.testing.http.junit5.SocketHttpClient; -import io.helidon.reactive.webclient.WebClient; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; - -/** - * Problem Description - * If the requested URL contains invalid characters, e.g. { (which are not encoded), Helidon returns HTTP 400 with message - * Illegal character in query at index ... This response comes from io.helidon.reactive.webserver.ForwardingHandler, line 112. The - * exception containing this message is throw from constructor of BareRequestImpl, because the passed URI violates the - * corresponding RFC. - * - * We use Helidon as a web server that serves static files (UI application). If user opens URL - * https:///tnt/flow/page=overview, browser shows the application. But when user puts (by accident) e.g. { to the URL - * (https:///tnt/flow?page=overview{) and hits enter to reload the page, he can see blank screen containing error message - * Illegal character in query at index .... - * - * When URL isn't valid, we want the user to be redirected to an application-specific error page, e.g. to - * https:///tnt/page-does-not-exist. - * - * Steps to reproduce - * This problem can be reproduced from curl. It's important to append -g parameter so that curl doesn't encode the special - * characters. - * - * Sample request: curl -g "http://hostname/tnt/page2{ - */ -@Deprecated(since = "3.0.0", forRemoval = true) -class Gh1893V2ApiTest { - private static final Duration TIMEOUT = Duration.ofSeconds(10); - private static final String CUSTOM_REASON_PHRASE = "Custom-bad-request"; - private static final String CUSTOM_ENTITY = "There we go"; - - private static WebServer webServer; - private static WebClient webClient; - private static SocketHttpClient socketClient; - - @BeforeAll - static void startServer() { - webServer = WebServer.builder() - .host("localhost") - .directHandler(Gh1893V2ApiTest::badRequestHandler, DirectHandler.EventType.BAD_REQUEST) - .routing(Routing.builder() - .get("/", (req, res) -> res.send("Hi"))) - .build() - .start() - .await(TIMEOUT); - - webClient = WebClient.builder() - .baseUri("http://localhost:" + webServer.port()) - .build(); - - socketClient = SocketHttpClient.create(webServer.port()); - } - - private static TransportResponse badRequestHandler(DirectHandler.TransportRequest request, - DirectHandler.EventType eventType, - Http.Status defaultStatus, - ServerResponseHeaders headers, - String message) { - if (request.path().equals("/redirect")) { - return TransportResponse.builder() - .status(Http.Status.TEMPORARY_REDIRECT_307) - .header(Http.Header.LOCATION, "/errorPage") - .build(); - } - return TransportResponse.builder() - .status(Http.Status.create(Http.Status.BAD_REQUEST_400.code(), - CUSTOM_REASON_PHRASE)) - .entity(CUSTOM_ENTITY) - .build(); - } - - @AfterAll - static void stopServer() throws Exception { - if (webServer != null) { - webServer.shutdown() - .await(TIMEOUT); - } - if (socketClient != null) { - socketClient.close(); - } - webServer = null; - webClient = null; - } - - @BeforeEach - void resetSocketClient() { - socketClient.disconnect(); - socketClient.connect(); - } - - @Test - void testOk() { - String response = webClient.get() - .request(String.class) - .await(TIMEOUT); - - assertThat(response, is("Hi")); - } - - @Test - void testInvalidRequest() throws Exception { - // wrong content length - String response = socketClient.sendAndReceive("/", - Http.Method.GET, - null, - List.of(Http.Header.CONTENT_LENGTH.defaultCase() + ": 47a")); - - assertThat(response, containsString("400 " + CUSTOM_REASON_PHRASE)); - assertThat(response, containsString(CUSTOM_ENTITY)); - } - - @Test - void testInvalidRequestWithRedirect() { - // wrong content length - String response = socketClient.sendAndReceive("/redirect", - Http.Method.GET, - null, - List.of(Http.Header.CONTENT_LENGTH.defaultCase() + ": 47a")); - - assertThat(SocketHttpClient.statusFromResponse(response), is(Http.Status.TEMPORARY_REDIRECT_307)); - - ClientResponseHeaders headers = SocketHttpClient.headersFromResponse(response); - assertThat(headers, HttpHeaderMatcher.hasHeader(Http.Header.create(Http.Header.LOCATION, "/errorPage"))); - } - - @Test - void testInvalidUri() throws Exception { - // must fail on creation of bare request impl (URI.create()) - String response = socketClient.sendAndReceive("/bad{", - Http.Method.GET, - null); - - assertThat(response, containsString("400 " + CUSTOM_REASON_PHRASE)); - assertThat(response, containsString(CUSTOM_ENTITY)); - } - - @Test - void testInvalidFirstLine() throws Exception { - try (SocketHttpClient client = SocketHttpClient.create(webServer.port())) { - - client.request("GET IT", "/", "HTTP/1.1", null, null, null); - String response = client.receive(); - - assertThat(response, containsString("400 " + CUSTOM_REASON_PHRASE)); - assertThat(response, containsString(CUSTOM_ENTITY)); - } - } -} diff --git a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/Gh377V2ApiTest.java b/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/Gh377V2ApiTest.java deleted file mode 100644 index 654bafbe08d..00000000000 --- a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/Gh377V2ApiTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2020, 2022 Oracle and/or its affiliates. - * - * 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 io.helidon.reactive.webserver; - -import java.util.concurrent.TimeUnit; - -import org.junit.jupiter.api.Test; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.greaterThan; -import static org.junit.jupiter.api.Assertions.assertThrows; - -/** - * Tests correct behavior when webserver is shutdown and an attempt is made to start it again. - * Github issue #377. - */ -@Deprecated(since = "3.0.0", forRemoval = true) -class Gh377V2ApiTest { - @Test - void testRestart() { - WebServer webServer = WebServer.builder() - .host("localhost") - .routing(Routing.builder() - .get("/", (req, res) -> res.send("Hello World")) - .build()) - .build() - .start() - .await(10, TimeUnit.SECONDS); - - assertThat(webServer.port(), greaterThan(0)); - - // shutdown - webServer.shutdown() - .await(10, TimeUnit.SECONDS); - - assertThat(webServer.port(), is(-1)); - - // attempt to start again - assertThrows(IllegalStateException.class, () -> webServer.start() - .await(10, TimeUnit.SECONDS)); - - assertThat(webServer.port(), is(-1)); - } -} diff --git a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/HttpPipelineTest.java b/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/HttpPipelineTest.java index f36d7e5c9ce..890783e6996 100644 --- a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/HttpPipelineTest.java +++ b/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/HttpPipelineTest.java @@ -28,6 +28,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.containsString; @@ -37,6 +38,7 @@ /** * Test support for HTTP 1.1 pipelining. */ +@Disabled("Fails on build pipelines, will change once on Nima") public class HttpPipelineTest { private static final Logger LOGGER = Logger.getLogger(HttpPipelineTest.class.getName()); private static final Duration TIMEOUT = Duration.ofSeconds(30); diff --git a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/HttpPipelineV2ApiTest.java b/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/HttpPipelineV2ApiTest.java deleted file mode 100644 index e2322bbc6d8..00000000000 --- a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/HttpPipelineV2ApiTest.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2020, 2022 Oracle and/or its affiliates. - * - * 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 io.helidon.reactive.webserver; - -import java.time.Duration; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.logging.Logger; - -import io.helidon.common.http.Http; -import io.helidon.common.testing.http.junit5.SocketHttpClient; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.MatcherAssert.assertThat; - -/** - * Test support for HTTP 1.1 pipelining. - */ -@Deprecated(since = "3.0.0", forRemoval = true) -public class HttpPipelineV2ApiTest { - private static final Logger LOGGER = Logger.getLogger(HttpPipelineV2ApiTest.class.getName()); - private static final Duration TIMEOUT = Duration.ofSeconds(30); - private static final AtomicInteger counter = new AtomicInteger(0); - private static final ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(1); - - private static WebServer webServer; - private static SocketHttpClient client; - - @BeforeAll - public static void startServer() throws Exception { - startServer(0); - } - - @AfterAll - public static void close() throws Exception { - if (webServer != null) { - webServer.shutdown() - .await(TIMEOUT); - } - } - - private static void startServer(int port) throws Exception { - webServer = WebServer.builder() - .host("localhost") - .port(port) - .routing(Routing.builder() - .put("/", (req, res) -> { - counter.set(0); - log("put server"); - req.content() - .as(String.class) - .forSingle(it -> { - log("put: " + it); - res.send(); - }); - }) - .get("/", (req, res) -> { - log("get server"); - int n = counter.getAndIncrement(); - int delay = (n % 2 == 0) ? 1000 : 0; // alternate delay 1 second and no delay - executor.schedule(() -> { - log("get server schedule"); - res.status(Http.Status.OK_200).send("Response " + n + "\n"); - }, - delay, TimeUnit.MILLISECONDS); - }) - .build()) - .build() - .start() - .await(TIMEOUT); - - client = SocketHttpClient.create(webServer.port()); - - LOGGER.info("Started server at: https://localhost:" + webServer.port()); - } - - /** - * Pipelines request_0 and request_1 and makes sure responses are returned in the - * correct order. Note that the server will delay the response for request_0 to - * make sure they are properly synchronized. - * - * @throws Exception If there are connection problems. - */ - @Test - public void testPipelining() throws Exception { - client.request(Http.Method.PUT, "/"); // reset server - client.request(Http.Method.GET, null); // request_0 - client.request(Http.Method.GET, null); // request_1 - log("put client"); - String reset = client.receive(); - assertThat(reset, notNullValue()); - log("request0 client"); - String request_0 = client.receive(); - assertThat(request_0, containsString("Response 0")); - log("request1 client"); - String request_1 = client.receive(); - assertThat(request_1, containsString("Response 1")); - } - - private static void log(String prefix) { - LOGGER.info(() -> prefix + " " + Thread.currentThread()); - } -} diff --git a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/KeepAliveV2ApiTest.java b/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/KeepAliveV2ApiTest.java deleted file mode 100644 index dc5b56e8991..00000000000 --- a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/KeepAliveV2ApiTest.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2022 Oracle and/or its affiliates. - * - * 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 io.helidon.reactive.webserver; - -import java.nio.ByteBuffer; -import java.time.Duration; -import java.util.Optional; -import java.util.concurrent.CompletionException; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -import io.helidon.common.http.DataChunk; -import io.helidon.common.reactive.Multi; -import io.helidon.logging.common.LogConfig; -import io.helidon.reactive.webclient.WebClient; -import io.helidon.reactive.webclient.WebClientResponse; - -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.util.AsciiString; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.RepeatedTest; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.equalToIgnoringCase; -import static org.hamcrest.Matchers.hasEntry; -import static org.hamcrest.Matchers.hasKey; -import static org.hamcrest.Matchers.not; - -@Deprecated(since = "3.0.0", forRemoval = true) -public class KeepAliveV2ApiTest { - private static WebServer server; - private static WebClient webClient; - - @BeforeAll - static void setUp() { - LogConfig.configureRuntime(); - server = WebServer.builder() - .routing(Routing.builder() - .register("/close", rules -> rules.any((req, res) -> { - req.content().forEach(dataChunk -> { - // consume only first from two chunks - dataChunk.release(); - throw new RuntimeException("BOOM!"); - }).exceptionally(res::send); - })) - .register("/plain", rules -> rules.any((req, res) -> { - req.content() - .forEach(DataChunk::release) - .onComplete(res::send) - .ignoreElement(); - })) - .build()) - .build(); - server.start().await(); - String serverUrl = "http://localhost:" + server.port(); - webClient = WebClient.builder() - .baseUri(serverUrl) - .keepAlive(true) - .build(); - - } - - @AfterAll - static void afterAll() { - server.shutdown(); - } - - @RepeatedTest(10) - void closeWithKeepAliveUnconsumedRequest() { - testCall(webClient, true, "/close", 500, HttpHeaderValues.CLOSE, true); - } - - @RepeatedTest(10) - void sendWithoutKeepAlive() { - testCall(webClient, false, "/plain", 200, null, false); - } - - @RepeatedTest(10) - void sendWithKeepAlive() { - testCall(webClient, true, "/plain", 200, HttpHeaderValues.KEEP_ALIVE, false); - } - - private static void testCall(WebClient webClient, - boolean keepAlive, - String path, - int expectedStatus, - AsciiString expectedConnectionHeader, - boolean ignoreConnectionClose) { - WebClientResponse res = null; - try { - res = webClient - .put() - .keepAlive(keepAlive) - .path(path) - .submit(Multi.interval(10, TimeUnit.MILLISECONDS, Executors.newSingleThreadScheduledExecutor()) - .limit(2) - .map(l -> "msg_"+ l) - .map(String::getBytes) - .map(ByteBuffer::wrap) - .map(bb -> DataChunk.create(true, true, bb)) - ) - .await(Duration.ofMinutes(5)); - - assertThat(res.status().code(), is(expectedStatus)); - if (expectedConnectionHeader != null) { - assertThat(res.headers().toMap(), - hasEntry(equalToIgnoringCase(HttpHeaderNames.CONNECTION.toString()), - contains(expectedConnectionHeader.toString()))); - } else { - assertThat(res.headers().toMap(), not(hasKey(HttpHeaderNames.CONNECTION.toString()))); - } - res.content().forEach(DataChunk::release); - } catch (CompletionException e) { - if (ignoreConnectionClose && e.getMessage().contains("Connection reset")) { - // this is an expected (intermittent) result - due to a natural race (between us writing the request - // data and server responding), we may either get a response - // or the socket may be closed for writing (the reset comes from an attempt to write entity to a closed - // socket) - return; - } - throw e; - } finally { - Optional.ofNullable(res).ifPresent(WebClientResponse::close); - } - } -} \ No newline at end of file diff --git a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/MultiPortV2ApiTest.java b/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/MultiPortV2ApiTest.java deleted file mode 100644 index 788e4b3c424..00000000000 --- a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/MultiPortV2ApiTest.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (c) 2017, 2022 Oracle and/or its affiliates. - * - * 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 io.helidon.reactive.webserver; - -import io.helidon.common.configurable.Resource; -import io.helidon.common.http.Http; -import io.helidon.common.pki.KeyConfig; -import io.helidon.config.Config; -import io.helidon.config.ConfigSources; -import io.helidon.reactive.webclient.WebClient; -import io.helidon.reactive.webclient.WebClientTls; - -import org.hamcrest.Matcher; -import org.hamcrest.core.AllOf; -import org.hamcrest.core.StringContains; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.Is.is; -import static org.junit.jupiter.api.Assertions.fail; - -/** - * The MultiPortTest. - */ -@Deprecated(since = "3.0.0", forRemoval = true) -public class MultiPortV2ApiTest { - - private static WebClient webClient; - private Handler commonHandler; - private WebServer webServer; - private WebServerTls webServerTls; - - @BeforeAll - public static void createClientAcceptingAllCertificates() { - webClient = WebClient.builder() - .followRedirects(true) - .tls(WebClientTls.builder().trustAll(true).build()) - .build(); - } - - @BeforeEach - public void init() throws Exception { - - commonHandler = new Handler() { - - private volatile int counter = 0; - - @Override - public void accept(ServerRequest req, ServerResponse res) { - res.send("Root! " + ++counter); - } - }; - - webServerTls = WebServerTls.builder() - .privateKey(KeyConfig.keystoreBuilder() - .keystore(Resource.create("ssl/certificate.p12")) - .keystorePassphrase("helidon".toCharArray()) - .build()) - .build(); - } - - @AfterEach - public void tearDown() throws Exception { - if (webServer != null) { - webServer.shutdown(); - } - } - - private void assertResponse(final String protocol, int port, String path, Matcher matcher) { - try { - webClient.get() - .uri(protocol + "://localhost:" + port) - .path(path) - .request(String.class) - .thenAccept(it -> assertThat("Unexpected response: " + it, it, matcher)) - .toCompletableFuture() - .get(); - } catch (Exception e) { - fail(e); - } - } - - @Test - public void programmaticNoCompound() throws Exception { - - WebServer webServer8080 = WebServer.create( - Routing.builder() - .get("/", commonHandler) - .get("/variable", (req, res) -> res.send("Variable 8080"))); - - WebServer webServer8443 = WebServer.builder( - Routing.builder() - .get("/", commonHandler) - .get("/variable", (req, res) -> res.send("Variable 8443"))) - .tls(webServerTls) - .build(); - - try { - webServer8080.start().toCompletableFuture().join(); - webServer8443.start().toCompletableFuture().join(); - - System.out.println("Webserver started on port: " + webServer8080.port()); - System.out.println("Webserver started on port: " + webServer8443.port()); - - assertResponse("https", webServer8443.port(), "/", is("Root! 1")); - assertResponse("http", webServer8080.port(), "/", is("Root! 2")); - assertResponse("https", webServer8443.port(), "/", is("Root! 3")); - assertResponse("http", webServer8080.port(), "/", is("Root! 4")); - - assertResponse("https", webServer8443.port(), "/variable", is("Variable 8443")); - assertResponse("http", webServer8080.port(), "/variable", is("Variable 8080")); - } finally { - try { - webServer8080.shutdown().toCompletableFuture().join(); - } finally { - webServer8443.shutdown().toCompletableFuture().join(); - } - } - } - - @Test - public void compositeInlinedWebServer() throws Exception { - // start all of the servers - webServer = WebServer.builder( - Routing.builder() - .get("/overridden", (req, res) -> res.send("Overridden 8443")) - .get("/", commonHandler) - .get("/variable", (req, res) -> res.send("Variable 8443")) - - .build()) - .tls(webServerTls) - .addSocket(SocketConfiguration.create("plain")) - .addNamedRouting("plain", - Routing.builder() - .get("/overridden", (req, res) -> res.send("Overridden 8080")) - .get("/", commonHandler) - .get("/variable", (req, res) -> res.send("Variable 8080"))) - .build(); - - webServer.start() - .toCompletableFuture() - .join(); - - assertResponse("https", webServer.port(), "/", is("Root! 1")); - assertResponse("http", webServer.port("plain"), "/", is("Root! 2")); - assertResponse("https", webServer.port(), "/", is("Root! 3")); - assertResponse("http", webServer.port("plain"), "/", is("Root! 4")); - - assertResponse("https", webServer.port(), "/variable", is("Variable 8443")); - assertResponse("http", webServer.port("plain"), "/variable", is("Variable 8080")); - } - - @Test - public void compositeSingleRoutingWebServer() throws Exception { - // start all of the servers - webServer = WebServer.builder( - Routing.builder() - .get("/overridden", (req, res) -> res.send("Overridden BOTH")) - .get("/", commonHandler) - .get("/variable", (req, res) -> res.send("Variable BOTH"))) - .addSocket(SocketConfiguration.builder() - .name("secured") - .tls(webServerTls)) - .build(); - - webServer.start() - .toCompletableFuture() - .join(); - - assertResponse("https", webServer.port("secured"), "/", is("Root! 1")); - assertResponse("http", webServer.port(), "/", is("Root! 2")); - assertResponse("https", webServer.port("secured"), "/", is("Root! 3")); - assertResponse("http", webServer.port(), "/", is("Root! 4")); - - assertResponse("https", webServer.port("secured"), "/variable", is("Variable BOTH")); - assertResponse("http", webServer.port(), "/variable", is("Variable BOTH")); - } - - @Test - public void compositeRedirectWebServer() throws Exception { - // start all of the servers - webServer = WebServer.builder(Routing.builder() - .get("/foo", commonHandler)) - .tls(webServerTls) - .addSocket(SocketConfiguration.create("redirect")) - .addNamedRouting("redirect", - Routing.builder() - .any((req, res) -> { - res.status(Http.Status.MOVED_PERMANENTLY_301) - .headers() - .add(Http.Header.LOCATION, - String.format("https://%s:%d%s", - req.headers() - .first(Http.Header.HOST) - .map(s -> s.contains(":") ? s - .subSequence(0, s.indexOf(":")) : s) - .orElseThrow(() -> new IllegalStateException( - "Header 'Host' not found!")), - req.webServer().port(), - req.path())); - res.send(); - }) - ) - .build(); - - webServer - .start() - .toCompletableFuture() - .join(); - - webServer.start() - .toCompletableFuture() - .join(); - - WebClient webClient = WebClient.builder() - .tls(WebClientTls.builder().trustAll(true).build()) - .build(); - - assertResponse("https", webServer.port(), "/foo", is("Root! 1")); - webClient.get() - .uri("http://localhost:" + webServer.port("redirect")) - .path("/foo") - .request() - .thenApply(it -> { - assertThat("Unexpected response: " + it, - it.headers().first(Http.Header.LOCATION).get(), - AllOf.allOf(StringContains.containsString("https://localhost:"), - StringContains.containsString("/foo"))); - assertThat("Unexpected response: " + it, it.status(), is(Http.Status.MOVED_PERMANENTLY_301)); - return it; - }) - .thenCompose(it -> webClient.get() - .uri(it.headers().first(Http.Header.LOCATION).get()) - .request(String.class)) - .thenAccept(it -> assertThat("Unexpected response: " + it, it, is("Root! 2"))) - .toCompletableFuture() - .get(); - } - - @Test - public void compositeFromConfig() throws Exception { - Config config = Config.create(ConfigSources.classpath("multiport/application.yaml")); - webServer = WebServer.builder() - .host("localhost") - .routing(Routing.builder() - .get("/", (req, res) -> res.send("Plain!"))) - .config(config.get("webserver")) - .addNamedRouting("secured", - Routing.builder() - .get("/", (req, res) -> res.send("Secured!"))) - .build(); - - webServer.start() - .toCompletableFuture() - .join(); - - assertResponse("http", webServer.port(), "/", is("Plain!")); - assertResponse("https", webServer.port("secured"), "/", is("Secured!")); - } -} diff --git a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/NettyWebServerV2ApiTest.java b/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/NettyWebServerV2ApiTest.java deleted file mode 100644 index 859c90b5642..00000000000 --- a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/NettyWebServerV2ApiTest.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (c) 2017, 2022 Oracle and/or its affiliates. - * - * 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 io.helidon.reactive.webserver; - -import java.util.Collections; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.Flow.Subscription; -import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.SubmissionPublisher; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.BiConsumer; -import java.util.logging.Level; -import java.util.logging.Logger; - -import io.helidon.common.http.DataChunk; -import io.helidon.common.http.Http; -import io.helidon.common.reactive.Multi; - -import org.hamcrest.collection.IsCollectionWithSize; -import org.hamcrest.core.Is; -import org.junit.jupiter.api.Test; - -import static io.helidon.common.testing.junit5.OptionalMatcher.optionalPresent; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.core.AllOf.allOf; -import static org.hamcrest.core.IsNot.not; -import static org.hamcrest.core.StringContains.containsString; -import static org.junit.jupiter.api.Assertions.fail; - -/** - * The NettyWebServerTest. - */ -@Deprecated(since = "3.0.0", forRemoval = true) -public class NettyWebServerV2ApiTest { - - private static final Logger LOGGER = Logger.getLogger(NettyWebServerV2ApiTest.class.getName()); - - /** - * Start the test and then run: - *


-     *     seq 1000 | head -c 1000 | curl -X PUT -Ssf http://localhost:8080 --data-binary @- http://localhost:8080 --data ahoj
-     * 
- *

- * - * @throws InterruptedException if the main thread is interrupted - */ - static void main(String[] args) throws InterruptedException { - WebServer webServer = WebServer.builder() - .port(8080) - .host("localhost") - .routing(routing((breq, bres) -> { - SubmissionPublisher responsePublisher = new SubmissionPublisher<>(ForkJoinPool.commonPool(), 1024); - responsePublisher.subscribe(bres); - - final AtomicReference subscription = new AtomicReference<>(); - - // Read request and immediately write to response - Multi.create(breq.bodyPublisher()).subscribe((DataChunk chunk) -> { - DataChunk responseChunk = DataChunk.create(true, chunk::release, chunk.data()); - responsePublisher.submit(responseChunk); - ForkJoinPool.commonPool().submit(() -> { - try { - Thread.sleep(1); - subscription.get().request(ThreadLocalRandom.current().nextLong(1, 3)); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new IllegalStateException(e); - } - }); - }, (Throwable ex) -> { - LOGGER.log(Level.WARNING, - "An error occurred during the flow consumption!", - ex); - }, responsePublisher::close, (Subscription s) -> { - subscription.set(s); - s.request(1); - bres.writeStatusAndHeaders(Http.Status.CREATED_201, - Collections.emptyMap()); - }); - })) - .build(); - - webServer.start(); - Thread.currentThread().join(); - } - - private static Routing routing(BiConsumer allHandler) { - return allHandler::accept; - } - - @Test - public void testShutdown() throws Exception { - WebServer webServer = WebServer.create( - routing((bareRequest, bareResponse) -> { - })); - - long startNanos = System.nanoTime(); - webServer.start().toCompletableFuture().get(10, TimeUnit.SECONDS); - long shutdownStartNanos = System.nanoTime(); - webServer.shutdown().toCompletableFuture().get(10, TimeUnit.SECONDS); - long endNanos = System.nanoTime(); - - System.out.println("Start took: " + TimeUnit.MILLISECONDS.convert(shutdownStartNanos - startNanos, - TimeUnit.NANOSECONDS) + " ms."); - System.out.println("Shutdown took: " + TimeUnit.MILLISECONDS.convert(endNanos - shutdownStartNanos, - TimeUnit.NANOSECONDS) + " ms."); - } - - @Test - public void testSinglePortsSuccessStart() throws Exception { - WebServer webServer = WebServer.create(Routing.builder()); - - webServer.start() - .toCompletableFuture() - .join(); - - try { - assertThat(webServer.port(), greaterThan(0)); - assertThat(webServer.configuration().sockets().entrySet(), IsCollectionWithSize.hasSize(1)); - assertThat(webServer.configuration().sockets() - .get(WebServer.DEFAULT_SOCKET_NAME).port(), Is.is(webServer.configuration().port())); - } finally { - webServer.shutdown() - .toCompletableFuture() - .join(); - } - } - - @Test - public void testMultiplePortsSuccessStart() { - WebServer webServer = WebServer.builder() - .host("localhost") - .addSocket(SocketConfiguration.create("1")) - .addSocket(SocketConfiguration.create("2")) - .addSocket(SocketConfiguration.create("3")) - .addSocket(SocketConfiguration.create("4")) - .build(); - - webServer.start() - .await(); - - try { - assertThat(webServer.port(), greaterThan(0)); - assertThat(webServer.port("1"), allOf(greaterThan(0), not(webServer.port()))); - assertThat(webServer.port("2"), - allOf(greaterThan(0), not(webServer.port()), not(webServer.port("1")))); - assertThat(webServer.port("3"), - allOf(greaterThan(0), not(webServer.port()), not(webServer.port("1")), not(webServer.port("2")))); - assertThat(webServer.port("4"), - allOf(greaterThan(0), - not(webServer.port()), - not(webServer.port("1")), - not(webServer.port("2")), - not(webServer.port("3")))); - } finally { - webServer.shutdown() - .toCompletableFuture() - .join(); - } - } - - @Test - public void testMultiplePortsAllTheSame() throws Exception { - int samePort = 9999; - WebServer webServer = WebServer.builder() - .host("localhost") - .port(samePort) - .addSocket(SocketConfiguration.builder().port(samePort).name("third")) - .build(); - - assertStartFailure(webServer); - } - - @Test - public void testManyPortsButTwoTheSame() throws Exception { - int samePort = 9999; - WebServer webServer = WebServer.builder() - .host("localhost") - .port(samePort) - .addSocket(SocketConfiguration.create("1")) - .addSocket(SocketConfiguration.builder() - .name("2") - .port(samePort)) - .addSocket(SocketConfiguration.create("3")) - .addSocket(SocketConfiguration.create("4")) - .addSocket(SocketConfiguration.create("5")) - .addSocket(SocketConfiguration.create("6")) - .build(); - - assertStartFailure(webServer); - } - - private void assertStartFailure(WebServer webServer) { - - try { - webServer.start() - .await(); - - fail("Should have failed!"); - } catch (CompletionException e) { - assertThat(e.getMessage(), containsString("WebServer was unable to start")); - CompletableFuture shutdownFuture = webServer.whenShutdown().toCompletableFuture(); - assertThat("Shutdown future not as expected: " + shutdownFuture, - shutdownFuture.isDone() && !shutdownFuture.isCompletedExceptionally(), - is(true)); - - } catch (Exception e) { - fail("No other exception expected!", e); - } finally { - webServer.shutdown() - .toCompletableFuture() - .join(); - } - } - - @Test - public void unpairedRoutingCausesAFailure() throws Exception { - try { - WebServer webServer = WebServer.builder() - .host("localhost") - .addSocket(SocketConfiguration.create("matched")) - .addNamedRouting("unmatched-first", Routing.builder()) - .addNamedRouting("matched", Routing.builder()) - .addNamedRouting("unmatched-second", Routing.builder()) - .build(); - - fail("Should have thrown an exception: " + webServer); - } catch (IllegalStateException e) { - assertThat(e.getMessage(), allOf(containsString("unmatched-first"), - containsString("unmatched-second"))); - } - } - - @Test - public void additionalPairedRoutingsDoWork() { - WebServer webServer = WebServer.builder() - .host("localhost") - .addSocket(SocketConfiguration.create("matched")) - .addNamedRouting("matched", Routing.builder()) - .build(); - - assertThat(webServer.configuration().namedSocket("matched"), notNullValue()); - } - - @Test - public void additionalCoupledPairedRoutingsDoWork() { - WebServer webServer = WebServer.builder() - .host("localhost") - .addSocket(SocketConfiguration.builder() - .name("matched") - .build(), - Routing.builder().build()) - .build(); - - assertThat(webServer.configuration().namedSocket("matched"), optionalPresent()); - } -} diff --git a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/PlainV2ApiTest.java b/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/PlainV2ApiTest.java deleted file mode 100644 index 0199e8f59b5..00000000000 --- a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/PlainV2ApiTest.java +++ /dev/null @@ -1,574 +0,0 @@ -/* - * Copyright (c) 2017, 2022 Oracle and/or its affiliates. - * - * 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 io.helidon.reactive.webserver; - -import java.time.Duration; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Executors; -import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.TimeUnit; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import io.helidon.common.http.ClientResponseHeaders; -import io.helidon.common.http.DataChunk; -import io.helidon.common.http.Http; -import io.helidon.common.reactive.Multi; -import io.helidon.common.testing.http.junit5.SocketHttpClient; -import io.helidon.reactive.webclient.WebClient; - -import org.hamcrest.collection.IsIterableWithSize; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static io.helidon.common.http.Http.HeaderValues.TRANSFER_ENCODING_CHUNKED; -import static io.helidon.common.testing.http.junit5.HttpHeaderMatcher.hasHeader; -import static io.helidon.common.testing.http.junit5.HttpHeaderMatcher.noHeader; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.endsWith; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.CoreMatchers.startsWith; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.equalToIgnoringCase; -import static org.hamcrest.collection.IsMapContaining.hasEntry; - -/** - * The PlainTest. - */ -@Deprecated(since = "3.0.0", forRemoval = true) -class PlainV2ApiTest { - private static final Duration TIMEOUT = Duration.ofSeconds(25); - private static final Logger LOGGER = Logger.getLogger(PlainV2ApiTest.class.getName()); - private static final RuntimeException TEST_EXCEPTION = new RuntimeException("BOOM!"); - private static WebServer webServer; - private static SocketHttpClient client; - - /** - * Start the Web Server - */ - @BeforeAll - static void startServer() { - webServer = WebServer.builder() - .host("localhost") - .routing(Routing.builder().any((req, res) -> { - res.headers().add(Http.Header.TRANSFER_ENCODING, "chunked"); - req.next(); - }) - .any("/exception", (req, res) -> { - throw new RuntimeException("my always thrown exception"); - }) - .get("/", (req, res) -> { - res.send("It works!"); - }) - .post("/unconsumed", (req, res) -> res.send("Payload not consumed!")) - .any("/deferred", (req, res) -> ForkJoinPool.commonPool().submit(() -> { - Thread.yield(); - res.send("I'm deferred!"); - })) - .trace("/trace", (req, res) -> { - res.send("In trace!"); - }) - .get("/force-chunked", (req, res) -> { - res.headers().set(Http.HeaderValues.TRANSFER_ENCODING_CHUNKED); - res.send("abcd"); - }) - .get("/multi", (req, res) -> { - res.send(Multi.just("test 1", "test 2", "test 3") - .map(String::getBytes) - .map(DataChunk::create)); - }) - .get("/multiFirstError", (req, res) -> { - res.send(Multi.error(TEST_EXCEPTION)); - }) - .get("/multiSecondError", (req, res) -> { - res.send(Multi.concat(Multi.just("test1\n").map(s -> DataChunk.create(s.getBytes())), - Multi.error(TEST_EXCEPTION))); - }) - .get("/multiThirdError", (req, res) -> { - res.send(Multi.concat(Multi.just("test1\n").map(s -> DataChunk.create(s.getBytes())), - Multi.error(TEST_EXCEPTION))); - }) - .get("/multiDelayedThirdError", (req, res) -> { - res.send(Multi.interval(100, 100, TimeUnit.MILLISECONDS, - Executors.newSingleThreadScheduledExecutor()) - .peek(i -> { - if (i > 2) { - throw TEST_EXCEPTION; - } - }) - .map(i -> DataChunk.create(("test " + i).getBytes()))); - }) - .get("/multi", (req, res) -> { - res.send(Multi.just("test1", "test2") - .map(i -> DataChunk.create(String.valueOf(i).getBytes()))); - }) - .get("/absoluteUri", (req, res) -> { - res.send(req.absoluteUri().toString()); - }) - .any(Handler.create(String.class, (req, res, entity) -> { - res.send("It works! Payload: " + entity); - })) - .build()) - .build() - .start() - .await(TIMEOUT); - - client = SocketHttpClient.create(webServer.port()); - - LOGGER.info("Started server at: https://localhost:" + webServer.port()); - } - - @AfterAll - static void close() throws Exception { - if (webServer != null) { - webServer.shutdown() - .await(TIMEOUT); - } - if (client != null) { - client.close(); - } - } - - @BeforeEach - void resetSocketClient() { - client.disconnect(); - client.connect(); - } - @Test - void getTest() { - String s = client.sendAndReceive(Http.Method.GET, null); - ClientResponseHeaders headers = SocketHttpClient.headersFromResponse(s); - assertThat(headers, hasHeader(Http.HeaderValues.CONNECTION_KEEP_ALIVE)); - assertThat(SocketHttpClient.entityFromResponse(s, false), is("9\nIt works!\n0\n\n")); - } - - @Test - void getDeferredTest() { - String s = client.sendAndReceive("/deferred", Http.Method.GET, null); - assertThat(SocketHttpClient.entityFromResponse(s, true), is("d\nI'm deferred!\n0\n\n")); - } - - @Test - void getWithPayloadDeferredTest() { - String s = client.sendAndReceive("/deferred", Http.Method.GET, "illegal-payload"); - assertThat(SocketHttpClient.entityFromResponse(s, true), is("d\nI'm deferred!\n0\n\n")); - } - - @Test - void getWithLargePayloadDeferredTest() { - String s = client.sendAndReceive("/deferred", - Http.Method.GET, - SocketHttpClient.longData(100_000).toString()); - assertThat(SocketHttpClient.entityFromResponse(s, true), is("d\nI'm deferred!\n0\n\n")); - } - - @Test - void getWithPayloadTest() { - String s = client.sendAndReceive(Http.Method.GET, "test-payload"); - assertThat(SocketHttpClient.entityFromResponse(s, true), is("9\nIt works!\n0\n\n")); - } - - @Test - void postNoPayloadTest() { - String s = client.sendAndReceive(Http.Method.POST, null); - assertThat(SocketHttpClient.entityFromResponse(s, true), is("13\nIt works! Payload: \n0\n\n")); - } - - @Test - void simplePostTest() { - String s = client.sendAndReceive(Http.Method.POST, "test-payload"); - assertThat(SocketHttpClient.entityFromResponse(s, true), is("1f\nIt works! Payload: test-payload\n0\n\n")); - } - - @Test - void twoGetsTest() { - getTest(); - getTest(); - } - - @Test - void twoGetsWithPayloadTest() { - getWithPayloadTest(); - getWithPayloadTest(); - } - - @Test - void testTwoGetsTheSameConnection() throws Exception { - // open - try (SocketHttpClient s = SocketHttpClient.create(webServer.port())) { - // get - s.request(Http.Method.GET); - // assert - assertThat(SocketHttpClient.entityFromResponse(s.receive(), true), is("9\nIt works!\n0\n\n")); - // get - s.request(Http.Method.GET); - // assert - assertThat(SocketHttpClient.entityFromResponse(s.receive(), true), is("9\nIt works!\n0\n\n")); - } - } - - @Test - void testTwoPostsTheSameConnection() throws Exception { - // open - try (SocketHttpClient s = SocketHttpClient.create(webServer.port())) { - // post - s.request(Http.Method.POST, "test-payload-1"); - // assert - assertThat(SocketHttpClient.entityFromResponse(s.receive(), true), - is("21\nIt works! Payload: test-payload-1\n0\n\n")); - // post - s.request(Http.Method.POST, "test-payload-2"); - // assert - assertThat(SocketHttpClient.entityFromResponse(s.receive(), true), - is("21\nIt works! Payload: test-payload-2\n0\n\n")); - } - } - - @Test - void postGetPostGetTheSameConnection() throws Exception { - try (SocketHttpClient s = SocketHttpClient.create(webServer.port())) { - // post - s.request(Http.Method.POST, "test-payload-1"); - // assert - assertThat(SocketHttpClient.entityFromResponse(s.receive(), true), - is("21\nIt works! Payload: test-payload-1\n0\n\n")); - // get - s.request(Http.Method.GET); - // assert - assertThat(SocketHttpClient.entityFromResponse(s.receive(), true), is("9\nIt works!\n0\n\n")); - // post - s.request(Http.Method.POST, "test-payload-2"); - // assert - assertThat(SocketHttpClient.entityFromResponse(s.receive(), true), - is("21\nIt works! Payload: test-payload-2\n0\n\n")); - // get - s.request(Http.Method.GET); - // assert - assertThat(SocketHttpClient.entityFromResponse(s.receive(), true), is("9\nIt works!\n0\n\n")); - } - } - - @Test - void getWithLargePayloadDoesNotCauseConnectionClose() throws Exception { - // open - try (SocketHttpClient s = SocketHttpClient.create(webServer.port())) { - // get - s.request(Http.Method.GET, SocketHttpClient.longData(100_000).toString()); - - // assert - assertThat(SocketHttpClient.entityFromResponse(s.receive(), true), is("9\nIt works!\n0\n\n")); - s.assertConnectionIsOpen(); - } - } - - @Test - void traceWithAnyPayloadCausesConnectionCloseButDoesNotFail() throws Exception { - // open - try (SocketHttpClient s = SocketHttpClient.create(webServer.port())) { - // get - s.request(Http.Method.TRACE, "/trace", "small"); - - // assert - assertThat(SocketHttpClient.entityFromResponse(s.receive(), true), is("9\nIn trace!\n0\n\n")); - s.assertConnectionIsClosed(); - } - } - - @Test - void traceWithAnyPayloadCausesConnectionCloseAndBadRequestWhenHandled() throws Exception { - // open - try (SocketHttpClient s = SocketHttpClient.create(webServer.port())) { - // get - s.request(Http.Method.TRACE, "small"); - - // assert that the Handler.of ContentReader transforms the exception to 400 error - assertThat(s.receive(), startsWith("HTTP/1.1 400 Bad Request\n")); - s.assertConnectionIsClosed(); - } - } - - @Test - void deferredGetWithLargePayloadDoesNotCauseConnectionClose() throws Exception { - // open - try (SocketHttpClient s = SocketHttpClient.create(webServer.port())) { - // get - s.request(Http.Method.GET, "/deferred", SocketHttpClient.longData(100_000).toString()); - // assert - assertThat(SocketHttpClient.entityFromResponse(s.receive(), true), is("d\nI'm deferred!\n0\n\n")); - s.assertConnectionIsOpen(); - } - } - - @Test - void getWithIllegalSmallEnoughPayloadDoesntCauseConnectionClose() throws Exception { - // open - try (SocketHttpClient s = SocketHttpClient.create(webServer.port())) { - // get - s.request(Http.Method.GET, "illegal-but-small-enough-payload"); - - // assert - assertThat(SocketHttpClient.entityFromResponse(s.receive(), true), is("9\nIt works!\n0\n\n")); - s.assertConnectionIsOpen(); - } - } - - @Test - void unconsumedSmallPostDataDoesNotCauseConnectionClose() throws Exception { - // open - try (SocketHttpClient s = SocketHttpClient.create(webServer.port())) { - // get - s.request(Http.Method.POST, "/unconsumed", "not-consumed-payload"); - String received = s.receive(); - System.out.println(received); - // assert - assertThat(SocketHttpClient.entityFromResponse(received, true), is("15\nPayload not consumed!\n0\n\n")); - s.assertConnectionIsOpen(); - } - } - - @Test - void unconsumedLargePostDataDoesNotCauseConnectionClose() throws Exception { - // open - try (SocketHttpClient s = SocketHttpClient.create(webServer.port())) { - // get - s.request(Http.Method.POST, "/unconsumed", SocketHttpClient.longData(100_000).toString()); - - // assert - assertThat(SocketHttpClient.entityFromResponse(s.receive(), true), is("15\nPayload not consumed!\n0\n\n")); - s.assertConnectionIsOpen(); - } - } - - @Test - void unconsumedDeferredLargePostDataDoesNotCauseConnectionClose() throws Exception { - // open - try (SocketHttpClient s = SocketHttpClient.create(webServer.port())) { - // get - s.request(Http.Method.POST, "/deferred", SocketHttpClient.longData(100_000).toString()); - - // assert - assertThat(SocketHttpClient.entityFromResponse(s.receive(), true), is("d\nI'm deferred!\n0\n\n")); - s.assertConnectionIsOpen(); - } - } - - @Test - void errorHandlerWithGetPayloadDoesNotCauseConnectionClose() throws Exception { - // open - try (SocketHttpClient s = SocketHttpClient.create(webServer.port())) { - // get - s.request(Http.Method.GET, "/exception", "not-consumed-payload"); - - // assert - assertThat(s.receive(), startsWith("HTTP/1.1 500 Internal Server Error\n")); - s.assertConnectionIsOpen(); - } - } - - @Test - void errorHandlerWithPostDataDoesNotCauseConnectionClose() throws Exception { - // open - try (SocketHttpClient s = SocketHttpClient.create(webServer.port())) { - // get - s.request(Http.Method.POST, "/exception", "not-consumed-payload"); - - // assert - assertThat(s.receive(), startsWith("HTTP/1.1 500 Internal Server Error\n")); - s.assertConnectionIsOpen(); - } - } - - @Test - void testConnectionCloseWhenKeepAliveOff() throws Exception { - try (SocketHttpClient s = SocketHttpClient.create(webServer.port())) { - // get - s.request(Http.Method.GET, "/", null, List.of("Connection: close")); - - // assert - assertThat(SocketHttpClient.entityFromResponse(s.receive(), true), is("9\nIt works!\n0\n\n")); - s.assertConnectionIsClosed(); - } - } - - @Test - void testForcedChunkedWithConnectionCloseHeader() { - String s = client.sendAndReceive("/force-chunked", - Http.Method.GET, - null, - List.of("Connection: close")); - ClientResponseHeaders headers = SocketHttpClient.headersFromResponse(s); - assertThat(headers, noHeader(Http.Header.CONNECTION)); - assertThat(headers, hasHeader(TRANSFER_ENCODING_CHUNKED)); - - assertThat(SocketHttpClient.entityFromResponse(s, false), is("4\nabcd\n0\n\n")); - } - - @Test - void testConnectionCloseHeader() { - String s = client.sendAndReceive("/", - Http.Method.GET, - null, - List.of("Connection: close")); - ClientResponseHeaders headers = SocketHttpClient.headersFromResponse(s); - assertThat(headers, noHeader(Http.Header.CONNECTION)); - assertThat(SocketHttpClient.entityFromResponse(s, false), is("9\nIt works!\n0\n\n")); - } - - @Test - void testBadURL() { - String s = client.sendAndReceive("/?p=|", - Http.Method.GET, - null, - List.of("Connection: close")); - assertThat(s, containsString("400 Bad Request")); - ClientResponseHeaders headers = SocketHttpClient.headersFromResponse(s); - assertThat(headers, hasHeader(Http.Header.CONTENT_TYPE)); - assertThat(headers, hasHeader(Http.Header.CONTENT_LENGTH)); - } - - @Test - void testBadContentType() { - String s = client.sendAndReceive("/", - Http.Method.GET, - null, - List.of("Content-Type: %", "Connection: close")); - assertThat(s, containsString("400 Bad Request")); - ClientResponseHeaders headers = SocketHttpClient.headersFromResponse(s); - assertThat(headers, hasHeader(Http.Header.CONTENT_TYPE)); - assertThat(headers, hasHeader(Http.Header.CONTENT_LENGTH)); - } - - @Test - void testAbsouteUri() { - String result = WebClient.create() - .get() - .uri("http://localhost:" + webServer.port() + "/absoluteUri?a=b") - .request(String.class) - .await(5, TimeUnit.SECONDS); - - assertThat(result, containsString("http://")); - assertThat(result, containsString(String.valueOf(webServer.port()))); - assertThat(result, endsWith("/absoluteUri?a=b")); - } - - @Test - void testMulti() { - String s = client.sendAndReceive("/multi", - Http.Method.GET, - null); - assertThat(s, startsWith("HTTP/1.1 200 OK\n")); - List chunks = Arrays.stream(s.split("\\n[0-9]\\n?\\s*")) - .skip(1) - .collect(Collectors.toList()); - assertThat(chunks, contains("test 1", "test 2", "test 3")); - Map trailerHeaders = cutTrailerHeaders(s); - assertThat(trailerHeaders.entrySet(), IsIterableWithSize.iterableWithSize(0)); - } - - /** - * HTTP/1.1 500 Internal Server Error - * Date: Thu, 9 Jul 2020 14:01:14 +0200 - * trailer: stream-status,stream-result - * transfer-encoding: chunked - * connection: keep-alive - * - * 0 - * stream-status: 500 - * stream-result: java.lang.RuntimeException: BOOM! - */ - @Test - void testMultiFirstError() { - String s = client.sendAndReceive("/multiFirstError", - Http.Method.GET, - null); - - assertThat(s, startsWith("HTTP/1.1 500 Internal Server Error\n")); - assertThat(SocketHttpClient.headersFromResponse(s), hasHeader(Http.Header.TRAILER)); - Map trailerHeaders = cutTrailerHeaders(s); - assertThat(trailerHeaders, hasEntry(equalToIgnoringCase("stream-status"), is("500"))); - assertThat(trailerHeaders, hasEntry(equalToIgnoringCase("stream-result"), is(TEST_EXCEPTION.toString()))); - } - - @Test - void testMultiSecondError() { - String s = client.sendAndReceive("/multiSecondError", - Http.Method.GET, - null); - assertThat(s, startsWith("HTTP/1.1 200 OK\n")); - Map trailerHeaders = cutTrailerHeaders(s); - assertThat(trailerHeaders, hasEntry("stream-status", "500")); - assertThat(trailerHeaders, hasEntry("stream-result", TEST_EXCEPTION.toString())); - } - - @Test - void testMultiThirdError() { - String s = client.sendAndReceive("/multiThirdError", - Http.Method.GET, - null); - assertThat(s, startsWith("HTTP/1.1 200 OK\n")); - Map trailerHeaders = cutTrailerHeaders(s); - assertThat(trailerHeaders, hasEntry("stream-status", "500")); - assertThat(trailerHeaders, hasEntry("stream-result", TEST_EXCEPTION.toString())); - } - - /** - * HTTP/1.1 200 OK - * Date: Thu, 9 Jul 2020 13:57:27 +0200 - * transfer-encoding: chunked - * connection: keep-alive - * - * 6 - * test 0 - * 6 - * test 1 - * 6 - * test 2 - * 0 - * stream-status: 500 - * stream-result: java.lang.RuntimeException: BOOM! - */ - @Test - void testMultiDelayedThirdError() { - String s = client.sendAndReceive("/multiDelayedThirdError", - Http.Method.GET, - null); - assertThat(s, startsWith("HTTP/1.1 200 OK\n")); - Map headers = cutTrailerHeaders(s); - assertThat(headers, hasEntry("stream-status", "500")); - assertThat(headers, hasEntry("stream-result", TEST_EXCEPTION.toString())); - } - - private Map cutTrailerHeaders(String response) { - Pattern trailerHeaderPattern = Pattern.compile("([^\\t,\\n,\\f,\\r, ,;,:,=]+)\\:\\s?([^\\n]+)"); - assertThat(response, notNullValue()); - int index = response.indexOf("\n0\n"); - return Arrays.stream(response.substring(index).split("\n")) - .map(trailerHeaderPattern::matcher) - .filter(Matcher::matches) - .collect(HashMap::new, (map, matcher) -> map.put(matcher.group(1), matcher.group(2)), (m1, m2) -> { - }); - } -} diff --git a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/SocketConfigurationV2ApiTest.java b/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/SocketConfigurationV2ApiTest.java deleted file mode 100644 index debb714649e..00000000000 --- a/reactive/webserver/webserver/src/test/java/io/helidon/reactive/webserver/SocketConfigurationV2ApiTest.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2020, 2022 Oracle and/or its affiliates. - * - * 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 io.helidon.reactive.webserver; - -import java.util.Optional; -import java.util.concurrent.ExecutionException; - -import io.helidon.config.Config; -import io.helidon.config.ConfigException; -import io.helidon.config.ConfigSources; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import static io.helidon.common.testing.junit5.OptionalMatcher.optionalPresent; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.MatcherAssert.assertThat; - -@Deprecated(since = "3.0.0", forRemoval = true) -public class SocketConfigurationV2ApiTest { - private static final String ERROR_PREFIX = "Config multiport/application.yaml "; - private static Config deprecated; - private static Config current; - private static Config runnable; - private static Config noname; - - @BeforeAll - static void initClass() { - Config config = Config.create(ConfigSources.classpath("multiport/application.yaml")); - deprecated = config.get("sockets.deprecated.server"); - current = config.get("sockets.current.server"); - runnable = config.get("sockets.runnable.server"); - noname = config.get("sockets.noname.server"); - } - - @Test - void testNoName() { - Assertions.assertThrows(ConfigException.class, () -> WebServer.builder() - .host("localhost") - .config(noname) - .build()); - } - - @Test - void testDeprecatedConfig() { - validateConfiguration("deprecated", deprecated); - } - - @Test - void testCurrentConfig() { - validateConfiguration("current", current); - } - - @Test - void testRunnableConfig() throws ExecutionException, InterruptedException { - WebServer server = WebServer.builder() - .host("localhost") - .config(runnable) - .build() - .start() - .toCompletableFuture() - .get(); - - try { - ServerConfiguration configuration = server.configuration(); - - validateRunnableSocket(WebServer.DEFAULT_SOCKET_NAME, configuration, true); - validateRunnablePort(WebServer.DEFAULT_SOCKET_NAME, server, true); - - Optional maybeConfig = configuration.namedSocket("admin"); - assertThat(ERROR_PREFIX + " runnable admin socket must be configured", - maybeConfig, - optionalPresent()); - validateRunnableSocket("admin", maybeConfig.get(), true); - validateRunnablePort("admin", server, true); - - maybeConfig = configuration.namedSocket("static"); - assertThat(ERROR_PREFIX + " runnable static socket must be configured", - maybeConfig, - optionalPresent()); - validateRunnableSocket("static", maybeConfig.get(), false); - validateRunnablePort("static", server, false); - } finally { - server.shutdown() - .toCompletableFuture() - .get(); - } - - } - - private void validateRunnablePort(String socketName, WebServer server, boolean enabled) { - if (enabled) { - assertThat(ERROR_PREFIX + " runnable \"" + socketName + "\" port must be an ephemeral port", - server.port(socketName), - not(0)); - } else { - assertThat(ERROR_PREFIX + " runnable \"" + socketName + "\" port must be disabled, yet running", - server.port(socketName), - // -1 is the value webserver returns when socket is not known or not active - is(-1)); - } - } - - private void validateRunnableSocket(String name, SocketConfiguration socketConfig, boolean enabled) { - assertThat(socketConfig.name(), is(name)); - assertThat(socketConfig.enabled(), is(enabled)); - } - - private void validateConfiguration(String type, Config config) { - WebServer server = WebServer.builder() - .host("localhost") - .config(config) - .build(); - - ServerConfiguration configuration = server.configuration(); - assertThat(ERROR_PREFIX + type + " socket configuration from multiport/application.yaml default server port", - configuration.port(), - is(8000)); - - // socket admin, port 8001 - validateSocket(type, configuration, "admin", 8001, true); - - // socket static, port 8002, enabled false - validateSocket(type, configuration, "static", 8002, false); - } - - private void validateSocket(String type, ServerConfiguration configuration, String name, int port, boolean enabled) { - Optional socketConfiguration = configuration.namedSocket(name); - assertThat(ERROR_PREFIX + type + " " + name + " socket must be configured", - socketConfiguration, - optionalPresent()); - - SocketConfiguration socket = socketConfiguration.get(); - assertThat(ERROR_PREFIX + type + " " + name + " socket port", - socket.port(), - is(port)); - - assertThat(ERROR_PREFIX + type + " " + name + " socket enabled", - socket.enabled(), - is(enabled)); - } -}