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 protected static final SpdyProtocolException INVALID_FRAME =
38 new SpdyProtocolException("Received invalid frame");
39
40 protected final SpdyFrameDecoder spdyFrameDecoder;
41 protected 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 private final boolean supportsUnknownFrames;
52
53
54
55
56
57
58
59
60
61 public SpdyFrameCodec(SpdyVersion version) {
62 this(version, true);
63 }
64
65
66
67
68
69
70
71
72
73 public SpdyFrameCodec(SpdyVersion version, boolean validateHeaders) {
74 this(version, 8192, 16384, 6, 15, 8, validateHeaders);
75 }
76
77
78
79
80
81 public SpdyFrameCodec(
82 SpdyVersion version, int maxChunkSize, int maxHeaderSize,
83 int compressionLevel, int windowBits, int memLevel) {
84 this(version, maxChunkSize, maxHeaderSize, compressionLevel, windowBits, memLevel, true);
85 }
86
87
88
89
90
91 public SpdyFrameCodec(
92 SpdyVersion version, int maxChunkSize, int maxHeaderSize,
93 int compressionLevel, int windowBits, int memLevel, boolean validateHeaders) {
94 this(version, maxChunkSize,
95 SpdyHeaderBlockDecoder.newInstance(version, maxHeaderSize),
96 SpdyHeaderBlockEncoder.newInstance(version, compressionLevel, windowBits, memLevel),
97 validateHeaders, false);
98 }
99
100
101
102
103
104 public SpdyFrameCodec(
105 SpdyVersion version, int maxChunkSize, int maxHeaderSize,
106 int compressionLevel, int windowBits, int memLevel, boolean validateHeaders,
107 boolean supportsUnknownFrames) {
108 this(version, maxChunkSize,
109 SpdyHeaderBlockDecoder.newInstance(version, maxHeaderSize),
110 SpdyHeaderBlockEncoder.newInstance(version, compressionLevel, windowBits, memLevel),
111 validateHeaders, supportsUnknownFrames);
112 }
113
114 protected SpdyFrameCodec(SpdyVersion version, int maxChunkSize,
115 SpdyHeaderBlockDecoder spdyHeaderBlockDecoder,
116 SpdyHeaderBlockEncoder spdyHeaderBlockEncoder,
117 boolean validateHeaders) {
118 this(version, maxChunkSize, spdyHeaderBlockDecoder, spdyHeaderBlockEncoder, validateHeaders, false);
119 }
120
121 protected SpdyFrameCodec(SpdyVersion version, int maxChunkSize,
122 SpdyHeaderBlockDecoder spdyHeaderBlockDecoder, SpdyHeaderBlockEncoder spdyHeaderBlockEncoder,
123 boolean validateHeaders, boolean supportsUnknownFrames) {
124 this.supportsUnknownFrames = supportsUnknownFrames;
125 spdyFrameDecoder = createDecoder(version, this, maxChunkSize);
126 spdyFrameEncoder = createEncoder(version);
127 this.spdyHeaderBlockDecoder = spdyHeaderBlockDecoder;
128 this.spdyHeaderBlockEncoder = spdyHeaderBlockEncoder;
129 this.validateHeaders = validateHeaders;
130 }
131
132 protected SpdyFrameDecoder createDecoder(SpdyVersion version, SpdyFrameDecoderDelegate delegate, int maxChunkSize) {
133 return new SpdyFrameDecoder(version, delegate, maxChunkSize) {
134 @Override
135 protected boolean isValidUnknownFrameHeader(int streamId, int type, byte flags, int length) {
136 if (supportsUnknownFrames) {
137 return SpdyFrameCodec.this.isValidUnknownFrameHeader(streamId, type, flags, length);
138 }
139 return super.isValidUnknownFrameHeader(streamId, type, flags, length);
140 }
141 };
142 }
143
144 protected SpdyFrameEncoder createEncoder(SpdyVersion version) {
145 return new SpdyFrameEncoder(version);
146 }
147
148 @Override
149 public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
150 super.handlerAdded(ctx);
151 this.ctx = ctx;
152 ctx.channel().closeFuture().addListener(new ChannelFutureListener() {
153 @Override
154 public void operationComplete(ChannelFuture future) throws Exception {
155 spdyHeaderBlockDecoder.end();
156 spdyHeaderBlockEncoder.end();
157 }
158 });
159 }
160
161 @Override
162 protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
163 spdyFrameDecoder.decode(in);
164 }
165
166 @Override
167 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
168 if (!read) {
169 if (!ctx.channel().config().isAutoRead()) {
170 ctx.read();
171 }
172 }
173 read = false;
174 super.channelReadComplete(ctx);
175 }
176
177 @Override
178 public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
179 ctx.bind(localAddress, promise);
180 }
181
182 @Override
183 public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
184 ChannelPromise promise) throws Exception {
185 ctx.connect(remoteAddress, localAddress, promise);
186 }
187
188 @Override
189 public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
190 ctx.disconnect(promise);
191 }
192
193 @Override
194 public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
195 ctx.close(promise);
196 }
197
198 @Override
199 public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
200 ctx.deregister(promise);
201 }
202
203 @Override
204 public void read(ChannelHandlerContext ctx) throws Exception {
205 ctx.read();
206 }
207
208 @Override
209 public void flush(ChannelHandlerContext ctx) throws Exception {
210 ctx.flush();
211 }
212
213 @Override
214 public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
215 ByteBuf frame;
216
217 if (msg instanceof SpdyDataFrame) {
218
219 SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
220 try {
221 frame = spdyFrameEncoder.encodeDataFrame(
222 ctx.alloc(),
223 spdyDataFrame.streamId(),
224 spdyDataFrame.isLast(),
225 spdyDataFrame.content()
226 );
227 ctx.write(frame, promise);
228 } finally {
229 spdyDataFrame.release();
230 }
231
232 } else if (msg instanceof SpdySynStreamFrame) {
233
234 SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
235 ByteBuf headerBlock = spdyHeaderBlockEncoder.encode(ctx.alloc(), spdySynStreamFrame);
236 try {
237 frame = spdyFrameEncoder.encodeSynStreamFrame(
238 ctx.alloc(),
239 spdySynStreamFrame.streamId(),
240 spdySynStreamFrame.associatedStreamId(),
241 spdySynStreamFrame.priority(),
242 spdySynStreamFrame.isLast(),
243 spdySynStreamFrame.isUnidirectional(),
244 headerBlock
245 );
246 } finally {
247 headerBlock.release();
248 }
249 ctx.write(frame, promise);
250
251 } else if (msg instanceof SpdySynReplyFrame) {
252
253 SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
254 ByteBuf headerBlock = spdyHeaderBlockEncoder.encode(ctx.alloc(), spdySynReplyFrame);
255 try {
256 frame = spdyFrameEncoder.encodeSynReplyFrame(
257 ctx.alloc(),
258 spdySynReplyFrame.streamId(),
259 spdySynReplyFrame.isLast(),
260 headerBlock
261 );
262 } finally {
263 headerBlock.release();
264 }
265 ctx.write(frame, promise);
266
267 } else if (msg instanceof SpdyRstStreamFrame) {
268
269 SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
270 frame = spdyFrameEncoder.encodeRstStreamFrame(
271 ctx.alloc(),
272 spdyRstStreamFrame.streamId(),
273 spdyRstStreamFrame.status().code()
274 );
275 ctx.write(frame, promise);
276
277 } else if (msg instanceof SpdySettingsFrame) {
278
279 SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;
280 frame = spdyFrameEncoder.encodeSettingsFrame(
281 ctx.alloc(),
282 spdySettingsFrame
283 );
284 ctx.write(frame, promise);
285
286 } else if (msg instanceof SpdyPingFrame) {
287
288 SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg;
289 frame = spdyFrameEncoder.encodePingFrame(
290 ctx.alloc(),
291 spdyPingFrame.id()
292 );
293 ctx.write(frame, promise);
294
295 } else if (msg instanceof SpdyGoAwayFrame) {
296
297 SpdyGoAwayFrame spdyGoAwayFrame = (SpdyGoAwayFrame) msg;
298 frame = spdyFrameEncoder.encodeGoAwayFrame(
299 ctx.alloc(),
300 spdyGoAwayFrame.lastGoodStreamId(),
301 spdyGoAwayFrame.status().code()
302 );
303 ctx.write(frame, promise);
304
305 } else if (msg instanceof SpdyHeadersFrame) {
306
307 SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
308 ByteBuf headerBlock = spdyHeaderBlockEncoder.encode(ctx.alloc(), spdyHeadersFrame);
309 try {
310 frame = spdyFrameEncoder.encodeHeadersFrame(
311 ctx.alloc(),
312 spdyHeadersFrame.streamId(),
313 spdyHeadersFrame.isLast(),
314 headerBlock
315 );
316 } finally {
317 headerBlock.release();
318 }
319 ctx.write(frame, promise);
320
321 } else if (msg instanceof SpdyWindowUpdateFrame) {
322
323 SpdyWindowUpdateFrame spdyWindowUpdateFrame = (SpdyWindowUpdateFrame) msg;
324 frame = spdyFrameEncoder.encodeWindowUpdateFrame(
325 ctx.alloc(),
326 spdyWindowUpdateFrame.streamId(),
327 spdyWindowUpdateFrame.deltaWindowSize()
328 );
329 ctx.write(frame, promise);
330 } else if (msg instanceof SpdyUnknownFrame) {
331 SpdyUnknownFrame spdyUnknownFrame = (SpdyUnknownFrame) msg;
332 try {
333 frame = spdyFrameEncoder.encodeUnknownFrame(
334 ctx.alloc(),
335 spdyUnknownFrame.frameType(),
336 spdyUnknownFrame.flags(),
337 spdyUnknownFrame.content());
338 ctx.write(frame, promise);
339 } finally {
340 spdyUnknownFrame.release();
341 }
342 } else {
343 throw new UnsupportedMessageTypeException(msg);
344 }
345 }
346
347 @Override
348 public void readDataFrame(int streamId, boolean last, ByteBuf data) {
349 read = true;
350
351 SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(streamId, data);
352 spdyDataFrame.setLast(last);
353 ctx.fireChannelRead(spdyDataFrame);
354 }
355
356 @Override
357 public void readSynStreamFrame(
358 int streamId, int associatedToStreamId, byte priority, boolean last, boolean unidirectional) {
359 SpdySynStreamFrame spdySynStreamFrame =
360 new DefaultSpdySynStreamFrame(streamId, associatedToStreamId, priority, validateHeaders);
361 spdySynStreamFrame.setLast(last);
362 spdySynStreamFrame.setUnidirectional(unidirectional);
363 spdyHeadersFrame = spdySynStreamFrame;
364 }
365
366 @Override
367 public void readSynReplyFrame(int streamId, boolean last) {
368 SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId, validateHeaders);
369 spdySynReplyFrame.setLast(last);
370 spdyHeadersFrame = spdySynReplyFrame;
371 }
372
373 @Override
374 public void readRstStreamFrame(int streamId, int statusCode) {
375 read = true;
376
377 SpdyRstStreamFrame spdyRstStreamFrame = new DefaultSpdyRstStreamFrame(streamId, statusCode);
378 ctx.fireChannelRead(spdyRstStreamFrame);
379 }
380
381 @Override
382 public void readSettingsFrame(boolean clearPersisted) {
383 read = true;
384
385 spdySettingsFrame = new DefaultSpdySettingsFrame();
386 spdySettingsFrame.setClearPreviouslyPersistedSettings(clearPersisted);
387 }
388
389 @Override
390 public void readSetting(int id, int value, boolean persistValue, boolean persisted) {
391 spdySettingsFrame.setValue(id, value, persistValue, persisted);
392 }
393
394 @Override
395 public void readSettingsEnd() {
396 read = true;
397
398 Object frame = spdySettingsFrame;
399 spdySettingsFrame = null;
400 ctx.fireChannelRead(frame);
401 }
402
403 @Override
404 public void readPingFrame(int id) {
405 read = true;
406
407 SpdyPingFrame spdyPingFrame = new DefaultSpdyPingFrame(id);
408 ctx.fireChannelRead(spdyPingFrame);
409 }
410
411 @Override
412 public void readGoAwayFrame(int lastGoodStreamId, int statusCode) {
413 read = true;
414
415 SpdyGoAwayFrame spdyGoAwayFrame = new DefaultSpdyGoAwayFrame(lastGoodStreamId, statusCode);
416 ctx.fireChannelRead(spdyGoAwayFrame);
417 }
418
419 @Override
420 public void readHeadersFrame(int streamId, boolean last) {
421 spdyHeadersFrame = new DefaultSpdyHeadersFrame(streamId, validateHeaders);
422 spdyHeadersFrame.setLast(last);
423 }
424
425 @Override
426 public void readWindowUpdateFrame(int streamId, int deltaWindowSize) {
427 read = true;
428
429 SpdyWindowUpdateFrame spdyWindowUpdateFrame = new DefaultSpdyWindowUpdateFrame(streamId, deltaWindowSize);
430 ctx.fireChannelRead(spdyWindowUpdateFrame);
431 }
432
433 @Override
434 public void readHeaderBlock(ByteBuf headerBlock) {
435 try {
436 spdyHeaderBlockDecoder.decode(ctx.alloc(), headerBlock, spdyHeadersFrame);
437 } catch (Exception e) {
438 ctx.fireExceptionCaught(e);
439 } finally {
440 headerBlock.release();
441 }
442 }
443
444 @Override
445 public void readHeaderBlockEnd() {
446 Object frame = null;
447 try {
448 spdyHeaderBlockDecoder.endHeaderBlock(spdyHeadersFrame);
449 frame = spdyHeadersFrame;
450 spdyHeadersFrame = null;
451 } catch (Exception e) {
452 ctx.fireExceptionCaught(e);
453 }
454 if (frame != null) {
455 read = true;
456
457 ctx.fireChannelRead(frame);
458 }
459 }
460
461 @Override
462 public void readUnknownFrame(int frameType, byte flags, ByteBuf payload) {
463 read = true;
464 ctx.fireChannelRead(newSpdyUnknownFrame(frameType, flags, payload));
465 }
466
467
468
469
470 protected SpdyFrame newSpdyUnknownFrame(int frameType, byte flags, ByteBuf payload) {
471 return new DefaultSpdyUnknownFrame(frameType, flags, payload);
472 }
473
474
475
476
477
478
479
480 protected boolean isValidUnknownFrameHeader(@SuppressWarnings("unused") int streamId,
481 @SuppressWarnings("unused") int type,
482 @SuppressWarnings("unused") byte flags,
483 @SuppressWarnings("unused") int length) {
484 return false;
485 }
486
487 @Override
488 public void readFrameError(String message) {
489 ctx.fireExceptionCaught(INVALID_FRAME);
490 }
491 }