com.cisco.oss.foundation.http.netlifx.apache.ApacheNetflixHttpClient.java Source code

Java tutorial

Introduction

Here is the source code for com.cisco.oss.foundation.http.netlifx.apache.ApacheNetflixHttpClient.java

Source

/*
 * Copyright 2015 Cisco Systems, Inc.
 *
 *  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.cisco.oss.foundation.http.netlifx.apache;

import com.cisco.oss.foundation.configuration.ConfigurationFactory;
import com.cisco.oss.foundation.flowcontext.FlowContextFactory;
import com.cisco.oss.foundation.http.*;
import com.cisco.oss.foundation.http.HttpRequest;
import com.cisco.oss.foundation.http.apache.ApacheHttpResponse;
import com.cisco.oss.foundation.http.apache.InfraConnectionKeepAliveStrategy;
import com.cisco.oss.foundation.loadbalancer.InternalServerProxy;
import com.cisco.oss.foundation.loadbalancer.LoadBalancerConstants;
import com.cisco.oss.foundation.loadbalancer.LoadBalancerStrategy;
import com.google.common.base.Joiner;
import com.netflix.appinfo.EurekaInstanceConfig;
import com.netflix.appinfo.MyDataCenterInstanceConfig;
import com.netflix.client.AbstractLoadBalancerAwareClient;
import com.netflix.client.RequestSpecificRetryHandler;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.client.config.IClientConfig;
import com.netflix.client.http.HttpRequest.Verb;
import com.netflix.config.ConfigurationManager;
import com.netflix.discovery.DefaultEurekaClientConfig;
import com.netflix.discovery.DiscoveryManager;
import com.netflix.discovery.EurekaClientConfig;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.DynamicServerListLoadBalancer;
import com.netflix.niws.loadbalancer.DiscoveryEnabledServer;
import org.apache.commons.configuration.AbstractConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContexts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyStore;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;

/**
 * Created by Yair Ogen (yaogen) on 13/12/2015.
 */
