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