1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.spdy;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.channel.ChannelFuture;
20 import io.netty.channel.ChannelFutureListener;
21 import io.netty.channel.ChannelHandler;
22 import io.netty.channel.ChannelHandlerContext;
23 import io.netty.channel.ChannelOutboundHandler;
24 import io.netty.channel.ChannelPromise;
25 import io.netty.handler.codec.ByteToMessageDecoder;
26 import io.netty.handler.codec.UnsupportedMessageTypeException;
27
28 import java.net.SocketAddress;
29 import java.util.List;
30
31
32
33
34 public class SpdyFrameCodec extends ByteToMessageDecoder
35 implements SpdyFrameDecoderDelegate, ChannelOutboundHandler {
36
37 private static final SpdyProtocolException INVALID_FRAME =
38 new SpdyProtocolException("Received invalid frame");
39
40 private final SpdyFrameDecoder spdyFrameDecoder;
41 private final SpdyFrameEncoder spdyFrameEncoder;
42 private final SpdyHeaderBlockDecoder spdyHeaderBlockDecoder;
43 private final SpdyHeaderBlockEncoder spdyHeaderBlockEncoder;
44
45 private SpdyHeadersFrame spdyHeadersFrame;
46 private SpdySettingsFrame spdySettingsFrame;
47
48 private ChannelHandlerContext ctx;
49 private boolean read;
50 private final boolean validateHeaders;
51
52
53
54
55
56
57
58
59
60 public SpdyFrameCodec(SpdyVersion version) {
61 this(version, true);
62 }
63
64
65
66
67
68
69
70
71
72 public SpdyFrameCodec(SpdyVersion version, boolean validateHeaders) {
73 this(version, 8192, 16384, 6, 15, 8, validateHeaders);
74 }
75
76
77
78
79
80 public SpdyFrameCodec(
81 SpdyVersion version, int maxChunkSize, int maxHeaderSize,
82 int compressionLevel, int windowBits, int memLevel) {
83 this(version, maxChunkSize, maxHeaderSize, compressionLevel, windowBits, memLevel, true);
84 }
85
86
87
88
89
90 public SpdyFrameCodec(
91 SpdyVersion version, int maxChunkSize, int maxHeaderSize,
92 int compressionLevel, int windowBits, int memLevel, boolean validateHeaders) {
93 this(version, maxChunkSize,
94 SpdyHeaderBlockDecoder.newInstance(version, maxHeaderSize),
95 SpdyHeaderBlockEncoder.newInstance(version, compressionLevel, windowBits, memLevel), validateHeaders);
96 }
97
98 protected SpdyFrameCodec(SpdyVersion version, int maxChunkSize,
99 SpdyHeaderBlockDecoder spdyHeaderBlockDecoder, SpdyHeaderBlockEncoder spdyHeaderBlockEncoder,
100 boolean validateHeaders) {
101 spdyFrameDecoder = new SpdyFrameDecoder(version, this, maxChunkSize);
102 spdyFrameEncoder = new SpdyFrameEncoder(version);
103 this.spdyHeaderBlockDecoder = spdyHeaderBlockDecoder;
104 this.spdyHeaderBlockEncoder = spdyHeaderBlockEncoder;
105 this.validateHeaders = validateHeaders;
106 }
107
108 @Override
109 public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
110 super.handlerAdded(ctx);
111 this.ctx = ctx;
112 ctx.channel().closeFuture().addListener(new ChannelFutureListener() {
113 @Override
114 public void operationComplete(ChannelFuture future) throws Exception {
115 spdyHeaderBlockDecoder.end();
116 spdyHeaderBlockEncoder.end();
117 }
118 });
119 }
120
121 @Override
122 protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
123 spdyFrameDecoder.decode(in);
124 }
125
126 @Override
127 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
128 if (!read) {
129 if (!ctx.channel().config().isAutoRead()) {
130 ctx.read();
131 }
132 }
133 read = false;
134 super.channelReadComplete(ctx);
135 }
136
137 @Override
138 public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
139 ctx.bind(localAddress, promise);
140 }
141
142 @Override
143 public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
144 ChannelPromise promise) throws Exception {
145 ctx.connect(remoteAddress, localAddress, promise);
146 }
147
148 @Override
149 public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
150 ctx.disconnect(promise);
151 }
152
153 @Override
154 public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
155 ctx.close(promise);
156 }
157
158 @Override
159 public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
160 ctx.deregister(promise);
161 }
162
163 @Override
164 public void read(ChannelHandlerContext ctx) throws Exception {
165 ctx.read();
166 }
167
168 @Override
169 public void flush(ChannelHandlerContext ctx) throws Exception {
170 ctx.flush();
171 }
172
173 @Override
174 public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
175 ByteBuf frame;
176
177 if (msg instanceof SpdyDataFrame) {
178
179 SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
180 frame = spdyFrameEncoder.encodeDataFrame(
181 ctx.alloc(),
182 spdyDataFrame.streamId(),
183 spdyDataFrame.isLast(),
184 spdyDataFrame.content()
185 );
186 spdyDataFrame.release();
187 ctx.write(frame, promise);
188
189 } else if (msg instanceof SpdySynStreamFrame) {
190
191 SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
192 ByteBuf headerBlock = spdyHeaderBlockEncoder.encode(ctx.alloc(), spdySynStreamFrame);
193 try {
194 frame = spdyFrameEncoder.encodeSynStreamFrame(
195 ctx.alloc(),
196 spdySynStreamFrame.streamId(),
197 spdySynStreamFrame.associatedStreamId(),
198 spdySynStreamFrame.priority(),
199 spdySynStreamFrame.isLast(),
200 spdySynStreamFrame.isUnidirectional(),
201 headerBlock
202 );
203 } finally {
204 headerBlock.release();
205 }
206 ctx.write(frame, promise);
207
208 } else if (msg instanceof SpdySynReplyFrame) {
209
210 SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
211 ByteBuf headerBlock = spdyHeaderBlockEncoder.encode(ctx.alloc(), spdySynReplyFrame);
212 try {
213 frame = spdyFrameEncoder.encodeSynReplyFrame(
214 ctx.alloc(),
215 spdySynReplyFrame.streamId(),
216 spdySynReplyFrame.isLast(),
217 headerBlock
218 );
219 } finally {
220 headerBlock.release();
221 }
222 ctx.write(frame, promise);
223
224 } else if (msg instanceof SpdyRstStreamFrame) {
225
226 SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
227 frame = spdyFrameEncoder.encodeRstStreamFrame(
228 ctx.alloc(),
229 spdyRstStreamFrame.streamId(),
230 spdyRstStreamFrame.status().code()
231 );
232 ctx.write(frame, promise);
233
234 } else if (msg instanceof SpdySettingsFrame) {
235
236 SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;
237 frame = spdyFrameEncoder.encodeSettingsFrame(
238 ctx.alloc(),
239 spdySettingsFrame
240 );
241 ctx.write(frame, promise);
242
243 } else if (msg instanceof SpdyPingFrame) {
244
245 SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg;
246 frame = spdyFrameEncoder.encodePingFrame(
247 ctx.alloc(),
248 spdyPingFrame.id()
249 );
250 ctx.write(frame, promise);
251
252 } else if (msg instanceof SpdyGoAwayFrame) {
253
254 SpdyGoAwayFrame spdyGoAwayFrame = (SpdyGoAwayFrame) msg;
255 frame = spdyFrameEncoder.encodeGoAwayFrame(
256 ctx.alloc(),
257 spdyGoAwayFrame.lastGoodStreamId(),
258 spdyGoAwayFrame.status().code()
259 );
260 ctx.write(frame, promise);
261
262 } else if (msg instanceof SpdyHeadersFrame) {
263
264 SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
265 ByteBuf headerBlock = spdyHeaderBlockEncoder.encode(ctx.alloc(), spdyHeadersFrame);
266 try {
267 frame = spdyFrameEncoder.encodeHeadersFrame(
268 ctx.alloc(),
269 spdyHeadersFrame.streamId(),
270 spdyHeadersFrame.isLast(),
271 headerBlock
272 );
273 } finally {
274 headerBlock.release();
275 }
276 ctx.write(frame, promise);
277
278 } else if (msg instanceof SpdyWindowUpdateFrame) {
279
280 SpdyWindowUpdateFrame spdyWindowUpdateFrame = (SpdyWindowUpdateFrame) msg;
281 frame = spdyFrameEncoder.encodeWindowUpdateFrame(
282 ctx.alloc(),
283 spdyWindowUpdateFrame.streamId(),
284 spdyWindowUpdateFrame.deltaWindowSize()
285 );
286 ctx.write(frame, promise);
287 } else {
288 throw new UnsupportedMessageTypeException(msg);
289 }
290 }
291
292 @Override
293 public void readDataFrame(int streamId, boolean last, ByteBuf data) {
294 read = true;
295
296 SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(streamId, data);
297 spdyDataFrame.setLast(last);
298 ctx.fireChannelRead(spdyDataFrame);
299 }
300
301 @Override
302 public void readSynStreamFrame(
303 int streamId, int associatedToStreamId, byte priority, boolean last, boolean unidirectional) {
304 SpdySynStreamFrame spdySynStreamFrame =
305 new DefaultSpdySynStreamFrame(streamId, associatedToStreamId, priority, validateHeaders);
306 spdySynStreamFrame.setLast(last);
307 spdySynStreamFrame.setUnidirectional(unidirectional);
308 spdyHeadersFrame = spdySynStreamFrame;
309 }
310
311 @Override
312 public void readSynReplyFrame(int streamId, boolean last) {
313 SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId, validateHeaders);
314 spdySynReplyFrame.setLast(last);
315 spdyHeadersFrame = spdySynReplyFrame;
316 }
317
318 @Override
319 public void readRstStreamFrame(int streamId, int statusCode) {
320 read = true;
321
322 SpdyRstStreamFrame spdyRstStreamFrame = new DefaultSpdyRstStreamFrame(streamId, statusCode);
323 ctx.fireChannelRead(spdyRstStreamFrame);
324 }
325
326 @Override
327 public void readSettingsFrame(boolean clearPersisted) {
328 read = true;
329
330 spdySettingsFrame = new DefaultSpdySettingsFrame();
331 spdySettingsFrame.setClearPreviouslyPersistedSettings(clearPersisted);
332 }
333
334 @Override
335 public void readSetting(int id, int value, boolean persistValue, boolean persisted) {
336 spdySettingsFrame.setValue(id, value, persistValue, persisted);
337 }
338
339 @Override
340 public void readSettingsEnd() {
341 read = true;
342
343 Object frame = spdySettingsFrame;
344 spdySettingsFrame = null;
345 ctx.fireChannelRead(frame);
346 }
347
348 @Override
349 public void readPingFrame(int id) {
350 read = true;
351
352 SpdyPingFrame spdyPingFrame = new DefaultSpdyPingFrame(id);
353 ctx.fireChannelRead(spdyPingFrame);
354 }
355
356 @Override
357 public void readGoAwayFrame(int lastGoodStreamId, int statusCode) {
358 read = true;
359
360 SpdyGoAwayFrame spdyGoAwayFrame = new DefaultSpdyGoAwayFrame(lastGoodStreamId, statusCode);
361 ctx.fireChannelRead(spdyGoAwayFrame);
362 }
363
364 @Override
365 public void readHeadersFrame(int streamId, boolean last) {
366 spdyHeadersFrame = new DefaultSpdyHeadersFrame(streamId, validateHeaders);
367 spdyHeadersFrame.setLast(last);
368 }
369
370 @Override
371 public void readWindowUpdateFrame(int streamId, int deltaWindowSize) {
372 read = true;
373
374 SpdyWindowUpdateFrame spdyWindowUpdateFrame = new DefaultSpdyWindowUpdateFrame(streamId, deltaWindowSize);
375 ctx.fireChannelRead(spdyWindowUpdateFrame);
376 }
377
378 @Override
379 public void readHeaderBlock(ByteBuf headerBlock) {
380 try {
381 spdyHeaderBlockDecoder.decode(ctx.alloc(), headerBlock, spdyHeadersFrame);
382 } catch (Exception e) {
383 ctx.fireExceptionCaught(e);
384 } finally {
385 headerBlock.release();
386 }
387 }
388
389 @Override
390 public void readHeaderBlockEnd() {
391 Object frame = null;
392 try {
393 spdyHeaderBlockDecoder.endHeaderBlock(spdyHeadersFrame);
394 frame = spdyHeadersFrame;
395 spdyHeadersFrame = null;
396 } catch (Exception e) {
397 ctx.fireExceptionCaught(e);
398 }
399 if (frame != null) {
400 read = true;
401
402 ctx.fireChannelRead(frame);
403 }
404 }
405
406 @Override
407 public void readFrameError(String message) {
408 ctx.fireExceptionCaught(INVALID_FRAME);
409 }
410 }