From 352fc9b8904d0e99eca1d307490e91c64ca88ce5 Mon Sep 17 00:00:00 2001
From: cowwen <cowwen.lin@watsons.com.cn>
Date: Sat, 9 Mar 2019 14:54:14 +0800
Subject: [PATCH 1/3] socks5 proxy resolve destination domain

---
 .../netty/channel/ChannelManager.java         |  7 +++++
 .../netty/request/NettyRequestSender.java     | 12 ++++++++-
 .../asynchttpclient/proxy/ProxyServer.java    | 26 ++++++++++++++++++-
 3 files changed, 43 insertions(+), 2 deletions(-)

diff --git a/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java b/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java
index 6a5ed05972..4ca3a8039b 100755
--- a/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java
+++ b/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java
@@ -33,6 +33,8 @@
 import io.netty.handler.proxy.Socks5ProxyHandler;
 import io.netty.handler.ssl.SslHandler;
 import io.netty.handler.stream.ChunkedWriteHandler;
+import io.netty.resolver.AddressResolver;
+import io.netty.resolver.AddressResolverGroup;
 import io.netty.resolver.NameResolver;
 import io.netty.util.Timer;
 import io.netty.util.concurrent.*;
@@ -56,6 +58,7 @@
 import javax.net.ssl.SSLException;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
+import java.net.SocketAddress;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.concurrent.ThreadFactory;
@@ -430,6 +433,10 @@ protected void initChannel(Channel channel) throws Exception {
               channel.pipeline().addFirst(SOCKS_HANDLER, socksProxyHandler);
             }
           });
