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