hc-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Ivan Shcheklein (JIRA)" <j...@apache.org>
Subject [jira] [Commented] (HTTPCLIENT-1522) SNI support
Date Tue, 17 Feb 2015 21:30:12 GMT

    [ https://issues.apache.org/jira/browse/HTTPCLIENT-1522?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14324923#comment-14324923
] 

Ivan Shcheklein commented on HTTPCLIENT-1522:
---------------------------------------------

This is not a bug, but it can be really useful to make it easier to retry after the SNI problem.
Let's face it - we need http client to work with real world, not with some rfc-perfect illusion
:)

The easiest way I came up with is the following:

{code:java}
public class CustomSSLSocketFactory extends SSLConnectionSocketFactory {

    private final SocketConfig socketConfig;
    private final String ENABLE_SNI = "__enable_sni__";

    /*
     * Implement any constructor you need for your particular application -
     * SSLConnectionSocketFactory has many variants
     */
    public CustomSSLSocketFactory(final SocketConfig sc, final SSLContext sslContext, final
HostnameVerifier verifier) {
        super(sslContext, verifier);
        this.socketConfig = sc;
    }

    /*
     * Socket initialisation code is from DefaultHttpClientConnectionOperator::connect
     * Unfortunately we need this to create a new socket on a retry.
     */
    private Socket createSocketCustom(final HttpContext context) throws IOException {
        Socket sock = super.createSocket(context);
        sock.setSoTimeout(socketConfig.getSoTimeout());
        sock.setReuseAddress(socketConfig.isSoReuseAddress());
        sock.setTcpNoDelay(socketConfig.isTcpNoDelay());
        sock.setKeepAlive(socketConfig.isSoKeepAlive());
        final int linger = socketConfig.getSoLinger();
        if (linger >= 0) {
            sock.setSoLinger(true, linger);
        }
        return sock;
    }

    private Socket connectSocketSni(
            final int connectTimeout,
            final Socket socket,
            final HttpHost host,
            final InetSocketAddress remoteAddress,
            final InetSocketAddress localAddress,
            final HttpContext context,
            final boolean enableSni) throws IOException {
        try {
            return super.connectSocket(connectTimeout, socket, host, remoteAddress, localAddress,
context);
        } catch (SSLProtocolException e) {
            if (enableSni && e.getMessage() != null && e.getMessage().equals("handshake
alert:  unrecognized_name")) {
                TimesLoggers.httpworker.warn("Server received saw wrong SNI host, retrying
without SNI, host: " + host);
                context.setAttribute(ENABLE_SNI, false);
                // We need to create a new socket to retry (the first one is closed after
IOException)
                // Is there a clean way to do this?
                return connectSocketSni(connectTimeout, createSocketCustom(context), host,
remoteAddress, localAddress, context, false);
            } else {
                throw e;
            }
        }
    }

    @Override
    public Socket connectSocket(
              final int connectTimeout,
              final Socket socket,
              final HttpHost host,
              final InetSocketAddress remoteAddress,
              final InetSocketAddress localAddress,
              final HttpContext context) throws IOException {
        context.setAttribute(ENABLE_SNI, true);
        return this.connectSocketSni(connectTimeout, socket, host, remoteAddress, localAddress,
context, true);
    }

    @Override
    public Socket createLayeredSocket(
            final Socket socket,
            final String target,
            final int port,
            final HttpContext context) throws IOException {
        Boolean enableSniValue = (Boolean) context.getAttribute(ENABLE_SNI);
        boolean enableSni = enableSniValue == null || enableSniValue;
        return super.createLayeredSocket(socket, enableSni ? target : "", port, context);
    }
}
{code}

The main problem with it is that we need to create a new socket on to retry request without
SNI and sockets are created and initialised in the DefaultHttpClientConnectionOperator::connect.
There is no way to call the default, SocketConfig-aware factory to get an unconnected socket.

Probably it's not a bug, but it looks like a problem in socket creation architecture. Probably
it should be located in one place to be easily accessed by descendants.

> SNI support
> -----------
>
>                 Key: HTTPCLIENT-1522
>                 URL: https://issues.apache.org/jira/browse/HTTPCLIENT-1522
>             Project: HttpComponents HttpClient
>          Issue Type: Bug
>          Components: HttpClient
>    Affects Versions: 4.3.4
>            Reporter: Igor
>
> Running HttpRequest to https://touchpoint.tealeaveshealth.com/consume
> causes an error
> {code}
> javax.net.ssl.SSLProtocolException: handshake alert:  unrecognized_name
> {code}
> to see how to fix, you can open *http://stackoverflow.com/questions/7615645/ssl-handshake-alert-unrecognized-name-error-since-upgrade-to-java-1-7-0*



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@hc.apache.org
For additional commands, e-mail: dev-help@hc.apache.org


Mime
View raw message