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 static io.netty.handler.codec.http.HttpHeaders.isKeepAlive;
19 import static io.netty.handler.codec.http.HttpHeaders.setContentLength;
20 import static io.netty.handler.codec.http.HttpHeaders.Names.HOST;
21 import static io.netty.handler.codec.http.HttpMethod.GET;
22 import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
23 import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN;
24 import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
26 import io.netty.buffer.ByteBuf;
27 import io.netty.buffer.Unpooled;
28 import io.netty.channel.ChannelFuture;
29 import io.netty.channel.ChannelFutureListener;
30 import io.netty.channel.ChannelHandlerContext;
31 import io.netty.channel.SimpleChannelInboundHandler;
32 import io.netty.handler.codec.http.DefaultFullHttpResponse;
33 import io.netty.handler.codec.http.FullHttpRequest;
34 import io.netty.handler.codec.http.FullHttpResponse;
35 import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
36 import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
37 import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
38 import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
39 import io.netty.handler.codec.http.websocketx.WebSocketFrame;
40 import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
41 import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
42 import io.netty.util.CharsetUtil;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
47 * Handles handshakes and messages
50 public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object> {
51 private static final Logger logger = LoggerFactory.getLogger(WebSocketServerHandler.class.getName());
53 private static final String WEBSOCKET_PATH = "/websocket";
55 private WebSocketServerHandshaker handshaker;
58 public void channelRead0(final ChannelHandlerContext ctx, final Object msg) throws Exception {
59 if (msg instanceof FullHttpRequest) {
60 handleHttpRequest(ctx, (FullHttpRequest) msg);
61 } else if (msg instanceof WebSocketFrame) {
62 handleWebSocketFrame(ctx, (WebSocketFrame) msg);
67 public void channelReadComplete(final ChannelHandlerContext ctx) throws Exception {
71 private void handleHttpRequest(final ChannelHandlerContext ctx, final FullHttpRequest req) throws Exception {
72 // Handle a bad request.
73 if (!req.getDecoderResult().isSuccess()) {
74 sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
78 // Allow only GET methods.
79 if (req.getMethod() != GET) {
80 sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
86 WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
87 getWebSocketLocation(req), null, false);
88 handshaker = wsFactory.newHandshaker(req);
89 if (handshaker == null) {
90 WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
92 handshaker.handshake(ctx.channel(), req);
96 private void handleWebSocketFrame(final ChannelHandlerContext ctx, final WebSocketFrame frame) {
98 // Check for closing frame
99 if (frame instanceof CloseWebSocketFrame) {
100 handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
103 if (frame instanceof PingWebSocketFrame) {
104 ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
107 if (!(frame instanceof TextWebSocketFrame)) {
108 throw new UnsupportedOperationException(String.format("%s frame types not supported", frame.getClass()
112 // Send the uppercase string back.
113 String request = ((TextWebSocketFrame) frame).text();
114 logger.debug(String.format("%s received %s", ctx.channel(), request));
115 ctx.channel().write(new TextWebSocketFrame(request.toUpperCase()));
118 private static void sendHttpResponse(
119 final ChannelHandlerContext ctx, final FullHttpRequest req, final FullHttpResponse res) {
120 // Generate an error page if response getStatus code is not OK (200).
121 if (res.getStatus().code() != 200) {
122 ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8);
123 res.content().writeBytes(buf);
125 setContentLength(res, res.content().readableBytes());
128 // Send the response and close the connection if necessary.
129 ChannelFuture f = ctx.channel().writeAndFlush(res);
130 if (!isKeepAlive(req) || res.getStatus().code() != 200) {
131 f.addListener(ChannelFutureListener.CLOSE);
136 public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {
137 logger.info("Exception caught {}",cause);
141 private static String getWebSocketLocation(final FullHttpRequest req) {
142 return "ws://" + req.headers().get(HOST) + WEBSOCKET_PATH;