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