1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty.handler.codec.json;
18
19 import static io.netty.util.internal.ObjectUtil.checkPositive;
20
21 import io.netty.buffer.ByteBuf;
22 import io.netty.buffer.ByteBufUtil;
23 import io.netty.channel.ChannelHandlerContext;
24 import io.netty.handler.codec.ByteToMessageDecoder;
25 import io.netty.handler.codec.CorruptedFrameException;
26 import io.netty.handler.codec.TooLongFrameException;
27 import io.netty.channel.ChannelPipeline;
28
29 import java.util.List;
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 public class JsonObjectDecoder extends ByteToMessageDecoder {
45
46 private static final int ST_CORRUPTED = -1;
47 private static final int ST_INIT = 0;
48 private static final int ST_DECODING_NORMAL = 1;
49 private static final int ST_DECODING_ARRAY_STREAM = 2;
50
51 private int openBraces;
52 private int idx;
53
54 private int lastReaderIndex;
55
56 private int state;
57 private boolean insideString;
58
59 private final int maxObjectLength;
60 private final boolean streamArrayElements;
61
62 public JsonObjectDecoder() {
63
64 this(1024 * 1024);
65 }
66
67 public JsonObjectDecoder(int maxObjectLength) {
68 this(maxObjectLength, false);
69 }
70
71 public JsonObjectDecoder(boolean streamArrayElements) {
72 this(1024 * 1024, streamArrayElements);
73 }
74
75
76
77
78
79
80
81
82
83
84 public JsonObjectDecoder(int maxObjectLength, boolean streamArrayElements) {
85 this.maxObjectLength = checkPositive(maxObjectLength, "maxObjectLength");
86 this.streamArrayElements = streamArrayElements;
87 }
88
89 @Override
90 protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
91 if (state == ST_CORRUPTED) {
92 in.skipBytes(in.readableBytes());
93 return;
94 }
95
96 if (this.idx > in.readerIndex() && lastReaderIndex != in.readerIndex()) {
97 this.idx = in.readerIndex() + (idx - lastReaderIndex);
98 }
99
100
101 int idx = this.idx;
102 int wrtIdx = in.writerIndex();
103
104 if (wrtIdx > maxObjectLength) {
105
106 in.skipBytes(in.readableBytes());
107 reset();
108 throw new TooLongFrameException(
109 "object length exceeds " + maxObjectLength + ": " + wrtIdx + " bytes discarded");
110 }
111
112 for (; idx < wrtIdx; idx++) {
113 byte c = in.getByte(idx);
114 if (state == ST_DECODING_NORMAL) {
115 decodeByte(c, in, idx);
116
117
118
119 if (openBraces == 0) {
120 ByteBuf json = extractObject(ctx, in, in.readerIndex(), idx + 1 - in.readerIndex());
121 if (json != null) {
122 out.add(json);
123 }
124
125
126
127 in.readerIndex(idx + 1);
128
129
130 reset();
131 }
132 } else if (state == ST_DECODING_ARRAY_STREAM) {
133 decodeByte(c, in, idx);
134
135 if (!insideString && (openBraces == 1 && c == ',' || openBraces == 0 && c == ']')) {
136
137
138 for (int i = in.readerIndex(); Character.isWhitespace(in.getByte(i)); i++) {
139 in.skipBytes(1);
140 }
141
142
143 int idxNoSpaces = idx - 1;
144 while (idxNoSpaces >= in.readerIndex() && Character.isWhitespace(in.getByte(idxNoSpaces))) {
145 idxNoSpaces--;
146 }
147
148 ByteBuf json = extractObject(ctx, in, in.readerIndex(), idxNoSpaces + 1 - in.readerIndex());
149 if (json != null) {
150 out.add(json);
151 }
152
153 in.readerIndex(idx + 1);
154
155 if (c == ']') {
156 reset();
157 }
158 }
159
160 } else if (c == '{' || c == '[') {
161 initDecoding(c);
162
163 if (state == ST_DECODING_ARRAY_STREAM) {
164
165 in.skipBytes(1);
166 }
167
168 } else if (Character.isWhitespace(c)) {
169 in.skipBytes(1);
170 } else {
171 state = ST_CORRUPTED;
172 throw new CorruptedFrameException(
173 "invalid JSON received at byte position " + idx + ": " + ByteBufUtil.hexDump(in));
174 }
175 }
176
177 if (in.readableBytes() == 0) {
178 this.idx = 0;
179 } else {
180 this.idx = idx;
181 }
182 this.lastReaderIndex = in.readerIndex();
183 }
184
185
186
187
188 @SuppressWarnings("UnusedParameters")
189 protected ByteBuf extractObject(ChannelHandlerContext ctx, ByteBuf buffer, int index, int length) {
190 return buffer.retainedSlice(index, length);
191 }
192
193 private void decodeByte(byte c, ByteBuf in, int idx) {
194 if ((c == '{' || c == '[') && !insideString) {
195 openBraces++;
196 } else if ((c == '}' || c == ']') && !insideString) {
197 openBraces--;
198 } else if (c == '"') {
199
200
201 if (!insideString) {
202 insideString = true;
203 } else {
204 int backslashCount = 0;
205 idx--;
206 while (idx >= 0) {
207 if (in.getByte(idx) == '\\') {
208 backslashCount++;
209 idx--;
210 } else {
211 break;
212 }
213 }
214
215 if (backslashCount % 2 == 0) {
216
217 insideString = false;
218 }
219 }
220 }
221 }
222
223 private void initDecoding(byte openingBrace) {
224 openBraces = 1;
225 if (openingBrace == '[' && streamArrayElements) {
226 state = ST_DECODING_ARRAY_STREAM;
227 } else {
228 state = ST_DECODING_NORMAL;
229 }
230 }
231
232 private void reset() {
233 insideString = false;
234 state = ST_INIT;
235 openBraces = 0;
236 }
237 }