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