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