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