From a8a239ab06525a0a22daa659af9c65401c12a344 Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Mon, 24 May 2021 17:50:54 +0200 Subject: [PATCH] Update Vert.x to 4.1.0.Beta1 and Netty to 4.1.65.Final This update introduces some changes. First, when the HTTP compression is set to identity, the header is not written into the response anymore. Then, the cookie interface has a new method (maxAge), so our implementations must be updated. Finally, and that's the more significant change. Netty now depends on Brotli4J. Brotli is a compression algorithm. The implementation uses a native dependency. At the moment, we won't support Brotli in native mode. This commit adds a substitution to disable it. All decoders using Brotli related classes have been substituted to avoid having to depend on the Brotli4J dependency. --- bom/application/pom.xml | 4 +- extensions/netty/runtime/pom.xml | 8 ++ .../runtime/graal/NettySubstitutions.java | 102 ++++++++++++++++-- .../quarkus/vertx/http/CompressionTest.java | 5 +- .../http/hotreload/VertxInjectionTest.java | 2 - .../filters/QuarkusRequestWrapper.java | 5 + 6 files changed, 111 insertions(+), 15 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 4e1e24a971c10..37f1d65cf73ca 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -114,7 +114,7 @@ 1.15.4.Final 1.8.7.Final 3.2.0.Final - 4.1.0.Beta1 + 4.1.0.CR1 4.5.13 4.4.14 4.1.4 @@ -137,7 +137,7 @@ 12.1.1.Final 4.4.0.Final 2.9.1 - 4.1.60.Final + 4.1.65.Final 1.0.3 3.4.1.Final 0.17.0 diff --git a/extensions/netty/runtime/pom.xml b/extensions/netty/runtime/pom.xml index 880977accf3a0..78bcd224ba879 100644 --- a/extensions/netty/runtime/pom.xml +++ b/extensions/netty/runtime/pom.xml @@ -22,6 +22,14 @@ io.netty netty-codec + + io.netty + netty-codec-http + + + io.netty + netty-codec-http2 + io.quarkus quarkus-arc diff --git a/extensions/netty/runtime/src/main/java/io/quarkus/netty/runtime/graal/NettySubstitutions.java b/extensions/netty/runtime/src/main/java/io/quarkus/netty/runtime/graal/NettySubstitutions.java index 2580740d1b690..19b5ed2e83dd2 100644 --- a/extensions/netty/runtime/src/main/java/io/quarkus/netty/runtime/graal/NettySubstitutions.java +++ b/extensions/netty/runtime/src/main/java/io/quarkus/netty/runtime/graal/NettySubstitutions.java @@ -1,5 +1,10 @@ package io.quarkus.netty.runtime.graal; +import static io.netty.handler.codec.http.HttpHeaderValues.DEFLATE; +import static io.netty.handler.codec.http.HttpHeaderValues.GZIP; +import static io.netty.handler.codec.http.HttpHeaderValues.X_DEFLATE; +import static io.netty.handler.codec.http.HttpHeaderValues.X_GZIP; + import java.nio.ByteBuffer; import java.security.PrivateKey; import java.security.Provider; @@ -17,6 +22,7 @@ import javax.net.ssl.TrustManagerFactory; import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.Delete; import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind; import com.oracle.svm.core.annotate.Substitute; @@ -29,7 +35,14 @@ import io.netty.buffer.ByteBufAllocator; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; import io.netty.channel.DefaultChannelPromise; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.compression.ZlibCodecFactory; +import io.netty.handler.codec.compression.ZlibWrapper; +import io.netty.handler.codec.http.HttpHeaderValues; +import io.netty.handler.codec.http2.Http2Exception; import io.netty.handler.ssl.ApplicationProtocolConfig; import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior; import io.netty.handler.ssl.CipherSuiteFilter; @@ -83,7 +96,9 @@ static boolean isAlpnSupported() { } } -/** Hardcode io.netty.handler.ssl.OpenSsl as non-available */ +/** + * Hardcode io.netty.handler.ssl.OpenSsl as non-available + */ @TargetClass(className = "io.netty.handler.ssl.OpenSsl") final class Target_io_netty_handler_ssl_OpenSsl { @@ -186,7 +201,8 @@ final class Target_io_netty_handler_ssl_JdkAlpnApplicationProtocolNegotiator_Alp @Substitute public SSLEngine wrapSslEngine(SSLEngine engine, ByteBufAllocator alloc, JdkApplicationProtocolNegotiator applicationNegotiator, boolean isServer) { - return (SSLEngine) (Object) new Target_io_netty_handler_ssl_JdkAlpnSslEngine(engine, applicationNegotiator, isServer); + return (SSLEngine) (Object) new Target_io_netty_handler_ssl_JdkAlpnSslEngine(engine, applicationNegotiator, + isServer); } } @@ -242,7 +258,8 @@ final class Target_io_netty_handler_ssl_SslContext { @Substitute static SslContext newServerContextInternal(SslProvider provider, Provider sslContextProvider, - X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain, + X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, + X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls, boolean enableOcsp, String keyStoreType, @@ -257,10 +274,12 @@ static SslContext newServerContextInternal(SslProvider provider, Provider sslCon } @Substitute - static SslContext newClientContextInternal(SslProvider provider, Provider sslContextProvider, X509Certificate[] trustCert, + static SslContext newClientContextInternal(SslProvider provider, Provider sslContextProvider, + X509Certificate[] trustCert, TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, - ApplicationProtocolConfig apn, String[] protocols, long sessionCacheSize, long sessionTimeout, boolean enableOcsp, + ApplicationProtocolConfig apn, String[] protocols, long sessionCacheSize, long sessionTimeout, + boolean enableOcsp, String keyStoreType, Map.Entry, Object>... options) throws SSLException { if (enableOcsp) { throw new IllegalArgumentException("OCSP is not supported with this SslProvider: " + provider); @@ -305,11 +324,11 @@ static JdkApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig c // .append(config.selectorFailureBehavior()).append(" failure behavior").toString()); // } SelectorFailureBehavior behavior = config.selectorFailureBehavior(); - if (behavior == SelectorFailureBehavior.FATAL_ALERT) + if (behavior == SelectorFailureBehavior.FATAL_ALERT) { return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols()); - else if (behavior == SelectorFailureBehavior.NO_ADVERTISE) + } else if (behavior == SelectorFailureBehavior.NO_ADVERTISE) { return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols()); - else { + } else { throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ") .append(config.selectorFailureBehavior()).append(" failure behavior").toString()); } @@ -321,12 +340,14 @@ else if (behavior == SelectorFailureBehavior.NO_ADVERTISE) return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols()); default: throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ") - .append(config.selectedListenerFailureBehavior()).append(" failure behavior").toString()); + .append(config.selectedListenerFailureBehavior()).append(" failure behavior") + .toString()); } } default: throw new UnsupportedOperationException( - new StringBuilder("JDK provider does not support ").append(config.protocol()).append(" protocol") + new StringBuilder("JDK provider does not support ").append(config.protocol()) + .append(" protocol") .toString()); } } @@ -515,6 +536,67 @@ public long memoryAddress() { } +@TargetClass(className = "io.netty.handler.codec.compression.BrotliDecoder") +@Delete +final class Target_BrotliDecoder { + +} + +@TargetClass(className = "io.netty.handler.codec.http.HttpContentDecompressor") +final class Target_io_netty_handler_codec_http_HttpContentDecompressor { + + @Alias + private boolean strict; + + @Alias + protected ChannelHandlerContext ctx; + + @Substitute + protected EmbeddedChannel newContentDecoder(String contentEncoding) throws Exception { + if (GZIP.contentEqualsIgnoreCase(contentEncoding) || + X_GZIP.contentEqualsIgnoreCase(contentEncoding)) { + return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), + ctx.channel().config(), ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP)); + } + if (DEFLATE.contentEqualsIgnoreCase(contentEncoding) || + X_DEFLATE.contentEqualsIgnoreCase(contentEncoding)) { + final ZlibWrapper wrapper = strict ? ZlibWrapper.ZLIB : ZlibWrapper.ZLIB_OR_NONE; + // To be strict, 'deflate' means ZLIB, but some servers were not implemented correctly. + return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), + ctx.channel().config(), ZlibCodecFactory.newZlibDecoder(wrapper)); + } + + // 'identity' or unsupported + return null; + } +} + +@TargetClass(className = "io.netty.handler.codec.http2.DelegatingDecompressorFrameListener") +final class Target_io_netty_handler_codec_http2_DelegatingDecompressorFrameListener { + + @Alias + boolean strict; + + @Substitute + protected EmbeddedChannel newContentDecompressor(ChannelHandlerContext ctx, CharSequence contentEncoding) + throws Http2Exception { + if (!HttpHeaderValues.GZIP.contentEqualsIgnoreCase(contentEncoding) + && !HttpHeaderValues.X_GZIP.contentEqualsIgnoreCase(contentEncoding)) { + if (!HttpHeaderValues.DEFLATE.contentEqualsIgnoreCase(contentEncoding) + && !HttpHeaderValues.X_DEFLATE.contentEqualsIgnoreCase(contentEncoding)) { + return null; + } else { + ZlibWrapper wrapper = this.strict ? ZlibWrapper.ZLIB : ZlibWrapper.ZLIB_OR_NONE; + return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), ctx.channel().config(), + new ChannelHandler[] { ZlibCodecFactory.newZlibDecoder(wrapper) }); + } + } else { + return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), ctx.channel().config(), + new ChannelHandler[] { ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP) }); + } + } +} + class NettySubstitutions { } diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/CompressionTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/CompressionTest.java index 4384a442e9986..29ab204a67472 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/CompressionTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/CompressionTest.java @@ -1,5 +1,8 @@ package io.quarkus.vertx.http; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; + import javax.enterprise.context.ApplicationScoped; import javax.enterprise.event.Observes; @@ -45,7 +48,7 @@ public void test() throws Exception { .body(Matchers.equalTo(longString)); RestAssured.given().get("/nocompress").then().statusCode(200) - .header("content-encoding", "identity") + .header("content-encoding", is(nullValue())) .header("content-length", Matchers.equalTo(Integer.toString(longString.length()))) .body(Matchers.equalTo(longString)); } diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/hotreload/VertxInjectionTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/hotreload/VertxInjectionTest.java index 993b549643bdf..194dbb538884c 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/hotreload/VertxInjectionTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/hotreload/VertxInjectionTest.java @@ -23,9 +23,7 @@ public void testEditingBeanUsingVertx() { .statusCode(200) .body(containsString("hello")); - System.out.println("Modification"); TEST.modifySourceFile("VertxEventBusConsumer.java", s -> s.replace("hello", "bonjour")); - System.out.println("After"); RestAssured.get("/").then() .statusCode(200) .body(containsString("bonjour")); diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/filters/QuarkusRequestWrapper.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/filters/QuarkusRequestWrapper.java index ab9210d6cdbdc..15efa520877ec 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/filters/QuarkusRequestWrapper.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/filters/QuarkusRequestWrapper.java @@ -204,6 +204,11 @@ public Cookie setMaxAge(long maxAge) { return null; } + @Override + public long getMaxAge() { + return Long.MIN_VALUE; + } + @Override public Cookie setSecure(boolean secure) { return null;