1 /* 2 * Copyright 2009 Red Hat, Inc. 3 * 4 * Red Hat licenses this file to you under the Apache License, version 2.0 5 * (the "License"); you may not use this file except in compliance with the 6 * 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.buffer.ChannelBufferFactory; 20 import org.jboss.netty.channel.Channel; 21 import org.jboss.netty.channel.ChannelHandlerContext; 22 import org.jboss.netty.channel.Channels; 23 import org.jboss.netty.handler.codec.serialization.ObjectDecoder; 24 25 /** 26 * A decoder that splits the received {@link ChannelBuffer}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 ChannelBuffer#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</b> = 1 (= the length of HDR1) 149 * lengthFieldLength</b> = 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 * 183 * @author <a href="http://www.jboss.org/netty/">The Netty Project</a> 184 * @author <a href="http://gleamynode.net/">Trustin Lee</a> 185 * 186 * @version $Rev:231 $, $Date:2008-06-12 16:44:50 +0900 (목, 12 6월 2008) $ 187 * 188 * @see LengthFieldPrepender 189 */ 190 public class LengthFieldBasedFrameDecoder extends FrameDecoder { 191 192 private final int maxFrameLength; 193 private final int lengthFieldOffset; 194 private final int lengthFieldLength; 195 private final int lengthFieldEndOffset; 196 private final int lengthAdjustment; 197 private final int initialBytesToStrip; 198 private boolean discardingTooLongFrame; 199 private long tooLongFrameLength; 200 private long bytesToDiscard; 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 */ 215 public LengthFieldBasedFrameDecoder( 216 int maxFrameLength, 217 int lengthFieldOffset, int lengthFieldLength) { 218 this(maxFrameLength, lengthFieldOffset, lengthFieldLength, 0, 0); 219 } 220 221 /** 222 * Creates a new instance. 223 * 224 * @param maxFrameLength 225 * the maximum length of the frame. If the length of the frame is 226 * greater than this value, {@link TooLongFrameException} will be 227 * thrown. 228 * @param lengthFieldOffset 229 * the offset of the length field 230 * @param lengthFieldLength 231 * the length of the length field 232 * @param lengthAdjustment 233 * the compensation value to add to the value of the length field 234 * @param initialBytesToStrip 235 * the number of first bytes to strip out from the decoded frame 236 */ 237 public LengthFieldBasedFrameDecoder( 238 int maxFrameLength, 239 int lengthFieldOffset, int lengthFieldLength, 240 int lengthAdjustment, int initialBytesToStrip) { 241 if (maxFrameLength <= 0) { 242 throw new IllegalArgumentException( 243 "maxFrameLength must be a positive integer: " + 244 maxFrameLength); 245 } 246 247 if (lengthFieldOffset < 0) { 248 throw new IllegalArgumentException( 249 "lengthFieldOffset must be a non-negative integer: " + 250 lengthFieldOffset); 251 } 252 253 if (initialBytesToStrip < 0) { 254 throw new IllegalArgumentException( 255 "initialBytesToStrip must be a non-negative integer: " + 256 initialBytesToStrip); 257 } 258 259 if (lengthFieldLength != 1 && lengthFieldLength != 2 && 260 lengthFieldLength != 3 && lengthFieldLength != 4 && 261 lengthFieldLength != 8) { 262 throw new IllegalArgumentException( 263 "lengthFieldLength must be either 1, 2, 3, 4, or 8: " + 264 lengthFieldLength); 265 } 266 267 if (lengthFieldOffset > maxFrameLength - lengthFieldLength) { 268 throw new IllegalArgumentException( 269 "maxFrameLength (" + maxFrameLength + ") " + 270 "must be equal to or greater than " + 271 "lengthFieldOffset (" + lengthFieldOffset + ") + " + 272 "lengthFieldLength (" + lengthFieldLength + ")."); 273 } 274 275 this.maxFrameLength = maxFrameLength; 276 this.lengthFieldOffset = lengthFieldOffset; 277 this.lengthFieldLength = lengthFieldLength; 278 this.lengthAdjustment = lengthAdjustment; 279 lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength; 280 this.initialBytesToStrip = initialBytesToStrip; 281 } 282 283 @Override 284 protected Object decode( 285 ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception { 286 287 if (discardingTooLongFrame) { 288 long bytesToDiscard = this.bytesToDiscard; 289 int localBytesToDiscard = (int) Math.min(bytesToDiscard, buffer.readableBytes()); 290 buffer.skipBytes(localBytesToDiscard); 291 bytesToDiscard -= localBytesToDiscard; 292 this.bytesToDiscard = bytesToDiscard; 293 failIfNecessary(ctx); 294 return null; 295 } 296 297 if (buffer.readableBytes() < lengthFieldEndOffset) { 298 return null; 299 } 300 301 int actualLengthFieldOffset = buffer.readerIndex() + lengthFieldOffset; 302 long frameLength; 303 switch (lengthFieldLength) { 304 case 1: 305 frameLength = buffer.getUnsignedByte(actualLengthFieldOffset); 306 break; 307 case 2: 308 frameLength = buffer.getUnsignedShort(actualLengthFieldOffset); 309 break; 310 case 3: 311 frameLength = buffer.getUnsignedMedium(actualLengthFieldOffset); 312 break; 313 case 4: 314 frameLength = buffer.getUnsignedInt(actualLengthFieldOffset); 315 break; 316 case 8: 317 frameLength = buffer.getLong(actualLengthFieldOffset); 318 break; 319 default: 320 throw new Error("should not reach here"); 321 } 322 323 if (frameLength < 0) { 324 buffer.skipBytes(lengthFieldEndOffset); 325 throw new CorruptedFrameException( 326 "negative pre-adjustment length field: " + frameLength); 327 } 328 329 frameLength += lengthAdjustment + lengthFieldEndOffset; 330 if (frameLength < lengthFieldEndOffset) { 331 buffer.skipBytes(lengthFieldEndOffset); 332 throw new CorruptedFrameException( 333 "Adjusted frame length (" + frameLength + ") is less " + 334 "than lengthFieldEndOffset: " + lengthFieldEndOffset); 335 } 336 337 if (frameLength > maxFrameLength) { 338 // Enter the discard mode and discard everything received so far. 339 discardingTooLongFrame = true; 340 tooLongFrameLength = frameLength; 341 bytesToDiscard = frameLength - buffer.readableBytes(); 342 buffer.skipBytes(buffer.readableBytes()); 343 failIfNecessary(ctx); 344 return null; 345 } 346 347 // never overflows because it's less than maxFrameLength 348 int frameLengthInt = (int) frameLength; 349 if (buffer.readableBytes() < frameLengthInt) { 350 return null; 351 } 352 353 if (initialBytesToStrip > frameLengthInt) { 354 buffer.skipBytes(frameLengthInt); 355 throw new CorruptedFrameException( 356 "Adjusted frame length (" + frameLength + ") is less " + 357 "than initialBytesToStrip: " + initialBytesToStrip); 358 } 359 buffer.skipBytes(initialBytesToStrip); 360 361 // extract frame 362 int readerIndex = buffer.readerIndex(); 363 int actualFrameLength = frameLengthInt - initialBytesToStrip; 364 ChannelBuffer frame = extractFrame(buffer, readerIndex, actualFrameLength); 365 buffer.readerIndex(readerIndex + actualFrameLength); 366 return frame; 367 } 368 369 private void failIfNecessary(ChannelHandlerContext ctx) { 370 if (bytesToDiscard == 0) { 371 // Reset to the initial state and tell the handlers that 372 // the frame was too large. 373 // TODO Let user choose when the exception should be raised - early or late? 374 // If early, fail() should be called when discardingTooLongFrame is set to true. 375 long tooLongFrameLength = this.tooLongFrameLength; 376 this.tooLongFrameLength = 0; 377 discardingTooLongFrame = false; 378 fail(ctx, tooLongFrameLength); 379 } else { 380 // Keep discarding. 381 } 382 } 383 384 /** 385 * Extract the sub-region of the specified buffer. This method is called by 386 * {@link #decode(ChannelHandlerContext, Channel, ChannelBuffer)} for each 387 * frame. The default implementation returns a copy of the sub-region. 388 * For example, you could override this method to use an alternative 389 * {@link ChannelBufferFactory}. 390 * <p> 391 * If you are sure that the frame and its content are not accessed after 392 * the current {@link #decode(ChannelHandlerContext, Channel, ChannelBuffer)} 393 * call returns, you can even avoid memory copy by returning the sliced 394 * sub-region (i.e. <tt>return buffer.slice(index, length)</tt>). 395 * It's often useful when you convert the extracted frame into an object. 396 * Refer to the source code of {@link ObjectDecoder} to see how this method 397 * is overridden to avoid memory copy. 398 */ 399 protected ChannelBuffer extractFrame(ChannelBuffer buffer, int index, int length) { 400 ChannelBuffer frame = buffer.factory().getBuffer(length); 401 frame.writeBytes(buffer, index, length); 402 return frame; 403 } 404 405 private void fail(ChannelHandlerContext ctx, long frameLength) { 406 if (frameLength > 0) { 407 Channels.fireExceptionCaught( 408 ctx.getChannel(), 409 new TooLongFrameException( 410 "Adjusted frame length exceeds " + maxFrameLength + 411 ": " + frameLength + " - discarded")); 412 } else { 413 Channels.fireExceptionCaught( 414 ctx.getChannel(), 415 new TooLongFrameException( 416 "Adjusted frame length exceeds " + maxFrameLength + 417 " - discarding")); 418 } 419 } 420 }