package io.vertx.core.net.impl; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.handler.proxy.HttpProxyHandler; import io.netty.handler.proxy.ProxyConnectionEvent; import io.netty.handler.proxy.ProxyHandler; import io.netty.handler.proxy.Socks4ProxyHandler; import io.netty.handler.proxy.Socks5ProxyHandler; import io.netty.resolver.NoopAddressResolverGroup; import io.vertx.core.AsyncResult; import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.impl.VertxInternal; import io.vertx.core.net.ProxyOptions; import io.vertx.core.net.ProxyType; import java.net.InetAddress; import java.net.InetSocketAddress; /** * A channel provider that connects via a Proxy : HTTP or SOCKS * * @author <a href="mailto:julien@julienviet.com">Julien Viet</a> */ public class ProxyChannelProvider extends ChannelProvider { public static final ChannelProvider INSTANCE = new ProxyChannelProvider(); private ProxyChannelProvider() { } @Override public void connect(VertxInternal vertx, Bootstrap bootstrap, ProxyOptions options, String host, int port, Handler<Channel> channelInitializer, Handler<AsyncResult<Channel>> channelHandler) { final String proxyHost = options.getHost(); final int proxyPort = options.getPort(); final String proxyUsername = options.getUsername(); final String proxyPassword = options.getPassword(); final ProxyType proxyType = options.getType(); vertx.resolveAddress(proxyHost, dnsRes -> { if (dnsRes.succeeded()) { InetAddress address = dnsRes.result(); InetSocketAddress proxyAddr = new InetSocketAddress(address, proxyPort); ProxyHandler proxy; switch (proxyType) { default: case HTTP: proxy = proxyUsername != null && proxyPassword != null ? new HttpProxyHandler(proxyAddr, proxyUsername, proxyPassword) : new HttpProxyHandler(proxyAddr); break; case SOCKS5: proxy = proxyUsername != null && proxyPassword != null ? new Socks5ProxyHandler(proxyAddr, proxyUsername, proxyPassword) : new Socks5ProxyHandler(proxyAddr); break; case SOCKS4: // SOCKS4 only supports a username and could authenticate the user via Ident proxy = proxyUsername != null ? new Socks4ProxyHandler(proxyAddr, proxyUsername) : new Socks4ProxyHandler(proxyAddr); break; } bootstrap.resolver(NoopAddressResolverGroup.INSTANCE); InetSocketAddress targetAddress = InetSocketAddress.createUnresolved(host, port); bootstrap.handler(new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addFirst("proxy", proxy); pipeline.addLast(new ChannelInboundHandlerAdapter() { @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof ProxyConnectionEvent) { pipeline.remove(proxy); pipeline.remove(this); channelInitializer.handle(ch); channelHandler.handle(Future.succeededFuture(ch)); } ctx.fireUserEventTriggered(evt); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { channelHandler.handle(Future.failedFuture(cause)); } }); } }); ChannelFuture future = bootstrap.connect(targetAddress); future.addListener(res -> { if (!res.isSuccess()) { channelHandler.handle(Future.failedFuture(res.cause())); } }); } else { channelHandler.handle(Future.failedFuture(dnsRes.cause())); } }); } }