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.channel.Channel; 20 import org.jboss.netty.channel.ChannelHandlerContext; 21 import org.jboss.netty.channel.Channels; 22 23 /** 24 * A decoder that splits the received {@link ChannelBuffer}s dynamically by the 25 * value of the length field in the message. It is particularly useful when you 26 * decode a binary message which has an integer header field that represents the 27 * length of the message body or the whole message. 28 * <p> 29 * {@link LengthFieldBasedFrameDecoder} has many configuration parameters so 30 * that it can decode any message with a length field, which is often seen in 31 * proprietary client-server protocols. Here are some example that will give 32 * you the basic idea on which option does what. 33 * 34 * <h3>2 bytes length field at offset 0, do not strip header</h3> 35 * 36 * The value of the length field in this example is <tt>12 (0x0C)</tt> which 37 * represents the length of "HELLO, WORLD". By default, the decoder assumes 38 * that the length field represents the number of the bytes that follows the 39 * length field. Therefore, it can be decoded with the simplistic parameter 40 * combination. 41 * <pre> 42 * <b>lengthFieldOffset</b> = <b>0</b> 43 * <b>lengthFieldLength</b> = <b>2</b> 44 * lengthAdjustment = 0 45 * initialBytesToStrip = 0 (= do not strip header) 46 * 47 * BEFORE DECODE (14 bytes) AFTER DECODE (14 bytes) 48 * +--------+----------------+ +--------+----------------+ 49 * | Length | Actual Content |----->| Length | Actual Content | 50 * | 0x000C | "HELLO, WORLD" | | 0x000C | "HELLO, WORLD" | 51 * +--------+----------------+ +--------+----------------+ 52 * </pre> 53 * 54 * <h3>2 bytes length field at offset 0, strip header</h3> 55 * 56 * Because we can get the length of the content by calling 57 * {@link ChannelBuffer#readableBytes()}, you might want to strip the length 58 * field by specifying <tt>initialBytesToStrip</tt>. In this example, we 59 * specified <tt>2</tt>, that is same with the length of the length field, to 60 * strip the first two bytes. 61 * <pre> 62 * lengthFieldOffset = 0 63 * lengthFieldLength = 2 64 * lengthAdjustment = 0 65 * <b>initialBytesToStrip</b> = <b>2</b> (= the length of the Length field) 66 * 67 * BEFORE DECODE (14 bytes) AFTER DECODE (12 bytes) 68 * +--------+----------------+ +----------------+ 69 * | Length | Actual Content |----->| Actual Content | 70 * | 0x000C | "HELLO, WORLD" | | "HELLO, WORLD" | 71 * +--------+----------------+ +----------------+ 72 * </pre> 73 * 74 * <h3>2 bytes length field at offset 0, do not strip header, the length field 75 * represents the length of the whole message</h3> 76 * 77 * In most cases, the length field represents the length of the message body 78 * only, as shown in the previous examples. However, in some protocols, the 79 * length field represents the length of the whole message, including the 80 * message header. In such a case, we specify a non-zero 81 * <tt>lengthAdjustment</tt>. Because the length value in this example message 82 * is always greater than the body length by <tt>2</tt>, we specify <tt>-2</tt> 83 * as <tt>lengthAdjustment</tt> for compensation. 84 * <pre> 85 * lengthFieldOffset = 0 86 * lengthFieldLength = 2 87 * <b>lengthAdjustment</b> = <b>-2</b> (= the length of the Length field) 88 * initialBytesToStrip = 0 89 * 90 * BEFORE DECODE (14 bytes) AFTER DECODE (14 bytes) 91 * +--------+----------------+ +--------+----------------+ 92 * | Length | Actual Content |----->| Length | Actual Content | 93 * | 0x000E | "HELLO, WORLD" | | 0x000E | "HELLO, WORLD" | 94 * +--------+----------------+ +--------+----------------+ 95 * </pre> 96 * 97 * <h3>3 bytes length field at the end of 5 bytes header, do not strip header</h3> 98 * 99 * The following message is a simple variation of the first example. An extra 100 * header value is prepended to the message. <tt>lengthAdjustment</tt> is zero 101 * again because the decoder always takes the length of the prepended data into 102 * account during frame length calculation. 103 * <pre> 104 * <b>lengthFieldOffset</b> = <b>2</b> (= the length of Header 1) 105 * <b>lengthFieldLength</b> = <b>3</b> 106 * lengthAdjustment = 0 107 * initialBytesToStrip = 0 108 * 109 * BEFORE DECODE (17 bytes) AFTER DECODE (17 bytes) 110 * +----------+----------+----------------+ +----------+----------+----------------+ 111 * | Header 1 | Length | Actual Content |----->| Header 1 | Length | Actual Content | 112 * | 0xCAFE | 0x00000C | "HELLO, WORLD" | | 0xCAFE | 0x00000C | "HELLO, WORLD" | 113 * +----------+----------+----------------+ +----------+----------+----------------+ 114 * </pre> 115 * 116 * <h3>3 bytes length field at the beginning of 5 bytes header, do not strip header</h3> 117 * 118 * This is an advanced example that shows the case where there is an extra 119 * header between the length field and the message body. You have to specify a 120 * positive <tt>lengthAdjustment</tt> so that the decoder counts the extra 121 * header into the frame length calculation. 122 * <pre> 123 * lengthFieldOffset = 0 124 * lengthFieldLength = 3 125 * <b>lengthAdjustment</b> = <b>2</b> (= the length of Header 1) 126 * initialBytesToStrip = 0 127 * 128 * BEFORE DECODE (17 bytes) AFTER DECODE (17 bytes) 129 * +----------+----------+----------------+ +----------+----------+----------------+ 130 * | Length | Header 1 | Actual Content |----->| Length | Header 1 | Actual Content | 131 * | 0x00000C | 0xCAFE | "HELLO, WORLD" | | 0x00000C | 0xCAFE | "HELLO, WORLD" | 132 * +----------+----------+----------------+ +----------+----------+----------------+ 133 * </pre> 134 * 135 * <h3>2 bytes length field at offset 1 in the middle of 4 bytes header, 136 * strip the first header field and the length field</h3> 137 * 138 * This is a combination of all the examples above. There are the prepended 139 * header before the length field and the extra header after the length field. 140 * The prepended header affects the <tt>lengthFieldOffset</tt> and the extra 141 * header affects the <tt>lengthAdjustment</tt>. We also specified a non-zero 142 * <tt>initialBytesToStrip</tt> to strip the length field and the prepended 143 * header from the frame. If you don't want to strip the prepended header, you 144 * could specify <tt>0</tt> for <tt>initialBytesToSkip</tt>. 145 * <pre> 146 * lengthFieldOffset = 1 (= the length of HDR1) 147 * lengthFieldLength = 2 148 * <b>lengthAdjustment</b> = <b>1</b> (= the length of HDR2) 149 * <b>initialBytesToStrip</b> = <b>3</b> (= the length of HDR1 + LEN) 150 * 151 * BEFORE DECODE (16 bytes) AFTER DECODE (13 bytes) 152 * +------+--------+------+----------------+ +------+----------------+ 153 * | HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content | 154 * | 0xCA | 0x000C | 0xFE | "HELLO, WORLD" | | 0xFE | "HELLO, WORLD" | 155 * +------+--------+------+----------------+ +------+----------------+ 156 * </pre> 157 * 158 * <h3>2 bytes length field at offset 1 in the middle of 4 bytes header, 159 * strip the first header field and the length field, the length field 160 * represents the length of the whole message</h3> 161 * 162 * Let's give another twist to the previous example. The only difference from 163 * the previous example is that the length field represents the length of the 164 * whole message instead of the message body, just like the third example. 165 * We have to count the length of HDR1 and Length into <tt>lengthAdjustment</tt>. 166 * Please note that we don't need to take the length of HDR2 into account 167 * because the length field already includes the whole header length. 168 * <pre> 169 * lengthFieldOffset = 1 170 * lengthFieldLength = 2 171 * <b>lengthAdjustment</b> = <b>-3</b> (= the length of HDR1 + LEN, negative) 172 * <b>initialBytesToStrip</b> = <b> 3</b> 173 * 174 * BEFORE DECODE (16 bytes) AFTER DECODE (13 bytes) 175 * +------+--------+------+----------------+ +------+----------------+ 176 * | HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content | 177 * | 0xCA | 0x0010 | 0xFE | "HELLO, WORLD" | | 0xFE | "HELLO, WORLD" | 178 * +------+--------+------+----------------+ +------+----------------+ 179 * </pre> 180 * 181 * @see LengthFieldPrepender 182 */ 183 public class LengthFieldBasedFrameDecoder extends FrameDecoder { 184 185 private final int maxFrameLength; 186 private final int lengthFieldOffset; 187 private final int lengthFieldLength; 188 private final int lengthFieldEndOffset; 189 private final int lengthAdjustment; 190 private final int initialBytesToStrip; 191 private final boolean failFast; 192 private boolean discardingTooLongFrame; 193 private long tooLongFrameLength; 194 private long bytesToDiscard; 195 196 /** 197 * Creates a new instance. 198 * 199 * @param maxFrameLength 200 * the maximum length of the frame. If the length of the frame is 201 * greater than this value, {@link TooLongFrameException} will be 202 * thrown. 203 * @param lengthFieldOffset 204 * the offset of the length field 205 * @param lengthFieldLength 206 * the length of the length field 207 */ 208 public LengthFieldBasedFrameDecoder( 209 int maxFrameLength, 210 int lengthFieldOffset, int lengthFieldLength) { 211 this(maxFrameLength, lengthFieldOffset, lengthFieldLength, 0, 0); 212 } 213 214 /** 215 * Creates a new instance. 216 * 217 * @param maxFrameLength 218 * the maximum length of the frame. If the length of the frame is 219 * greater than this value, {@link TooLongFrameException} will be 220 * thrown. 221 * @param lengthFieldOffset 222 * the offset of the length field 223 * @param lengthFieldLength 224 * the length of the length field 225 * @param lengthAdjustment 226 * the compensation value to add to the value of the length field 227 * @param initialBytesToStrip 228 * the number of first bytes to strip out from the decoded frame 229 */ 230 public LengthFieldBasedFrameDecoder( 231 int maxFrameLength, 232 int lengthFieldOffset, int lengthFieldLength, 233 int lengthAdjustment, int initialBytesToStrip) { 234 this( 235 maxFrameLength, 236 lengthFieldOffset, lengthFieldLength, lengthAdjustment, 237 initialBytesToStrip, false); 238 } 239 240 /** 241 * Creates a new instance. 242 * 243 * @param maxFrameLength 244 * the maximum length of the frame. If the length of the frame is 245 * greater than this value, {@link TooLongFrameException} will be 246 * thrown. 247 * @param lengthFieldOffset 248 * the offset of the length field 249 * @param lengthFieldLength 250 * the length of the length field 251 * @param lengthAdjustment 252 * the compensation value to add to the value of the length field 253 * @param initialBytesToStrip 254 * the number of first bytes to strip out from the decoded frame 255 * @param failFast 256 * If <tt>true</tt>, a {@link TooLongFrameException} is thrown as 257 * soon as the decoder notices the length of the frame will exceed 258 * <tt>maxFrameLength</tt> regardless of whether the entire frame 259 * has been read. If <tt>false</tt>, a {@link TooLongFrameException} 260 * is thrown after the entire frame that exceeds <tt>maxFrameLength</tt> 261 * has been read. 262 */ 263 public LengthFieldBasedFrameDecoder( 264 int maxFrameLength, 265 int lengthFieldOffset, int lengthFieldLength, 266 int lengthAdjustment, int initialBytesToStrip, boolean failFast) { 267 if (maxFrameLength <= 0) { 268 throw new IllegalArgumentException( 269 "maxFrameLength must be a positive integer: " + 270 maxFrameLength); 271 } 272 273 if (lengthFieldOffset < 0) { 274 throw new IllegalArgumentException( 275 "lengthFieldOffset must be a non-negative integer: " + 276 lengthFieldOffset); 277 } 278 279 if (initialBytesToStrip < 0) { 280 throw new IllegalArgumentException( 281 "initialBytesToStrip must be a non-negative integer: " + 282 initialBytesToStrip); 283 } 284 285 if (lengthFieldLength != 1 && lengthFieldLength != 2 && 286 lengthFieldLength != 3 && lengthFieldLength != 4 && 287 lengthFieldLength != 8) { 288 throw new IllegalArgumentException( 289 "lengthFieldLength must be either 1, 2, 3, 4, or 8: " + 290 lengthFieldLength); 291 } 292 293 if (lengthFieldOffset > maxFrameLength - lengthFieldLength) { 294 throw new IllegalArgumentException( 295 "maxFrameLength (" + maxFrameLength + ") " + 296 "must be equal to or greater than " + 297 "lengthFieldOffset (" + lengthFieldOffset + ") + " + 298 "lengthFieldLength (" + lengthFieldLength + ")."); 299 } 300 301 this.maxFrameLength = maxFrameLength; 302 this.lengthFieldOffset = lengthFieldOffset; 303 this.lengthFieldLength = lengthFieldLength; 304 this.lengthAdjustment = lengthAdjustment; 305 lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength; 306 this.initialBytesToStrip = initialBytesToStrip; 307 this.failFast = failFast; 308 } 309 310 @Override 311 protected Object decode( 312 ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception { 313 314 if (discardingTooLongFrame) { 315 long bytesToDiscard = this.bytesToDiscard; 316 int localBytesToDiscard = (int) Math.min(bytesToDiscard, buffer.readableBytes()); 317 buffer.skipBytes(localBytesToDiscard); 318 bytesToDiscard -= localBytesToDiscard; 319 this.bytesToDiscard = bytesToDiscard; 320 failIfNecessary(ctx, false); 321 return null; 322 } 323 324 if (buffer.readableBytes() < lengthFieldEndOffset) { 325 return null; 326 } 327 328 int actualLengthFieldOffset = buffer.readerIndex() + lengthFieldOffset; 329 long frameLength; 330 switch (lengthFieldLength) { 331 case 1: 332 frameLength = buffer.getUnsignedByte(actualLengthFieldOffset); 333 break; 334 case 2: 335 frameLength = buffer.getUnsignedShort(actualLengthFieldOffset); 336 break; 337 case 3: 338 frameLength = buffer.getUnsignedMedium(actualLengthFieldOffset); 339 break; 340 case 4: 341 frameLength = buffer.getUnsignedInt(actualLengthFieldOffset); 342 break; 343 case 8: 344 frameLength = buffer.getLong(actualLengthFieldOffset); 345 break; 346 default: 347 throw new Error("should not reach here"); 348 } 349 350 if (frameLength < 0) { 351 buffer.skipBytes(lengthFieldEndOffset); 352 throw new CorruptedFrameException( 353 "negative pre-adjustment length field: " + frameLength); 354 } 355 356 frameLength += lengthAdjustment + lengthFieldEndOffset; 357 if (frameLength < lengthFieldEndOffset) { 358 buffer.skipBytes(lengthFieldEndOffset); 359 throw new CorruptedFrameException( 360 "Adjusted frame length (" + frameLength + ") is less " + 361 "than lengthFieldEndOffset: " + lengthFieldEndOffset); 362 } 363 364 if (frameLength > maxFrameLength) { 365 // Enter the discard mode and discard everything received so far. 366 discardingTooLongFrame = true; 367 tooLongFrameLength = frameLength; 368 bytesToDiscard = frameLength - buffer.readableBytes(); 369 buffer.skipBytes(buffer.readableBytes()); 370 failIfNecessary(ctx, true); 371 return null; 372 } 373 374 // never overflows because it's less than maxFrameLength 375 int frameLengthInt = (int) frameLength; 376 if (buffer.readableBytes() < frameLengthInt) { 377 return null; 378 } 379 380 if (initialBytesToStrip > frameLengthInt) { 381 buffer.skipBytes(frameLengthInt); 382 throw new CorruptedFrameException( 383 "Adjusted frame length (" + frameLength + ") is less " + 384 "than initialBytesToStrip: " + initialBytesToStrip); 385 } 386 buffer.skipBytes(initialBytesToStrip); 387 388 // extract frame 389 int readerIndex = buffer.readerIndex(); 390 int actualFrameLength = frameLengthInt - initialBytesToStrip; 391 ChannelBuffer frame = extractFrame(buffer, readerIndex, actualFrameLength); 392 buffer.readerIndex(readerIndex + actualFrameLength); 393 return frame; 394 } 395 396 private void failIfNecessary(ChannelHandlerContext ctx, boolean firstDetectionOfTooLongFrame) { 397 if (bytesToDiscard == 0) { 398 // Reset to the initial state and tell the handlers that 399 // the frame was too large. 400 long tooLongFrameLength = this.tooLongFrameLength; 401 this.tooLongFrameLength = 0; 402 discardingTooLongFrame = false; 403 if (!failFast || 404 failFast && firstDetectionOfTooLongFrame) { 405 fail(ctx, tooLongFrameLength); 406 } 407 } else { 408 // Keep discarding and notify handlers if necessary. 409 if (failFast && firstDetectionOfTooLongFrame) { 410 fail(ctx, tooLongFrameLength); 411 } 412 } 413 } 414 415 private void fail(ChannelHandlerContext ctx, long frameLength) { 416 if (frameLength > 0) { 417 Channels.fireExceptionCaught( 418 ctx.getChannel(), 419 new TooLongFrameException( 420 "Adjusted frame length exceeds " + maxFrameLength + 421 ": " + frameLength + " - discarded")); 422 } else { 423 Channels.fireExceptionCaught( 424 ctx.getChannel(), 425 new TooLongFrameException( 426 "Adjusted frame length exceeds " + maxFrameLength + 427 " - discarding")); 428 } 429 } 430 }