View Javadoc

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.jboss.netty.example.http.websocketx.autobahn;
17  
18  import static org.jboss.netty.handler.codec.http.HttpHeaders.*;
19  import static org.jboss.netty.handler.codec.http.HttpMethod.*;
20  import static org.jboss.netty.handler.codec.http.HttpResponseStatus.*;
21  import static org.jboss.netty.handler.codec.http.HttpVersion.*;
22  
23  import org.jboss.netty.buffer.ChannelBuffers;
24  import org.jboss.netty.channel.ChannelFuture;
25  import org.jboss.netty.channel.ChannelFutureListener;
26  import org.jboss.netty.channel.ChannelHandlerContext;
27  import org.jboss.netty.channel.ExceptionEvent;
28  import org.jboss.netty.channel.MessageEvent;
29  import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
30  import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
31  import org.jboss.netty.handler.codec.http.HttpHeaders;
32  import org.jboss.netty.handler.codec.http.HttpRequest;
33  import org.jboss.netty.handler.codec.http.HttpResponse;
34  import org.jboss.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
35  import org.jboss.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
36  import org.jboss.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame;
37  import org.jboss.netty.handler.codec.http.websocketx.PingWebSocketFrame;
38  import org.jboss.netty.handler.codec.http.websocketx.PongWebSocketFrame;
39  import org.jboss.netty.handler.codec.http.websocketx.TextWebSocketFrame;
40  import org.jboss.netty.handler.codec.http.websocketx.WebSocketFrame;
41  import org.jboss.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
42  import org.jboss.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
43  import org.jboss.netty.logging.InternalLogger;
44  import org.jboss.netty.logging.InternalLoggerFactory;
45  import org.jboss.netty.util.CharsetUtil;
46  
47  /**
48   * Handles handshakes and messages
49   */
50  public class AutobahnServerHandler extends SimpleChannelUpstreamHandler {
51      private static final InternalLogger logger = InternalLoggerFactory.getInstance(AutobahnServerHandler.class);
52  
53      private WebSocketServerHandshaker handshaker;
54  
55      @Override
56      public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
57          Object msg = e.getMessage();
58          if (msg instanceof HttpRequest) {
59              handleHttpRequest(ctx, (HttpRequest) msg);
60          } else if (msg instanceof WebSocketFrame) {
61              handleWebSocketFrame(ctx, (WebSocketFrame) msg);
62          }
63      }
64  
65      private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req) throws Exception {
66          // Allow only GET methods.
67          if (req.getMethod() != GET) {
68              sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN));
69              return;
70          }
71  
72          // Handshake
73          WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
74                  getWebSocketLocation(req), null, false);
75          handshaker = wsFactory.newHandshaker(req);
76          if (handshaker == null) {
77              wsFactory.sendUnsupportedWebSocketVersionResponse(ctx.getChannel());
78          } else {
79              handshaker.handshake(ctx.getChannel(), req).addListener(WebSocketServerHandshaker.HANDSHAKE_LISTENER);
80          }
81      }
82  
83      private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
84          if (logger.isDebugEnabled()) {
85              logger.debug(String
86                      .format("Channel %s received %s", ctx.getChannel().getId(), frame.getClass().getSimpleName()));
87          }
88  
89  
90          if (frame instanceof CloseWebSocketFrame) {
91              handshaker.close(ctx.getChannel(), (CloseWebSocketFrame) frame);
92          } else if (frame instanceof PingWebSocketFrame) {
93              ctx.getChannel().write(
94                      new PongWebSocketFrame(frame.isFinalFragment(), frame.getRsv(), frame.getBinaryData()));
95          } else if (frame instanceof TextWebSocketFrame) {
96              // String text = ((TextWebSocketFrame) frame).getText();
97              ctx.getChannel().write(
98                      new TextWebSocketFrame(frame.isFinalFragment(), frame.getRsv(), frame.getBinaryData()));
99          } else if (frame instanceof BinaryWebSocketFrame) {
100             ctx.getChannel().write(
101                     new BinaryWebSocketFrame(frame.isFinalFragment(), frame.getRsv(), frame.getBinaryData()));
102         } else if (frame instanceof ContinuationWebSocketFrame) {
103             ctx.getChannel().write(
104                     new ContinuationWebSocketFrame(frame.isFinalFragment(), frame.getRsv(), frame.getBinaryData()));
105         } else if (frame instanceof PongWebSocketFrame) {
106             // Ignore
107         } else {
108             throw new UnsupportedOperationException(String.format("%s frame types not supported", frame.getClass()
109                     .getName()));
110         }
111     }
112 
113     private static void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) {
114         // Generate an error page if response status code is not OK (200).
115         if (res.getStatus().getCode() != 200) {
116             res.setContent(ChannelBuffers.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8));
117             setContentLength(res, res.getContent().readableBytes());
118         }
119 
120         // Send the response and close the connection if necessary.
121         ChannelFuture f = ctx.getChannel().write(res);
122         if (!isKeepAlive(req) || res.getStatus().getCode() != 200) {
123             f.addListener(ChannelFutureListener.CLOSE);
124         }
125     }
126 
127     @Override
128     public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
129         e.getCause().printStackTrace();
130         e.getChannel().close();
131     }
132 
133     private static String getWebSocketLocation(HttpRequest req) {
134         return "ws://" + req.getHeader(HttpHeaders.Names.HOST);
135     }
136 }