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