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