1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 public class DelimiterBasedFrameDecoder extends FrameDecoder {
62
63 private final ChannelBuffer[] delimiters;
64 private final int maxFrameLength;
65 private final boolean stripDelimiter;
66 private final boolean failFast;
67 private boolean discardingTooLongFrame;
68 private int tooLongFrameLength;
69
70 private final LineBasedFrameDecoder lineBasedDecoder;
71
72
73
74
75
76
77
78
79
80 public DelimiterBasedFrameDecoder(int maxFrameLength, ChannelBuffer delimiter) {
81 this(maxFrameLength, true, delimiter);
82 }
83
84
85
86
87
88
89
90
91
92
93
94 public DelimiterBasedFrameDecoder(
95 int maxFrameLength, boolean stripDelimiter, ChannelBuffer delimiter) {
96 this(maxFrameLength, stripDelimiter, false, delimiter);
97 }
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116 public DelimiterBasedFrameDecoder(
117 int maxFrameLength, boolean stripDelimiter, boolean failFast,
118 ChannelBuffer delimiter) {
119 this(maxFrameLength, stripDelimiter, failFast, new ChannelBuffer[] {
120 delimiter.slice(
121 delimiter.readerIndex(), delimiter.readableBytes()) });
122 }
123
124
125
126
127
128
129
130
131
132 public DelimiterBasedFrameDecoder(int maxFrameLength, ChannelBuffer... delimiters) {
133 this(maxFrameLength, true, delimiters);
134 }
135
136
137
138
139
140
141
142
143
144
145
146 public DelimiterBasedFrameDecoder(
147 int maxFrameLength, boolean stripDelimiter, ChannelBuffer... delimiters) {
148 this(maxFrameLength, stripDelimiter, false, delimiters);
149 }
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168 public DelimiterBasedFrameDecoder(
169 int maxFrameLength, boolean stripDelimiter, boolean failFast, ChannelBuffer... delimiters) {
170 validateMaxFrameLength(maxFrameLength);
171 if (delimiters == null) {
172 throw new NullPointerException("delimiters");
173 }
174 if (delimiters.length == 0) {
175 throw new IllegalArgumentException("empty delimiters");
176 }
177
178 if (isLineBased(delimiters) && !isSubclass()) {
179 lineBasedDecoder = new LineBasedFrameDecoder(maxFrameLength, stripDelimiter, failFast);
180 this.delimiters = null;
181 } else {
182 this.delimiters = new ChannelBuffer[delimiters.length];
183 for (int i = 0; i < delimiters.length; i ++) {
184 ChannelBuffer d = delimiters[i];
185 validateDelimiter(d);
186 this.delimiters[i] = d.slice(d.readerIndex(), d.readableBytes());
187 }
188 lineBasedDecoder = null;
189 }
190 this.maxFrameLength = maxFrameLength;
191 this.stripDelimiter = stripDelimiter;
192 this.failFast = failFast;
193 }
194
195
196 private static boolean isLineBased(final ChannelBuffer[] delimiters) {
197 if (delimiters.length != 2) {
198 return false;
199 }
200 ChannelBuffer a = delimiters[0];
201 ChannelBuffer b = delimiters[1];
202 if (a.capacity() < b.capacity()) {
203 a = delimiters[1];
204 b = delimiters[0];
205 }
206 return a.capacity() == 2 && b.capacity() == 1
207 && a.getByte(0) == '\r' && a.getByte(1) == '\n'
208 && b.getByte(0) == '\n';
209 }
210
211
212
213
214 private boolean isSubclass() {
215 return getClass() != DelimiterBasedFrameDecoder.class;
216 }
217
218 @Override
219 protected Object decode(
220 ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
221 if (lineBasedDecoder != null) {
222 return lineBasedDecoder.decode(ctx, channel, buffer);
223 }
224
225 int minFrameLength = Integer.MAX_VALUE;
226 ChannelBuffer minDelim = null;
227 for (ChannelBuffer delim: delimiters) {
228 int frameLength = indexOf(buffer, delim);
229 if (frameLength >= 0 && frameLength < minFrameLength) {
230 minFrameLength = frameLength;
231 minDelim = delim;
232 }
233 }
234
235 if (minDelim != null) {
236 int minDelimLength = minDelim.capacity();
237 ChannelBuffer frame;
238
239 if (discardingTooLongFrame) {
240
241
242 discardingTooLongFrame = false;
243 buffer.skipBytes(minFrameLength + minDelimLength);
244
245 int tooLongFrameLength = this.tooLongFrameLength;
246 this.tooLongFrameLength = 0;
247 if (!failFast) {
248 fail(ctx, tooLongFrameLength);
249 }
250 return null;
251 }
252
253 if (minFrameLength > maxFrameLength) {
254
255 buffer.skipBytes(minFrameLength + minDelimLength);
256 fail(ctx, minFrameLength);
257 return null;
258 }
259
260 if (stripDelimiter) {
261 frame = extractFrame(buffer, buffer.readerIndex(), minFrameLength);
262 } else {
263 frame = extractFrame(buffer, buffer.readerIndex(), minFrameLength + minDelimLength);
264 }
265 buffer.skipBytes(minFrameLength + minDelimLength);
266
267 return frame;
268 } else {
269 if (!discardingTooLongFrame) {
270 if (buffer.readableBytes() > maxFrameLength) {
271
272 tooLongFrameLength = buffer.readableBytes();
273 buffer.skipBytes(buffer.readableBytes());
274 discardingTooLongFrame = true;
275 if (failFast) {
276 fail(ctx, tooLongFrameLength);
277 }
278 }
279 } else {
280
281 tooLongFrameLength += buffer.readableBytes();
282 buffer.skipBytes(buffer.readableBytes());
283 }
284 return null;
285 }
286 }
287
288 private void fail(ChannelHandlerContext ctx, long frameLength) {
289 if (frameLength > 0) {
290 Channels.fireExceptionCaught(
291 ctx.getChannel(),
292 new TooLongFrameException(
293 "frame length exceeds " + maxFrameLength +
294 ": " + frameLength + " - discarded"));
295 } else {
296 Channels.fireExceptionCaught(
297 ctx.getChannel(),
298 new TooLongFrameException(
299 "frame length exceeds " + maxFrameLength +
300 " - discarding"));
301 }
302 }
303
304
305
306
307
308
309 private static int indexOf(ChannelBuffer haystack, ChannelBuffer needle) {
310 for (int i = haystack.readerIndex(); i < haystack.writerIndex(); i ++) {
311 int haystackIndex = i;
312 int needleIndex;
313 for (needleIndex = 0; needleIndex < needle.capacity(); needleIndex ++) {
314 if (haystack.getByte(haystackIndex) != needle.getByte(needleIndex)) {
315 break;
316 } else {
317 haystackIndex ++;
318 if (haystackIndex == haystack.writerIndex() &&
319 needleIndex != needle.capacity() - 1) {
320 return -1;
321 }
322 }
323 }
324
325 if (needleIndex == needle.capacity()) {
326
327 return i - haystack.readerIndex();
328 }
329 }
330 return -1;
331 }
332
333 private static void validateDelimiter(ChannelBuffer delimiter) {
334 if (delimiter == null) {
335 throw new NullPointerException("delimiter");
336 }
337 if (!delimiter.readable()) {
338 throw new IllegalArgumentException("empty delimiter");
339 }
340 }
341
342 private static void validateMaxFrameLength(int maxFrameLength) {
343 if (maxFrameLength <= 0) {
344 throw new IllegalArgumentException(
345 "maxFrameLength must be a positive integer: " +
346 maxFrameLength);
347 }
348 }
349 }