View Javadoc
1   /*
2    * Copyright 2013 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  
19  import org.jboss.netty.buffer.ChannelBuffer;
20  import org.jboss.netty.buffer.ChannelBuffers;
21  import org.jboss.netty.channel.Channel;
22  import org.jboss.netty.channel.ChannelHandlerContext;
23  import org.jboss.netty.handler.codec.frame.TooLongFrameException;
24  import org.jboss.netty.handler.codec.oneone.OneToOneDecoder;
25  
26  /**
27   * Handler that aggregate fragmented WebSocketFrame's.
28   *
29   * Be aware if PING/PONG/CLOSE frames are send in the middle of a fragmented {@link WebSocketFrame} they will
30   * just get forwarded to the next handler in the pipeline.
31   */
32  public class WebSocketFrameAggregator extends OneToOneDecoder {
33      private final int maxFrameSize;
34      private WebSocketFrame currentFrame;
35      private boolean tooLongFrameFound;
36  
37      /**
38       * Construct a new instance
39       *
40       * @param maxFrameSize      If the size of the aggregated frame exceeds this value,
41       *                          a {@link TooLongFrameException} is thrown.
42       */
43      public WebSocketFrameAggregator(int maxFrameSize) {
44          if (maxFrameSize < 1) {
45              throw new IllegalArgumentException("maxFrameSize must be > 0");
46          }
47          this.maxFrameSize = maxFrameSize;
48      }
49  
50      @Override
51      protected Object decode(ChannelHandlerContext ctx, Channel channel, Object message) throws Exception {
52          if (!(message instanceof WebSocketFrame)) {
53              return message;
54          }
55          WebSocketFrame msg = (WebSocketFrame) message;
56          if (currentFrame == null) {
57              tooLongFrameFound = false;
58              if (msg.isFinalFragment()) {
59                  return msg;
60              }
61              ChannelBuffer buf = msg.getBinaryData();
62  
63              if (msg instanceof TextWebSocketFrame) {
64                  currentFrame = new TextWebSocketFrame(true, msg.getRsv(), buf);
65              } else if (msg instanceof BinaryWebSocketFrame) {
66                  currentFrame = new BinaryWebSocketFrame(true, msg.getRsv(), buf);
67              } else {
68                  throw new IllegalStateException(
69                          "WebSocket frame was not of type TextWebSocketFrame or BinaryWebSocketFrame");
70              }
71              return null;
72          }
73          if (msg instanceof ContinuationWebSocketFrame) {
74              if (tooLongFrameFound) {
75                  if (msg.isFinalFragment()) {
76                      currentFrame = null;
77                  }
78                  return null;
79              }
80              ChannelBuffer content = currentFrame.getBinaryData();
81              if (content.readableBytes() > maxFrameSize - msg.getBinaryData().readableBytes()) {
82                  tooLongFrameFound = true;
83                  throw new TooLongFrameException(
84                          "WebSocketFrame length exceeded " + content +
85                                  " bytes.");
86              }
87              currentFrame.setBinaryData(ChannelBuffers.wrappedBuffer(content, msg.getBinaryData()));
88  
89              if (msg.isFinalFragment()) {
90                  WebSocketFrame currentFrame = this.currentFrame;
91                  this.currentFrame = null;
92                  return currentFrame;
93              } else {
94                  return null;
95              }
96          }
97          // It is possible to receive CLOSE/PING/PONG frames during fragmented frames so just pass them to the next
98          // handler in the chain
99          return msg;
100     }
101 }