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