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