Java tutorial
/** * Copyright 2015 Santhosh Kumar Tekuri * * The JLibs authors license 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 jlibs.wamp4j.netty; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.PooledByteBufAllocator; import io.netty.channel.*; 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.*; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import jlibs.wamp4j.Util; import jlibs.wamp4j.spi.ConnectListener; import jlibs.wamp4j.spi.NamedThreadFactory; import jlibs.wamp4j.spi.WAMPClientEndpoint; import java.net.URI; /** * @author Santhosh Kumar Tekuri */ public class NettyClientEndpoint extends NettyEndpoint implements WAMPClientEndpoint { public NettyClientEndpoint() { super(NamedThreadFactory.CLIENT_THREAD_FACTORY); } @Override public void connect(final URI uri, final ConnectListener listener, final String... subProtocols) { final SslContext sslContext; if ("wss".equals(uri.getScheme())) { try { if (sslSettings == null) { sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE) .build(); } else { sslContext = SslContextBuilder.forClient().trustManager(sslSettings.trustCertChainFile) .keyManager(sslSettings.certificateFile, sslSettings.keyFile, sslSettings.keyPassword) .build(); } } catch (Throwable thr) { listener.onError(thr); return; } } else if ("ws".equals(uri.getScheme())) sslContext = null; else throw new IllegalArgumentException("invalid protocol: " + uri.getScheme()); final int port = uri.getPort() == -1 ? (sslContext == null ? 80 : 443) : uri.getPort(); Bootstrap bootstrap = new Bootstrap().group(eventLoopGroup).channel(NioSocketChannel.class) .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT) .option(ChannelOption.MAX_MESSAGES_PER_READ, 50000).option(ChannelOption.WRITE_SPIN_COUNT, 50000) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { if (sslContext != null) ch.pipeline().addLast(sslContext.newHandler(ch.alloc(), uri.getHost(), port)); WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker(uri, WebSocketVersion.V13, Util.toString(subProtocols), false, new DefaultHttpHeaders()); ch.pipeline().addLast(new HttpClientCodec(), new HttpObjectAggregator(8192), new WebSocketClientProtocolHandler(handshaker) { @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { super.exceptionCaught(ctx, cause); listener.onError(cause); } }, new HandshakeListener(handshaker, listener)); } }); bootstrap.connect(uri.getHost(), port).addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (!future.isSuccess()) { assert !future.channel().isOpen(); listener.onError(future.cause()); } } }); } private static class HandshakeListener extends SimpleChannelInboundHandler<Object> { private final WebSocketClientHandshaker handshaker; private final ConnectListener connectListener; public HandshakeListener(WebSocketClientHandshaker handshaker, ConnectListener connectListener) { this.handshaker = handshaker; this.connectListener = connectListener; } @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) { } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { if (handshaker.isHandshakeComplete()) super.exceptionCaught(ctx, cause); else { connectListener.onError(cause); ctx.close(); } } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt == WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_COMPLETE) { NettyWebSocket webSocket = new NettyWebSocket(null, handshaker.actualSubprotocol()); ctx.pipeline().addLast("ws-aggregator", new WebSocketFrameAggregator(16 * 1024 * 1024)); // 16MB ctx.pipeline().addLast("websocket", webSocket); ctx.pipeline().remove(this); webSocket.channelActive(ctx); connectListener.onConnect(webSocket); } else ctx.fireUserEventTriggered(evt); } } }