ch.cyberduck.core.http.HttpConnectionPoolBuilder.java Source code

Java tutorial

Introduction

Here is the source code for ch.cyberduck.core.http.HttpConnectionPoolBuilder.java

Source

package ch.cyberduck.core.http;

/*
 * Copyright (c) 2002-2015 David Kocher. All rights reserved.
 * http://cyberduck.ch/
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * Bug fixes, suggestions and comments should be sent to feedback@cyberduck.ch
 */

import ch.cyberduck.core.Host;
import ch.cyberduck.core.PreferencesUseragentProvider;
import ch.cyberduck.core.Scheme;
import ch.cyberduck.core.TranscriptListener;
import ch.cyberduck.core.preferences.Preferences;
import ch.cyberduck.core.preferences.PreferencesFactory;
import ch.cyberduck.core.proxy.Proxy;
import ch.cyberduck.core.proxy.ProxyFinder;
import ch.cyberduck.core.proxy.ProxySocketFactory;
import ch.cyberduck.core.ssl.CustomTrustSSLProtocolSocketFactory;
import ch.cyberduck.core.ssl.ThreadLocalHostnameDelegatingTrustManager;
import ch.cyberduck.core.ssl.TrustManagerHostnameCallback;
import ch.cyberduck.core.ssl.X509KeyManager;
import ch.cyberduck.core.ssl.X509TrustManager;

import org.apache.http.HttpHost;
import org.apache.http.auth.AuthSchemeProvider;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.NoConnectionReuseStrategy;
import org.apache.http.impl.auth.BasicSchemeFactory;
import org.apache.http.impl.auth.DigestSchemeFactory;
import org.apache.http.impl.auth.KerberosSchemeFactory;
import org.apache.http.impl.auth.NTLMSchemeFactory;
import org.apache.http.impl.auth.SPNegoSchemeFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpCoreContext;
import org.apache.log4j.Logger;

import javax.net.SocketFactory;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.Charset;

public class HttpConnectionPoolBuilder {
    private static final Logger log = Logger.getLogger(HttpConnectionPoolBuilder.class);

    private Preferences preferences = PreferencesFactory.get();

    private ConnectionSocketFactory socketFactory;

    private ConnectionSocketFactory sslSocketFactory;

    private HttpClientBuilder builder = HttpClients.custom();

    private ProxyFinder proxyFinder;

    private Host host;