class ApacheNetflixHttpClient extends
        AbstractLoadBalancerAwareClient<com.netflix.client.http.HttpRequest, com.netflix.client.http.HttpResponse>
        implements HttpClient<HttpRequest, ApacheNetflixHttpResponse> {

    static {
        if (!ConfigurationManager.isConfigurationInstalled()) {
            ConfigurationManager.install((AbstractConfiguration) ConfigurationFactory.getConfiguration());
        }
    }

    private static final Logger LOGGER = LoggerFactory.getLogger(ApacheNetflixHttpClient.class);
    CloseableHttpAsyncClient httpAsyncClient = null;
    private CloseableHttpClient httpClient = null;
    private HostnameVerifier hostnameVerifier;
    public static ThreadLocal<HttpContext> HTTP_CONTEXT_THREAD_LOCAL = new ThreadLocal<>();
    protected InternalServerProxyMetadata metadata = null;
    protected boolean followRedirects = false;
    protected boolean autoEncodeUri = true;
    private IClientConfig clientConfig = null;
    private Joiner joiner = Joiner.on(",").skipNulls();
    private String apiName = "HTTP";
    protected boolean enableLoadBalancing = true;
    BaseLoadBalancer loadBalancer = new DynamicServerListLoadBalancer<DiscoveryEnabledServer>();
    private boolean startEurekaClient = true;

    public boolean isAutoCloseable() {
        return autoCloseable;
    }

    private boolean autoCloseable = true;

    @Override
    public ApacheNetflixHttpResponse execute(HttpRequest request) {

        //        throw new UnsupportedOperationException("execute is currently not supported");

        try {
            if (enableLoadBalancing) {
                return executeWithLoadBalancer(request);
            } else {
                com.netflix.client.http.HttpRequest httpRequest = buildNetflixHttpRequest(request, joiner);
                return (ApacheNetflixHttpResponse) execute(httpRequest, null);
            }

        } catch (Exception e) {
            throw new ClientException(e.toString(), e);
        }
    }

    @Override
    public ApacheNetflixHttpResponse executeWithLoadBalancer(HttpRequest request) {
        //        throw new UnsupportedOperationException("executeWithLoadBalancer is currently not supported");
        try {
            return ((ApacheNetflixHttpResponse) executeWithLoadBalancer(buildNetflixHttpRequest(request, joiner),
                    clientConfig));
        } catch (Exception e) {
            LOGGER.error("error executing client request: {}", e, e);
            throw new ClientException(e.toString(), e);
        }
    }

    @Override
    public void executeWithLoadBalancer(HttpRequest request,
            ResponseCallback<ApacheNetflixHttpResponse> responseCallback) {
        throw new UnsupportedOperationException("ASYNC Client is currently not supported");
    }

    @Override
    public RequestSpecificRetryHandler getRequestSpecificRetryHandler(com.netflix.client.http.HttpRequest request,
            IClientConfig requestConfig) {
        //        if (!request.isRetriable()) {
        return new RequestSpecificRetryHandler(false, false, this.getRetryHandler(), requestConfig);
        //        }
        //        if (this.ncc.get(CommonClientConfigKey.OkToRetryOnAllOperations, false)) {
        //            return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(), requestConfig);
        //        }
        //        if (request.getVerb() != com.netflix.client.http.HttpRequest.Verb.GET) {
        //            return new RequestSpecificRetryHandler(true, false, this.getRetryHandler(), requestConfig);
        //        } else {
        //            return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(), requestConfig);
        //        }
    }

    private com.netflix.client.http.HttpRequest buildNetflixHttpRequest(HttpRequest httpRequest, Joiner joiner) {
        com.netflix.client.http.HttpRequest.Builder builder = com.netflix.client.http.HttpRequest.newBuilder();
        //        URI requestUri = buildUri(httpRequest, joiner);
        builder.entity(httpRequest.getEntity()).uri(httpRequest.getUri())
                .verb(httpMethodToVerb(httpRequest.getHttpMethod()));

        Map<String, Collection<String>> headers = httpRequest.getHeaders();
        for (Map.Entry<String, Collection<String>> stringCollectionEntry : headers.entrySet()) {
            String key = stringCollectionEntry.getKey();
            Collection<String> stringCollection = stringCollectionEntry.getValue();
            String value = joiner.join(stringCollection);
            builder.header(key, value);
        }

        builder.header("FLOW_CONTEXT", FlowContextFactory.serializeNativeFlowContext());

        if (StringUtils.isNoneEmpty(httpRequest.getContentType())) {
            String contentType = httpRequest.getContentType();
            builder.header("Content-Type", contentType);
        }

        Map<String, Collection<String>> queryParams = httpRequest.getQueryParams();
        if (queryParams != null && !queryParams.isEmpty()) {

            for (Map.Entry<String, Collection<String>> stringCollectionEntry : queryParams.entrySet()) {
                String key = stringCollectionEntry.getKey();
                Collection<String> queryParamsValueList = stringCollectionEntry.getValue();
                //                if (request.isQueryParamsParseAsMultiValue()) {
                //                    for (String queryParamsValue : queryParamsValueList) {
                //                        uriBuilder.addParameter(key, queryParamsValue);
                //                        queryStringBuilder.append(key).append("=").append(queryParamsValue).append("&");
                //                    }
                //                }else{
                String value = joiner.join(queryParamsValueList);
                builder.queryParam(key, value);
                //                    queryStringBuilder.append(key).append("=").append(value).append("&");
                //                }
            }

        }

        return builder.build();
    }

    private Verb httpMethodToVerb(HttpMethod method) {
        switch (method) {
        case GET:
            return Verb.GET;
        case DELETE:
            return Verb.DELETE;
        case HEAD:
            return Verb.HEAD;
        case PUT:
            return Verb.PUT;
        case POST:
            return Verb.POST;
        case OPTIONS:
            return Verb.OPTIONS;
        case PATCH:
            throw new IllegalArgumentException("Netflix client doesn't support patch method");
        default:
            throw new IllegalArgumentException("Got invalid http method");
        }
    }

    @Override
    public String getApiName() {
        return apiName;
    }

    @Override
    public com.netflix.client.http.HttpResponse execute(final com.netflix.client.http.HttpRequest request,
            IClientConfig requestConfig) throws Exception {
        //        HystrixCommand.Setter setter = HystrixCommand.Setter.withGroupKey(new HystrixCommandGroupKey() {
        //            @Override
        //            public String name() {
        //                return getApiName();
        //            }
        //        });
        //
        //        HystrixCommandProperties.Setter commandPropertiesDefaults = HystrixCommandProperties.Setter();
        //
        //        setter.andCommandPropertiesDefaults(commandPropertiesDefaults);
        //        HystrixCommand<com.netflix.client.http.HttpResponse> hystrixCommand = new HystrixCommand<com.netflix.client.http.HttpResponse>(setter) {
        //            @Override
        //            protected HttpResponse run() throws Exception {
        LOGGER.info("sending request: {}-{}", request.getVerb(), request.getUri());
        URI requestUri = request.getUri();
        org.apache.http.HttpRequest httpRequest = buildHttpUriRequest(request, joiner, requestUri);
        HttpHost httpHost = new HttpHost(requestUri.getHost(), requestUri.getPort(), requestUri.getScheme());
        HttpContext httpContext = HTTP_CONTEXT_THREAD_LOCAL.get();
        CloseableHttpResponse response = httpContext == null ? httpClient.execute(httpHost, httpRequest)
                : httpClient.execute(httpHost, httpRequest, httpContext);
        ApacheNetflixHttpResponse netflixHttpResponse = new ApacheNetflixHttpResponse(response, requestUri,
                autoCloseable);
        LOGGER.info("got response status: {} for request: {}", netflixHttpResponse.getStatus(),
                netflixHttpResponse.getRequestedURI());
        return netflixHttpResponse;
        //            }
        //        };
        //
        //        return hystrixCommand.execute();

    }

    public ApacheNetflixHttpClient(String apiName, Configuration configuration, boolean enableLoadBalancing,
            HostnameVerifier hostnameVerifier) {
        super(null);
        this.hostnameVerifier = hostnameVerifier;
        this.apiName = apiName;
        this.enableLoadBalancing = enableLoadBalancing;
        this.metadata = loadServersMetadataConfiguration();
        configureClient();
    }

    public ApacheNetflixHttpClient(String apiName, LoadBalancerStrategy.STRATEGY_TYPE strategyType,
            Configuration configuration, boolean enableLoadBalancing, HostnameVerifier hostnameVerifier) {
        super(null);
        this.hostnameVerifier = hostnameVerifier;
        this.apiName = apiName;
        this.enableLoadBalancing = enableLoadBalancing;
        this.metadata = loadServersMetadataConfiguration();
        configureClient();
    }

    //    ApacheNetflixHttpClient(String apiName, Configuration configuration, boolean enableLoadBalancing, HostnameVerifier hostnameVerifier) {
    //        super(apiName, configuration, enableLoadBalancing);
    //        this.hostnameVerifier = hostnameVerifier;
    //        configureClient();
    //    }

    //    ApacheNetflixHttpClient(String apiName, LoadBalancerStrategy.STRATEGY_TYPE strategyType, Configuration configuration, boolean enableLoadBalancing, HostnameVerifier hostnameVerifier) {
    //        super(apiName, strategyType, configuration, enableLoadBalancing);
    //        this.hostnameVerifier = hostnameVerifier;
    //        configureClient();
    //    }

    //    @Override
    protected void configureClient() {

        clientConfig = new DefaultClientConfigImpl();
        clientConfig.loadProperties(getApiName());
        setLoadBalancer(loadBalancer);
        //        clientConfig.set(CommonClientConfigKey.NIWSServerListClassName, DiscoveryEnabledNIWSServerList.class.getName());
        //        clientConfig.set(IClientConfigKey.Keys.DeploymentContextBasedVipAddresses, metadata.getServiceName());
        //        clientConfig.set(CommonClientConfigKey.NFLoadBalancerRuleClassName, RoundRobinRule.class.getName());
        //        clientConfig.set(CommonClientConfigKey.NFLoadBalancerPingClassName, NIWSDiscoveryPing.class.getName());
        //        clientConfig.set(CommonClientConfigKey.VipAddressResolverClassName, SimpleVipAddressResolver.class.getName());

        if (DiscoveryManager.getInstance().getDiscoveryClient() == null && startEurekaClient) {
            EurekaInstanceConfig eurekaInstanceConfig = new MyDataCenterInstanceConfig(getApiName());
            EurekaClientConfig eurekaClientConfig = new DefaultEurekaClientConfig(getApiName() + ".");
            DiscoveryManager.getInstance().initComponent(eurekaInstanceConfig, eurekaClientConfig);
        }

        loadBalancer.initWithNiwsConfig(clientConfig);

        //        if (HystrixPlugins.getInstance().getMetricsPublisher() == null) {
        //            HystrixPlugins.getInstance().registerMetricsPublisher(HystrixMetricsPublisherDefault.getInstance());
        //        }

        RequestConfig.Builder requestBuilder = RequestConfig.custom();
        requestBuilder = requestBuilder.setConnectTimeout(metadata.getConnectTimeout());
        requestBuilder = requestBuilder.setSocketTimeout(metadata.getReadTimeout());
        requestBuilder = requestBuilder.setStaleConnectionCheckEnabled(metadata.isStaleConnectionCheckEnabled());

        RequestConfig requestConfig = requestBuilder.build();

        boolean addSslSupport = StringUtils.isNotEmpty(metadata.getKeyStorePath())
                && StringUtils.isNotEmpty(metadata.getKeyStorePassword());

        boolean addTrustSupport = StringUtils.isNotEmpty(metadata.getTrustStorePath())
                && StringUtils.isNotEmpty(metadata.getTrustStorePassword());

        autoCloseable = metadata.isAutoCloseable();

        autoEncodeUri = metadata.isAutoEncodeUri();
        followRedirects = metadata.isFollowRedirects();

        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();

        SSLContext sslContext = null;

        try {

            String keystoreType = "JKS";
            if (addSslSupport && addTrustSupport) {

                KeyStore keyStore = KeyStore.getInstance(keystoreType);
                keyStore.load(new FileInputStream(metadata.getKeyStorePath()),
                        metadata.getKeyStorePassword().toCharArray());

                KeyStore trustStore = KeyStore.getInstance(keystoreType);
                trustStore.load(new FileInputStream(metadata.getTrustStorePath()),
                        metadata.getTrustStorePassword().toCharArray());

                sslContext = SSLContexts.custom().useProtocol("TLS")
                        .loadKeyMaterial(keyStore, metadata.getKeyStorePassword().toCharArray())
                        .loadTrustMaterial(trustStore, null).build();

            } else if (addSslSupport) {

                TrustManagerFactory tmf = TrustManagerFactory
                        .getInstance(TrustManagerFactory.getDefaultAlgorithm());

                KeyStore keyStore = KeyStore.getInstance(keystoreType);
                keyStore.load(new FileInputStream(metadata.getKeyStorePath()),
                        metadata.getKeyStorePassword().toCharArray());

                tmf.init(keyStore);

                sslContext = SSLContexts.custom().useProtocol("SSL")
                        .loadKeyMaterial(keyStore, metadata.getKeyStorePassword().toCharArray()).build();

                sslContext.init(null, tmf.getTrustManagers(), null);

                SSLConnectionSocketFactory sf = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);

                httpClientBuilder.setSSLSocketFactory(sf);

            } else if (addTrustSupport) {

                KeyStore trustStore = KeyStore.getInstance(keystoreType);
                trustStore.load(new FileInputStream(metadata.getTrustStorePath()),
                        metadata.getTrustStorePassword().toCharArray());

                sslContext = SSLContexts.custom().useProtocol("TLS").loadTrustMaterial(trustStore, null).build();

            }

            if (addSslSupport | addTrustSupport) {
                SSLContext.setDefault(sslContext);
                httpClientBuilder.setSslcontext(sslContext);
            }

        } catch (Exception e) {
            LOGGER.error("can't set TLS Support. Error is: {}", e, e);
        }

        httpClientBuilder.setMaxConnPerRoute(metadata.getMaxConnectionsPerAddress())
                .setMaxConnTotal(metadata.getMaxConnectionsTotal()).setDefaultRequestConfig(requestConfig)
                .evictExpiredConnections().evictIdleConnections(metadata.getIdleTimeout(), TimeUnit.MILLISECONDS)
                .setKeepAliveStrategy(new InfraConnectionKeepAliveStrategy(metadata.getIdleTimeout()));

        HttpAsyncClientBuilder httpAsyncClientBuilder = HttpAsyncClients.custom();

        httpAsyncClientBuilder.setDefaultRequestConfig(requestConfig)
                .setMaxConnPerRoute(metadata.getMaxConnectionsPerAddress())
                .setMaxConnTotal(metadata.getMaxConnectionsTotal())
                .setKeepAliveStrategy(new InfraConnectionKeepAliveStrategy(metadata.getIdleTimeout()))
                .setSSLContext(sslContext);

        if (metadata.isDisableCookies()) {
            httpClientBuilder.disableCookieManagement();
            httpAsyncClientBuilder.disableCookieManagement();
        }

        if (hostnameVerifier != null) {
            httpClientBuilder.setSSLHostnameVerifier(hostnameVerifier);
            httpAsyncClientBuilder.setSSLHostnameVerifier(hostnameVerifier);
        }

        if (!followRedirects) {
            httpClientBuilder.disableRedirectHandling();
        }

        httpClient = httpClientBuilder.build();

        httpAsyncClient = httpAsyncClientBuilder.build();

        httpAsyncClient.start();

    }

    @Override
    public ApacheNetflixHttpResponse executeDirect(HttpRequest request) {
        return execute(request);

    }

    private URI buildUri(HttpRequest request, Joiner joiner) {
        URI requestUri = request.getUri();

        Map<String, Collection<String>> queryParams = request.getQueryParams();
        if (queryParams != null && !queryParams.isEmpty()) {
            URIBuilder uriBuilder = new URIBuilder();
            StringBuilder queryStringBuilder = new StringBuilder();
            boolean hasQuery = !queryParams.isEmpty();
            for (Map.Entry<String, Collection<String>> stringCollectionEntry : queryParams.entrySet()) {
                String key = stringCollectionEntry.getKey();
                Collection<String> queryParamsValueList = stringCollectionEntry.getValue();
                if (request.isQueryParamsParseAsMultiValue()) {
                    for (String queryParamsValue : queryParamsValueList) {
                        uriBuilder.addParameter(key, queryParamsValue);
                        queryStringBuilder.append(key).append("=").append(queryParamsValue).append("&");
                    }
                } else {
                    String value = joiner.join(queryParamsValueList);
                    uriBuilder.addParameter(key, value);
                    queryStringBuilder.append(key).append("=").append(value).append("&");
                }
            }
            uriBuilder.setFragment(requestUri.getFragment());
            uriBuilder.setHost(requestUri.getHost());
            uriBuilder.setPath(requestUri.getPath());
            uriBuilder.setPort(requestUri.getPort());
            uriBuilder.setScheme(requestUri.getScheme());
            uriBuilder.setUserInfo(requestUri.getUserInfo());
            try {

                if (!autoEncodeUri) {
                    String urlPath = "";
                    if (requestUri.getRawPath() != null && requestUri.getRawPath().startsWith("/")) {
                        urlPath = requestUri.getRawPath();
                    } else {
                        urlPath = "/" + requestUri.getRawPath();
                    }

                    if (hasQuery) {
                        String query = queryStringBuilder.substring(0, queryStringBuilder.length() - 1);
                        requestUri = new URI(requestUri.getScheme() + "://" + requestUri.getHost() + ":"
                                + requestUri.getPort() + urlPath + "?" + query);
                    } else {
                        requestUri = new URI(requestUri.getScheme() + "://" + requestUri.getHost() + ":"
                                + requestUri.getPort() + urlPath);
                    }
                } else {
                    requestUri = uriBuilder.build();
                }
            } catch (URISyntaxException e) {
                LOGGER.warn("could not update uri: {}", requestUri);
            }
        }
        return requestUri;
    }

    private InternalServerProxyMetadata loadServersMetadataConfiguration() {

        Configuration subset = ConfigurationFactory.getConfiguration().subset(getApiName());
        final Iterator<String> keysIterator = subset.getKeys();

        // read default values
        int readTimeout = subset.getInt("http." + LoadBalancerConstants.READ_TIME_OUT,
                LoadBalancerConstants.DEFAULT_READ_TIMEOUT);
        int connectTimeout = subset.getInt("http." + LoadBalancerConstants.CONNECT_TIME_OUT,
                LoadBalancerConstants.DEFAULT_CONNECT_TIMEOUT);
        long waitingTime = subset.getLong("http." + LoadBalancerConstants.WAITING_TIME,
                LoadBalancerConstants.DEFAULT_WAITING_TIME);
        int numberOfAttempts = subset.getInt("http." + LoadBalancerConstants.NUMBER_OF_ATTEMPTS,
                LoadBalancerConstants.DEFAULT_NUMBER_OF_ATTEMPTS);
        long retryDelay = subset.getLong("http." + LoadBalancerConstants.RETRY_DELAY,
                LoadBalancerConstants.DEFAULT_RETRY_DELAY);

        long idleTimeout = subset.getLong("http." + LoadBalancerConstants.IDLE_TIME_OUT,
                LoadBalancerConstants.DEFAULT_IDLE_TIMEOUT);
        int maxConnectionsPerAddress = subset.getInt("http." + LoadBalancerConstants.MAX_CONNECTIONS_PER_ADDRESS,
                LoadBalancerConstants.DEFAULT_MAX_CONNECTIONS_PER_ADDRESS);
        int maxConnectionsTotal = subset.getInt("http." + LoadBalancerConstants.MAX_CONNECTIONS_TOTAL,
                LoadBalancerConstants.DEFAULT_MAX_CONNECTIONS_TOTAL);
        int maxQueueSizePerAddress = subset.getInt("http." + LoadBalancerConstants.MAX_QUEUE_PER_ADDRESS,
                LoadBalancerConstants.DEFAULT_MAX_QUEUE_PER_ADDRESS);
        boolean followRedirects = subset.getBoolean("http." + LoadBalancerConstants.FOLLOW_REDIRECTS, false);
        boolean disableCookies = subset.getBoolean("http." + LoadBalancerConstants.DISABLE_COOKIES, false);
        boolean autoCloseable = subset.getBoolean("http." + LoadBalancerConstants.AUTO_CLOSEABLE, true);
        boolean autoEncodeUri = subset.getBoolean("http." + LoadBalancerConstants.AUTO_ENCODE_URI, true);
        boolean staleConnectionCheckEnabled = subset
                .getBoolean("http." + LoadBalancerConstants.IS_STALE_CONN_CHECK_ENABLED, false);
        boolean serviceDirectoryEnabled = subset
                .getBoolean("http." + LoadBalancerConstants.SERVICE_DIRECTORY_IS_ENABLED, false);
        String serviceName = subset.getString("http." + LoadBalancerConstants.SERVICE_DIRECTORY_SERVICE_NAME,
                "UNKNOWN");

        String keyStorePath = subset.getString("http." + LoadBalancerConstants.KEYSTORE_PATH, "");
        String keyStorePassword = subset.getString("http." + LoadBalancerConstants.KEYSTORE_PASSWORD, "");
        String trustStorePath = subset.getString("http." + LoadBalancerConstants.TRUSTSTORE_PATH, "");
        String trustStorePassword = subset.getString("http." + LoadBalancerConstants.TRUSTSTORE_PASSWORD, "");
        startEurekaClient = subset.getBoolean("http.startEurekaClient", true);

        final List<String> keys = new ArrayList<String>();

        while (keysIterator.hasNext()) {
            String key = keysIterator.next();
            keys.add(key);
        }

        Collections.sort(keys);

        List<Pair<String, Integer>> hostAndPortPairs = new CopyOnWriteArrayList<Pair<String, Integer>>();

        for (String key : keys) {

            if (key.contains(LoadBalancerConstants.HOST)) {

                String host = subset.getString(key);

                // trim the host name
                if (org.apache.commons.lang.StringUtils.isNotEmpty(host)) {
                    host = host.trim();
                }
                final String portKey = key.replace(LoadBalancerConstants.HOST, LoadBalancerConstants.PORT);
                if (subset.containsKey(portKey)) {
                    int port = subset.getInt(portKey);
                    // save host and port for future creation of server list
                    hostAndPortPairs.add(Pair.of(host, port));
                }
            }

        }

        InternalServerProxyMetadata metadata = new InternalServerProxyMetadata(readTimeout, connectTimeout,
                idleTimeout, maxConnectionsPerAddress, maxConnectionsTotal, maxQueueSizePerAddress, waitingTime,
                numberOfAttempts, retryDelay, hostAndPortPairs, keyStorePath, keyStorePassword, trustStorePath,
                trustStorePassword, followRedirects, autoCloseable, staleConnectionCheckEnabled, disableCookies,
                serviceDirectoryEnabled, serviceName, autoEncodeUri);
        //        metadata.getHostAndPortPairs().addAll(hostAndPortPairs);
        //        metadata.setReadTimeout(readTimeout);
        //        metadata.setConnectTimeout(connectTimeout);
        //        metadata.setNumberOfRetries(numberOfAttempts);
        //        metadata.setRetryDelay(retryDelay);
        //        metadata.setWaitingTime(waitingTime);

        return metadata;

    }

    //    @Override
    public void execute(HttpRequest request, ResponseCallback responseCallback,
            LoadBalancerStrategy loadBalancerStrategy, String apiName) {
        throw new UnsupportedOperationException("ASYNC Client is currently not supported");
        //        InternalServerProxy serverProxy = loadBalancerStrategy.getServerProxy(request);
        //        Throwable lastCaugtException = null;
        //
        //
        //        if (serverProxy == null) {
        //            // server proxy will be null if the configuration was not
        //            // configured properly
        //            // or if all the servers are passivated.
        //            loadBalancerStrategy.handleNullserverProxy(apiName, lastCaugtException);
        //        }
        //
        ////        request = updateRequestUri((S)request, serverProxy);
        //
        //        if (request.isSilentLogging()) {
        //            LOGGER.trace("sending request: {}-{}", request.getHttpMethod(), request.getUri());
        //        } else {
        //            LOGGER.info("sending request: {}-{}", request.getHttpMethod(), request.getUri());
        //        }
        //
        //
        //        org.apache.http.HttpRequest httpRequest = null;
        //
        //        Joiner joiner = Joiner.on(",").skipNulls();
        //        URI requestUri = buildUri(request, joiner);
        //
        //        httpRequest = buildHttpUriRequest(request, joiner, requestUri);
        //
        //        HttpHost httpHost = new HttpHost(requestUri.getHost(), requestUri.getPort(), requestUri.getScheme());
        //
        //        httpAsyncClient.execute(httpHost, httpRequest, new FoundationFutureCallBack(this, request, responseCallback, serverProxy, loadBalancerStrategy, apiName));

    }

    //    @Override
    public void close() {
        try {
            if (httpClient != null) {
                httpClient.close();
            }

            if (httpAsyncClient != null) {
                httpAsyncClient.close();
            }
        } catch (IOException e) {
            LOGGER.warn("can't close http client: {}", e);
        }
    }

    private org.apache.http.HttpRequest buildHttpUriRequest(com.netflix.client.http.HttpRequest request,
            Joiner joiner, URI requestUri) {

        org.apache.http.HttpRequest httpRequest;
        if (autoEncodeUri) {
            switch (request.getVerb()) {
            case GET:
                httpRequest = new HttpGet(requestUri);
                break;
            case POST:
                httpRequest = new HttpPost(requestUri);
                break;
            case PUT:
                httpRequest = new HttpPut(requestUri);
                break;
            case DELETE:
                httpRequest = new HttpDelete(requestUri);
                break;
            case HEAD:
                httpRequest = new HttpHead(requestUri);
                break;
            case OPTIONS:
                httpRequest = new HttpOptions(requestUri);
                break;
            //                    case PATCH:
            //                        httpRequest = new HttpPatch(requestUri);
            //                        break;
            default:
                throw new ClientException("You have to one of the REST verbs such as GET, POST etc.");
            }
        } else {
            switch (request.getVerb()) {
            case POST:
            case PUT:
            case DELETE:
                //                    case PATCH:
                //                        httpRequest = new BasicHttpEntityEnclosingRequest(request.getHttpMethod().method(), requestUri.toString());
                //                        break;
            default:
                httpRequest = new BasicHttpRequest(request.getVerb().verb(), requestUri.toString());
            }

        }

        Object entity = request.getEntity();
        if (entity != null) {
            if (httpRequest instanceof HttpEntityEnclosingRequest) {
                HttpEntityEnclosingRequest httpEntityEnclosingRequestBase = (HttpEntityEnclosingRequest) httpRequest;
                httpEntityEnclosingRequestBase.setEntity(new ByteArrayEntity((byte[]) entity,
                        ContentType.create(request.getHttpHeaders().getFirstValue("Content-Type"))));
            } else {
                throw new ClientException(
                        "sending content for request type " + request.getVerb() + " is not supported!");
            }
        }

        Map<String, Collection<String>> headers = request.getHeaders();
        for (Map.Entry<String, Collection<String>> stringCollectionEntry : headers.entrySet()) {
            String key = stringCollectionEntry.getKey();
            Collection<String> stringCollection = stringCollectionEntry.getValue();
            String value = joiner.join(stringCollection);
            httpRequest.setHeader(key, value);
        }
        return httpRequest;
    }

    private static class FoundationFutureCallBack implements FutureCallback<org.apache.http.HttpResponse> {
        private ResponseCallback responseCallback;
        private InternalServerProxy serverProxy;
        private LoadBalancerStrategy loadBalancerStrategy;
        private String apiName;
        private HttpRequest request;
        private ApacheNetflixHttpClient apacheHttpClient;

        private FoundationFutureCallBack(ApacheNetflixHttpClient apacheHttpClient, HttpRequest request,
                ResponseCallback<ApacheHttpResponse> responseCallback, InternalServerProxy serverProxy,
                LoadBalancerStrategy loadBalancerStrategy, String apiName) {
            this.responseCallback = responseCallback;
            this.apacheHttpClient = apacheHttpClient;
            this.serverProxy = serverProxy;
            this.loadBalancerStrategy = loadBalancerStrategy;
            this.apiName = apiName;
            this.request = request;
        }

        @Override
        public void completed(org.apache.http.HttpResponse response) {

            serverProxy.setCurrentNumberOfAttempts(0);
            serverProxy.setFailedAttemptTimeStamp(0);

            ApacheHttpResponse apacheHttpResponse = new ApacheHttpResponse(response, request.getUri(),
                    apacheHttpClient.isAutoCloseable());
            if (request.isSilentLogging()) {
                LOGGER.trace("got response status: {} for request: {}", apacheHttpResponse.getStatus(),
                        apacheHttpResponse.getRequestedURI());
            } else {
                LOGGER.info("got response status: {} for request: {}", apacheHttpResponse.getStatus(),
                        apacheHttpResponse.getRequestedURI());
            }
            responseCallback.completed(apacheHttpResponse);
        }

        @Override
        public void failed(Exception ex) {

            try {
                loadBalancerStrategy.handleException(apiName, serverProxy, ex);
            } catch (Exception e) {
                LOGGER.error("Error running request {}. Error is: {}", request.getUri(), e);
                responseCallback.failed(e);
            }

            try {
                apacheHttpClient.execute(request, responseCallback, loadBalancerStrategy, apiName);
            } catch (Throwable e) {
                //                result.getRequest().abort(e);
                responseCallback.failed(e);
            }
        }

        @Override
        public void cancelled() {
            responseCallback.cancelled();
        }
    }

}