Repository: scalecube/scalecube-services
Issue: Client-sdk doesn't support redirecting

When we launch the following example: 

```java
class Scratch {

  public static void main(String[] args) throws Exception {
    Client client =
        onWebsocket(
            ClientSettings.builder()
                .host("develop-ws.aws.com")
                .port(80) /// 443
                .loopResources(LoopResources.create("test"))
                .build());

    client
        .forService(UserService.class)
        .find(new FindUserRequest("123"))
        .subscribe(
            System.out::println, Throwable::printStackTrace, () -> System.out.println("Complete"));

    Thread.currentThread().join();
  }
}
```

it throws:

```c
io.netty.handler.codec.http.websocketx.WebSocketHandshakeException: Invalid handshake response getStatus: 301 Moved Permanently
	at io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker13.verify(WebSocketClientHandshaker13.java:191)
	at io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker.finishHandshake(WebSocketClientHandshaker.java:216)
	at reactor.netty.http.client.WebsocketClientOperations.onInboundNext(WebsocketClientOperations.java:109)
	at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:141)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:102)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:438)
	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:310)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:284)
	at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:253)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1434)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:965)
	at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:808)
	at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:410)
	at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:310)
	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884)
	at java.lang.Thread.run(Thread.java:748)

```

So can we use the client-sdk with redirecting?

Changed files:
services-gateway-clientsdk/src/main/java/io/scalecube/services/gateway/clientsdk/websocket/WebsocketClientTransport.java
services-gateway-clientsdk/src/main/java/io/scalecube/services/gateway/clientsdk/rsocket/RSocketClientTransport.java
services-gateway-clientsdk/src/main/java/io/scalecube/services/gateway/clientsdk/ClientSettings.java
services-gateway-clientsdk/src/main/java/io/scalecube/services/gateway/clientsdk/http/HttpClientTransport.java

diff --git a/services-gateway-clientsdk/src/main/java/io/scalecube/services/gateway/clientsdk/ClientSettings.java b/services-gateway-clientsdk/src/main/java/io/scalecube/services/gateway/clientsdk/ClientSettings.java
index a51df11c..e418c881 100644
--- a/services-gateway-clientsdk/src/main/java/io/scalecube/services/gateway/clientsdk/ClientSettings.java
+++ b/services-gateway-clientsdk/src/main/java/io/scalecube/services/gateway/clientsdk/ClientSettings.java
@@ -2,6 +2,7 @@ package io.scalecube.services.gateway.clientsdk;
 
 import java.net.InetSocketAddress;
 import reactor.netty.resources.LoopResources;
