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.buffer.ByteBufUtil;
22 import io.netty.channel.ChannelHandlerContext;
23 import io.netty.util.internal.ObjectUtil;
24
25 import java.util.List;
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
62 public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder {
63
64 private final ByteBuf[] delimiters;
65 private final int maxFrameLength;
66 private final boolean stripDelimiter;
67 private final boolean failFast;
68 private boolean discardingTooLongFrame;
69 private int tooLongFrameLength;
70
71 private final LineBasedFrameDecoder lineBasedDecoder;
72
73
74
75
76
77
78
79
80
81 public DelimiterBasedFrameDecoder(int maxFrameLength, ByteBuf delimiter) {
82 this(maxFrameLength, true, delimiter);
83 }
84
85
86
87
88
89
90
91
92
93
94
95 public DelimiterBasedFrameDecoder(
96 int maxFrameLength, boolean stripDelimiter, ByteBuf delimiter) {
97 this(maxFrameLength, stripDelimiter, true, delimiter);
98 }
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117 public DelimiterBasedFrameDecoder(
118 int maxFrameLength, boolean stripDelimiter, boolean failFast,
119 ByteBuf delimiter) {
120 this(maxFrameLength, stripDelimiter, failFast, new ByteBuf[] {
121 delimiter.slice(delimiter.readerIndex(), delimiter.readableBytes())});
122 }
123
124
125
126
127
128
129
130
131
132 public DelimiterBasedFrameDecoder(int maxFrameLength, ByteBuf... 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, ByteBuf... delimiters) {
148 this(maxFrameLength, stripDelimiter, true, 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, ByteBuf... delimiters) {
170 validateMaxFrameLength(maxFrameLength);
171 ObjectUtil.checkNonEmpty(delimiters, "delimiters");
172
173 if (isLineBased(delimiters) && !isSubclass()) {
174 lineBasedDecoder = new LineBasedFrameDecoder(maxFrameLength, stripDelimiter, failFast);
175 this.delimiters = null;
176 } else {
177 this.delimiters = new ByteBuf[delimiters.length];
178 for (int i = 0; i < delimiters.length; i ++) {
179 ByteBuf d = delimiters[i];
180 validateDelimiter(d);
181 this.delimiters[i] = d.slice(d.readerIndex(), d.readableBytes());
182 }
183 lineBasedDecoder = null;
184 }
185 this.maxFrameLength = maxFrameLength;
186 this.stripDelimiter = stripDelimiter;
187 this.failFast = failFast;
188 }
189
190
191 private static boolean isLineBased(final ByteBuf[] delimiters) {
192 if (delimiters.length != 2) {
193 return false;
194 }
195 ByteBuf a = delimiters[0];
196 ByteBuf b = delimiters[1];
197 if (a.capacity() < b.capacity()) {
198 a = delimiters[1];
199 b = delimiters[0];
200 }
201 return a.capacity() == 2 && b.capacity() == 1
202 && a.getByte(0) == '\r' && a.getByte(1) == '\n'
203 && b.getByte(0) == '\n';
204 }
205
206
207
208
209 private boolean isSubclass() {
210 return getClass() != DelimiterBasedFrameDecoder.class;
211 }
212
213 @Override
214 protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
215 Object decoded = decode(ctx, in);
216 if (decoded != null) {
217 out.add(decoded);
218 }
219 }
220
221
222
223
224
225
226
227
228
229 protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
230 if (lineBasedDecoder != null) {
231 return lineBasedDecoder.decode(ctx, buffer);
232 }
233
234 int minFrameLength = Integer.MAX_VALUE;
235 ByteBuf minDelim = null;
236 for (ByteBuf delim: delimiters) {
237 int frameLength = indexOf(buffer, delim);
238 if (frameLength >= 0 && frameLength < minFrameLength) {
239 minFrameLength = frameLength;
240 minDelim = delim;
241 }
242 }
243
244 if (minDelim != null) {
245 int minDelimLength = minDelim.capacity();
246 ByteBuf frame;
247
248 if (discardingTooLongFrame) {
249
250
251 discardingTooLongFrame = false;
252 buffer.skipBytes(minFrameLength + minDelimLength);
253
254 int tooLongFrameLength = this.tooLongFrameLength;
255 this.tooLongFrameLength = 0;
256 if (!failFast) {
257 fail(tooLongFrameLength);
258 }
259 return null;
260 }
261
262 if (minFrameLength > maxFrameLength) {
263
264 buffer.skipBytes(minFrameLength + minDelimLength);
265 fail(minFrameLength);
266 return null;
267 }
268
269 if (stripDelimiter) {
270 frame = buffer.readRetainedSlice(minFrameLength);
271 buffer.skipBytes(minDelimLength);
272 } else {
273 frame = buffer.readRetainedSlice(minFrameLength + minDelimLength);
274 }
275
276 return frame;
277 } else {
278 if (!discardingTooLongFrame) {
279 if (buffer.readableBytes() > maxFrameLength) {
280
281 tooLongFrameLength = buffer.readableBytes();
282 buffer.skipBytes(buffer.readableBytes());
283 discardingTooLongFrame = true;
284 if (failFast) {
285 fail(tooLongFrameLength);
286 }
287 }
288 } else {
289
290 tooLongFrameLength += buffer.readableBytes();
291 buffer.skipBytes(buffer.readableBytes());
292 }
293 return null;
294 }
295 }
296
297 private void fail(long frameLength) {
298 if (frameLength > 0) {
299 throw new TooLongFrameException(
300 "frame length exceeds " + maxFrameLength +
301 ": " + frameLength + " - discarded");
302 } else {
303 throw new TooLongFrameException(
304 "frame length exceeds " + maxFrameLength +
305 " - discarding");
306 }
307 }
308
309
310
311
312
313
314 private static int indexOf(ByteBuf haystack, ByteBuf needle) {
315 int index = ByteBufUtil.indexOf(needle, haystack);
316 if (index == -1) {
317 return -1;
318 }
319 return index - haystack.readerIndex();
320 }
321
322 private static void validateDelimiter(ByteBuf delimiter) {
323 ObjectUtil.checkNotNull(delimiter, "delimiter");
324 if (!delimiter.isReadable()) {
325 throw new IllegalArgumentException("empty delimiter");
326 }
327 }
328
329 private static void validateMaxFrameLength(int maxFrameLength) {
330 checkPositive(maxFrameLength, "maxFrameLength");
331 }
332 }