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 channelInactive(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.channelInactive(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 try {
206 FullHttpRequest 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
215 } catch (Throwable ignored) {
216 SpdyRstStreamFrame spdyRstStreamFrame =
217 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
218 ctx.writeAndFlush(spdyRstStreamFrame);
219 }
220 } else {
221
222
223
224
225 if (spdySynStreamFrame.isTruncated()) {
226 SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId);
227 spdySynReplyFrame.setLast(true);
228 SpdyHeaders frameHeaders = spdySynReplyFrame.headers();
229 frameHeaders.setInt(STATUS, HttpResponseStatus.REQUEST_HEADER_FIELDS_TOO_LARGE.code());
230 frameHeaders.setObject(VERSION, HttpVersion.HTTP_1_0);
231 ctx.writeAndFlush(spdySynReplyFrame);
232 return;
233 }
234
235 try {
236 FullHttpRequest httpRequestWithEntity = createHttpRequest(spdySynStreamFrame, ctx.alloc());
237
238
239 httpRequestWithEntity.headers().setInt(Names.STREAM_ID, streamId);
240
241 if (spdySynStreamFrame.isLast()) {
242 out.add(httpRequestWithEntity);
243 } else {
244
245 putMessage(streamId, httpRequestWithEntity);
246 }
247 } catch (Throwable t) {
248
249
250
251 SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId);
252 spdySynReplyFrame.setLast(true);
253 SpdyHeaders frameHeaders = spdySynReplyFrame.headers();
254 frameHeaders.setInt(STATUS, HttpResponseStatus.BAD_REQUEST.code());
255 frameHeaders.setObject(VERSION, HttpVersion.HTTP_1_0);
256 ctx.writeAndFlush(spdySynReplyFrame);
257 }
258 }
259
260 } else if (msg instanceof SpdySynReplyFrame) {
261
262 SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
263 int streamId = spdySynReplyFrame.streamId();
264
265
266
267 if (spdySynReplyFrame.isTruncated()) {
268 SpdyRstStreamFrame spdyRstStreamFrame =
269 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.INTERNAL_ERROR);
270 ctx.writeAndFlush(spdyRstStreamFrame);
271 return;
272 }
273
274 try {
275 FullHttpResponse httpResponseWithEntity =
276 createHttpResponse(spdySynReplyFrame, ctx.alloc());
277
278
279 httpResponseWithEntity.headers().setInt(Names.STREAM_ID, streamId);
280
281 if (spdySynReplyFrame.isLast()) {
282 HttpUtil.setContentLength(httpResponseWithEntity, 0);
283 out.add(httpResponseWithEntity);
284 } else {
285
286 putMessage(streamId, httpResponseWithEntity);
287 }
288 } catch (Throwable t) {
289
290
291 SpdyRstStreamFrame spdyRstStreamFrame =
292 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
293 ctx.writeAndFlush(spdyRstStreamFrame);
294 }
295
296 } else if (msg instanceof SpdyHeadersFrame) {
297
298 SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
299 int streamId = spdyHeadersFrame.streamId();
300 FullHttpMessage fullHttpMessage = getMessage(streamId);
301
302 if (fullHttpMessage == null) {
303
304 if (SpdyCodecUtil.isServerId(streamId)) {
305
306
307
308 if (spdyHeadersFrame.isTruncated()) {
309 SpdyRstStreamFrame spdyRstStreamFrame =
310 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.INTERNAL_ERROR);
311 ctx.writeAndFlush(spdyRstStreamFrame);
312 return;
313 }
314
315 try {
316 fullHttpMessage = createHttpResponse(spdyHeadersFrame, ctx.alloc());
317
318
319 fullHttpMessage.headers().setInt(Names.STREAM_ID, streamId);
320
321 if (spdyHeadersFrame.isLast()) {
322 HttpUtil.setContentLength(fullHttpMessage, 0);
323 out.add(fullHttpMessage);
324 } else {
325
326 putMessage(streamId, fullHttpMessage);
327 }
328 } catch (Throwable t) {
329
330
331 SpdyRstStreamFrame spdyRstStreamFrame =
332 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
333 ctx.writeAndFlush(spdyRstStreamFrame);
334 }
335 }
336 return;
337 }
338
339
340 if (!spdyHeadersFrame.isTruncated()) {
341 for (Map.Entry<CharSequence, CharSequence> e: spdyHeadersFrame.headers()) {
342 fullHttpMessage.headers().add(e.getKey(), e.getValue());
343 }
344 }
345
346 if (spdyHeadersFrame.isLast()) {
347 HttpUtil.setContentLength(fullHttpMessage, fullHttpMessage.content().readableBytes());
348 removeMessage(streamId);
349 out.add(fullHttpMessage);
350 }
351
352 } else if (msg instanceof SpdyDataFrame) {
353
354 SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
355 int streamId = spdyDataFrame.streamId();
356 FullHttpMessage fullHttpMessage = getMessage(streamId);
357
358
359 if (fullHttpMessage == null) {
360 return;
361 }
362
363 ByteBuf content = fullHttpMessage.content();
364 if (content.readableBytes() > maxContentLength - spdyDataFrame.content().readableBytes()) {
365 removeMessage(streamId);
366 throw new TooLongFrameException(
367 "HTTP content length exceeded " + maxContentLength + " bytes: "
368 + spdyDataFrame.content().readableBytes());
369 }
370
371 ByteBuf spdyDataFrameData = spdyDataFrame.content();
372 int spdyDataFrameDataLen = spdyDataFrameData.readableBytes();
373 content.writeBytes(spdyDataFrameData, spdyDataFrameData.readerIndex(), spdyDataFrameDataLen);
374
375 if (spdyDataFrame.isLast()) {
376 HttpUtil.setContentLength(fullHttpMessage, content.readableBytes());
377 removeMessage(streamId);
378 out.add(fullHttpMessage);
379 }
380
381 } else if (msg instanceof SpdyRstStreamFrame) {
382
383 SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
384 int streamId = spdyRstStreamFrame.streamId();
385 removeMessage(streamId);
386 }
387 }
388
389 private static FullHttpRequest createHttpRequest(SpdyHeadersFrame requestFrame, ByteBufAllocator alloc)
390 throws Exception {
391
392 SpdyHeaders headers = requestFrame.headers();
393 HttpMethod method = HttpMethod.valueOf(headers.getAsString(METHOD));
394 String url = headers.getAsString(PATH);
395 HttpVersion httpVersion = HttpVersion.valueOf(headers.getAsString(VERSION));
396 headers.remove(METHOD);
397 headers.remove(PATH);
398 headers.remove(VERSION);
399
400 boolean release = true;
401 ByteBuf buffer = alloc.buffer();
402 try {
403 FullHttpRequest req = new DefaultFullHttpRequest(httpVersion, method, url, buffer);
404
405
406 headers.remove(SCHEME);
407
408
409 CharSequence host = headers.get(HOST);
410 headers.remove(HOST);
411 req.headers().set(HttpHeaderNames.HOST, host);
412
413 for (Map.Entry<CharSequence, CharSequence> e : requestFrame.headers()) {
414 req.headers().add(e.getKey(), e.getValue());
415 }
416
417
418 HttpUtil.setKeepAlive(req, true);
419
420
421 req.headers().remove(HttpHeaderNames.TRANSFER_ENCODING);
422 release = false;
423 return req;
424 } finally {
425 if (release) {
426 buffer.release();
427 }
428 }
429 }
430
431 private FullHttpResponse createHttpResponse(SpdyHeadersFrame responseFrame, ByteBufAllocator alloc)
432 throws Exception {
433
434
435 SpdyHeaders headers = responseFrame.headers();
436 HttpResponseStatus status = HttpResponseStatus.parseLine(headers.get(STATUS));
437 HttpVersion version = HttpVersion.valueOf(headers.getAsString(VERSION));
438 headers.remove(STATUS);
439 headers.remove(VERSION);
440
441 boolean release = true;
442 ByteBuf buffer = alloc.buffer();
443 try {
444 FullHttpResponse res = new DefaultFullHttpResponse(
445 version, status, buffer, headersFactory, trailersFactory);
446 for (Map.Entry<CharSequence, CharSequence> e: responseFrame.headers()) {
447 res.headers().add(e.getKey(), e.getValue());
448 }
449
450
451 HttpUtil.setKeepAlive(res, true);
452
453
454 res.headers().remove(HttpHeaderNames.TRANSFER_ENCODING);
455 res.headers().remove(HttpHeaderNames.TRAILER);
456
457 release = false;
458 return res;
459 } finally {
460 if (release) {
461 buffer.release();
462 }
463 }
464 }
465 }