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 }