[ 
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: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to