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