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..5a4526289954
--- /dev/null
+++ b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITPostQuantumCryptography.java
@@ -0,0 +1,266 @@
+/*
+ * 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.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.Provider;
+import java.security.Security;
+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.SSLParameters;
+import javax.net.ssl.TrustManagerFactory;
+import org.conscrypt.Conscrypt;
+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:
+ *
+ *
+ * - {@code x-showcase-tls-group}: The negotiated key exchange named group (e.g.
+ * X25519MLKEM768).
+ *
- {@code x-showcase-tls-version}: The TLS version negotiated (e.g. TLS 1.3).
+ *
- {@code x-showcase-tls-cipher}: The negotiated cipher suite (e.g. TLS_AES_128_GCM_SHA256).
+ *
- {@code x-showcase-tls-client-supported-groups}: The list of groups offered by the client.
+ *
+ *
+ * Verification cases:
+ *
+ *
+ * - {@code testHttpJsonPqc}: Verifies that HTTP/JSON transport defaults to Conscrypt and
+ * negotiates the hybrid post-quantum group {@code X25519MLKEM768}.
+ *
- {@code testHttpJsonPqc_withExplicitSecurityProvider}: Verifies that overriding the
+ * transport's SSLSocketFactory to explicitly use standard JDK JSSE provider (SunJSSE) falls
+ * back gracefully to classical key exchange ({@code X25519}) instead of crashing.
+ *
+ */
+public class ITPostQuantumCryptography {
+
+ // TLS response header names from Showcase server
+ private static final String TLS_GROUP_HEADER = "x-showcase-tls-group";
+ private static final String TLS_SUPPORTED_GROUPS_HEADER =
+ "x-showcase-tls-client-supported-groups";
+
+ // Expected TLS parameters
+ private static final String EXPECTED_PQC_GROUP = "X25519MLKEM768";
+
+ private static final String DEFAULT_CA_CERT_PATH = getCaCertPath();
+
+ /**
+ * Resolves the absolute path to the Showcase server's CA certificate PEM file.
+ *
+ * @return absolute path to the CA certificate file
+ */
+ private static String getCaCertPath() {
+ String prop = System.getProperty("showcase.ca.cert.path");
+ if (prop != null) {
+ return prop;
+ }
+ if (new File("/tmp/showcase-ca.pem").isFile()) {
+ return "/tmp/showcase-ca.pem";
+ }
+ return "target/showcase-ca.pem";
+ }
+
+ private static final String SECURE_ENDPOINT =
+ System.getProperty("showcase.secure.endpoint", "localhost:7470");
+
+ @BeforeAll
+ static void setUp() {
+ File certFile = new File(DEFAULT_CA_CERT_PATH);
+ assertWithMessage("CA certificate file not found at " + DEFAULT_CA_CERT_PATH)
+ .that(certFile.isFile())
+ .isTrue();
+ }
+
+ @Test
+ void testHttpJsonPqc() throws Exception {
+
+ NetHttpTransport transport =
+ new NetHttpTransport.Builder()
+ .setSecurityProvider(Conscrypt.newProvider())
+ .setSslSocketConfigurator(
+ socket -> {
+ if (Conscrypt.isConscrypt(socket)) {
+ Conscrypt.setNamedGroups(
+ socket, InstantiatingHttpJsonChannelProvider.DEFAULT_PQC_GROUPS);
+ }
+ })
+ .trustCertificates(null, loadCaCert(DEFAULT_CA_CERT_PATH), "")
+ .build();
+
+ HttpJsonCapturingClientInterceptor interceptor = new HttpJsonCapturingClientInterceptor();
+
+ InstantiatingHttpJsonChannelProvider transportChannelProvider =
+ EchoSettings.defaultHttpJsonTransportProviderBuilder()
+ .setHttpTransport(transport)
+ .setEndpoint("https://" + SECURE_ENDPOINT)
+ .setInterceptorProvider(() -> Collections.singletonList(interceptor))
+ .build();
+
+ EchoSettings settings =
+ EchoSettings.newHttpJsonBuilder()
+ .setCredentialsProvider(NoCredentialsProvider.create())
+ .setTransportChannelProvider(transportChannelProvider)
+ .build();
+
+ try (EchoClient client = EchoClient.create(settings)) {
+ EchoResponse response =
+ client.echo(EchoRequest.newBuilder().setContent("pqc-httpjson-test").build());
+ assertThat(response.getContent()).isEqualTo("pqc-httpjson-test");
+
+ HttpJsonMetadata capturedHeaders = interceptor.metadata;
+ assertThat(capturedHeaders).isNotNull();
+
+ String negotiatedGroup = getSingleHeaderString(capturedHeaders, TLS_GROUP_HEADER);
+ assertThat(negotiatedGroup).isEqualTo(EXPECTED_PQC_GROUP);
+
+ String supportedGroups = getSingleHeaderString(capturedHeaders, TLS_SUPPORTED_GROUPS_HEADER);
+ assertThat(supportedGroups).isNotNull();
+ }
+ }
+
+ @Test
+ void testHttpJsonPqc_withExplicitSecurityProviderNoPqcGroups() throws Exception {
+ // Explicitly use SunJSSE (JDK default) instead of Conscrypt
+ Provider sunJsseProvider = Security.getProvider("SunJSSE");
+ assertThat(sunJsseProvider).isNotNull();
+
+ // Initialize SSLContext and TrustManagerFactory explicitly with SunJSSE provider to trust the
+ // CA
+ SSLContext sslContext = SSLContext.getInstance("TLS", sunJsseProvider);
+ TrustManagerFactory tmf =
+ TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm(), sunJsseProvider);
+ tmf.init(loadCaCert(DEFAULT_CA_CERT_PATH));
+ sslContext.init(null, tmf.getTrustManagers(), null);
+
+ // This test verifies client transport behavior when PQC algorithms are not offered.
+ // Future Java versions (e.g. JDK 27+) will enable PQC (ML-KEM) by default in standard JDK JSSE.
+ // Explicitly setting named groups to classical algorithms ensures that this test reliably
+ // tests the non-PQC classical TLS connection path regardless of underlying JDK defaults.
+ NetHttpTransport transport =
+ new NetHttpTransport.Builder()
+ .setSslSocketFactory(sslContext.getSocketFactory())
+ .setSslSocketConfigurator(
+ socket -> {
+ try {
+ SSLParameters params = socket.getSSLParameters();
+ params.setNamedGroups(new String[] {"X25519", "SecP256r1"});
+ socket.setSSLParameters(params);
+ } catch (Exception e) {
+ // For JDK 8-19, SSLParameters.setNamedGroups() is unsupported, and JSSE
+ // naturally
+ // defaults to classical algorithms. Setting classical named groups is primarily
+ // for
+ // JDK 20+ when PQC becomes the default in standard JSSE.
+ }
+ })
+ .build();
+
+ HttpJsonCapturingClientInterceptor interceptor = new HttpJsonCapturingClientInterceptor();
+
+ InstantiatingHttpJsonChannelProvider transportChannelProvider =
+ EchoSettings.defaultHttpJsonTransportProviderBuilder()
+ .setHttpTransport(transport)
+ .setEndpoint("https://" + SECURE_ENDPOINT)
+ .setInterceptorProvider(() -> Collections.singletonList(interceptor))
+ .build();
+
+ EchoSettings settings =
+ EchoSettings.newHttpJsonBuilder()
+ .setCredentialsProvider(NoCredentialsProvider.create())
+ .setTransportChannelProvider(transportChannelProvider)
+ .build();
+
+ try (EchoClient client = EchoClient.create(settings)) {
+ EchoResponse response =
+ client.echo(
+ EchoRequest.newBuilder().setContent("pqc-httpjson-explicit-provider-test").build());
+ assertThat(response.getContent()).isEqualTo("pqc-httpjson-explicit-provider-test");
+
+ HttpJsonMetadata capturedHeaders = interceptor.metadata;
+ assertThat(capturedHeaders).isNotNull();
+
+ String negotiatedGroup = getSingleHeaderString(capturedHeaders, TLS_GROUP_HEADER);
+ // Under classical non-PQC configuration, negotiated group is a classical curve
+ assertThat(negotiatedGroup).isAnyOf("X25519", "SecP256r1", "CurveP256");
+ assertThat(negotiatedGroup).isNotEqualTo(EXPECTED_PQC_GROUP);
+ }
+ }
+
+ /**
+ * Extracts the first string value of a specified HTTP response header from metadata.
+ *
+ * @param metadata the HTTP metadata containing response headers
+ * @param name the case-insensitive header key name
+ * @return header value string, or {@code null} if not found
+ */
+ private static String getSingleHeaderString(HttpJsonMetadata metadata, String name) {
+ Object valueObj = metadata.getHeaders().get(name);
+ if (valueObj instanceof List) {
+ List> list = (List>) valueObj;
+ if (!list.isEmpty()) {
+ return String.valueOf(list.get(0));
+ }
+ } else if (valueObj != null) {
+ return String.valueOf(valueObj);
+ }
+ return null;
+ }
+
+ /**
+ * Loads an X.509 CA certificate file from disk into a new KeyStore instance.
+ *
+ * @param certPath path to the X.509 certificate file
+ * @return initialized KeyStore containing the certificate entry
+ * @throws Exception if reading or parsing the certificate fails
+ */
+ private static KeyStore loadCaCert(String certPath) throws Exception {
+ KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ trustStore.load(null, null);
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ try (InputStream is = Files.newInputStream(Paths.get(certPath))) {
+ Certificate cert = cf.generateCertificate(is);
+ trustStore.setCertificateEntry("showcase-ca", cert);
+ }
+ return trustStore;
+ }
+}
diff --git a/sdk-platform-java/gax-java/gax-httpjson/pom.xml b/sdk-platform-java/gax-java/gax-httpjson/pom.xml
index de839fe6e074..d70dbfe4459b 100644
--- a/sdk-platform-java/gax-java/gax-httpjson/pom.xml
+++ b/sdk-platform-java/gax-java/gax-httpjson/pom.xml
@@ -104,6 +104,11 @@
error_prone_annotations
${errorprone.version}
+
+ org.conscrypt
+ conscrypt-openjdk-uber
+ ${conscrypt.version}
+
diff --git a/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonTransportUtils.java b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonTransportUtils.java
new file mode 100644
index 000000000000..ed46d9ad581c
--- /dev/null
+++ b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonTransportUtils.java
@@ -0,0 +1,123 @@
+/*
+ * 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 com.google.api.client.http.javanet.NetHttpTransport;
+import com.google.api.core.InternalApi;
+import java.security.Provider;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.conscrypt.Conscrypt;
+
+/**
+ * Utility class for creating and configuring {@link NetHttpTransport} instances with Post-Quantum
+ * Cryptography (PQC) Conscrypt support.
+ */
+@InternalApi
+public class HttpJsonTransportUtils {
+
+ private static final Logger LOG = Logger.getLogger(HttpJsonTransportUtils.class.getName());
+
+ /**
+ * Default TLS 1.3 Post-Quantum Cryptography (PQC) named groups configured when Conscrypt security
+ * provider is present:
+ *
+ *
+ * - {@code X25519MLKEM768}: Primary preferred hybrid key exchange algorithm combining
+ * Curve25519 ECDHE with NIST FIPS 203 (ML-KEM-768 / Kyber768).
+ *
- {@code SecP256r1MLKEM768}: Secondary preferred hybrid key exchange algorithm combining
+ * NIST P-256 (SecP256r1) with NIST FIPS 203 (ML-KEM-768) for environments requiring
+ * FIPS-compliant elliptic curves.
+ *
- {@code X25519}: Classical non-quantum key exchange fallback to ensure compatibility with
+ * standard TLS 1.3 endpoints if ML-KEM is not negotiated.
+ *
+ */
+ public static final String[] DEFAULT_PQC_GROUPS =
+ new String[] {"X25519MLKEM768", "SecP256r1MLKEM768", "X25519"};
+
+ /**
+ * Lazy initialization holder for Conscrypt {@link Provider}.
+ *
+ * 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;
+ }
+ }
+ }
+
+ /**
+ * Creates a {@link NetHttpTransport.Builder} pre-configured 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.
+ */
+ public static NetHttpTransport.Builder createConscryptHttpTransportBuilder() {
+ NetHttpTransport.Builder builder = new NetHttpTransport.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 (Throwable t) {
+ // Catch runtime socket configuration errors (e.g. version mismatch or unexpected
+ // socket implementation) to gracefully fall back to Conscrypt's default TLS groups
+ // without failing transport creation.
+ LOG.log(
+ Level.WARNING,
+ "Failed to set PQC named groups on Conscrypt socket. Falling back to"
+ + " Conscrypt default TLS groups.",
+ t);
+ }
+ });
+ }
+
+ private HttpJsonTransportUtils() {}
+}
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 495fec1ed450..b31904d64a6a 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
@@ -191,16 +191,19 @@ public TransportChannelProvider withCredentials(Credentials credentials) {
}
HttpTransport createHttpTransport() throws IOException, GeneralSecurityException {
- if (mtlsProvider == null) {
- return null;
- }
- if (certificateBasedAccess.useMtlsClientCertificate()) {
+ NetHttpTransport.Builder builder = HttpJsonTransportUtils.createConscryptHttpTransportBuilder();
+ return configureMtls(builder).build();
+ }
+
+ private NetHttpTransport.Builder configureMtls(NetHttpTransport.Builder builder)
+ throws IOException, GeneralSecurityException {
+ if (mtlsProvider != null && certificateBasedAccess.useMtlsClientCertificate()) {
KeyStore mtlsKeyStore = mtlsProvider.getKeyStore();
if (mtlsKeyStore != null) {
- return new NetHttpTransport.Builder().trustCertificates(null, mtlsKeyStore, "").build();
+ builder.trustCertificates(null, mtlsKeyStore, "");
}
}
- return null;
+ return builder;
}
private HttpJsonTransportChannel createChannel() throws IOException, GeneralSecurityException {
@@ -364,7 +367,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/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 17ad9f2cbf2b..255628759ad7 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
@@ -32,6 +32,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
+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;
@@ -192,6 +193,27 @@ protected Object getMtlsObjectFromTransportChannel(
.setHeaderProvider(Mockito.mock(HeaderProvider.class))
.setExecutor(Mockito.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(HttpJsonTransportUtils.DEFAULT_PQC_GROUPS)
+ .asList()
+ .containsExactly("X25519MLKEM768", "SecP256r1MLKEM768", "X25519")
+ .inOrder();
}
}
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 @@
error_prone_annotations
+
+ org.conscrypt
+ conscrypt-openjdk-uber
+
+
org.junit.platform
diff --git a/sdk-platform-java/java-core/google-cloud-core-http/src/main/java/com/google/cloud/http/HttpTransportOptions.java b/sdk-platform-java/java-core/google-cloud-core-http/src/main/java/com/google/cloud/http/HttpTransportOptions.java
index f5ad54532f66..31222d114494 100644
--- a/sdk-platform-java/java-core/google-cloud-core-http/src/main/java/com/google/cloud/http/HttpTransportOptions.java
+++ b/sdk-platform-java/java-core/google-cloud-core-http/src/main/java/com/google/cloud/http/HttpTransportOptions.java
@@ -22,10 +22,10 @@
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpTransport;
-import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.gax.core.GaxProperties;
import com.google.api.gax.httpjson.HttpHeadersUtils;
import com.google.api.gax.httpjson.HttpJsonStatusCode;
+import com.google.api.gax.httpjson.HttpJsonTransportUtils;
import com.google.api.gax.rpc.ApiClientHeaderProvider;
import com.google.api.gax.rpc.EndpointContext;
import com.google.api.gax.rpc.HeaderProvider;
@@ -41,6 +41,7 @@
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Objects;
+import java.util.logging.Logger;
/** Class representing service options for those services that use HTTP as the transport layer. */
public class HttpTransportOptions implements TransportOptions {
@@ -54,6 +55,7 @@ public class HttpTransportOptions implements TransportOptions {
public static class DefaultHttpTransportFactory implements HttpTransportFactory {
+ private static final Logger LOG = Logger.getLogger(DefaultHttpTransportFactory.class.getName());
private static final HttpTransportFactory INSTANCE = new DefaultHttpTransportFactory();
@Override
@@ -66,7 +68,8 @@ public HttpTransport create() {
// Maybe not on App Engine
}
}
- return new NetHttpTransport();
+
+ return HttpJsonTransportUtils.createConscryptHttpTransportBuilder().build();
}
}