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 @Deprecated
58 public SpdyHttpDecoder(int maxContentLength) {
59 this(2, maxContentLength);
60 }
61
62
63
64
65
66
67
68
69
70 public SpdyHttpDecoder(int version, int maxContentLength) {
71 this(version, maxContentLength, new HashMap<Integer, HttpMessage>());
72 }
73
74
75
76
77
78
79
80
81
82
83 protected SpdyHttpDecoder(int version, int maxContentLength, Map<Integer, HttpMessage> messageMap) {
84 if (version < SPDY_MIN_VERSION || version > SPDY_MAX_VERSION) {
85 throw new IllegalArgumentException(
86 "unsupported version: " + version);
87 }
88 if (maxContentLength <= 0) {
89 throw new IllegalArgumentException(
90 "maxContentLength must be a positive integer: " + maxContentLength);
91 }
92 spdyVersion = version;
93 this.maxContentLength = maxContentLength;
94 this.messageMap = messageMap;
95 }
96
97 protected HttpMessage putMessage(int streamId, HttpMessage message) {
98 return messageMap.put(streamId, message);
99 }
100
101 protected HttpMessage getMessage(int streamId) {
102 return messageMap.get(streamId);
103 }
104
105 protected HttpMessage removeMessage(int streamId) {
106 return messageMap.remove(streamId);
107 }
108
109 @Override
110 protected Object decode(ChannelHandlerContext ctx, Channel channel, Object msg)
111 throws Exception {
112
113 if (msg instanceof SpdySynStreamFrame) {
114
115
116 SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
117 int streamId = spdySynStreamFrame.getStreamId();
118
119 if (isServerId(streamId)) {
120
121 int associatedToStreamId = spdySynStreamFrame.getAssociatedToStreamId();
122
123
124
125 if (associatedToStreamId == 0) {
126 SpdyRstStreamFrame spdyRstStreamFrame =
127 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.INVALID_STREAM);
128 Channels.write(ctx, Channels.future(channel), spdyRstStreamFrame);
129 }
130
131 String URL = SpdyHeaders.getUrl(spdyVersion, spdySynStreamFrame);
132
133
134
135 if (URL == null) {
136 SpdyRstStreamFrame spdyRstStreamFrame =
137 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
138 Channels.write(ctx, Channels.future(channel), spdyRstStreamFrame);
139 }
140
141 try {
142 HttpResponse httpResponse = createHttpResponse(spdyVersion, spdySynStreamFrame);
143
144
145 SpdyHttpHeaders.setStreamId(httpResponse, streamId);
146 SpdyHttpHeaders.setAssociatedToStreamId(httpResponse, associatedToStreamId);
147 SpdyHttpHeaders.setPriority(httpResponse, spdySynStreamFrame.getPriority());
148 SpdyHttpHeaders.setUrl(httpResponse, URL);
149
150 if (spdySynStreamFrame.isLast()) {
151 HttpHeaders.setContentLength(httpResponse, 0);
152 return httpResponse;
153 } else {
154
155 putMessage(streamId, httpResponse);
156 }
157 } catch (Exception e) {
158 SpdyRstStreamFrame spdyRstStreamFrame =
159 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
160 Channels.write(ctx, Channels.future(channel), spdyRstStreamFrame);
161 }
162 } else {
163
164 try {
165 HttpRequest httpRequest = createHttpRequest(spdyVersion, spdySynStreamFrame);
166
167
168 SpdyHttpHeaders.setStreamId(httpRequest, streamId);
169
170 if (spdySynStreamFrame.isLast()) {
171 return httpRequest;
172 } else {
173
174 putMessage(streamId, httpRequest);
175 }
176 } catch (Exception e) {
177
178
179
180 SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId);
181 spdySynReplyFrame.setLast(true);
182 SpdyHeaders.setStatus(spdyVersion, spdySynReplyFrame, HttpResponseStatus.BAD_REQUEST);
183 SpdyHeaders.setVersion(spdyVersion, spdySynReplyFrame, HttpVersion.HTTP_1_0);
184 Channels.write(ctx, Channels.future(channel), spdySynReplyFrame);
185 }
186 }
187
188 } else if (msg instanceof SpdySynReplyFrame) {
189
190 SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
191 int streamId = spdySynReplyFrame.getStreamId();
192
193 try {
194 HttpResponse httpResponse = createHttpResponse(spdyVersion, spdySynReplyFrame);
195
196
197 SpdyHttpHeaders.setStreamId(httpResponse, streamId);
198
199 if (spdySynReplyFrame.isLast()) {
200 HttpHeaders.setContentLength(httpResponse, 0);
201 return httpResponse;
202 } else {
203
204 putMessage(streamId, httpResponse);
205 }
206 } catch (Exception e) {
207
208
209 SpdyRstStreamFrame spdyRstStreamFrame =
210 new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
211 Channels.write(ctx, Channels.future(channel), spdyRstStreamFrame);
212 }
213
214 } else if (msg instanceof SpdyHeadersFrame) {
215
216 SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
217 int streamId = spdyHeadersFrame.getStreamId();
218 HttpMessage httpMessage = getMessage(streamId);
219
220
221 if (httpMessage == null) {
222 return null;
223 }
224
225 for (Map.Entry<String, String> e: spdyHeadersFrame.getHeaders()) {
226 httpMessage.addHeader(e.getKey(), e.getValue());
227 }
228
229 if (spdyHeadersFrame.isLast()) {
230 HttpHeaders.setContentLength(httpMessage, httpMessage.getContent().readableBytes());
231 removeMessage(streamId);
232 return httpMessage;
233 }
234
235 } else if (msg instanceof SpdyDataFrame) {
236
237 SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
238 int streamId = spdyDataFrame.getStreamId();
239 HttpMessage httpMessage = getMessage(streamId);
240
241
242 if (httpMessage == null) {
243 return null;
244 }
245
246 ChannelBuffer content = httpMessage.getContent();
247 if (content.readableBytes() > maxContentLength - spdyDataFrame.getData().readableBytes()) {
248 removeMessage(streamId);
249 throw new TooLongFrameException(
250 "HTTP content length exceeded " + maxContentLength + " bytes.");
251 }
252
253 if (content == ChannelBuffers.EMPTY_BUFFER) {
254 content = ChannelBuffers.dynamicBuffer(channel.getConfig().getBufferFactory());
255 content.writeBytes(spdyDataFrame.getData());
256 httpMessage.setContent(content);
257 } else {
258 content.writeBytes(spdyDataFrame.getData());
259 }
260
261 if (spdyDataFrame.isLast()) {
262 HttpHeaders.setContentLength(httpMessage, content.readableBytes());
263 removeMessage(streamId);
264 return httpMessage;
265 }
266
267 } else if (msg instanceof SpdyRstStreamFrame) {
268
269 SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
270 int streamId = spdyRstStreamFrame.getStreamId();
271 removeMessage(streamId);
272 }
273
274 return null;
275 }
276
277 private static HttpRequest createHttpRequest(int spdyVersion, SpdyHeaderBlock requestFrame)
278 throws Exception {
279
280 HttpMethod method = SpdyHeaders.getMethod(spdyVersion, requestFrame);
281 String url = SpdyHeaders.getUrl(spdyVersion, requestFrame);
282 HttpVersion httpVersion = SpdyHeaders.getVersion(spdyVersion, requestFrame);
283 SpdyHeaders.removeMethod(spdyVersion, requestFrame);
284 SpdyHeaders.removeUrl(spdyVersion, requestFrame);
285 SpdyHeaders.removeVersion(spdyVersion, requestFrame);
286
287 HttpRequest httpRequest = new DefaultHttpRequest(httpVersion, method, url);
288
289
290 SpdyHeaders.removeScheme(spdyVersion, requestFrame);
291
292 if (spdyVersion >= 3) {
293
294 String host = SpdyHeaders.getHost(requestFrame);
295 SpdyHeaders.removeHost(requestFrame);
296 HttpHeaders.setHost(httpRequest, host);
297 }
298
299 for (Map.Entry<String, String> e: requestFrame.getHeaders()) {
300 httpRequest.addHeader(e.getKey(), e.getValue());
301 }
302
303
304 HttpHeaders.setKeepAlive(httpRequest, true);
305
306
307 httpRequest.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
308
309 return httpRequest;
310 }
311
312 private static HttpResponse createHttpResponse(int spdyVersion, SpdyHeaderBlock responseFrame)
313 throws Exception {
314
315 HttpResponseStatus status = SpdyHeaders.getStatus(spdyVersion, responseFrame);
316 HttpVersion version = SpdyHeaders.getVersion(spdyVersion, responseFrame);
317 SpdyHeaders.removeStatus(spdyVersion, responseFrame);
318 SpdyHeaders.removeVersion(spdyVersion, responseFrame);
319
320 HttpResponse httpResponse = new DefaultHttpResponse(version, status);
321 for (Map.Entry<String, String> e: responseFrame.getHeaders()) {
322 httpResponse.addHeader(e.getKey(), e.getValue());
323 }
324
325
326 HttpHeaders.setKeepAlive(httpResponse, true);
327
328
329 httpResponse.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
330 httpResponse.removeHeader(HttpHeaders.Names.TRAILER);
331
332 return httpResponse;
333 }
334 }