diff --git a/.github/workflows/showcase.yaml b/.github/workflows/showcase.yaml index 3325dfadcc3a..6c1f135d0703 100644 --- a/.github/workflows/showcase.yaml +++ b/.github/workflows/showcase.yaml @@ -61,7 +61,22 @@ jobs: curl --location https://github.com/googleapis/gapic-showcase/releases/download/v${SHOWCASE_VERSION}/gapic-showcase-${SHOWCASE_VERSION}-linux-amd64.tar.gz --output /usr/src/showcase/showcase-${SHOWCASE_VERSION}-linux-amd64.tar.gz cd /usr/src/showcase/ tar -xf showcase-* + # Start standard insecure showcase server on default port 7469 for standard integration tests ./gapic-showcase run & + # Start secure TLS showcase server on port 7470 for PQC TLS integration tests + ./gapic-showcase run --port 7470 --tls --ca-cert-output-file /tmp/showcase-ca.pem & + # Wait deterministically for both background showcase servers to finish binding ports 7469/7470 + # and writing /tmp/showcase-ca.pem. Starting TLS requires RSA key generation and disk I/O, + # which can cause race conditions if tests start before /tmp/showcase-ca.pem is created. + for i in $(seq 1 30); do + if (echo > /dev/tcp/127.0.0.1/7469) 2>/dev/null && \ + (echo > /dev/tcp/127.0.0.1/7470) 2>/dev/null && \ + [ -f /tmp/showcase-ca.pem ]; then + echo "Showcase servers (ports 7469, 7470) and CA cert ready in attempt $i." + break + fi + sleep 0.2 + done cd - - name: Showcase integration tests working-directory: java-showcase @@ -166,7 +181,22 @@ jobs: curl --location https://github.com/googleapis/gapic-showcase/releases/download/v${SHOWCASE_VERSION}/gapic-showcase-${SHOWCASE_VERSION}-linux-amd64.tar.gz --output /usr/src/showcase/showcase-${SHOWCASE_VERSION}-linux-amd64.tar.gz cd /usr/src/showcase/ tar -xf showcase-* + # Start standard insecure showcase server on default port 7469 for standard integration tests ./gapic-showcase run & + # Start secure TLS showcase server on port 7470 for PQC TLS integration tests + ./gapic-showcase run --port 7470 --tls --ca-cert-output-file /tmp/showcase-ca.pem & + # Wait deterministically for both background showcase servers to finish binding ports 7469/7470 + # and writing /tmp/showcase-ca.pem. Starting TLS requires RSA key generation and disk I/O, + # which can cause race conditions if tests start before /tmp/showcase-ca.pem is created. + for i in $(seq 1 30); do + if (echo > /dev/tcp/127.0.0.1/7469) 2>/dev/null && \ + (echo > /dev/tcp/127.0.0.1/7470) 2>/dev/null && \ + [ -f /tmp/showcase-ca.pem ]; then + echo "Showcase servers (ports 7469, 7470) and CA cert ready in attempt $i." + break + fi + sleep 0.2 + done cd - - name: Showcase integration tests working-directory: java-showcase diff --git a/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITPostQuantumCryptography.java b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITPostQuantumCryptography.java new file mode 100644 index 000000000000..7229a31d1a51 --- /dev/null +++ b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITPostQuantumCryptography.java @@ -0,0 +1,258 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed 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 + * + * https://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 com.google.showcase.v1beta1.it; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.gax.core.NoCredentialsProvider; +import com.google.api.gax.httpjson.HttpJsonConscryptUtils; +import com.google.api.gax.httpjson.HttpJsonMetadata; +import com.google.api.gax.httpjson.InstantiatingHttpJsonChannelProvider; +import com.google.showcase.v1beta1.EchoClient; +import com.google.showcase.v1beta1.EchoRequest; +import com.google.showcase.v1beta1.EchoResponse; +import com.google.showcase.v1beta1.EchoSettings; +import com.google.showcase.v1beta1.it.util.HttpJsonCapturingClientInterceptor; +import java.io.File; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.util.Collections; +import java.util.List; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; +import org.conscrypt.Conscrypt; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +/** + * Integration tests to verify Post-Quantum Cryptography (PQC) TLS negotiation for HTTP/JSON (REST) + * clients. + * + *
These tests execute calls against a local secure (TLS-enabled) Showcase server. During the TLS + * handshake, the client and server negotiate cipher suites and key exchange groups. Showcase + * injects information about the negotiated TLS connection parameters into custom headers: + * + *
Verification cases: + * + *
Caches the Conscrypt {@link Provider} instance (or {@code null} if initialization fails) to + * avoid repeated expensive JNI initialization operations on every transport creation. + * + *
Returns {@code null} on failure so that transport creation can fall back to default JDK TLS,
+ * ensuring that setting Conscrypt as the default security provider does not cause breaking
+ * failures for customers running on environments where Conscrypt is unsupported or unavailable.
+ */
+ private static class ConscryptProviderHolder {
+ private static final Provider INSTANCE = createProvider();
+
+ private static Provider createProvider() {
+ try {
+ return Conscrypt.newProvider();
+ } catch (Throwable t) {
+ LOG.log(
+ Level.WARNING, "Conscrypt native libraries not available. Falling back to JDK TLS.", t);
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Configures the given {@link NetHttpTransport.Builder} with Conscrypt as the security provider
+ * by default if Conscrypt is available. Users can customize the {@link NetHttpTransport.Builder}
+ * to use a different security provider.
+ *
+ * @param builder the {@link NetHttpTransport.Builder} to configure
+ * @return the configured {@link NetHttpTransport.Builder}
+ */
+ public static NetHttpTransport.Builder configureConscryptSecurityProvider(
+ NetHttpTransport.Builder builder) {
+ Provider conscryptProvider = ConscryptProviderHolder.INSTANCE;
+ if (conscryptProvider == null) {
+ return builder;
+ }
+ return builder
+ .setSecurityProvider(conscryptProvider)
+ .setSslSocketConfigurator(
+ socket -> {
+ if (!Conscrypt.isConscrypt(socket)) {
+ return;
+ }
+ try {
+ Conscrypt.setNamedGroups(socket, DEFAULT_PQC_GROUPS);
+ } catch (Exception e) {
+ // Native JNI linkage errors (e.g. UnsatisfiedLinkError) are caught during
+ // ConscryptProviderHolder initialization. Catching Exception here safely
+ // intercepts runtime socket configuration errors (e.g. unsupported groups or
+ // closed socket) without swallowing JVM errors like OutOfMemoryError.
+ LOG.log(
+ Level.WARNING,
+ "Failed to set PQC named groups on Conscrypt socket. Falling back to Conscrypt"
+ + " default TLS groups.",
+ e);
+ }
+ });
+ }
+
+ /**
+ * Returns the Conscrypt {@link Provider} instance, or {@code null} if Conscrypt is unavailable.
+ *
+ * @return the Conscrypt provider or null
+ */
+ static Provider getConscryptProvider() {
+ return ConscryptProviderHolder.INSTANCE;
+ }
+
+ private HttpJsonConscryptUtils() {}
+}
diff --git a/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProvider.java b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProvider.java
index 4cffc99cea59..14f23fbca8dc 100644
--- a/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProvider.java
+++ b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProvider.java
@@ -31,6 +31,7 @@
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
+import com.google.api.client.util.SslUtils;
import com.google.api.core.InternalExtensionOnly;
import com.google.api.gax.core.ExecutorProvider;
import com.google.api.gax.rpc.FixedHeaderProvider;
@@ -45,11 +46,13 @@
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
+import java.security.Provider;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Level;
import java.util.logging.Logger;
+import javax.net.ssl.SSLContext;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@@ -192,17 +195,42 @@ public TransportChannelProvider withCredentials(Credentials credentials) {
"InstantiatingHttpJsonChannelProvider doesn't need credentials");
}
- @Nullable HttpTransport createHttpTransport() throws IOException, GeneralSecurityException {
- if (mtlsProvider == null) {
- return null;
+ HttpTransport createHttpTransport() throws IOException, GeneralSecurityException {
+ NetHttpTransport.Builder builder = new NetHttpTransport.Builder();
+ configureMtls(builder);
+ HttpJsonConscryptUtils.configureConscryptSecurityProvider(builder);
+ return builder.build();
+ }
+
+ private NetHttpTransport.Builder configureMtls(NetHttpTransport.Builder builder)
+ throws IOException, GeneralSecurityException {
+ if (mtlsProvider == null || !certificateBasedAccess.useMtlsClientCertificate()) {
+ return builder;
}
- if (certificateBasedAccess.useMtlsClientCertificate()) {
- KeyStore mtlsKeyStore = mtlsProvider.getKeyStore();
- if (mtlsKeyStore != null) {
- return new NetHttpTransport.Builder().trustCertificates(null, mtlsKeyStore, "").build();
- }
+ KeyStore mtlsKeyStore = mtlsProvider.getKeyStore();
+ if (mtlsKeyStore == null) {
+ return builder;
+ }
+ Provider conscryptProvider = HttpJsonConscryptUtils.getConscryptProvider();
+ if (conscryptProvider == null) {
+ // Fall back to standard JDK JSSE if Conscrypt provider is unavailable
+ builder.trustCertificates(null, mtlsKeyStore, "");
+ return builder;
}
- return null;
+ // Explicitly initialize SSLContext with the Conscrypt provider so that the client certificate
+ // key managers
+ // and trust manager factory (TMF) are bound to Conscrypt's TLS implementation (supporting PQC
+ // key exchange).
+ SSLContext sslContext = SSLContext.getInstance("TLS", conscryptProvider);
+ SslUtils.initSslContext(
+ sslContext,
+ null,
+ SslUtils.getPkixTrustManagerFactory(),
+ mtlsKeyStore,
+ "",
+ SslUtils.getDefaultKeyManagerFactory());
+ builder.setSslSocketFactory(sslContext.getSocketFactory());
+ return builder;
}
private HttpJsonTransportChannel createChannel() throws IOException, GeneralSecurityException {
@@ -366,7 +394,8 @@ public InstantiatingHttpJsonChannelProvider build() {
"DefaultMtlsProviderFactory encountered unexpected IOException: " + e.getMessage());
LOG.log(
Level.WARNING,
- "mTLS configuration was detected on the device, but mTLS failed to initialize. Falling back to non-mTLS channel.");
+ "mTLS configuration was detected on the device, but mTLS failed to initialize."
+ + " Falling back to non-mTLS channel.");
}
}
}
diff --git a/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ManagedHttpJsonChannel.java b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ManagedHttpJsonChannel.java
index 8e24eafcce97..0208b8fc561f 100644
--- a/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ManagedHttpJsonChannel.java
+++ b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ManagedHttpJsonChannel.java
@@ -63,6 +63,10 @@ String getEndpoint() {
return endpoint;
}
+ HttpTransport getHttpTransport() {
+ return httpTransport;
+ }
+
private ManagedHttpJsonChannel(
@Nullable Executor executor,
boolean usingDefaultExecutor,
@@ -71,7 +75,12 @@ private ManagedHttpJsonChannel(
this.executor = executor;
this.usingDefaultExecutor = usingDefaultExecutor;
this.endpoint = endpoint;
- this.httpTransport = httpTransport == null ? new NetHttpTransport() : httpTransport;
+ this.httpTransport =
+ httpTransport == null
+ ? HttpJsonConscryptUtils.configureConscryptSecurityProvider(
+ new NetHttpTransport.Builder())
+ .build()
+ : httpTransport;
this.deadlineScheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
}
@@ -227,11 +236,14 @@ public ManagedHttpJsonChannel build() {
usingDefaultExecutor = true;
}
- return new ManagedHttpJsonChannel(
- executor,
- usingDefaultExecutor,
- endpoint,
- httpTransport == null ? new NetHttpTransport() : httpTransport);
+ if (httpTransport == null) {
+ httpTransport =
+ HttpJsonConscryptUtils.configureConscryptSecurityProvider(
+ new NetHttpTransport.Builder())
+ .build();
+ }
+
+ return new ManagedHttpJsonChannel(executor, usingDefaultExecutor, endpoint, httpTransport);
}
}
}
diff --git a/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonConscryptUtilsTest.java b/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonConscryptUtilsTest.java
new file mode 100644
index 000000000000..b3b55c5622f0
--- /dev/null
+++ b/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonConscryptUtilsTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2026 Google LLC
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.google.api.gax.httpjson;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import java.security.Provider;
+import org.junit.jupiter.api.Test;
+
+class HttpJsonConscryptUtilsTest {
+
+ @Test
+ void testDefaultPqcGroups_containsExpectedGroups() {
+ assertThat(HttpJsonConscryptUtils.DEFAULT_PQC_GROUPS)
+ .asList()
+ .containsExactly("X25519MLKEM768", "SecP256r1MLKEM768", "X25519")
+ .inOrder();
+ }
+
+ @Test
+ void testGetConscryptProvider_doesNotThrow() {
+ Provider provider = HttpJsonConscryptUtils.getConscryptProvider();
+ if (provider != null) {
+ assertThat(provider.getName()).isEqualTo("Conscrypt");
+ }
+ }
+}
diff --git a/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProviderTest.java b/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProviderTest.java
index 0eeb2f27cd85..b8f112752c3f 100644
--- a/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProviderTest.java
+++ b/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/InstantiatingHttpJsonChannelProviderTest.java
@@ -33,6 +33,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
+import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.gax.rpc.HeaderProvider;
import com.google.api.gax.rpc.TransportChannelProvider;
import com.google.api.gax.rpc.mtls.AbstractMtlsTransportChannelTest;
@@ -194,6 +195,35 @@ protected Object getMtlsObjectFromTransportChannel(
mock(HeaderProvider.class, Mockito.withSettings().withoutAnnotations()))
.setExecutor(mock(Executor.class))
.build();
- return channelProvider.createHttpTransport();
+ NetHttpTransport transport = (NetHttpTransport) channelProvider.createHttpTransport();
+ return (transport != null && transport.isMtls()) ? transport : null;
+ }
+
+ @Test
+ void testCreateHttpTransport_returnsValidTransport() throws Exception {
+ InstantiatingHttpJsonChannelProvider channelProvider =
+ InstantiatingHttpJsonChannelProvider.newBuilder()
+ .setEndpoint("localhost:8080")
+ .setHeaderProvider(Mockito.mock(HeaderProvider.class))
+ .setExecutor(Mockito.mock(Executor.class))
+ .build();
+ NetHttpTransport transport = (NetHttpTransport) channelProvider.createHttpTransport();
+ assertThat(transport).isNotNull();
+ }
+
+ @Test
+ void testDefaultPqcGroups_containsExpectedGroups() {
+ assertThat(HttpJsonConscryptUtils.DEFAULT_PQC_GROUPS)
+ .asList()
+ .containsExactly("X25519MLKEM768", "SecP256r1MLKEM768", "X25519")
+ .inOrder();
+ }
+
+ @Test
+ void testConfigureConscryptSecurityProvider_returnsConfiguredBuilder() {
+ NetHttpTransport.Builder builder = new NetHttpTransport.Builder();
+ NetHttpTransport.Builder result =
+ HttpJsonConscryptUtils.configureConscryptSecurityProvider(builder);
+ assertThat(result).isSameInstanceAs(builder);
}
}
diff --git a/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ManagedHttpJsonChannelTest.java b/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ManagedHttpJsonChannelTest.java
new file mode 100644
index 000000000000..234a8afe34d5
--- /dev/null
+++ b/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ManagedHttpJsonChannelTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2026 Google LLC
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.google.api.gax.httpjson;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.api.client.http.HttpTransport;
+import com.google.api.client.http.javanet.NetHttpTransport;
+import org.junit.jupiter.api.Test;
+
+class ManagedHttpJsonChannelTest {
+
+ @Test
+ void testBuilder_defaultHttpTransport_createsNetHttpTransport() {
+ ManagedHttpJsonChannel channel =
+ ManagedHttpJsonChannel.newBuilder().setEndpoint("localhost:8080").build();
+ assertThat(channel.getHttpTransport()).isInstanceOf(NetHttpTransport.class);
+ }
+
+ @Test
+ void testBuilder_customHttpTransport_usesProvidedTransport() {
+ HttpTransport customTransport = new NetHttpTransport.Builder().build();
+ ManagedHttpJsonChannel channel =
+ ManagedHttpJsonChannel.newBuilder()
+ .setEndpoint("localhost:8080")
+ .setHttpTransport(customTransport)
+ .build();
+ assertThat(channel.getHttpTransport()).isSameInstanceAs(customTransport);
+ }
+}
diff --git a/sdk-platform-java/java-core/google-cloud-core-http/pom.xml b/sdk-platform-java/java-core/google-cloud-core-http/pom.xml
index 9bcac047678b..ee8beb09cbf0 100644
--- a/sdk-platform-java/java-core/google-cloud-core-http/pom.xml
+++ b/sdk-platform-java/java-core/google-cloud-core-http/pom.xml
@@ -75,6 +75,11 @@