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