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 }