From c97e77c474ac51856488aec0df41215cdd08d4b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kraus?= Date: Thu, 2 Feb 2023 13:55:32 +0100 Subject: [PATCH] [main] Issue 5383: ContentEncodingContext Builder and passing ContentEncodingContext instance from WebServer to Http1Connection. (#5921) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Issue 5383: ContentEncodingContext Builder and passing ContentEncodingContext instance from WebServer to Http1Connection. * junit test to check ContentEncodingContext initialization from server config * Added discover-services to both connection-providers and content-encoding nodes. Added jUnits to verify config options. Signed-off-by: Tomáš Kraus --- nima/http/encoding/encoding/pom.xml | 6 +- .../http/encoding/ContentEncodingContext.java | 115 ++++++++++- .../encoding/ContentEncodingSupportImpl.java | 80 +++----- .../encoding/src/main/java/module-info.java | 3 +- .../http2/webserver/ConnectionConfigTest.java | 14 +- nima/webserver/webserver/pom.xml | 6 + .../io/helidon/nima/webserver/WebServer.java | 6 +- .../nima/webserver/http1/Http1Connection.java | 4 +- .../nima/webserver/WebServerConfigTest.java | 89 ++++++++ .../webserver/http1/ConnectionConfigTest.java | 190 +++--------------- .../src/test/resources/application.yaml | 10 + nima/websocket/webserver/pom.xml | 5 + .../WsUpgradeProviderConfigTest.java | 101 ++-------- 13 files changed, 311 insertions(+), 318 deletions(-) create mode 100644 nima/webserver/webserver/src/test/java/io/helidon/nima/webserver/WebServerConfigTest.java diff --git a/nima/http/encoding/encoding/pom.xml b/nima/http/encoding/encoding/pom.xml index ab933198509..fd26e3721f7 100644 --- a/nima/http/encoding/encoding/pom.xml +++ b/nima/http/encoding/encoding/pom.xml @@ -1,6 +1,6 @@ + + helidon-nima-http-encoding-gzip + io.helidon.nima.http.encoding + test + org.junit.jupiter junit-jupiter-api diff --git a/nima/webserver/webserver/src/main/java/io/helidon/nima/webserver/WebServer.java b/nima/webserver/webserver/src/main/java/io/helidon/nima/webserver/WebServer.java index fdc69a22053..47349aeb1a6 100644 --- a/nima/webserver/webserver/src/main/java/io/helidon/nima/webserver/WebServer.java +++ b/nima/webserver/webserver/src/main/java/io/helidon/nima/webserver/WebServer.java @@ -250,9 +250,12 @@ public Builder config(Config config) { connConfig.get("tcp-no-delay").asBoolean().ifPresent(socketOptionsBuilder::tcpNoDelay); }); }); + // Configure content encoding + config.get("content-encoding") + .as(ContentEncodingContext::create) + .ifPresent(this::contentEncodingContext); // Store providers config node for later usage. providersConfig = config.get("connection-providers"); - return this; } @@ -503,6 +506,7 @@ Map routers() { } List connectionProviders() { + providersConfig.get("discover-services").asBoolean().ifPresent(connectionProviders::useSystemServiceLoader); List providers = connectionProviders.build().asList(); // Send configuration nodes to providers return providers.stream() diff --git a/nima/webserver/webserver/src/main/java/io/helidon/nima/webserver/http1/Http1Connection.java b/nima/webserver/webserver/src/main/java/io/helidon/nima/webserver/http1/Http1Connection.java index 48003ab8848..666d411cb0d 100644 --- a/nima/webserver/webserver/src/main/java/io/helidon/nima/webserver/http1/Http1Connection.java +++ b/nima/webserver/webserver/src/main/java/io/helidon/nima/webserver/http1/Http1Connection.java @@ -68,8 +68,7 @@ public class Http1Connection implements ServerConnection, InterruptableTask providers = wsBuilder.connectionProviders(); + // Providers shall be loaded with ServiceLoader. + assertThat(providers, notNullValue()); + assertThat(providers, is(not(empty()))); + } + + @Test + void testConnectionProvidersDisabled() { + // This will pick up application.yaml from the classpath as default configuration file + Config config = Config.create(); + WebServer.Builder wsBuilder = WebServer.builder().config(config.get("server2")); + List providers = wsBuilder.connectionProviders(); + // No providers shall be loaded with ServiceLoader disabled for connection providers. + assertThat(providers, notNullValue()); + assertThat(providers, is(empty())); + } + + // Check that WebServer ContentEncodingContext is disabled when disable is present in config + @Test + void testContentEncodingConfig() { + // This will pick up application.yaml from the classpath as default configuration file + Config config = Config.create(); + WebServer.Builder wsBuilder = WebServer.builder().config(config.get("server")); + ContentEncodingContext contentEncodingContext = wsBuilder.contentEncodingContext(); + assertThat(contentEncodingContext.contentEncodingEnabled(), is(true)); + assertThat(contentEncodingContext.contentDecodingEnabled(), is(true)); + failsWith(() -> contentEncodingContext.decoder("gzip"), NoSuchElementException.class); + failsWith(() -> contentEncodingContext.decoder("gzip"), NoSuchElementException.class); + failsWith(() -> contentEncodingContext.encoder("gzip"), NoSuchElementException.class); + failsWith(() -> contentEncodingContext.decoder("x-gzip"), NoSuchElementException.class); + failsWith(() -> contentEncodingContext.encoder("x-gzip"), NoSuchElementException.class); + } + + // Verify that provided task throws an exception + private static void failsWith(Runnable task, Class exception) { + try { + task.run(); + // Fail the test when no Exception was thrown + fail(String.format("Exception %s was not thrown", exception.getName())); + } catch (Exception ex) { + if (!exception.isAssignableFrom(ex.getClass())) { + throw ex; + } + } + } + +} diff --git a/nima/webserver/webserver/src/test/java/io/helidon/nima/webserver/http1/ConnectionConfigTest.java b/nima/webserver/webserver/src/test/java/io/helidon/nima/webserver/http1/ConnectionConfigTest.java index 63fee771eb3..90580b7440d 100644 --- a/nima/webserver/webserver/src/test/java/io/helidon/nima/webserver/http1/ConnectionConfigTest.java +++ b/nima/webserver/webserver/src/test/java/io/helidon/nima/webserver/http1/ConnectionConfigTest.java @@ -16,187 +16,61 @@ package io.helidon.nima.webserver.http1; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.List; -import java.util.concurrent.ExecutorService; +import java.util.function.Function; + +import org.junit.jupiter.api.Test; -import io.helidon.common.buffers.BufferData; -import io.helidon.common.buffers.DataReader; -import io.helidon.common.buffers.DataWriter; -import io.helidon.common.socket.HelidonSocket; -import io.helidon.common.socket.PeerInfo; import io.helidon.config.Config; -import io.helidon.nima.webserver.ConnectionContext; -import io.helidon.nima.webserver.Router; -import io.helidon.nima.webserver.Routing; -import io.helidon.nima.webserver.spi.ServerConnectionSelector; -import io.helidon.nima.webserver.ServerContext; import io.helidon.nima.webserver.WebServer; -import io.helidon.nima.webserver.http.DirectHandlers; - -import org.junit.jupiter.api.Test; +import io.helidon.nima.webserver.spi.ServerConnectionProvider; +import io.helidon.nima.webserver.spi.ServerConnectionSelector; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.mock; public class ConnectionConfigTest { - // ConnectionContext mockup - private static class TestContext implements ConnectionContext { - - @Override - public PeerInfo remotePeer() { - return null; - } - - @Override - public PeerInfo localPeer() { - return null; - } + @Test + void testConnectionConfig() { + // This will pick up application.yaml from the classpath as default configuration file + Config config = Config.create(); + TestProvider provider = new TestProvider(); + WebServer.builder().config(config.get("server")).addConnectionProvider(provider).build(); + assertThat(provider.isConfig(), is(true)); + Http1Config http1Config = provider.config(); + assertThat(http1Config.maxPrologueLength(), is(4096)); + assertThat(http1Config.maxHeadersSize(), is(8192)); + assertThat(http1Config.validatePath(), is(false)); + assertThat(http1Config.validateHeaders(), is(false)); + } - @Override - public boolean isSecure() { - return false; - } + private static class TestProvider implements ServerConnectionProvider { - @Override - public String socketId() { - return null; - } + private Http1Config http1Config = null; + private Config config = null; @Override - public String childSocketId() { - return null; + public Iterable configKeys() { + return List.of("http_1_1"); } @Override - public ServerContext serverContext() { - return null; + public ServerConnectionSelector create(Function configs) { + config = configs.apply("http_1_1"); + http1Config = DefaultHttp1Config.toBuilder(config).build(); + return mock(ServerConnectionSelector.class); } - @Override - public ExecutorService sharedExecutor() { - return null; + private Http1Config config() { + return http1Config; } - @Override - public DataWriter dataWriter() { - return null; + private boolean isConfig() { + return http1Config != null; } - @Override - public DataReader dataReader() { - return new DataReader(new HelidonSocket() { - @Override - public void close() { - } - - @Override - public int read(BufferData buffer) { - return 0; - } - - @Override - public void write(BufferData buffer) { - } - - @Override - public PeerInfo remotePeer() { - return null; - } - - @Override - public PeerInfo localPeer() { - return null; - } - - @Override - public boolean isSecure() { - return false; - } - - @Override - public String socketId() { - return null; - } - - @Override - public String childSocketId() { - return null; - } - - @Override - public byte[] get() { - return new byte[0]; - } - }); - } - - @Override - public Router router() { - return new Router() { - @Override - public T routing(Class routingType, T defaultValue) { - return null; - } - - @Override - public void afterStop() { - } - - @Override - public void beforeStart() { - } - }; - } - - @Override - public long maxPayloadSize() { - return 0; - } - - @Override - public DirectHandlers directHandlers() { - return null; - } - - } - - @Test - void testConnectionConfig() - throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - - // This will pick up application.yaml from the classpath as default configuration file - Config config = Config.create(); - - // Builds LoomServer instance including connectionProviders list. - WebServer.Builder wsBuilder = WebServer.builder() - .config(config.get("server")); - - // Call wsBuilder.connectionProviders() trough reflection - Method connectionProviders - = WebServer.Builder.class.getDeclaredMethod("connectionProviders", (Class[]) null); - connectionProviders.setAccessible(true); - @SuppressWarnings("unchecked") - List providers - = (List) connectionProviders.invoke(wsBuilder, (Object[]) null); - - // Check whether at least one Http2ConnectionProvider was found - boolean haveHttp1Provider = false; - - for (ServerConnectionSelector provider : providers) { - if (provider instanceof Http1ConnectionSelector) { - haveHttp1Provider = true; - Http1Connection conn = (Http1Connection) provider.connection(new TestContext()); - // Verify values to be updated from configuration file - assertThat(conn.config().maxPrologueLength(), is(4096)); - assertThat(conn.config().maxHeadersSize(), is(8192)); - assertThat(conn.config().validatePath(), is(false)); - assertThat(conn.config().validateHeaders(), is(false)); - } - } - assertThat("No Http12ConnectionProvider was found", haveHttp1Provider, is(true)); } } diff --git a/nima/webserver/webserver/src/test/resources/application.yaml b/nima/webserver/webserver/src/test/resources/application.yaml index 366919bc37f..02025644349 100644 --- a/nima/webserver/webserver/src/test/resources/application.yaml +++ b/nima/webserver/webserver/src/test/resources/application.yaml @@ -24,3 +24,13 @@ server: max-headers-size: 8192 validate-headers: false validate-path: false + + content-encoding: + discover-services: false + +server2: + port: 8079 + host: 127.0.0.1 + + connection-providers: + discover-services: false diff --git a/nima/websocket/webserver/pom.xml b/nima/websocket/webserver/pom.xml index c1e5b19ead6..1d9b18b0c3e 100644 --- a/nima/websocket/webserver/pom.xml +++ b/nima/websocket/webserver/pom.xml @@ -56,6 +56,11 @@ junit-jupiter-api test + + mockito-core + org.mockito + test + org.hamcrest hamcrest-all diff --git a/nima/websocket/webserver/src/test/java/io/helidon/nima/websocket/webserver/WsUpgradeProviderConfigTest.java b/nima/websocket/webserver/src/test/java/io/helidon/nima/websocket/webserver/WsUpgradeProviderConfigTest.java index 06447de699a..535739934ef 100644 --- a/nima/websocket/webserver/src/test/java/io/helidon/nima/websocket/webserver/WsUpgradeProviderConfigTest.java +++ b/nima/websocket/webserver/src/test/java/io/helidon/nima/websocket/webserver/WsUpgradeProviderConfigTest.java @@ -22,109 +22,28 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.ExecutorService; + +import org.junit.jupiter.api.Test; import io.helidon.common.buffers.DataReader; -import io.helidon.common.buffers.DataWriter; -import io.helidon.common.socket.PeerInfo; import io.helidon.config.Config; import io.helidon.nima.webserver.ConnectionContext; import io.helidon.nima.webserver.Router; -import io.helidon.nima.webserver.Routing; -import io.helidon.nima.webserver.spi.ServerConnectionSelector; import io.helidon.nima.webserver.ServerContext; import io.helidon.nima.webserver.WebServer; -import io.helidon.nima.webserver.http.DirectHandlers; import io.helidon.nima.webserver.http1.Http1Connection; import io.helidon.nima.webserver.http1.Http1ConnectionSelector; import io.helidon.nima.webserver.http1.spi.Http1Upgrader; - -import org.junit.jupiter.api.Test; +import io.helidon.nima.webserver.spi.ServerConnectionSelector; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class WsUpgradeProviderConfigTest { - // ConnectionContext mockup - private static class TestContext implements ConnectionContext { - - @Override - public PeerInfo remotePeer() { - return null; - } - - @Override - public PeerInfo localPeer() { - return null; - } - - @Override - public boolean isSecure() { - return false; - } - - @Override - public String socketId() { - return null; - } - - @Override - public String childSocketId() { - return null; - } - - @Override - public ServerContext serverContext() { - return null; - } - - @Override - public ExecutorService sharedExecutor() { - return null; - } - - @Override - public DataWriter dataWriter() { - return null; - } - - @Override - public DataReader dataReader() { - return new DataReader(() -> new byte[0]); - } - - @Override - public Router router() { - return new Router() { - @Override - public T routing(Class routingType, T defaultValue) { - return null; - } - - @Override - public void afterStop() { - } - - @Override - public void beforeStart() { - } - }; - } - - @Override - public long maxPayloadSize() { - return 0; - } - - @Override - public DirectHandlers directHandlers() { - return null; - } - - } - // Verify that WsUpgrader is properly configured from config file @Test void testConnectionConfig() @@ -147,7 +66,7 @@ void testConnectionConfig() for (ServerConnectionSelector provider : providers) { if (provider instanceof Http1ConnectionSelector) { - Http1Connection conn = (Http1Connection) provider.connection(new TestContext()); + Http1Connection conn = (Http1Connection) provider.connection(mockContext()); assertThat(conn, notNullValue()); // Retrieve private upgradeProviderMap from Http1Connection trough reflection @@ -177,4 +96,12 @@ void testUpgraderConfigBuilder() { assertThat(origins, containsInAnyOrder("bOrigin1", "bOrigin2")); } + private static ConnectionContext mockContext() { + ConnectionContext ctx = mock(ConnectionContext.class); + when(ctx.dataReader()).thenReturn(mock(DataReader.class)); + when(ctx.router()).thenReturn(Router.empty()); + when(ctx.serverContext()).thenReturn(mock(ServerContext.class)); + return ctx; + } + }