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 }