1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.handler.codec;
17
18 import io.netty5.buffer.api.Buffer;
19 import io.netty5.channel.ChannelHandlerContext;
20 import io.netty5.util.ByteProcessor;
21
22
23
24
25
26
27
28
29
30
31
32
33
34 public class LineBasedFrameDecoder extends ByteToMessageDecoder {
35
36
37 private final int maxLength;
38
39 private final boolean failFast;
40 private final boolean stripDelimiter;
41
42
43 private boolean discarding;
44 private int discardedBytes;
45
46
47 private int offset;
48
49
50
51
52
53
54
55 public LineBasedFrameDecoder(final int maxLength) {
56 this(maxLength, true, false);
57 }
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 public LineBasedFrameDecoder(final int maxLength, final boolean stripDelimiter, final boolean failFast) {
75 this.maxLength = maxLength;
76 this.failFast = failFast;
77 this.stripDelimiter = stripDelimiter;
78 }
79
80 @Override
81 protected final void decode(ChannelHandlerContext ctx, Buffer in) throws Exception {
82 Object decoded = decode0(ctx, in);
83 if (decoded != null) {
84 ctx.fireChannelRead(decoded);
85 }
86 }
87
88
89
90
91
92
93
94
95
96 protected Object decode0(ChannelHandlerContext ctx, Buffer buffer) {
97 final int eol = findEndOfLine(buffer);
98 if (!discarding) {
99 if (eol >= 0) {
100 final Buffer frame;
101 final int length = eol - buffer.readerOffset();
102 final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;
103
104 if (length > maxLength) {
105 buffer.readerOffset(eol + delimLength);
106 fail(ctx, length);
107 return null;
108 }
109
110 if (stripDelimiter) {
111 frame = buffer.readSplit(length);
112 buffer.skipReadableBytes(delimLength);
113 } else {
114 frame = buffer.readSplit(length + delimLength);
115 }
116
117 return frame;
118 } else {
119 final int length = buffer.readableBytes();
120 if (length > maxLength) {
121 discardedBytes = length;
122 buffer.readerOffset(buffer.writerOffset());
123 discarding = true;
124 offset = 0;
125 if (failFast) {
126 fail(ctx, "over " + discardedBytes);
127 }
128 }
129 return null;
130 }
131 } else {
132 if (eol >= 0) {
133 final int length = discardedBytes + eol - buffer.readerOffset();
134 final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;
135 buffer.readerOffset(eol + delimLength);
136 discardedBytes = 0;
137 discarding = false;
138 if (!failFast) {
139 fail(ctx, length);
140 }
141 } else {
142 discardedBytes += buffer.readableBytes();
143 buffer.readerOffset(buffer.writerOffset());
144
145 offset = 0;
146 }
147 return null;
148 }
149 }
150
151 private void fail(final ChannelHandlerContext ctx, int length) {
152 fail(ctx, String.valueOf(length));
153 }
154
155 private void fail(final ChannelHandlerContext ctx, String length) {
156 ctx.fireChannelExceptionCaught(
157 new TooLongFrameException(
158 "frame length (" + length + ") exceeds the allowed maximum (" + maxLength + ')'));
159 }
160
161
162
163
164
165 private int findEndOfLine(final Buffer buffer) {
166 int totalLength = buffer.readableBytes();
167 int index = buffer.readerOffset() + offset;
168 int i = buffer.openCursor(index, totalLength - offset).process(ByteProcessor.FIND_LF);
169 i = i == -1 ? -1 : index + i;
170 if (i >= 0) {
171 offset = 0;
172 if (i > 0 && buffer.getByte(i - 1) == '\r') {
173 i--;
174 }
175 } else {
176 offset = totalLength;
177 }
178 return i;
179 }
180 }