Skip to content

Commit

Permalink
feat: Provide HttpTransport support for google certificates with apac…
Browse files Browse the repository at this point in the history
…he HTTP client v5 (#2497) (#2503)

provide new GoogleApache5HttpTransport that returns HttpTransport with google certificates using apache HTTP client v5
  • Loading branch information
ldetmer authored Aug 27, 2024
1 parent b3df1f6 commit 0d39b43
Show file tree
Hide file tree
Showing 11 changed files with 521 additions and 7 deletions.
9 changes: 9 additions & 0 deletions docs/component-modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
177 changes: 177 additions & 0 deletions google-api-client-apache-v5/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
<?xml version="1.0" encoding="UTF-8"?>
<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>com.google.api-client</groupId>
<artifactId>google-api-client-parent</artifactId>
<version>2.6.1-SNAPSHOT</version>
</parent>
<artifactId>google-api-client-apache-v5</artifactId>
<name>Apache extensions to the Google APIs Client Library for Java</name>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>resources</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<links>
<link>https://docs.oracle.com/javase/8/docs/api/</link>
<link>https://cloud.google.com/appengine/docs/standard/java/javadoc/</link>
<link>https://googleapis.dev/java/google-http-client/${project.http.version}/</link>
<link>https://googleapis.dev/java/google-oauth-client/${project.oauth.version}/</link>
</links>
<doctitle>${project.name} ${project.version}</doctitle>
<windowtitle>${project.artifactId} ${project.version}</windowtitle>
</configuration>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
<manifestEntries>
<Automatic-Module-Name>google.api.client</Automatic-Module-Name>
</manifestEntries>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>5.1.9</version>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-DocURL>https://developers.google.com/api-client-library/java/</Bundle-DocURL>
<Bundle-Description>Google HTTP transport wrapper for the Apache 5 Http Client.
</Bundle-Description>
<Bundle-SymbolicName>com.google.api.client.googleapis.apache</Bundle-SymbolicName>
</instructions>
</configuration>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>source-jar</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<executions>
<execution>
<id>validate-google-style</id>
<phase>validate</phase>
<goals>
<goal>check</goal>
</goals>
<configuration>
<configLocation>google_checks.xml</configLocation>
<suppressionsLocation>src/checkstyle/checkstyle-suppressions.xml</suppressionsLocation>
<includeTestSourceDirectory>true</includeTestSourceDirectory>
<failOnViolation>true</failOnViolation>
<violationSeverity>warning</violationSeverity>
</configuration>
</execution>
</executions>
</plugin>
</plugins>

<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/properties</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client-apache-v5</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.core5</groupId>
<artifactId>httpcore5</artifactId>
</dependency>
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client</artifactId>
<type>test-jar</type>
<scope>test</scope>
<version> ${project.version}</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0"?>
<!DOCTYPE suppressions PUBLIC
"-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN"
"https://checkstyle.org/dtds/suppressions_1_2.dtd">
<suppressions>
<!-- Test checkstyle suppressions -->
<suppress checks="MissingJavadocType" files=".*Test.java"/>
<suppress checks="AbbreviationAsWordInName" files=".*Test.java"/>
<suppress checks="VariableDeclarationUsageDistance" files=".*Test.java"/>
</suppressions>
Original file line number Diff line number Diff line change
@@ -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} <br>
* 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<ConnectionSocketFactory> 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.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", socketFactory)
.build();
}

public Registry<ConnectionSocketFactory> getSocketFactoryRegistry() {
return this.socketFactoryRegistry;
}

public boolean isMtls() {
return this.isMtls;
}
}

private GoogleApache5HttpTransport() {}
}
Original file line number Diff line number Diff line change
@@ -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;
Loading

0 comments on commit 0d39b43

Please sign in to comment.