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.handler.codec.http.websocketx;
    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 server, take a look into the
    29   * <tt>WebSocketServer</tt> example located in the {@code org.jboss.netty.example.http.websocket} package.
    30   *
    31   * @apiviz.landmark
    32   * @apiviz.uses org.jboss.netty.handler.codec.http.websocket.WebSocketFrame
    33   */
    34  public class WebSocket00FrameDecoder extends ReplayingDecoder<VoidEnum> {
    35
    36      private static final int DEFAULT_MAX_FRAME_SIZE = 16384;
    37
    38      private final long maxFrameSize;
    39      private boolean receivedClosingHandshake;
    40
    41      public WebSocket00FrameDecoder() {
    42          this(DEFAULT_MAX_FRAME_SIZE);
    43      }
    44
    45      /**
    46       * Creates a new instance of {@code WebSocketFrameDecoder} with the specified {@code maxFrameSize}. If the client
    47       * sends a frame size larger than {@code maxFrameSize}, the channel will be closed.
    48       *
    49       * @param maxFrameSize
    50       *            the maximum frame size to decode
    51       * @deprecated
    52       */
    53      @Deprecated
    54      public WebSocket00FrameDecoder(int maxFrameSize) {
    55          this.maxFrameSize = maxFrameSize;
    56      }
    57
    58      /**
    59       * Creates a new instance of {@code WebSocketFrameDecoder} with the specified {@code maxFrameSize}. If the client
    60       * sends a frame size larger than {@code maxFrameSize}, the channel will be closed.
    61       *
    62       * @param maxFrameSize
    63       *            the maximum frame size to decode
    64       */
    65      public WebSocket00FrameDecoder(long maxFrameSize) {
    66          this.maxFrameSize = maxFrameSize;
    67      }
    68
    69      @Override
    70      protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, VoidEnum state)
    71              throws Exception {
    72
    73          // Discard all data received if closing handshake was received before.
    74          if (receivedClosingHandshake) {
    75              buffer.skipBytes(actualReadableBytes());
    76              return null;
    77          }
    78
    79          // Decode a frame otherwise.
    80          byte type = buffer.readByte();
    81          if ((type & 0x80) == 0x80) {
    82              // If the MSB on type is set, decode the frame length
    83              return decodeBinaryFrame(type, buffer);
    84          } else {
    85              // Decode a 0xff terminated UTF-8 string
    86              return decodeTextFrame(buffer);
    87          }
    88      }
    89
    90      private WebSocketFrame decodeBinaryFrame(byte type, ChannelBuffer buffer) throws TooLongFrameException {
    91          long frameSize = 0;
    92          int lengthFieldSize = 0;
    93          byte b;
    94          do {
    95              b = buffer.readByte();
    96              frameSize <<= 7;
    97              frameSize |= b & 0x7f;
    98              if (frameSize > maxFrameSize) {
    99                  throw new TooLongFrameException();
    100             }
    101             lengthFieldSize++;
    102             if (lengthFieldSize > 8) {
    103                 // Perhaps a malicious peer?
    104                 throw new TooLongFrameException();
    105             }
    106         } while ((b & 0x80) == 0x80);
    107
    108         if (type == (byte) 0xFF && frameSize == 0) {
    109             receivedClosingHandshake = true;
    110             return new CloseWebSocketFrame();
    111         }
    112
    113         return new BinaryWebSocketFrame(buffer.readBytes((int) frameSize));
    114     }
    115
    116     private WebSocketFrame decodeTextFrame(ChannelBuffer buffer) throws TooLongFrameException {
    117         int ridx = buffer.readerIndex();
    118         int rbytes = actualReadableBytes();
    119         int delimPos = buffer.indexOf(ridx, ridx + rbytes, (byte) 0xFF);
    120         if (delimPos == -1) {
    121             // Frame delimiter (0xFF) not found
    122             if (rbytes > maxFrameSize) {
    123                 // Frame length exceeded the maximum
    124                 throw new TooLongFrameException();
    125             } else {
    126                 // Wait until more data is received
    127                 return null;
    128             }
    129         }
    130
    131         int frameSize = delimPos - ridx;
    132         if (frameSize > maxFrameSize) {
    133             throw new TooLongFrameException();
    134         }
    135
    136         ChannelBuffer binaryData = buffer.readBytes(frameSize);
    137         buffer.skipBytes(1);
    138
    139         int ffDelimPos = binaryData.indexOf(binaryData.readerIndex(), binaryData.writerIndex(), (byte) 0xFF);
    140         if (ffDelimPos >= 0) {
    141             throw new IllegalArgumentException("a text frame should not contain 0xFF.");
    142         }
    143
    144         return new TextWebSocketFrame(binaryData);
    145     }
    146 }