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 }