blob: b7ae0a008f8bb8a457ab7fd7b2f0e3aa0989c638 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you 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 org.apache.calcite.avatica.remote;
import org.apache.http.auth.AuthSchemeProvider;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.KerberosCredentials;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.auth.SPNegoSchemeFactory;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.util.EntityUtils;
import org.ietf.jgss.GSSCredential;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.Principal;
/**
* Implementation of an AvaticaHttpClient which uses SPNEGO.
*
* <p>(At this point it could probably be just merged back into
* {@link AvaticaCommonsHttpClientImpl}.)
*/
public class AvaticaCommonsHttpClientSpnegoImpl extends AvaticaCommonsHttpClientImpl {
private static final Logger LOG = LoggerFactory
.getLogger(AvaticaCommonsHttpClientSpnegoImpl.class);
public static final String CACHED_CONNECTIONS_MAX_KEY = "avatica.http.spnego.max_cached";
public static final String CACHED_CONNECTIONS_MAX_PER_ROUTE_KEY =
"avatica.http.spnego.max_per_route";
private static final boolean USE_CANONICAL_HOSTNAME =
Boolean.parseBoolean(
System.getProperty("avatica.http.spnego.use_canonical_hostname", "true"));
private static final boolean STRIP_PORT_ON_SERVER_LOOKUP = true;
/**
* Constructs an http client with the expectation that the user is already logged in with their
* Kerberos identity via JAAS.
*
* @param url The URL for the Avatica server
*/
public AvaticaCommonsHttpClientSpnegoImpl(URL url) {
this(url, null);
}
/**
* Constructs an HTTP client with user specified by the given credentials.
*
* @param url The URL for the Avatica server
* @param credential The GSS credentials
*/
public AvaticaCommonsHttpClientSpnegoImpl(URL url, GSSCredential credential) {
super(url);
setGSSCredential(credential);
}
@Override protected void configureConnectionPool(Registry<ConnectionSocketFactory> registry) {
super.configureConnectionPool(registry);
//For backwards compatibility, override the standard values if set
final String maxCnxns =
System.getProperty(CACHED_CONNECTIONS_MAX_KEY);
if (maxCnxns != null) {
pool.setMaxTotal(Integer.parseInt(maxCnxns));
}
//For backwards compatibility, override the standard values if set
final String maxCnxnsPerRoute = System.getProperty(CACHED_CONNECTIONS_MAX_PER_ROUTE_KEY);
if (maxCnxnsPerRoute != null) {
pool.setDefaultMaxPerRoute(Integer.parseInt(maxCnxnsPerRoute));
}
}
public void setGSSCredential(GSSCredential credential) {
this.authRegistry = RegistryBuilder.<AuthSchemeProvider>create().register(AuthSchemes.SPNEGO,
new SPNegoSchemeFactory(STRIP_PORT_ON_SERVER_LOOKUP, USE_CANONICAL_HOSTNAME)).build();
this.credentialsProvider = new BasicCredentialsProvider();
if (null != credential) {
// Non-null credential should be used directly with KerberosCredentials.
// This is never set by the JDBC driver, nor the tests
this.credentialsProvider.setCredentials(AuthScope.ANY, new KerberosCredentials(credential));
} else {
// A null credential implies that the user is logged in via JAAS using the
// java.security.auth.login.config system property
this.credentialsProvider.setCredentials(AuthScope.ANY, EmptyCredentials.INSTANCE);
}
}
@Override public byte[] send(byte[] request) {
HttpClientContext context = HttpClientContext.create();
context.setTargetHost(host);
context.setCredentialsProvider(credentialsProvider);
context.setAuthSchemeRegistry(authRegistry);
context.setAuthCache(authCache);
ByteArrayEntity entity = new ByteArrayEntity(request, ContentType.APPLICATION_OCTET_STREAM);
// Create the client with the AuthSchemeRegistry and manager
HttpPost post = new HttpPost(uri);
post.setEntity(entity);
try (CloseableHttpResponse response = client.execute(post, context)) {
final int statusCode = response.getStatusLine().getStatusCode();
if (HttpURLConnection.HTTP_OK == statusCode
|| HttpURLConnection.HTTP_INTERNAL_ERROR == statusCode) {
return EntityUtils.toByteArray(response.getEntity());
}
throw new RuntimeException("Failed to execute HTTP Request, got HTTP/" + statusCode);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
LOG.debug("Failed to execute HTTP request", e);
throw new RuntimeException(e);
}
}
/**
* A credentials implementation which returns null.
*/
private static class EmptyCredentials implements Credentials {
public static final EmptyCredentials INSTANCE = new EmptyCredentials();
@Override public String getPassword() {
return null;
}
@Override public Principal getUserPrincipal() {
return null;
}
}
}
// End AvaticaCommonsHttpClientSpnegoImpl.java