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