1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.spdy;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.buffer.ByteBufAllocator;
20 import io.netty.util.internal.ObjectUtil;
21
22 import java.nio.charset.StandardCharsets;
23
24 import static io.netty.handler.codec.spdy.SpdyCodecUtil.getSignedInt;
25
26 public class SpdyHeaderBlockRawDecoder extends SpdyHeaderBlockDecoder {
27
28 private static final int LENGTH_FIELD_SIZE = 4;
29
30 private final int maxHeaderSize;
31
32 private State state;
33
34 private ByteBuf cumulation;
35
36 private int headerSize;
37 private int numHeaders;
38 private int length;
39 private String name;
40
41 private enum State {
42 READ_NUM_HEADERS,
43 READ_NAME_LENGTH,
44 READ_NAME,
45 SKIP_NAME,
46 READ_VALUE_LENGTH,
47 READ_VALUE,
48 SKIP_VALUE,
49 END_HEADER_BLOCK,
50 ERROR
51 }
52
53 public SpdyHeaderBlockRawDecoder(SpdyVersion spdyVersion, int maxHeaderSize) {
54 ObjectUtil.checkNotNull(spdyVersion, "spdyVersion");
55 this.maxHeaderSize = maxHeaderSize;
56 state = State.READ_NUM_HEADERS;
57 }
58
59 private static int readLengthField(ByteBuf buffer) {
60 int length = getSignedInt(buffer, buffer.readerIndex());
61 buffer.skipBytes(LENGTH_FIELD_SIZE);
62 return length;
63 }
64
65 @Override
66 void decode(ByteBufAllocator alloc, ByteBuf headerBlock, SpdyHeadersFrame frame) throws Exception {
67 ObjectUtil.checkNotNull(headerBlock, "headerBlock");
68 ObjectUtil.checkNotNull(frame, "frame");
69
70 if (cumulation == null) {
71 decodeHeaderBlock(headerBlock, frame);
72 if (headerBlock.isReadable()) {
73 cumulation = alloc.buffer(headerBlock.readableBytes());
74 cumulation.writeBytes(headerBlock);
75 }
76 } else {
77 cumulation.writeBytes(headerBlock);
78 decodeHeaderBlock(cumulation, frame);
79 if (cumulation.isReadable()) {
80 cumulation.discardReadBytes();
81 } else {
82 releaseBuffer();
83 }
84 }
85 }
86
87 protected void decodeHeaderBlock(ByteBuf headerBlock, SpdyHeadersFrame frame) throws Exception {
88 int skipLength;
89 while (headerBlock.isReadable()) {
90 switch(state) {
91 case READ_NUM_HEADERS:
92 if (headerBlock.readableBytes() < LENGTH_FIELD_SIZE) {
93 return;
94 }
95
96 numHeaders = readLengthField(headerBlock);
97
98 if (numHeaders < 0) {
99 state = State.ERROR;
100 frame.setInvalid();
101 } else if (numHeaders == 0) {
102 state = State.END_HEADER_BLOCK;
103 } else {
104 state = State.READ_NAME_LENGTH;
105 }
106 break;
107
108 case READ_NAME_LENGTH:
109 if (headerBlock.readableBytes() < LENGTH_FIELD_SIZE) {
110 return;
111 }
112
113 length = readLengthField(headerBlock);
114
115
116 if (length <= 0) {
117 state = State.ERROR;
118 frame.setInvalid();
119 } else if (length > maxHeaderSize || headerSize > maxHeaderSize - length) {
120 headerSize = maxHeaderSize + 1;
121 state = State.SKIP_NAME;
122 frame.setTruncated();
123 } else {
124 headerSize += length;
125 state = State.READ_NAME;
126 }
127 break;
128
129 case READ_NAME:
130 if (headerBlock.readableBytes() < length) {
131 return;
132 }
133
134 byte[] nameBytes = new byte[length];
135 headerBlock.readBytes(nameBytes);
136 name = new String(nameBytes, StandardCharsets.UTF_8);
137
138
139 if (frame.headers().contains(name)) {
140 state = State.ERROR;
141 frame.setInvalid();
142 } else {
143 state = State.READ_VALUE_LENGTH;
144 }
145 break;
146
147 case SKIP_NAME:
148 skipLength = Math.min(headerBlock.readableBytes(), length);
149 headerBlock.skipBytes(skipLength);
150 length -= skipLength;
151
152 if (length == 0) {
153 state = State.READ_VALUE_LENGTH;
154 }
155 break;
156
157 case READ_VALUE_LENGTH:
158 if (headerBlock.readableBytes() < LENGTH_FIELD_SIZE) {
159 return;
160 }
161
162 length = readLengthField(headerBlock);
163
164
165 if (length < 0) {
166 state = State.ERROR;
167 frame.setInvalid();
168 } else if (length == 0) {
169 if (!frame.isTruncated()) {
170
171 frame.headers().add(name, "");
172 }
173
174 name = null;
175 if (--numHeaders == 0) {
176 state = State.END_HEADER_BLOCK;
177 } else {
178 state = State.READ_NAME_LENGTH;
179 }
180
181 } else if (length > maxHeaderSize || headerSize > maxHeaderSize - length) {
182 headerSize = maxHeaderSize + 1;
183 name = null;
184 state = State.SKIP_VALUE;
185 frame.setTruncated();
186 } else {
187 headerSize += length;
188 state = State.READ_VALUE;
189 }
190 break;
191
192 case READ_VALUE:
193 if (headerBlock.readableBytes() < length) {
194 return;
195 }
196
197 byte[] valueBytes = new byte[length];
198 headerBlock.readBytes(valueBytes);
199
200
201 int index = 0;
202 int offset = 0;
203
204
205 if (valueBytes[0] == (byte) 0) {
206 state = State.ERROR;
207 frame.setInvalid();
208 break;
209 }
210
211 while (index < length) {
212 while (index < valueBytes.length && valueBytes[index] != (byte) 0) {
213 index ++;
214 }
215 if (index < valueBytes.length) {
216
217 if (index + 1 == valueBytes.length || valueBytes[index + 1] == (byte) 0) {
218
219
220
221 state = State.ERROR;
222 frame.setInvalid();
223 break;
224 }
225 }
226 String value = new String(valueBytes, offset, index - offset, StandardCharsets.UTF_8);
227
228 try {
229 frame.headers().add(name, value);
230 } catch (IllegalArgumentException e) {
231
232 state = State.ERROR;
233 frame.setInvalid();
234 break;
235 }
236 index ++;
237 offset = index;
238 }
239
240 name = null;
241
242
243 if (state == State.ERROR) {
244 break;
245 }
246
247 if (--numHeaders == 0) {
248 state = State.END_HEADER_BLOCK;
249 } else {
250 state = State.READ_NAME_LENGTH;
251 }
252 break;
253
254 case SKIP_VALUE:
255 skipLength = Math.min(headerBlock.readableBytes(), length);
256 headerBlock.skipBytes(skipLength);
257 length -= skipLength;
258
259 if (length == 0) {
260 if (--numHeaders == 0) {
261 state = State.END_HEADER_BLOCK;
262 } else {
263 state = State.READ_NAME_LENGTH;
264 }
265 }
266 break;
267
268 case END_HEADER_BLOCK:
269 state = State.ERROR;
270 frame.setInvalid();
271 break;
272
273 case ERROR:
274 headerBlock.skipBytes(headerBlock.readableBytes());
275 return;
276
277 default:
278 throw new Error("Shouldn't reach here.");
279 }
280 }
281 }
282
283 @Override
284 void endHeaderBlock(SpdyHeadersFrame frame) throws Exception {
285 if (state != State.END_HEADER_BLOCK) {
286 frame.setInvalid();
287 }
288
289 releaseBuffer();
290
291
292 headerSize = 0;
293 name = null;
294 state = State.READ_NUM_HEADERS;
295 }
296
297 @Override
298 void end() {
299 releaseBuffer();
300 }
301
302 private void releaseBuffer() {
303 if (cumulation != null) {
304 cumulation.release();
305 cumulation = null;
306 }
307 }
308 }