Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@

import static com.google.cloud.bigquery.storage.v1.stub.BigQueryReadStubSettings.defaultGrpcTransportProviderBuilder;

import com.google.api.client.googleapis.GoogleUtils;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.apache.v5.Apache5HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.gax.rpc.TransportChannelProvider;
import com.google.auth.http.HttpTransportFactory;
import com.google.cloud.bigquery.exception.BigQueryJdbcRuntimeException;
Expand All @@ -35,11 +37,15 @@
import java.net.SocketAddress;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.impl.DefaultAuthenticationStrategy;
Expand Down Expand Up @@ -136,17 +142,27 @@ static HttpTransportOptions getHttpTransportOptions(
boolean hasProxyOrSsl =
proxyProperties.containsKey(BigQueryJdbcUrlUtility.PROXY_HOST_PROPERTY_NAME)
|| sslTrustStorePath != null;
boolean hasTimeoutConfig = connectTimeout != null || readTimeout != null;

if (!hasProxyOrSsl && !hasTimeoutConfig) {
return null;
}

HttpTransportOptions.Builder httpTransportOptionsBuilder = HttpTransportOptions.newBuilder();
if (hasProxyOrSsl) {
httpTransportOptionsBuilder.setHttpTransportFactory(
getHttpTransportFactory(
proxyProperties, sslTrustStorePath, sslTrustStorePassword, callerClassName));
} else {
// Default to NetHttpTransport configured with a MergedTrustManager that trusts
// both the JVM's default trust store and Google's bundled certificate store.
httpTransportOptionsBuilder.setHttpTransportFactory(
() -> {
try {
SSLContext sslContext = createMergedSslContext();
return new NetHttpTransport.Builder()
.setSslSocketFactory(sslContext.getSocketFactory())
.build();
} catch (GeneralSecurityException | IOException e) {
throw new BigQueryJdbcRuntimeException(
"Failed to configure SSL for HTTP transport", e);
}
});
Comment thread
keshavdandeva marked this conversation as resolved.
Outdated
}

if (connectTimeout != null) {
Expand Down Expand Up @@ -196,15 +212,19 @@ private static HttpTransportFactory getHttpTransportFactory(
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);

SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext);
httpClientBuilder.setConnectionManager(
PoolingHttpClientConnectionManagerBuilder.create()
.setSSLSocketFactory(sslSocketFactory)
.build());
setSslSocketFactory(httpClientBuilder, sslContext);
} catch (IOException | GeneralSecurityException e) {
throw new BigQueryJdbcRuntimeException(
"Failed to configure SSL TrustStore for HTTP transport", e);
}
} else {
// Default to MergedTrustManager when no custom SSLTrustStore is specified, ensuring standard
// JVM properties (like javax.net.ssl.trustStore) and google.p12 fallback are respected.
try {
setSslSocketFactory(httpClientBuilder, createMergedSslContext());
} catch (IOException | GeneralSecurityException e) {
throw new BigQueryJdbcRuntimeException("Failed to configure SSL for HTTP transport", e);
}
Comment thread
keshavdandeva marked this conversation as resolved.
Outdated
}
addAuthToProxyIfPresent(proxyProperties, httpClientBuilder, callerClassName);

Expand Down Expand Up @@ -318,4 +338,91 @@ private static HttpConnectProxiedSocketAddress getHttpConnectProxiedSocketAddres
}
return builder.build();
}

private static SSLContext createMergedSslContext() throws GeneralSecurityException, IOException {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] {createMergedTrustManager()}, null);
return sslContext;
}

private static void setSslSocketFactory(
HttpClientBuilder httpClientBuilder, SSLContext sslContext) {
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext);
httpClientBuilder.setConnectionManager(
PoolingHttpClientConnectionManagerBuilder.create()
.setSSLSocketFactory(sslSocketFactory)
.build());
}

private static X509TrustManager createMergedTrustManager()
throws GeneralSecurityException, IOException {
// 1. Get default JVM TrustManager
TrustManagerFactory defaultTmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
defaultTmf.init((KeyStore) null);
X509TrustManager defaultTm = findX509TrustManager(defaultTmf);

// 2. Get Google TrustManager
KeyStore googleKeystore = GoogleUtils.getCertificateTrustStore();
TrustManagerFactory googleTmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
googleTmf.init(googleKeystore);
X509TrustManager googleTm = findX509TrustManager(googleTmf);

if (defaultTm == null || googleTm == null) {
throw new IllegalStateException("Could not find X509TrustManager");
}

return new MergedTrustManager(defaultTm, googleTm);
}

private static X509TrustManager findX509TrustManager(TrustManagerFactory tmf) {
for (TrustManager tm : tmf.getTrustManagers()) {
if (tm instanceof X509TrustManager) {
return (X509TrustManager) tm;
}
}
return null;
}

private static class MergedTrustManager implements X509TrustManager {
private final X509TrustManager defaultTm;
private final X509TrustManager googleTm;

public MergedTrustManager(X509TrustManager defaultTm, X509TrustManager googleTm) {
this.defaultTm = defaultTm;
this.googleTm = googleTm;
}

@Override
public X509Certificate[] getAcceptedIssuers() {
X509Certificate[] defaultIssuers = defaultTm.getAcceptedIssuers();
X509Certificate[] googleIssuers = googleTm.getAcceptedIssuers();
X509Certificate[] result = new X509Certificate[defaultIssuers.length + googleIssuers.length];
System.arraycopy(defaultIssuers, 0, result, 0, defaultIssuers.length);
System.arraycopy(googleIssuers, 0, result, defaultIssuers.length, googleIssuers.length);
return result;
}

@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
try {
defaultTm.checkClientTrusted(chain, authType);
} catch (CertificateException e) {
googleTm.checkClientTrusted(chain, authType);
}
}

@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
try {
defaultTm.checkServerTrusted(chain, authType);
} catch (CertificateException e) {
// Fall back to Google's trusted certs
googleTm.checkServerTrusted(chain, authType);
}
}
}
}
Comment thread
keshavdandeva marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ public void testGetHttpTransportOptionsWithNonAuthenticatedProxy() {
}

@Test
public void testGetHttpTransportOptionsWithNoProxySettingsReturnsNull() {
public void testGetHttpTransportOptionsWithNoProxySettingsReturnsDefaultOptions() {
String connection_uri =
"jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;"
+ "ProjectId=TestProject"
Expand All @@ -172,7 +172,8 @@ public void testGetHttpTransportOptionsWithNoProxySettingsReturnsNull() {
HttpTransportOptions result =
BigQueryJdbcProxyUtility.getHttpTransportOptions(
proxyProperties, null, null, null, null, "TestClass");
assertNull(result);
assertNotNull(result);
assertNotNull(result.getHttpTransportFactory());
}

private String getTestResourcePath(String resourceName) throws URISyntaxException {
Expand Down Expand Up @@ -299,11 +300,12 @@ public void testGetTransportChannelProvider_noProxyNoSsl_returnsNull() {
}

@Test
public void testGetHttpTransportOptions_noProxyNoSsl_returnsNull() {
public void testGetHttpTransportOptions_noProxyNoSsl_returnsDefaultOptions() {
HttpTransportOptions options =
BigQueryJdbcProxyUtility.getHttpTransportOptions(
Collections.<String, String>emptyMap(), null, null, null, null, "TestClass");
assertNull(options);
assertNotNull(options);
assertNotNull(options.getHttpTransportFactory());
}

@Test
Expand Down
Loading