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 }