diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 27d5b78e771d2..0de9d4a1701b7 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -123,7 +123,7 @@ 1.0.1.Final 2.2.1.Final 3.5.0.Final - 4.4.8 + 4.4.9 4.5.14 4.4.16 4.1.5 @@ -145,7 +145,7 @@ 14.0.11.Final 4.6.2.Final 3.1.5 - 4.1.100.Final + 4.1.108.Final 1.12.0 1.0.4 3.5.1.Final diff --git a/extensions/grpc-common/deployment/src/main/java/io/quarkus/grpc/common/deployment/GrpcCommonProcessor.java b/extensions/grpc-common/deployment/src/main/java/io/quarkus/grpc/common/deployment/GrpcCommonProcessor.java index 477ad5690a4a6..82d43e7c7919f 100644 --- a/extensions/grpc-common/deployment/src/main/java/io/quarkus/grpc/common/deployment/GrpcCommonProcessor.java +++ b/extensions/grpc-common/deployment/src/main/java/io/quarkus/grpc/common/deployment/GrpcCommonProcessor.java @@ -75,7 +75,8 @@ NativeImageConfigBuildItem nativeImageConfiguration() { .addRuntimeInitializedClass("io.grpc.netty.Utils") .addRuntimeInitializedClass("io.grpc.netty.NettyServerBuilder") .addRuntimeInitializedClass("io.grpc.netty.NettyChannelBuilder") - .addRuntimeInitializedClass("io.grpc.internal.RetriableStream"); + .addRuntimeInitializedClass("io.grpc.internal.RetriableStream") + .addRuntimeReinitializedClass("com.google.protobuf.UnsafeUtil"); return builder.build(); } diff --git a/extensions/grpc-common/runtime/pom.xml b/extensions/grpc-common/runtime/pom.xml index 540731e0cc3b0..6f988e789e581 100644 --- a/extensions/grpc-common/runtime/pom.xml +++ b/extensions/grpc-common/runtime/pom.xml @@ -99,9 +99,28 @@ io.quarkus quarkus-vertx + + io.grpc + grpc-core + + + com.google.code.findbugs + jsr305 + + + org.codehaus.mojo + animal-sniffer-annotations + + + com.google.android + annotations + + + org.graalvm.nativeimage svm + provided diff --git a/extensions/grpc-common/runtime/src/main/java/io/quarkus/grpc/common/runtime/graal/GrpcSubstitutions.java b/extensions/grpc-common/runtime/src/main/java/io/quarkus/grpc/common/runtime/graal/GrpcSubstitutions.java index 349eb09e264b8..1cdaed9d44fc5 100644 --- a/extensions/grpc-common/runtime/src/main/java/io/quarkus/grpc/common/runtime/graal/GrpcSubstitutions.java +++ b/extensions/grpc-common/runtime/src/main/java/io/quarkus/grpc/common/runtime/graal/GrpcSubstitutions.java @@ -2,6 +2,7 @@ import static io.grpc.InternalServiceProviders.getCandidatesViaHardCoded; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -12,6 +13,8 @@ import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; +import sun.misc.Unsafe; + @SuppressWarnings("unused") @TargetClass(className = "io.grpc.ServiceProviders") final class Target_io_grpc_ServiceProviders { // NOSONAR @@ -79,6 +82,20 @@ interface Target_io_grpc_ServiceProviders_PriorityAccessor { // NOSONAR int getPriority(T provider); } +@TargetClass(className = "com.google.protobuf.UnsafeUtil") +final class Target_com_google_protobuf_UnsafeUtil { + @Substitute + static sun.misc.Unsafe getUnsafe() { + try { + Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); + theUnsafe.setAccessible(true); + return (Unsafe) theUnsafe.get(null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} + @SuppressWarnings("unused") class GrpcSubstitutions { } diff --git a/extensions/infinispan-client/deployment/src/main/java/io/quarkus/infinispan/client/deployment/InfinispanClientProcessor.java b/extensions/infinispan-client/deployment/src/main/java/io/quarkus/infinispan/client/deployment/InfinispanClientProcessor.java index 58201a89f0b4f..7e11774606f90 100644 --- a/extensions/infinispan-client/deployment/src/main/java/io/quarkus/infinispan/client/deployment/InfinispanClientProcessor.java +++ b/extensions/infinispan-client/deployment/src/main/java/io/quarkus/infinispan/client/deployment/InfinispanClientProcessor.java @@ -205,7 +205,6 @@ InfinispanPropertiesBuildItem setup(ApplicationArchivesBuildItem applicationArch additionalBeans.produce(AdditionalBeanBuildItem.builder().addBeanClass(InfinispanClientName.class).build()); additionalBeans.produce(AdditionalBeanBuildItem.builder().addBeanClass(Remote.class).build()); - systemProperties.produce(new SystemPropertyBuildItem("io.netty.noUnsafe", "true")); hotDeployment .produce(new HotDeploymentWatchedFileBuildItem(META_INF + File.separator + DEFAULT_HOTROD_CLIENT_PROPERTIES)); diff --git a/extensions/netty/deployment/src/main/java/io/quarkus/netty/deployment/NettyProcessor.java b/extensions/netty/deployment/src/main/java/io/quarkus/netty/deployment/NettyProcessor.java index 766d2f8c00800..3f33afd645b5e 100644 --- a/extensions/netty/deployment/src/main/java/io/quarkus/netty/deployment/NettyProcessor.java +++ b/extensions/netty/deployment/src/main/java/io/quarkus/netty/deployment/NettyProcessor.java @@ -105,6 +105,8 @@ NativeImageConfigBuildItem build( .addRuntimeInitializedClass("io.netty.handler.ssl.ReferenceCountedOpenSslEngine") .addRuntimeInitializedClass("io.netty.handler.ssl.ReferenceCountedOpenSslContext") .addRuntimeInitializedClass("io.netty.handler.ssl.ReferenceCountedOpenSslClientContext") + .addRuntimeInitializedClass("io.netty.handler.ssl.JdkSslServerContext") + .addRuntimeInitializedClass("io.netty.handler.ssl.JdkSslClientContext") .addRuntimeInitializedClass("io.netty.handler.ssl.util.ThreadLocalInsecureRandom") .addRuntimeInitializedClass("io.netty.buffer.ByteBufUtil$HexUtil") .addRuntimeInitializedClass("io.netty.buffer.PooledByteBufAllocator") @@ -120,6 +122,7 @@ NativeImageConfigBuildItem build( .addRuntimeInitializedClass("io.netty.handler.codec.http.websocketx.extensions.compression.DeflateDecoder") .addRuntimeInitializedClass("io.netty.handler.codec.http.websocketx.WebSocket00FrameEncoder") .addRuntimeInitializedClass("io.netty.handler.codec.compression.ZstdOptions") + .addRuntimeInitializedClass("io.netty.handler.codec.compression.ZstdConstants") .addRuntimeInitializedClass("io.netty.handler.codec.compression.BrotliOptions"); } else { log.debug("Not registering Netty HTTP classes as they were not found"); @@ -162,6 +165,28 @@ NativeImageConfigBuildItem build( log.debug("Not registering Netty native kqueue classes as they were not found"); } + builder.addRuntimeReinitializedClass("io.netty.util.internal.PlatformDependent") + .addRuntimeReinitializedClass("io.netty.util.internal.PlatformDependent0"); + + if (QuarkusClassLoader.isClassPresentAtRuntime("io.netty.buffer.UnpooledByteBufAllocator")) { + builder.addRuntimeReinitializedClass("io.netty.buffer.UnpooledByteBufAllocator") + .addRuntimeReinitializedClass("io.netty.buffer.Unpooled") + .addRuntimeReinitializedClass("io.vertx.core.http.impl.Http1xServerResponse") + .addRuntimeReinitializedClass("io.netty.handler.codec.http.HttpObjectAggregator") + .addRuntimeReinitializedClass("io.netty.handler.codec.ReplayingDecoderByteBuf") + .addRuntimeReinitializedClass("io.vertx.core.parsetools.impl.RecordParserImpl"); + + if (QuarkusClassLoader.isClassPresentAtRuntime("io.vertx.ext.web.client.impl.MultipartFormUpload")) { + builder.addRuntimeReinitializedClass("io.vertx.ext.web.client.impl.MultipartFormUpload"); + } + + if (QuarkusClassLoader + .isClassPresentAtRuntime("org.jboss.resteasy.reactive.client.impl.multipart.QuarkusMultipartFormUpload")) { + builder.addRuntimeReinitializedClass( + "org.jboss.resteasy.reactive.client.impl.multipart.QuarkusMultipartFormUpload"); + } + } + return builder //TODO: make configurable .build(); } diff --git a/extensions/netty/runtime/src/main/java/io/quarkus/netty/runtime/graal/HttpContentCompressorSubstitutions.java b/extensions/netty/runtime/src/main/java/io/quarkus/netty/runtime/graal/HttpContentCompressorSubstitutions.java index baeb441687e80..2f2b360db19f6 100644 --- a/extensions/netty/runtime/src/main/java/io/quarkus/netty/runtime/graal/HttpContentCompressorSubstitutions.java +++ b/extensions/netty/runtime/src/main/java/io/quarkus/netty/runtime/graal/HttpContentCompressorSubstitutions.java @@ -29,6 +29,23 @@ public void flush(final ChannelHandlerContext ctx) { } } + @Substitute + @TargetClass(className = "io.netty.handler.codec.compression.ZstdConstants", onlyWith = IsZstdAbsent.class) + public static final class ZstdConstants { + + // The constants make calls to com.github.luben.zstd.Zstd so we cut links with that substitution. + + static final int DEFAULT_COMPRESSION_LEVEL = 0; + + static final int MIN_COMPRESSION_LEVEL = 0; + + static final int MAX_COMPRESSION_LEVEL = 0; + + static final int MAX_BLOCK_SIZE = 0; + + static final int DEFAULT_BLOCK_SIZE = 0; + } + public static class IsZstdAbsent implements BooleanSupplier { private boolean zstdAbsent; diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/form/FormTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/form/FormTest.java new file mode 100644 index 0000000000000..16425d4fb7ead --- /dev/null +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/form/FormTest.java @@ -0,0 +1,175 @@ +package io.quarkus.vertx.http.form; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; +import jakarta.inject.Inject; + +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.vertx.core.Vertx; +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.HttpClientResponse; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.RequestOptions; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.handler.BodyHandler; + +public class FormTest { + + private static final String APP_PROPS = "quarkus.http.limits.max-form-fields=10\n" + + "quarkus.http.limits.max-form-buffered-bytes=100\n"; + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addAsResource(new StringAsset(APP_PROPS), "application.properties") + .addClasses(BeanRegisteringRouteUsingObserves.class)); + + @Inject + Vertx vertx; + + @Test + public void testTooManyFields() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + AtomicReference reference = new AtomicReference<>(); + HttpClient client = vertx.createHttpClient(); + client.request(new RequestOptions().setMethod(HttpMethod.POST).setAbsoluteURI("http://localhost:8081/form")) + .onComplete(ar -> { + var req = ar.result(); + req.setChunked(true); + req.putHeader("content-type", "application/x-www-form-urlencoded"); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 20; i++) { + if (i > 0) { + sb.append('&'); + } + sb.append("a").append(i).append("=").append("b"); + } + req.write(sb.toString()); + vertx.setTimer(10, id -> { + req.end(); + }); + + req.response().onComplete(rc -> { + reference.set(rc.result()); + latch.countDown(); + }); + }); + latch.await(10, TimeUnit.SECONDS); + Assertions.assertEquals(400, reference.get().statusCode()); + } + + @Test + public void testOkForm() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + AtomicReference reference = new AtomicReference<>(); + HttpClient client = vertx.createHttpClient(); + client.request(new RequestOptions().setMethod(HttpMethod.POST).setAbsoluteURI("http://localhost:8081/form")) + .onComplete(ar -> { + var req = ar.result(); + req.setChunked(true); + req.putHeader("content-type", "application/x-www-form-urlencoded"); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 10; i++) { + if (i > 0) { + sb.append('&'); + } + sb.append("a").append(i).append("=").append("b"); + } + req.write(sb.toString()); + vertx.setTimer(10, id -> { + req.end(); + }); + + req.response().onComplete(rc -> { + reference.set(rc.result()); + latch.countDown(); + }); + }); + latch.await(10, TimeUnit.SECONDS); + Assertions.assertEquals(200, reference.get().statusCode()); + } + + @Test + public void testTooManyBytes() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + AtomicReference reference = new AtomicReference<>(); + HttpClient client = vertx.createHttpClient(); + client.request(new RequestOptions().setMethod(HttpMethod.POST).setAbsoluteURI("http://localhost:8081/form")) + .onComplete(ar -> { + var req = ar.result(); + req.setChunked(true); + req.putHeader("content-type", "application/x-www-form-urlencoded"); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 200; i++) { + sb.append("a"); + } + req.write(sb.toString()); + vertx.setTimer(10, id -> { + req.end("=b"); + }); + + req.response().onComplete(rc -> { + reference.set(rc.result()); + latch.countDown(); + }); + }); + latch.await(10, TimeUnit.SECONDS); + Assertions.assertEquals(400, reference.get().statusCode()); + } + + @Test + public void testBytesOk() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + AtomicReference reference = new AtomicReference<>(); + HttpClient client = vertx.createHttpClient(); + client.request(new RequestOptions().setMethod(HttpMethod.POST).setAbsoluteURI("http://localhost:8081/form")) + .onComplete(ar -> { + var req = ar.result(); + req.setChunked(true); + req.putHeader("content-type", "application/x-www-form-urlencoded"); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 100; i++) { + sb.append("a"); + } + req.write(sb.toString()); + vertx.setTimer(10, id -> { + req.end("=b"); + }); + + req.response().onComplete(rc -> { + reference.set(rc.result()); + latch.countDown(); + }); + }); + latch.await(10, TimeUnit.SECONDS); + Assertions.assertEquals(200, reference.get().statusCode()); + } + + @ApplicationScoped + static class BeanRegisteringRouteUsingObserves { + + public void register(@Observes Router router) { + + router + .post().order(Integer.MIN_VALUE).handler(rc -> { + rc.request().setExpectMultipart(true); + rc.next(); + }); + router.post().handler(BodyHandler.create()); + router.post("/form") + .handler(rc -> { + rc.response().end("OK"); + }); + } + + } +} diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/ServerLimitsConfig.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/ServerLimitsConfig.java index 1ef9a2b47a6fe..78e6c8620bb7a 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/ServerLimitsConfig.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/ServerLimitsConfig.java @@ -41,6 +41,27 @@ public class ServerLimitsConfig { @ConfigItem(defaultValue = "2048") public MemorySize maxFormAttributeSize; + /** + * Set the maximum number of fields of a form. Set to {@code -1} to allow unlimited number of attributes. + */ + @ConfigItem(defaultValue = "256") + public int maxFormFields; + + /** + * Set the maximum number of bytes a server can buffer when decoding a form. + * Set to {@code -1} to allow unlimited length + **/ + @ConfigItem(defaultValue = "1K") + public MemorySize maxFormBufferedBytes; + + /** + * The maximum number of HTTP request parameters permitted for incoming requests. + *

+ * If a client sends more than this number of parameters in a request, the connection is closed. + */ + @ConfigItem(defaultValue = "1000") + public int maxParameters; + /** * The maximum number of connections that are allowed at any one time. If this is set * it is recommended to set a short idle timeout. diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/HttpServerOptionsUtils.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/HttpServerOptionsUtils.java index 87ccd93b3ba59..8763c8481368c 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/HttpServerOptionsUtils.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/HttpServerOptionsUtils.java @@ -256,6 +256,8 @@ public static void applyCommonOptions(HttpServerOptions httpServerOptions, httpServerOptions.setMaxHeaderSize(httpConfiguration.limits.maxHeaderSize.asBigInteger().intValueExact()); httpServerOptions.setMaxChunkSize(httpConfiguration.limits.maxChunkSize.asBigInteger().intValueExact()); httpServerOptions.setMaxFormAttributeSize(httpConfiguration.limits.maxFormAttributeSize.asBigInteger().intValueExact()); + httpServerOptions.setMaxFormFields(httpConfiguration.limits.maxFormFields); + httpServerOptions.setMaxFormBufferedBytes(httpConfiguration.limits.maxFormBufferedBytes.asBigInteger().intValue()); httpServerOptions.setWebSocketSubProtocols(websocketSubProtocols); httpServerOptions.setReusePort(httpConfiguration.soReusePort); httpServerOptions.setTcpQuickAck(httpConfiguration.tcpQuickAck); @@ -317,6 +319,8 @@ public static void applyCommonOptionsForManagementInterface(HttpServerOptions op options.setMaxHeaderSize(httpConfiguration.limits.maxHeaderSize.asBigInteger().intValueExact()); options.setMaxChunkSize(httpConfiguration.limits.maxChunkSize.asBigInteger().intValueExact()); options.setMaxFormAttributeSize(httpConfiguration.limits.maxFormAttributeSize.asBigInteger().intValueExact()); + options.setMaxFormFields(httpConfiguration.limits.maxFormFields); + options.setMaxFormBufferedBytes(httpConfiguration.limits.maxFormBufferedBytes.asBigInteger().intValue()); options.setMaxInitialLineLength(httpConfiguration.limits.maxInitialLineLength); options.setWebSocketSubProtocols(websocketSubProtocols); options.setAcceptBacklog(httpConfiguration.acceptBacklog); diff --git a/independent-projects/resteasy-reactive/pom.xml b/independent-projects/resteasy-reactive/pom.xml index 7c0a48647e1bd..25bfaca0740ee 100644 --- a/independent-projects/resteasy-reactive/pom.xml +++ b/independent-projects/resteasy-reactive/pom.xml @@ -63,7 +63,7 @@ 3.0.0 2.3.1 2.1.0 - 4.4.8 + 4.4.9 5.3.0 1.0.0.Final 2.15.2