1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.memcache.binary;
17
18 import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
19
20 import io.netty.buffer.ByteBuf;
21 import io.netty.buffer.Unpooled;
22 import io.netty.channel.ChannelHandlerContext;
23 import io.netty.handler.codec.CorruptedFrameException;
24 import io.netty.handler.codec.DecoderResult;
25 import io.netty.handler.codec.memcache.AbstractMemcacheObjectDecoder;
26 import io.netty.handler.codec.memcache.DefaultLastMemcacheContent;
27 import io.netty.handler.codec.memcache.DefaultMemcacheContent;
28 import io.netty.handler.codec.memcache.LastMemcacheContent;
29 import io.netty.handler.codec.memcache.MemcacheContent;
30 import io.netty.util.internal.UnstableApi;
31
32 import java.util.List;
33
34
35
36
37
38
39 @UnstableApi
40 public abstract class AbstractBinaryMemcacheDecoder<M extends BinaryMemcacheMessage>
41 extends AbstractMemcacheObjectDecoder {
42
43 public static final int DEFAULT_MAX_CHUNK_SIZE = 8192;
44
45 private final int chunkSize;
46
47 private M currentMessage;
48 private int alreadyReadChunkSize;
49
50 private State state = State.READ_HEADER;
51
52
53
54
55 protected AbstractBinaryMemcacheDecoder() {
56 this(DEFAULT_MAX_CHUNK_SIZE);
57 }
58
59
60
61
62
63
64 protected AbstractBinaryMemcacheDecoder(int chunkSize) {
65 checkPositiveOrZero(chunkSize, "chunkSize");
66
67 this.chunkSize = chunkSize;
68 }
69
70 @Override
71 protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
72 switch (state) {
73 case READ_HEADER: try {
74 if (in.readableBytes() < 24) {
75 return;
76 }
77 resetDecoder();
78
79 currentMessage = decodeHeader(in);
80 validateHeader(currentMessage);
81 state = State.READ_EXTRAS;
82 } catch (Exception e) {
83 resetDecoder();
84 out.add(invalidMessage(e));
85 return;
86 }
87 case READ_EXTRAS: try {
88 int extrasLength = currentMessage.extrasLength() & 0xFF;
89 if (extrasLength > 0) {
90 if (in.readableBytes() < extrasLength) {
91 return;
92 }
93
94 currentMessage.setExtras(in.readRetainedSlice(extrasLength));
95 }
96
97 state = State.READ_KEY;
98 } catch (Exception e) {
99 resetDecoder();
100 out.add(invalidMessage(e));
101 return;
102 }
103 case READ_KEY: try {
104 int keyLength = currentMessage.keyLength() & 0xFFFF;
105 if (keyLength > 0) {
106 if (in.readableBytes() < keyLength) {
107 return;
108 }
109
110 currentMessage.setKey(in.readRetainedSlice(keyLength));
111 }
112 out.add(currentMessage.retain());
113 state = State.READ_CONTENT;
114 } catch (Exception e) {
115 resetDecoder();
116 out.add(invalidMessage(e));
117 return;
118 }
119 case READ_CONTENT: try {
120 int valueLength = currentMessage.totalBodyLength()
121 - (currentMessage.keyLength() & 0xFFFF)
122 - (currentMessage.extrasLength() & 0xFF);
123 int toRead = in.readableBytes();
124 if (valueLength > 0) {
125 if (toRead == 0) {
126 return;
127 }
128
129 if (toRead > chunkSize) {
130 toRead = chunkSize;
131 }
132
133 int remainingLength = valueLength - alreadyReadChunkSize;
134 if (toRead > remainingLength) {
135 toRead = remainingLength;
136 }
137
138 ByteBuf chunkBuffer = in.readRetainedSlice(toRead);
139
140 MemcacheContent chunk;
141 if ((alreadyReadChunkSize += toRead) >= valueLength) {
142 chunk = new DefaultLastMemcacheContent(chunkBuffer);
143 } else {
144 chunk = new DefaultMemcacheContent(chunkBuffer);
145 }
146
147 out.add(chunk);
148 if (alreadyReadChunkSize < valueLength) {
149 return;
150 }
151 } else {
152 out.add(LastMemcacheContent.EMPTY_LAST_CONTENT);
153 }
154
155 resetDecoder();
156 state = State.READ_HEADER;
157 return;
158 } catch (Exception e) {
159 resetDecoder();
160 out.add(invalidChunk(e));
161 return;
162 }
163 case BAD_MESSAGE:
164 in.skipBytes(actualReadableBytes());
165 return;
166 default:
167 throw new Error("Unknown state reached: " + state);
168 }
169 }
170
171
172
173
174
175
176
177
178
179
180
181
182
183 private static void validateHeader(final BinaryMemcacheMessage header) {
184 final int totalBodyLength = header.totalBodyLength();
185
186
187 if (totalBodyLength < 0) {
188 throw new CorruptedFrameException(
189 "totalBodyLength must neither be negative nor be larger than " + Integer.MAX_VALUE + ", but was: "
190 + (totalBodyLength & 0xFFFFFFFFL));
191 }
192
193
194 final int extrasAndKeyLength = (header.extrasLength() & 0xFF) + (header.keyLength() & 0xFFFF);
195 if (extrasAndKeyLength > totalBodyLength) {
196 throw new CorruptedFrameException(
197 "extrasLength + keyLength must not be larger than totalBodyLength, but was: "
198 + extrasAndKeyLength + " > " + totalBodyLength);
199 }
200 }
201
202
203
204
205
206
207
208 private M invalidMessage(Exception cause) {
209 state = State.BAD_MESSAGE;
210 M message = buildInvalidMessage();
211 message.setDecoderResult(DecoderResult.failure(cause));
212 return message;
213 }
214
215
216
217
218
219
220
221 private MemcacheContent invalidChunk(Exception cause) {
222 state = State.BAD_MESSAGE;
223 MemcacheContent chunk = new DefaultLastMemcacheContent(Unpooled.EMPTY_BUFFER);
224 chunk.setDecoderResult(DecoderResult.failure(cause));
225 return chunk;
226 }
227
228
229
230
231
232
233
234 @Override
235 public void channelInactive(ChannelHandlerContext ctx) throws Exception {
236 super.channelInactive(ctx);
237
238 resetDecoder();
239 }
240
241
242
243
244 protected void resetDecoder() {
245 if (currentMessage != null) {
246 currentMessage.release();
247 currentMessage = null;
248 }
249 alreadyReadChunkSize = 0;
250 }
251
252
253
254
255
256
257
258 protected abstract M decodeHeader(ByteBuf in);
259
260
261
262
263
264
265 protected abstract M buildInvalidMessage();
266
267
268
269
270
271
272
273
274 enum State {
275
276
277
278 READ_HEADER,
279
280
281
282
283 READ_EXTRAS,
284
285
286
287
288 READ_KEY,
289
290
291
292
293 READ_CONTENT,
294
295
296
297
298 BAD_MESSAGE
299 }
300
301 }