    public HttpConnectionPoolBuilder(final Host host, final ThreadLocalHostnameDelegatingTrustManager trust,
            final X509KeyManager key, final ProxyFinder proxy) {
        this(host, new PlainConnectionSocketFactory() {
            @Override
            public Socket createSocket(final HttpContext context) throws IOException {
                // Return socket factory with disabled support for HTTP tunneling as provided internally
                return new ProxySocketFactory(host.getProtocol(), new TrustManagerHostnameCallback() {
                    @Override
                    public String getTarget() {
                        return context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST).toString();
                    }
                }, proxy).disable(Proxy.Type.HTTP).disable(Proxy.Type.HTTPS).createSocket();
            }
        }, new SSLConnectionSocketFactory(new CustomTrustSSLProtocolSocketFactory(trust, key),
                new DisabledX509HostnameVerifier()) {
            @Override
            public Socket createSocket(final HttpContext context) throws IOException {
                return new ProxySocketFactory(host.getProtocol(), new TrustManagerHostnameCallback() {
                    @Override
                    public String getTarget() {
                        return context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST).toString();
                    }
                }, proxy).disable(Proxy.Type.HTTP).disable(Proxy.Type.HTTPS).createSocket();
            }

            @Override
            public Socket connectSocket(final int connectTimeout, final Socket socket, final HttpHost host,
                    final InetSocketAddress remoteAddress, final InetSocketAddress localAddress,
                    final HttpContext context) throws IOException {
                trust.setTarget(remoteAddress.getHostName());
                return super.connectSocket(connectTimeout, socket, host, remoteAddress, localAddress, context);
            }
        }, proxy);
    }

    protected HttpConnectionPoolBuilder(final Host host, final X509TrustManager trust, final X509KeyManager key,
            final ProxyFinder proxy, final SocketFactory socketFactory) {
        this(host, new PlainConnectionSocketFactory() {
            @Override
            public Socket createSocket(final HttpContext context) throws IOException {
                return socketFactory.createSocket();
            }
        }, new SSLConnectionSocketFactory(new CustomTrustSSLProtocolSocketFactory(trust, key),
                new DisabledX509HostnameVerifier()) {
            @Override
            public Socket createSocket(final HttpContext context) throws IOException {
                return socketFactory.createSocket();
            }

            @Override
            public Socket connectSocket(final int connectTimeout, final Socket socket, final HttpHost host,
                    final InetSocketAddress remoteAddress, final InetSocketAddress localAddress,
                    final HttpContext context) throws IOException {
                if (trust instanceof ThreadLocalHostnameDelegatingTrustManager) {
                    ((ThreadLocalHostnameDelegatingTrustManager) trust).setTarget(remoteAddress.getHostName());
                }
                return super.connectSocket(connectTimeout, socket, host, remoteAddress, localAddress, context);
            }
        }, proxy);
    }

    public HttpConnectionPoolBuilder(final Host host, final ConnectionSocketFactory socketFactory,
            final ConnectionSocketFactory sslSocketFactory, final ProxyFinder proxyFinder) {
        this.host = host;
        this.socketFactory = socketFactory;
        this.sslSocketFactory = sslSocketFactory;
        this.proxyFinder = proxyFinder;
    }

    public HttpClientBuilder build(final TranscriptListener listener) {
        // Use HTTP Connect proxy implementation provided here instead of
        // relying on internal proxy support in socket factory
        final Proxy proxy = proxyFinder.find(host);
        if (proxy.getType() == Proxy.Type.HTTP) {
            final HttpHost h = new HttpHost(proxy.getHostname(), proxy.getPort(), Scheme.http.name());
            if (log.isInfoEnabled()) {
                log.info(String.format("Setup proxy %s", h));
            }
            builder.setProxy(h);
        }
        if (proxy.getType() == Proxy.Type.HTTPS) {
            final HttpHost h = new HttpHost(proxy.getHostname(), proxy.getPort(), Scheme.https.name());
            if (log.isInfoEnabled()) {
                log.info(String.format("Setup proxy %s", h));
            }
            builder.setProxy(h);
        }
        builder.setUserAgent(new PreferencesUseragentProvider().get());
        final int timeout = preferences.getInteger("connection.timeout.seconds") * 1000;
        builder.setDefaultSocketConfig(SocketConfig.custom().setTcpNoDelay(true).setSoTimeout(timeout).build());
        builder.setDefaultRequestConfig(RequestConfig.custom().setRedirectsEnabled(true)
                // Disable use of Expect: Continue by default for all methods
                .setExpectContinueEnabled(false).setAuthenticationEnabled(true).setConnectTimeout(timeout)
                // Sets the timeout in milliseconds used when retrieving a connection from the ClientConnectionManager
                .setConnectionRequestTimeout(preferences.getInteger("http.manager.timeout"))
                .setSocketTimeout(timeout).build());
        final String encoding;
        if (null == host.getEncoding()) {
            encoding = preferences.getProperty("browser.charset.encoding");
        } else {
            encoding = host.getEncoding();
        }
        builder.setDefaultConnectionConfig(
                ConnectionConfig.custom().setBufferSize(preferences.getInteger("http.socket.buffer"))
                        .setCharset(Charset.forName(encoding)).build());
        if (preferences.getBoolean("http.connections.reuse")) {
            builder.setConnectionReuseStrategy(new DefaultConnectionReuseStrategy());
        } else {
            builder.setConnectionReuseStrategy(new NoConnectionReuseStrategy());
        }
        builder.setRetryHandler(
                new ExtendedHttpRequestRetryHandler(preferences.getInteger("http.connections.retry")));
        if (!preferences.getBoolean("http.compression.enable")) {
            builder.disableContentCompression();
        }
        builder.setRequestExecutor(new LoggingHttpRequestExecutor(listener));
        // Always register HTTP for possible use with proxy. Contains a number of protocol properties such as the
        // default port and the socket factory to be used to create the java.net.Socket instances for the given protocol
        builder.setConnectionManager(this.pool(this.registry().build()));
        builder.setDefaultAuthSchemeRegistry(RegistryBuilder.<AuthSchemeProvider>create()
                .register(AuthSchemes.BASIC,
                        new BasicSchemeFactory(
                                Charset.forName(preferences.getProperty("http.credentials.charset"))))
                .register(AuthSchemes.DIGEST,
                        new DigestSchemeFactory(
                                Charset.forName(preferences.getProperty("http.credentials.charset"))))
                .register(AuthSchemes.NTLM, new NTLMSchemeFactory())
                .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory())
                .register(AuthSchemes.KERBEROS, new KerberosSchemeFactory()).build());
        return builder;
    }

    protected RegistryBuilder<ConnectionSocketFactory> registry() {
        return RegistryBuilder.<ConnectionSocketFactory>create().register(Scheme.http.toString(), socketFactory)
                .register(Scheme.https.toString(), sslSocketFactory);
    }

    protected PoolingHttpClientConnectionManager pool(final Registry<ConnectionSocketFactory> registry) {
        if (log.isDebugEnabled()) {
            log.debug(String.format("Setup connection pool with registry %s", registry));
        }
        final PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(registry);
        manager.setMaxTotal(preferences.getInteger("http.connections.total"));
        manager.setDefaultMaxPerRoute(preferences.getInteger("http.connections.route"));
        manager.setValidateAfterInactivity(5000);
        return manager;
    }
}