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 static org.jboss.netty.handler.codec.spdy.SpdyCodecUtil.*;
19
20 import org.jboss.netty.buffer.ChannelBuffer;
21 import org.jboss.netty.buffer.ChannelBuffers;
22 import org.jboss.netty.channel.Channel;
23 import org.jboss.netty.channel.ChannelHandlerContext;
24 import org.jboss.netty.channel.Channels;
25 import org.jboss.netty.handler.codec.frame.FrameDecoder;
26 import org.jboss.netty.handler.codec.frame.TooLongFrameException;
27
28
29
30
31 public class SpdyFrameDecoder extends FrameDecoder {
32
33 private final int spdyVersion;
34 private final int maxChunkSize;
35 private final int maxHeaderSize;
36
37 private final SpdyHeaderBlockDecompressor headerBlockDecompressor;
38
39 private State state;
40 private SpdySettingsFrame spdySettingsFrame;
41 private SpdyHeaderBlock spdyHeaderBlock;
42
43
44 private byte flags;
45 private int length;
46 private int version;
47 private int type;
48 private int streamID;
49
50
51 private int headerSize;
52 private int numHeaders;
53 private ChannelBuffer decompressed;
54
55 private enum State {
56 READ_COMMON_HEADER,
57 READ_CONTROL_FRAME,
58 READ_SETTINGS_FRAME,
59 READ_HEADER_BLOCK_FRAME,
60 READ_HEADER_BLOCK,
61 READ_DATA_FRAME,
62 DISCARD_FRAME,
63 FRAME_ERROR
64 }
65
66
67
68
69
70 @Deprecated
71 public SpdyFrameDecoder() {
72 this(2);
73 }
74
75
76
77
78
79 public SpdyFrameDecoder(int version) {
80 this(version, 8192, 16384);
81 }
82
83
84
85
86 public SpdyFrameDecoder(int version, int maxChunkSize, int maxHeaderSize) {
87 super(false);
88 if (version < SPDY_MIN_VERSION || version > SPDY_MAX_VERSION) {
89 throw new IllegalArgumentException(
90 "unsupported version: " + version);
91 }
92 if (maxChunkSize <= 0) {
93 throw new IllegalArgumentException(
94 "maxChunkSize must be a positive integer: " + maxChunkSize);
95 }
96 if (maxHeaderSize <= 0) {
97 throw new IllegalArgumentException(
98 "maxHeaderSize must be a positive integer: " + maxHeaderSize);
99 }
100 spdyVersion = version;
101 this.maxChunkSize = maxChunkSize;
102 this.maxHeaderSize = maxHeaderSize;
103 headerBlockDecompressor = SpdyHeaderBlockDecompressor.newInstance(version);
104 state = State.READ_COMMON_HEADER;
105 }
106
107 @Override
108 protected Object decodeLast(
109 ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer)
110 throws Exception {
111 try {
112 return decode(ctx, channel, buffer);
113 } finally {
114 headerBlockDecompressor.end();
115 }
116 }
117
118 @Override
119 protected Object decode(
120 ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer)
121 throws Exception {
122 switch(state) {
123 case READ_COMMON_HEADER:
124 state = readCommonHeader(buffer);
125 if (state == State.FRAME_ERROR) {
126 if (version != spdyVersion) {
127 fireProtocolException(ctx, "Unsupported version: " + version);
128 } else {
129 fireInvalidControlFrameException(ctx);
130 }
131 }
132
133
134
135 if (length == 0) {
136 if (state == State.READ_DATA_FRAME) {
137 if (streamID == 0) {
138 state = State.FRAME_ERROR;
139 fireProtocolException(ctx, "Received invalid data frame");
140 return null;
141 }
142
143 SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(streamID);
144 spdyDataFrame.setLast((flags & SPDY_DATA_FLAG_FIN) != 0);
145 state = State.READ_COMMON_HEADER;
146 return spdyDataFrame;
147 }
148
149 state = State.READ_COMMON_HEADER;
150 }
151
152 return null;
153
154 case READ_CONTROL_FRAME:
155 try {
156 Object frame = readControlFrame(buffer);
157 if (frame != null) {
158 state = State.READ_COMMON_HEADER;
159 }
160 return frame;
161 } catch (IllegalArgumentException e) {
162 state = State.FRAME_ERROR;
163 fireInvalidControlFrameException(ctx);
164 }
165 return null;
166
167 case READ_SETTINGS_FRAME:
168 if (spdySettingsFrame == null) {
169
170 if (buffer.readableBytes() < 4) {
171 return null;
172 }
173 int numEntries = getUnsignedInt(buffer, buffer.readerIndex());
174 buffer.skipBytes(4);
175 length -= 4;
176
177
178 if ((length & 0x07) != 0 || length >> 3 != numEntries) {
179 state = State.FRAME_ERROR;
180 fireInvalidControlFrameException(ctx);
181 return null;
182 }
183
184 spdySettingsFrame = new DefaultSpdySettingsFrame();
185
186 boolean clear = (flags & SPDY_SETTINGS_CLEAR) != 0;
187 spdySettingsFrame.setClearPreviouslyPersistedSettings(clear);
188 }
189
190 int readableEntries = Math.min(buffer.readableBytes() >> 3, length >> 3);
191 for (int i = 0; i < readableEntries; i ++) {
192 int ID;
193 byte ID_flags;
194 if (version < 3) {
195
196
197
198 ID = buffer.readByte() & 0xFF |
199 (buffer.readByte() & 0xFF) << 8 |
200 (buffer.readByte() & 0xFF) << 16;
201 ID_flags = buffer.readByte();
202 } else {
203 ID_flags = buffer.readByte();
204 ID = getUnsignedMedium(buffer, buffer.readerIndex());
205 buffer.skipBytes(3);
206 }
207 int value = getSignedInt(buffer, buffer.readerIndex());
208 buffer.skipBytes(4);
209
210
211 if (ID == 0) {
212 state = State.FRAME_ERROR;
213 spdySettingsFrame = null;
214 fireInvalidControlFrameException(ctx);
215 return null;
216 }
217
218 if (!spdySettingsFrame.isSet(ID)) {
219 boolean persistVal = (ID_flags & SPDY_SETTINGS_PERSIST_VALUE) != 0;
220 boolean persisted = (ID_flags & SPDY_SETTINGS_PERSISTED) != 0;
221 spdySettingsFrame.setValue(ID, value, persistVal, persisted);
222 }
223 }
224
225 length -= 8 * readableEntries;
226 if (length == 0) {
227 state = State.READ_COMMON_HEADER;
228 Object frame = spdySettingsFrame;
229 spdySettingsFrame = null;
230 return frame;
231 }
232 return null;
233
234 case READ_HEADER_BLOCK_FRAME:
235 try {
236 spdyHeaderBlock = readHeaderBlockFrame(buffer);
237 if (spdyHeaderBlock != null) {
238 if (length == 0) {
239 state = State.READ_COMMON_HEADER;
240 Object frame = spdyHeaderBlock;
241 spdyHeaderBlock = null;
242 return frame;
243 }
244 state = State.READ_HEADER_BLOCK;
245 }
246 return null;
247 } catch (IllegalArgumentException e) {
248 state = State.FRAME_ERROR;
249 fireInvalidControlFrameException(ctx);
250 return null;
251 }
252
253 case READ_HEADER_BLOCK:
254 int compressedBytes = Math.min(buffer.readableBytes(), length);
255 length -= compressedBytes;
256
257 try {
258 decodeHeaderBlock(buffer.readSlice(compressedBytes));
259 } catch (Exception e) {
260 state = State.FRAME_ERROR;
261 spdyHeaderBlock = null;
262 decompressed = null;
263 Channels.fireExceptionCaught(ctx, e);
264 return null;
265 }
266
267 if (spdyHeaderBlock != null && spdyHeaderBlock.isInvalid()) {
268 Object frame = spdyHeaderBlock;
269 spdyHeaderBlock = null;
270 decompressed = null;
271 if (length == 0) {
272 state = State.READ_COMMON_HEADER;
273 }
274 return frame;
275 }
276
277 if (length == 0) {
278 Object frame = spdyHeaderBlock;
279 spdyHeaderBlock = null;
280 state = State.READ_COMMON_HEADER;
281 return frame;
282 }
283 return null;
284
285 case READ_DATA_FRAME:
286 if (streamID == 0) {
287 state = State.FRAME_ERROR;
288 fireProtocolException(ctx, "Received invalid data frame");
289 return null;
290 }
291
292
293 int dataLength = Math.min(maxChunkSize, length);
294
295
296 if (buffer.readableBytes() < dataLength) {
297 return null;
298 }
299
300 SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(streamID);
301 spdyDataFrame.setData(buffer.readBytes(dataLength));
302 length -= dataLength;
303
304 if (length == 0) {
305 spdyDataFrame.setLast((flags & SPDY_DATA_FLAG_FIN) != 0);
306 state = State.READ_COMMON_HEADER;
307 }
308 return spdyDataFrame;
309
310 case DISCARD_FRAME:
311 int numBytes = Math.min(buffer.readableBytes(), length);
312 buffer.skipBytes(numBytes);
313 length -= numBytes;
314 if (length == 0) {
315 state = State.READ_COMMON_HEADER;
316 }
317 return null;
318
319 case FRAME_ERROR:
320 buffer.skipBytes(buffer.readableBytes());
321 return null;
322
323 default:
324 throw new Error("Shouldn't reach here.");
325 }
326 }
327
328 private State readCommonHeader(ChannelBuffer buffer) {
329
330 if (buffer.readableBytes() < SPDY_HEADER_SIZE) {
331 return State.READ_COMMON_HEADER;
332 }
333
334 int frameOffset = buffer.readerIndex();
335 int flagsOffset = frameOffset + SPDY_HEADER_FLAGS_OFFSET;
336 int lengthOffset = frameOffset + SPDY_HEADER_LENGTH_OFFSET;
337 buffer.skipBytes(SPDY_HEADER_SIZE);
338
339
340 boolean control = (buffer.getByte(frameOffset) & 0x80) != 0;
341 flags = buffer.getByte(flagsOffset);
342 length = getUnsignedMedium(buffer, lengthOffset);
343
344 if (control) {
345
346 version = getUnsignedShort(buffer, frameOffset) & 0x7FFF;
347
348 int typeOffset = frameOffset + SPDY_HEADER_TYPE_OFFSET;
349 type = getUnsignedShort(buffer, typeOffset);
350
351
352 if (version != spdyVersion || !isValidControlFrameHeader()) {
353 return State.FRAME_ERROR;
354 }
355
356
357 State nextState;
358 if (willGenerateControlFrame()) {
359 switch (type) {
360 case SPDY_SYN_STREAM_FRAME:
361 case SPDY_SYN_REPLY_FRAME:
362 case SPDY_HEADERS_FRAME:
363 nextState = State.READ_HEADER_BLOCK_FRAME;
364 break;
365
366 case SPDY_SETTINGS_FRAME:
367 nextState = State.READ_SETTINGS_FRAME;
368 break;
369
370 default:
371 nextState = State.READ_CONTROL_FRAME;
372 }
373 } else if (length != 0) {
374 nextState = State.DISCARD_FRAME;
375 } else {
376 nextState = State.READ_COMMON_HEADER;
377 }
378 return nextState;
379 } else {
380
381 streamID = getUnsignedInt(buffer, frameOffset);
382
383 return State.READ_DATA_FRAME;
384 }
385 }
386
387 private Object readControlFrame(ChannelBuffer buffer) {
388 int streamID;
389 int statusCode;
390 switch (type) {
391 case SPDY_RST_STREAM_FRAME:
392 if (buffer.readableBytes() < 8) {
393 return null;
394 }
395
396 streamID = getUnsignedInt(buffer, buffer.readerIndex());
397 statusCode = getSignedInt(buffer, buffer.readerIndex() + 4);
398 buffer.skipBytes(8);
399
400 return new DefaultSpdyRstStreamFrame(streamID, statusCode);
401
402 case SPDY_PING_FRAME:
403 if (buffer.readableBytes() < 4) {
404 return null;
405 }
406
407 int ID = getSignedInt(buffer, buffer.readerIndex());
408 buffer.skipBytes(4);
409
410 return new DefaultSpdyPingFrame(ID);
411
412 case SPDY_GOAWAY_FRAME:
413 int minLength = version < 3 ? 4 : 8;
414 if (buffer.readableBytes() < minLength) {
415 return null;
416 }
417
418 int lastGoodStreamID = getUnsignedInt(buffer, buffer.readerIndex());
419 buffer.skipBytes(4);
420
421 if (version < 3) {
422 return new DefaultSpdyGoAwayFrame(lastGoodStreamID);
423 }
424
425 statusCode = getSignedInt(buffer, buffer.readerIndex());
426 buffer.skipBytes(4);
427
428 return new DefaultSpdyGoAwayFrame(lastGoodStreamID, statusCode);
429
430 case SPDY_WINDOW_UPDATE_FRAME:
431 if (buffer.readableBytes() < 8) {
432 return null;
433 }
434
435 streamID = getUnsignedInt(buffer, buffer.readerIndex());
436 int deltaWindowSize = getUnsignedInt(buffer, buffer.readerIndex() + 4);
437 buffer.skipBytes(8);
438
439 return new DefaultSpdyWindowUpdateFrame(streamID, deltaWindowSize);
440
441 default:
442 throw new Error("Shouldn't reach here.");
443 }
444 }
445
446 private SpdyHeaderBlock readHeaderBlockFrame(ChannelBuffer buffer) {
447 int minLength;
448 int streamID;
449 switch (type) {
450 case SPDY_SYN_STREAM_FRAME:
451 minLength = version < 3 ? 12 : 10;
452 if (buffer.readableBytes() < minLength) {
453 return null;
454 }
455
456 int offset = buffer.readerIndex();
457 streamID = getUnsignedInt(buffer, offset);
458 int associatedToStreamID = getUnsignedInt(buffer, offset + 4);
459 byte priority = (byte) (buffer.getByte(offset + 8) >> 5 & 0x07);
460 if (version < 3) {
461 priority >>= 1;
462 }
463 buffer.skipBytes(10);
464 length -= 10;
465
466
467 if (version < 3 && length == 2 && buffer.getShort(buffer.readerIndex()) == 0) {
468 buffer.skipBytes(2);
469 length = 0;
470 }
471
472 SpdySynStreamFrame spdySynStreamFrame =
473 new DefaultSpdySynStreamFrame(streamID, associatedToStreamID, priority);
474 spdySynStreamFrame.setLast((flags & SPDY_FLAG_FIN) != 0);
475 spdySynStreamFrame.setUnidirectional((flags & SPDY_FLAG_UNIDIRECTIONAL) != 0);
476
477 return spdySynStreamFrame;
478
479 case SPDY_SYN_REPLY_FRAME:
480 minLength = version < 3 ? 8 : 4;
481 if (buffer.readableBytes() < minLength) {
482 return null;
483 }
484
485 streamID = getUnsignedInt(buffer, buffer.readerIndex());
486 buffer.skipBytes(4);
487 length -= 4;
488
489
490 if (version < 3) {
491 buffer.skipBytes(2);
492 length -= 2;
493 }
494
495
496 if (version < 3 && length == 2 && buffer.getShort(buffer.readerIndex()) == 0) {
497 buffer.skipBytes(2);
498 length = 0;
499 }
500
501 SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID);
502 spdySynReplyFrame.setLast((flags & SPDY_FLAG_FIN) != 0);
503
504 return spdySynReplyFrame;
505
506 case SPDY_HEADERS_FRAME:
507 if (buffer.readableBytes() < 4) {
508 return null;
509 }
510
511
512 if (version < 3 && length > 4 && buffer.readableBytes() < 8) {
513 return null;
514 }
515
516 streamID = getUnsignedInt(buffer, buffer.readerIndex());
517 buffer.skipBytes(4);
518 length -= 4;
519
520
521 if (version < 3 && length != 0) {
522 buffer.skipBytes(2);
523 length -= 2;
524 }
525
526
527 if (version < 3 && length == 2 && buffer.getShort(buffer.readerIndex()) == 0) {
528 buffer.skipBytes(2);
529 length = 0;
530 }
531
532 SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(streamID);
533 spdyHeadersFrame.setLast((flags & SPDY_FLAG_FIN) != 0);
534
535 return spdyHeadersFrame;
536
537 default:
538 throw new Error("Shouldn't reach here.");
539 }
540 }
541
542 private boolean ensureBytes(int bytes) throws Exception {
543 if (decompressed.readableBytes() >= bytes) {
544 return true;
545 }
546
547 int numBytes;
548 boolean done;
549 do {
550 numBytes = headerBlockDecompressor.decode(decompressed);
551 done = decompressed.readableBytes() >= bytes;
552 } while (!done && numBytes > 0);
553 return done;
554 }
555
556 private int readLengthField() {
557 if (version < 3) {
558 return decompressed.readUnsignedShort();
559 } else {
560 return decompressed.readInt();
561 }
562 }
563
564 private void decodeHeaderBlock(ChannelBuffer buffer) throws Exception {
565 if (decompressed == null) {
566
567
568 headerSize = 0;
569 numHeaders = -1;
570 decompressed = ChannelBuffers.dynamicBuffer(8192);
571 }
572
573
574 headerBlockDecompressor.setInput(buffer);
575 headerBlockDecompressor.decode(decompressed);
576
577 if (spdyHeaderBlock == null) {
578
579 decompressed = null;
580 return;
581 }
582
583 int lengthFieldSize = version < 3 ? 2 : 4;
584
585 if (numHeaders == -1) {
586
587 if (decompressed.readableBytes() < lengthFieldSize) {
588 return;
589 }
590 numHeaders = readLengthField();
591 if (numHeaders < 0) {
592 spdyHeaderBlock.setInvalid();
593 return;
594 }
595 }
596
597 while (numHeaders > 0) {
598 int headerSize = this.headerSize;
599 decompressed.markReaderIndex();
600
601
602 if (!ensureBytes(lengthFieldSize)) {
603 decompressed.resetReaderIndex();
604 decompressed.discardReadBytes();
605 return;
606 }
607 int nameLength = readLengthField();
608
609
610 if (nameLength <= 0) {
611 spdyHeaderBlock.setInvalid();
612 return;
613 }
614 headerSize += nameLength;
615 if (headerSize > maxHeaderSize) {
616 throw new TooLongFrameException(
617 "Header block exceeds " + maxHeaderSize);
618 }
619
620
621 if (!ensureBytes(nameLength)) {
622 decompressed.resetReaderIndex();
623 decompressed.discardReadBytes();
624 return;
625 }
626 byte[] nameBytes = new byte[nameLength];
627 decompressed.readBytes(nameBytes);
628 String name = new String(nameBytes, "UTF-8");
629
630
631 if (spdyHeaderBlock.containsHeader(name)) {
632 spdyHeaderBlock.setInvalid();
633 return;
634 }
635
636
637 if (!ensureBytes(lengthFieldSize)) {
638 decompressed.resetReaderIndex();
639 decompressed.discardReadBytes();
640 return;
641 }
642 int valueLength = readLengthField();
643
644
645 if (valueLength < 0) {
646 spdyHeaderBlock.setInvalid();
647 return;
648 }
649
650
651 if (valueLength == 0) {
652 if (version < 3) {
653 spdyHeaderBlock.setInvalid();
654 return;
655 } else {
656 spdyHeaderBlock.addHeader(name, "");
657 numHeaders --;
658 this.headerSize = headerSize;
659 continue;
660 }
661 }
662
663 headerSize += valueLength;
664 if (headerSize > maxHeaderSize) {
665 throw new TooLongFrameException(
666 "Header block exceeds " + maxHeaderSize);
667 }
668
669
670 if (!ensureBytes(valueLength)) {
671 decompressed.resetReaderIndex();
672 decompressed.discardReadBytes();
673 return;
674 }
675 byte[] valueBytes = new byte[valueLength];
676 decompressed.readBytes(valueBytes);
677
678
679 int index = 0;
680 int offset = 0;
681 while (index < valueLength) {
682 while (index < valueBytes.length && valueBytes[index] != (byte) 0) {
683 index ++;
684 }
685 if (index < valueBytes.length && valueBytes[index + 1] == (byte) 0) {
686
687
688 spdyHeaderBlock.setInvalid();
689 return;
690 }
691 String value = new String(valueBytes, offset, index - offset, "UTF-8");
692
693 try {
694 spdyHeaderBlock.addHeader(name, value);
695 } catch (IllegalArgumentException e) {
696
697 spdyHeaderBlock.setInvalid();
698 return;
699 }
700 index ++;
701 offset = index;
702 }
703 numHeaders --;
704 this.headerSize = headerSize;
705 }
706 decompressed = null;
707 }
708
709 private boolean isValidControlFrameHeader() {
710 switch (type) {
711 case SPDY_SYN_STREAM_FRAME:
712 return version < 3 ? length >= 12 : length >= 10;
713
714 case SPDY_SYN_REPLY_FRAME:
715 return version < 3 ? length >= 8 : length >= 4;
716
717 case SPDY_RST_STREAM_FRAME:
718 return flags == 0 && length == 8;
719
720 case SPDY_SETTINGS_FRAME:
721 return length >= 4;
722
723 case SPDY_NOOP_FRAME:
724 return length == 0;
725
726 case SPDY_PING_FRAME:
727 return length == 4;
728
729 case SPDY_GOAWAY_FRAME:
730 return version < 3 ? length == 4 : length == 8;
731
732 case SPDY_HEADERS_FRAME:
733 if (version < 3) {
734 return length == 4 || length >= 8;
735 } else {
736 return length >= 4;
737 }
738
739 case SPDY_WINDOW_UPDATE_FRAME:
740 return length == 8;
741
742 case SPDY_CREDENTIAL_FRAME:
743 default:
744 return true;
745 }
746 }
747
748 private boolean willGenerateControlFrame() {
749 switch (type) {
750 case SPDY_SYN_STREAM_FRAME:
751 case SPDY_SYN_REPLY_FRAME:
752 case SPDY_RST_STREAM_FRAME:
753 case SPDY_SETTINGS_FRAME:
754 case SPDY_PING_FRAME:
755 case SPDY_GOAWAY_FRAME:
756 case SPDY_HEADERS_FRAME:
757 case SPDY_WINDOW_UPDATE_FRAME:
758 return true;
759
760 case SPDY_NOOP_FRAME:
761 case SPDY_CREDENTIAL_FRAME:
762 default:
763 return false;
764 }
765 }
766
767 private void fireInvalidControlFrameException(ChannelHandlerContext ctx) {
768 String message = "Received invalid control frame";
769 switch (type) {
770 case SPDY_SYN_STREAM_FRAME:
771 message = "Received invalid SYN_STREAM control frame";
772 break;
773
774 case SPDY_SYN_REPLY_FRAME:
775 message = "Received invalid SYN_REPLY control frame";
776 break;
777
778 case SPDY_RST_STREAM_FRAME:
779 message = "Received invalid RST_STREAM control frame";
780 break;
781
782 case SPDY_SETTINGS_FRAME:
783 message = "Received invalid SETTINGS control frame";
784 break;
785
786 case SPDY_NOOP_FRAME:
787 message = "Received invalid NOOP control frame";
788 break;
789
790 case SPDY_PING_FRAME:
791 message = "Received invalid PING control frame";
792 break;
793
794 case SPDY_GOAWAY_FRAME:
795 message = "Received invalid GOAWAY control frame";
796 break;
797
798 case SPDY_HEADERS_FRAME:
799 message = "Received invalid HEADERS control frame";
800 break;
801
802 case SPDY_WINDOW_UPDATE_FRAME:
803 message = "Received invalid WINDOW_UPDATE control frame";
804 break;
805
806 case SPDY_CREDENTIAL_FRAME:
807 message = "Received invalid CREDENTIAL control frame";
808 break;
809 }
810 fireProtocolException(ctx, message);
811 }
812
813 private static void fireProtocolException(ChannelHandlerContext ctx, String message) {
814 Channels.fireExceptionCaught(ctx, new SpdyProtocolException(message));
815 }
816 }