2 * Copyright 2012 The Netty Project
4 * The Netty Project licenses this file to you under the Apache License,
5 * version 2.0 (the "License"); you may not use this file except in compliance
6 * with the License. You may obtain a copy of the License at:
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
16 package org.opendaylight.yangtools.websocket.server;
18 import io.netty.buffer.ByteBuf;
19 import io.netty.buffer.Unpooled;
20 import io.netty.channel.ChannelFuture;
21 import io.netty.channel.ChannelFutureListener;
22 import io.netty.channel.ChannelHandlerContext;
23 import io.netty.channel.SimpleChannelInboundHandler;
24 import io.netty.handler.codec.http.DefaultFullHttpResponse;
25 import io.netty.handler.codec.http.FullHttpRequest;
26 import io.netty.handler.codec.http.FullHttpResponse;
27 import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
28 import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
29 import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
30 import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
31 import io.netty.handler.codec.http.websocketx.WebSocketFrame;
32 import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
33 import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
34 import io.netty.util.CharsetUtil;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37 import static io.netty.handler.codec.http.HttpHeaders.Names.HOST;
38 import static io.netty.handler.codec.http.HttpHeaders.isKeepAlive;
39 import static io.netty.handler.codec.http.HttpHeaders.setContentLength;
40 import static io.netty.handler.codec.http.HttpMethod.GET;
41 import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
42 import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN;
43 import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
46 * Handles handshakes and messages
48 public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object> {
49 private static final Logger logger = LoggerFactory.getLogger(WebSocketServerHandler.class.getName());
51 private static final String WEBSOCKET_PATH = "/websocket";
53 private WebSocketServerHandshaker handshaker;
56 public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
57 if (msg instanceof FullHttpRequest) {
58 handleHttpRequest(ctx, (FullHttpRequest) msg);
59 } else if (msg instanceof WebSocketFrame) {
60 handleWebSocketFrame(ctx, (WebSocketFrame) msg);
65 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
69 private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
70 // Handle a bad request.
71 if (!req.getDecoderResult().isSuccess()) {
72 sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
76 // Allow only GET methods.
77 if (req.getMethod() != GET) {
78 sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
84 WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
85 getWebSocketLocation(req), null, false);
86 handshaker = wsFactory.newHandshaker(req);
87 if (handshaker == null) {
88 WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
90 handshaker.handshake(ctx.channel(), req);
94 private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
96 // Check for closing frame
97 if (frame instanceof CloseWebSocketFrame) {
98 handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
101 if (frame instanceof PingWebSocketFrame) {
102 ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
105 if (!(frame instanceof TextWebSocketFrame)) {
106 throw new UnsupportedOperationException(String.format("%s frame types not supported", frame.getClass()
110 // Send the uppercase string back.
111 String request = ((TextWebSocketFrame) frame).text();
112 logger.debug(String.format("%s received %s", ctx.channel(), request));
113 ctx.channel().write(new TextWebSocketFrame(request.toUpperCase()));
116 private static void sendHttpResponse(
117 ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) {
118 // Generate an error page if response getStatus code is not OK (200).
119 if (res.getStatus().code() != 200) {
120 ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8);
121 res.content().writeBytes(buf);
123 setContentLength(res, res.content().readableBytes());
126 // Send the response and close the connection if necessary.
127 ChannelFuture f = ctx.channel().writeAndFlush(res);
128 if (!isKeepAlive(req) || res.getStatus().code() != 200) {
129 f.addListener(ChannelFutureListener.CLOSE);
134 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
135 logger.info("Exception caught {}",cause);
139 private static String getWebSocketLocation(FullHttpRequest req) {
140 return "ws://" + req.headers().get(HOST) + WEBSOCKET_PATH;