diff --git a/docs/component-modules.md b/docs/component-modules.md
index 7277febc0..ad780053b 100644
--- a/docs/component-modules.md
+++ b/docs/component-modules.md
@@ -55,4 +55,13 @@ XML extensions to the Google API Client Library for Java
(`google-api-client-xml`). This module depends on `google-api-client` and
`google-http-client-xml`.
+## google-api-client-apache-v5
+
+Provides Apache extension to the Google HTTP Client Library for Java (`google-api-client-apache-v5`) that
+returns an implementation of `HttpTransport` based on the Apache HTTP Client (v5) with Google certificates loaded in
+truststore. This module depends on `google-http-client`.
+
+Please note this is the preferred Apache extension to be used, over the GoogleApacheHttpTransport included
+in `google-api-client` module. The previous google transport utilizes the EOL Apache HTTP Client v4.
+
[protobuf]: https://developers.google.com/protocol-buffers/docs/overview
diff --git a/google-api-client-apache-v5/pom.xml b/google-api-client-apache-v5/pom.xml
new file mode 100644
index 000000000..8c5350d79
--- /dev/null
+++ b/google-api-client-apache-v5/pom.xml
@@ -0,0 +1,177 @@
+
+
+ 4.0.0
+
+ com.google.api-client
+ google-api-client-parent
+ 2.6.1-SNAPSHOT
+
+ google-api-client-apache-v5
+ Apache extensions to the Google APIs Client Library for Java
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+
+
+
+ resources
+
+
+
+
+
+ maven-javadoc-plugin
+
+
+ https://docs.oracle.com/javase/8/docs/api/
+ https://cloud.google.com/appengine/docs/standard/java/javadoc/
+ https://googleapis.dev/java/google-http-client/${project.http.version}/
+ https://googleapis.dev/java/google-oauth-client/${project.oauth.version}/
+
+ ${project.name} ${project.version}
+ ${project.artifactId} ${project.version}
+
+
+
+ maven-jar-plugin
+
+
+ ${project.build.outputDirectory}/META-INF/MANIFEST.MF
+
+ true
+
+
+ google.api.client
+
+ ${project.build.outputDirectory}/META-INF/MANIFEST.MF
+
+
+
+
+ org.apache.felix
+ maven-bundle-plugin
+ 5.1.9
+
+
+ bundle-manifest
+ process-classes
+
+ manifest
+
+
+
+
+
+ https://developers.google.com/api-client-library/java/
+ Google HTTP transport wrapper for the Apache 5 Http Client.
+
+ com.google.api.client.googleapis.apache
+
+
+
+
+ maven-source-plugin
+
+
+ source-jar
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+
+
+ validate-google-style
+ validate
+
+ check
+
+
+ google_checks.xml
+ src/checkstyle/checkstyle-suppressions.xml
+ true
+ true
+ warning
+
+
+
+
+
+
+
+
+ src/main/resources
+
+
+ src/main/properties
+ true
+
+
+
+
+
+ junit
+ junit
+ test
+
+
+ com.google.api-client
+ google-api-client
+
+
+ org.apache.httpcomponents
+ httpcore
+
+
+ org.apache.httpcomponents
+ httpclient
+
+
+
+
+ com.google.http-client
+ google-http-client-apache-v5
+
+
+ com.google.guava
+ guava
+
+
+ org.apache.httpcomponents.client5
+ httpclient5
+
+
+ org.apache.httpcomponents.core5
+ httpcore5
+
+
+ com.google.http-client
+ google-http-client
+
+
+ org.apache.httpcomponents
+ httpcore
+
+
+ org.apache.httpcomponents
+ httpclient
+
+
+
+
+ com.google.api-client
+ google-api-client
+ test-jar
+ test
+ ${project.version}
+
+
+
diff --git a/google-api-client-apache-v5/src/checkstyle/checkstyle-suppressions.xml b/google-api-client-apache-v5/src/checkstyle/checkstyle-suppressions.xml
new file mode 100644
index 000000000..812319b66
--- /dev/null
+++ b/google-api-client-apache-v5/src/checkstyle/checkstyle-suppressions.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
diff --git a/google-api-client-apache-v5/src/main/java/com/google/api/client/googleapis/apache/v5/GoogleApache5HttpTransport.java b/google-api-client-apache-v5/src/main/java/com/google/api/client/googleapis/apache/v5/GoogleApache5HttpTransport.java
new file mode 100644
index 000000000..00a7673b3
--- /dev/null
+++ b/google-api-client-apache-v5/src/main/java/com/google/api/client/googleapis/apache/v5/GoogleApache5HttpTransport.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2024 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
+ *
+ * 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 com.google.api.client.googleapis.apache.v5;
+
+import com.google.api.client.googleapis.GoogleUtils;
+import com.google.api.client.googleapis.mtls.MtlsProvider;
+import com.google.api.client.googleapis.mtls.MtlsUtils;
+import com.google.api.client.googleapis.util.Utils;
+import com.google.api.client.http.apache.v5.Apache5HttpTransport;
+import com.google.api.client.util.SslUtils;
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.VisibleForTesting;
+import java.io.IOException;
+import java.net.ProxySelector;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLContext;
+import org.apache.hc.client5.http.config.ConnectionConfig;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
+import org.apache.hc.client5.http.impl.routing.SystemDefaultRoutePlanner;
+import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
+import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory;
+import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory;
+import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
+import org.apache.hc.core5.http.config.Registry;
+import org.apache.hc.core5.http.config.RegistryBuilder;
+
+/**
+ * Utilities for Google APIs based on {@link Apache5HttpTransport}.
+ *
+ * @since 2.6.1
+ */
+public final class GoogleApache5HttpTransport {
+
+ /**
+ * Returns a new instance of {@link Apache5HttpTransport} that uses {@link
+ * GoogleUtils#getCertificateTrustStore()} for the trusted certificates. If
+ * `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true", and the default
+ * client certificate key store from {@link Utils#loadDefaultMtlsKeyStore()} is not null, then the
+ * transport uses the default client certificate and is mutual TLS.
+ */
+ public static Apache5HttpTransport newTrustedTransport()
+ throws GeneralSecurityException, IOException {
+ return newTrustedTransport(MtlsUtils.getDefaultMtlsProvider());
+ }
+
+ /**
+ * {@link Beta}
+ * Returns a new instance of {@link Apache5HttpTransport} that uses {@link
+ * GoogleUtils#getCertificateTrustStore()} for the trusted certificates. mtlsProvider can be used
+ * to configure mutual TLS for the transport.
+ *
+ * @param mtlsProvider MtlsProvider to configure mutual TLS for the transport
+ */
+ @Beta
+ public static Apache5HttpTransport newTrustedTransport(MtlsProvider mtlsProvider)
+ throws GeneralSecurityException, IOException {
+
+ SocketFactoryRegistryHandler handler = new SocketFactoryRegistryHandler(mtlsProvider);
+
+ PoolingHttpClientConnectionManager connectionManager =
+ new PoolingHttpClientConnectionManager(handler.getSocketFactoryRegistry());
+ connectionManager.setMaxTotal(200);
+ connectionManager.setDefaultMaxPerRoute(20);
+ connectionManager.setDefaultConnectionConfig(
+ ConnectionConfig.custom()
+ .setTimeToLive(-1, TimeUnit.MILLISECONDS)
+ .setValidateAfterInactivity(-1L, TimeUnit.MILLISECONDS)
+ .build());
+
+ CloseableHttpClient client =
+ HttpClients.custom()
+ .useSystemProperties()
+ .setConnectionManager(connectionManager)
+ .setRoutePlanner(new SystemDefaultRoutePlanner(ProxySelector.getDefault()))
+ .disableRedirectHandling()
+ .disableAutomaticRetries()
+ .build();
+
+ return new Apache5HttpTransport(client, handler.isMtls());
+ }
+
+ @VisibleForTesting
+ static class SocketFactoryRegistryHandler {
+ private final Registry socketFactoryRegistry;
+ private final boolean isMtls;
+
+ public SocketFactoryRegistryHandler(MtlsProvider mtlsProvider)
+ throws GeneralSecurityException, IOException {
+ KeyStore mtlsKeyStore = null;
+ String mtlsKeyStorePassword = null;
+ if (mtlsProvider.useMtlsClientCertificate()) {
+ mtlsKeyStore = mtlsProvider.getKeyStore();
+ mtlsKeyStorePassword = mtlsProvider.getKeyStorePassword();
+ }
+
+ // Use the included trust store
+ KeyStore trustStore = GoogleUtils.getCertificateTrustStore();
+ SSLContext sslContext = SslUtils.getTlsSslContext();
+
+ if (mtlsKeyStore != null && mtlsKeyStorePassword != null) {
+ this.isMtls = true;
+ SslUtils.initSslContext(
+ sslContext,
+ trustStore,
+ SslUtils.getPkixTrustManagerFactory(),
+ mtlsKeyStore,
+ mtlsKeyStorePassword,
+ SslUtils.getDefaultKeyManagerFactory());
+ } else {
+ this.isMtls = false;
+ SslUtils.initSslContext(sslContext, trustStore, SslUtils.getPkixTrustManagerFactory());
+ }
+ LayeredConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext);
+
+ this.socketFactoryRegistry =
+ RegistryBuilder.create()
+ .register("http", PlainConnectionSocketFactory.getSocketFactory())
+ .register("https", socketFactory)
+ .build();
+ }
+
+ public Registry getSocketFactoryRegistry() {
+ return this.socketFactoryRegistry;
+ }
+
+ public boolean isMtls() {
+ return this.isMtls;
+ }
+ }
+
+ private GoogleApache5HttpTransport() {}
+}
diff --git a/google-api-client-apache-v5/src/main/java/com/google/api/client/googleapis/apache/v5/package-info.java b/google-api-client-apache-v5/src/main/java/com/google/api/client/googleapis/apache/v5/package-info.java
new file mode 100644
index 000000000..f4a1e9131
--- /dev/null
+++ b/google-api-client-apache-v5/src/main/java/com/google/api/client/googleapis/apache/v5/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2024 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
+ *
+ * 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.
+ */
+
+/**
+ * Google APIs support based on the Apache HTTP Client v5.
+ *
+ * @since 2.6.1
+ */
+package com.google.api.client.googleapis.apache.v5;
diff --git a/google-api-client-apache-v5/src/test/java/com/google/api/client/googleapis/apache/v5/GoogleApache5HttpTransportTest.java b/google-api-client-apache-v5/src/test/java/com/google/api/client/googleapis/apache/v5/GoogleApache5HttpTransportTest.java
new file mode 100644
index 000000000..07728325a
--- /dev/null
+++ b/google-api-client-apache-v5/src/test/java/com/google/api/client/googleapis/apache/v5/GoogleApache5HttpTransportTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2024 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
+ *
+ * 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 com.google.api.client.googleapis.apache.v5;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.api.client.googleapis.mtls.MtlsProvider;
+import com.google.api.client.googleapis.mtls.MtlsTransportBaseTest;
+import com.google.api.client.http.HttpTransport;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import org.junit.Test;
+
+public class GoogleApache5HttpTransportTest extends MtlsTransportBaseTest {
+ @Override
+ protected HttpTransport buildTrustedTransport(MtlsProvider mtlsProvider)
+ throws GeneralSecurityException, IOException {
+ return GoogleApache5HttpTransport.newTrustedTransport(mtlsProvider);
+ }
+
+ @Test
+ public void socketFactoryRegistryHandlerTest() throws GeneralSecurityException, IOException {
+ MtlsProvider mtlsProvider = new TestMtlsProvider(true, createTestMtlsKeyStore(), "", false);
+ GoogleApache5HttpTransport.SocketFactoryRegistryHandler handler =
+ new GoogleApache5HttpTransport.SocketFactoryRegistryHandler(mtlsProvider);
+ assertNotNull(handler.getSocketFactoryRegistry().lookup("http"));
+ assertNotNull(handler.getSocketFactoryRegistry().lookup("https"));
+ assertTrue(handler.isMtls());
+ }
+}
diff --git a/google-api-client-apache-v5/src/test/java/com/google/api/client/googleapis/apache/v5/ITGoogleApache5HttpTransportTest.java b/google-api-client-apache-v5/src/test/java/com/google/api/client/googleapis/apache/v5/ITGoogleApache5HttpTransportTest.java
new file mode 100644
index 000000000..f8e9cbed1
--- /dev/null
+++ b/google-api-client-apache-v5/src/test/java/com/google/api/client/googleapis/apache/v5/ITGoogleApache5HttpTransportTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2024 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
+ *
+ * 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 com.google.api.client.googleapis.apache.v5;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import com.google.api.client.http.apache.v5.Apache5HttpTransport;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import javax.net.ssl.SSLHandshakeException;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.io.HttpClientResponseHandler;
+import org.junit.Test;
+
+public class ITGoogleApache5HttpTransportTest {
+
+ @Test
+ public void testHttpRequestFailsWhenMakingRequestToSiteWithoutGoogleCerts()
+ throws GeneralSecurityException, IOException {
+ Apache5HttpTransport apache5HttpTransport = GoogleApache5HttpTransport.newTrustedTransport();
+ HttpGet httpGet = new HttpGet("https://maven.com/");
+ Exception exception = null;
+ try {
+ apache5HttpTransport
+ .getHttpClient()
+ .execute(
+ httpGet,
+ new HttpClientResponseHandler() {
+ @Override
+ public Void handleResponse(ClassicHttpResponse response) {
+ fail("Should not have been able to complete SSL request on non google site.");
+ return null;
+ }
+ });
+ fail("Expected SSLHandshakeException was not thrown");
+ } catch (SSLHandshakeException e) {
+ exception = e;
+ }
+
+ assertNotNull(exception);
+ assertEquals(exception.getClass(), SSLHandshakeException.class);
+ }
+
+ @Test
+ public void testHttpRequestPassesWhenMakingRequestToGoogleSite() throws Exception {
+ Apache5HttpTransport apache5HttpTransport = GoogleApache5HttpTransport.newTrustedTransport();
+ HttpGet httpGet = new HttpGet("https://www.google.com/");
+
+ apache5HttpTransport
+ .getHttpClient()
+ .execute(
+ httpGet,
+ new HttpClientResponseHandler() {
+ @Override
+ public Void handleResponse(ClassicHttpResponse response) {
+ assertEquals(200, response.getCode());
+ return null;
+ }
+ });
+ }
+}
diff --git a/google-api-client/pom.xml b/google-api-client/pom.xml
index 8d73dbad4..d42452c6d 100644
--- a/google-api-client/pom.xml
+++ b/google-api-client/pom.xml
@@ -87,6 +87,18 @@
jar
+
+ Jar Tests Package
+ package
+
+ test-jar
+
+
+
+ **/Mtls/**
+
+
+
diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java
index cb0813b45..abec44b7a 100644
--- a/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java
+++ b/google-api-client/src/main/java/com/google/api/client/googleapis/mtls/MtlsProvider.java
@@ -22,7 +22,8 @@
/**
* {@link Beta}
* Provider interface for mutual TLS. It is used in {@link
- * GoogleApacheHttpTransport#newTrustedTransport(MtlsProvider)} and {@link
+ * GoogleApacheHttpTransport#newTrustedTransport(MtlsProvider)}, {@link
+ * GoogleApache5HttpTransport#newTrustedTransport(MtlsProvider)} and {@link
* GoogleNetHttpTransport#newTrustedTransport(MtlsProvider)} to configure the mutual TLS in the
* transport.
*
diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsTransportBaseTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsTransportBaseTest.java
index 096ff4abf..87b44fdf4 100644
--- a/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsTransportBaseTest.java
+++ b/google-api-client/src/test/java/com/google/api/client/googleapis/mtls/MtlsTransportBaseTest.java
@@ -35,11 +35,11 @@ protected KeyStore createTestMtlsKeyStore() throws IOException, GeneralSecurityE
return SecurityUtils.createMtlsKeyStore(certAndKey);
}
- protected static class TestMtlsProvider implements MtlsProvider {
- private boolean useClientCertificate;
- private KeyStore keyStore;
- private String keyStorePassword;
- private boolean throwExceptionForGetKeyStore;
+ public static class TestMtlsProvider implements MtlsProvider {
+ private final boolean useClientCertificate;
+ private final KeyStore keyStore;
+ private final String keyStorePassword;
+ private final boolean throwExceptionForGetKeyStore;
public TestMtlsProvider(
boolean useClientCertificate,
diff --git a/pom.xml b/pom.xml
index 1d332f0f5..5fa6a62d4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -80,6 +80,7 @@
google-api-client-jackson2
google-api-client-protobuf
google-api-client-xml
+ google-api-client-apache-v5
@@ -241,6 +242,21 @@
${gson.version}
test
+
+ org.apache.httpcomponents.client5
+ httpclient5
+ ${project.httpclient5.version}
+
+
+ org.apache.httpcomponents.core5
+ httpcore5
+ ${project.httpcore5.version}
+
+
+ com.google.http-client
+ google-http-client-apache-v5
+ ${project.http.version}
+
@@ -513,9 +529,11 @@
UTF-8
- 1.44.2
+ 1.45.0
4.4.16
4.5.14
+ 5.2.4
+ 5.3.1
1.17.0
1.36.0
1.23.0