All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.niws.client.http.RestClient Maven / Gradle / Ivy

There is a newer version: 2.7.18
Show newest version
/*
*
* Copyright 2013 Netflix, 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.netflix.niws.client.http;

import java.io.File;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.security.KeyStore;
import java.util.Collection;
import java.util.Map;

import org.apache.http.HttpHost;
import org.apache.http.client.HttpClient;
import org.apache.http.client.UserTokenHandler;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnRouteParams;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.scheme.SchemeSocketFactory;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.AbstractHttpClient;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HttpContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.netflix.client.AbstractLoadBalancerAwareClient;
import com.netflix.client.ClientException;
import com.netflix.client.ClientFactory;
import com.netflix.client.RequestSpecificRetryHandler;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.client.config.IClientConfig;
import com.netflix.client.config.IClientConfigKey;
import com.netflix.client.http.HttpRequest;
import com.netflix.client.http.HttpResponse;
import com.netflix.client.ssl.AbstractSslContextFactory;
import com.netflix.client.ssl.ClientSslSocketFactoryException;
import com.netflix.client.ssl.URLSslContextFactory;
import com.netflix.config.ConfigurationManager;
import com.netflix.config.DynamicIntProperty;
import com.netflix.config.DynamicPropertyFactory;
import com.netflix.http4.NFHttpClient;
import com.netflix.http4.NFHttpClientConstants;
import com.netflix.http4.NFHttpClientFactory;
import com.netflix.http4.NFHttpMethodRetryHandler;
import com.netflix.http4.ssl.KeyStoreAwareSocketFactory;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.util.Pair;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.WebResource.Builder;
import com.sun.jersey.client.apache4.ApacheHttpClient4;
import com.sun.jersey.client.apache4.ApacheHttpClient4Handler;
import com.sun.jersey.client.apache4.config.ApacheHttpClient4Config;
import com.sun.jersey.client.apache4.config.DefaultApacheHttpClient4Config;

/**
 * A client that is essentially a wrapper around Jersey client. By default, it uses HttpClient for underlying HTTP communication.
 * Application can set its own Jersey client with this class, but doing so will void all client configurations set in {@link IClientConfig}.
 *
 * @deprecated Please see ribbon-rxnetty module for the Netty based client. 
 *
 * @author awang
 *
 */
@Deprecated
public class RestClient extends AbstractLoadBalancerAwareClient {

    private Client restClient;
    private HttpClient httpClient4;
    private IClientConfig ncc;
    private String restClientName;

    private boolean enableConnectionPoolCleanerTask = false;
    private DynamicIntProperty connIdleEvictTimeMilliSeconds;
    private int connectionCleanerRepeatInterval;
    private int maxConnectionsperHost;
    private int maxTotalConnections;
    private int connectionTimeout;
    private int readTimeout;
    private String proxyHost;
    private int proxyPort;
    private boolean isSecure;
    private boolean isHostnameValidationRequired;
    private boolean isClientAuthRequired;
    private boolean ignoreUserToken;
    private ApacheHttpClient4Config config;

    boolean bFollowRedirects = DefaultClientConfigImpl.DEFAULT_FOLLOW_REDIRECTS;

    private static final Logger logger = LoggerFactory.getLogger(RestClient.class);
    
    public RestClient() {
        super(null);
    }
    
    public RestClient(ILoadBalancer lb) {
        super(lb);
        restClientName = "default";
    }

    public RestClient(ILoadBalancer lb, IClientConfig ncc) {
        super(lb, ncc);
        initWithNiwsConfig(ncc);
    }

    public RestClient(IClientConfig ncc) {
        super(null, ncc);
        initWithNiwsConfig(ncc);
    }
    
    public RestClient(ILoadBalancer lb, Client jerseyClient) {
        super(lb);
        this.restClient = jerseyClient;
        this.setRetryHandler(new HttpClientLoadBalancerErrorHandler());
    }

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
        super.initWithNiwsConfig(clientConfig);
        this.ncc = clientConfig;
        this.restClientName = ncc.getClientName();
        this.isSecure = getBooleanFromConfig(ncc, CommonClientConfigKey.IsSecure, this.isSecure);
        this.isHostnameValidationRequired = getBooleanFromConfig(ncc, CommonClientConfigKey.IsHostnameValidationRequired, this.isHostnameValidationRequired);
        this.isClientAuthRequired = getBooleanFromConfig(ncc, CommonClientConfigKey.IsClientAuthRequired, this.isClientAuthRequired);
        this.bFollowRedirects = getBooleanFromConfig(ncc, CommonClientConfigKey.FollowRedirects, true);
        this.ignoreUserToken = getBooleanFromConfig(ncc, CommonClientConfigKey.IgnoreUserTokenInConnectionPoolForSecureClient, this.ignoreUserToken);

