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