/* ************************************************************************ # # DivConq # # http://divconq.com/ # # Copyright: # Copyright 2014 eTimeline, LLC. All rights reserved. # # License: # See the license.txt file in the project's top-level directory for details. # # Authors: # * Andy White # ************************************************************************ */ package divconq.bus.net; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; import io.netty.handler.codec.http.websocketx.PongWebSocketFrame; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketFrame; import io.netty.util.CharsetUtil; import divconq.bus.MessageUtil; import divconq.lang.op.FuncResult; import divconq.struct.CompositeParser; import divconq.struct.CompositeStruct; import divconq.struct.RecordStruct; import static io.netty.handler.codec.http.HttpHeaders.Names.*; /** * Handles handshakes and messages */ abstract public class CommonHandler extends SimpleChannelInboundHandler<Object> { static protected final String BUS_PATH = "/dcBus"; protected Session session = null; public CommonHandler(SocketInfo info, boolean isServer) { this.session = new Session(info, isServer); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { // TODO logging System.out.println("dcBus " + this.session.getSessionMode() + " disconnected!"); this.session.closed(); } @Override public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof FullHttpRequest) this.handleHttpRequest(ctx, (FullHttpRequest) msg); else if (msg instanceof WebSocketFrame) this.handleWebSocketFrame(ctx, (WebSocketFrame) msg); } public void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception { // TODO logging FullHttpResponse response = (FullHttpResponse) req; throw new Exception("dcBus Unexpected FullHttpResponse (getStatus=" + response.getStatus() + ", content=" + response.content().toString(CharsetUtil.UTF_8) + ')'); } public void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) { Channel ch = ctx.channel(); // Check for closing frame if (frame instanceof CloseWebSocketFrame) { System.out.println("dcBus " + this.session.getSessionMode() + " received close"); ch.close(); return; } if (frame instanceof PingWebSocketFrame) { System.out.println("dcBus " + this.session.getSessionMode() + " received ping"); ctx.channel().writeAndFlush(new PongWebSocketFrame(frame.content().retain())); return; } if (frame instanceof PongWebSocketFrame) { System.out.println("dcBus " + this.session.getSessionMode() + " received pong"); return; } if (frame instanceof TextWebSocketFrame) { String data = ((TextWebSocketFrame) frame).text(); //System.out.println("dcBus " + this.session.getSessionMode() + " received message: " + data); FuncResult<CompositeStruct> res = CompositeParser.parseJson(data); if (res.hasErrors()) { // TODO logging System.out.println("dcBus " + this.session.getSessionMode() + " got a bad message: " + res.getMessage()); ch.close(); // don't stay with bad messages return; } this.session.receiveMessage(this.session, ch, MessageUtil.fromRecord((RecordStruct)res.getResult())); return; } // TODO unhandled frame type // TODO logging } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { // TODO logging cause.printStackTrace(); ctx.close(); } public static String getWebSocketLocation(FullHttpRequest req) { return "wss://" + req.headers().get(HOST) + BUS_PATH; } }