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, supportsUnknownFrames ? new SpdyFrameDecoderExtendedDelegate() {
126 @Override
127 public void readUnknownFrame(int frameType, byte flags, ByteBuf payload) {
128 SpdyFrameCodec.this.readUnknownFrame(frameType, flags, payload);
129 }
130
131 @Override
132 public void readDataFrame(int streamId, boolean last, ByteBuf data) {
133 SpdyFrameCodec.this.readDataFrame(streamId, last, data);
134 }
135
136 @Override
137 public void readSynStreamFrame(int streamId, int associatedToStreamId, byte priority,
138 boolean last, boolean unidirectional) {
139 SpdyFrameCodec.this.readSynStreamFrame(streamId, associatedToStreamId, priority, last, unidirectional);
140 }
141
142 @Override
143 public void readSynReplyFrame(int streamId, boolean last) {
144 SpdyFrameCodec.this.readSynReplyFrame(streamId, last);
145 }
146
147 @Override
148 public void readRstStreamFrame(int streamId, int statusCode) {
149 SpdyFrameCodec.this.readRstStreamFrame(streamId, statusCode);
150 }
151
152 @Override
153 public void readSettingsFrame(boolean clearPersisted) {
154 SpdyFrameCodec.this.readSettingsFrame(clearPersisted);
155 }
156
157 @Override
158 public void readSetting(int id, int value, boolean persistValue, boolean persisted) {
159 SpdyFrameCodec.this.readSetting(id, value, persistValue, persisted);
160 }
161
162 @Override
163 public void readSettingsEnd() {
164 SpdyFrameCodec.this.readSettingsEnd();
165 }
166
167 @Override
168 public void readPingFrame(int id) {
169 SpdyFrameCodec.this.readPingFrame(id);
170 }
171
172 @Override
173 public void readGoAwayFrame(int lastGoodStreamId, int statusCode) {
174 SpdyFrameCodec.this.readGoAwayFrame(lastGoodStreamId, statusCode);
175 }
176
177 @Override
178 public void readHeadersFrame(int streamId, boolean last) {
179 SpdyFrameCodec.this.readHeadersFrame(streamId, last);
180 }
181
182 @Override
183 public void readWindowUpdateFrame(int streamId, int deltaWindowSize) {
184 SpdyFrameCodec.this.readWindowUpdateFrame(streamId, deltaWindowSize);
185 }
186
187 @Override
188 public void readHeaderBlock(ByteBuf headerBlock) {
189 SpdyFrameCodec.this.readHeaderBlock(headerBlock);
190 }
191
192 @Override
193 public void readHeaderBlockEnd() {
194 SpdyFrameCodec.this.readHeaderBlockEnd();
195 }
196
197 @Override
198 public void readFrameError(String message) {
199 SpdyFrameCodec.this.readFrameError(message);
200 }
201 } : this, maxChunkSize);
202 spdyFrameEncoder = createEncoder(version);
203 this.spdyHeaderBlockDecoder = spdyHeaderBlockDecoder;
204 this.spdyHeaderBlockEncoder = spdyHeaderBlockEncoder;
205 this.validateHeaders = validateHeaders;
206 }
207
208 protected SpdyFrameDecoder createDecoder(SpdyVersion version, SpdyFrameDecoderDelegate delegate, int maxChunkSize) {
209 return new SpdyFrameDecoder(version, delegate, maxChunkSize) {
210 @Override
211 protected boolean isValidUnknownFrameHeader(int streamId, int type, byte flags, int length) {
212 if (supportsUnknownFrames) {
213 return SpdyFrameCodec.this.isValidUnknownFrameHeader(streamId, type, flags, length);
214 }
215 return super.isValidUnknownFrameHeader(streamId, type, flags, length);
216 }
217 };
218 }
219
220 protected SpdyFrameEncoder createEncoder(SpdyVersion version) {
221 return new SpdyFrameEncoder(version);
222 }
223
224 @Override
225 public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
226 super.handlerAdded(ctx);
227 this.ctx = ctx;
228 ctx.channel().closeFuture().addListener(new ChannelFutureListener() {
229 @Override
230 public void operationComplete(ChannelFuture future) throws Exception {
231 spdyHeaderBlockDecoder.end();
232 spdyHeaderBlockEncoder.end();
233 }
234 });
235 }
236
237 @Override
238 protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
239 spdyFrameDecoder.decode(in);
240 }
241
242 @Override
243 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
244 if (!read) {
245 if (!ctx.channel().config().isAutoRead()) {
246 ctx.read();
247 }
248 }
249 read = false;
250 super.channelReadComplete(ctx);
251 }
252
253 @Override
254 public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
255 ctx.bind(localAddress, promise);
256 }
257
258 @Override
259 public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
260 ChannelPromise promise) throws Exception {
261 ctx.connect(remoteAddress, localAddress, promise);
262 }
263
264 @Override
265 public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
266 ctx.disconnect(promise);
267 }
268
269 @Override
270 public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
271 ctx.close(promise);
272 }
273
274 @Override
275 public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
276 ctx.deregister(promise);
277 }
278
279 @Override
280 public void read(ChannelHandlerContext ctx) throws Exception {
281 ctx.read();
282 }
283
284 @Override
285 public void flush(ChannelHandlerContext ctx) throws Exception {
286 ctx.flush();
287 }
288
289 @Override
290 public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
291 ByteBuf frame;
292
293 if (msg instanceof SpdyDataFrame) {
294
295 SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
296 try {
297 frame = spdyFrameEncoder.encodeDataFrame(
298 ctx.alloc(),
299 spdyDataFrame.streamId(),
300 spdyDataFrame.isLast(),
301 spdyDataFrame.content()
302 );
303 ctx.write(frame, promise);
304 } finally {
305 spdyDataFrame.release();
306 }
307
308 } else if (msg instanceof SpdySynStreamFrame) {
309
310 SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
311 ByteBuf headerBlock = spdyHeaderBlockEncoder.encode(ctx.alloc(), spdySynStreamFrame);
312 try {
313 frame = spdyFrameEncoder.encodeSynStreamFrame(
314 ctx.alloc(),
315 spdySynStreamFrame.streamId(),
316 spdySynStreamFrame.associatedStreamId(),
317 spdySynStreamFrame.priority(),
318 spdySynStreamFrame.isLast(),
319 spdySynStreamFrame.isUnidirectional(),
320 headerBlock
321 );
322 } finally {
323 headerBlock.release();
324 }
325 ctx.write(frame, promise);
326
327 } else if (msg instanceof SpdySynReplyFrame) {
328
329 SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
330 ByteBuf headerBlock = spdyHeaderBlockEncoder.encode(ctx.alloc(), spdySynReplyFrame);
331 try {
332 frame = spdyFrameEncoder.encodeSynReplyFrame(
333 ctx.alloc(),
334 spdySynReplyFrame.streamId(),
335 spdySynReplyFrame.isLast(),
336 headerBlock
337 );
338 } finally {
339 headerBlock.release();
340 }
341 ctx.write(frame, promise);
342
343 } else if (msg instanceof SpdyRstStreamFrame) {
344
345 SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
346 frame = spdyFrameEncoder.encodeRstStreamFrame(
347 ctx.alloc(),
348 spdyRstStreamFrame.streamId(),
349 spdyRstStreamFrame.status().code()
350 );
351 ctx.write(frame, promise);
352
353 } else if (msg instanceof SpdySettingsFrame) {
354
355 SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;
356 frame = spdyFrameEncoder.encodeSettingsFrame(
357 ctx.alloc(),
358 spdySettingsFrame
359 );
360 ctx.write(frame, promise);
361
362 } else if (msg instanceof SpdyPingFrame) {
363
364 SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg;
365 frame = spdyFrameEncoder.encodePingFrame(
366 ctx.alloc(),
367 spdyPingFrame.id()
368 );
369 ctx.write(frame, promise);
370
371 } else if (msg instanceof SpdyGoAwayFrame) {
372
373 SpdyGoAwayFrame spdyGoAwayFrame = (SpdyGoAwayFrame) msg;
374 frame = spdyFrameEncoder.encodeGoAwayFrame(
375 ctx.alloc(),
376 spdyGoAwayFrame.lastGoodStreamId(),
377 spdyGoAwayFrame.status().code()
378 );
379 ctx.write(frame, promise);
380
381 } else if (msg instanceof SpdyHeadersFrame) {
382
383 SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
384 ByteBuf headerBlock = spdyHeaderBlockEncoder.encode(ctx.alloc(), spdyHeadersFrame);
385 try {
386 frame = spdyFrameEncoder.encodeHeadersFrame(
387 ctx.alloc(),
388 spdyHeadersFrame.streamId(),
389 spdyHeadersFrame.isLast(),
390 headerBlock
391 );
392 } finally {
393 headerBlock.release();
394 }
395 ctx.write(frame, promise);
396
397 } else if (msg instanceof SpdyWindowUpdateFrame) {
398
399 SpdyWindowUpdateFrame spdyWindowUpdateFrame = (SpdyWindowUpdateFrame) msg;
400 frame = spdyFrameEncoder.encodeWindowUpdateFrame(
401 ctx.alloc(),
402 spdyWindowUpdateFrame.streamId(),
403 spdyWindowUpdateFrame.deltaWindowSize()
404 );
405 ctx.write(frame, promise);
406 } else if (msg instanceof SpdyUnknownFrame) {
407 SpdyUnknownFrame spdyUnknownFrame = (SpdyUnknownFrame) msg;
408 try {
409 frame = spdyFrameEncoder.encodeUnknownFrame(
410 ctx.alloc(),
411 spdyUnknownFrame.frameType(),
412 spdyUnknownFrame.flags(),
413 spdyUnknownFrame.content());
414 ctx.write(frame, promise);
415 } finally {
416 spdyUnknownFrame.release();
417 }
418 } else {
419 throw new UnsupportedMessageTypeException(msg);
420 }
421 }
422
423 @Override
424 public void readDataFrame(int streamId, boolean last, ByteBuf data) {
425 read = true;
426
427 SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(streamId, data);
428 spdyDataFrame.setLast(last);
429 ctx.fireChannelRead(spdyDataFrame);
430 }
431
432 @Override
433 public void readSynStreamFrame(
434 int streamId, int associatedToStreamId, byte priority, boolean last, boolean unidirectional) {
435 SpdySynStreamFrame spdySynStreamFrame =
436 new DefaultSpdySynStreamFrame(streamId, associatedToStreamId, priority, validateHeaders);
437 spdySynStreamFrame.setLast(last);
438 spdySynStreamFrame.setUnidirectional(unidirectional);
439 spdyHeadersFrame = spdySynStreamFrame;
440 }
441
442 @Override
443 public void readSynReplyFrame(int streamId, boolean last) {
444 SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId, validateHeaders);
445 spdySynReplyFrame.setLast(last);
446 spdyHeadersFrame = spdySynReplyFrame;
447 }
448
449 @Override
450 public void readRstStreamFrame(int streamId, int statusCode) {
451 read = true;
452
453 SpdyRstStreamFrame spdyRstStreamFrame = new DefaultSpdyRstStreamFrame(streamId, statusCode);
454 ctx.fireChannelRead(spdyRstStreamFrame);
455 }
456
457 @Override
458 public void readSettingsFrame(boolean clearPersisted) {
459 read = true;
460
461 spdySettingsFrame = new DefaultSpdySettingsFrame();
462 spdySettingsFrame.setClearPreviouslyPersistedSettings(clearPersisted);
463 }
464
465 @Override
466 public void readSetting(int id, int value, boolean persistValue, boolean persisted) {
467 spdySettingsFrame.setValue(id, value, persistValue, persisted);
468 }
469
470 @Override
471 public void readSettingsEnd() {
472 read = true;
473
474 Object frame = spdySettingsFrame;
475 spdySettingsFrame = null;
476 ctx.fireChannelRead(frame);
477 }
478
479 @Override
480 public void readPingFrame(int id) {
481 read = true;
482
483 SpdyPingFrame spdyPingFrame = new DefaultSpdyPingFrame(id);
484 ctx.fireChannelRead(spdyPingFrame);
485 }
486
487 @Override
488 public void readGoAwayFrame(int lastGoodStreamId, int statusCode) {
489 read = true;
490
491 SpdyGoAwayFrame spdyGoAwayFrame = new DefaultSpdyGoAwayFrame(lastGoodStreamId, statusCode);
492 ctx.fireChannelRead(spdyGoAwayFrame);
493 }
494
495 @Override
496 public void readHeadersFrame(int streamId, boolean last) {
497 spdyHeadersFrame = new DefaultSpdyHeadersFrame(streamId, validateHeaders);
498 spdyHeadersFrame.setLast(last);
499 }
500
501 @Override
502 public void readWindowUpdateFrame(int streamId, int deltaWindowSize) {
503 read = true;
504
505 SpdyWindowUpdateFrame spdyWindowUpdateFrame = new DefaultSpdyWindowUpdateFrame(streamId, deltaWindowSize);
506 ctx.fireChannelRead(spdyWindowUpdateFrame);
507 }
508
509 @Override
510 public void readHeaderBlock(ByteBuf headerBlock) {
511 try {
512 spdyHeaderBlockDecoder.decode(ctx.alloc(), headerBlock, spdyHeadersFrame);
513 } catch (Exception e) {
514 ctx.fireExceptionCaught(e);
515 } finally {
516 headerBlock.release();
517 }
518 }
519
520 @Override
521 public void readHeaderBlockEnd() {
522 Object frame = null;
523 try {
524 spdyHeaderBlockDecoder.endHeaderBlock(spdyHeadersFrame);
525 frame = spdyHeadersFrame;
526 spdyHeadersFrame = null;
527 } catch (Exception e) {
528 ctx.fireExceptionCaught(e);
529 }
530 if (frame != null) {
531 read = true;
532
533 ctx.fireChannelRead(frame);
534 }
535 }
536
537 private void readUnknownFrame(int frameType, byte flags, ByteBuf payload) {
538 read = true;
539 ctx.fireChannelRead(newSpdyUnknownFrame(frameType, flags, payload));
540 }
541
542
543
544
545 protected SpdyFrame newSpdyUnknownFrame(int frameType, byte flags, ByteBuf payload) {
546 return new DefaultSpdyUnknownFrame(frameType, flags, payload);
547 }
548
549
550
551
552
553
554
555 protected boolean isValidUnknownFrameHeader(@SuppressWarnings("unused") int streamId,
556 @SuppressWarnings("unused") int type,
557 @SuppressWarnings("unused") byte flags,
558 @SuppressWarnings("unused") int length) {
559 return false;
560 }
561
562 @Override
563 public void readFrameError(String message) {
564 ctx.fireExceptionCaught(INVALID_FRAME);
565 }
566 }