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
137 } else {
138
139 try {
140 HttpRequest httpRequest = createHttpRequest(spdyVersion, spdySynStreamFrame);
141
142
143 SpdyHttpHeaders.setStreamId(httpRequest, streamID);
144
145 if (spdySynStreamFrame.isLast()) {
146 return httpRequest;
147 } else {
148
149 messageMap.put(streamID, httpRequest);
150 }
151 } catch (Exception e) {
152
153
154
155 SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID);
156 spdySynReplyFrame.setLast(true);
157 SpdyHeaders.setStatus(spdyVersion, spdySynReplyFrame, HttpResponseStatus.BAD_REQUEST);
158 SpdyHeaders.setVersion(spdyVersion, spdySynReplyFrame, HttpVersion.HTTP_1_0);
159 Channels.write(ctx, Channels.future(channel), spdySynReplyFrame);
160 }
161 }
162
163 } else if (msg instanceof SpdySynReplyFrame) {
164
165 SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
166 int streamID = spdySynReplyFrame.getStreamId();
167
168 try {
169 HttpResponse httpResponse = createHttpResponse(spdyVersion, spdySynReplyFrame);
170
171
172 SpdyHttpHeaders.setStreamId(httpResponse, streamID);
173
174 if (spdySynReplyFrame.isLast()) {
175 HttpHeaders.setContentLength(httpResponse, 0);
176 return httpResponse;
177 } else {
178
179 messageMap.put(streamID, httpResponse);
180 }
181 } catch (Exception e) {
182
183
184 SpdyRstStreamFrame spdyRstStreamFrame =
185 new DefaultSpdyRstStreamFrame(streamID, SpdyStreamStatus.PROTOCOL_ERROR);
186 Channels.write(ctx, Channels.future(channel), spdyRstStreamFrame);
187 }
188
189 } else if (msg instanceof SpdyHeadersFrame) {
190
191 SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
192 Integer streamID = spdyHeadersFrame.getStreamId();
193 HttpMessage httpMessage = messageMap.get(streamID);
194
195
196
197 if (httpMessage == null) {
198 return null;
199 }
200
201 for (Map.Entry<String, String> e: spdyHeadersFrame.getHeaders()) {
202 httpMessage.addHeader(e.getKey(), e.getValue());
203 }
204
205 } else if (msg instanceof SpdyDataFrame) {
206
207 SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
208 Integer streamID = spdyDataFrame.getStreamId();
209 HttpMessage httpMessage = messageMap.get(streamID);
210
211
212
213 if (httpMessage == null) {
214 return null;
215 }
216
217 ChannelBuffer content = httpMessage.getContent();
218 if (content.readableBytes() > maxContentLength - spdyDataFrame.getData().readableBytes()) {
219 messageMap.remove(streamID);
220 throw new TooLongFrameException(
221 "HTTP content length exceeded " + maxContentLength + " bytes.");
222 }
223
224 if (content == ChannelBuffers.EMPTY_BUFFER) {
225 content = ChannelBuffers.dynamicBuffer(channel.getConfig().getBufferFactory());
226 content.writeBytes(spdyDataFrame.getData());
227 httpMessage.setContent(content);
228 } else {
229 content.writeBytes(spdyDataFrame.getData());
230 }
231
232 if (spdyDataFrame.isLast()) {
233 HttpHeaders.setContentLength(httpMessage, content.readableBytes());
234 messageMap.remove(streamID);
235 return httpMessage;
236 }
237
238 } else if (msg instanceof SpdyRstStreamFrame) {
239
240 SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
241 Integer streamID = spdyRstStreamFrame.getStreamId();
242 messageMap.remove(streamID);
243 }
244
245 return null;
246 }
247
248 private static HttpRequest createHttpRequest(int spdyVersion, SpdyHeaderBlock requestFrame)
249 throws Exception {
250
251 HttpMethod method = SpdyHeaders.getMethod(spdyVersion, requestFrame);
252 String url = SpdyHeaders.getUrl(spdyVersion, requestFrame);
253 HttpVersion httpVersion = SpdyHeaders.getVersion(spdyVersion, requestFrame);
254 SpdyHeaders.removeMethod(spdyVersion, requestFrame);
255 SpdyHeaders.removeUrl(spdyVersion, requestFrame);
256 SpdyHeaders.removeVersion(spdyVersion, requestFrame);
257
258 HttpRequest httpRequest = new DefaultHttpRequest(httpVersion, method, url);
259
260
261 SpdyHeaders.removeScheme(spdyVersion, requestFrame);
262
263 if (spdyVersion >= 3) {
264
265 String host = SpdyHeaders.getHost(requestFrame);
266 SpdyHeaders.removeHost(requestFrame);
267 HttpHeaders.setHost(httpRequest, host);
268 }
269
270 for (Map.Entry<String, String> e: requestFrame.getHeaders()) {
271 httpRequest.addHeader(e.getKey(), e.getValue());
272 }
273
274
275 HttpHeaders.setKeepAlive(httpRequest, true);
276
277
278 httpRequest.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
279
280 return httpRequest;
281 }
282
283 private static HttpResponse createHttpResponse(int spdyVersion, SpdyHeaderBlock responseFrame)
284 throws Exception {
285
286 HttpResponseStatus status = SpdyHeaders.getStatus(spdyVersion, responseFrame);
287 HttpVersion version = SpdyHeaders.getVersion(spdyVersion, responseFrame);
288 SpdyHeaders.removeStatus(spdyVersion, responseFrame);
289 SpdyHeaders.removeVersion(spdyVersion, responseFrame);
290
291 HttpResponse httpResponse = new DefaultHttpResponse(version, status);
292 for (Map.Entry<String, String> e: responseFrame.getHeaders()) {
293 httpResponse.addHeader(e.getKey(), e.getValue());
294 }
295
296
297 HttpHeaders.setKeepAlive(httpResponse, true);
298
299
300 httpResponse.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
301 httpResponse.removeHeader(HttpHeaders.Names.TRAILER);
302
303 return httpResponse;
304 }
305 }