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 }
117
118 String URL = SpdyHeaders.getUrl(spdyVersion, spdySynStreamFrame);
119
120
121
122 if (URL == null) {
123 SpdyRstStreamFrame spdyRstStreamFrame =
124 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
125 Channels.write(ctx, Channels.future(channel), spdyRstStreamFrame);
126 }
127
128
129
130 if (spdySynStreamFrame.isTruncated()) {
131 SpdyRstStreamFrame spdyRstStreamFrame =
132 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.INTERNAL_ERROR);
133 Channels.write(ctx, Channels.future(channel), spdyRstStreamFrame);
134 }
135
136 try {
137 HttpResponse httpResponse = createHttpResponse(spdyVersion, spdySynStreamFrame);
138
139
140 SpdyHttpHeaders.setStreamId(httpResponse, streamId);
141 SpdyHttpHeaders.setAssociatedToStreamId(httpResponse, associatedToStreamId);
142 SpdyHttpHeaders.setPriority(httpResponse, spdySynStreamFrame.getPriority());
143 SpdyHttpHeaders.setUrl(httpResponse, URL);
144
145 if (spdySynStreamFrame.isLast()) {
146 HttpHeaders.setContentLength(httpResponse, 0);
147 return httpResponse;
148 } else {
149
150 putMessage(streamId, httpResponse);
151 }
152 } catch (Exception e) {
153 SpdyRstStreamFrame spdyRstStreamFrame =
154 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
155 Channels.write(ctx, Channels.future(channel), spdyRstStreamFrame);
156 }
157 } else {
158
159
160
161
162 if (spdySynStreamFrame.isTruncated()) {
163 SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId);
164 spdySynReplyFrame.setLast(true);
165 SpdyHeaders.setStatus(spdyVersion,
166 spdySynReplyFrame,
167 HttpResponseStatus.REQUEST_HEADER_FIELDS_TOO_LARGE);
168 SpdyHeaders.setVersion(spdyVersion, spdySynReplyFrame, HttpVersion.HTTP_1_0);
169 Channels.write(ctx, Channels.future(channel), spdySynReplyFrame);
170 }
171
172 try {
173 HttpRequest httpRequest = createHttpRequest(spdyVersion, spdySynStreamFrame);
174
175
176 SpdyHttpHeaders.setStreamId(httpRequest, streamId);
177
178 if (spdySynStreamFrame.isLast()) {
179 return httpRequest;
180 } else {
181
182 putMessage(streamId, httpRequest);
183 }
184 } catch (Exception e) {
185
186
187
188 SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId);
189 spdySynReplyFrame.setLast(true);
190 SpdyHeaders.setStatus(spdyVersion, spdySynReplyFrame, HttpResponseStatus.BAD_REQUEST);
191 SpdyHeaders.setVersion(spdyVersion, spdySynReplyFrame, HttpVersion.HTTP_1_0);
192 Channels.write(ctx, Channels.future(channel), spdySynReplyFrame);
193 }
194 }
195
196 } else if (msg instanceof SpdySynReplyFrame) {
197
198 SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
199 int streamId = spdySynReplyFrame.getStreamId();
200
201
202
203 if (spdySynReplyFrame.isTruncated()) {
204 SpdyRstStreamFrame spdyRstStreamFrame =
205 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.INTERNAL_ERROR);
206 Channels.write(ctx, Channels.future(channel), spdyRstStreamFrame);
207 }
208
209 try {
210 HttpResponse httpResponse = createHttpResponse(spdyVersion, spdySynReplyFrame);
211
212
213 SpdyHttpHeaders.setStreamId(httpResponse, streamId);
214
215 if (spdySynReplyFrame.isLast()) {
216 HttpHeaders.setContentLength(httpResponse, 0);
217 return httpResponse;
218 } else {
219
220 putMessage(streamId, httpResponse);
221 }
222 } catch (Exception e) {
223
224
225 SpdyRstStreamFrame spdyRstStreamFrame =
226 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
227 Channels.write(ctx, Channels.future(channel), spdyRstStreamFrame);
228 }
229
230 } else if (msg instanceof SpdyHeadersFrame) {
231
232 SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
233 int streamId = spdyHeadersFrame.getStreamId();
234 HttpMessage httpMessage = getMessage(streamId);
235
236
237 if (httpMessage == null) {
238 return null;
239 }
240
241
242 if (!spdyHeadersFrame.isTruncated()) {
243 for (Map.Entry<String, String> e : spdyHeadersFrame.headers()) {
244 httpMessage.headers().add(e.getKey(), e.getValue());
245 }
246 }
247
248 if (spdyHeadersFrame.isLast()) {
249 HttpHeaders.setContentLength(httpMessage, httpMessage.getContent().readableBytes());
250 removeMessage(streamId);
251 return httpMessage;
252 }
253
254 } else if (msg instanceof SpdyDataFrame) {
255
256 SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
257 int streamId = spdyDataFrame.getStreamId();
258 HttpMessage httpMessage = getMessage(streamId);
259
260
261 if (httpMessage == null) {
262 return null;
263 }
264
265 ChannelBuffer content = httpMessage.getContent();
266 if (content.readableBytes() > maxContentLength - spdyDataFrame.getData().readableBytes()) {
267 removeMessage(streamId);
268 throw new TooLongFrameException(
269 "HTTP content length exceeded " + maxContentLength + " bytes.");
270 }
271
272 if (content == ChannelBuffers.EMPTY_BUFFER) {
273 content = ChannelBuffers.dynamicBuffer(channel.getConfig().getBufferFactory());
274 content.writeBytes(spdyDataFrame.getData());
275 httpMessage.setContent(content);
276 } else {
277 content.writeBytes(spdyDataFrame.getData());
278 }
279
280 if (spdyDataFrame.isLast()) {
281 HttpHeaders.setContentLength(httpMessage, content.readableBytes());
282 removeMessage(streamId);
283 return httpMessage;
284 }
285
286 } else if (msg instanceof SpdyRstStreamFrame) {
287
288 SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
289 int streamId = spdyRstStreamFrame.getStreamId();
290 removeMessage(streamId);
291 }
292
293 return null;
294 }
295
296 private static HttpRequest createHttpRequest(int spdyVersion, SpdyHeadersFrame requestFrame)
297 throws Exception {
298
299 HttpMethod method = SpdyHeaders.getMethod(spdyVersion, requestFrame);
300 String url = SpdyHeaders.getUrl(spdyVersion, requestFrame);
301 HttpVersion httpVersion = SpdyHeaders.getVersion(spdyVersion, requestFrame);
302 SpdyHeaders.removeMethod(spdyVersion, requestFrame);
303 SpdyHeaders.removeUrl(spdyVersion, requestFrame);
304 SpdyHeaders.removeVersion(spdyVersion, requestFrame);
305
306 HttpRequest httpRequest = new DefaultHttpRequest(httpVersion, method, url);
307
308
309 SpdyHeaders.removeScheme(spdyVersion, requestFrame);
310
311 if (spdyVersion >= 3) {
312
313 String host = SpdyHeaders.getHost(requestFrame);
314 SpdyHeaders.removeHost(requestFrame);
315 HttpHeaders.setHost(httpRequest, host);
316 }
317
318 for (Map.Entry<String, String> e: requestFrame.headers()) {
319 httpRequest.headers().add(e.getKey(), e.getValue());
320 }
321
322
323 HttpHeaders.setKeepAlive(httpRequest, true);
324
325
326 httpRequest.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);
327
328 return httpRequest;
329 }
330
331 private static HttpResponse createHttpResponse(int spdyVersion, SpdyHeadersFrame responseFrame)
332 throws Exception {
333
334 HttpResponseStatus status = SpdyHeaders.getStatus(spdyVersion, responseFrame);
335 HttpVersion version = SpdyHeaders.getVersion(spdyVersion, responseFrame);
336 SpdyHeaders.removeStatus(spdyVersion, responseFrame);
337 SpdyHeaders.removeVersion(spdyVersion, responseFrame);
338
339 HttpResponse httpResponse = new DefaultHttpResponse(version, status);
340 for (Map.Entry<String, String> e: responseFrame.headers()) {
341 httpResponse.headers().add(e.getKey(), e.getValue());
342 }
343
344
345 HttpHeaders.setKeepAlive(httpResponse, true);
346
347
348 httpResponse.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);
349 httpResponse.headers().remove(HttpHeaders.Names.TRAILER);
350
351 return httpResponse;
352 }
353 }