package org.littleshoot.proxy.mitm.example.handshakeFailesWithUnrecognizedName; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.HttpClientCodec; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslHandshakeCompletionEvent; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import java.io.File; import java.net.URI; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import de.ganskef.test.Client; /** * An example client with handling for reconnect after a handshake failure * unrecognized_name occurs. * * It works, but... Restarting the event loop group on unrecognized name which * is a sledge hammer solution. */ public class RestartClient extends Client { private static final Logger LOG = LoggerFactory .getLogger(RestartClient.class); private SslContext sslCtx; private String host; private int port; private String url; private boolean unrecognizedName; public RestartClient() { this(false); } public RestartClient(boolean unrecognizedName) { this.unrecognizedName = unrecognizedName; } public static void main(String[] args) throws Exception { new RestartClient().get("https://wiki.gnome.org/"); } @Override public File get(final String url) throws Exception { this.url = url; URI uri = new URI(url); String scheme = uri.getScheme() == null ? "http" : uri.getScheme(); host = uri.getHost() == null ? "127.0.0.1" : uri.getHost(); if (uri.getPort() == -1) { if ("http".equalsIgnoreCase(scheme)) { port = 80; } else if ("https".equalsIgnoreCase(scheme)) { port = 443; } else { port = -1; } } else { port = uri.getPort(); } if (!"http".equalsIgnoreCase(scheme) && !"https".equalsIgnoreCase(scheme)) { LOG.warn("Only HTTP(S) is supported."); return null; } final boolean ssl = "https".equalsIgnoreCase(scheme); if (ssl) { sslCtx = SslContext .newClientContext(InsecureTrustManagerFactory.INSTANCE); } else { sslCtx = null; } final EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group); b.channel(NioSocketChannel.class); b.handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) { ChannelPipeline p = ch.pipeline(); if (sslCtx != null) { if (unrecognizedName) { p.addLast(sslCtx.newHandler(ch.alloc())); } else { p.addLast(sslCtx.newHandler(ch.alloc(), host, port)); } } p.addLast(new HttpClientCodec()); p.addLast(new HandshakeClientHandler()); } }); Channel ch = b.connect(host, port).sync().channel(); HttpRequest request = new DefaultFullHttpRequest( HttpVersion.HTTP_1_1, HttpMethod.GET, url); request.headers().set(HttpHeaders.Names.HOST, host); request.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE); ch.writeAndFlush(request); ch.closeFuture().sync(); } finally { group.shutdownGracefully(); } return null; } class HandshakeClientHandler extends SimpleChannelInboundHandler<Object> { private boolean unrecognizedName; @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { LOG.info(">>> userEventTriggered " + evt); if (evt instanceof SslHandshakeCompletionEvent) { SslHandshakeCompletionEvent hce = (SslHandshakeCompletionEvent) evt; if (!hce.isSuccess() && hce.cause().getMessage() .contains("unrecognized_name")) { LOG.info(">>> unrecognized_name"); ctx.close(); unrecognizedName = true; return; } } super.userEventTriggered(ctx, evt); } @Override public void handlerAdded(ChannelHandlerContext ctx) { LOG.info(">>> handlerAdded"); } @Override public void channelActive(ChannelHandlerContext ctx) { LOG.info(">>> channelActive"); } @Override public void channelInactive(ChannelHandlerContext ctx) { LOG.info(">>> channelInactive Client disconnected!"); } @Override public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { LOG.info(">>> channelUnregistered"); if (unrecognizedName) { LOG.info(">>> unrecognizedName retry"); new RestartClient(unrecognizedName).get(url); } } @Override public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { LOG.info(">>> channelRead0"); LOG.info(String.valueOf(msg)); if (msg instanceof LastHttpContent) { LOG.info(">>> close sucessfully"); ctx.close(); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { LOG.warn(">>> exceptionCaught"); if (unrecognizedName) { return; // ignore this exception } cause.printStackTrace(); ctx.close(); } } }