diff --git a/.github/workflows/ci-actions.yml b/.github/workflows/ci-actions.yml
index 99502fe508760..35c05b9315825 100644
--- a/.github/workflows/ci-actions.yml
+++ b/.github/workflows/ci-actions.yml
@@ -523,6 +523,7 @@ jobs:
grpc-interceptors
grpc-mutual-auth
grpc-plain-text
+ grpc-plain-text-mutiny
grpc-proto-v2
grpc-streaming
grpc-tls
diff --git a/bom/application/pom.xml b/bom/application/pom.xml
index f71198ab6a754..42f3ef9ecec2d 100644
--- a/bom/application/pom.xml
+++ b/bom/application/pom.xml
@@ -100,7 +100,7 @@
1.0.6.Final
2.1.0.Final
1.7.30
- 1.2.1.Final
+ 1.2.0.Final
1.5.4.Final-format-001
1.0.1.Final
1.12.1.Final
diff --git a/docs/src/main/asciidoc/amazon-lambda-http.adoc b/docs/src/main/asciidoc/amazon-lambda-http.adoc
index b21896dc2b461..f70d34ac00751 100644
--- a/docs/src/main/asciidoc/amazon-lambda-http.adoc
+++ b/docs/src/main/asciidoc/amazon-lambda-http.adoc
@@ -217,7 +217,7 @@ as a dependencies. The extension automatically generates everything you might n
NOTE: In previous versions of this extension you had to set up your pom or gradle
to zip up your executable for native deployments, but this is not the case anymore.
-Also, at least in the generated maven archetype `pom.xml`, the `quarkus-resteasy`, `quarkus-vertx-web`, and `quarkus-underdow`
+Also, at least in the generated maven archetype `pom.xml`, the `quarkus-resteasy`, `quarkus-vertx-web`, and `quarkus-undertow`
dependencies are all optional. Pick which http framework(s) you want to use (JAX-RS, Vertx Web, and/or Servlet) and
remove the other dependencies to shrink your deployment.
diff --git a/docs/src/main/asciidoc/cdi-reference.adoc b/docs/src/main/asciidoc/cdi-reference.adoc
index 7df259a17c09e..489ec456dfe6b 100644
--- a/docs/src/main/asciidoc/cdi-reference.adoc
+++ b/docs/src/main/asciidoc/cdi-reference.adoc
@@ -644,7 +644,7 @@ It's possible to annotate a non-private static method with an interceptor bindin
class Services {
@Logged <1>
- static BigDecimal computePrice(long amout) { <2>
+ static BigDecimal computePrice(long amount) { <2>
BigDecimal price;
// Perform computations...
return price;
@@ -690,7 +690,7 @@ import io.quarkus.arc.Lock;
@ApplicationScoped
class SharedService {
- void addAmount(BigDecimal amout) {
+ void addAmount(BigDecimal amount) {
// ...changes some internal state of the bean
}
diff --git a/docs/src/main/asciidoc/cdi.adoc b/docs/src/main/asciidoc/cdi.adoc
index 49fe0dfd3c0ba..3556057f22635 100644
--- a/docs/src/main/asciidoc/cdi.adoc
+++ b/docs/src/main/asciidoc/cdi.adoc
@@ -101,7 +101,7 @@ public class Translator {
A: Yes, you can.
In fact, in CDI the "setter injection" is superseded by more powerful https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#initializer_methods[initializer methods, window="_blank"].
-Intializers may accept multiple parameters and don't have to follow the JavaBean naming conventions.
+Initializers may accept multiple parameters and don't have to follow the JavaBean naming conventions.
.Initialized and Constructor Injection Example
[source,java]
@@ -183,13 +183,13 @@ A: You can use all the built-in scopes mentioned by the specification except for
|`@javax.inject.Singleton` | Just like `@ApplicationScoped` except that no client proxy is used. The instance is created when an injection point that resolves to a @Singleton bean is being injected.
|`@javax.enterprise.context.RequestScoped` | The bean instance is associated with the current _request_ (usually an HTTP request).
|`@javax.enterprise.context.Dependent` | This is a pseudo-scope. The instances are not shared and every injection point spawns a new instance of the dependent bean. The lifecycle of dependent bean is bound to the bean injecting it - it will be created and destroyed along with the bean injecting it.
-|`@javax.enterprise.context.SessionScoped` | This scope is backed by an `javax.servlet.http.HttpSession` object. It's only available if the `quarkus-undertow` extension is used.
+|`@javax.enterprise.context.SessionScoped` | This scope is backed by a `javax.servlet.http.HttpSession` object. It's only available if the `quarkus-undertow` extension is used.
|===
NOTE: There can be other custom scopes provided by Quarkus extensions. For example, `quarkus-narayana-jta` provides `javax.transaction.TransactionScoped`.
[[client_proxies]]
-== _Q: I don't undestand the concept of client proxies._
+== _Q: I don't understand the concept of client proxies._
Indeed, the https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#client_proxies[client proxies, window="_blank"] could be hard to grasp but they provide some useful functionality.
A client proxy is basically an object that delegates all method invocations to a target bean instance.
diff --git a/docs/src/main/asciidoc/command-mode-reference.adoc b/docs/src/main/asciidoc/command-mode-reference.adoc
index 76ae957b596d9..78d16472b192a 100644
--- a/docs/src/main/asciidoc/command-mode-reference.adoc
+++ b/docs/src/main/asciidoc/command-mode-reference.adoc
@@ -113,7 +113,7 @@ As command mode applications will often require arguments to be passed on the co
mvn compile quarkus:dev -Dquarkus.args='--help'
----
-The same can be achived with Gradle:
+The same can be achieved with Gradle:
----
./gradlew quarkusDev --quarkus-args='--help'
diff --git a/docs/src/main/asciidoc/deploying-to-kubernetes.adoc b/docs/src/main/asciidoc/deploying-to-kubernetes.adoc
index e261bb8593d82..43dca79367bed 100644
--- a/docs/src/main/asciidoc/deploying-to-kubernetes.adoc
+++ b/docs/src/main/asciidoc/deploying-to-kubernetes.adoc
@@ -188,6 +188,19 @@ quarkus.kubernetes.name=todo-rest
quarkus.kubernetes.version=1.0-rc.1
----
+[NOTE]
+====
+As is described in detail in the <<#openshift, OpenShift>> section, customizing OpenShift (or Knative) properties is done in the same way, but replacing
+`kubernetes` with `openshift` (or `knative`). The previous example for OpenShift would look like this:
+
+[source]
+----
+quarkus.openshift.part-of=todo-app
+quarkus.openshift.name=todo-rest
+quarkus.openshift.version=1.0-rc.1
+----
+====
+
The labels in generated resources will look like:
[source, json]
@@ -352,6 +365,17 @@ It's also possible to use the value from another field to add a new environment
quarkus.kubernetes.env.fields.foo=metadata.name
----
+[NOTE]
+====
+As is described in detail in the <<#openshift, OpenShift>> section, customizing OpenShift properties is done in the same way, but replacing
+`kubernetes` with `openshift`. The previous example for OpenShift would look like this:
+
+[source]
+----
+quarkus.openshift.env.fields.foo=metadata.name
+----
+====
+
===== Validation
A conflict between two definitions, e.g. mistakenly assigning both a value and specifying that a variable is derived from a field, will result in an error being thrown at build time so that you get the opportunity to fix the issue before you deploy your application to your cluster where it might be more difficult to diagnose the source of the issue.
@@ -1000,7 +1024,7 @@ spec:
- env:
- name: "FOO" <3>
value: "BAR"
- image: "<>/kubernetes-quickstart:1.0-SNAPSHOT" <4>
+ image: "<>/kubernetes-quickstart:1.0-SNAPSHOT" <4>
imagePullPolicy: "Always"
name: "kubernetes-quickstart"
ports:
diff --git a/docs/src/main/asciidoc/funqy-knative-events.adoc b/docs/src/main/asciidoc/funqy-knative-events.adoc
index 128b96456d0ec..869755ed7542f 100644
--- a/docs/src/main/asciidoc/funqy-knative-events.adoc
+++ b/docs/src/main/asciidoc/funqy-knative-events.adoc
@@ -310,7 +310,7 @@ You'll need two different terminal windows. One to do a curl request to the Bro
files so you can see the messages flowing through the Funqy function event chain.
Make sure you have the `stern` tool installed. See the Knative Tutorial setup for information on that. Run stern
-to look for logs outputed by our Funqy deployment
+to look for logs outputted by our Funqy deployment
[source, shell]
----
diff --git a/docs/src/main/asciidoc/gcp-functions.adoc b/docs/src/main/asciidoc/gcp-functions.adoc
index 70678112f6cbe..437c67ee8f7e3 100755
--- a/docs/src/main/asciidoc/gcp-functions.adoc
+++ b/docs/src/main/asciidoc/gcp-functions.adoc
@@ -77,11 +77,11 @@ gcloud components install beta
== Creating the functions
-For this example project, we will create three functions, one `HttpFunction`, one `BackgroundFunction` (Storage event) and one `RawBakgroundFunction` (PubSub event).
+For this example project, we will create three functions, one `HttpFunction`, one `BackgroundFunction` (Storage event) and one `RawBackgroundFunction` (PubSub event).
== Choose Your Function
-The `quarkus-google-cloud-functions` extension scans your project for a class that directly implements the Google Cloud `HttpFunction`, `BackgroundFunction` or `RawBakgroundFunction` interface.
+The `quarkus-google-cloud-functions` extension scans your project for a class that directly implements the Google Cloud `HttpFunction`, `BackgroundFunction` or `RawBackgroundFunction` interface.
It must find a class in your project that implements one of these interfaces or it will throw a build time failure.
If it finds more than one function classes, a build time exception will also be thrown.
diff --git a/docs/src/main/asciidoc/qute-reference.adoc b/docs/src/main/asciidoc/qute-reference.adoc
index f98141f4f915e..e64c34bdfdba3 100644
--- a/docs/src/main/asciidoc/qute-reference.adoc
+++ b/docs/src/main/asciidoc/qute-reference.adoc
@@ -963,7 +963,7 @@ class BigDecimalExtensions {
<1> `item.discountedPrice` is resolved to an instance of `BigDecimal`.
[[namespace_extension_methods]]
-==== Namespace Extention Methods
+==== Namespace Extension Methods
If `TemplateExtension#namespace()` is specified then the extension method is used to resolve expressions with the given <>.
Template extension methods that share the same namespace are grouped in one resolver ordered by `TemplateExtension#priority()`.
@@ -1239,7 +1239,7 @@ NOTE: A warning message is logged for each _unused_ parameter.
The default locale of the Java Virtual Machine used to *build the application* is used for the `@MessageBundle` interface by default.
However, the `io.quarkus.qute.i18n.MessageBundle#locale()` can be used to specify a custom locale.
-Additionaly, there are two ways to define a localized bundle:
+Additionally, there are two ways to define a localized bundle:
1. Create an interface that extends the default interface that is annotated with `@Localized`
2. Create an UTF-8 encoded file located in `src/main/resources/messages`; e.g. `msg_de.properties`.
diff --git a/docs/src/main/asciidoc/scheduler-reference.adoc b/docs/src/main/asciidoc/scheduler-reference.adoc
index 4632a22bc01ba..a2d40a3c89f27 100644
--- a/docs/src/main/asciidoc/scheduler-reference.adoc
+++ b/docs/src/main/asciidoc/scheduler-reference.adoc
@@ -182,7 +182,7 @@ class MyService {
== Programmatic Scheduling
-If you need to schedule a job programmatically you'll need to add the link:quartz[Quartz extension] and use the Quartz API direcly.
+If you need to schedule a job programmatically you'll need to add the link:quartz[Quartz extension] and use the Quartz API directly.
.Programmatic Scheduling with Quartz API
[source,java]
diff --git a/docs/src/main/asciidoc/scripting.adoc b/docs/src/main/asciidoc/scripting.adoc
index 8350ff6be6d42..f6e8eb8da4a6a 100644
--- a/docs/src/main/asciidoc/scripting.adoc
+++ b/docs/src/main/asciidoc/scripting.adoc
@@ -353,7 +353,7 @@ hello quarkus
== Debugging
To debug the application you use `jbang --debug quarkusapp.java` and you can use your IDE to connect on port 4004; if you want to use the
-more traditonal Quarkus debug port you can use `jbang --debug=5005 quarkusapp.java`.
+more traditional Quarkus debug port you can use `jbang --debug=5005 quarkusapp.java`.
Note: `jbang` debugging always suspends thus you need to connect the debugger to have the application run.
diff --git a/docs/src/main/asciidoc/tests-with-coverage.adoc b/docs/src/main/asciidoc/tests-with-coverage.adoc
index fb46d008fb54a..221e4e35f9673 100644
--- a/docs/src/main/asciidoc/tests-with-coverage.adoc
+++ b/docs/src/main/asciidoc/tests-with-coverage.adoc
@@ -705,4 +705,4 @@ You should end up with something like this (note the addition of the `merge-resu
== Conclusion
You now have all the information you need to study the coverage of your tests!
-But remember, some code that is not covered is certinaly not well tested. But some code that is covered is not necessarily *well* tested. Make sure to write good tests!
+But remember, some code that is not covered is certainly not well tested. But some code that is covered is not necessarily *well* tested. Make sure to write good tests!
diff --git a/docs/src/main/asciidoc/transaction.adoc b/docs/src/main/asciidoc/transaction.adoc
index 1dc455cb2c693..42604df3d18f6 100644
--- a/docs/src/main/asciidoc/transaction.adoc
+++ b/docs/src/main/asciidoc/transaction.adoc
@@ -206,7 +206,7 @@ Does it work everywhere I want to?::
Yep, it works in your Quarkus application, in your IDE, in your tests, because all of these are Quarkus applications.
JTA has some bad press for some people.
I don't know why.
-Let's just say that this is not your grand'pa's JTA implementation.
+Let's just say that this is not your grandpa's JTA implementation.
What we have is perfectly embeddable and lean.
Does it do 2 Phase Commit and slow down my app?::
diff --git a/extensions/funqy/funqy-knative-events/runtime/src/main/java/io/quarkus/funqy/runtime/bindings/knative/events/HeaderCloudEventImpl.java b/extensions/funqy/funqy-knative-events/runtime/src/main/java/io/quarkus/funqy/runtime/bindings/knative/events/HeaderCloudEventImpl.java
index bdc5787533e33..f3b90e559587e 100644
--- a/extensions/funqy/funqy-knative-events/runtime/src/main/java/io/quarkus/funqy/runtime/bindings/knative/events/HeaderCloudEventImpl.java
+++ b/extensions/funqy/funqy-knative-events/runtime/src/main/java/io/quarkus/funqy/runtime/bindings/knative/events/HeaderCloudEventImpl.java
@@ -57,7 +57,10 @@ public String subject() {
@Override
public OffsetDateTime time() {
if (time == null) {
- time = OffsetDateTime.parse(this.request.getHeader("ce-time"));
+ String t = this.request.getHeader("ce-time");
+ if (t != null) {
+ time = OffsetDateTime.parse(t);
+ }
}
return time;
diff --git a/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcServerProcessor.java b/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcServerProcessor.java
index af6228a251384..d3b0d5a3bfc8d 100644
--- a/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcServerProcessor.java
+++ b/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcServerProcessor.java
@@ -160,7 +160,13 @@ HealthBuildItem addHealthChecks(GrpcServerBuildTimeConfig config,
NativeImageConfigBuildItem nativeImageConfiguration() {
NativeImageConfigBuildItem.Builder builder = NativeImageConfigBuildItem.builder()
.addRuntimeInitializedClass("io.grpc.netty.Utils$ByteBufAllocatorPreferDirectHolder")
- .addRuntimeInitializedClass("io.grpc.netty.Utils$ByteBufAllocatorPreferHeapHolder");
+ .addRuntimeInitializedClass("io.grpc.netty.Utils$ByteBufAllocatorPreferHeapHolder")
+ // substitutions are runtime-only, Utils have to be substituted until we cannot use EPoll
+ // in native. NettyServerBuilder and NettyChannelBuilder would "bring in" Utils in build time
+ // if they were not marked as runtime initialized:
+ .addRuntimeInitializedClass("io.grpc.netty.Utils")
+ .addRuntimeInitializedClass("io.grpc.netty.NettyServerBuilder")
+ .addRuntimeInitializedClass("io.grpc.netty.NettyChannelBuilder");
return builder.build();
}
diff --git a/extensions/grpc/runtime/pom.xml b/extensions/grpc/runtime/pom.xml
index 4695dc08aa157..96924164b9dc6 100644
--- a/extensions/grpc/runtime/pom.xml
+++ b/extensions/grpc/runtime/pom.xml
@@ -17,6 +17,10 @@
jakarta.annotation
jakarta.annotation-api
+
+ com.google.code.findbugs
+ jsr305
+
io.vertx
vertx-grpc
diff --git a/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/graal/GrpcNettySubtitutions.java b/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/graal/GrpcNettySubstitutions.java
similarity index 87%
rename from extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/graal/GrpcNettySubtitutions.java
rename to extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/graal/GrpcNettySubstitutions.java
index 9b90c24e54e40..4337cbbb391b1 100644
--- a/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/graal/GrpcNettySubtitutions.java
+++ b/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/graal/GrpcNettySubstitutions.java
@@ -55,6 +55,20 @@ public static SslContextBuilder configure(SslContextBuilder builder, Provider jd
}
+@TargetClass(className = "io.grpc.netty.Utils")
+final class Target_io_grpc_netty_Utils {
+
+ @Substitute
+ static boolean isEpollAvailable() {
+ return false;
+ }
+
+ @Substitute
+ private static Throwable getEpollUnavailabilityCause() {
+ return null;
+ }
+}
+
@SuppressWarnings("unused")
class GrpcNettySubstitutions {
}
diff --git a/extensions/kubernetes-client/deployment-internal/src/main/java/io/quarkus/kubernetes/client/deployment/KubernetesClientErrorHanlder.java b/extensions/kubernetes-client/deployment-internal/src/main/java/io/quarkus/kubernetes/client/deployment/KubernetesClientErrorHanlder.java
index e3c4a3c130660..960c0b50fc5ef 100644
--- a/extensions/kubernetes-client/deployment-internal/src/main/java/io/quarkus/kubernetes/client/deployment/KubernetesClientErrorHanlder.java
+++ b/extensions/kubernetes-client/deployment-internal/src/main/java/io/quarkus/kubernetes/client/deployment/KubernetesClientErrorHanlder.java
@@ -11,7 +11,7 @@ public class KubernetesClientErrorHanlder {
public static void handle(Exception e) {
if (e.getCause() instanceof SSLHandshakeException) {
LOG.error(
- "The application could not be deployed to the cluster because the Kubernetes API Server certificates are not trusted. The certificates can be configured using the relevant configuration propertiers under the 'quarkus.kubernetes-client' config root, or \"quarkus.kubernetes-client.trust-certs=true\" can be set to explicitly trust the certificates (not recommended)");
+ "The application could not be deployed to the cluster because the Kubernetes API Server certificates are not trusted. The certificates can be configured using the relevant configuration properties under the 'quarkus.kubernetes-client' config root, or \"quarkus.kubernetes-client.trust-certs=true\" can be set to explicitly trust the certificates (not recommended)");
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else {
diff --git a/extensions/rest-client/deployment/src/main/java/io/quarkus/restclient/deployment/RestClientProcessor.java b/extensions/rest-client/deployment/src/main/java/io/quarkus/restclient/deployment/RestClientProcessor.java
index 17e3f4ff95263..db86b354fa5e2 100644
--- a/extensions/rest-client/deployment/src/main/java/io/quarkus/restclient/deployment/RestClientProcessor.java
+++ b/extensions/rest-client/deployment/src/main/java/io/quarkus/restclient/deployment/RestClientProcessor.java
@@ -173,7 +173,6 @@ void processInterfaces(
BuildProducer reflectiveClass,
BuildProducer reflectiveHierarchy,
BuildProducer beanRegistrars,
- BuildProducer extensionSslNativeSupport,
BuildProducer serviceProvider,
BuildProducer restClient) {
@@ -254,9 +253,11 @@ public void register(RegistrationContext registrationContext) {
}
}
}));
+ }
- // Indicates that this extension would like the SSL support to be enabled
- extensionSslNativeSupport.produce(new ExtensionSslNativeSupportBuildItem(Feature.REST_CLIENT));
+ @BuildStep
+ ExtensionSslNativeSupportBuildItem activateSslNativeSupport() {
+ return new ExtensionSslNativeSupportBuildItem(Feature.REST_CLIENT);
}
// currently default methods on a rest-client interface
diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/InputStreamResource.java b/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/InputStreamResource.java
new file mode 100644
index 0000000000000..c14d23ff9fbbc
--- /dev/null
+++ b/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/InputStreamResource.java
@@ -0,0 +1,61 @@
+package io.quarkus.resteasy.test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.LinkedBlockingDeque;
+
+import javax.annotation.PreDestroy;
+import javax.enterprise.event.Observes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+
+import io.vertx.core.Handler;
+import io.vertx.ext.web.Router;
+import io.vertx.ext.web.RoutingContext;
+
+@Path("/in")
+public class InputStreamResource {
+
+ Timer timer = new Timer();
+
+ public static final LinkedBlockingDeque THROWABLES = new LinkedBlockingDeque<>();
+
+ @PreDestroy
+ void stop() {
+ timer.cancel();
+ }
+
+ @POST
+ public String read(InputStream inputStream) throws IOException {
+ try {
+ byte[] buf = new byte[1024];
+ int r;
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ while ((r = inputStream.read(buf)) > 0) {
+ out.write(buf, 0, r);
+ }
+ return new String(out.toByteArray(), StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ THROWABLES.add(e);
+ throw e;
+ }
+ }
+
+ public void delayFilter(@Observes Router router) {
+ router.route().order(Integer.MIN_VALUE).handler(new Handler() {
+ @Override
+ public void handle(RoutingContext event) {
+ timer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ event.next();
+ }
+ }, 1000);
+ }
+ });
+ }
+}
diff --git a/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/VertxIOHangTestCase.java b/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/VertxIOHangTestCase.java
new file mode 100644
index 0000000000000..7295bcfd62af9
--- /dev/null
+++ b/extensions/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/VertxIOHangTestCase.java
@@ -0,0 +1,47 @@
+package io.quarkus.resteasy.test;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.TimeUnit;
+
+import org.hamcrest.Matchers;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+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.quarkus.test.common.http.TestHTTPResource;
+import io.restassured.RestAssured;
+
+public class VertxIOHangTestCase {
+
+ @RegisterExtension
+ static QuarkusUnitTest runner = new QuarkusUnitTest()
+ .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
+ .addClasses(InputStreamResource.class));
+
+ @TestHTTPResource
+ URI uri;
+
+ @Test
+ public void testDelayFilter() {
+ // makes sure that everything works as normal
+ RestAssured.given().body("hello world").post("/in").then().body(Matchers.is("hello world"));
+ }
+
+ @Test
+ public void testDelayFilterConnectionKilled() throws Exception {
+ // makes sure that everything works as normal
+ try (Socket s = new Socket(uri.getHost(), uri.getPort())) {
+ s.getOutputStream().write(
+ "POST /in HTTP/1.1\r\nHost:localhost\r\nContent-Length: 100\r\n\r\n".getBytes(StandardCharsets.UTF_8));
+ s.getOutputStream().flush();
+ }
+ Throwable exception = InputStreamResource.THROWABLES.poll(3, TimeUnit.SECONDS);
+ Assertions.assertTrue(exception instanceof IOException);
+ }
+}
diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxInputStream.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxInputStream.java
index d3c6637ab3e96..80f3675e78331 100644
--- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxInputStream.java
+++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxInputStream.java
@@ -3,6 +3,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
+import java.nio.channels.ClosedChannelException;
import java.util.ArrayDeque;
import java.util.Deque;
@@ -15,6 +16,7 @@
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
+import io.vertx.core.net.impl.ConnectionBase;
import io.vertx.ext.web.RoutingContext;
public class VertxInputStream extends InputStream {
@@ -27,7 +29,6 @@ public class VertxInputStream extends InputStream {
private final long limit;
public VertxInputStream(RoutingContext request, long timeout) throws IOException {
-
this.exchange = new VertxBlockingInput(request.request(), timeout);
Long limitObj = request.get(VertxHttpRecorder.MAX_REQUEST_SIZE_KEY);
if (limitObj == null) {
@@ -153,46 +154,51 @@ public static class VertxBlockingInput implements Handler {
public VertxBlockingInput(HttpServerRequest request, long timeout) throws IOException {
this.request = request;
this.timeout = timeout;
- if (!request.isEnded()) {
- request.pause();
- request.handler(this);
- request.endHandler(new Handler() {
- @Override
- public void handle(Void event) {
- synchronized (request.connection()) {
- eof = true;
- if (waiting) {
- request.connection().notify();
+ final ConnectionBase connection = (ConnectionBase) request.connection();
+ synchronized (connection) {
+ if (!connection.channel().isOpen()) {
+ readException = new ClosedChannelException();
+ } else if (!request.isEnded()) {
+ request.pause();
+ request.handler(this);
+ request.endHandler(new Handler() {
+ @Override
+ public void handle(Void event) {
+ synchronized (connection) {
+ eof = true;
+ if (waiting) {
+ connection.notify();
+ }
}
}
- }
- });
- request.exceptionHandler(new Handler() {
- @Override
- public void handle(Throwable event) {
- synchronized (request.connection()) {
- readException = new IOException(event);
- if (input1 != null) {
- input1.getByteBuf().release();
- input1 = null;
- }
- if (inputOverflow != null) {
- Buffer d = inputOverflow.poll();
- while (d != null) {
- d.getByteBuf().release();
- d = inputOverflow.poll();
+ });
+ request.exceptionHandler(new Handler() {
+ @Override
+ public void handle(Throwable event) {
+ synchronized (connection) {
+ readException = new IOException(event);
+ if (input1 != null) {
+ input1.getByteBuf().release();
+ input1 = null;
+ }
+ if (inputOverflow != null) {
+ Buffer d = inputOverflow.poll();
+ while (d != null) {
+ d.getByteBuf().release();
+ d = inputOverflow.poll();
+ }
+ }
+ if (waiting) {
+ connection.notify();
}
- }
- if (waiting) {
- request.connection().notify();
}
}
- }
- });
- request.fetch(1);
- } else {
- eof = true;
+ });
+ request.fetch(1);
+ } else {
+ eof = true;
+ }
}
}
diff --git a/integration-tests/grpc-plain-text-mutiny/pom.xml b/integration-tests/grpc-plain-text-mutiny/pom.xml
new file mode 100644
index 0000000000000..4696971975b71
--- /dev/null
+++ b/integration-tests/grpc-plain-text-mutiny/pom.xml
@@ -0,0 +1,165 @@
+
+
+ 4.0.0
+
+
+ quarkus-integration-tests-parent
+ io.quarkus
+ 999-SNAPSHOT
+ ../
+
+
+ quarkus-integration-test-grpc-plain-text-mutiny
+ Quarkus - Integration Tests - gRPC - Plain Text with Mutiny
+
+
+
+ io.quarkus
+ quarkus-resteasy
+
+
+ io.quarkus
+ quarkus-resteasy-mutiny
+
+
+ io.quarkus
+ quarkus-grpc
+
+
+ io.quarkus
+ quarkus-junit5
+ test
+
+
+ io.rest-assured
+ rest-assured
+ test
+
+
+ org.awaitility
+ awaitility
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+
+
+ io.quarkus
+ quarkus-grpc-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+ io.quarkus
+ quarkus-resteasy-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+ io.quarkus
+ quarkus-resteasy-mutiny-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+
+
+
+
+ io.quarkus
+ quarkus-maven-plugin
+
+
+
+ generate-code
+ build
+
+
+
+
+
+
+
+
+
+ native-image
+
+
+ native
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ ${native.surefire.skip}
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+
+
+ integration-test
+ verify
+
+
+
+ ${project.build.directory}/${project.build.finalName}-runner
+
+
+
+
+
+
+ io.quarkus
+ quarkus-maven-plugin
+
+
+ native-image
+
+ native-image
+
+
+ true
+ true
+ ${graalvmHome}
+
+
+
+
+
+
+
+
+
diff --git a/integration-tests/grpc-plain-text-mutiny/src/main/java/io/quarkus/grpc/examples/hello/HelloWorldEndpoint.java b/integration-tests/grpc-plain-text-mutiny/src/main/java/io/quarkus/grpc/examples/hello/HelloWorldEndpoint.java
new file mode 100644
index 0000000000000..9028734a3105b
--- /dev/null
+++ b/integration-tests/grpc-plain-text-mutiny/src/main/java/io/quarkus/grpc/examples/hello/HelloWorldEndpoint.java
@@ -0,0 +1,43 @@
+package io.quarkus.grpc.examples.hello;
+
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+
+import examples.GreeterGrpc;
+import examples.HelloReply;
+import examples.HelloRequest;
+import examples.MutinyGreeterGrpc;
+import io.quarkus.grpc.runtime.annotations.GrpcService;
+import io.smallrye.mutiny.Uni;
+
+@Path("/hello")
+public class HelloWorldEndpoint {
+
+ @Inject
+ @GrpcService("hello")
+ GreeterGrpc.GreeterBlockingStub blockingHelloService;
+ @Inject
+ @GrpcService("hello")
+ MutinyGreeterGrpc.MutinyGreeterStub mutinyHelloService;
+
+ @GET
+ @Path("/blocking/{name}")
+ public String helloBlocking(@PathParam("name") String name) {
+ HelloReply reply = blockingHelloService.sayHello(HelloRequest.newBuilder().setName(name).build());
+ return generateResponse(reply);
+
+ }
+
+ @GET
+ @Path("/mutiny/{name}")
+ public Uni helloMutiny(@PathParam("name") String name) {
+ return mutinyHelloService.sayHello(HelloRequest.newBuilder().setName(name).build())
+ .onItem().transform((reply) -> generateResponse(reply));
+ }
+
+ public String generateResponse(HelloReply reply) {
+ return String.format("%s! HelloWorldService has been called %d number of times.", reply.getMessage(), reply.getCount());
+ }
+}
diff --git a/integration-tests/grpc-plain-text-mutiny/src/main/java/io/quarkus/grpc/examples/hello/HelloWorldService.java b/integration-tests/grpc-plain-text-mutiny/src/main/java/io/quarkus/grpc/examples/hello/HelloWorldService.java
new file mode 100644
index 0000000000000..667afdbe987e6
--- /dev/null
+++ b/integration-tests/grpc-plain-text-mutiny/src/main/java/io/quarkus/grpc/examples/hello/HelloWorldService.java
@@ -0,0 +1,24 @@
+package io.quarkus.grpc.examples.hello;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.inject.Singleton;
+
+import examples.HelloReply;
+import examples.HelloRequest;
+import examples.MutinyGreeterGrpc;
+import io.smallrye.mutiny.Uni;
+
+@Singleton
+public class HelloWorldService extends MutinyGreeterGrpc.GreeterImplBase {
+
+ AtomicInteger counter = new AtomicInteger();
+
+ @Override
+ public Uni sayHello(HelloRequest request) {
+ int count = counter.incrementAndGet();
+ String name = request.getName();
+ return Uni.createFrom().item("Hello " + name)
+ .map(res -> HelloReply.newBuilder().setMessage(res).setCount(count).build());
+ }
+}
diff --git a/integration-tests/grpc-plain-text-mutiny/src/main/proto/helloworld.proto b/integration-tests/grpc-plain-text-mutiny/src/main/proto/helloworld.proto
new file mode 100644
index 0000000000000..89ccb456d8308
--- /dev/null
+++ b/integration-tests/grpc-plain-text-mutiny/src/main/proto/helloworld.proto
@@ -0,0 +1,54 @@
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "examples";
+option java_outer_classname = "HelloWorldProto";
+option objc_class_prefix = "HLW";
+
+package helloworld;
+
+// The greeting service definition.
+service Greeter {
+ // Sends a greeting
+ rpc SayHello (HelloRequest) returns (HelloReply) {}
+}
+
+// The request message containing the user's name.
+message HelloRequest {
+ string name = 1;
+}
+
+// The response message containing the greetings
+message HelloReply {
+ string message = 1;
+ int32 count = 2;
+}
diff --git a/integration-tests/grpc-plain-text-mutiny/src/main/resources/application.properties b/integration-tests/grpc-plain-text-mutiny/src/main/resources/application.properties
new file mode 100644
index 0000000000000..f8a95fc9c4c11
--- /dev/null
+++ b/integration-tests/grpc-plain-text-mutiny/src/main/resources/application.properties
@@ -0,0 +1 @@
+quarkus.grpc.clients.hello.host=localhost
\ No newline at end of file
diff --git a/integration-tests/grpc-plain-text-mutiny/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldEndpointIT.java b/integration-tests/grpc-plain-text-mutiny/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldEndpointIT.java
new file mode 100644
index 0000000000000..17f195143ebf5
--- /dev/null
+++ b/integration-tests/grpc-plain-text-mutiny/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldEndpointIT.java
@@ -0,0 +1,8 @@
+package io.quarkus.grpc.examples.hello;
+
+import io.quarkus.test.junit.NativeImageTest;
+
+@NativeImageTest
+class HelloWorldEndpointIT extends HelloWorldEndpointTest {
+
+}
\ No newline at end of file
diff --git a/integration-tests/grpc-plain-text-mutiny/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldEndpointTest.java b/integration-tests/grpc-plain-text-mutiny/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldEndpointTest.java
new file mode 100644
index 0000000000000..2c6c673852346
--- /dev/null
+++ b/integration-tests/grpc-plain-text-mutiny/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldEndpointTest.java
@@ -0,0 +1,25 @@
+package io.quarkus.grpc.examples.hello;
+
+import static io.restassured.RestAssured.get;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+
+import io.quarkus.test.junit.QuarkusTest;
+
+@QuarkusTest
+class HelloWorldEndpointTest {
+
+ @Test
+ public void testHelloWorldServiceUsingBlockingStub() {
+ String response = get("/hello/blocking/neo").asString();
+ assertThat(response).startsWith("Hello neo");
+ }
+
+ @Test
+ public void testHelloWorldServiceUsingMutinyStub() {
+ String response = get("/hello/mutiny/neo-mutiny").asString();
+ assertThat(response).startsWith("Hello neo-mutiny");
+ }
+
+}
diff --git a/integration-tests/grpc-plain-text-mutiny/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldServiceIT.java b/integration-tests/grpc-plain-text-mutiny/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldServiceIT.java
new file mode 100644
index 0000000000000..addf2badc869b
--- /dev/null
+++ b/integration-tests/grpc-plain-text-mutiny/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldServiceIT.java
@@ -0,0 +1,8 @@
+package io.quarkus.grpc.examples.hello;
+
+import io.quarkus.test.junit.NativeImageTest;
+
+@NativeImageTest
+class HelloWorldServiceIT extends HelloWorldEndpointTest {
+
+}
\ No newline at end of file
diff --git a/integration-tests/grpc-plain-text-mutiny/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldServiceTest.java b/integration-tests/grpc-plain-text-mutiny/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldServiceTest.java
new file mode 100644
index 0000000000000..1f775a5177ae8
--- /dev/null
+++ b/integration-tests/grpc-plain-text-mutiny/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldServiceTest.java
@@ -0,0 +1,52 @@
+package io.quarkus.grpc.examples.hello;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.time.Duration;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import examples.GreeterGrpc;
+import examples.HelloReply;
+import examples.HelloRequest;
+import examples.MutinyGreeterGrpc;
+import io.grpc.ManagedChannel;
+import io.grpc.ManagedChannelBuilder;
+import io.quarkus.test.junit.QuarkusTest;
+
+@QuarkusTest
+class HelloWorldServiceTest {
+
+ private ManagedChannel channel;
+
+ @BeforeEach
+ public void init() {
+ channel = ManagedChannelBuilder.forAddress("localhost", 9000).usePlaintext().build();
+ }
+
+ @AfterEach
+ public void cleanup() throws InterruptedException {
+ channel.shutdown();
+ channel.awaitTermination(10, TimeUnit.SECONDS);
+ }
+
+ @Test
+ public void testHelloWorldServiceUsingBlockingStub() {
+ GreeterGrpc.GreeterBlockingStub client = GreeterGrpc.newBlockingStub(channel);
+ HelloReply reply = client
+ .sayHello(HelloRequest.newBuilder().setName("neo-blocking").build());
+ assertThat(reply.getMessage()).isEqualTo("Hello neo-blocking");
+ }
+
+ @Test
+ public void testHelloWorldServiceUsingMutinyStub() {
+ HelloReply reply = MutinyGreeterGrpc.newMutinyStub(channel)
+ .sayHello(HelloRequest.newBuilder().setName("neo-blocking").build())
+ .await().atMost(Duration.ofSeconds(5));
+ assertThat(reply.getMessage()).isEqualTo("Hello neo-blocking");
+ }
+
+}
diff --git a/integration-tests/grpc-plain-text/pom.xml b/integration-tests/grpc-plain-text/pom.xml
index 9a3f60042fe46..4f8f259956c22 100644
--- a/integration-tests/grpc-plain-text/pom.xml
+++ b/integration-tests/grpc-plain-text/pom.xml
@@ -88,6 +88,28 @@
+
+
+
+ io.quarkus
+ quarkus-infinispan-client
+
+
+ io.quarkus
+ quarkus-infinispan-client-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
diff --git a/integration-tests/grpc-plain-text/src/main/java/io/quarkus/grpc/examples/hello/HelloWorldService.java b/integration-tests/grpc-plain-text/src/main/java/io/quarkus/grpc/examples/hello/HelloWorldService.java
index 667afdbe987e6..aa1967bbd877c 100644
--- a/integration-tests/grpc-plain-text/src/main/java/io/quarkus/grpc/examples/hello/HelloWorldService.java
+++ b/integration-tests/grpc-plain-text/src/main/java/io/quarkus/grpc/examples/hello/HelloWorldService.java
@@ -4,21 +4,22 @@
import javax.inject.Singleton;
+import examples.GreeterGrpc;
import examples.HelloReply;
import examples.HelloRequest;
-import examples.MutinyGreeterGrpc;
-import io.smallrye.mutiny.Uni;
+import io.grpc.stub.StreamObserver;
@Singleton
-public class HelloWorldService extends MutinyGreeterGrpc.GreeterImplBase {
+public class HelloWorldService extends GreeterGrpc.GreeterImplBase {
AtomicInteger counter = new AtomicInteger();
@Override
- public Uni sayHello(HelloRequest request) {
+ public void sayHello(HelloRequest request, StreamObserver responseObserver) {
int count = counter.incrementAndGet();
String name = request.getName();
- return Uni.createFrom().item("Hello " + name)
- .map(res -> HelloReply.newBuilder().setMessage(res).setCount(count).build());
+ String res = "Hello " + name;
+ responseObserver.onNext(HelloReply.newBuilder().setMessage(res).setCount(count).build());
+ responseObserver.onCompleted();
}
}
diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml
index ed3bd80c72c2e..e8b5a8353daae 100644
--- a/integration-tests/pom.xml
+++ b/integration-tests/pom.xml
@@ -137,8 +137,9 @@
micrometer-prometheus
- grpc-plain-text
grpc-tls
+ grpc-plain-text
+ grpc-plain-text-mutiny
grpc-mutual-auth
grpc-streaming
grpc-interceptors
diff --git a/test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/KubernetesMockServerTestResource.java b/test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/KubernetesMockServerTestResource.java
index 838dee849e3c9..d0b1e51cc1212 100644
--- a/test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/KubernetesMockServerTestResource.java
+++ b/test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/KubernetesMockServerTestResource.java
@@ -20,6 +20,7 @@ public Map start() {
systemProps.put(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
systemProps.put(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false");
systemProps.put(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "test");
+ systemProps.put(Config.KUBERNETES_HTTP2_DISABLE, "true");
mockServer = new KubernetesMockServer(useHttps());
mockServer.init();