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 io.netty.handler.codec;
17
18 import java.nio.ByteOrder;
19 import java.util.List;
20
21 import io.netty.buffer.ByteBuf;
22 import io.netty.channel.ChannelHandlerContext;
23 import io.netty.handler.codec.serialization.ObjectDecoder;
24
25 /**
26 * A decoder that splits the received {@link ByteBuf}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 ByteBuf#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 * @see LengthFieldPrepender
183 */
184 public class LengthFieldBasedFrameDecoder extends ByteToMessageDecoder {
185
186 private final ByteOrder byteOrder;
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, true);
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, int lengthFieldOffset, int lengthFieldLength,
267 int lengthAdjustment, int initialBytesToStrip, boolean failFast) {
268 this(
269 ByteOrder.BIG_ENDIAN, maxFrameLength, lengthFieldOffset, lengthFieldLength,
270 lengthAdjustment, initialBytesToStrip, failFast);
271 }
272
273 /**
274 * Creates a new instance.
275 *
276 * @param byteOrder
277 * the {@link ByteOrder} of the length field
278 * @param maxFrameLength
279 * the maximum length of the frame. If the length of the frame is
280 * greater than this value, {@link TooLongFrameException} will be
281 * thrown.
282 * @param lengthFieldOffset
283 * the offset of the length field
284 * @param lengthFieldLength
285 * the length of the length field
286 * @param lengthAdjustment
287 * the compensation value to add to the value of the length field
288 * @param initialBytesToStrip
289 * the number of first bytes to strip out from the decoded frame
290 * @param failFast
291 * If <tt>true</tt>, a {@link TooLongFrameException} is thrown as
292 * soon as the decoder notices the length of the frame will exceed
293 * <tt>maxFrameLength</tt> regardless of whether the entire frame
294 * has been read. If <tt>false</tt>, a {@link TooLongFrameException}
295 * is thrown after the entire frame that exceeds <tt>maxFrameLength</tt>
296 * has been read.
297 */
298 public LengthFieldBasedFrameDecoder(
299 ByteOrder byteOrder, int maxFrameLength, int lengthFieldOffset, int lengthFieldLength,
300 int lengthAdjustment, int initialBytesToStrip, boolean failFast) {
301 if (byteOrder == null) {
302 throw new NullPointerException("byteOrder");
303 }
304
305 if (maxFrameLength <= 0) {
306 throw new IllegalArgumentException(
307 "maxFrameLength must be a positive integer: " +
308 maxFrameLength);
309 }
310
311 if (lengthFieldOffset < 0) {
312 throw new IllegalArgumentException(
313 "lengthFieldOffset must be a non-negative integer: " +
314 lengthFieldOffset);
315 }
316
317 if (initialBytesToStrip < 0) {
318 throw new IllegalArgumentException(
319 "initialBytesToStrip must be a non-negative integer: " +
320 initialBytesToStrip);
321 }
322
323 if (lengthFieldOffset > maxFrameLength - lengthFieldLength) {
324 throw new IllegalArgumentException(
325 "maxFrameLength (" + maxFrameLength + ") " +
326 "must be equal to or greater than " +
327 "lengthFieldOffset (" + lengthFieldOffset + ") + " +
328 "lengthFieldLength (" + lengthFieldLength + ").");
329 }
330
331 this.byteOrder = byteOrder;
332 this.maxFrameLength = maxFrameLength;
333 this.lengthFieldOffset = lengthFieldOffset;
334 this.lengthFieldLength = lengthFieldLength;
335 this.lengthAdjustment = lengthAdjustment;
336 lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength;
337 this.initialBytesToStrip = initialBytesToStrip;
338 this.failFast = failFast;
339 }
340
341 @Override
342 protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
343 Object decoded = decode(ctx, in);
344 if (decoded != null) {
345 out.add(decoded);
346 }
347 }
348
349 private void discardingTooLongFrame(ByteBuf in) {
350 long bytesToDiscard = this.bytesToDiscard;
351 int localBytesToDiscard = (int) Math.min(bytesToDiscard, in.readableBytes());
352 in.skipBytes(localBytesToDiscard);
353 bytesToDiscard -= localBytesToDiscard;
354 this.bytesToDiscard = bytesToDiscard;
355
356 failIfNecessary(false);
357 }
358
359 private static void failOnNegativeLengthField(ByteBuf in, long frameLength, int lengthFieldEndOffset) {
360 in.skipBytes(lengthFieldEndOffset);
361 throw new CorruptedFrameException(
362 "negative pre-adjustment length field: " + frameLength);
363 }
364
365 private static void failOnFrameLengthLessThanLengthFieldEndOffset(ByteBuf in,
366 long frameLength,
367 int lengthFieldEndOffset) {
368 in.skipBytes(lengthFieldEndOffset);
369 throw new CorruptedFrameException(
370 "Adjusted frame length (" + frameLength + ") is less " +
371 "than lengthFieldEndOffset: " + lengthFieldEndOffset);
372 }
373
374 private void exceededFrameLength(ByteBuf in, long frameLength) {
375 long discard = frameLength - in.readableBytes();
376 tooLongFrameLength = frameLength;
377
378 if (discard < 0) {
379 // buffer contains more bytes then the frameLength so we can discard all now
380 in.skipBytes((int) frameLength);
381 } else {
382 // Enter the discard mode and discard everything received so far.
383 discardingTooLongFrame = true;
384 bytesToDiscard = discard;
385 in.skipBytes(in.readableBytes());
386 }
387 failIfNecessary(true);
388 }
389
390 private static void failOnFrameLengthLessThanInitialBytesToStrip(ByteBuf in,
391 long frameLength,
392 int initialBytesToStrip) {
393 in.skipBytes((int) frameLength);
394 throw new CorruptedFrameException(
395 "Adjusted frame length (" + frameLength + ") is less " +
396 "than initialBytesToStrip: " + initialBytesToStrip);
397 }
398
399 /**
400 * Create a frame out of the {@link ByteBuf} and return it.
401 *
402 * @param ctx the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to
403 * @param in the {@link ByteBuf} from which to read data
404 * @return frame the {@link ByteBuf} which represent the frame or {@code null} if no frame could
405 * be created.
406 */
407 protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
408 if (discardingTooLongFrame) {
409 discardingTooLongFrame(in);
410 }
411
412 if (in.readableBytes() < lengthFieldEndOffset) {
413 return null;
414 }
415
416 int actualLengthFieldOffset = in.readerIndex() + lengthFieldOffset;
417 long frameLength = getUnadjustedFrameLength(in, actualLengthFieldOffset, lengthFieldLength, byteOrder);
418
419 if (frameLength < 0) {
420 failOnNegativeLengthField(in, frameLength, lengthFieldEndOffset);
421 }
422
423 frameLength += lengthAdjustment + lengthFieldEndOffset;
424
425 if (frameLength < lengthFieldEndOffset) {
426 failOnFrameLengthLessThanLengthFieldEndOffset(in, frameLength, lengthFieldEndOffset);
427 }
428
429 if (frameLength > maxFrameLength) {
430 exceededFrameLength(in, frameLength);
431 return null;
432 }
433
434 // never overflows because it's less than maxFrameLength
435 int frameLengthInt = (int) frameLength;
436 if (in.readableBytes() < frameLengthInt) {
437 return null;
438 }
439
440 if (initialBytesToStrip > frameLengthInt) {
441 failOnFrameLengthLessThanInitialBytesToStrip(in, frameLength, initialBytesToStrip);
442 }
443 in.skipBytes(initialBytesToStrip);
444
445 // extract frame
446 int readerIndex = in.readerIndex();
447 int actualFrameLength = frameLengthInt - initialBytesToStrip;
448 ByteBuf frame = extractFrame(ctx, in, readerIndex, actualFrameLength);
449 in.readerIndex(readerIndex + actualFrameLength);
450 return frame;
451 }
452
453 /**
454 * Decodes the specified region of the buffer into an unadjusted frame length. The default implementation is
455 * capable of decoding the specified region into an unsigned 8/16/24/32/64 bit integer. Override this method to
456 * decode the length field encoded differently. Note that this method must not modify the state of the specified
457 * buffer (e.g. {@code readerIndex}, {@code writerIndex}, and the content of the buffer.)
458 *
459 * @throws DecoderException if failed to decode the specified region
460 */
461 protected long getUnadjustedFrameLength(ByteBuf buf, int offset, int length, ByteOrder order) {
462 buf = buf.order(order);
463 long frameLength;
464 switch (length) {
465 case 1:
466 frameLength = buf.getUnsignedByte(offset);
467 break;
468 case 2:
469 frameLength = buf.getUnsignedShort(offset);
470 break;
471 case 3:
472 frameLength = buf.getUnsignedMedium(offset);
473 break;
474 case 4:
475 frameLength = buf.getUnsignedInt(offset);
476 break;
477 case 8:
478 frameLength = buf.getLong(offset);
479 break;
480 default:
481 throw new DecoderException(
482 "unsupported lengthFieldLength: " + lengthFieldLength + " (expected: 1, 2, 3, 4, or 8)");
483 }
484 return frameLength;
485 }
486
487 private void failIfNecessary(boolean firstDetectionOfTooLongFrame) {
488 if (bytesToDiscard == 0) {
489 // Reset to the initial state and tell the handlers that
490 // the frame was too large.
491 long tooLongFrameLength = this.tooLongFrameLength;
492 this.tooLongFrameLength = 0;
493 discardingTooLongFrame = false;
494 if (!failFast || firstDetectionOfTooLongFrame) {
495 fail(tooLongFrameLength);
496 }
497 } else {
498 // Keep discarding and notify handlers if necessary.
499 if (failFast && firstDetectionOfTooLongFrame) {
500 fail(tooLongFrameLength);
501 }
502 }
503 }
504
505 /**
506 * Extract the sub-region of the specified buffer.
507 * <p>
508 * If you are sure that the frame and its content are not accessed after
509 * the current {@link #decode(ChannelHandlerContext, ByteBuf)}
510 * call returns, you can even avoid memory copy by returning the sliced
511 * sub-region (i.e. <tt>return buffer.slice(index, length)</tt>).
512 * It's often useful when you convert the extracted frame into an object.
513 * Refer to the source code of {@link ObjectDecoder} to see how this method
514 * is overridden to avoid memory copy.
515 */
516 protected ByteBuf extractFrame(ChannelHandlerContext ctx, ByteBuf buffer, int index, int length) {
517 return buffer.slice(index, length).retain();
518 }
519
520 private void fail(long frameLength) {
521 if (frameLength > 0) {
522 throw new TooLongFrameException(
523 "Adjusted frame length exceeds " + maxFrameLength +
524 ": " + frameLength + " - discarded");
525 } else {
526 throw new TooLongFrameException(
527 "Adjusted frame length exceeds " + maxFrameLength +
528 " - discarding");
529 }
530 }
531 }