io.crossbar.autobahn.wamp.transports.NettyWebSocket.java Source code

Java tutorial

Introduction

Here is the source code for io.crossbar.autobahn.wamp.transports.NettyWebSocket.java

Source

///////////////////////////////////////////////////////////////////////////////
//
//   AutobahnJava - http://crossbar.io/autobahn
//
//   Copyright (c) Crossbar.io Technologies GmbH and contributors
//
//   Licensed under the MIT License.
//   http://www.opensource.org/licenses/mit-license.php
//
///////////////////////////////////////////////////////////////////////////////

package io.crossbar.autobahn.wamp.transports;

import java.net.URI;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.SSLException;

import io.crossbar.autobahn.utils.ABLogger;
import io.crossbar.autobahn.utils.IABLogger;
import io.crossbar.autobahn.wamp.interfaces.ITransport;
import io.crossbar.autobahn.wamp.interfaces.ITransportHandler;
import io.crossbar.autobahn.wamp.serializers.CBORSerializer;
import io.crossbar.autobahn.wamp.serializers.JSONSerializer;
import io.crossbar.autobahn.wamp.serializers.MessagePackSerializer;
import io.crossbar.autobahn.wamp.types.CloseDetails;
import io.crossbar.autobahn.wamp.types.TransportOptions;
import io.crossbar.autobahn.wamp.types.WebSocketOptions;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
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.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
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;

public class NettyWebSocket implements ITransport {

    private static final IABLogger LOGGER = ABLogger.getLogger(NettyWebSocket.class.getName());
    private static final String SERIALIZERS_DEFAULT = String.format("%s,%s,%s", CBORSerializer.NAME,
            MessagePackSerializer.NAME, JSONSerializer.NAME);

    private Channel mChannel;
    private NettyWebSocketClientHandler mHandler;
    private final String mUri;

    private WebSocketOptions mOptions;
    private String mSerializers;

    public NettyWebSocket(String uri) {
        this(uri, (WebSocketOptions) null);
    }

    public NettyWebSocket(String uri, List<String> serializers) {
        this(uri, serializers, null);
    }

    @Deprecated
    public NettyWebSocket(String uri, WebSocketOptions options) {
        this(uri, null, options);
    }

    @Deprecated
    public NettyWebSocket(String uri, List<String> serializers, WebSocketOptions options) {
        mUri = uri;

        if (serializers == null) {
            mSerializers = SERIALIZERS_DEFAULT;
        } else {
            StringBuilder result = new StringBuilder();
            for (String serializer : serializers) {
                result.append(serializer).append(",");
            }
            mSerializers = result.toString();
        }

        if (options == null) {
            mOptions = new WebSocketOptions();
        } else {
            mOptions = options;
        }
    }

    private int validateURIAndGetPort(URI uri) {
        String scheme = uri.getScheme();
        if (!"ws".equalsIgnoreCase(scheme) && !"wss".equalsIgnoreCase(scheme)) {
            throw new IllegalArgumentException("Only WS(S) is supported.");
        }
        int port = uri.getPort();
        if (port == -1) {
            if ("ws".equalsIgnoreCase(scheme)) {
                port = 80;
            } else if ("wss".equalsIgnoreCase(scheme)) {
                port = 443;
            }
        }
        return port;
    }

    private SslContext getSSLContext(String scheme) throws SSLException {
        return "wss".equalsIgnoreCase(scheme)
                ? SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build()
                : null;
    }

    @Override
    public void connect(ITransportHandler transportHandler) throws Exception {
        connect(transportHandler, new TransportOptions());
    }

    @Override
    public void connect(ITransportHandler transportHandler, TransportOptions options) throws Exception {

        if (options == null) {
            if (mOptions == null) {
                options = new TransportOptions();
            } else {
                options = new TransportOptions();
                options.setAutoPingInterval(mOptions.getAutoPingInterval());
                options.setAutoPingTimeout(mOptions.getAutoPingTimeout());
                options.setMaxFramePayloadSize(mOptions.getMaxFramePayloadSize());
            }
        }

        URI uri;
        uri = new URI(mUri);
        int port = validateURIAndGetPort(uri);
        String scheme = uri.getScheme();
        String host = uri.getHost();

        final SslContext sslContext = getSSLContext(scheme);

        WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker(uri,
                WebSocketVersion.V13, mSerializers, true, new DefaultHttpHeaders(),
                options.getMaxFramePayloadSize());
        mHandler = new NettyWebSocketClientHandler(handshaker, this, transportHandler);

        EventLoopGroup group = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group);
        bootstrap.channel(NioSocketChannel.class);

        TransportOptions opt = options;
        bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                ChannelPipeline channelPipeline = ch.pipeline();
                if (sslContext != null) {
                    channelPipeline.addLast(sslContext.newHandler(ch.alloc(), host, port));
                }
                channelPipeline.addLast(new HttpClientCodec(), new HttpObjectAggregator(8192),
                        WebSocketClientCompressionHandler.INSTANCE,
                        new IdleStateHandler(opt.getAutoPingInterval() + opt.getAutoPingTimeout(),
                                opt.getAutoPingInterval(), 0, TimeUnit.SECONDS),
                        mHandler);
            }
        });

        mChannel = bootstrap.connect(uri.getHost(), port).sync().channel();
        mHandler.getHandshakeFuture().sync();
    }

    @Override
    public void send(byte[] payload, boolean isBinary) {
        WebSocketFrame frame;
        if (isBinary) {
            frame = new BinaryWebSocketFrame(toByteBuf(payload));
        } else {
            frame = new TextWebSocketFrame(toByteBuf(payload));
        }
        mChannel.writeAndFlush(frame);
    }

    @Override
    public boolean isOpen() {
        return mChannel != null && mChannel.isOpen();
    }

    @Override
    public void close() throws Exception {
        LOGGER.v("close()");
        if (mHandler != null && mChannel != null) {
            mHandler.close(mChannel, true, new CloseDetails(CloseDetails.REASON_DEFAULT, null));
        }
    }

    @Override
    public void abort() throws Exception {
        LOGGER.v("abort()");
        close();
    }

    @Override
    public void setOptions(TransportOptions options) {
        throw new UnsupportedOperationException("Not implemented yet, provide options using connect() instead");
    }

    private ByteBuf toByteBuf(byte[] bytes) {
        return Unpooled.copiedBuffer(bytes);
    }
}