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 }