org.wso2.carbon.transport.http.netty.sender.websocket.WebSocketClient.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.transport.http.netty.sender.websocket.WebSocketClient.java

Source

/*
 *  Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */

package org.wso2.carbon.transport.http.netty.sender.websocket;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
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.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.carbon.transport.http.netty.contract.websocket.HandshakeFuture;
import org.wso2.carbon.transport.http.netty.contract.websocket.WebSocketConnectorListener;
import org.wso2.carbon.transport.http.netty.contractimpl.websocket.HandshakeFutureImpl;
import org.wso2.carbon.transport.http.netty.internal.websocket.WebSocketSessionImpl;

import java.net.URI;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLException;

/**
 * WebSocket client for sending and receiving messages in WebSocket as a client.
 */
public class WebSocketClient {

    private static final Logger log = LoggerFactory.getLogger(WebSocketClient.class);

    private WebSocketTargetHandler handler;
    private EventLoopGroup group;

    private final String url;
    private final String subProtocols;
    private final String target;
    private final int idleTimeout;
    private final Map<String, String> headers;
    private final WebSocketConnectorListener connectorListener;

    /**
     *
     * @param url url of the remote endpoint.
     * @param target target for the inbound messages from the remote server.
     * @param subProtocols the negotiable sub-protocol if server is asking for it.
     * @param idleTimeout Idle timeout of the connection.
     * @param headers any specific headers which need to send to the server.
     * @param connectorListener connector listener to notify incoming messages.
     */
    public WebSocketClient(String url, String target, String subProtocols, int idleTimeout,
            Map<String, String> headers, WebSocketConnectorListener connectorListener) {
        this.url = url;
        this.target = target;
        this.subProtocols = subProtocols;
        this.idleTimeout = idleTimeout;
        this.headers = headers;
        this.connectorListener = connectorListener;
    }

    /**
     * Handle the handshake with the server.
     *
     * @return handshake future for connection.
     */
    public HandshakeFuture handshake() {
        HandshakeFutureImpl handshakeFuture = new HandshakeFutureImpl();
        try {
            URI uri = new URI(url);
            String scheme = uri.getScheme() == null ? "ws" : uri.getScheme();
            final String host = uri.getHost() == null ? "127.0.0.1" : uri.getHost();
            final int port;
            if (uri.getPort() == -1) {
                if ("ws".equalsIgnoreCase(scheme)) {
                    port = 80;
                } else if ("wss".equalsIgnoreCase(scheme)) {
                    port = 443;
                } else {
                    port = -1;
                }
            } else {
                port = uri.getPort();
            }

            if (!"ws".equalsIgnoreCase(scheme) && !"wss".equalsIgnoreCase(scheme)) {
                log.error("Only WS(S) is supported.");
                throw new SSLException("");
            }

            final boolean ssl = "wss".equalsIgnoreCase(scheme);
            final SslContext sslCtx;
            if (ssl) {
                sslCtx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
            } else {
                sslCtx = null;
            }

            group = new NioEventLoopGroup();
            HttpHeaders httpHeaders = new DefaultHttpHeaders();

            // Adding custom headers to the handshake request.

            if (headers != null) {
                headers.entrySet().forEach(entry -> httpHeaders.add(entry.getKey(), entry.getValue()));
            }

            WebSocketClientHandshaker websocketHandshaker = WebSocketClientHandshakerFactory.newHandshaker(uri,
                    WebSocketVersion.V13, subProtocols, true, httpHeaders);
            handler = new WebSocketTargetHandler(websocketHandshaker, ssl, url, target, connectorListener);

            Bootstrap b = new Bootstrap();
            b.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) {
                    ChannelPipeline p = ch.pipeline();
                    if (sslCtx != null) {
                        p.addLast(sslCtx.newHandler(ch.alloc(), host, port));
                    }
                    p.addLast(new HttpClientCodec());
                    p.addLast(new HttpObjectAggregator(8192));
                    p.addLast(WebSocketClientCompressionHandler.INSTANCE);
                    if (idleTimeout > 0) {
                        p.addLast(
                                new IdleStateHandler(idleTimeout, idleTimeout, idleTimeout, TimeUnit.MILLISECONDS));
                    }
                    p.addLast(handler);
                }
            });

            b.connect(uri.getHost(), port).sync();
            ChannelFuture future = handler.handshakeFuture().addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) {
                    Throwable cause = future.cause();
                    if (future.isSuccess() && cause == null) {
                        WebSocketSessionImpl session = (WebSocketSessionImpl) handler.getChannelSession();
                        String actualSubProtocol = websocketHandshaker.actualSubprotocol();
                        handler.setActualSubProtocol(actualSubProtocol);
                        session.setNegotiatedSubProtocol(actualSubProtocol);
                        session.setIsOpen(true);
                        handshakeFuture.notifySuccess(session);
                    } else {
                        handshakeFuture.notifyError(cause);
                    }
                }
            }).sync();
            handshakeFuture.setChannelFuture(future);
        } catch (Throwable t) {
            handshakeFuture.notifyError(t);
        }

        return handshakeFuture;
    }
}