        this.config = new DefaultApacheHttpClient4Config();
        this.config.getProperties().put(
                ApacheHttpClient4Config.PROPERTY_CONNECT_TIMEOUT,
                Integer.parseInt(String.valueOf(ncc.getProperty(CommonClientConfigKey.ConnectTimeout))));
        this.config.getProperties().put(
                ApacheHttpClient4Config.PROPERTY_READ_TIMEOUT,
                Integer.parseInt(String.valueOf(ncc.getProperty(CommonClientConfigKey.ReadTimeout))));

        this.restClient = apacheHttpClientSpecificInitialization();
        this.setRetryHandler(new HttpClientLoadBalancerErrorHandler(ncc));
    }

    protected Client apacheHttpClientSpecificInitialization() {
        httpClient4 = NFHttpClientFactory.getNamedNFHttpClient(restClientName, this.ncc, true);

        if (httpClient4 instanceof AbstractHttpClient) {
            // DONT use our NFHttpClient's default Retry Handler since we have
            // retry handling (same server/next server) in RestClient itself
            ((AbstractHttpClient) httpClient4).setHttpRequestRetryHandler(new NFHttpMethodRetryHandler(restClientName, 0, false, 0));
        } else {
            logger.warn("Unexpected error: Unable to disable NFHttpClient "
                    + "retry handler, this most likely will not cause an "
                    + "issue but probably should be looked at");
        }

        HttpParams httpClientParams = httpClient4.getParams();
        // initialize Connection Manager cleanup facility
        NFHttpClient nfHttpClient = (NFHttpClient) httpClient4;
        // should we enable connection cleanup for idle connections?
        try {
            enableConnectionPoolCleanerTask = Boolean.parseBoolean(ncc.getProperty(CommonClientConfigKey.ConnectionPoolCleanerTaskEnabled,
                    NFHttpClientConstants.DEFAULT_CONNECTIONIDLE_TIMETASK_ENABLED).toString());
            nfHttpClient.getConnPoolCleaner().setEnableConnectionPoolCleanerTask(enableConnectionPoolCleanerTask);
        } catch (Exception e1) {
            throw new IllegalArgumentException("Invalid value for property:"
                    + CommonClientConfigKey.ConnectionPoolCleanerTaskEnabled, e1);
        }
        if (enableConnectionPoolCleanerTask) {
            try {
                connectionCleanerRepeatInterval = Integer
                .parseInt(String.valueOf(ncc.getProperty(CommonClientConfigKey.ConnectionCleanerRepeatInterval,
                        NFHttpClientConstants.DEFAULT_CONNECTION_IDLE_TIMERTASK_REPEAT_IN_MSECS)));
                nfHttpClient.getConnPoolCleaner().setConnectionCleanerRepeatInterval(connectionCleanerRepeatInterval);
            } catch (Exception e1) {
                throw new IllegalArgumentException(
                        "Invalid value for property:"
                        + CommonClientConfigKey.ConnectionCleanerRepeatInterval, e1);
            }

            try {
                int iConnIdleEvictTimeMilliSeconds = Integer
                .parseInt(""
                        + ncc
                        .getProperty(CommonClientConfigKey.ConnIdleEvictTimeMilliSeconds,
                                NFHttpClientConstants.DEFAULT_CONNECTIONIDLE_TIME_IN_MSECS));
                connIdleEvictTimeMilliSeconds = DynamicPropertyFactory.getInstance().getIntProperty(
                        restClientName
                        + ".nfhttpclient.connIdleEvictTimeMilliSeconds",
                        iConnIdleEvictTimeMilliSeconds);
                nfHttpClient.setConnIdleEvictTimeMilliSeconds(connIdleEvictTimeMilliSeconds);
            } catch (Exception e1) {
                throw new IllegalArgumentException(
                        "Invalid value for property:"
                        + CommonClientConfigKey.ConnIdleEvictTimeMilliSeconds,
                        e1);
            }

            nfHttpClient.initConnectionCleanerTask();
        }

        try {
            maxConnectionsperHost = Integer
            .parseInt(""
                    + ncc
                    .getProperty(CommonClientConfigKey.MaxHttpConnectionsPerHost,
                            maxConnectionsperHost));
            ClientConnectionManager connMgr = httpClient4.getConnectionManager();
            if (connMgr instanceof ThreadSafeClientConnManager) {
                ((ThreadSafeClientConnManager) connMgr)
                .setDefaultMaxPerRoute(maxConnectionsperHost);
            }
        } catch (Exception e1) {
            throw new IllegalArgumentException("Invalid value for property:"
                    + CommonClientConfigKey.MaxHttpConnectionsPerHost, e1);
        }

        try {
            maxTotalConnections = Integer
            .parseInt(""
                    + ncc
                    .getProperty(CommonClientConfigKey.MaxTotalHttpConnections,
                            maxTotalConnections));
            ClientConnectionManager connMgr = httpClient4
            .getConnectionManager();
            if (connMgr instanceof ThreadSafeClientConnManager) {
                ((ThreadSafeClientConnManager) connMgr)
                .setMaxTotal(maxTotalConnections);
            }
        } catch (Exception e1) {
            throw new IllegalArgumentException("Invalid value for property:"
                    + CommonClientConfigKey.MaxTotalHttpConnections, e1);
        }

        try {
            connectionTimeout = Integer.parseInt(""
                    + ncc.getProperty(CommonClientConfigKey.ConnectTimeout,
                            connectionTimeout));
            HttpConnectionParams.setConnectionTimeout(httpClientParams,
                    connectionTimeout);
        } catch (Exception e1) {
            throw new IllegalArgumentException("Invalid value for property:"
                    + CommonClientConfigKey.ConnectTimeout, e1);
        }

        try {
            readTimeout = Integer.parseInt(""
                    + ncc.getProperty(CommonClientConfigKey.ReadTimeout,
                            readTimeout));
            HttpConnectionParams.setSoTimeout(httpClientParams, readTimeout);
        } catch (Exception e1) {
            throw new IllegalArgumentException("Invalid value for property:"
                    + CommonClientConfigKey.ReadTimeout, e1);
        }

        // httpclient 4 seems to only have one buffer size controlling both
        // send/receive - so let's take the bigger of the two values and use
        // it as buffer size
        int bufferSize = Integer.MIN_VALUE;
        if (ncc.getProperty(CommonClientConfigKey.ReceiveBufferSize) != null) {
            try {
                bufferSize = Integer
                .parseInt(""
                        + ncc
                        .getProperty(CommonClientConfigKey.ReceiveBufferSize));
            } catch (Exception e) {
                throw new IllegalArgumentException(
                        "Invalid value for property:"
                        + CommonClientConfigKey.ReceiveBufferSize,
                        e);
            }
            if (ncc.getProperty(CommonClientConfigKey.SendBufferSize) != null) {
                try {
                    int sendBufferSize = Integer
                    .parseInt(""
                            + ncc
                            .getProperty(CommonClientConfigKey.SendBufferSize));
                    if (sendBufferSize > bufferSize) {
                        bufferSize = sendBufferSize;
                    }
                } catch (Exception e) {
                    throw new IllegalArgumentException(
                            "Invalid value for property:"
                            + CommonClientConfigKey.SendBufferSize,
                            e);
                }
            }
        }
        if (bufferSize != Integer.MIN_VALUE) {
            HttpConnectionParams.setSocketBufferSize(httpClientParams,
                    bufferSize);
        }

        if (ncc.getProperty(CommonClientConfigKey.StaleCheckingEnabled) != null) {
            try {
                HttpConnectionParams
                .setStaleCheckingEnabled(httpClientParams,
                        Boolean.parseBoolean(ncc.getProperty(CommonClientConfigKey.StaleCheckingEnabled, false).toString()));
            } catch (Exception e) {
                throw new IllegalArgumentException(
                        "Invalid value for property:"
                        + CommonClientConfigKey.StaleCheckingEnabled,
                        e);
            }
        }

        if (ncc.getProperty(CommonClientConfigKey.Linger) != null) {
            try {
                HttpConnectionParams.setLinger(httpClientParams,
                        Integer.parseInt(""
                                + ncc.getProperty(CommonClientConfigKey.Linger)));
            } catch (Exception e) {
                throw new IllegalArgumentException(
                        "Invalid value for property:"
                        + CommonClientConfigKey.Linger,
                        e);
            }
        }

        if (ncc.getProperty(CommonClientConfigKey.ProxyHost) != null) {
            try {
                proxyHost = (String) ncc
                .getProperty(CommonClientConfigKey.ProxyHost);
                proxyPort = Integer.parseInt(""
                        + ncc.getProperty(CommonClientConfigKey.ProxyPort));
                HttpHost proxy = new HttpHost(proxyHost, proxyPort);
                httpClient4
                .getParams()
                .setParameter(ConnRouteParams.DEFAULT_PROXY, proxy);
            } catch (Exception e) {
                throw new IllegalArgumentException(
                        "Invalid value for property:"
                        + CommonClientConfigKey.ProxyHost,
                        e);
            }
        }

        if (isSecure) {
            final URL trustStoreUrl = getResourceForOptionalProperty(CommonClientConfigKey.TrustStore);
            final URL keyStoreUrl = getResourceForOptionalProperty(CommonClientConfigKey.KeyStore);

        	final ClientConnectionManager currentManager = httpClient4.getConnectionManager();

            AbstractSslContextFactory abstractFactory = null;


            if (    // if client auth is required, need both a truststore and a keystore to warrant configuring
            		// if client is not is not required, we only need a keystore OR a truststore to warrant configuring
            		(isClientAuthRequired && (trustStoreUrl != null && keyStoreUrl != null))
            		||
            		(!isClientAuthRequired && (trustStoreUrl != null || keyStoreUrl != null))
            		) {

                try {
                	abstractFactory = new URLSslContextFactory(trustStoreUrl,
                            (String) ncc.getProperty(CommonClientConfigKey.TrustStorePassword),
                            keyStoreUrl,
                            (String) ncc.getProperty(CommonClientConfigKey.KeyStorePassword));

                } catch (ClientSslSocketFactoryException e) {
                    throw new IllegalArgumentException("Unable to configure custom secure socket factory", e);
                }
            }

        	KeyStoreAwareSocketFactory awareSocketFactory;
        	try {
        		awareSocketFactory = isHostnameValidationRequired ? new KeyStoreAwareSocketFactory(abstractFactory) :
        		    new KeyStoreAwareSocketFactory(abstractFactory, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        		currentManager.getSchemeRegistry().register(new Scheme(
                        "https",443, awareSocketFactory));

        	} catch (Exception e) {
        		throw new IllegalArgumentException("Unable to configure custom secure socket factory", e);
        	}
        }

        // Warning that if user tokens are used (i.e. ignoreUserToken == false) this may be prevent SSL connections from being
        // reused, which is generally not the intent for long-living proxy connections and the like.
        // See http://hc.apache.org/httpcomponents-client-ga/tutorial/html/advanced.html
        if (ignoreUserToken) {
            ((DefaultHttpClient) httpClient4).setUserTokenHandler(new UserTokenHandler() {
                @Override
                public Object getUserToken(HttpContext context) {
                    return null;
                }
            });
        }

        // custom SSL Factory handler
        String customSSLFactoryClassName = (String) ncc.getProperty(CommonClientConfigKey.CustomSSLSocketFactoryClassName);

        if(customSSLFactoryClassName != null){

            try{
                SSLSocketFactory customSocketFactory = (SSLSocketFactory) ClientFactory.instantiateInstanceWithClientConfig(customSSLFactoryClassName, ncc);

                httpClient4.getConnectionManager().getSchemeRegistry().register(new Scheme(
                        "https",443, customSocketFactory));

            }catch(Exception e){
                throw new IllegalArgumentException("Invalid value associated with property:"
                        + CommonClientConfigKey.CustomSSLSocketFactoryClassName, e);
            }
        }

        ApacheHttpClient4Handler handler = new ApacheHttpClient4Handler(httpClient4, new BasicCookieStore(), false);

        return new ApacheHttpClient4(handler, config);
    }

    public void resetSSLSocketFactory(AbstractSslContextFactory abstractContextFactory){

    	try {

    		KeyStoreAwareSocketFactory awareSocketFactory = isHostnameValidationRequired ? new KeyStoreAwareSocketFactory(abstractContextFactory) :
                new KeyStoreAwareSocketFactory(abstractContextFactory, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
    		httpClient4.getConnectionManager().getSchemeRegistry().register(new Scheme(
                    "https",443, awareSocketFactory));

    	} catch (Exception e) {
    		throw new IllegalArgumentException("Unable to configure custom secure socket factory", e);
    	}
    }

    public KeyStore getKeyStore(){

    	SchemeRegistry registry = httpClient4.getConnectionManager().getSchemeRegistry();

    	if(! registry.getSchemeNames().contains("https")){
    		throw new IllegalStateException("Registry does not include an 'https' entry.");
    	}

    	SchemeSocketFactory awareSocketFactory = httpClient4.getConnectionManager().getSchemeRegistry().getScheme("https").getSchemeSocketFactory();

    	if(awareSocketFactory instanceof KeyStoreAwareSocketFactory){
    		return ((KeyStoreAwareSocketFactory) awareSocketFactory).getKeyStore();
    	}else{
    		throw new IllegalStateException("Cannot extract keystore from scheme socket factory of type: " + awareSocketFactory.getClass().getName());
    	}
    }


    public static URL getResource(String resourceName)
    {
        URL url = null;
        // attempt to load from the context classpath
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        if (loader != null) {
            url = loader.getResource(resourceName);
        }
        if (url == null) {
            // attempt to load from the system classpath
            url = ClassLoader.getSystemResource(resourceName);
        }
        if (url == null) {
            // attempt to load from the system classpath
            url = ConfigurationManager.class.getResource(resourceName);
        }
        if (url == null) {
            // attempt to load from the system classpath
            url = ConfigurationManager.class.getClassLoader().getResource(resourceName);
        }
        if (url == null) {
            try {
                resourceName = URLDecoder.decode(resourceName, "UTF-8");
                url = (new File(resourceName)).toURI().toURL();
            } catch (Exception e) {
            	logger.error("Problem loading resource", e);
            }
        }
        return url;
    }

    public Client getJerseyClient() {
        return restClient;
    }

    public void setJerseyClient(Client c) {
        restClient = c;
    }

    private URL getResourceForOptionalProperty(final IClientConfigKey configKey) {
        final String propValue = (String) ncc.getProperty(configKey);
        URL result = null;

        if (propValue != null) {
            result = getResource(propValue);
            if (result == null) {
                throw new IllegalArgumentException("No resource found for " + configKey + ": "
                        + propValue);
            }
        }
        return result;
    }

    public HttpResponse execute(HttpRequest task) throws Exception  {
        return execute(task, null);
    }
    
    @Override
    public HttpResponse execute(HttpRequest task, IClientConfig requestConfig) throws Exception {
        IClientConfig config = (requestConfig == null) ? task.getOverrideConfig() : requestConfig;
        return execute(task.getVerb(), task.getUri(),
                task.getHeaders(), task.getQueryParams(), config, task.getEntity());
    }


    private boolean getBooleanFromConfig(IClientConfig overriddenClientConfig, IClientConfigKey key, boolean defaultValue){
    	if(overriddenClientConfig != null && overriddenClientConfig.containsProperty(key)){
    		defaultValue = Boolean.parseBoolean(overriddenClientConfig.getProperty(key).toString());
    	}
    	return defaultValue;
    }

    @Override
    protected int getDefaultPortFromScheme(String scheme) {
        int port = super.getDefaultPortFromScheme(scheme);
        if (port < 0) {
            return 80;
        } else {
            return port;
        }
    }

    @Override
    protected Pair deriveSchemeAndPortFromPartialUri(URI uri) {
        boolean isSecure = getBooleanFromConfig(ncc, CommonClientConfigKey.IsSecure, this.isSecure);
        String scheme = uri.getScheme();
        if (scheme != null) {
            isSecure = 	scheme.equalsIgnoreCase("https");
        }
        int port = uri.getPort();
        if (port < 0 && !isSecure){
            port = 80;
        } else if (port < 0 && isSecure){
            port = 443;
        }
        if (scheme == null){
            if (isSecure) {
                scheme = "https";
            } else {
                scheme = "http";
            }
        }
        return new Pair(scheme, port);
    }

    private HttpResponse execute(HttpRequest.Verb verb, URI uri,
            Map> headers, Map> params,
            IClientConfig overriddenClientConfig, Object requestEntity) throws Exception {
        HttpClientResponse thisResponse = null;
        boolean bbFollowRedirects = bFollowRedirects;
        // read overriden props
        if (overriddenClientConfig != null
        		// set whether we should auto follow redirects
        		&& overriddenClientConfig.getProperty(CommonClientConfigKey.FollowRedirects)!=null){
        	// use default directive from overall config
        	Boolean followRedirects = Boolean.valueOf(""+overriddenClientConfig.getProperty(CommonClientConfigKey.FollowRedirects, bFollowRedirects));
        	bbFollowRedirects = followRedirects.booleanValue();
        }
        restClient.setFollowRedirects(bbFollowRedirects);

        if (logger.isDebugEnabled()) {
            logger.debug("RestClient sending new Request(" + verb
                    + ": ) " + uri);
        }


        WebResource xResource = restClient.resource(uri.toString());
        if (params != null) {
            for (Map.Entry> entry: params.entrySet()) {
                String name = entry.getKey();
                for (String value: entry.getValue()) {
                    xResource = xResource.queryParam(name, value);
                }
            }
        }
        ClientResponse jerseyResponse;

        Builder b = xResource.getRequestBuilder();

        if (headers != null) {
            for (Map.Entry> entry: headers.entrySet()) {
                String name = entry.getKey();
                for (String value: entry.getValue()) {
                    b = b.header(name, value);
                }
            }
        }
        Object entity = requestEntity;
        
        switch (verb) {
        case GET:
            jerseyResponse = b.get(ClientResponse.class);
            break;
        case POST:
            jerseyResponse = b.post(ClientResponse.class, entity);
            break;
        case PUT:
            jerseyResponse = b.put(ClientResponse.class, entity);
            break;
        case DELETE:
            jerseyResponse = b.delete(ClientResponse.class);
            break;
        case HEAD:
            jerseyResponse = b.head();
            break;
        case OPTIONS:
            jerseyResponse = b.options(ClientResponse.class);
            break;
        default:
            throw new ClientException(
                    ClientException.ErrorType.GENERAL,
                    "You have to one of the REST verbs such as GET, POST etc.");
        }

        thisResponse = new HttpClientResponse(jerseyResponse, uri, overriddenClientConfig);
        if (thisResponse.getStatus() == 503){
            thisResponse.close();
            throw new ClientException(ClientException.ErrorType.SERVER_THROTTLED);
        }
        return thisResponse;
    }

    @Override
    protected boolean isRetriableException(Throwable e) {
        if (e instanceof ClientException
                && ((ClientException)e).getErrorType() == ClientException.ErrorType.SERVER_THROTTLED){
            return false;
        }
        boolean shouldRetry = isConnectException(e) || isSocketException(e);
        return shouldRetry;
    }

    @Override
    protected boolean isCircuitBreakerException(Throwable e) {
        if (e instanceof ClientException) {
            ClientException clientException = (ClientException) e;
            if (clientException.getErrorType() == ClientException.ErrorType.SERVER_THROTTLED) {
                return true;
            }
        }
        return isConnectException(e) || isSocketException(e);
    }

    private static boolean isSocketException(Throwable e) {
        int levelCount = 0;
        while (e != null && levelCount < 10) {
            if ((e instanceof SocketException) || (e instanceof SocketTimeoutException)) {
                return true;
            }
            e = e.getCause();
            levelCount++;
        }
        return false;

    }

    private static boolean isConnectException(Throwable e) {
        int levelCount = 0;
        while (e != null && levelCount < 10) {
            if ((e instanceof SocketException)
                    || ((e instanceof org.apache.http.conn.ConnectTimeoutException)
                            && !(e instanceof org.apache.http.conn.ConnectionPoolTimeoutException))) {
                return true;
            }
            e = e.getCause();
            levelCount++;
        }
        return false;
    }

	@Override
	protected Pair deriveHostAndPortFromVipAddress(String vipAddress)
			throws URISyntaxException, ClientException {
		if (!vipAddress.contains("http")) {
			vipAddress = "http://" + vipAddress;
		}
		return super.deriveHostAndPortFromVipAddress(vipAddress);
	}

    @Override
    public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
            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() != HttpRequest.Verb.GET) {
            return new RequestSpecificRetryHandler(true, false, this.getRetryHandler(), requestConfig);
        } else {
            return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(), requestConfig);
        } 
    }
	
	public void shutdown() {
	    ILoadBalancer lb = this.getLoadBalancer();
	    if (lb instanceof BaseLoadBalancer) {
	        ((BaseLoadBalancer) lb).shutdown();
	    }
	    NFHttpClientFactory.shutdownNFHttpClient(restClientName);
	}
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy