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