f5771f9131638b19464373fcbd7e7429b47accad
[yangtools.git] / websocket / websocket-client / src / test / java / org / opendaylight / yangtools / websocket / server / WebSocketServerHandler.java
1 /*
2  * Copyright 2012 The Netty Project
3  *
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:
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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
14  * under the License.
15  */
16 package org.opendaylight.yangtools.websocket.server;
17
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;
25
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;
45
46 /**
47  * Handles handshakes and messages
48  */
49 @Deprecated
50 public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object> {
51     private static final Logger logger = LoggerFactory.getLogger(WebSocketServerHandler.class.getName());
52
53     private static final String WEBSOCKET_PATH = "/websocket";
54
55     private WebSocketServerHandshaker handshaker;
56
57     @Override
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);
63         }
64     }
65
66     @Override
67     public void channelReadComplete(final ChannelHandlerContext ctx) throws Exception {
68         ctx.flush();
69     }
70
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));
75             return;
76         }
77
78         // Allow only GET methods.
79         if (req.getMethod() != GET) {
80             sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
81             return;
82         }
83
84
85         // Handshake
86         WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
87                 getWebSocketLocation(req), null, false);
88         handshaker = wsFactory.newHandshaker(req);
89         if (handshaker == null) {
90             WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
91         } else {
92             handshaker.handshake(ctx.channel(), req);
93         }
94     }
95
96     private void handleWebSocketFrame(final ChannelHandlerContext ctx, final WebSocketFrame frame) {
97
98         // Check for closing frame
99         if (frame instanceof CloseWebSocketFrame) {
100             handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
101             return;
102         }
103         if (frame instanceof PingWebSocketFrame) {
104             ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
105             return;
106         }
107         if (!(frame instanceof TextWebSocketFrame)) {
108             throw new UnsupportedOperationException(String.format("%s frame types not supported", frame.getClass()
109                     .getName()));
110         }
111
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()));
116     }
117
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);
124             buf.release();
125             setContentLength(res, res.content().readableBytes());
126         }
127
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);
132         }
133     }
134
135     @Override
136     public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {
137         logger.info("Exception caught {}",cause);
138         ctx.close();
139     }
140
141     private static String getWebSocketLocation(final FullHttpRequest req) {
142         return "ws://" + req.headers().get(HOST) + WEBSOCKET_PATH;
143     }
144
145
146 }