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