View Javadoc

    1   /*
    2    * Copyright 2010 Red Hat, Inc.
    3    *
    4    * Red Hat licenses this file to you under the Apache License, version 2.0
    5    * (the "License"); you may not use this file except in compliance with the
    6    * 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.handler.codec.http.websocket;
    17
    18  import org.jboss.netty.buffer.ChannelBuffer;
    19  import org.jboss.netty.channel.Channel;
    20  import org.jboss.netty.channel.ChannelHandlerContext;
    21  import org.jboss.netty.handler.codec.frame.TooLongFrameException;
    22  import org.jboss.netty.handler.codec.replay.ReplayingDecoder;
    23  import org.jboss.netty.handler.codec.replay.VoidEnum;
    24
    25  /**
    26   * Decodes {@link ChannelBuffer}s into {@link WebSocketFrame}s.
    27   * <p>
    28   * For the detailed instruction on adding add Web Socket support to your HTTP
    29   * server, take a look into the <tt>WebSocketServer</tt> example located in the
    30   * {@code org.jboss.netty.example.http.websocket} package.
    31   *
    32   * @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
    33   * @author Mike Heath (mheath@apache.org)
    34   * @author <a href="http://gleamynode.net/">Trustin Lee</a>
    35   * @version $Rev: 2342 $, $Date: 2010-07-07 14:07:39 +0900 (Wed, 07 Jul 2010) $
    36   *
    37   * @apiviz.landmark
    38   * @apiviz.uses org.jboss.netty.handler.codec.http.websocket.WebSocketFrame
    39   */
    40  public class WebSocketFrameDecoder extends ReplayingDecoder<VoidEnum> {
    41
    42      public static final int DEFAULT_MAX_FRAME_SIZE = 16384;
    43
    44      private final int maxFrameSize;
    45      private boolean receivedClosingHandshake;
    46
    47      public WebSocketFrameDecoder() {
    48          this(DEFAULT_MAX_FRAME_SIZE);
    49      }
    50
    51      /**
    52       * Creates a new instance of {@code WebSocketFrameDecoder} with the specified {@code maxFrameSize}.  If the client
    53       * sends a frame size larger than {@code maxFrameSize}, the channel will be closed.
    54       *
    55       * @param maxFrameSize  the maximum frame size to decode
    56       */
    57      public WebSocketFrameDecoder(int maxFrameSize) {
    58          this.maxFrameSize = maxFrameSize;
    59      }
    60
    61      @Override
    62      protected Object decode(ChannelHandlerContext ctx, Channel channel,
    63              ChannelBuffer buffer, VoidEnum state) throws Exception {
    64
    65          // Discard all data received if closing handshake was received before.
    66          if (receivedClosingHandshake) {
    67              buffer.skipBytes(actualReadableBytes());
    68              return null;
    69          }
    70
    71          // Decode a frame otherwise.
    72          byte type = buffer.readByte();
    73          if ((type & 0x80) == 0x80) {
    74              // If the MSB on type is set, decode the frame length
    75              return decodeBinaryFrame(type, buffer);
    76          } else {
    77              // Decode a 0xff terminated UTF-8 string
    78              return decodeTextFrame(type, buffer);
    79          }
    80      }
    81
    82      private WebSocketFrame decodeBinaryFrame(int type, ChannelBuffer buffer) throws TooLongFrameException {
    83          long frameSize = 0;
    84          int lengthFieldSize = 0;
    85          byte b;
    86          do {
    87              b = buffer.readByte();
    88              frameSize <<= 7;
    89              frameSize |= b & 0x7f;
    90              if (frameSize > maxFrameSize) {
    91                  throw new TooLongFrameException();
    92              }
    93              lengthFieldSize ++;
    94              if (lengthFieldSize > 8) {
    95                  // Perhaps a malicious peer?
    96                  throw new TooLongFrameException();
    97              }
    98          } while ((b & 0x80) == 0x80);
    99
    100         if (type == 0xFF && frameSize == 0) {
    101             receivedClosingHandshake = true;
    102         }
    103
    104         return new DefaultWebSocketFrame(
    105                 type, buffer.readBytes((int) frameSize));
    106     }
    107
    108     private WebSocketFrame decodeTextFrame(int type, ChannelBuffer buffer) throws TooLongFrameException {
    109         int ridx = buffer.readerIndex();
    110         int rbytes = actualReadableBytes();
    111         int delimPos = buffer.indexOf(ridx, ridx + rbytes, (byte) 0xFF);
    112         if (delimPos == -1) {
    113             // Frame delimiter (0xFF) not found
    114             if (rbytes > maxFrameSize) {
    115                 // Frame length exceeded the maximum
    116                 throw new TooLongFrameException();
    117             } else {
    118                 // Wait until more data is received
    119                 return null;
    120             }
    121         }
    122
    123         int frameSize = delimPos - ridx;
    124         if (frameSize > maxFrameSize) {
    125             throw new TooLongFrameException();
    126         }
    127
    128         ChannelBuffer binaryData = buffer.readBytes(frameSize);
    129         buffer.skipBytes(1);
    130         return new DefaultWebSocketFrame(type, binaryData);
    131     }
    132 }