+import reactor.netty.tcp.SslProvider;
 
 public class ClientSettings {
 
@@ -12,12 +13,16 @@ public class ClientSettings {
   private final int port;
   private final String contentType;
   private final LoopResources loopResources;
+  private final boolean followRedirect;
+  private final SslProvider sslProvider;
 
   private ClientSettings(Builder builder) {
     this.host = builder.host;
     this.port = builder.port;
     this.contentType = builder.contentType;
     this.loopResources = builder.loopResources;
+    this.followRedirect = builder.followRedirect;
+    this.sslProvider = builder.sslProvider;
   }
 
   public String host() {
@@ -36,22 +41,29 @@ public class ClientSettings {
     return loopResources;
   }
 
+  public boolean followRedirect() {
+    return followRedirect;
+  }
+
+  public SslProvider sslProvider() {
+    return sslProvider;
+  }
+
   public static Builder builder() {
     return new Builder();
   }
 
   @Override
   public String toString() {
-    return "ClientSettings{"
-        + "host='"
-        + host
-        + '\\''
-        + ", port="
-        + port
-        + ", contentType='"
-        + contentType
-        + '\\''
-        + '}';
+    final StringBuilder sb = new StringBuilder("ClientSettings{");
+    sb.append("host='").append(host).append('\\'');
+    sb.append(", port=").append(port);
+    sb.append(", contentType='").append(contentType).append('\\'');
+    sb.append(", loopResources=").append(loopResources);
+    sb.append(", followRedirect=").append(followRedirect);
+    sb.append(", sslProvider=").append(sslProvider);
+    sb.append('}');
+    return sb.toString();
   }
 
   public static class Builder {
@@ -59,6 +71,8 @@ public class ClientSettings {
     private int port;
     private String contentType = DEFAULT_CONTENT_TYPE;
     private LoopResources loopResources;
+    private boolean followRedirect = true;
+    private SslProvider sslProvider;
 
     private Builder() {}
 
@@ -86,6 +100,38 @@ public class ClientSettings {
       return this;
     }
 
+    /**
+     * Specifies is auto-redirect enabled for HTTP 301/302 status codes. Enabled by default.
+     *
+     * @param followRedirect if <code>true</code> auto-redirect is enabled, otherwise disabled
+     * @return builder
+     */
+    public Builder followRedirect(boolean followRedirect) {
+      this.followRedirect = followRedirect;
+      return this;
+    }
+
+    /**
+     * Use default SSL client provider.
+     *
+     * @return builder
+     */
+    public Builder secure() {
+      this.sslProvider = SslProvider.defaultClientProvider();
+      return this;
+    }
+
+    /**
+     * Use specified SSL provider.
+     *
+     * @param sslProvider SSL provider
+     * @return builder
+     */
+    public Builder secure(SslProvider sslProvider) {
+      this.sslProvider = sslProvider;
+      return this;
+    }
+
     public ClientSettings build() {
       return new ClientSettings(this);
     }
diff --git a/services-gateway-clientsdk/src/main/java/io/scalecube/services/gateway/clientsdk/http/HttpClientTransport.java b/services-gateway-clientsdk/src/main/java/io/scalecube/services/gateway/clientsdk/http/HttpClientTransport.java
index f0c2b489..3edaf942 100644
--- a/services-gateway-clientsdk/src/main/java/io/scalecube/services/gateway/clientsdk/http/HttpClientTransport.java
+++ b/services-gateway-clientsdk/src/main/java/io/scalecube/services/gateway/clientsdk/http/HttpClientTransport.java
@@ -40,9 +40,14 @@ public final class HttpClientTransport implements ClientTransport {
 
     httpClient =
         HttpClient.create(connectionProvider)
+            .followRedirect(settings.followRedirect())
             .tcpConfiguration(
-                tcpClient ->
-                    tcpClient.runOn(loopResources).host(settings.host()).port(settings.port()));
+                tcpClient -> {
+                  if (settings.sslProvider() != null) {
+                    tcpClient = tcpClient.secure(settings.sslProvider());
+                  }
+                  return tcpClient.runOn(loopResources).host(settings.host()).port(settings.port());
+                });
   }
 
   @Override
diff --git a/services-gateway-clientsdk/src/main/java/io/scalecube/services/gateway/clientsdk/rsocket/RSocketClientTransport.java b/services-gateway-clientsdk/src/main/java/io/scalecube/services/gateway/clientsdk/rsocket/RSocketClientTransport.java
index bcdd5245..7b5e5f41 100644
--- a/services-gateway-clientsdk/src/main/java/io/scalecube/services/gateway/clientsdk/rsocket/RSocketClientTransport.java
+++ b/services-gateway-clientsdk/src/main/java/io/scalecube/services/gateway/clientsdk/rsocket/RSocketClientTransport.java
@@ -139,9 +139,14 @@ public final class RSocketClientTransport implements ClientTransport {
 
     HttpClient httpClient =
         HttpClient.newConnection()
+            .followRedirect(settings.followRedirect())
             .tcpConfiguration(
-                tcpClient ->
-                    tcpClient.runOn(loopResources).host(settings.host()).port(settings.port()));
+                tcpClient -> {
+                  if (settings.sslProvider() != null) {
+                    tcpClient = tcpClient.secure(settings.sslProvider());
+                  }
+                  return tcpClient.runOn(loopResources).host(settings.host()).port(settings.port());
+                });
 
     return WebsocketClientTransport.create(httpClient, path);
   }
diff --git a/services-gateway-clientsdk/src/main/java/io/scalecube/services/gateway/clientsdk/websocket/WebsocketClientTransport.java b/services-gateway-clientsdk/src/main/java/io/scalecube/services/gateway/clientsdk/websocket/WebsocketClientTransport.java
index 5f5af7fa..471d8e21 100644
--- a/services-gateway-clientsdk/src/main/java/io/scalecube/services/gateway/clientsdk/websocket/WebsocketClientTransport.java
+++ b/services-gateway-clientsdk/src/main/java/io/scalecube/services/gateway/clientsdk/websocket/WebsocketClientTransport.java
@@ -49,9 +49,14 @@ public final class WebsocketClientTransport implements ClientTransport {
 
     httpClient =
         HttpClient.newConnection()
+            .followRedirect(settings.followRedirect())
             .tcpConfiguration(
-                tcpClient ->
-                    tcpClient.runOn(loopResources).host(settings.host()).port(settings.port()));
+                tcpClient -> {
+                  if (settings.sslProvider() != null) {
+                    tcpClient = tcpClient.secure(settings.sslProvider());
+                  }
+                  return tcpClient.runOn(loopResources).host(settings.host()).port(settings.port());
+                });
   }
 
   @Override