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.frame;
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.channel.Channels;
22  
23  /**
24   * A decoder that splits the received {@link ChannelBuffer}s dynamically by the
25   * value of the length field in the message.  It is particularly useful when you
26   * decode a binary message which has an integer header field that represents the
27   * length of the message body or the whole message.
28   * <p>
29   * {@link LengthFieldBasedFrameDecoder} has many configuration parameters so
30   * that it can decode any message with a length field, which is often seen in
31   * proprietary client-server protocols. Here are some example that will give
32   * you the basic idea on which option does what.
33   *
34   * <h3>2 bytes length field at offset 0, do not strip header</h3>
35   *
36   * The value of the length field in this example is <tt>12 (0x0C)</tt> which
37   * represents the length of "HELLO, WORLD".  By default, the decoder assumes
38   * that the length field represents the number of the bytes that follows the
39   * length field.  Therefore, it can be decoded with the simplistic parameter
40   * combination.
41   * <pre>
42   * <b>lengthFieldOffset</b>   = <b>0</b>
43   * <b>lengthFieldLength</b>   = <b>2</b>
44   * lengthAdjustment    = 0
45   * initialBytesToStrip = 0 (= do not strip header)
46   *
47   * BEFORE DECODE (14 bytes)         AFTER DECODE (14 bytes)
48   * +--------+----------------+      +--------+----------------+
49   * | Length | Actual Content |----->| Length | Actual Content |
50   * | 0x000C | "HELLO, WORLD" |      | 0x000C | "HELLO, WORLD" |
51   * +--------+----------------+      +--------+----------------+
52   * </pre>
53   *
54   * <h3>2 bytes length field at offset 0, strip header</h3>
55   *
56   * Because we can get the length of the content by calling
57   * {@link ChannelBuffer#readableBytes()}, you might want to strip the length
58   * field by specifying <tt>initialBytesToStrip</tt>.  In this example, we
59   * specified <tt>2</tt>, that is same with the length of the length field, to
60   * strip the first two bytes.
61   * <pre>
62   * lengthFieldOffset   = 0
63   * lengthFieldLength   = 2
64   * lengthAdjustment    = 0
65   * <b>initialBytesToStrip</b> = <b>2</b> (= the length of the Length field)
66   *
67   * BEFORE DECODE (14 bytes)         AFTER DECODE (12 bytes)
68   * +--------+----------------+      +----------------+
69   * | Length | Actual Content |----->| Actual Content |
70   * | 0x000C | "HELLO, WORLD" |      | "HELLO, WORLD" |
71   * +--------+----------------+      +----------------+
72   * </pre>
73   *
74   * <h3>2 bytes length field at offset 0, do not strip header, the length field
75   *     represents the length of the whole message</h3>
76   *
77   * In most cases, the length field represents the length of the message body
78   * only, as shown in the previous examples.  However, in some protocols, the
79   * length field represents the length of the whole message, including the
80   * message header.  In such a case, we specify a non-zero
81   * <tt>lengthAdjustment</tt>.  Because the length value in this example message
82   * is always greater than the body length by <tt>2</tt>, we specify <tt>-2</tt>
83   * as <tt>lengthAdjustment</tt> for compensation.
84   * <pre>
85   * lengthFieldOffset   =  0
86   * lengthFieldLength   =  2
87   * <b>lengthAdjustment</b>    = <b>-2</b> (= the length of the Length field)
88   * initialBytesToStrip =  0
89   *
90   * BEFORE DECODE (14 bytes)         AFTER DECODE (14 bytes)
91   * +--------+----------------+      +--------+----------------+
92   * | Length | Actual Content |----->| Length | Actual Content |
93   * | 0x000E | "HELLO, WORLD" |      | 0x000E | "HELLO, WORLD" |
94   * +--------+----------------+      +--------+----------------+
95   * </pre>
96   *
97   * <h3>3 bytes length field at the end of 5 bytes header, do not strip header</h3>
98   *
99   * The following message is a simple variation of the first example.  An extra
100  * header value is prepended to the message.  <tt>lengthAdjustment</tt> is zero
101  * again because the decoder always takes the length of the prepended data into
102  * account during frame length calculation.
103  * <pre>
104  * <b>lengthFieldOffset</b>   = <b>2</b> (= the length of Header 1)
105  * <b>lengthFieldLength</b>   = <b>3</b>
106  * lengthAdjustment    = 0
107  * initialBytesToStrip = 0
108  *
109  * BEFORE DECODE (17 bytes)                      AFTER DECODE (17 bytes)
110  * +----------+----------+----------------+      +----------+----------+----------------+
111  * | Header 1 |  Length  | Actual Content |----->| Header 1 |  Length  | Actual Content |
112  * |  0xCAFE  | 0x00000C | "HELLO, WORLD" |      |  0xCAFE  | 0x00000C | "HELLO, WORLD" |
113  * +----------+----------+----------------+      +----------+----------+----------------+
114  * </pre>
115  *
116  * <h3>3 bytes length field at the beginning of 5 bytes header, do not strip header</h3>
117  *
118  * This is an advanced example that shows the case where there is an extra
119  * header between the length field and the message body.  You have to specify a
120  * positive <tt>lengthAdjustment</tt> so that the decoder counts the extra
121  * header into the frame length calculation.
122  * <pre>
123  * lengthFieldOffset   = 0
124  * lengthFieldLength   = 3
125  * <b>lengthAdjustment</b>    = <b>2</b> (= the length of Header 1)
126  * initialBytesToStrip = 0
127  *
128  * BEFORE DECODE (17 bytes)                      AFTER DECODE (17 bytes)
129  * +----------+----------+----------------+      +----------+----------+----------------+
130  * |  Length  | Header 1 | Actual Content |----->|  Length  | Header 1 | Actual Content |
131  * | 0x00000C |  0xCAFE  | "HELLO, WORLD" |      | 0x00000C |  0xCAFE  | "HELLO, WORLD" |
132  * +----------+----------+----------------+      +----------+----------+----------------+
133  * </pre>
134  *
135  * <h3>2 bytes length field at offset 1 in the middle of 4 bytes header,
136  *     strip the first header field and the length field</h3>
137  *
138  * This is a combination of all the examples above.  There are the prepended
139  * header before the length field and the extra header after the length field.
140  * The prepended header affects the <tt>lengthFieldOffset</tt> and the extra
141  * header affects the <tt>lengthAdjustment</tt>.  We also specified a non-zero
142  * <tt>initialBytesToStrip</tt> to strip the length field and the prepended
143  * header from the frame.  If you don't want to strip the prepended header, you
144  * could specify <tt>0</tt> for <tt>initialBytesToSkip</tt>.
145  * <pre>
146  * lengthFieldOffset   = 1 (= the length of HDR1)
147  * lengthFieldLength   = 2
148  * <b>lengthAdjustment</b>    = <b>1</b> (= the length of HDR2)
149  * <b>initialBytesToStrip</b> = <b>3</b> (= the length of HDR1 + LEN)
150  *
151  * BEFORE DECODE (16 bytes)                       AFTER DECODE (13 bytes)
152  * +------+--------+------+----------------+      +------+----------------+
153  * | HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content |
154  * | 0xCA | 0x000C | 0xFE | "HELLO, WORLD" |      | 0xFE | "HELLO, WORLD" |
155  * +------+--------+------+----------------+      +------+----------------+
156  * </pre>
157  *
158  * <h3>2 bytes length field at offset 1 in the middle of 4 bytes header,
159  *     strip the first header field and the length field, the length field
160  *     represents the length of the whole message</h3>
161  *
162  * Let's give another twist to the previous example.  The only difference from
163  * the previous example is that the length field represents the length of the
164  * whole message instead of the message body, just like the third example.
165  * We have to count the length of HDR1 and Length into <tt>lengthAdjustment</tt>.
166  * Please note that we don't need to take the length of HDR2 into account
167  * because the length field already includes the whole header length.
168  * <pre>
169  * lengthFieldOffset   =  1
170  * lengthFieldLength   =  2
171  * <b>lengthAdjustment</b>    = <b>-3</b> (= the length of HDR1 + LEN, negative)
172  * <b>initialBytesToStrip</b> = <b> 3</b>
173  *
174  * BEFORE DECODE (16 bytes)                       AFTER DECODE (13 bytes)
175  * +------+--------+------+----------------+      +------+----------------+
176  * | HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content |
177  * | 0xCA | 0x0010 | 0xFE | "HELLO, WORLD" |      | 0xFE | "HELLO, WORLD" |
178  * +------+--------+------+----------------+      +------+----------------+
179  * </pre>
180  *
181  * @see LengthFieldPrepender
182  */
183 public class LengthFieldBasedFrameDecoder extends FrameDecoder {
184 
185     private final int maxFrameLength;
186     private final int lengthFieldOffset;
187     private final int lengthFieldLength;
188     private final int lengthFieldEndOffset;
189     private final int lengthAdjustment;
190     private final int initialBytesToStrip;
191     private final boolean failFast;
192     private boolean discardingTooLongFrame;
193     private long tooLongFrameLength;
194     private long bytesToDiscard;
195 
196     /**
197      * Creates a new instance.
198      *
199      * @param maxFrameLength
200      *        the maximum length of the frame.  If the length of the frame is
201      *        greater than this value, {@link TooLongFrameException} will be
202      *        thrown.
203      * @param lengthFieldOffset
204      *        the offset of the length field
205      * @param lengthFieldLength
206      *        the length of the length field
207      */
208     public LengthFieldBasedFrameDecoder(
209             int maxFrameLength,
210             int lengthFieldOffset, int lengthFieldLength) {
211         this(maxFrameLength, lengthFieldOffset, lengthFieldLength, 0, 0);
212     }
213 
214     /**
215      * Creates a new instance.
216      *
217      * @param maxFrameLength
218      *        the maximum length of the frame.  If the length of the frame is
219      *        greater than this value, {@link TooLongFrameException} will be
220      *        thrown.
221      * @param lengthFieldOffset
222      *        the offset of the length field
223      * @param lengthFieldLength
224      *        the length of the length field
225      * @param lengthAdjustment
226      *        the compensation value to add to the value of the length field
227      * @param initialBytesToStrip
228      *        the number of first bytes to strip out from the decoded frame
229      */
230     public LengthFieldBasedFrameDecoder(
231             int maxFrameLength,
232             int lengthFieldOffset, int lengthFieldLength,
233             int lengthAdjustment, int initialBytesToStrip) {
234         this(
235                 maxFrameLength,
236                 lengthFieldOffset, lengthFieldLength, lengthAdjustment,
237                 initialBytesToStrip, false);
238     }
239 
240     /**
241      * Creates a new instance.
242      *
243      * @param maxFrameLength
244      *        the maximum length of the frame.  If the length of the frame is
245      *        greater than this value, {@link TooLongFrameException} will be
246      *        thrown.
247      * @param lengthFieldOffset
248      *        the offset of the length field
249      * @param lengthFieldLength
250      *        the length of the length field
251      * @param lengthAdjustment
252      *        the compensation value to add to the value of the length field
253      * @param initialBytesToStrip
254      *        the number of first bytes to strip out from the decoded frame
255      * @param failFast
256      *        If <tt>true</tt>, a {@link TooLongFrameException} is thrown as
257      *        soon as the decoder notices the length of the frame will exceed
258      *        <tt>maxFrameLength</tt> regardless of whether the entire frame
259      *        has been read.  If <tt>false</tt>, a {@link TooLongFrameException}
260      *        is thrown after the entire frame that exceeds <tt>maxFrameLength</tt>
261      *        has been read.
262      */
263     public LengthFieldBasedFrameDecoder(
264             int maxFrameLength,
265             int lengthFieldOffset, int lengthFieldLength,
266             int lengthAdjustment, int initialBytesToStrip, boolean failFast) {
267         if (maxFrameLength <= 0) {
268             throw new IllegalArgumentException(
269                     "maxFrameLength must be a positive integer: " +
270                     maxFrameLength);
271         }
272 
273         if (lengthFieldOffset < 0) {
274             throw new IllegalArgumentException(
275                     "lengthFieldOffset must be a non-negative integer: " +
276                     lengthFieldOffset);
277         }
278 
279         if (initialBytesToStrip < 0) {
280             throw new IllegalArgumentException(
281                     "initialBytesToStrip must be a non-negative integer: " +
282                     initialBytesToStrip);
283         }
284 
285         if (lengthFieldLength != 1 && lengthFieldLength != 2 &&
286             lengthFieldLength != 3 && lengthFieldLength != 4 &&
287             lengthFieldLength != 8) {
288             throw new IllegalArgumentException(
289                     "lengthFieldLength must be either 1, 2, 3, 4, or 8: " +
290                     lengthFieldLength);
291         }
292 
293         if (lengthFieldOffset > maxFrameLength - lengthFieldLength) {
294             throw new IllegalArgumentException(
295                     "maxFrameLength (" + maxFrameLength + ") " +
296                     "must be equal to or greater than " +
297                     "lengthFieldOffset (" + lengthFieldOffset + ") + " +
298                     "lengthFieldLength (" + lengthFieldLength + ").");
299         }
300 
301         this.maxFrameLength = maxFrameLength;
302         this.lengthFieldOffset = lengthFieldOffset;
303         this.lengthFieldLength = lengthFieldLength;
304         this.lengthAdjustment = lengthAdjustment;
305         lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength;
306         this.initialBytesToStrip = initialBytesToStrip;
307         this.failFast = failFast;
308     }
309 
310     @Override
311     protected Object decode(
312             ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
313 
314         if (discardingTooLongFrame) {
315             long bytesToDiscard = this.bytesToDiscard;
316             int localBytesToDiscard = (int) Math.min(bytesToDiscard, buffer.readableBytes());
317             buffer.skipBytes(localBytesToDiscard);
318             bytesToDiscard -= localBytesToDiscard;
319             this.bytesToDiscard = bytesToDiscard;
320             failIfNecessary(ctx, false);
321             return null;
322         }
323 
324         if (buffer.readableBytes() < lengthFieldEndOffset) {
325             return null;
326         }
327 
328         int actualLengthFieldOffset = buffer.readerIndex() + lengthFieldOffset;
329         long frameLength;
330         switch (lengthFieldLength) {
331         case 1:
332             frameLength = buffer.getUnsignedByte(actualLengthFieldOffset);
333             break;
334         case 2:
335             frameLength = buffer.getUnsignedShort(actualLengthFieldOffset);
336             break;
337         case 3:
338             frameLength = buffer.getUnsignedMedium(actualLengthFieldOffset);
339             break;
340         case 4:
341             frameLength = buffer.getUnsignedInt(actualLengthFieldOffset);
342             break;
343         case 8:
344             frameLength = buffer.getLong(actualLengthFieldOffset);
345             break;
346         default:
347             throw new Error("should not reach here");
348         }
349 
350         if (frameLength < 0) {
351             buffer.skipBytes(lengthFieldEndOffset);
352             throw new CorruptedFrameException(
353                     "negative pre-adjustment length field: " + frameLength);
354         }
355 
356         frameLength += lengthAdjustment + lengthFieldEndOffset;
357         if (frameLength < lengthFieldEndOffset) {
358             buffer.skipBytes(lengthFieldEndOffset);
359             throw new CorruptedFrameException(
360                     "Adjusted frame length (" + frameLength + ") is less " +
361                     "than lengthFieldEndOffset: " + lengthFieldEndOffset);
362         }
363 
364         if (frameLength > maxFrameLength) {
365             // Enter the discard mode and discard everything received so far.
366             discardingTooLongFrame = true;
367             tooLongFrameLength = frameLength;
368             bytesToDiscard = frameLength - buffer.readableBytes();
369             buffer.skipBytes(buffer.readableBytes());
370             failIfNecessary(ctx, true);
371             return null;
372         }
373 
374         // never overflows because it's less than maxFrameLength
375         int frameLengthInt = (int) frameLength;
376         if (buffer.readableBytes() < frameLengthInt) {
377             return null;
378         }
379 
380         if (initialBytesToStrip > frameLengthInt) {
381             buffer.skipBytes(frameLengthInt);
382             throw new CorruptedFrameException(
383                     "Adjusted frame length (" + frameLength + ") is less " +
384                     "than initialBytesToStrip: " + initialBytesToStrip);
385         }
386         buffer.skipBytes(initialBytesToStrip);
387 
388         // extract frame
389         int readerIndex = buffer.readerIndex();
390         int actualFrameLength = frameLengthInt - initialBytesToStrip;
391         ChannelBuffer frame = extractFrame(buffer, readerIndex, actualFrameLength);
392         buffer.readerIndex(readerIndex + actualFrameLength);
393         return frame;
394     }
395 
396     private void failIfNecessary(ChannelHandlerContext ctx, boolean firstDetectionOfTooLongFrame) {
397         if (bytesToDiscard == 0) {
398             // Reset to the initial state and tell the handlers that
399             // the frame was too large.
400             long tooLongFrameLength = this.tooLongFrameLength;
401             this.tooLongFrameLength = 0;
402             discardingTooLongFrame = false;
403             if (!failFast ||
404                 failFast && firstDetectionOfTooLongFrame) {
405                 fail(ctx, tooLongFrameLength);
406             }
407         } else {
408             // Keep discarding and notify handlers if necessary.
409             if (failFast && firstDetectionOfTooLongFrame) {
410                 fail(ctx, tooLongFrameLength);
411             }
412         }
413     }
414 
415     private void fail(ChannelHandlerContext ctx, long frameLength) {
416         if (frameLength > 0) {
417             Channels.fireExceptionCaught(
418                     ctx.getChannel(),
419                     new TooLongFrameException(
420                             "Adjusted frame length exceeds " + maxFrameLength +
421                             ": " + frameLength + " - discarded"));
422         } else {
423             Channels.fireExceptionCaught(
424                     ctx.getChannel(),
425                     new TooLongFrameException(
426                             "Adjusted frame length exceeds " + maxFrameLength +
427                             " - discarding"));
428         }
429     }
430 }