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 }