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 org.jboss.netty.channel.ChannelDownstreamHandler;
19 import org.jboss.netty.channel.ChannelEvent;
20 import org.jboss.netty.channel.ChannelFuture;
21 import org.jboss.netty.channel.ChannelFutureListener;
22 import org.jboss.netty.channel.ChannelHandlerContext;
23 import org.jboss.netty.channel.Channels;
24 import org.jboss.netty.channel.MessageEvent;
25 import org.jboss.netty.handler.codec.http.HttpChunk;
26 import org.jboss.netty.handler.codec.http.HttpChunkTrailer;
27 import org.jboss.netty.handler.codec.http.HttpHeaders;
28 import org.jboss.netty.handler.codec.http.HttpMessage;
29 import org.jboss.netty.handler.codec.http.HttpRequest;
30 import org.jboss.netty.handler.codec.http.HttpResponse;
31
32 import java.util.List;
33 import java.util.Map;
34
35 import static org.jboss.netty.handler.codec.spdy.SpdyCodecUtil.*;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126 public class SpdyHttpEncoder implements ChannelDownstreamHandler {
127
128 private final int spdyVersion;
129 private volatile int currentStreamID;
130
131
132
133
134 @Deprecated
135 public SpdyHttpEncoder() {
136 this(2);
137 }
138
139
140
141
142
143
144 public SpdyHttpEncoder(int version) {
145 if (version < SPDY_MIN_VERSION || version > SPDY_MAX_VERSION) {
146 throw new IllegalArgumentException(
147 "unsupported version: " + version);
148 }
149 spdyVersion = version;
150 }
151
152
153 public void handleDownstream(ChannelHandlerContext ctx, ChannelEvent evt)
154 throws Exception {
155 if (!(evt instanceof MessageEvent)) {
156 ctx.sendDownstream(evt);
157 return;
158 }
159
160 MessageEvent e = (MessageEvent) evt;
161 Object msg = e.getMessage();
162
163 if (msg instanceof HttpRequest) {
164
165 HttpRequest httpRequest = (HttpRequest) msg;
166 SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpRequest);
167 int streamID = spdySynStreamFrame.getStreamId();
168 ChannelFuture future = getContentFuture(ctx, e, streamID, httpRequest);
169 Channels.write(ctx, future, spdySynStreamFrame, e.getRemoteAddress());
170
171 } else if (msg instanceof HttpResponse) {
172
173 HttpResponse httpResponse = (HttpResponse) msg;
174 if (httpResponse.containsHeader(SpdyHttpHeaders.Names.ASSOCIATED_TO_STREAM_ID)) {
175 SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpResponse);
176 int streamID = spdySynStreamFrame.getStreamId();
177 ChannelFuture future = getContentFuture(ctx, e, streamID, httpResponse);
178 Channels.write(ctx, future, spdySynStreamFrame, e.getRemoteAddress());
179 } else {
180 SpdySynReplyFrame spdySynReplyFrame = createSynReplyFrame(httpResponse);
181 int streamID = spdySynReplyFrame.getStreamId();
182 ChannelFuture future = getContentFuture(ctx, e, streamID, httpResponse);
183 Channels.write(ctx, future, spdySynReplyFrame, e.getRemoteAddress());
184 }
185
186 } else if (msg instanceof HttpChunk) {
187
188 HttpChunk chunk = (HttpChunk) msg;
189 SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(currentStreamID);
190 spdyDataFrame.setData(chunk.getContent());
191 spdyDataFrame.setLast(chunk.isLast());
192
193 if (chunk instanceof HttpChunkTrailer) {
194 HttpChunkTrailer trailer = (HttpChunkTrailer) chunk;
195 List<Map.Entry<String, String>> trailers = trailer.getHeaders();
196 if (trailers.isEmpty()) {
197 Channels.write(ctx, e.getFuture(), spdyDataFrame, e.getRemoteAddress());
198 } else {
199
200 SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(currentStreamID);
201 for (Map.Entry<String, String> entry: trailers) {
202 spdyHeadersFrame.addHeader(entry.getKey(), entry.getValue());
203 }
204
205
206 ChannelFuture future = Channels.future(e.getChannel());
207 future.addListener(new SpdyFrameWriter(ctx, e, spdyDataFrame));
208 Channels.write(ctx, future, spdyHeadersFrame, e.getRemoteAddress());
209 }
210 } else {
211 Channels.write(ctx, e.getFuture(), spdyDataFrame, e.getRemoteAddress());
212 }
213 } else {
214
215 ctx.sendDownstream(evt);
216 }
217 }
218
219 private ChannelFuture getContentFuture(
220 ChannelHandlerContext ctx, MessageEvent e, int streamID, HttpMessage httpMessage) {
221 if (httpMessage.getContent().readableBytes() == 0) {
222 return e.getFuture();
223 }
224
225
226 SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(streamID);
227 spdyDataFrame.setData(httpMessage.getContent());
228 spdyDataFrame.setLast(true);
229
230
231 ChannelFuture future = Channels.future(e.getChannel());
232 future.addListener(new SpdyFrameWriter(ctx, e, spdyDataFrame));
233
234 return future;
235 }
236
237 private static class SpdyFrameWriter implements ChannelFutureListener {
238
239 private final ChannelHandlerContext ctx;
240 private final MessageEvent e;
241 private final Object spdyFrame;
242
243 SpdyFrameWriter(ChannelHandlerContext ctx, MessageEvent e, Object spdyFrame) {
244 this.ctx = ctx;
245 this.e = e;
246 this.spdyFrame = spdyFrame;
247 }
248
249 public void operationComplete(ChannelFuture future) throws Exception {
250 if (future.isSuccess()) {
251 Channels.write(ctx, e.getFuture(), spdyFrame, e.getRemoteAddress());
252 } else if (future.isCancelled()) {
253 e.getFuture().cancel();
254 } else {
255 e.getFuture().setFailure(future.getCause());
256 }
257 }
258 }
259
260 private SpdySynStreamFrame createSynStreamFrame(HttpMessage httpMessage)
261 throws Exception {
262 boolean chunked = httpMessage.isChunked();
263
264
265 int streamID = SpdyHttpHeaders.getStreamId(httpMessage);
266 int associatedToStreamID = SpdyHttpHeaders.getAssociatedToStreamId(httpMessage);
267 byte priority = SpdyHttpHeaders.getPriority(httpMessage);
268 String URL = SpdyHttpHeaders.getUrl(httpMessage);
269 String scheme = SpdyHttpHeaders.getScheme(httpMessage);
270 SpdyHttpHeaders.removeStreamId(httpMessage);
271 SpdyHttpHeaders.removeAssociatedToStreamId(httpMessage);
272 SpdyHttpHeaders.removePriority(httpMessage);
273 SpdyHttpHeaders.removeUrl(httpMessage);
274 SpdyHttpHeaders.removeScheme(httpMessage);
275
276
277
278 httpMessage.removeHeader(HttpHeaders.Names.CONNECTION);
279 httpMessage.removeHeader("Keep-Alive");
280 httpMessage.removeHeader("Proxy-Connection");
281 httpMessage.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
282
283 SpdySynStreamFrame spdySynStreamFrame =
284 new DefaultSpdySynStreamFrame(streamID, associatedToStreamID, priority);
285
286
287 if (httpMessage instanceof HttpRequest) {
288 HttpRequest httpRequest = (HttpRequest) httpMessage;
289 SpdyHeaders.setMethod(spdyVersion, spdySynStreamFrame, httpRequest.getMethod());
290 SpdyHeaders.setUrl(spdyVersion, spdySynStreamFrame, httpRequest.getUri());
291 SpdyHeaders.setVersion(spdyVersion, spdySynStreamFrame, httpMessage.getProtocolVersion());
292 }
293 if (httpMessage instanceof HttpResponse) {
294 HttpResponse httpResponse = (HttpResponse) httpMessage;
295 SpdyHeaders.setStatus(spdyVersion, spdySynStreamFrame, httpResponse.getStatus());
296 SpdyHeaders.setUrl(spdyVersion, spdySynStreamFrame, URL);
297 SpdyHeaders.setVersion(spdyVersion, spdySynStreamFrame, httpMessage.getProtocolVersion());
298 spdySynStreamFrame.setUnidirectional(true);
299 }
300
301
302 if (spdyVersion >= 3) {
303 String host = HttpHeaders.getHost(httpMessage);
304 httpMessage.removeHeader(HttpHeaders.Names.HOST);
305 SpdyHeaders.setHost(spdySynStreamFrame, host);
306 }
307
308
309 if (scheme == null) {
310 scheme = "https";
311 }
312 SpdyHeaders.setScheme(spdyVersion, spdySynStreamFrame, scheme);
313
314
315 for (Map.Entry<String, String> entry: httpMessage.getHeaders()) {
316 spdySynStreamFrame.addHeader(entry.getKey(), entry.getValue());
317 }
318
319 if (chunked) {
320 currentStreamID = streamID;
321 spdySynStreamFrame.setLast(false);
322 } else {
323 spdySynStreamFrame.setLast(httpMessage.getContent().readableBytes() == 0);
324 }
325
326 return spdySynStreamFrame;
327 }
328
329 private SpdySynReplyFrame createSynReplyFrame(HttpResponse httpResponse)
330 throws Exception {
331 boolean chunked = httpResponse.isChunked();
332
333
334 int streamID = SpdyHttpHeaders.getStreamId(httpResponse);
335 SpdyHttpHeaders.removeStreamId(httpResponse);
336
337
338
339 httpResponse.removeHeader(HttpHeaders.Names.CONNECTION);
340 httpResponse.removeHeader("Keep-Alive");
341 httpResponse.removeHeader("Proxy-Connection");
342 httpResponse.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
343
344 SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID);
345
346
347 SpdyHeaders.setStatus(spdyVersion, spdySynReplyFrame, httpResponse.getStatus());
348 SpdyHeaders.setVersion(spdyVersion, spdySynReplyFrame, httpResponse.getProtocolVersion());
349
350
351 for (Map.Entry<String, String> entry: httpResponse.getHeaders()) {
352 spdySynReplyFrame.addHeader(entry.getKey(), entry.getValue());
353 }
354
355 if (chunked) {
356 currentStreamID = streamID;
357 spdySynReplyFrame.setLast(false);
358 } else {
359 spdySynReplyFrame.setLast(httpResponse.getContent().readableBytes() == 0);
360 }
361
362 return spdySynReplyFrame;
363 }
364 }