package alien4cloud.it.utils.websocket;

import java.nio.charset.Charset;
import java.util.Set;

import lombok.extern.slf4j.Slf4j;
import alien4cloud.it.exception.ITException;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.ClientCookieEncoder;
import io.netty.handler.codec.http.Cookie;
import io.netty.handler.codec.http.CookieDecoder;
import io.netty.handler.codec.http.DefaultHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
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.HttpResponse;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.util.ReferenceCountUtil;

@Slf4j
public class WebSocketClientHandler<T> extends SimpleChannelInboundHandler<Object> {

    private final WebSocketClientHandshaker handShaker;

    private ChannelPromise handshakeFuture;

    private String host;

    private String user;

    private String password;

    private String authenticationUrl;

    private Set<Cookie> cookies;

    public WebSocketClientHandler(WebSocketClientHandshaker handShaker, String host, String user, String password, String authenticationUrl) {
        this.handShaker = handShaker;
        this.host = host;
        this.user = user;
        this.password = password;
        this.authenticationUrl = authenticationUrl;
    }

    public ChannelFuture handshakeFuture() {
        return handshakeFuture;
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        handshakeFuture = ctx.newPromise();
    }

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        if (msg instanceof HttpRequest) {
            if (this.cookies != null && !this.cookies.isEmpty()) {
                HttpRequest request = (HttpRequest) msg;
                request.headers().set(HttpHeaders.Names.COOKIE, ClientCookieEncoder.encode(cookies));
                if (log.isDebugEnabled()) {
                    log.debug("Write HttpRequest {} enriched with security cookie", request);
                }
            }
            super.write(ctx, msg, promise);
            return;
        }
        String wrappedFrame = ((ByteBuf) msg).toString(Charset.forName("UTF-8"));
        if (log.isDebugEnabled()) {
            log.debug("Write text frame {}", wrappedFrame);
        }
        WebSocketFrame webSocketFrame = new TextWebSocketFrame(wrappedFrame);
        super.write(ctx, webSocketFrame, promise);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        if (this.authenticationUrl != null) {
            HttpRequest loginRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, this.authenticationUrl);
            loginRequest.headers().set(HttpHeaders.Names.HOST, this.host);
            HttpPostRequestEncoder bodyRequestEncoder = new HttpPostRequestEncoder(loginRequest, false);
            bodyRequestEncoder.addBodyAttribute("j_username", user);
            bodyRequestEncoder.addBodyAttribute("j_password", password);
            bodyRequestEncoder.addBodyAttribute("submit", "Login");
            loginRequest = bodyRequestEncoder.finalizeRequest();
            if (log.isDebugEnabled()) {
                log.debug("Authentication request for user {} to {}", this.user, this.authenticationUrl);
            }
            ctx.writeAndFlush(loginRequest);
        } else {
            handShaker.handshake(ctx.channel());
        }
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        if (log.isDebugEnabled()) {
            log.debug("Channel disconnected");
        }
    }

    @Override
    public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
        // The first message must be authentication response
        if (this.authenticationUrl != null && (this.cookies == null || this.cookies.isEmpty())) {
            HttpResponse response = (HttpResponse) msg;
            CharSequence cookieData = response.headers().get(HttpHeaders.Names.SET_COOKIE);
            if (cookieData != null) {
                this.cookies = CookieDecoder.decode(cookieData.toString());
                if (this.cookies == null || this.cookies.isEmpty()) {
                    throw new WebSocketAuthenticationFailureException("Could not authenticate");
                }
                if (log.isDebugEnabled()) {
                    for (Cookie cookie : this.cookies) {
                        log.debug("Server says must set cookie with name {} and value {}", cookie.name(), cookie.value());
                    }
                }
            } else {
                throw new ITException("Could not authenticate");
            }
            if (log.isDebugEnabled()) {
                log.debug("Authentication succeeded for user {}", this.user);
            }
            handShaker.handshake(ctx.channel());
            return;
        }

        // The second one must be the response for web socket handshake
        if (!handShaker.isHandshakeComplete()) {
            handShaker.finishHandshake(ctx.channel(), (FullHttpResponse) msg);
            if (log.isDebugEnabled()) {
                log.debug("Web socket client connected for user {}", this.user);
            }
            handshakeFuture.setSuccess();
            return;
        }
        // Take the byte buff and send it up to Stomp decoder
        if (msg instanceof WebSocketFrame) {
            if (log.isDebugEnabled()) {
                if (msg instanceof TextWebSocketFrame) {
                    log.debug("Received text frame {}", ((TextWebSocketFrame) msg).text());
                }
            }
            ReferenceCountUtil.retain(msg);
            ctx.fireChannelRead(((WebSocketFrame) msg).content());
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.error("Web socket error", cause);
        if (!handshakeFuture.isDone()) {
            handshakeFuture.setFailure(cause);
        }
        ctx.close();
    }
}

@@ -38,143 +38,8 @@
 t;%0A%0A
-import java.nio.charset.Charset;%0Aimport java.util.Set;%0A%0Aimport lombok.extern.slf4j.Slf4j;%0Aimport alien4cloud.it.exception.ITException;%0A
 impo
@@ -66,16 +66,16 @@
 yteBuf;%0A
+
 import i
@@ -241,24 +241,67 @@
 undHandler;%0A
+import io.netty.handler.codec.AsciiString;%0A
 import io.ne
@@ -549,56 +549,8 @@
 se;%0A
-import io.netty.handler.codec.http.HttpHeaders;%0A
 impo
@@ -1049,16 +1049,152 @@
 tUtil;%0A%0A
+import java.nio.charset.Charset;%0Aimport java.util.Set;%0A%0Aimport lombok.extern.slf4j.Slf4j;%0Aimport alien4cloud.it.exception.ITException;%0A%0A
 @Slf4j%0Ap
@@ -2394,32 +2394,33 @@
 set(
-HttpHeaders.Names.COOKIE
+new AsciiString(%22cookie%22)
 , Cl
@@ -3328,30 +3328,31 @@
 set(
-HttpHeaders.Names.HOST
+new AsciiString(%22host%22)
 , th
@@ -4588,36 +4588,37 @@
 get(
-HttpHeaders.Names.SET_COOKIE
+new AsciiString(%22set-cookie%22)
 );%0A 

Fix errors introduce by change in HttpHeaders