/**
* 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.hadoop.gateway.dispatch;
import org.apache.hadoop.gateway.config.GatewayConfig;
import org.apache.hadoop.gateway.services.GatewayServices;
import org.apache.hadoop.gateway.services.metrics.MetricsService;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolException;
import org.apache.http.auth.AuthSchemeProvider;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.client.CookieStore;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.RedirectStrategy;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HttpContext;
import org.joda.time.Period;
import org.joda.time.format.PeriodFormatter;
import org.joda.time.format.PeriodFormatterBuilder;
import javax.servlet.FilterConfig;
import java.io.IOException;
import java.security.Principal;
import java.util.Collections;
import java.util.Date;
import java.util.List;
public class DefaultHttpClientFactory implements HttpClientFactory {
@Override
public HttpClient createHttpClient(FilterConfig filterConfig) {
HttpClientBuilder builder = null;
GatewayConfig gatewayConfig = (GatewayConfig) filterConfig.getServletContext().getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE);
if (gatewayConfig != null && gatewayConfig.isMetricsEnabled()) {
GatewayServices services = (GatewayServices) filterConfig.getServletContext()
.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
MetricsService metricsService = services.getService(GatewayServices.METRICS_SERVICE);
builder = metricsService.getInstrumented(HttpClientBuilder.class);
} else {
builder = HttpClients.custom();
}
if ( "true".equals(System.getProperty(GatewayConfig.HADOOP_KERBEROS_SECURED)) ) {
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UseJaasCredentials());
Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
.register(AuthSchemes.SPNEGO, new KnoxSpnegoAuthSchemeFactory(true))
.build();
builder = builder.setDefaultAuthSchemeRegistry(authSchemeRegistry)
.setDefaultCookieStore(new HadoopAuthCookieStore())
.setDefaultCredentialsProvider(credentialsProvider);
} else {
builder = builder.setDefaultCookieStore(new NoCookieStore());
}
builder.setKeepAliveStrategy( DefaultConnectionKeepAliveStrategy.INSTANCE );
builder.setConnectionReuseStrategy( DefaultConnectionReuseStrategy.INSTANCE );
builder.setRedirectStrategy( new NeverRedirectStrategy() );
builder.setRetryHandler( new NeverRetryHandler() );
int maxConnections = getMaxConnections( filterConfig );
builder.setMaxConnTotal( maxConnections );
builder.setMaxConnPerRoute( maxConnections );
builder.setDefaultRequestConfig( getRequestConfig( filterConfig ) );
HttpClient client = builder.build();
return client;
}
private static RequestConfig getRequestConfig( FilterConfig config ) {
RequestConfig.Builder builder = RequestConfig.custom();
int connectionTimeout = getConnectionTimeout( config );
if ( connectionTimeout != -1 ) {
builder.setConnectTimeout( connectionTimeout );
builder.setConnectionRequestTimeout( connectionTimeout );
}
int socketTimeout = getSocketTimeout( config );
if( socketTimeout != -1 ) {
builder.setSocketTimeout( socketTimeout );
}
return builder.build();
}
private static class NoCookieStore implements CookieStore {
@Override
public void addCookie(Cookie cookie) {
//no op
}
@Override
public List<Cookie> getCookies() {
return Collections.emptyList();
}
@Override
public boolean clearExpired(Date date) {
return true;
}
@Override
public void clear() {
//no op
}
}
private static class NeverRedirectStrategy implements RedirectStrategy {
@Override
public boolean isRedirected( HttpRequest request, HttpResponse response, HttpContext context )
throws ProtocolException {
return false;
}
@Override
public HttpUriRequest getRedirect( HttpRequest request, HttpResponse response, HttpContext context )
throws ProtocolException {
return null;
}
}
private static class NeverRetryHandler implements HttpRequestRetryHandler {
@Override
public boolean retryRequest( IOException exception, int executionCount, HttpContext context ) {
return false;
}
}
private static class UseJaasCredentials implements Credentials {
public String getPassword() {
return null;
}
public Principal getUserPrincipal() {
return null;
}
}
private int getMaxConnections( FilterConfig filterConfig ) {
int maxConnections = 32;
GatewayConfig config =
(GatewayConfig)filterConfig.getServletContext().getAttribute( GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE );
if( config != null ) {
maxConnections = config.getHttpClientMaxConnections();
}
String str = filterConfig.getInitParameter( "httpclient.maxConnections" );
if( str != null ) {
try {
maxConnections = Integer.parseInt( str );
} catch ( NumberFormatException e ) {
// Ignore it and use the default.
}
}
return maxConnections;
}
private static int getConnectionTimeout( FilterConfig filterConfig ) {
int timeout = -1;
GatewayConfig globalConfig =
(GatewayConfig)filterConfig.getServletContext().getAttribute( GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE );
if( globalConfig != null ) {
timeout = globalConfig.getHttpClientConnectionTimeout();
}
String str = filterConfig.getInitParameter( "httpclient.connectionTimeout" );
if( str != null ) {
try {
timeout = (int)parseTimeout( str );
} catch ( Exception e ) {
// Ignore it and use the default.
}
}
return timeout;
}
private static int getSocketTimeout( FilterConfig filterConfig ) {
int timeout = -1;
GatewayConfig globalConfig =
(GatewayConfig)filterConfig.getServletContext().getAttribute( GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE );
if( globalConfig != null ) {
timeout = globalConfig.getHttpClientSocketTimeout();
}
String str = filterConfig.getInitParameter( "httpclient.socketTimeout" );
if( str != null ) {
try {
timeout = (int)parseTimeout( str );
} catch ( Exception e ) {
// Ignore it and use the default.
}
}
return timeout;
}
private static long parseTimeout( String s ) {
PeriodFormatter f = new PeriodFormatterBuilder()
.appendMinutes().appendSuffix("m"," min")
.appendSeconds().appendSuffix("s"," sec")
.appendMillis().toFormatter();
Period p = Period.parse( s, f );
return p.toStandardDuration().getMillis();
}
}