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.channel.ChannelHandlerContext;
20 import io.netty.handler.codec.MessageToMessageDecoder;
21 import io.netty.handler.codec.TooLongFrameException;
22 import io.netty.handler.codec.http.DefaultFullHttpRequest;
23 import io.netty.handler.codec.http.DefaultFullHttpResponse;
24 import io.netty.handler.codec.http.FullHttpMessage;
25 import io.netty.handler.codec.http.FullHttpRequest;
26 import io.netty.handler.codec.http.FullHttpResponse;
27 import io.netty.handler.codec.http.HttpHeaders;
28 import io.netty.handler.codec.http.HttpMethod;
29 import io.netty.handler.codec.http.HttpResponseStatus;
30 import io.netty.handler.codec.http.HttpVersion;
31
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Map;
35
36
37
38
39
40 public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
41
42 private final boolean validateHeaders;
43 private final int spdyVersion;
44 private final int maxContentLength;
45 private final Map<Integer, FullHttpMessage> messageMap;
46
47
48
49
50
51
52
53
54
55 public SpdyHttpDecoder(SpdyVersion version, int maxContentLength) {
56 this(version, maxContentLength, new HashMap<Integer, FullHttpMessage>(), true);
57 }
58
59
60
61
62
63
64
65
66
67
68 public SpdyHttpDecoder(SpdyVersion version, int maxContentLength, boolean validateHeaders) {
69 this(version, maxContentLength, new HashMap<Integer, FullHttpMessage>(), validateHeaders);
70 }
71
72
73
74
75
76
77
78
79
80
81 protected SpdyHttpDecoder(SpdyVersion version, int maxContentLength, Map<Integer, FullHttpMessage> messageMap) {
82 this(version, maxContentLength, messageMap, true);
83 }
84
85
86
87
88
89
90
91
92
93
94
95 protected SpdyHttpDecoder(SpdyVersion version, int maxContentLength, Map<Integer,
96 FullHttpMessage> messageMap, boolean validateHeaders) {
97 if (version == null) {
98 throw new NullPointerException("version");
99 }
100 if (maxContentLength <= 0) {
101 throw new IllegalArgumentException(
102 "maxContentLength must be a positive integer: " + maxContentLength);
103 }
104 spdyVersion = version.getVersion();
105 this.maxContentLength = maxContentLength;
106 this.messageMap = messageMap;
107 this.validateHeaders = validateHeaders;
108 }
109
110 protected FullHttpMessage putMessage(int streamId, FullHttpMessage message) {
111 return messageMap.put(streamId, message);
112 }
113
114 protected FullHttpMessage getMessage(int streamId) {
115 return messageMap.get(streamId);
116 }
117
118 protected FullHttpMessage removeMessage(int streamId) {
119 return messageMap.remove(streamId);
120 }
121
122 @Override
123 protected void decode(ChannelHandlerContext ctx, SpdyFrame msg, List<Object> out)
124 throws Exception {
125 if (msg instanceof SpdySynStreamFrame) {
126
127
128 SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
129 int streamId = spdySynStreamFrame.streamId();
130
131 if (SpdyCodecUtil.isServerId(streamId)) {
132
133 int associatedToStreamId = spdySynStreamFrame.associatedStreamId();
134
135
136
137 if (associatedToStreamId == 0) {
138 SpdyRstStreamFrame spdyRstStreamFrame =
139 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.INVALID_STREAM);
140 ctx.writeAndFlush(spdyRstStreamFrame);
141 return;
142 }
143
144
145
146
147 if (spdySynStreamFrame.isLast()) {
148 SpdyRstStreamFrame spdyRstStreamFrame =
149 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
150 ctx.writeAndFlush(spdyRstStreamFrame);
151 return;
152 }
153
154
155
156 if (spdySynStreamFrame.isTruncated()) {
157 SpdyRstStreamFrame spdyRstStreamFrame =
158 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.INTERNAL_ERROR);
159 ctx.writeAndFlush(spdyRstStreamFrame);
160 return;
161 }
162
163 try {
164 FullHttpRequest httpRequestWithEntity = createHttpRequest(spdyVersion, spdySynStreamFrame);
165
166
167 SpdyHttpHeaders.setStreamId(httpRequestWithEntity, streamId);
168 SpdyHttpHeaders.setAssociatedToStreamId(httpRequestWithEntity, associatedToStreamId);
169 SpdyHttpHeaders.setPriority(httpRequestWithEntity, spdySynStreamFrame.priority());
170
171 out.add(httpRequestWithEntity);
172
173 } catch (Exception e) {
174 SpdyRstStreamFrame spdyRstStreamFrame =
175 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
176 ctx.writeAndFlush(spdyRstStreamFrame);
177 }
178 } else {
179
180
181
182
183 if (spdySynStreamFrame.isTruncated()) {
184 SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId);
185 spdySynReplyFrame.setLast(true);
186 SpdyHeaders.setStatus(spdyVersion,
187 spdySynReplyFrame,
188 HttpResponseStatus.REQUEST_HEADER_FIELDS_TOO_LARGE);
189 SpdyHeaders.setVersion(spdyVersion, spdySynReplyFrame, HttpVersion.HTTP_1_0);
190 ctx.writeAndFlush(spdySynReplyFrame);
191 return;
192 }
193
194 try {
195 FullHttpRequest httpRequestWithEntity = createHttpRequest(spdyVersion, spdySynStreamFrame);
196
197
198 SpdyHttpHeaders.setStreamId(httpRequestWithEntity, streamId);
199
200 if (spdySynStreamFrame.isLast()) {
201 out.add(httpRequestWithEntity);
202 } else {
203
204 putMessage(streamId, httpRequestWithEntity);
205 }
206 } catch (Exception e) {
207
208
209
210 SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId);
211 spdySynReplyFrame.setLast(true);
212 SpdyHeaders.setStatus(spdyVersion, spdySynReplyFrame, HttpResponseStatus.BAD_REQUEST);
213 SpdyHeaders.setVersion(spdyVersion, spdySynReplyFrame, HttpVersion.HTTP_1_0);
214 ctx.writeAndFlush(spdySynReplyFrame);
215 }
216 }
217
218 } else if (msg instanceof SpdySynReplyFrame) {
219
220 SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
221 int streamId = spdySynReplyFrame.streamId();
222
223
224
225 if (spdySynReplyFrame.isTruncated()) {
226 SpdyRstStreamFrame spdyRstStreamFrame =
227 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.INTERNAL_ERROR);
228 ctx.writeAndFlush(spdyRstStreamFrame);
229 return;
230 }
231
232 try {
233 FullHttpResponse httpResponseWithEntity =
234 createHttpResponse(ctx, spdyVersion, spdySynReplyFrame, validateHeaders);
235
236
237 SpdyHttpHeaders.setStreamId(httpResponseWithEntity, streamId);
238
239 if (spdySynReplyFrame.isLast()) {
240 HttpHeaders.setContentLength(httpResponseWithEntity, 0);
241 out.add(httpResponseWithEntity);
242 } else {
243
244 putMessage(streamId, httpResponseWithEntity);
245 }
246 } catch (Exception e) {
247
248
249 SpdyRstStreamFrame spdyRstStreamFrame =
250 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
251 ctx.writeAndFlush(spdyRstStreamFrame);
252 }
253
254 } else if (msg instanceof SpdyHeadersFrame) {
255
256 SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
257 int streamId = spdyHeadersFrame.streamId();
258 FullHttpMessage fullHttpMessage = getMessage(streamId);
259
260 if (fullHttpMessage == null) {
261
262 if (SpdyCodecUtil.isServerId(streamId)) {
263
264
265
266 if (spdyHeadersFrame.isTruncated()) {
267 SpdyRstStreamFrame spdyRstStreamFrame =
268 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.INTERNAL_ERROR);
269 ctx.writeAndFlush(spdyRstStreamFrame);
270 return;
271 }
272
273 try {
274 fullHttpMessage = createHttpResponse(ctx, spdyVersion, spdyHeadersFrame, validateHeaders);
275
276
277 SpdyHttpHeaders.setStreamId(fullHttpMessage, streamId);
278
279 if (spdyHeadersFrame.isLast()) {
280 HttpHeaders.setContentLength(fullHttpMessage, 0);
281 out.add(fullHttpMessage);
282 } else {
283
284 putMessage(streamId, fullHttpMessage);
285 }
286 } catch (Exception e) {
287
288
289 SpdyRstStreamFrame spdyRstStreamFrame =
290 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
291 ctx.writeAndFlush(spdyRstStreamFrame);
292 }
293 }
294 return;
295 }
296
297
298 if (!spdyHeadersFrame.isTruncated()) {
299 for (Map.Entry<String, String> e: spdyHeadersFrame.headers()) {
300 fullHttpMessage.headers().add(e.getKey(), e.getValue());
301 }
302 }
303
304 if (spdyHeadersFrame.isLast()) {
305 HttpHeaders.setContentLength(fullHttpMessage, fullHttpMessage.content().readableBytes());
306 removeMessage(streamId);
307 out.add(fullHttpMessage);
308 }
309
310 } else if (msg instanceof SpdyDataFrame) {
311
312 SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
313 int streamId = spdyDataFrame.streamId();
314 FullHttpMessage fullHttpMessage = getMessage(streamId);
315
316
317 if (fullHttpMessage == null) {
318 return;
319 }
320
321 ByteBuf content = fullHttpMessage.content();
322 if (content.readableBytes() > maxContentLength - spdyDataFrame.content().readableBytes()) {
323 removeMessage(streamId);
324 throw new TooLongFrameException(
325 "HTTP content length exceeded " + maxContentLength + " bytes.");
326 }
327
328 ByteBuf spdyDataFrameData = spdyDataFrame.content();
329 int spdyDataFrameDataLen = spdyDataFrameData.readableBytes();
330 content.writeBytes(spdyDataFrameData, spdyDataFrameData.readerIndex(), spdyDataFrameDataLen);
331
332 if (spdyDataFrame.isLast()) {
333 HttpHeaders.setContentLength(fullHttpMessage, content.readableBytes());
334 removeMessage(streamId);
335 out.add(fullHttpMessage);
336 }
337
338 } else if (msg instanceof SpdyRstStreamFrame) {
339
340 SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
341 int streamId = spdyRstStreamFrame.streamId();
342 removeMessage(streamId);
343 }
344 }
345
346 private static FullHttpRequest createHttpRequest(int spdyVersion, SpdyHeadersFrame requestFrame)
347 throws Exception {
348
349 SpdyHeaders headers = requestFrame.headers();
350 HttpMethod method = SpdyHeaders.getMethod(spdyVersion, requestFrame);
351 String url = SpdyHeaders.getUrl(spdyVersion, requestFrame);
352 HttpVersion httpVersion = SpdyHeaders.getVersion(spdyVersion, requestFrame);
353 SpdyHeaders.removeMethod(spdyVersion, requestFrame);
354 SpdyHeaders.removeUrl(spdyVersion, requestFrame);
355 SpdyHeaders.removeVersion(spdyVersion, requestFrame);
356
357 FullHttpRequest req = new DefaultFullHttpRequest(httpVersion, method, url);
358
359
360 SpdyHeaders.removeScheme(spdyVersion, requestFrame);
361
362
363 String host = headers.get(SpdyHeaders.HttpNames.HOST);
364 headers.remove(SpdyHeaders.HttpNames.HOST);
365 req.headers().set(HttpHeaders.Names.HOST, host);
366
367 for (Map.Entry<String, String> e: requestFrame.headers()) {
368 req.headers().add(e.getKey(), e.getValue());
369 }
370
371
372 HttpHeaders.setKeepAlive(req, true);
373
374
375 req.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);
376
377 return req;
378 }
379
380 private static FullHttpResponse createHttpResponse(ChannelHandlerContext ctx, int spdyVersion,
381 SpdyHeadersFrame responseFrame,
382 boolean validateHeaders) throws Exception {
383
384
385 HttpResponseStatus status = SpdyHeaders.getStatus(spdyVersion, responseFrame);
386 HttpVersion version = SpdyHeaders.getVersion(spdyVersion, responseFrame);
387 SpdyHeaders.removeStatus(spdyVersion, responseFrame);
388 SpdyHeaders.removeVersion(spdyVersion, responseFrame);
389
390 FullHttpResponse res = new DefaultFullHttpResponse(version, status, ctx.alloc().buffer(), validateHeaders);
391 for (Map.Entry<String, String> e: responseFrame.headers()) {
392 res.headers().add(e.getKey(), e.getValue());
393 }
394
395
396 HttpHeaders.setKeepAlive(res, true);
397
398
399 res.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);
400 res.headers().remove(HttpHeaders.Names.TRAILER);
401
402 return res;
403 }
404 }