in kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/KyuubiConnection.java [435:606]
private CloseableHttpClient getHttpClient(Boolean useSsl) throws SQLException {
boolean isCookieEnabled = isCookieEnabled();
String cookieName = sessConfMap.getOrDefault(COOKIE_NAME, DEFAULT_COOKIE_NAMES_HS2);
CookieStore cookieStore = isCookieEnabled ? new BasicCookieStore() : null;
// Request interceptor for any request pre-processing logic
Map<String, String> additionalHttpHeaders = new HashMap<>();
Map<String, String> customCookies = new HashMap<>();
// Retrieve the additional HttpHeaders
for (Map.Entry<String, String> entry : sessConfMap.entrySet()) {
String key = entry.getKey();
if (key.startsWith(HTTP_HEADER_PREFIX)) {
additionalHttpHeaders.put(key.substring(HTTP_HEADER_PREFIX.length()), entry.getValue());
}
if (key.startsWith(HTTP_COOKIE_PREFIX)) {
customCookies.put(key.substring(HTTP_COOKIE_PREFIX.length()), entry.getValue());
}
}
HttpRequestInterceptor requestInterceptor;
if (!isSaslAuthMode()) {
requestInterceptor = null;
} else if (isPlainSaslAuthMode()) {
if (isJwtAuthMode()) {
final String signedJwt = getJWT();
Preconditions.checkArgument(
signedJwt != null && !signedJwt.isEmpty(),
"For jwt auth mode," + " a signed jwt must be provided");
/*
* Add an interceptor to pass jwt token in the header. In https mode, the entire
* information is encrypted
*/
requestInterceptor =
new HttpJwtAuthRequestInterceptor(
signedJwt, cookieStore, cookieName, useSsl, additionalHttpHeaders, customCookies);
} else {
/*
* Add an interceptor to pass username/password in the header. In https mode, the entire
* information is encrypted
*/
requestInterceptor =
new HttpBasicAuthInterceptor(
getUserName(),
getPassword(),
cookieStore,
cookieName,
useSsl,
additionalHttpHeaders,
customCookies);
}
} else {
// Configure http client for kerberos-based authentication
Subject subject = createSubject();
/*
* Add an interceptor which sets the appropriate header in the request. It does the kerberos
* authentication and get the final service ticket, for sending to the server before every
* request. In https mode, the entire information is encrypted
*/
requestInterceptor =
new HttpKerberosRequestInterceptor(
sessConfMap.get(AUTH_PRINCIPAL),
host,
subject,
cookieStore,
cookieName,
useSsl,
additionalHttpHeaders,
customCookies);
}
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
// Set timeout
RequestConfig config =
RequestConfig.custom()
.setConnectTimeout(connectTimeout)
.setSocketTimeout(socketTimeout)
.build();
httpClientBuilder.setDefaultRequestConfig(config);
// Configure http client for cookie based authentication
if (isCookieEnabled) {
// Create a http client with a retry mechanism when the server returns a status code of 401.
httpClientBuilder.setServiceUnavailableRetryStrategy(
new ServiceUnavailableRetryStrategy() {
@Override
public boolean retryRequest(
final HttpResponse response, final int executionCount, final HttpContext context) {
int statusCode = response.getStatusLine().getStatusCode();
boolean ret = statusCode == 401 && executionCount <= 1;
// Set the context attribute to true which will be interpreted by the request
// interceptor
if (ret) {
context.setAttribute(HIVE_SERVER2_RETRY_KEY, HIVE_SERVER2_RETRY_TRUE);
}
return ret;
}
@Override
public long getRetryInterval() {
// Immediate retry
return 0;
}
});
}
// In case the server's idletimeout is set to a lower value, it might close it's side of
// connection. However we retry one more time on NoHttpResponseException
httpClientBuilder.setRetryHandler(
(exception, executionCount, context) -> {
if (executionCount > 1) {
LOG.info("Retry attempts to connect to server exceeded.");
return false;
}
if (exception instanceof NoHttpResponseException) {
LOG.info("Could not connect to the server. Retrying one more time.");
return true;
}
return false;
});
// Add the request interceptor to the client builder
httpClientBuilder.addInterceptorFirst(requestInterceptor);
// Add an interceptor to add in an XSRF header
httpClientBuilder.addInterceptorLast(new HttpXsrfRequestInterceptor());
// Configure http client for SSL
if (useSsl) {
String useTwoWaySSL = sessConfMap.get(USE_TWO_WAY_SSL);
String sslTrustStorePath = sessConfMap.get(SSL_TRUST_STORE);
String sslTrustStorePassword =
Utils.getPassword(sessConfMap, JdbcConnectionParams.SSL_TRUST_STORE_PASSWORD);
KeyStore sslTrustStore;
SSLConnectionSocketFactory socketFactory;
SSLContext sslContext;
/*
* The code within the try block throws: SSLInitializationException, KeyStoreException,
* IOException, NoSuchAlgorithmException, CertificateException, KeyManagementException &
* UnrecoverableKeyException. We don't want the client to retry on any of these, hence we
* catch all and throw a SQLException.
*/
try {
if (useTwoWaySSL != null && useTwoWaySSL.equalsIgnoreCase(TRUE)) {
socketFactory = getTwoWaySSLSocketFactory();
} else if (sslTrustStorePath == null || sslTrustStorePath.isEmpty()) {
// Create a default socket factory based on standard JSSE trust material
socketFactory = SSLConnectionSocketFactory.getSocketFactory();
} else {
// Pick trust store config from the given path
sslTrustStore = KeyStore.getInstance(SSL_TRUST_STORE_TYPE);
try (FileInputStream fis = new FileInputStream(sslTrustStorePath)) {
sslTrustStore.load(
fis, sslTrustStorePassword != null ? sslTrustStorePassword.toCharArray() : null);
}
sslContext = SSLContexts.custom().loadTrustMaterial(sslTrustStore, null).build();
socketFactory =
new SSLConnectionSocketFactory(sslContext, new DefaultHostnameVerifier(null));
}
final Registry<ConnectionSocketFactory> registry =
RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", socketFactory)
.build();
httpClientBuilder.setConnectionManager(new BasicHttpClientConnectionManager(registry));
} catch (Exception e) {
String msg =
"Could not create an https connection to " + jdbcUriString + ". " + e.getMessage();
throw new KyuubiSQLException(msg, "08S01", e);
}
}
return httpClientBuilder.build();
}