1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.jboss.netty.handler.codec.spdy;
17
18 import static org.jboss.netty.handler.codec.spdy.SpdyCodecUtil.*;
19
20 import java.util.HashMap;
21 import java.util.Map;
22
23 import org.jboss.netty.buffer.ChannelBuffer;
24 import org.jboss.netty.buffer.ChannelBuffers;
25 import org.jboss.netty.channel.Channel;
26 import org.jboss.netty.channel.ChannelHandlerContext;
27 import org.jboss.netty.channel.Channels;
28 import org.jboss.netty.handler.codec.frame.TooLongFrameException;
29 import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
30 import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
31 import org.jboss.netty.handler.codec.http.HttpHeaders;
32 import org.jboss.netty.handler.codec.http.HttpMessage;
33 import org.jboss.netty.handler.codec.http.HttpMethod;
34 import org.jboss.netty.handler.codec.http.HttpRequest;
35 import org.jboss.netty.handler.codec.http.HttpResponse;
36 import org.jboss.netty.handler.codec.http.HttpResponseStatus;
37 import org.jboss.netty.handler.codec.http.HttpVersion;
38 import org.jboss.netty.handler.codec.oneone.OneToOneDecoder;
39
40
41
42
43
44 public class SpdyHttpDecoder extends OneToOneDecoder {
45
46 private final int spdyVersion;
47 private final int maxContentLength;
48 private final Map<Integer, HttpMessage> messageMap;
49
50
51
52
53
54
55
56
57
58 public SpdyHttpDecoder(SpdyVersion spdyVersion, int maxContentLength) {
59 this(spdyVersion, maxContentLength, new HashMap<Integer, HttpMessage>());
60 }
61
62
63
64
65
66
67
68
69
70
71 protected SpdyHttpDecoder(SpdyVersion spdyVersion, int maxContentLength, Map<Integer, HttpMessage> messageMap) {
72 if (spdyVersion == null) {
73 throw new NullPointerException("spdyVersion");
74 }
75 if (maxContentLength <= 0) {
76 throw new IllegalArgumentException(
77 "maxContentLength must be a positive integer: " + maxContentLength);
78 }
79 this.spdyVersion = spdyVersion.getVersion();
80 this.maxContentLength = maxContentLength;
81 this.messageMap = messageMap;
82 }
83
84 protected HttpMessage putMessage(int streamId, HttpMessage message) {
85 return messageMap.put(streamId, message);
86 }
87
88 protected HttpMessage getMessage(int streamId) {
89 return messageMap.get(streamId);
90 }
91
92 protected HttpMessage removeMessage(int streamId) {
93 return messageMap.remove(streamId);
94 }
95
96 @Override
97 protected Object decode(ChannelHandlerContext ctx, Channel channel, Object msg)
98 throws Exception {
99
100 if (msg instanceof SpdySynStreamFrame) {
101
102
103 SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
104 int streamId = spdySynStreamFrame.getStreamId();
105
106 if (isServerId(streamId)) {
107
108 int associatedToStreamId = spdySynStreamFrame.getAssociatedToStreamId();
109
110
111
112 if (associatedToStreamId == 0) {
113 SpdyRstStreamFrame spdyRstStreamFrame =
114 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.INVALID_STREAM);
115 Channels.write(ctx, Channels.future(channel), spdyRstStreamFrame);
116 return null;
117 }
118
119
120
121
122 if (spdySynStreamFrame.isLast()) {
123 SpdyRstStreamFrame spdyRstStreamFrame =
124 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
125 Channels.write(ctx, Channels.future(channel), spdyRstStreamFrame);
126 return null;
127 }
128
129
130
131 if (spdySynStreamFrame.isTruncated()) {
132 SpdyRstStreamFrame spdyRstStreamFrame =
133 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.INTERNAL_ERROR);
134 Channels.write(ctx, Channels.future(channel), spdyRstStreamFrame);
135 return null;
136 }
137
138 try {
139 HttpRequest httpRequest = createHttpRequest(spdyVersion, spdySynStreamFrame);
140
141
142 SpdyHttpHeaders.setStreamId(httpRequest, streamId);
143 SpdyHttpHeaders.setAssociatedToStreamId(httpRequest, associatedToStreamId);
144 SpdyHttpHeaders.setPriority(httpRequest, spdySynStreamFrame.getPriority());
145
146 return httpRequest;
147
148 } catch (Exception e) {
149 SpdyRstStreamFrame spdyRstStreamFrame =
150 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
151 Channels.write(ctx, Channels.future(channel), spdyRstStreamFrame);
152 }
153 } else {
154
155
156
157
158 if (spdySynStreamFrame.isTruncated()) {
159 SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId);
160 spdySynReplyFrame.setLast(true);
161 SpdyHeaders.setStatus(spdyVersion,
162 spdySynReplyFrame,
163 HttpResponseStatus.REQUEST_HEADER_FIELDS_TOO_LARGE);
164 SpdyHeaders.setVersion(spdyVersion, spdySynReplyFrame, HttpVersion.HTTP_1_0);
165 Channels.write(ctx, Channels.future(channel), spdySynReplyFrame);
166 return null;
167 }
168
169 try {
170 HttpRequest httpRequest = createHttpRequest(spdyVersion, spdySynStreamFrame);
171
172
173 SpdyHttpHeaders.setStreamId(httpRequest, streamId);
174
175 if (spdySynStreamFrame.isLast()) {
176 return httpRequest;
177 } else {
178
179 putMessage(streamId, httpRequest);
180 }
181 } catch (Exception e) {
182
183
184
185 SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId);
186 spdySynReplyFrame.setLast(true);
187 SpdyHeaders.setStatus(spdyVersion, spdySynReplyFrame, HttpResponseStatus.BAD_REQUEST);
188 SpdyHeaders.setVersion(spdyVersion, spdySynReplyFrame, HttpVersion.HTTP_1_0);
189 Channels.write(ctx, Channels.future(channel), spdySynReplyFrame);
190 }
191 }
192
193 } else if (msg instanceof SpdySynReplyFrame) {
194
195 SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
196 int streamId = spdySynReplyFrame.getStreamId();
197
198
199
200 if (spdySynReplyFrame.isTruncated()) {
201 SpdyRstStreamFrame spdyRstStreamFrame =
202 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.INTERNAL_ERROR);
203 Channels.write(ctx, Channels.future(channel), spdyRstStreamFrame);
204 return null;
205 }
206
207 try {
208 HttpResponse httpResponse = createHttpResponse(spdyVersion, spdySynReplyFrame);
209
210
211 SpdyHttpHeaders.setStreamId(httpResponse, streamId);
212
213 if (spdySynReplyFrame.isLast()) {
214 HttpHeaders.setContentLength(httpResponse, 0);
215 return httpResponse;
216 } else {
217
218 putMessage(streamId, httpResponse);
219 }
220 } catch (Exception e) {
221
222
223 SpdyRstStreamFrame spdyRstStreamFrame =
224 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
225 Channels.write(ctx, Channels.future(channel), spdyRstStreamFrame);
226 }
227
228 } else if (msg instanceof SpdyHeadersFrame) {
229
230 SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
231 int streamId = spdyHeadersFrame.getStreamId();
232 HttpMessage httpMessage = getMessage(streamId);
233
234 if (httpMessage == null) {
235
236 if (isServerId(streamId)) {
237
238
239
240 if (spdyHeadersFrame.isTruncated()) {
241 SpdyRstStreamFrame spdyRstStreamFrame =
242 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.INTERNAL_ERROR);
243 Channels.write(ctx, Channels.future(channel), spdyRstStreamFrame);
244 return null;
245 }
246
247 try {
248 httpMessage = createHttpResponse(spdyVersion, spdyHeadersFrame);
249
250
251 SpdyHttpHeaders.setStreamId(httpMessage, streamId);
252
253 if (spdyHeadersFrame.isLast()) {
254 HttpHeaders.setContentLength(httpMessage, 0);
255 return httpMessage;
256 } else {
257
258 putMessage(streamId, httpMessage);
259 }
260 } catch (Exception e) {
261
262
263 SpdyRstStreamFrame spdyRstStreamFrame =
264 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
265 Channels.write(ctx, Channels.future(channel), spdyRstStreamFrame);
266 return null;
267 }
268 }
269 return null;
270 }
271
272
273 if (!spdyHeadersFrame.isTruncated()) {
274 for (Map.Entry<String, String> e : spdyHeadersFrame.headers()) {
275 httpMessage.headers().add(e.getKey(), e.getValue());
276 }
277 }
278
279 if (spdyHeadersFrame.isLast()) {
280 HttpHeaders.setContentLength(httpMessage, httpMessage.getContent().readableBytes());
281 removeMessage(streamId);
282 return httpMessage;
283 }
284
285 } else if (msg instanceof SpdyDataFrame) {
286
287 SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
288 int streamId = spdyDataFrame.getStreamId();
289 HttpMessage httpMessage = getMessage(streamId);
290
291
292 if (httpMessage == null) {
293 return null;
294 }
295
296 ChannelBuffer content = httpMessage.getContent();
297 if (content.readableBytes() > maxContentLength - spdyDataFrame.getData().readableBytes()) {
298 removeMessage(streamId);
299 throw new TooLongFrameException(
300 "HTTP content length exceeded " + maxContentLength + " bytes.");
301 }
302
303 if (content == ChannelBuffers.EMPTY_BUFFER) {
304 content = ChannelBuffers.dynamicBuffer(channel.getConfig().getBufferFactory());
305 content.writeBytes(spdyDataFrame.getData());
306 httpMessage.setContent(content);
307 } else {
308 content.writeBytes(spdyDataFrame.getData());
309 }
310
311 if (spdyDataFrame.isLast()) {
312 HttpHeaders.setContentLength(httpMessage, content.readableBytes());
313 removeMessage(streamId);
314 return httpMessage;
315 }
316
317 } else if (msg instanceof SpdyRstStreamFrame) {
318
319 SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
320 int streamId = spdyRstStreamFrame.getStreamId();
321 removeMessage(streamId);
322 }
323
324 return null;
325 }
326
327 private static HttpRequest createHttpRequest(int spdyVersion, SpdyHeadersFrame requestFrame)
328 throws Exception {
329
330 HttpMethod method = SpdyHeaders.getMethod(spdyVersion, requestFrame);
331 String url = SpdyHeaders.getUrl(spdyVersion, requestFrame);
332 HttpVersion httpVersion = SpdyHeaders.getVersion(spdyVersion, requestFrame);
333 SpdyHeaders.removeMethod(spdyVersion, requestFrame);
334 SpdyHeaders.removeUrl(spdyVersion, requestFrame);
335 SpdyHeaders.removeVersion(spdyVersion, requestFrame);
336
337 HttpRequest httpRequest = new DefaultHttpRequest(httpVersion, method, url);
338
339
340 SpdyHeaders.removeScheme(spdyVersion, requestFrame);
341
342
343 String host = SpdyHeaders.getHost(requestFrame);
344 SpdyHeaders.removeHost(requestFrame);
345 HttpHeaders.setHost(httpRequest, host);
346
347 for (Map.Entry<String, String> e: requestFrame.headers()) {
348 httpRequest.headers().add(e.getKey(), e.getValue());
349 }
350
351
352 HttpHeaders.setKeepAlive(httpRequest, true);
353
354
355 httpRequest.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);
356
357 return httpRequest;
358 }
359
360 private static HttpResponse createHttpResponse(int spdyVersion, SpdyHeadersFrame responseFrame)
361 throws Exception {
362
363 HttpResponseStatus status = SpdyHeaders.getStatus(spdyVersion, responseFrame);
364 HttpVersion version = SpdyHeaders.getVersion(spdyVersion, responseFrame);
365 SpdyHeaders.removeStatus(spdyVersion, responseFrame);
366 SpdyHeaders.removeVersion(spdyVersion, responseFrame);
367
368 HttpResponse httpResponse = new DefaultHttpResponse(version, status);
369 for (Map.Entry<String, String> e: responseFrame.headers()) {
370 httpResponse.headers().add(e.getKey(), e.getValue());
371 }
372
373
374 HttpHeaders.setKeepAlive(httpResponse, true);
375
376
377 httpResponse.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);
378 httpResponse.headers().remove(HttpHeaders.Names.TRAILER);
379
380 return httpResponse;
381 }
382 }