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