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