+          AddressResolver<SocketAddress> addressResolver = proxy.getAddressResolver();
+          if (addressResolver != null) {
+            socksBootstrap.resolver((AddressResolverGroup<?>) addressResolver);
+          }
           promise.setSuccess(socksBootstrap);
 
         } else {
diff --git a/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestSender.java b/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestSender.java
index 03255731f7..8563773d12 100755
--- a/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestSender.java
+++ b/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestSender.java
@@ -37,6 +37,7 @@
 import org.asynchttpclient.netty.channel.*;
 import org.asynchttpclient.netty.timeout.TimeoutsHolder;
 import org.asynchttpclient.proxy.ProxyServer;
+import org.asynchttpclient.proxy.ProxyType;
 import org.asynchttpclient.resolver.RequestHostnameResolver;
 import org.asynchttpclient.uri.Uri;
 import org.asynchttpclient.ws.WebSocketUpgradeHandler;
@@ -46,6 +47,7 @@
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
+import java.util.ArrayList;
 import java.util.List;
 
 import static io.netty.handler.codec.http.HttpHeaderNames.EXPECT;
@@ -342,7 +344,15 @@ private <T> Future<List<InetSocketAddress>> resolveAddresses(Request request,
       InetSocketAddress unresolvedRemoteAddress = InetSocketAddress.createUnresolved(proxy.getHost(), port);
       scheduleRequestTimeout(future, unresolvedRemoteAddress);
       return RequestHostnameResolver.INSTANCE.resolve(request.getNameResolver(), unresolvedRemoteAddress, asyncHandler);
-
+    } else if (proxy != null && proxy.isResolveDomain() && ProxyType.SOCKS_V5 == proxy.getProxyType()) {
+      // resolve domain in socks 5 server
+      int port = uri.getExplicitPort();
+      InetSocketAddress unresolvedRemoteAddress = InetSocketAddress.createUnresolved(uri.getHost(), port);
+      final Promise<List<InetSocketAddress>> unResolvedPromise = ImmediateEventExecutor.INSTANCE.newPromise();
+      List<InetSocketAddress> unresolvedLiWrapper = new ArrayList<>(1);
+      unresolvedLiWrapper.add(unresolvedRemoteAddress);
+      unResolvedPromise.trySuccess(unresolvedLiWrapper);
+      return unResolvedPromise;
     } else {
       int port = uri.getExplicitPort();
 
diff --git a/client/src/main/java/org/asynchttpclient/proxy/ProxyServer.java b/client/src/main/java/org/asynchttpclient/proxy/ProxyServer.java
index 13c33590be..42793b821c 100644
--- a/client/src/main/java/org/asynchttpclient/proxy/ProxyServer.java
+++ b/client/src/main/java/org/asynchttpclient/proxy/ProxyServer.java
@@ -16,8 +16,10 @@
  */
 package org.asynchttpclient.proxy;
 
+import io.netty.resolver.AddressResolver;
 import org.asynchttpclient.Realm;
 
+import java.net.SocketAddress;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -36,6 +38,8 @@ public class ProxyServer {
   private final Realm realm;
   private final List<String> nonProxyHosts;
   private final ProxyType proxyType;
+  private AddressResolver<SocketAddress> addressResolver;
+  // server can resolve domain in socks 5
 
   public ProxyServer(String host, int port, int securedPort, Realm realm, List<String> nonProxyHosts,
                      ProxyType proxyType) {
@@ -47,6 +51,12 @@ public ProxyServer(String host, int port, int securedPort, Realm realm, List<Str
     this.proxyType = proxyType;
   }
 
+  public ProxyServer(String host, int port, int securedPort, Realm realm, List<String> nonProxyHosts,
+                     ProxyType proxyType, AddressResolver<SocketAddress> addressResolver) {
+    this(host, port, securedPort, realm, nonProxyHosts, proxyType);
+    this.addressResolver = addressResolver;
+  }
+
   public String getHost() {
     return host;
   }
@@ -71,6 +81,14 @@ public ProxyType getProxyType() {
     return proxyType;
   }
 
+  public AddressResolver<SocketAddress> getAddressResolver() {
+    return addressResolver;
+  }
+
+  public boolean isResolveDomain() {
+    return addressResolver != null;
+  }
+
   /**
    * Checks whether proxy should be used according to nonProxyHosts settings of
    * it, or we want to go directly to target host. If <code>null</code> proxy is
@@ -118,6 +136,7 @@ public static class Builder {
     private Realm realm;
     private List<String> nonProxyHosts;
     private ProxyType proxyType;
+    private AddressResolver<SocketAddress> addressResolver;
 
     public Builder(String host, int port) {
       this.host = host;
@@ -157,11 +176,16 @@ public Builder setProxyType(ProxyType proxyType) {
       return this;
     }
 
+    public Builder setAddressResolver(AddressResolver<SocketAddress> addressResolver) {
+      this.addressResolver = addressResolver;
+      return this;
+    }
+
     public ProxyServer build() {
       List<String> nonProxyHosts = this.nonProxyHosts != null ? Collections.unmodifiableList(this.nonProxyHosts)
               : Collections.emptyList();
       ProxyType proxyType = this.proxyType != null ? this.proxyType : ProxyType.HTTP;
-      return new ProxyServer(host, port, securedPort, realm, nonProxyHosts, proxyType);
+      return new ProxyServer(host, port, securedPort, realm, nonProxyHosts, proxyType, this.addressResolver);
     }
   }
 }

From 129b9e6914e0688b3ca8790eb63a49b0fe46535d Mon Sep 17 00:00:00 2001
From: cowwen <cowwen.lin@watsons.com.cn>
Date: Mon, 11 Mar 2019 11:36:47 +0800
Subject: [PATCH 2/3] websocket proxy in http is need...

---
 .../netty/channel/ChannelManager.java         | 118 ++++++++++--------
 .../netty/request/NettyRequestSender.java     |   1 +
 .../asynchttpclient/proxy/ProxyServer.java    |  23 ++--
 3 files changed, 80 insertions(+), 62 deletions(-)

diff --git a/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java b/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java
index 4ca3a8039b..c1c6a883d1 100755
--- a/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java
+++ b/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java
@@ -33,7 +33,6 @@
 import io.netty.handler.proxy.Socks5ProxyHandler;
 import io.netty.handler.ssl.SslHandler;
 import io.netty.handler.stream.ChunkedWriteHandler;
-import io.netty.resolver.AddressResolver;
 import io.netty.resolver.AddressResolverGroup;
 import io.netty.resolver.NameResolver;
 import io.netty.util.Timer;
@@ -58,7 +57,6 @@
 import javax.net.ssl.SSLException;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
-import java.net.SocketAddress;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.concurrent.ThreadFactory;
@@ -395,59 +393,79 @@ public Future<Bootstrap> getBootstrap(Uri uri, NameResolver<InetAddress> nameRes
 
     final Promise<Bootstrap> promise = ImmediateEventExecutor.INSTANCE.newPromise();
 
-    if (uri.isWebSocket() && proxy == null) {
-      return promise.setSuccess(wsBootstrap);
-
-    } else if (proxy != null && proxy.getProxyType().isSocks()) {
-      Bootstrap socksBootstrap = httpBootstrap.clone();
-      ChannelHandler httpBootstrapHandler = socksBootstrap.config().handler();
-
-      nameResolver.resolve(proxy.getHost()).addListener((Future<InetAddress> whenProxyAddress) -> {
-        if (whenProxyAddress.isSuccess()) {
-          socksBootstrap.handler(new ChannelInitializer<Channel>() {
-            @Override
-            public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
-              httpBootstrapHandler.handlerAdded(ctx);
-              super.handlerAdded(ctx);
-            }
-
-            @Override
-            protected void initChannel(Channel channel) throws Exception {
-              InetSocketAddress proxyAddress = new InetSocketAddress(whenProxyAddress.get(), proxy.getPort());
-              Realm realm = proxy.getRealm();
-              String username = realm != null ? realm.getPrincipal() : null;
-              String password = realm != null ? realm.getPassword() : null;
-              ProxyHandler socksProxyHandler;
-              switch (proxy.getProxyType()) {
-                case SOCKS_V4:
-                  socksProxyHandler = new Socks4ProxyHandler(proxyAddress, username);
-                  break;
-
-                case SOCKS_V5:
-                  socksProxyHandler = new Socks5ProxyHandler(proxyAddress, username, password);
-                  break;
-
-                default:
-                  throw new IllegalArgumentException("Only SOCKS4 and SOCKS5 supported at the moment.");
-              }
-              channel.pipeline().addFirst(SOCKS_HANDLER, socksProxyHandler);
-            }
-          });
-          AddressResolver<SocketAddress> addressResolver = proxy.getAddressResolver();
-          if (addressResolver != null) {
-            socksBootstrap.resolver((AddressResolverGroup<?>) addressResolver);
-          }
-          promise.setSuccess(socksBootstrap);
+    // direct connect
+    if (proxy == null) {
+      if (uri.isWebSocket()) {
+        promise.setSuccess(wsBootstrap);
+      } else {
+        promise.setSuccess(httpBootstrap);
+      }
+      return promise;
+    }
 
-        } else {
-          promise.setFailure(whenProxyAddress.cause());
-        }
-      });
+    // http proxy
+    if (proxy.getProxyType().isHttp()) {
+      promise.setSuccess(httpBootstrap);
+      return promise;
+    }
 
+    // socks proxy
+    Bootstrap bs;
+    ChannelHandler handler;
+    if (uri.isWebSocket()) {
+      bs = wsBootstrap;
+      handler = wsBootstrap.config().handler();
     } else {
-      promise.setSuccess(httpBootstrap);
+      bs = httpBootstrap;
+      handler = httpBootstrap.config().handler();
     }
 
+    final Bootstrap wrapFinalBs = bs;
+    final ChannelHandler wrapFinalHandler = handler;
+
+    nameResolver.resolve(proxy.getHost()).addListener((Future<InetAddress> whenProxyAddress) -> {
+      if (!whenProxyAddress.isSuccess()) {
+        promise.setFailure(whenProxyAddress.cause());
+        return;
+      }
+      wrapFinalBs.handler(new ChannelInitializer<Channel>() {
+        @Override
+        public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
+          wrapFinalHandler.handlerAdded(ctx);
+          super.handlerAdded(ctx);
+        }
+
+        @Override
+        protected void initChannel(Channel channel) throws Exception {
+          InetSocketAddress proxyAddress = new InetSocketAddress(whenProxyAddress.get(), proxy.getPort());
+          Realm realm = proxy.getRealm();
+          String username = realm != null ? realm.getPrincipal() : null;
+          String password = realm != null ? realm.getPassword() : null;
+          ProxyHandler socksProxyHandler;
+          switch (proxy.getProxyType()) {
+            case SOCKS_V4:
+              socksProxyHandler = new Socks4ProxyHandler(proxyAddress, username);
+              break;
+
+            case SOCKS_V5:
+              socksProxyHandler = new Socks5ProxyHandler(proxyAddress, username, password);
+              break;
+
+            default:
+              throw new IllegalArgumentException("Only SOCKS4 and SOCKS5 supported at the moment.");
+          }
+          channel.pipeline().addFirst(SOCKS_HANDLER, socksProxyHandler);
+        }
+      });
+      if (proxy.getProxyType().isSocks()) {
+        AddressResolverGroup addressResolverGroup = proxy.getAddressResolverGroup();
+        if (addressResolverGroup != null) {
+          wrapFinalBs.resolver(addressResolverGroup);
+        }
+      }
+      promise.setSuccess(wrapFinalBs);
+    });
+
     return promise;
   }
 
diff --git a/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestSender.java b/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestSender.java
index 8563773d12..ba851f2d56 100755
--- a/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestSender.java
+++ b/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestSender.java
@@ -351,6 +351,7 @@ private <T> Future<List<InetSocketAddress>> resolveAddresses(Request request,
       final Promise<List<InetSocketAddress>> unResolvedPromise = ImmediateEventExecutor.INSTANCE.newPromise();
       List<InetSocketAddress> unresolvedLiWrapper = new ArrayList<>(1);
       unresolvedLiWrapper.add(unresolvedRemoteAddress);
+      scheduleRequestTimeout(future, unresolvedRemoteAddress);
       unResolvedPromise.trySuccess(unresolvedLiWrapper);
       return unResolvedPromise;
     } else {
diff --git a/client/src/main/java/org/asynchttpclient/proxy/ProxyServer.java b/client/src/main/java/org/asynchttpclient/proxy/ProxyServer.java
index 42793b821c..3fc38096f7 100644
--- a/client/src/main/java/org/asynchttpclient/proxy/ProxyServer.java
+++ b/client/src/main/java/org/asynchttpclient/proxy/ProxyServer.java
@@ -16,10 +16,9 @@
  */
 package org.asynchttpclient.proxy;
 
-import io.netty.resolver.AddressResolver;
+import io.netty.resolver.AddressResolverGroup;
 import org.asynchttpclient.Realm;
 
-import java.net.SocketAddress;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -38,7 +37,7 @@ public class ProxyServer {
   private final Realm realm;
   private final List<String> nonProxyHosts;
   private final ProxyType proxyType;
-  private AddressResolver<SocketAddress> addressResolver;
+  private AddressResolverGroup addressResolverGroup;
   // server can resolve domain in socks 5
 
   public ProxyServer(String host, int port, int securedPort, Realm realm, List<String> nonProxyHosts,
@@ -52,9 +51,9 @@ public ProxyServer(String host, int port, int securedPort, Realm realm, List<Str
   }
 
   public ProxyServer(String host, int port, int securedPort, Realm realm, List<String> nonProxyHosts,
-                     ProxyType proxyType, AddressResolver<SocketAddress> addressResolver) {
+                     ProxyType proxyType, AddressResolverGroup addressResolverGroup) {
     this(host, port, securedPort, realm, nonProxyHosts, proxyType);
-    this.addressResolver = addressResolver;
+    this.addressResolverGroup = addressResolverGroup;
   }
 
   public String getHost() {
@@ -81,12 +80,12 @@ public ProxyType getProxyType() {
     return proxyType;
   }
 
-  public AddressResolver<SocketAddress> getAddressResolver() {
-    return addressResolver;
+  public AddressResolverGroup getAddressResolverGroup() {
+    return addressResolverGroup;
   }
 
   public boolean isResolveDomain() {
-    return addressResolver != null;
+    return addressResolverGroup != null;
   }
 
   /**
@@ -136,7 +135,7 @@ public static class Builder {
     private Realm realm;
     private List<String> nonProxyHosts;
     private ProxyType proxyType;
-    private AddressResolver<SocketAddress> addressResolver;
+    private AddressResolverGroup addressResolverGroup;
 
     public Builder(String host, int port) {
       this.host = host;
@@ -176,8 +175,8 @@ public Builder setProxyType(ProxyType proxyType) {
       return this;
     }
 
-    public Builder setAddressResolver(AddressResolver<SocketAddress> addressResolver) {
-      this.addressResolver = addressResolver;
+    public Builder setAddressResolverGroup(AddressResolverGroup addressResolverGroup) {
+      this.addressResolverGroup = addressResolverGroup;
       return this;
     }
 
@@ -185,7 +184,7 @@ public ProxyServer build() {
       List<String> nonProxyHosts = this.nonProxyHosts != null ? Collections.unmodifiableList(this.nonProxyHosts)
               : Collections.emptyList();
       ProxyType proxyType = this.proxyType != null ? this.proxyType : ProxyType.HTTP;
-      return new ProxyServer(host, port, securedPort, realm, nonProxyHosts, proxyType, this.addressResolver);
+      return new ProxyServer(host, port, securedPort, realm, nonProxyHosts, proxyType, this.addressResolverGroup);
     }
   }
 }

From 4a6affc6bef945d6dc3f0f975cbcfbc95f889aa0 Mon Sep 17 00:00:00 2001
From: cowwen <cowwen.lin@watsons.com.cn>
Date: Fri, 15 Mar 2019 16:57:24 +0800
Subject: [PATCH 3/3] resolve domain based on proxy type is socks and setting a
 addressResolverGroup

---
 .../org/asynchttpclient/netty/request/NettyRequestSender.java   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestSender.java b/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestSender.java
index ba851f2d56..5e2bf8495a 100755
--- a/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestSender.java
+++ b/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestSender.java
@@ -344,7 +344,7 @@ private <T> Future<List<InetSocketAddress>> resolveAddresses(Request request,
       InetSocketAddress unresolvedRemoteAddress = InetSocketAddress.createUnresolved(proxy.getHost(), port);
       scheduleRequestTimeout(future, unresolvedRemoteAddress);
       return RequestHostnameResolver.INSTANCE.resolve(request.getNameResolver(), unresolvedRemoteAddress, asyncHandler);
-    } else if (proxy != null && proxy.isResolveDomain() && ProxyType.SOCKS_V5 == proxy.getProxyType()) {
+    } else if (proxy != null && proxy.isResolveDomain() && proxy.getProxyType().isSocks()) {
       // resolve domain in socks 5 server
       int port = uri.getExplicitPort();
       InetSocketAddress unresolvedRemoteAddress = InetSocketAddress.createUnresolved(uri.getHost(), port);