1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.channel.ChannelHandlerContext;
20
21 import java.util.List;
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 public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder {
59
60 private final ByteBuf[] delimiters;
61 private final int maxFrameLength;
62 private final boolean stripDelimiter;
63 private final boolean failFast;
64 private boolean discardingTooLongFrame;
65 private int tooLongFrameLength;
66
67 private final LineBasedFrameDecoder lineBasedDecoder;
68
69
70
71
72
73
74
75
76
77 public DelimiterBasedFrameDecoder(int maxFrameLength, ByteBuf 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, ByteBuf delimiter) {
93 this(maxFrameLength, stripDelimiter, true, 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 ByteBuf delimiter) {
116 this(maxFrameLength, stripDelimiter, failFast, new ByteBuf[] {
117 delimiter.slice(delimiter.readerIndex(), delimiter.readableBytes())});
118 }
119
120
121
122
123
124
125
126
127
128 public DelimiterBasedFrameDecoder(int maxFrameLength, ByteBuf... delimiters) {
129 this(maxFrameLength, true, delimiters);
130 }
131
132
133
134
135
136
137
138
139
140
141
142 public DelimiterBasedFrameDecoder(
143 int maxFrameLength, boolean stripDelimiter, ByteBuf... delimiters) {
144 this(maxFrameLength, stripDelimiter, true, delimiters);
145 }
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164 public DelimiterBasedFrameDecoder(
165 int maxFrameLength, boolean stripDelimiter, boolean failFast, ByteBuf... delimiters) {
166 validateMaxFrameLength(maxFrameLength);
167 if (delimiters == null) {
168 throw new NullPointerException("delimiters");
169 }
170 if (delimiters.length == 0) {
171 throw new IllegalArgumentException("empty delimiters");
172 }
173
174 if (isLineBased(delimiters) && !isSubclass()) {
175 lineBasedDecoder = new LineBasedFrameDecoder(maxFrameLength, stripDelimiter, failFast);
176 this.delimiters = null;
177 } else {
178 this.delimiters = new ByteBuf[delimiters.length];
179 for (int i = 0; i < delimiters.length; i ++) {
180 ByteBuf d = delimiters[i];
181 validateDelimiter(d);
182 this.delimiters[i] = d.slice(d.readerIndex(), d.readableBytes());
183 }
184 lineBasedDecoder = null;
185 }
186 this.maxFrameLength = maxFrameLength;
187 this.stripDelimiter = stripDelimiter;
188 this.failFast = failFast;
189 }
190
191
192 private static boolean isLineBased(final ByteBuf[] delimiters) {
193 if (delimiters.length != 2) {
194 return false;
195 }
196 ByteBuf a = delimiters[0];
197 ByteBuf b = delimiters[1];
198 if (a.capacity() < b.capacity()) {
199 a = delimiters[1];
200 b = delimiters[0];
201 }
202 return a.capacity() == 2 && b.capacity() == 1
203 && a.getByte(0) == '\r' && a.getByte(1) == '\n'
204 && b.getByte(0) == '\n';
205 }
206
207
208
209
210 private boolean isSubclass() {
211 return getClass() != DelimiterBasedFrameDecoder.class;
212 }
213
214 @Override
215 protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
216 Object decoded = decode(ctx, in);
217 if (decoded != null) {
218 out.add(decoded);
219 }
220 }
221
222
223
224
225
226
227
228
229
230 protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
231 if (lineBasedDecoder != null) {
232 return lineBasedDecoder.decode(ctx, buffer);
233 }
234
235 int minFrameLength = Integer.MAX_VALUE;
236 ByteBuf minDelim = null;
237 for (ByteBuf delim: delimiters) {
238 int frameLength = indexOf(buffer, delim);
239 if (frameLength >= 0 && frameLength < minFrameLength) {
240 minFrameLength = frameLength;
241 minDelim = delim;
242 }
243 }
244
245 if (minDelim != null) {
246 int minDelimLength = minDelim.capacity();
247 ByteBuf frame;
248
249 if (discardingTooLongFrame) {
250
251
252 discardingTooLongFrame = false;
253 buffer.skipBytes(minFrameLength + minDelimLength);
254
255 int tooLongFrameLength = this.tooLongFrameLength;
256 this.tooLongFrameLength = 0;
257 if (!failFast) {
258 fail(tooLongFrameLength);
259 }
260 return null;
261 }
262
263 if (minFrameLength > maxFrameLength) {
264
265 buffer.skipBytes(minFrameLength + minDelimLength);
266 fail(minFrameLength);
267 return null;
268 }
269
270 if (stripDelimiter) {
271 frame = buffer.readSlice(minFrameLength);
272 buffer.skipBytes(minDelimLength);
273 } else {
274 frame = buffer.readSlice(minFrameLength + minDelimLength);
275 }
276
277 return frame.retain();
278 } else {
279 if (!discardingTooLongFrame) {
280 if (buffer.readableBytes() > maxFrameLength) {
281
282 tooLongFrameLength = buffer.readableBytes();
283 buffer.skipBytes(buffer.readableBytes());
284 discardingTooLongFrame = true;
285 if (failFast) {
286 fail(tooLongFrameLength);
287 }
288 }
289 } else {
290
291 tooLongFrameLength += buffer.readableBytes();
292 buffer.skipBytes(buffer.readableBytes());
293 }
294 return null;
295 }
296 }
297
298 private void fail(long frameLength) {
299 if (frameLength > 0) {
300 throw new TooLongFrameException(
301 "frame length exceeds " + maxFrameLength +
302 ": " + frameLength + " - discarded");
303 } else {
304 throw new TooLongFrameException(
305 "frame length exceeds " + maxFrameLength +
306 " - discarding");
307 }
308 }
309
310
311
312
313
314
315 private static int indexOf(ByteBuf haystack, ByteBuf needle) {
316 for (int i = haystack.readerIndex(); i < haystack.writerIndex(); i ++) {
317 int haystackIndex = i;
318 int needleIndex;
319 for (needleIndex = 0; needleIndex < needle.capacity(); needleIndex ++) {
320 if (haystack.getByte(haystackIndex) != needle.getByte(needleIndex)) {
321 break;
322 } else {
323 haystackIndex ++;
324 if (haystackIndex == haystack.writerIndex() &&
325 needleIndex != needle.capacity() - 1) {
326 return -1;
327 }
328 }
329 }
330
331 if (needleIndex == needle.capacity()) {
332
333 return i - haystack.readerIndex();
334 }
335 }
336 return -1;
337 }
338
339 private static void validateDelimiter(ByteBuf delimiter) {
340 if (delimiter == null) {
341 throw new NullPointerException("delimiter");
342 }
343 if (!delimiter.isReadable()) {
344 throw new IllegalArgumentException("empty delimiter");
345 }
346 }
347
348 private static void validateMaxFrameLength(int maxFrameLength) {
349 if (maxFrameLength <= 0) {
350 throw new IllegalArgumentException(
351 "maxFrameLength must be a positive integer: " +
352 maxFrameLength);
353 }
354 }
355 }