View Javadoc

1   /*
2    * Copyright 2009 Red Hat, Inc.
3    *
4    * Red Hat licenses this file to you under the Apache License, version 2.0
5    * (the "License"); you may not use this file except in compliance with the
6    * 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</b>   = 1 (= the length of HDR1)
149  * lengthFieldLength</b>   = 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  * @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
184  * @author <a href="http://gleamynode.net/">Trustin Lee</a>
185  *
186  * @version $Rev:231 $, $Date:2008-06-12 16:44:50 +0900 (목, 12 6월 2008) $
187  *
188  * @see LengthFieldPrepender
189  */
190 public class LengthFieldBasedFrameDecoder extends FrameDecoder {
191 
192     private final int maxFrameLength;
193     private final int lengthFieldOffset;
194     private final int lengthFieldLength;
195     private final int lengthFieldEndOffset;
196     private final int lengthAdjustment;
197     private final int initialBytesToStrip;
198     private boolean discardingTooLongFrame;
199     private long tooLongFrameLength;
200     private long bytesToDiscard;
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      */
215     public LengthFieldBasedFrameDecoder(
216             int maxFrameLength,
217             int lengthFieldOffset, int lengthFieldLength) {
218         this(maxFrameLength, lengthFieldOffset, lengthFieldLength, 0, 0);
219     }
220 
221     /**
222      * Creates a new instance.
223      *
224      * @param maxFrameLength
225      *        the maximum length of the frame.  If the length of the frame is
226      *        greater than this value, {@link TooLongFrameException} will be
227      *        thrown.
228      * @param lengthFieldOffset
229      *        the offset of the length field
230      * @param lengthFieldLength
231      *        the length of the length field
232      * @param lengthAdjustment
233      *        the compensation value to add to the value of the length field
234      * @param initialBytesToStrip
235      *        the number of first bytes to strip out from the decoded frame
236      */
237     public LengthFieldBasedFrameDecoder(
238             int maxFrameLength,
239             int lengthFieldOffset, int lengthFieldLength,
240             int lengthAdjustment, int initialBytesToStrip) {
241         if (maxFrameLength <= 0) {
242             throw new IllegalArgumentException(
243                     "maxFrameLength must be a positive integer: " +
244                     maxFrameLength);
245         }
246 
247         if (lengthFieldOffset < 0) {
248             throw new IllegalArgumentException(
249                     "lengthFieldOffset must be a non-negative integer: " +
250                     lengthFieldOffset);
251         }
252 
253         if (initialBytesToStrip < 0) {
254             throw new IllegalArgumentException(
255                     "initialBytesToStrip must be a non-negative integer: " +
256                     initialBytesToStrip);
257         }
258 
259         if (lengthFieldLength != 1 && lengthFieldLength != 2 &&
260             lengthFieldLength != 3 && lengthFieldLength != 4 &&
261             lengthFieldLength != 8) {
262             throw new IllegalArgumentException(
263                     "lengthFieldLength must be either 1, 2, 3, 4, or 8: " +
264                     lengthFieldLength);
265         }
266 
267         if (lengthFieldOffset > maxFrameLength - lengthFieldLength) {
268             throw new IllegalArgumentException(
269                     "maxFrameLength (" + maxFrameLength + ") " +
270                     "must be equal to or greater than " +
271                     "lengthFieldOffset (" + lengthFieldOffset + ") + " +
272                     "lengthFieldLength (" + lengthFieldLength + ").");
273         }
274 
275         this.maxFrameLength = maxFrameLength;
276         this.lengthFieldOffset = lengthFieldOffset;
277         this.lengthFieldLength = lengthFieldLength;
278         this.lengthAdjustment = lengthAdjustment;
279         lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength;
280         this.initialBytesToStrip = initialBytesToStrip;
281     }
282 
283     @Override
284     protected Object decode(
285             ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
286 
287         if (discardingTooLongFrame) {
288             long bytesToDiscard = this.bytesToDiscard;
289             int localBytesToDiscard = (int) Math.min(bytesToDiscard, buffer.readableBytes());
290             buffer.skipBytes(localBytesToDiscard);
291             bytesToDiscard -= localBytesToDiscard;
292             this.bytesToDiscard = bytesToDiscard;
293             failIfNecessary(ctx);
294             return null;
295         }
296 
297         if (buffer.readableBytes() < lengthFieldEndOffset) {
298             return null;
299         }
300 
301         int actualLengthFieldOffset = buffer.readerIndex() + lengthFieldOffset;
302         long frameLength;
303         switch (lengthFieldLength) {
304         case 1:
305             frameLength = buffer.getUnsignedByte(actualLengthFieldOffset);
306             break;
307         case 2:
308             frameLength = buffer.getUnsignedShort(actualLengthFieldOffset);
309             break;
310         case 3:
311             frameLength = buffer.getUnsignedMedium(actualLengthFieldOffset);
312             break;
313         case 4:
314             frameLength = buffer.getUnsignedInt(actualLengthFieldOffset);
315             break;
316         case 8:
317             frameLength = buffer.getLong(actualLengthFieldOffset);
318             break;
319         default:
320             throw new Error("should not reach here");
321         }
322 
323         if (frameLength < 0) {
324             buffer.skipBytes(lengthFieldEndOffset);
325             throw new CorruptedFrameException(
326                     "negative pre-adjustment length field: " + frameLength);
327         }
328 
329         frameLength += lengthAdjustment + lengthFieldEndOffset;
330         if (frameLength < lengthFieldEndOffset) {
331             buffer.skipBytes(lengthFieldEndOffset);
332             throw new CorruptedFrameException(
333                     "Adjusted frame length (" + frameLength + ") is less " +
334                     "than lengthFieldEndOffset: " + lengthFieldEndOffset);
335         }
336 
337         if (frameLength > maxFrameLength) {
338             // Enter the discard mode and discard everything received so far.
339             discardingTooLongFrame = true;
340             tooLongFrameLength = frameLength;
341             bytesToDiscard = frameLength - buffer.readableBytes();
342             buffer.skipBytes(buffer.readableBytes());
343             failIfNecessary(ctx);
344             return null;
345         }
346 
347         // never overflows because it's less than maxFrameLength
348         int frameLengthInt = (int) frameLength;
349         if (buffer.readableBytes() < frameLengthInt) {
350             return null;
351         }
352 
353         if (initialBytesToStrip > frameLengthInt) {
354             buffer.skipBytes(frameLengthInt);
355             throw new CorruptedFrameException(
356                     "Adjusted frame length (" + frameLength + ") is less " +
357                     "than initialBytesToStrip: " + initialBytesToStrip);
358         }
359         buffer.skipBytes(initialBytesToStrip);
360 
361         // extract frame
362         int readerIndex = buffer.readerIndex();
363         int actualFrameLength = frameLengthInt - initialBytesToStrip;
364         ChannelBuffer frame = extractFrame(buffer, readerIndex, actualFrameLength);
365         buffer.readerIndex(readerIndex + actualFrameLength);
366         return frame;
367     }
368 
369     private void failIfNecessary(ChannelHandlerContext ctx) {
370         if (bytesToDiscard == 0) {
371             // Reset to the initial state and tell the handlers that
372             // the frame was too large.
373             // TODO Let user choose when the exception should be raised - early or late?
374             //      If early, fail() should be called when discardingTooLongFrame is set to true.
375             long tooLongFrameLength = this.tooLongFrameLength;
376             this.tooLongFrameLength = 0;
377             discardingTooLongFrame = false;
378             fail(ctx, tooLongFrameLength);
379         } else {
380             // Keep discarding.
381         }
382     }
383 
384     /**
385      * Extract the sub-region of the specified buffer. This method is called by
386      * {@link #decode(ChannelHandlerContext, Channel, ChannelBuffer)} for each
387      * frame.  The default implementation returns a copy of the sub-region.
388      * For example, you could override this method to use an alternative
389      * {@link ChannelBufferFactory}.
390      * <p>
391      * If you are sure that the frame and its content are not accessed after
392      * the current {@link #decode(ChannelHandlerContext, Channel, ChannelBuffer)}
393      * call returns, you can even avoid memory copy by returning the sliced
394      * sub-region (i.e. <tt>return buffer.slice(index, length)</tt>).
395      * It's often useful when you convert the extracted frame into an object.
396      * Refer to the source code of {@link ObjectDecoder} to see how this method
397      * is overridden to avoid memory copy.
398      */
399     protected ChannelBuffer extractFrame(ChannelBuffer buffer, int index, int length) {
400         ChannelBuffer frame = buffer.factory().getBuffer(length);
401         frame.writeBytes(buffer, index, length);
402         return frame;
403     }
404 
405     private void fail(ChannelHandlerContext ctx, long frameLength) {
406         if (frameLength > 0) {
407             Channels.fireExceptionCaught(
408                     ctx.getChannel(),
409                     new TooLongFrameException(
410                             "Adjusted frame length exceeds " + maxFrameLength +
411                             ": " + frameLength + " - discarded"));
412         } else {
413             Channels.fireExceptionCaught(
414                     ctx.getChannel(),
415                     new TooLongFrameException(
416                             "Adjusted frame length exceeds " + maxFrameLength +
417                             " - discarding"));
418         }
419     }
420 }