You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ja...@apache.org on 2022/03/09 07:22:30 UTC
[camel-quarkus] branch main updated: Add Azure Core HTTP Client Vert.x extension
This is an automated email from the ASF dual-hosted git repository.
jamesnetherton pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
The following commit(s) were added to refs/heads/main by this push:
new 75a4b1d Add Azure Core HTTP Client Vert.x extension
75a4b1d is described below
commit 75a4b1d6d4132b12f610c774a20346cb930f8755
Author: James Netherton <ja...@gmail.com>
AuthorDate: Tue Mar 8 07:19:23 2022 +0000
Add Azure Core HTTP Client Vert.x extension
Fixes #2196
---
extensions-jvm/azure-cosmosdb/deployment/pom.xml | 2 +-
extensions-jvm/azure-cosmosdb/runtime/pom.xml | 8 +-
extensions-jvm/azure-servicebus/deployment/pom.xml | 4 +
extensions-jvm/azure-servicebus/runtime/pom.xml | 10 +
.../azure-storage-datalake/deployment/pom.xml | 2 +-
.../azure-storage-datalake/runtime/pom.xml | 8 +-
.../deployment}/pom.xml | 72 +++--
.../vertx/AzureCoreHttpClientVertxProcessor.java | 31 ++
.../azure/core/http/vertx/DeadlockTests.java | 89 ++++++
.../http/vertx/VertxHttpClientBuilderTests.java | 181 ++++++++++++
.../http/vertx/VertxHttpClientHttpClientTests.java | 55 ++++
.../http/vertx/VertxHttpClientProviderTests.java | 113 ++++++++
.../vertx/VertxHttpClientResponseTransformer.java | 56 ++++
.../http/vertx/VertxHttpClientRestProxyTests.java | 63 ++++
...VertxHttpClientRestProxyWithHttpProxyTests.java | 86 ++++++
.../http/vertx/VertxHttpClientTestResource.java | 73 +++++
.../core/http/vertx/VertxHttpClientTests.java | 317 +++++++++++++++++++++
.../deployment/src/test/resources/upload.txt | 1 +
.../azure-core-http-client-vertx/pom.xml | 37 +++
.../runtime/pom.xml | 22 +-
.../core/http/vertx/BufferedVertxHttpResponse.java | 72 +++++
.../core/http/vertx/VertxHttpAsyncResponse.java | 52 ++++
.../azure/core/http/vertx/VertxHttpClient.java | 133 +++++++++
.../core/http/vertx/VertxHttpClientBuilder.java | 250 ++++++++++++++++
.../core/http/vertx/VertxHttpClientProvider.java | 76 +++++
.../azure/core/http/vertx/VertxHttpRequest.java | 37 +++
.../azure/core/http/vertx/VertxHttpResponse.java | 73 +++++
.../core/http/vertx/VertxHttpResponseHandler.java | 59 ++++
.../main/resources/META-INF/quarkus-extension.yaml | 27 ++
.../com.azure.core.http.HttpClientProvider | 1 +
extensions-support/azure-core/deployment/pom.xml | 6 +-
extensions-support/azure-core/runtime/pom.xml | 12 +-
extensions-support/pom.xml | 1 +
extensions/azure-eventhubs/runtime/pom.xml | 6 +
extensions/azure-storage-blob/deployment/pom.xml | 2 +-
extensions/azure-storage-blob/runtime/pom.xml | 8 +-
extensions/azure-storage-queue/deployment/pom.xml | 2 +-
extensions/azure-storage-queue/runtime/pom.xml | 8 +-
.../storage/blob/it/AzureStorageBlobTest.java | 39 +--
pom.xml | 2 +
poms/bom-test/pom.xml | 20 ++
poms/bom/pom.xml | 10 +
42 files changed, 2062 insertions(+), 64 deletions(-)
diff --git a/extensions-jvm/azure-cosmosdb/deployment/pom.xml b/extensions-jvm/azure-cosmosdb/deployment/pom.xml
index 1c7501c..2af136b 100644
--- a/extensions-jvm/azure-cosmosdb/deployment/pom.xml
+++ b/extensions-jvm/azure-cosmosdb/deployment/pom.xml
@@ -36,7 +36,7 @@
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-support-azure-core-deployment</artifactId>
+ <artifactId>camel-quarkus-support-azure-core-http-client-vertx-deployment</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
diff --git a/extensions-jvm/azure-cosmosdb/runtime/pom.xml b/extensions-jvm/azure-cosmosdb/runtime/pom.xml
index 37f26bf..6302c56 100644
--- a/extensions-jvm/azure-cosmosdb/runtime/pom.xml
+++ b/extensions-jvm/azure-cosmosdb/runtime/pom.xml
@@ -53,11 +53,17 @@
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-support-azure-core</artifactId>
+ <artifactId>camel-quarkus-support-azure-core-http-client-vertx</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-azure-cosmosdb</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>com.azure</groupId>
+ <artifactId>azure-core-http-netty</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
</dependencies>
diff --git a/extensions-jvm/azure-servicebus/deployment/pom.xml b/extensions-jvm/azure-servicebus/deployment/pom.xml
index fe1d73e..c33bc7f 100644
--- a/extensions-jvm/azure-servicebus/deployment/pom.xml
+++ b/extensions-jvm/azure-servicebus/deployment/pom.xml
@@ -38,6 +38,10 @@
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-support-azure-core-http-client-vertx-deployment</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-azure-servicebus</artifactId>
</dependency>
</dependencies>
diff --git a/extensions-jvm/azure-servicebus/runtime/pom.xml b/extensions-jvm/azure-servicebus/runtime/pom.xml
index 7ed8115..a4d0a9e 100644
--- a/extensions-jvm/azure-servicebus/runtime/pom.xml
+++ b/extensions-jvm/azure-servicebus/runtime/pom.xml
@@ -54,8 +54,18 @@
<artifactId>camel-quarkus-core</artifactId>
</dependency>
<dependency>
+ <groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-support-azure-core-http-client-vertx</artifactId>
+ </dependency>
+ <dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-azure-servicebus</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>com.azure</groupId>
+ <artifactId>azure-core-http-netty</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
</dependencies>
diff --git a/extensions-jvm/azure-storage-datalake/deployment/pom.xml b/extensions-jvm/azure-storage-datalake/deployment/pom.xml
index 8758926..4628cc2 100644
--- a/extensions-jvm/azure-storage-datalake/deployment/pom.xml
+++ b/extensions-jvm/azure-storage-datalake/deployment/pom.xml
@@ -36,7 +36,7 @@
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-support-azure-core-deployment</artifactId>
+ <artifactId>camel-quarkus-support-azure-core-http-client-vertx-deployment</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
diff --git a/extensions-jvm/azure-storage-datalake/runtime/pom.xml b/extensions-jvm/azure-storage-datalake/runtime/pom.xml
index ae64da2..3f75f0d 100644
--- a/extensions-jvm/azure-storage-datalake/runtime/pom.xml
+++ b/extensions-jvm/azure-storage-datalake/runtime/pom.xml
@@ -53,11 +53,17 @@
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-support-azure-core</artifactId>
+ <artifactId>camel-quarkus-support-azure-core-http-client-vertx</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-azure-storage-datalake</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>com.azure</groupId>
+ <artifactId>azure-core-http-netty</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
</dependencies>
diff --git a/extensions-support/azure-core/runtime/pom.xml b/extensions-support/azure-core-http-client-vertx/deployment/pom.xml
similarity index 55%
copy from extensions-support/azure-core/runtime/pom.xml
copy to extensions-support/azure-core-http-client-vertx/deployment/pom.xml
index 701cd25..5e9d670 100644
--- a/extensions-support/azure-core/runtime/pom.xml
+++ b/extensions-support/azure-core-http-client-vertx/deployment/pom.xml
@@ -21,24 +21,19 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-support-azure-core-parent</artifactId>
+ <artifactId>camel-quarkus-support-azure-core-http-client-vertx-parent</artifactId>
<version>2.8.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
- <artifactId>camel-quarkus-support-azure-core</artifactId>
- <name>Camel Quarkus :: Support :: Azure Core :: Runtime</name>
-
- <properties>
- <camel.quarkus.jvmSince>1.7.0</camel.quarkus.jvmSince>
- <camel.quarkus.nativeSince>1.7.0</camel.quarkus.nativeSince>
- </properties>
+ <artifactId>camel-quarkus-support-azure-core-http-client-vertx-deployment</artifactId>
+ <name>Camel Quarkus :: Support :: Azure Core HTTP Client Vert.x :: Deployment</name>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-bom</artifactId>
+ <artifactId>camel-quarkus-bom-test</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>import</scope>
@@ -49,29 +44,73 @@
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
- <artifactId>quarkus-core</artifactId>
+ <artifactId>quarkus-core-deployment</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-vertx-deployment</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-support-jackson-dataformat-xml</artifactId>
+ <artifactId>camel-quarkus-support-azure-core-deployment</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-support-reactor-netty</artifactId>
+ <artifactId>camel-quarkus-support-azure-core-http-client-vertx</artifactId>
+ </dependency>
+
+
+ <dependency>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-junit5-internal</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>testcontainers</artifactId>
+ <scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-integration-wiremock-support</artifactId>
+ <scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-core</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+
+ <!-- Azure core test support -->
+ <dependency>
<groupId>com.azure</groupId>
<artifactId>azure-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.azure</groupId>
+ <artifactId>azure-core-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.azure</groupId>
+ <artifactId>azure-core-test</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.projectreactor</groupId>
+ <artifactId>reactor-test</artifactId>
+ <scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
- <groupId>io.quarkus</groupId>
- <artifactId>quarkus-bootstrap-maven-plugin</artifactId>
- </plugin>
- <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
@@ -86,4 +125,5 @@
</plugin>
</plugins>
</build>
+
</project>
diff --git a/extensions-support/azure-core-http-client-vertx/deployment/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/AzureCoreHttpClientVertxProcessor.java b/extensions-support/azure-core-http-client-vertx/deployment/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/AzureCoreHttpClientVertxProcessor.java
new file mode 100644
index 0000000..db0972b
--- /dev/null
+++ b/extensions-support/azure-core-http-client-vertx/deployment/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/AzureCoreHttpClientVertxProcessor.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.quarkus.support.azure.core.http.vertx;
+
+import io.netty.handler.ssl.OpenSsl;
+import io.quarkus.deployment.annotations.BuildProducer;
+import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem;
+
+public class AzureCoreHttpClientVertxProcessor {
+
+ @BuildStep
+ void runtimeInitializedClasses(BuildProducer<RuntimeInitializedClassBuildItem> runtimeInitializedClasses) {
+ runtimeInitializedClasses.produce(new RuntimeInitializedClassBuildItem(OpenSsl.class.getName()));
+ runtimeInitializedClasses.produce(new RuntimeInitializedClassBuildItem("io.netty.internal.tcnative.SSL"));
+ }
+}
diff --git a/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/DeadlockTests.java b/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/DeadlockTests.java
new file mode 100644
index 0000000..18237b4
--- /dev/null
+++ b/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/DeadlockTests.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.quarkus.support.azure.core.http.vertx;
+
+import java.security.SecureRandom;
+
+import com.azure.core.http.HttpClient;
+import com.azure.core.http.HttpMethod;
+import com.azure.core.http.HttpRequest;
+import com.azure.core.util.FluxUtil;
+import com.github.tomakehurst.wiremock.WireMockServer;
+import com.github.tomakehurst.wiremock.client.WireMock;
+import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
+import io.quarkus.test.QuarkusUnitTest;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+public class DeadlockTests {
+
+ @RegisterExtension
+ static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+ .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class));
+
+ private static final String GET_ENDPOINT = "/get";
+
+ private WireMockServer server;
+ private byte[] expectedGetBytes;
+
+ @BeforeEach
+ public void configureWireMockServer() {
+ expectedGetBytes = new byte[10 * 1024 * 1024];
+ new SecureRandom().nextBytes(expectedGetBytes);
+
+ server = new WireMockServer(WireMockConfiguration.options()
+ .dynamicPort()
+ .disableRequestJournal()
+ .gzipDisabled(true));
+
+ server.stubFor(WireMock.get(GET_ENDPOINT).willReturn(WireMock.aResponse().withBody(expectedGetBytes)));
+
+ server.start();
+ }
+
+ @AfterEach
+ public void shutdownWireMockServer() {
+ if (server != null) {
+ server.shutdown();
+ }
+ }
+
+ @Test
+ public void attemptToDeadlock() {
+ HttpClient httpClient = new VertxHttpClientProvider().createInstance();
+
+ String endpoint = server.baseUrl() + GET_ENDPOINT;
+
+ for (int i = 0; i < 100; i++) {
+ StepVerifier.create(httpClient.send(new HttpRequest(HttpMethod.GET, endpoint))
+ .flatMap(response -> FluxUtil.collectBytesInByteBufferStream(response.getBody())
+ .zipWith(Mono.just(response.getStatusCode()))))
+ .assertNext(responseTuple -> {
+ Assertions.assertEquals(200, responseTuple.getT2());
+ Assertions.assertArrayEquals(expectedGetBytes, responseTuple.getT1());
+ })
+ .verifyComplete();
+ }
+ }
+}
diff --git a/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientBuilderTests.java b/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientBuilderTests.java
new file mode 100644
index 0000000..03f6099
--- /dev/null
+++ b/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientBuilderTests.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.quarkus.support.azure.core.http.vertx;
+
+import java.net.InetSocketAddress;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+import com.azure.core.http.HttpClient;
+import com.azure.core.http.HttpMethod;
+import com.azure.core.http.HttpRequest;
+import com.azure.core.http.ProxyOptions;
+import com.azure.core.util.Configuration;
+import com.github.tomakehurst.wiremock.WireMockServer;
+import com.github.tomakehurst.wiremock.client.WireMock;
+import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
+import io.vertx.core.Vertx;
+import io.vertx.ext.web.client.WebClientOptions;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+import reactor.test.StepVerifier;
+
+import static org.apache.camel.quarkus.support.azure.core.http.vertx.VertxHttpClientTestResource.PROXY_PASSWORD;
+import static org.apache.camel.quarkus.support.azure.core.http.vertx.VertxHttpClientTestResource.PROXY_USER;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Tests {@link VertxHttpClientBuilder}.
+ */
+public class VertxHttpClientBuilderTests {
+ private static final String COOKIE_VALIDATOR_PATH = "/cookieValidator";
+ private static final String DEFAULT_PATH = "/default";
+ private static final String DISPATCHER_PATH = "/dispatcher";
+
+ private static final WireMockServer server = new WireMockServer(
+ WireMockConfiguration.options().dynamicPort().disableRequestJournal());
+ private static final Vertx vertx = Vertx.vertx();
+
+ private static String defaultUrl;
+
+ @BeforeAll
+ public static void setupWireMock() {
+ // Mocked endpoint to test building a client with a prebuilt client.
+ server.stubFor(WireMock.get(COOKIE_VALIDATOR_PATH).withCookie("test", WireMock.matching("success"))
+ .willReturn(WireMock.aResponse().withStatus(200)));
+
+ // Mocked endpoint to test building a client with a timeout.
+ server.stubFor(WireMock.get(DEFAULT_PATH).willReturn(WireMock.aResponse().withStatus(200)));
+
+ // Mocked endpoint to test building a client with a dispatcher and uses a delayed response.
+ server.stubFor(WireMock.get(DISPATCHER_PATH).willReturn(WireMock.aResponse().withStatus(200)
+ .withFixedDelay(5000)));
+
+ server.start();
+
+ defaultUrl = "http://localhost:" + server.port() + DEFAULT_PATH;
+ }
+
+ @AfterAll
+ public static void afterAll() throws InterruptedException {
+ if (server.isRunning()) {
+ server.shutdown();
+ }
+ CountDownLatch latch = new CountDownLatch(1);
+ vertx.close(x -> latch.countDown());
+ latch.await();
+ }
+
+ @Test
+ public void buildWithConfigurationNone() {
+ HttpClient client = new VertxHttpClientBuilder(vertx)
+ .configuration(Configuration.NONE)
+ .build();
+ try {
+ StepVerifier.create(client.send(new HttpRequest(HttpMethod.GET, defaultUrl)))
+ .assertNext(response -> assertEquals(200, response.getStatusCode()))
+ .verifyComplete();
+ } finally {
+ ((VertxHttpClient) client).close();
+ }
+ }
+
+ @Test
+ public void buildWithDefaultConnectionOptions() {
+ WebClientOptions options = new WebClientOptions();
+
+ HttpClient client = new VertxHttpClientBuilder(vertx)
+ .webClientOptions(options)
+ .build();
+
+ try {
+ StepVerifier.create(client.send(new HttpRequest(HttpMethod.GET, defaultUrl)))
+ .assertNext(response -> assertEquals(200, response.getStatusCode()))
+ .verifyComplete();
+
+ assertEquals(options.getConnectTimeout(), 10000);
+ assertEquals(options.getIdleTimeout(), 60);
+ assertEquals(options.getReadIdleTimeout(), 60);
+ assertEquals(options.getWriteIdleTimeout(), 60);
+ } finally {
+ ((VertxHttpClient) client).close();
+ }
+ }
+
+ @Test
+ public void buildWithConnectionOptions() {
+ WebClientOptions options = new WebClientOptions();
+
+ HttpClient client = new VertxHttpClientBuilder(vertx)
+ .webClientOptions(options)
+ .connectTimeout(Duration.ofSeconds(10))
+ .idleTimeout(Duration.ofSeconds(20))
+ .readIdleTimeout(Duration.ofSeconds(30))
+ .writeIdleTimeout(Duration.ofSeconds(40))
+ .build();
+
+ try {
+ StepVerifier.create(client.send(new HttpRequest(HttpMethod.GET, defaultUrl)))
+ .assertNext(response -> assertEquals(200, response.getStatusCode()))
+ .verifyComplete();
+
+ assertEquals(options.getConnectTimeout(), 10000);
+ assertEquals(options.getIdleTimeout(), 20);
+ assertEquals(options.getReadIdleTimeout(), 30);
+ assertEquals(options.getWriteIdleTimeout(), 40);
+ } finally {
+ ((VertxHttpClient) client).close();
+ }
+ }
+
+ @ParameterizedTest
+ @EnumSource(ProxyOptions.Type.class)
+ public void allProxyOptions(ProxyOptions.Type type) {
+ WebClientOptions options = new WebClientOptions();
+ InetSocketAddress address = new InetSocketAddress("localhost", 8888);
+ ProxyOptions proxyOptions = new ProxyOptions(type, address);
+ proxyOptions.setCredentials(PROXY_USER, PROXY_PASSWORD);
+ proxyOptions.setNonProxyHosts("foo.*|*bar.com|microsoft.com");
+
+ HttpClient client = new VertxHttpClientBuilder(vertx)
+ .webClientOptions(options)
+ .proxy(proxyOptions)
+ .build();
+
+ try {
+ io.vertx.core.net.ProxyOptions vertxProxyOptions = options.getProxyOptions();
+ assertEquals(vertxProxyOptions.getHost(), address.getHostName());
+ assertEquals(vertxProxyOptions.getPort(), address.getPort());
+ assertEquals(vertxProxyOptions.getType().name(), type.name());
+ assertEquals(vertxProxyOptions.getUsername(), PROXY_USER);
+ assertEquals(vertxProxyOptions.getPassword(), PROXY_PASSWORD);
+
+ List<String> proxyHosts = new ArrayList<>();
+ proxyHosts.add("foo*");
+ proxyHosts.add(".*bar.com");
+ proxyHosts.add("microsoft.com");
+ assertEquals(proxyHosts, options.getNonProxyHosts());
+ } finally {
+ ((VertxHttpClient) client).close();
+ }
+ }
+}
diff --git a/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientHttpClientTests.java b/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientHttpClientTests.java
new file mode 100644
index 0000000..67dae66
--- /dev/null
+++ b/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientHttpClientTests.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.quarkus.support.azure.core.http.vertx;
+
+import java.util.concurrent.CountDownLatch;
+
+import com.azure.core.http.HttpClient;
+import com.azure.core.test.HttpClientTestsWireMockServer;
+import com.azure.core.test.http.HttpClientTests;
+import com.github.tomakehurst.wiremock.WireMockServer;
+import io.vertx.core.Vertx;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+
+public class VertxHttpClientHttpClientTests extends HttpClientTests {
+ private static final WireMockServer server = HttpClientTestsWireMockServer.getHttpClientTestsServer();
+ private static final Vertx vertx = Vertx.vertx();
+
+ @BeforeAll
+ public static void getWireMockServer() {
+ server.start();
+ }
+
+ @AfterAll
+ public static void afterAll() throws InterruptedException {
+ server.shutdown();
+ CountDownLatch latch = new CountDownLatch(1);
+ vertx.close(x -> latch.countDown());
+ latch.await();
+ }
+
+ @Override
+ protected int getWireMockPort() {
+ return server.port();
+ }
+
+ @Override
+ protected HttpClient createHttpClient() {
+ return new VertxHttpClientBuilder(vertx).build();
+ }
+}
diff --git a/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientProviderTests.java b/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientProviderTests.java
new file mode 100644
index 0000000..402d154
--- /dev/null
+++ b/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientProviderTests.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.quarkus.support.azure.core.http.vertx;
+
+import java.net.InetSocketAddress;
+import java.time.Duration;
+
+import com.azure.core.http.ProxyOptions;
+import com.azure.core.util.Configuration;
+import com.azure.core.util.HttpClientOptions;
+import io.quarkus.test.QuarkusUnitTest;
+import io.vertx.ext.web.client.WebClientOptions;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * Tests {@link VertxHttpClientProvider}.
+ */
+
+public class VertxHttpClientProviderTests {
+
+ @RegisterExtension
+ static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+ .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class));
+
+ @Test
+ public void nullOptionsReturnsBaseClient() {
+ VertxHttpClient httpClient = (VertxHttpClient) new VertxHttpClientProvider()
+ .createInstance(null);
+
+ ProxyOptions environmentProxy = ProxyOptions.fromConfiguration(Configuration.getGlobalConfiguration());
+ WebClientOptions options = httpClient.getWebClientOptions();
+ io.vertx.core.net.ProxyOptions proxyOptions = options.getProxyOptions();
+ if (environmentProxy == null) {
+ assertNull(proxyOptions);
+ } else {
+ assertNotNull(proxyOptions);
+ assertEquals(environmentProxy.getAddress().getHostName(), proxyOptions.getHost());
+ }
+ }
+
+ @Test
+ public void defaultOptionsReturnsBaseClient() {
+ VertxHttpClient httpClient = (VertxHttpClient) new VertxHttpClientProvider()
+ .createInstance(new HttpClientOptions());
+
+ ProxyOptions environmentProxy = ProxyOptions.fromConfiguration(Configuration.getGlobalConfiguration());
+ WebClientOptions options = httpClient.getWebClientOptions();
+ io.vertx.core.net.ProxyOptions proxyOptions = options.getProxyOptions();
+ if (environmentProxy == null) {
+ assertNull(proxyOptions);
+ } else {
+ assertNotNull(proxyOptions);
+ assertEquals(environmentProxy.getAddress().getHostName(), proxyOptions.getHost());
+ }
+ }
+
+ @Test
+ public void optionsWithAProxy() {
+ ProxyOptions proxyOptions = new ProxyOptions(ProxyOptions.Type.HTTP, new InetSocketAddress("localhost", 8888));
+ proxyOptions.setNonProxyHosts("foo.*|bar.*|cheese.com|wine.org");
+
+ HttpClientOptions clientOptions = new HttpClientOptions().setProxyOptions(proxyOptions);
+
+ VertxHttpClient httpClient = (VertxHttpClient) new VertxHttpClientProvider()
+ .createInstance(clientOptions);
+
+ WebClientOptions options = httpClient.getWebClientOptions();
+ io.vertx.core.net.ProxyOptions vertxProxyOptions = options.getProxyOptions();
+ assertNotNull(vertxProxyOptions);
+ assertEquals(proxyOptions.getAddress().getHostName(), vertxProxyOptions.getHost());
+ assertEquals(proxyOptions.getAddress().getPort(), vertxProxyOptions.getPort());
+ assertEquals(proxyOptions.getType().name(), vertxProxyOptions.getType().name());
+ }
+
+ @Test
+ public void optionsWithTimeouts() {
+ long expectedTimeout = 15000;
+ Duration timeout = Duration.ofMillis(expectedTimeout);
+ HttpClientOptions clientOptions = new HttpClientOptions()
+ .setWriteTimeout(timeout)
+ .setResponseTimeout(timeout)
+ .setReadTimeout(timeout);
+
+ VertxHttpClient httpClient = (VertxHttpClient) new VertxHttpClientProvider()
+ .createInstance(clientOptions);
+
+ WebClientOptions options = httpClient.getWebClientOptions();
+
+ assertEquals(timeout.getSeconds(), options.getWriteIdleTimeout());
+ assertEquals(timeout.getSeconds(), options.getReadIdleTimeout());
+ }
+}
diff --git a/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientResponseTransformer.java b/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientResponseTransformer.java
new file mode 100644
index 0000000..fe7aa1c
--- /dev/null
+++ b/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientResponseTransformer.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.quarkus.support.azure.core.http.vertx;
+
+import com.github.tomakehurst.wiremock.common.FileSource;
+import com.github.tomakehurst.wiremock.extension.Parameters;
+import com.github.tomakehurst.wiremock.extension.ResponseTransformer;
+import com.github.tomakehurst.wiremock.http.Request;
+import com.github.tomakehurst.wiremock.http.Response;
+
+import static org.apache.camel.quarkus.support.azure.core.http.vertx.VertxHttpClientTests.RETURN_HEADERS_AS_IS_PATH;
+
+/**
+ * Mock response transformer used to test {@link VertxHttpClient}.
+ */
+public class VertxHttpClientResponseTransformer extends ResponseTransformer {
+ public static final String NAME = "vertx-http-client-response-transformer";
+
+ @Override
+ public Response transform(Request request, Response response, FileSource fileSource, Parameters parameters) {
+ String url = request.getUrl();
+
+ if (RETURN_HEADERS_AS_IS_PATH.equalsIgnoreCase(url)) {
+ return Response.response()
+ .status(200)
+ .headers(request.getHeaders())
+ .build();
+ }
+
+ return response;
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public boolean applyGlobally() {
+ return false;
+ }
+}
diff --git a/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientRestProxyTests.java b/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientRestProxyTests.java
new file mode 100644
index 0000000..153d840
--- /dev/null
+++ b/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientRestProxyTests.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.quarkus.support.azure.core.http.vertx;
+
+import javax.inject.Inject;
+
+import com.azure.core.http.HttpClient;
+import com.azure.core.test.RestProxyTestsWireMockServer;
+import com.azure.core.test.implementation.RestProxyTests;
+import com.github.tomakehurst.wiremock.WireMockServer;
+import io.quarkus.test.QuarkusUnitTest;
+import io.vertx.core.Vertx;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+public class VertxHttpClientRestProxyTests extends RestProxyTests {
+ private final static WireMockServer server = RestProxyTestsWireMockServer.getRestProxyTestsServer();
+
+ @RegisterExtension
+ static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+ .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
+ .addAsResource("upload.txt", "upload.txt"));
+
+ @Inject
+ Vertx vertx;
+
+ @BeforeAll
+ public static void getWireMockServer() {
+ server.start();
+ }
+
+ @AfterAll
+ public static void shutdownWireMockServer() {
+ server.shutdown();
+ }
+
+ @Override
+ protected int getWireMockPort() {
+ return server.port();
+ }
+
+ @Override
+ protected HttpClient createHttpClient() {
+ return new VertxHttpClientBuilder(vertx).build();
+ }
+}
diff --git a/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientRestProxyWithHttpProxyTests.java b/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientRestProxyWithHttpProxyTests.java
new file mode 100644
index 0000000..748ede8
--- /dev/null
+++ b/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientRestProxyWithHttpProxyTests.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.quarkus.support.azure.core.http.vertx;
+
+import java.net.InetSocketAddress;
+
+import javax.inject.Inject;
+
+import com.azure.core.http.HttpClient;
+import com.azure.core.http.ProxyOptions;
+import com.azure.core.test.RestProxyTestsWireMockServer;
+import com.azure.core.test.implementation.RestProxyTests;
+import com.github.tomakehurst.wiremock.WireMockServer;
+import io.quarkus.test.QuarkusUnitTest;
+import io.quarkus.test.common.QuarkusTestResource;
+import io.vertx.core.Vertx;
+import org.eclipse.microprofile.config.Config;
+import org.eclipse.microprofile.config.ConfigProvider;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.apache.camel.quarkus.support.azure.core.http.vertx.VertxHttpClientTestResource.PROXY_PASSWORD;
+import static org.apache.camel.quarkus.support.azure.core.http.vertx.VertxHttpClientTestResource.PROXY_USER;
+
+@QuarkusTestResource(VertxHttpClientTestResource.class)
+public class VertxHttpClientRestProxyWithHttpProxyTests extends RestProxyTests {
+ private static WireMockServer server;
+
+ @RegisterExtension
+ static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+ .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
+ .addAsResource("upload.txt", "upload.txt"));
+
+ @Inject
+ Vertx vertx;
+
+ @BeforeAll
+ public static void getWireMockServer() {
+ server = RestProxyTestsWireMockServer.getRestProxyTestsServer();
+ server.start();
+ }
+
+ @AfterAll
+ public static void shutdownWireMockServer() {
+ if (server != null) {
+ server.shutdown();
+ }
+ }
+
+ @Override
+ protected int getWireMockPort() {
+ return server.port();
+ }
+
+ @Override
+ protected HttpClient createHttpClient() {
+ Config config = ConfigProvider.getConfig();
+ String proxyHost = config.getValue("tiny.proxy.host", String.class);
+ int proxyPort = config.getValue("tiny.proxy.port", int.class);
+
+ InetSocketAddress address = new InetSocketAddress(proxyHost, proxyPort);
+ ProxyOptions proxyOptions = new ProxyOptions(ProxyOptions.Type.HTTP, address);
+ proxyOptions.setCredentials(PROXY_USER, PROXY_PASSWORD);
+
+ return new VertxHttpClientBuilder(vertx)
+ .proxy(proxyOptions)
+ .build();
+ }
+}
diff --git a/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientTestResource.java b/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientTestResource.java
new file mode 100644
index 0000000..2a54ab7
--- /dev/null
+++ b/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientTestResource.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.quarkus.support.azure.core.http.vertx;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
+import org.apache.commons.lang3.SystemUtils;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.utility.DockerImageName;
+
+public class VertxHttpClientTestResource implements QuarkusTestResourceLifecycleManager {
+
+ public static final String PROXY_USER = "admin";
+ public static final String PROXY_PASSWORD = "p4ssw0rd";
+
+ private static final DockerImageName TINY_PROXY_IMAGE_NAME = DockerImageName.parse("monokal/tinyproxy");
+ private static final Integer TINY_PROXY_PORT = 8888;
+ private GenericContainer container;
+
+ @Override
+ public Map<String, String> start() {
+ String host;
+ int port;
+
+ container = new GenericContainer(TINY_PROXY_IMAGE_NAME)
+ .withEnv("BASIC_AUTH_USER", PROXY_USER)
+ .withEnv("BASIC_AUTH_PASSWORD", PROXY_PASSWORD)
+ .withCommand("ANY")
+ .waitingFor(Wait.forListeningPort());
+
+ if (SystemUtils.IS_OS_LINUX) {
+ container.withNetworkMode("host");
+ port = TINY_PROXY_PORT;
+ host = "localhost";
+ } else {
+ container.withNetworkMode("bridge")
+ .withExposedPorts(TINY_PROXY_PORT);
+ port = container.getMappedPort(TINY_PROXY_PORT);
+ host = "host.docker.internal";
+ }
+
+ container.start();
+
+ Map<String, String> options = new HashMap<>();
+ options.put("tiny.proxy.host", host);
+ options.put("tiny.proxy.port", String.valueOf(port));
+ return options;
+ }
+
+ @Override
+ public void stop() {
+ if (container != null) {
+ container.stop();
+ }
+ }
+}
diff --git a/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientTests.java b/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientTests.java
new file mode 100644
index 0000000..ba72040
--- /dev/null
+++ b/extensions-support/azure-core-http-client-vertx/deployment/src/test/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientTests.java
@@ -0,0 +1,317 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.quarkus.support.azure.core.http.vertx;
+
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+import javax.inject.Inject;
+
+import com.azure.core.http.HttpClient;
+import com.azure.core.http.HttpHeader;
+import com.azure.core.http.HttpHeaders;
+import com.azure.core.http.HttpMethod;
+import com.azure.core.http.HttpRequest;
+import com.azure.core.http.HttpResponse;
+import com.github.tomakehurst.wiremock.WireMockServer;
+import com.github.tomakehurst.wiremock.client.WireMock;
+import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
+import io.quarkus.test.QuarkusUnitTest;
+import io.vertx.core.Vertx;
+import io.vertx.core.VertxException;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+import reactor.core.scheduler.Schedulers;
+import reactor.test.StepVerifier;
+import reactor.test.StepVerifierOptions;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class VertxHttpClientTests {
+ static final String RETURN_HEADERS_AS_IS_PATH = "/returnHeadersAsIs";
+
+ private static final String SHORT_BODY = "hi there";
+ private static final String LONG_BODY = createLongBody();
+
+ private static WireMockServer server;
+
+ @Inject
+ Vertx vertx;
+
+ @RegisterExtension
+ static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+ .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
+ .addClass(VertxHttpClientResponseTransformer.class));
+
+ @BeforeAll
+ public static void beforeClass() {
+ server = new WireMockServer(WireMockConfiguration.options()
+ .extensions(new VertxHttpClientResponseTransformer())
+ .dynamicPort()
+ .disableRequestJournal()
+ .gzipDisabled(true));
+
+ server.stubFor(WireMock.get("/short").willReturn(WireMock.aResponse().withBody(SHORT_BODY)));
+ server.stubFor(WireMock.get("/long").willReturn(WireMock.aResponse().withBody(LONG_BODY)));
+ server.stubFor(WireMock.get("/error").willReturn(WireMock.aResponse().withBody("error").withStatus(500)));
+ server.stubFor(WireMock.post("/shortPost").willReturn(WireMock.aResponse().withBody(SHORT_BODY)));
+ server.stubFor(WireMock.get(RETURN_HEADERS_AS_IS_PATH).willReturn(WireMock.aResponse()
+ .withTransformers(VertxHttpClientResponseTransformer.NAME)));
+
+ server.start();
+ }
+
+ @AfterAll
+ public static void afterClass() {
+ if (server != null) {
+ server.shutdown();
+ }
+ }
+
+ @Test
+ public void testFlowableResponseShortBodyAsByteArrayAsync() {
+ checkBodyReceived(SHORT_BODY, "/short");
+ }
+
+ @Test
+ public void testFlowableResponseLongBodyAsByteArrayAsync() {
+ checkBodyReceived(LONG_BODY, "/long");
+ }
+
+ @Test
+ public void testFlowableWhenServerReturnsBodyAndNoErrorsWhenHttp500Returned() {
+ HttpResponse response = getResponse("/error");
+ assertEquals(500, response.getStatusCode());
+ StepVerifier.create(response.getBodyAsString())
+ .expectNext("error")
+ .expectComplete()
+ .verify(Duration.ofSeconds(20));
+ }
+
+ @Test
+ public void testFlowableBackpressure() {
+ HttpResponse response = getResponse("/long");
+
+ StepVerifierOptions stepVerifierOptions = StepVerifierOptions.create();
+ stepVerifierOptions.initialRequest(0);
+
+ StepVerifier.create(response.getBody(), stepVerifierOptions)
+ .expectNextCount(0)
+ .thenRequest(1)
+ .expectNextCount(1)
+ .thenRequest(3)
+ .expectNextCount(3)
+ .thenRequest(Long.MAX_VALUE)
+ .thenConsumeWhile(ByteBuffer::hasRemaining)
+ .verifyComplete();
+ }
+
+ @Test
+ public void testRequestBodyIsErrorShouldPropagateToResponse() {
+ HttpClient client = new VertxHttpClientProvider().createInstance();
+ HttpRequest request = new HttpRequest(HttpMethod.POST, url(server, "/shortPost"))
+ .setHeader("Content-Length", "123")
+ .setBody(Flux.error(new RuntimeException("boo")));
+
+ StepVerifier.create(client.send(request))
+ .expectErrorMessage("boo")
+ .verify();
+ }
+
+ @Test
+ public void testRequestBodyEndsInErrorShouldPropagateToResponse() {
+ HttpClient client = new VertxHttpClientProvider().createInstance();
+ String contentChunk = "abcdefgh";
+ int repetitions = 1000;
+ HttpRequest request = new HttpRequest(HttpMethod.POST, url(server, "/shortPost"))
+ .setHeader("Content-Length", String.valueOf(contentChunk.length() * (repetitions + 1)))
+ .setBody(Flux.just(contentChunk)
+ .repeat(repetitions)
+ .map(s -> ByteBuffer.wrap(s.getBytes(StandardCharsets.UTF_8)))
+ .concatWith(Flux.error(new RuntimeException("boo"))));
+ StepVerifier.create(client.send(request))
+ .expectErrorMessage("boo")
+ .verify(Duration.ofSeconds(10));
+ }
+
+ @Test
+ public void testServerShutsDownSocketShouldPushErrorToContentFlowable() {
+ Assertions.assertTimeout(Duration.ofMillis(5000), () -> {
+ CountDownLatch latch = new CountDownLatch(1);
+ try (ServerSocket ss = new ServerSocket(0)) {
+ Mono.fromCallable(() -> {
+ latch.countDown();
+ Socket socket = ss.accept();
+ // give the client time to get request across
+ Thread.sleep(500);
+ // respond but don't send the complete response
+ byte[] bytes = new byte[1024];
+ int n = socket.getInputStream().read(bytes);
+ System.out.println(new String(bytes, 0, n, StandardCharsets.UTF_8));
+ String response = "HTTP/1.1 200 OK\r\n" //
+ + "Content-Type: text/plain\r\n" //
+ + "Content-Length: 10\r\n" //
+ + "\r\n" //
+ + "zi";
+ OutputStream out = socket.getOutputStream();
+ out.write(response.getBytes());
+ out.flush();
+ // kill the socket with HTTP response body incomplete
+ socket.close();
+ return 1;
+ }).subscribeOn(Schedulers.boundedElastic()).subscribe();
+ //
+ latch.await();
+ HttpClient client = new VertxHttpClientBuilder(vertx).build();
+ HttpRequest request = new HttpRequest(HttpMethod.GET,
+ new URL("http://localhost:" + ss.getLocalPort() + "/ioException"));
+
+ StepVerifier.create(client.send(request))
+ .verifyError(VertxException.class);
+ }
+ });
+ }
+
+ @Test
+ public void testConcurrentRequests() throws NoSuchAlgorithmException {
+ int numRequests = 100; // 100 = 1GB of data read
+ HttpClient client = new VertxHttpClientProvider().createInstance();
+ byte[] expectedDigest = digest(LONG_BODY);
+ long expectedByteCount = (long) numRequests * LONG_BODY.getBytes(StandardCharsets.UTF_8).length;
+
+ Mono<Long> numBytesMono = Flux.range(1, numRequests)
+ .parallel(10)
+ .runOn(Schedulers.boundedElastic())
+ .flatMap(n -> Mono.fromCallable(() -> getResponse(client, "/long")).flatMapMany(response -> {
+ MessageDigest md = md5Digest();
+ return response.getBody()
+ .doOnNext(buffer -> md.update(buffer.duplicate()))
+ .doOnComplete(() -> assertArrayEquals(expectedDigest, md.digest(), "wrong digest!"));
+ }))
+ .sequential()
+ .map(buffer -> (long) buffer.remaining())
+ .reduce(Long::sum);
+
+ StepVerifier.create(numBytesMono)
+ .expectNext(expectedByteCount)
+ .expectComplete()
+ .verify(Duration.ofSeconds(60));
+ }
+
+ @Test
+ public void validateHeadersReturnAsIs() {
+ HttpClient client = new VertxHttpClientProvider().createInstance();
+
+ final String singleValueHeaderName = "singleValue";
+ final String singleValueHeaderValue = "value";
+
+ final String multiValueHeaderName = "Multi-value";
+ final List<String> multiValueHeaderValue = Arrays.asList("value1", "value2");
+
+ HttpHeaders headers = new HttpHeaders()
+ .set(singleValueHeaderName, singleValueHeaderValue)
+ .set(multiValueHeaderName, multiValueHeaderValue);
+
+ StepVerifier.create(client.send(new HttpRequest(HttpMethod.GET, url(server, RETURN_HEADERS_AS_IS_PATH),
+ headers, Flux.empty())))
+ .assertNext(response -> {
+ Assertions.assertEquals(200, response.getStatusCode());
+
+ HttpHeaders responseHeaders = response.getHeaders();
+ HttpHeader singleValueHeader = responseHeaders.get(singleValueHeaderName);
+ assertEquals(singleValueHeaderName, singleValueHeader.getName());
+ assertEquals(singleValueHeaderValue, singleValueHeader.getValue());
+
+ HttpHeader multiValueHeader = responseHeaders.get("Multi-value");
+ assertEquals(multiValueHeaderName, multiValueHeader.getName());
+ })
+ .expectComplete()
+ .verify(Duration.ofSeconds(10));
+ }
+
+ private static MessageDigest md5Digest() {
+ try {
+ return MessageDigest.getInstance("MD5");
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static byte[] digest(String s) throws NoSuchAlgorithmException {
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ md.update(s.getBytes(StandardCharsets.UTF_8));
+ return md.digest();
+ }
+
+ private HttpResponse getResponse(String path) {
+ HttpClient client = new VertxHttpClientBuilder(vertx).build();
+ return getResponse(client, path);
+ }
+
+ private static HttpResponse getResponse(HttpClient client, String path) {
+ HttpRequest request = new HttpRequest(HttpMethod.GET, url(server, path));
+ return client.send(request).block();
+ }
+
+ static URL url(WireMockServer server, String path) {
+ try {
+ return new URL("http://localhost:" + server.port() + path);
+ } catch (MalformedURLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static String createLongBody() {
+ StringBuilder builder = new StringBuilder("abcdefghijk".length() * 1000000);
+ for (int i = 0; i < 1000000; i++) {
+ builder.append("abcdefghijk");
+ }
+
+ return builder.toString();
+ }
+
+ private void checkBodyReceived(String expectedBody, String path) {
+ HttpClient client = new VertxHttpClientBuilder(vertx).build();
+ StepVerifier.create(doRequest(client, path).getBodyAsByteArray())
+ .assertNext(bytes -> assertEquals(expectedBody, new String(bytes, StandardCharsets.UTF_8)))
+ .verifyComplete();
+ }
+
+ private HttpResponse doRequest(HttpClient client, String path) {
+ HttpRequest request = new HttpRequest(HttpMethod.GET, url(server, path));
+ return client.send(request).block();
+ }
+}
diff --git a/extensions-support/azure-core-http-client-vertx/deployment/src/test/resources/upload.txt b/extensions-support/azure-core-http-client-vertx/deployment/src/test/resources/upload.txt
new file mode 100644
index 0000000..ff3bb63
--- /dev/null
+++ b/extensions-support/azure-core-http-client-vertx/deployment/src/test/resources/upload.txt
@@ -0,0 +1 @@
+The quick brown fox jumps over the lazy dog
\ No newline at end of file
diff --git a/extensions-support/azure-core-http-client-vertx/pom.xml b/extensions-support/azure-core-http-client-vertx/pom.xml
new file mode 100644
index 0000000..82f4083
--- /dev/null
+++ b/extensions-support/azure-core-http-client-vertx/pom.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-extensions-support</artifactId>
+ <version>2.8.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>camel-quarkus-support-azure-core-http-client-vertx-parent</artifactId>
+ <name>Camel Quarkus :: Support :: Azure Core HTTP Client Vert.x</name>
+ <packaging>pom</packaging>
+
+ <modules>
+ <module>deployment</module>
+ <module>runtime</module>
+ </modules>
+</project>
diff --git a/extensions-support/azure-core/runtime/pom.xml b/extensions-support/azure-core-http-client-vertx/runtime/pom.xml
similarity index 81%
copy from extensions-support/azure-core/runtime/pom.xml
copy to extensions-support/azure-core-http-client-vertx/runtime/pom.xml
index 701cd25..6a86758 100644
--- a/extensions-support/azure-core/runtime/pom.xml
+++ b/extensions-support/azure-core-http-client-vertx/runtime/pom.xml
@@ -21,17 +21,17 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-support-azure-core-parent</artifactId>
+ <artifactId>camel-quarkus-support-azure-core-http-client-vertx-parent</artifactId>
<version>2.8.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
- <artifactId>camel-quarkus-support-azure-core</artifactId>
- <name>Camel Quarkus :: Support :: Azure Core :: Runtime</name>
+ <artifactId>camel-quarkus-support-azure-core-http-client-vertx</artifactId>
+ <name>Camel Quarkus :: Support :: Azure Core HTTP Client Vert.x :: Runtime</name>
<properties>
- <camel.quarkus.jvmSince>1.7.0</camel.quarkus.jvmSince>
- <camel.quarkus.nativeSince>1.7.0</camel.quarkus.nativeSince>
+ <camel.quarkus.jvmSince>2.8.0</camel.quarkus.jvmSince>
+ <camel.quarkus.nativeSince>2.8.0</camel.quarkus.nativeSince>
</properties>
<dependencyManagement>
@@ -52,17 +52,21 @@
<artifactId>quarkus-core</artifactId>
</dependency>
<dependency>
- <groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-support-jackson-dataformat-xml</artifactId>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-vertx</artifactId>
</dependency>
<dependency>
- <groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-support-reactor-netty</artifactId>
+ <groupId>io.vertx</groupId>
+ <artifactId>vertx-web-client</artifactId>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-core</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-support-azure-core</artifactId>
+ </dependency>
</dependencies>
<build>
diff --git a/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/BufferedVertxHttpResponse.java b/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/BufferedVertxHttpResponse.java
new file mode 100644
index 0000000..344251f
--- /dev/null
+++ b/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/BufferedVertxHttpResponse.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.quarkus.support.azure.core.http.vertx;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+import com.azure.core.http.HttpRequest;
+import com.azure.core.http.HttpResponse;
+import io.vertx.core.buffer.Buffer;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public final class BufferedVertxHttpResponse extends VertxHttpAsyncResponse {
+
+ private final Buffer body;
+
+ BufferedVertxHttpResponse(HttpRequest request, io.vertx.ext.web.client.HttpResponse response, Buffer body) {
+ super(request, response);
+ this.body = body;
+ }
+
+ @Override
+ public Flux<ByteBuffer> getBody() {
+ return Flux.defer(() -> {
+ if (this.body == null || this.body.length() == 0) {
+ return Flux.empty();
+ }
+ return Flux.just(this.body.getByteBuf().nioBuffer());
+ });
+ }
+
+ @Override
+ public Mono<byte[]> getBodyAsByteArray() {
+ return Mono.defer(() -> {
+ if (this.body == null || this.body.length() == 0) {
+ return Mono.empty();
+ }
+ return Mono.just(this.body.getBytes());
+ });
+ }
+
+ @Override
+ public Mono<InputStream> getBodyAsInputStream() {
+ return Mono.defer(() -> {
+ if (this.body == null || this.body.length() == 0) {
+ return Mono.empty();
+ }
+ return Mono.just(new ByteArrayInputStream(this.body.getBytes()));
+ });
+ }
+
+ @Override
+ public HttpResponse buffer() {
+ return this;
+ }
+}
diff --git a/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpAsyncResponse.java b/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpAsyncResponse.java
new file mode 100644
index 0000000..740c442
--- /dev/null
+++ b/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpAsyncResponse.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.quarkus.support.azure.core.http.vertx;
+
+import java.nio.ByteBuffer;
+
+import com.azure.core.http.HttpRequest;
+import io.vertx.core.buffer.Buffer;
+import io.vertx.ext.web.client.HttpResponse;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public class VertxHttpAsyncResponse extends VertxHttpResponse {
+
+ VertxHttpAsyncResponse(HttpRequest request, HttpResponse response) {
+ super(request, response);
+ }
+
+ @Override
+ public Flux<ByteBuffer> getBody() {
+ Buffer responseBody = getVertxHttpResponse().bodyAsBuffer();
+ if (responseBody == null || responseBody.length() == 0) {
+ return Flux.empty();
+ }
+ return Flux.just(responseBody.getByteBuf().nioBuffer());
+ }
+
+ @Override
+ public Mono<byte[]> getBodyAsByteArray() {
+ return Mono.fromCallable(() -> {
+ Buffer responseBody = getVertxHttpResponse().bodyAsBuffer();
+ if (responseBody == null || responseBody.length() == 0) {
+ return null;
+ }
+ return responseBody.getBytes();
+ });
+ }
+}
diff --git a/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClient.java b/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClient.java
new file mode 100644
index 0000000..5500a7d
--- /dev/null
+++ b/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClient.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.quarkus.support.azure.core.http.vertx;
+
+import java.io.Closeable;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.util.Objects;
+
+import com.azure.core.http.HttpClient;
+import com.azure.core.http.HttpMethod;
+import com.azure.core.http.HttpRequest;
+import com.azure.core.http.HttpResponse;
+import com.azure.core.util.Context;
+import io.vertx.core.buffer.Buffer;
+import io.vertx.ext.web.client.WebClient;
+import io.vertx.ext.web.client.WebClientOptions;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+/**
+ * {@link HttpClient} implementation for the Vert.x {@link WebClient}.
+ */
+public class VertxHttpClient implements HttpClient, Closeable {
+
+ private final WebClient client;
+ private final WebClientOptions options;
+
+ public VertxHttpClient(WebClient client, WebClientOptions options) {
+ Objects.requireNonNull(client, "client cannot be null");
+ Objects.requireNonNull(client, "options cannot be null");
+ this.client = client;
+ this.options = options;
+ }
+
+ @Override
+ public Mono<HttpResponse> send(HttpRequest request) {
+ return send(request, Context.NONE);
+ }
+
+ @Override
+ public Mono<HttpResponse> send(HttpRequest request, Context context) {
+ boolean eagerlyReadResponse = (boolean) context.getData("azure-eagerly-read-response").orElse(false);
+ return Mono.create(sink -> sink.onRequest(value -> {
+ toVertxHttpRequest(request).subscribe(vertxHttpRequest -> {
+ vertxHttpRequest.send(new VertxHttpResponseHandler(request, sink, eagerlyReadResponse));
+ }, sink::error);
+ }));
+ }
+
+ public void close() {
+ this.client.close();
+ }
+
+ // Exposed for testing
+ public WebClientOptions getWebClientOptions() {
+ return options;
+ }
+
+ private Mono<VertxHttpRequest> toVertxHttpRequest(HttpRequest request) {
+ return Mono.from(convertBodyToBuffer(request))
+ .map(buffer -> {
+ HttpMethod httpMethod = request.getHttpMethod();
+ io.vertx.core.http.HttpMethod requestMethod = io.vertx.core.http.HttpMethod.valueOf(httpMethod.name());
+
+ URL url = request.getUrl();
+ if (url.getPath().isEmpty()) {
+ try {
+ // Azure API documentation states:
+ //
+ // The URI must always include the forward slash (/) to separate the host name
+ // from the path and query portions of the URI.
+ //
+ url = new URL(url.getProtocol(), url.getHost(), url.getPort(), "/" + url.getFile());
+ } catch (MalformedURLException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ io.vertx.ext.web.client.HttpRequest<Buffer> delegate = client
+ .requestAbs(requestMethod, url.toString());
+
+ if (request.getHeaders() != null) {
+ request.getHeaders()
+ .stream()
+ .forEach(httpHeader -> delegate.putHeader(httpHeader.getName(),
+ httpHeader.getValuesList()));
+ }
+
+ return new VertxHttpRequest(delegate, buffer);
+ });
+ }
+
+ private Mono<Buffer> convertBodyToBuffer(HttpRequest request) {
+ return Mono.using(() -> Buffer.buffer(),
+ buffer -> getBody(request).reduce(buffer, (b, byteBuffer) -> {
+ for (int i = 0; i < byteBuffer.limit(); i++) {
+ b.appendByte(byteBuffer.get(i));
+ }
+ return b;
+ }), buffer -> buffer.getClass());
+ }
+
+ private Flux<ByteBuffer> getBody(HttpRequest request) {
+ long contentLength = 0;
+ String contentLengthHeader = request.getHeaders().getValue("content-length");
+ if (contentLengthHeader != null) {
+ contentLength = Long.parseLong(contentLengthHeader);
+ }
+
+ Flux<ByteBuffer> body = request.getBody();
+ if (body == null || contentLength <= 0) {
+ body = Flux.just(Buffer.buffer().getByteBuf().nioBuffer());
+ }
+
+ return body;
+ }
+}
diff --git a/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientBuilder.java b/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientBuilder.java
new file mode 100644
index 0000000..0cb5a14
--- /dev/null
+++ b/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientBuilder.java
@@ -0,0 +1,250 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.quarkus.support.azure.core.http.vertx;
+
+import java.net.InetSocketAddress;
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import com.azure.core.http.HttpClient;
+import com.azure.core.http.ProxyOptions;
+import com.azure.core.util.Configuration;
+import com.azure.core.util.logging.ClientLogger;
+import io.vertx.core.Vertx;
+import io.vertx.core.net.ProxyType;
+import io.vertx.ext.web.client.WebClient;
+import io.vertx.ext.web.client.WebClientOptions;
+
+import static com.azure.core.util.Configuration.PROPERTY_AZURE_REQUEST_CONNECT_TIMEOUT;
+import static com.azure.core.util.Configuration.PROPERTY_AZURE_REQUEST_READ_TIMEOUT;
+import static com.azure.core.util.Configuration.PROPERTY_AZURE_REQUEST_WRITE_TIMEOUT;
+import static com.azure.core.util.CoreUtils.getDefaultTimeoutFromEnvironment;
+
+/**
+ * Builds a {@link VertxHttpClient}.
+ */
+public class VertxHttpClientBuilder {
+
+ private static final long DEFAULT_CONNECT_TIMEOUT;
+ private static final long DEFAULT_WRITE_TIMEOUT;
+ private static final long DEFAULT_READ_TIMEOUT;
+
+ static {
+ ClientLogger logger = new ClientLogger(VertxHttpClientBuilder.class);
+ Configuration configuration = Configuration.getGlobalConfiguration();
+ DEFAULT_CONNECT_TIMEOUT = getDefaultTimeoutFromEnvironment(configuration,
+ PROPERTY_AZURE_REQUEST_CONNECT_TIMEOUT, Duration.ofSeconds(10), logger).toMillis();
+ DEFAULT_WRITE_TIMEOUT = getDefaultTimeoutFromEnvironment(configuration, PROPERTY_AZURE_REQUEST_WRITE_TIMEOUT,
+ Duration.ofSeconds(60), logger).toSeconds();
+ DEFAULT_READ_TIMEOUT = getDefaultTimeoutFromEnvironment(configuration, PROPERTY_AZURE_REQUEST_READ_TIMEOUT,
+ Duration.ofSeconds(60), logger).toSeconds();
+ }
+
+ private Duration readIdleTimeout;
+ private Duration writeIdleTimeout;
+ private Duration connectTimeout;
+ private Duration idleTimeout = Duration.ofSeconds(60);
+ private ProxyOptions proxyOptions;
+ private Configuration configuration;
+ private WebClientOptions webClientOptions;
+ private final Vertx vertx;
+
+ /**
+ * Creates VertxAsyncHttpClientBuilder.
+ *
+ * @param vertx The {@link Vertx} instance to pass to the {@link WebClient}.
+ */
+ public VertxHttpClientBuilder(Vertx vertx) {
+ Objects.requireNonNull(vertx, "vertx cannot be null");
+ this.vertx = vertx;
+ }
+
+ /**
+ * Sets the read idle timeout.
+ *
+ * The default read idle timeout is 60 seconds.
+ *
+ * @param readIdleTimeout the read idle timeout
+ * @return the updated VertxAsyncHttpClientBuilder object
+ */
+ public VertxHttpClientBuilder readIdleTimeout(Duration readIdleTimeout) {
+ this.readIdleTimeout = readIdleTimeout;
+ return this;
+ }
+
+ /**
+ * Sets the write idle timeout.
+ *
+ * The default read idle timeout is 60 seconds.
+ *
+ * @param writeIdleTimeout the write idle timeout
+ * @return the updated VertxAsyncHttpClientBuilder object
+ */
+ public VertxHttpClientBuilder writeIdleTimeout(Duration writeIdleTimeout) {
+ this.writeIdleTimeout = writeIdleTimeout;
+ return this;
+ }
+
+ /**
+ * Sets the connect timeout.
+ *
+ * The default connect timeout is 10 seconds.
+ *
+ * @param connectTimeout the connection timeout
+ * @return the updated VertxAsyncHttpClientBuilder object
+ */
+ public VertxHttpClientBuilder connectTimeout(Duration connectTimeout) {
+ this.connectTimeout = connectTimeout;
+ return this;
+ }
+
+ /**
+ * Sets the connection idle timeout.
+ *
+ * The default connect timeout is 60 seconds.
+ *
+ * @param idleTimeout the connection idle timeout
+ * @return the updated VertxAsyncHttpClientBuilder object
+ */
+ public VertxHttpClientBuilder idleTimeout(Duration idleTimeout) {
+ this.idleTimeout = idleTimeout;
+ return this;
+ }
+
+ /**
+ * Sets proxy configuration.
+ *
+ * @param proxyOptions The proxy configuration to use.
+ * @return The updated VertxAsyncHttpClientBuilder object.
+ */
+ public VertxHttpClientBuilder proxy(ProxyOptions proxyOptions) {
+ this.proxyOptions = proxyOptions;
+ return this;
+ }
+
+ /**
+ * Sets the configuration store that is used during construction of the HTTP client.
+ * <p>
+ * The default configuration store is a clone of the {@link Configuration#getGlobalConfiguration() global
+ * configuration store}, use {@link Configuration#NONE} to bypass using configuration settings during construction.
+ *
+ * @param configuration The configuration store.
+ * @return The updated VertxAsyncHttpClientBuilder object.
+ */
+ public VertxHttpClientBuilder configuration(Configuration configuration) {
+ this.configuration = configuration;
+ return this;
+ }
+
+ /**
+ * Sets custom {@link WebClientOptions} for the constructed {@link WebClient}.
+ *
+ * @param webClientOptions The options of the web client.
+ * @return The updated VertxAsyncHttpClientBuilder object
+ */
+ public VertxHttpClientBuilder webClientOptions(WebClientOptions webClientOptions) {
+ this.webClientOptions = webClientOptions;
+ return this;
+ }
+
+ /**
+ * Creates a new Vert.x {@link com.azure.core.http.HttpClient} instance on every call, using the
+ * configuration set in the builder at the time of the build method call.
+ *
+ * @return A new Vert.x backed {@link com.azure.core.http.HttpClient} instance.
+ */
+ public HttpClient build() {
+ if (this.webClientOptions == null) {
+ this.webClientOptions = new WebClientOptions();
+ }
+
+ if (this.connectTimeout != null) {
+ this.webClientOptions.setConnectTimeout((int) this.connectTimeout.toMillis());
+ } else {
+ this.webClientOptions.setConnectTimeout((int) DEFAULT_CONNECT_TIMEOUT);
+ }
+
+ if (this.readIdleTimeout != null) {
+ this.webClientOptions.setReadIdleTimeout((int) this.readIdleTimeout.toSeconds());
+ } else {
+ this.webClientOptions.setReadIdleTimeout((int) DEFAULT_READ_TIMEOUT);
+ }
+
+ if (this.writeIdleTimeout != null) {
+ this.webClientOptions.setWriteIdleTimeout((int) this.writeIdleTimeout.toSeconds());
+ } else {
+ this.webClientOptions.setWriteIdleTimeout((int) DEFAULT_WRITE_TIMEOUT);
+ }
+
+ this.webClientOptions.setIdleTimeout((int) this.idleTimeout.toSeconds());
+
+ Configuration buildConfiguration = (configuration == null)
+ ? Configuration.getGlobalConfiguration()
+ : configuration;
+
+ ProxyOptions buildProxyOptions = (this.proxyOptions == null && buildConfiguration != Configuration.NONE)
+ ? ProxyOptions.fromConfiguration(buildConfiguration, true)
+ : this.proxyOptions;
+
+ if (buildProxyOptions != null) {
+ io.vertx.core.net.ProxyOptions vertxProxyOptions = new io.vertx.core.net.ProxyOptions();
+ InetSocketAddress proxyAddress = buildProxyOptions.getAddress();
+
+ if (proxyAddress != null) {
+ vertxProxyOptions.setHost(proxyAddress.getHostName());
+ vertxProxyOptions.setPort(proxyAddress.getPort());
+ }
+
+ String proxyUsername = buildProxyOptions.getUsername();
+ String proxyPassword = buildProxyOptions.getPassword();
+ if (proxyUsername != null && proxyPassword != null) {
+ vertxProxyOptions.setUsername(proxyUsername);
+ vertxProxyOptions.setPassword(proxyPassword);
+ }
+
+ ProxyOptions.Type type = buildProxyOptions.getType();
+ if (type != null) {
+ try {
+ ProxyType proxyType = ProxyType.valueOf(type.name());
+ vertxProxyOptions.setType(proxyType);
+ } catch (IllegalArgumentException e) {
+ throw new IllegalStateException("Unknown Vert.x proxy type: " + type.name(), e);
+ }
+ }
+
+ String nonProxyHostsString = proxyOptions.getNonProxyHosts();
+ if (nonProxyHostsString != null) {
+ // Undo Azure ProxyOptions string sanitization since Vert.x has its own logic
+ List<String> nonProxyHosts = Arrays.asList(nonProxyHostsString.split("\\|"))
+ .stream()
+ .map(host -> host.replaceAll("\\\\E", "")
+ .replaceAll("\\\\Q", "")
+ .replaceAll("\\.\\.", ""))
+ .collect(Collectors.toList());
+ webClientOptions.setNonProxyHosts(nonProxyHosts);
+ }
+
+ webClientOptions.setProxyOptions(vertxProxyOptions);
+ }
+
+ WebClient client = WebClient.create(this.vertx, this.webClientOptions);
+ return new VertxHttpClient(client, this.webClientOptions);
+ }
+}
diff --git a/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientProvider.java b/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientProvider.java
new file mode 100644
index 0000000..73120d3
--- /dev/null
+++ b/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpClientProvider.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.quarkus.support.azure.core.http.vertx;
+
+import java.util.Set;
+
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.enterprise.inject.spi.CDI;
+
+import com.azure.core.http.HttpClient;
+import com.azure.core.http.HttpClientProvider;
+import com.azure.core.util.HttpClientOptions;
+import io.vertx.core.Vertx;
+import io.vertx.ext.web.client.WebClient;
+
+/**
+ * {@link HttpClientProvider} backed by the Vert.x {@link WebClient}
+ */
+public class VertxHttpClientProvider implements HttpClientProvider {
+
+ @Override
+ public HttpClient createInstance() {
+ return createInstance(null);
+ }
+
+ @Override
+ public HttpClient createInstance(HttpClientOptions clientOptions) {
+ VertxHttpClientBuilder builder = new VertxHttpClientBuilder(getVertx());
+ if (clientOptions != null) {
+ builder = builder.proxy(clientOptions.getProxyOptions())
+ .configuration(clientOptions.getConfiguration())
+ .connectTimeout(clientOptions.getConnectTimeout())
+ .idleTimeout(clientOptions.getConnectionIdleTimeout())
+ .writeIdleTimeout(clientOptions.getWriteTimeout())
+ .readIdleTimeout(clientOptions.getReadTimeout());
+ }
+ return builder.build();
+ }
+
+ /**
+ * Obtains a reference to the Quarkus managed {@link Vertx} instance
+ *
+ * @return The Quarkus managed {@link Vertx} instance
+ */
+ private static final Vertx getVertx() {
+ BeanManager beanManager = CDI.current().getBeanManager();
+ Set<Bean<?>> beans = beanManager.getBeans(Vertx.class);
+ if (beans.isEmpty()) {
+ throw new IllegalStateException("Failed to discover Vert.x bean from the CDI bean manager");
+ }
+
+ if (beans.size() > 1) {
+ throw new IllegalStateException(
+ "Expected 1 Vert.x bean in the CDI bean manager but " + beans.size() + " were found");
+ }
+
+ Bean<?> bean = beanManager.resolve(beans);
+ Object reference = beanManager.getReference(bean, Vertx.class, beanManager.createCreationalContext(bean));
+ return Vertx.class.cast(reference);
+ }
+}
diff --git a/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpRequest.java b/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpRequest.java
new file mode 100644
index 0000000..e6f9e60
--- /dev/null
+++ b/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpRequest.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.quarkus.support.azure.core.http.vertx;
+
+import io.vertx.core.buffer.Buffer;
+import io.vertx.ext.web.client.HttpRequest;
+
+/**
+ * Holds a Vert.x {@link HttpRequest} together with a body payload.
+ */
+class VertxHttpRequest {
+ private final Buffer body;
+ private final HttpRequest<Buffer> delegate;
+
+ public VertxHttpRequest(HttpRequest<Buffer> delegate, Buffer body) {
+ this.delegate = delegate;
+ this.body = body;
+ }
+
+ public void send(VertxHttpResponseHandler handler) {
+ delegate.sendBuffer(body, handler);
+ }
+}
diff --git a/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpResponse.java b/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpResponse.java
new file mode 100644
index 0000000..7d71964
--- /dev/null
+++ b/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpResponse.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.quarkus.support.azure.core.http.vertx;
+
+import java.nio.charset.Charset;
+
+import com.azure.core.http.HttpHeaders;
+import com.azure.core.http.HttpRequest;
+import com.azure.core.http.HttpResponse;
+import com.azure.core.util.CoreUtils;
+import io.vertx.core.MultiMap;
+import reactor.core.publisher.Mono;
+
+abstract class VertxHttpResponse extends HttpResponse {
+
+ private final io.vertx.ext.web.client.HttpResponse response;
+ private final HttpHeaders headers;
+
+ VertxHttpResponse(HttpRequest request, io.vertx.ext.web.client.HttpResponse response) {
+ super(request);
+ this.response = response;
+ this.headers = fromVertxHttpHeaders(response.headers());
+ }
+
+ private HttpHeaders fromVertxHttpHeaders(MultiMap headers) {
+ HttpHeaders azureHeaders = new HttpHeaders();
+ headers.names().forEach(name -> azureHeaders.set(name, headers.getAll(name)));
+ return azureHeaders;
+ }
+
+ protected io.vertx.ext.web.client.HttpResponse getVertxHttpResponse() {
+ return this.response;
+ }
+
+ @Override
+ public int getStatusCode() {
+ return response.statusCode();
+ }
+
+ @Override
+ public String getHeaderValue(String name) {
+ return this.headers.getValue(name);
+ }
+
+ @Override
+ public HttpHeaders getHeaders() {
+ return this.headers;
+ }
+
+ @Override
+ public final Mono<String> getBodyAsString() {
+ return getBodyAsByteArray().map(bytes -> CoreUtils.bomAwareToString(bytes, getHeaderValue("Content-Type")));
+ }
+
+ @Override
+ public final Mono<String> getBodyAsString(Charset charset) {
+ return Mono.fromCallable(() -> this.response.bodyAsString(charset.toString()));
+ }
+}
diff --git a/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpResponseHandler.java b/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpResponseHandler.java
new file mode 100644
index 0000000..9e33e0b
--- /dev/null
+++ b/extensions-support/azure-core-http-client-vertx/runtime/src/main/java/org/apache/camel/quarkus/support/azure/core/http/vertx/VertxHttpResponseHandler.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.quarkus.support.azure.core.http.vertx;
+
+import com.azure.core.http.HttpRequest;
+import io.vertx.core.AsyncResult;
+import io.vertx.core.Handler;
+import io.vertx.core.buffer.Buffer;
+import io.vertx.ext.web.client.HttpResponse;
+import reactor.core.publisher.MonoSink;
+
+/**
+ * {@link Handler} for Azure HTTP responses.
+ */
+class VertxHttpResponseHandler implements Handler<AsyncResult<HttpResponse<Buffer>>> {
+
+ private final HttpRequest request;
+ private final MonoSink<com.azure.core.http.HttpResponse> sink;
+ private final boolean eagerlyReadResponse;
+
+ VertxHttpResponseHandler(HttpRequest request, MonoSink<com.azure.core.http.HttpResponse> sink,
+ boolean eagerlyReadResponse) {
+ this.request = request;
+ this.sink = sink;
+ this.eagerlyReadResponse = eagerlyReadResponse;
+ }
+
+ @Override
+ public void handle(AsyncResult<HttpResponse<Buffer>> event) {
+ if (event.succeeded()) {
+ VertxHttpResponse response;
+ if (eagerlyReadResponse) {
+ io.vertx.ext.web.client.HttpResponse<Buffer> originalResponse = event.result();
+ response = new BufferedVertxHttpResponse(request, originalResponse, originalResponse.body());
+ } else {
+ response = new VertxHttpAsyncResponse(request, event.result());
+ }
+ sink.success(response);
+ } else {
+ if (event.cause() != null) {
+ sink.error(event.cause());
+ }
+ }
+ }
+}
diff --git a/extensions-support/azure-core-http-client-vertx/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions-support/azure-core-http-client-vertx/runtime/src/main/resources/META-INF/quarkus-extension.yaml
new file mode 100644
index 0000000..5f875c0
--- /dev/null
+++ b/extensions-support/azure-core-http-client-vertx/runtime/src/main/resources/META-INF/quarkus-extension.yaml
@@ -0,0 +1,27 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+
+---
+name: "Camel Quarkus Support Azure Core HTTP Client Vert.x"
+description: "Camel Quarkus Support Azure Core HTTP Client Vert.x"
+metadata:
+ unlisted: true
+ keywords:
+ - "camel"
+ guide: "https://quarkus.io/guides/camel"
+ categories:
+ - "integration"
\ No newline at end of file
diff --git a/extensions-support/azure-core-http-client-vertx/runtime/src/main/resources/META-INF/services/com.azure.core.http.HttpClientProvider b/extensions-support/azure-core-http-client-vertx/runtime/src/main/resources/META-INF/services/com.azure.core.http.HttpClientProvider
new file mode 100644
index 0000000..8487b59
--- /dev/null
+++ b/extensions-support/azure-core-http-client-vertx/runtime/src/main/resources/META-INF/services/com.azure.core.http.HttpClientProvider
@@ -0,0 +1 @@
+org.apache.camel.quarkus.support.azure.core.http.vertx.VertxHttpClientProvider
\ No newline at end of file
diff --git a/extensions-support/azure-core/deployment/pom.xml b/extensions-support/azure-core/deployment/pom.xml
index 91a5018..b762e0a 100644
--- a/extensions-support/azure-core/deployment/pom.xml
+++ b/extensions-support/azure-core/deployment/pom.xml
@@ -35,12 +35,12 @@
<artifactId>quarkus-core-deployment</artifactId>
</dependency>
<dependency>
- <groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-support-jackson-dataformat-xml-deployment</artifactId>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-netty-deployment</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-support-reactor-netty-deployment</artifactId>
+ <artifactId>camel-quarkus-support-jackson-dataformat-xml-deployment</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
diff --git a/extensions-support/azure-core/runtime/pom.xml b/extensions-support/azure-core/runtime/pom.xml
index 701cd25..0319e50 100644
--- a/extensions-support/azure-core/runtime/pom.xml
+++ b/extensions-support/azure-core/runtime/pom.xml
@@ -52,16 +52,22 @@
<artifactId>quarkus-core</artifactId>
</dependency>
<dependency>
- <groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-support-jackson-dataformat-xml</artifactId>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-netty</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-support-reactor-netty</artifactId>
+ <artifactId>camel-quarkus-support-jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-core</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>com.azure</groupId>
+ <artifactId>azure-core-http-netty</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
</dependencies>
diff --git a/extensions-support/pom.xml b/extensions-support/pom.xml
index 4a8cad3..5e6b35e 100644
--- a/extensions-support/pom.xml
+++ b/extensions-support/pom.xml
@@ -38,6 +38,7 @@
<module>aws</module>
<module>aws2</module>
<module>azure-core</module>
+ <module>azure-core-http-client-vertx</module>
<module>bouncycastle</module>
<module>commons-logging</module>
<module>consul-client</module>
diff --git a/extensions/azure-eventhubs/runtime/pom.xml b/extensions/azure-eventhubs/runtime/pom.xml
index 3c79b20..5be7c8b 100644
--- a/extensions/azure-eventhubs/runtime/pom.xml
+++ b/extensions/azure-eventhubs/runtime/pom.xml
@@ -59,6 +59,12 @@
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-azure-eventhubs</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>com.azure</groupId>
+ <artifactId>azure-core-http-netty</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
</dependencies>
diff --git a/extensions/azure-storage-blob/deployment/pom.xml b/extensions/azure-storage-blob/deployment/pom.xml
index 49ff729..e341501 100644
--- a/extensions/azure-storage-blob/deployment/pom.xml
+++ b/extensions/azure-storage-blob/deployment/pom.xml
@@ -36,7 +36,7 @@
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-support-azure-core-deployment</artifactId>
+ <artifactId>camel-quarkus-support-azure-core-http-client-vertx-deployment</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
diff --git a/extensions/azure-storage-blob/runtime/pom.xml b/extensions/azure-storage-blob/runtime/pom.xml
index d432e32..3e59347 100644
--- a/extensions/azure-storage-blob/runtime/pom.xml
+++ b/extensions/azure-storage-blob/runtime/pom.xml
@@ -54,11 +54,17 @@
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-support-azure-core</artifactId>
+ <artifactId>camel-quarkus-support-azure-core-http-client-vertx</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-azure-storage-blob</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>com.azure</groupId>
+ <artifactId>azure-core-http-netty</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
</dependencies>
diff --git a/extensions/azure-storage-queue/deployment/pom.xml b/extensions/azure-storage-queue/deployment/pom.xml
index 6819a3e..e6b3473 100644
--- a/extensions/azure-storage-queue/deployment/pom.xml
+++ b/extensions/azure-storage-queue/deployment/pom.xml
@@ -36,7 +36,7 @@
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-support-azure-core-deployment</artifactId>
+ <artifactId>camel-quarkus-support-azure-core-http-client-vertx-deployment</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
diff --git a/extensions/azure-storage-queue/runtime/pom.xml b/extensions/azure-storage-queue/runtime/pom.xml
index ecb18ff..4afc269 100644
--- a/extensions/azure-storage-queue/runtime/pom.xml
+++ b/extensions/azure-storage-queue/runtime/pom.xml
@@ -54,11 +54,17 @@
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-support-azure-core</artifactId>
+ <artifactId>camel-quarkus-support-azure-core-http-client-vertx</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-azure-storage-queue</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>com.azure</groupId>
+ <artifactId>azure-core-http-netty</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
</dependencies>
diff --git a/integration-test-groups/azure/azure-storage-blob/src/test/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobTest.java b/integration-test-groups/azure/azure-storage-blob/src/test/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobTest.java
index 6594824..7a3e8c1 100644
--- a/integration-test-groups/azure/azure-storage-blob/src/test/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobTest.java
+++ b/integration-test-groups/azure/azure-storage-blob/src/test/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobTest.java
@@ -27,13 +27,7 @@ import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
-import com.azure.core.http.policy.HttpLogDetailLevel;
-import com.azure.core.http.policy.HttpLogOptions;
-import com.azure.storage.blob.BlobContainerClient;
-import com.azure.storage.blob.BlobServiceClient;
-import com.azure.storage.blob.BlobServiceClientBuilder;
import com.azure.storage.blob.models.BlockListType;
-import com.azure.storage.common.StorageSharedKeyCredential;
import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.RestAssured;
@@ -65,31 +59,26 @@ class AzureStorageBlobTest {
@BeforeAll
static void beforeAll() {
- getClient().create();
+ final Config config = ConfigProvider.getConfig();
+ String containerName = config.getValue("azure.blob.container.name", String.class);
+ int port = config.getValue("quarkus.http.test-port", int.class);
+ RestAssured.port = port;
+ RestAssured.given()
+ .queryParam("containerName", containerName)
+ .post("/azure-storage-blob/blob/container")
+ .then()
+ .statusCode(201);
}
@AfterAll
static void afterAll() {
- getClient().delete();
- }
-
- private static BlobContainerClient getClient() {
final Config config = ConfigProvider.getConfig();
- final String azureStorageAccountName = config.getValue("azure.storage.account-name",
- String.class);
- final String azureStorageAccountKey = config
- .getValue("azure.storage.account-key", String.class);
-
- StorageSharedKeyCredential credentials = new StorageSharedKeyCredential(azureStorageAccountName,
- azureStorageAccountKey);
- BlobServiceClient client = new BlobServiceClientBuilder()
- .endpoint(config.getValue("azure.blob.service.url", String.class))
- .credential(credentials)
- .httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS).setPrettyPrintBody(true))
- .buildClient();
-
String containerName = config.getValue("azure.blob.container.name", String.class);
- return client.getBlobContainerClient(containerName);
+ RestAssured.given()
+ .queryParam("containerName", containerName)
+ .delete("/azure-storage-blob/blob/container")
+ .then()
+ .statusCode(204);
}
@Test
diff --git a/pom.xml b/pom.xml
index 5cbdeea..5c3b1f8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -69,6 +69,8 @@
<awssdk.version>2.17.127</awssdk.version><!-- @sync io.quarkiverse.amazonservices:quarkus-amazon-services-parent:${quarkiverse-amazonservices.version} prop:awssdk.version -->
<aws-java-sdk.version>1.11.714</aws-java-sdk.version>
<azure-sdk-bom.version>1.0.5</azure-sdk-bom.version><!-- Keep in sync with camel-azure component versions -->
+ <azure-core.version>1.21.0</azure-core.version><!-- @sync com.azure:azure-sdk-bom:${azure-sdk-bom.version} dep:com.azure:azure-core -->
+ <azure-core-test.version>1.7.3</azure-core-test.version>
<bouncycastle.version>1.70</bouncycastle.version><!-- @sync io.quarkus:quarkus-bom:${quarkus.version} dep:org.bouncycastle:bcprov-jdk15on -->
<commons-beanutils.version>${commons-beanutils-version}</commons-beanutils.version>
<commons-cli.version>1.4</commons-cli.version><!-- keep in sync with Quarkus, via quarkus-bootstrap-core -->
diff --git a/poms/bom-test/pom.xml b/poms/bom-test/pom.xml
index fced647..53b1e1c 100644
--- a/poms/bom-test/pom.xml
+++ b/poms/bom-test/pom.xml
@@ -301,6 +301,26 @@
<artifactId>aws-java-sdk-core</artifactId>
<version>${aws-java-sdk.version}</version>
</dependency>
+ <dependency>
+ <groupId>com.azure</groupId>
+ <artifactId>azure-core</artifactId>
+ <version>${azure-core.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.azure</groupId>
+ <artifactId>azure-core-test</artifactId>
+ <version>${azure-core-test.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.azure</groupId>
+ <artifactId>azure-core-test</artifactId>
+ <version>${azure-core-test.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</dependencyManagement>
diff --git a/poms/bom/pom.xml b/poms/bom/pom.xml
index 2218c60..564e4a2 100644
--- a/poms/bom/pom.xml
+++ b/poms/bom/pom.xml
@@ -5556,6 +5556,16 @@
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-support-azure-core-http-client-vertx</artifactId>
+ <version>${camel-quarkus.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-support-azure-core-http-client-vertx-deployment</artifactId>
+ <version>${camel-quarkus.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-support-bouncycastle</artifactId>
<version>${camel-quarkus.version}</version>
</dependency>