package com.blade.server.netty; import com.blade.Blade; import com.blade.mvc.handler.WebSocketHandlerWrapper; import com.blade.mvc.websocket.WebSocketContext; import com.blade.mvc.websocket.WebSocketSession; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.websocketx.*; import io.netty.util.ReferenceCountUtil; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.CompletableFuture; /** * Http Server Handler * * @author biezhi,darren * 2017/5/31, */ @Slf4j public class WebSocketHandler extends SimpleChannelInboundHandler<Object> { private WebSocketServerHandshaker handshaker; private WebSocketSession session; private com.blade.mvc.handler.WebSocketHandler handler; private String uri; private Blade blade; public WebSocketHandler(Blade blade) { this.blade = blade; } @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) { if (msg instanceof HttpRequest) { handleHttpRequest(ctx, (HttpRequest) msg); } else if (msg instanceof WebSocketFrame) { initHandlerWrapper(); handleWebSocketFrame(ctx, (WebSocketFrame) msg); } else { ReferenceCountUtil.retain(msg); ctx.fireChannelRead(msg); } } @Override public void channelReadComplete(ChannelHandlerContext ctx) { ctx.flush(); } private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req) { if (isWebSocketRequest(req)) { WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(req.uri(), null, true); this.handshaker = wsFactory.newHandshaker(req); if (this.handshaker == null) { //Return that we need cannot not support the web socket version WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); } else { this.handshaker.handshake(ctx.channel(), req); this.session = new WebSocketSession(ctx); this.uri = req.uri(); initHandlerWrapper(); //Allows the user to send messages in the event of onConnect CompletableFuture.completedFuture(new WebSocketContext(this.session,this.handler)) .thenAcceptAsync(this.handler::onConnect,ctx.executor()); } } else { ReferenceCountUtil.retain(req); ctx.fireChannelRead(req); } } /** * Only supported TextWebSocketFrame * * @param ctx * @param frame */ private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) { if (frame instanceof CloseWebSocketFrame) { this.handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain()); CompletableFuture.completedFuture(new WebSocketContext(this.session,this.handler)) .thenAcceptAsync(this.handler::onDisConnect); return; } if (frame instanceof PingWebSocketFrame) { ctx.channel().write(new PongWebSocketFrame(frame.content().retain())); return; } if (!(frame instanceof TextWebSocketFrame)) { throw new UnsupportedOperationException("unsupported frame type: " + frame.getClass().getName()); } CompletableFuture.completedFuture(new WebSocketContext(this.session,this.handler,((TextWebSocketFrame) frame).text())) .thenAcceptAsync(this.handler::onText,ctx.executor()); } private boolean isWebSocketRequest(HttpRequest req){ return req != null && (this.handler = this.blade.routeMatcher().getWebSocket(req.uri())) != null && req.decoderResult().isSuccess() && "websocket".equals(req.headers().get("Upgrade")); } private void initHandlerWrapper(){ if(this.handler != null && this.handler instanceof WebSocketHandlerWrapper){ ((WebSocketHandlerWrapper) this.handler).setPath(this.uri); } } }