View Javadoc

    1   /*
    2    * Copyright 2012 The Netty Project
    3    *
    4    * The Netty Project licenses this file to you under the Apache License,
    5    * version 2.0 (the "License"); you may not use this file except in compliance
    6    * with the License. You may obtain a copy of the License at:
    7    *
    8    *   http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    13   * License for the specific language governing permissions and limitations
    14   * under the License.
    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   * Decodes {@link ChannelBuffer}s into SPDY Data and Control Frames.
    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      // SPDY common header fields
    44      private byte flags;
    45      private int length;
    46      private int version;
    47      private int type;
    48      private int streamID;
    49
    50      // Header block decoding fields
    51      private int headerSize;
    52      private int numHeaders;
    53      private ChannelBuffer decompressed;
    54
    55      private static 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       * Creates a new instance with the default {@code version (2)},
    68       * {@code maxChunkSize (8192)}, and {@code maxHeaderSize (16384)}.
    69       */
    70      @Deprecated
    71      public SpdyFrameDecoder() {
    72          this(2);
    73      }
    74
    75      /**
    76       * Creates a new instance with the specified {@code version} and the default
    77       * {@code maxChunkSize (8192)} and {@code maxHeaderSize (16384)}.
    78       */
    79      public SpdyFrameDecoder(int version) {
    80          this(version, 8192, 16384);
    81      }
    82
    83      /**
    84       * Creates a new instance with the specified parameters.
    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             // FrameDecoders must consume data when producing frames
    134             // All length 0 frames must be generated now
    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                 // There are no length 0 control frames
    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                 // Validate frame length against number of entries
    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                 // Each ID/Value entry is 8 bytes
    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                     // Chromium Issue 79156
    196                     // SPDY setting ids are not written in network byte order
    197                     // Read id assuming the architecture is little endian
    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                 // Check for invalid ID -- avoid IllegalArgumentException in setValue
    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             // Generate data frames that do not exceed maxChunkSize
    293             int dataLength = Math.min(maxChunkSize, length);
    294
    295             // Wait until entire frame is readable
    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         // Wait until entire header is readable
    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         // Read common header fields
    340         boolean control = (buffer.getByte(frameOffset) & 0x80) != 0;
    341         flags  = buffer.getByte(flagsOffset);
    342         length = getUnsignedMedium(buffer, lengthOffset);
    343
    344         if (control) {
    345             // Decode control frame common header
    346             version = getUnsignedShort(buffer, frameOffset) & 0x7FFF;
    347
    348             int typeOffset = frameOffset + SPDY_HEADER_TYPE_OFFSET;
    349             type = getUnsignedShort(buffer, typeOffset);
    350
    351             // Check version first then validity
    352             if (version != spdyVersion || !isValidControlFrameHeader()) {
    353                 return State.FRAME_ERROR;
    354             }
    355
    356             // Make sure decoder will produce a frame or consume input
    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             // Decode data frame common header
    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             // SPDY/2 requires 16-bits of padding for empty header blocks
    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             // SPDY/2 has 16-bits of unused space
    490             if (version < 3) {
    491                 buffer.skipBytes(2);
    492                 length -= 2;
    493             }
    494
    495             // SPDY/2 requires 16-bits of padding for empty header blocks
    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             // SPDY/2 allows length 4 frame when there are no name/value pairs
    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             // SPDY/2 has 16-bits of unused space
    521             if (version < 3 && length != 0) {
    522                 buffer.skipBytes(2);
    523                 length -= 2;
    524             }
    525
    526             // SPDY/2 requires 16-bits of padding for empty header blocks
    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         // Perhaps last call to decode filled output buffer
    547         headerBlockDecompressor.decode(decompressed);
    548         return decompressed.readableBytes() >= bytes;
    549     }
    550
    551     private int readLengthField() {
    552         if (version < 3) {
    553             return decompressed.readUnsignedShort();
    554         } else {
    555             return decompressed.readInt();
    556         }
    557     }
    558
    559     private void decodeHeaderBlock(ChannelBuffer buffer) throws Exception {
    560         if (decompressed == null) {
    561             // First time we start to decode a header block
    562             // Initialize header block decoding fields
    563             headerSize = 0;
    564             numHeaders = -1;
    565             decompressed = ChannelBuffers.dynamicBuffer(8192);
    566         }
    567
    568         // Accumulate decompressed data
    569         headerBlockDecompressor.setInput(buffer);
    570         headerBlockDecompressor.decode(decompressed);
    571
    572         if (spdyHeaderBlock == null) {
    573             // Only decompressing data to keep decompression context in sync
    574             decompressed = null;
    575             return;
    576         }
    577
    578         int lengthFieldSize = version < 3 ? 2 : 4;
    579
    580         if (numHeaders == -1) {
    581             // Read number of Name/Value pairs
    582             if (decompressed.readableBytes() < lengthFieldSize) {
    583                 return;
    584             }
    585             numHeaders = readLengthField();
    586             if (numHeaders < 0) {
    587                 spdyHeaderBlock.setInvalid();
    588                 return;
    589             }
    590         }
    591
    592         while (numHeaders > 0) {
    593             int headerSize = this.headerSize;
    594             decompressed.markReaderIndex();
    595
    596             // Try to read length of name
    597             if (!ensureBytes(lengthFieldSize)) {
    598                 decompressed.resetReaderIndex();
    599                 decompressed.discardReadBytes();
    600                 return;
    601             }
    602             int nameLength = readLengthField();
    603
    604             // Recipients of a zero-length name must issue a stream error
    605             if (nameLength <= 0) {
    606                 spdyHeaderBlock.setInvalid();
    607                 return;
    608             }
    609             headerSize += nameLength;
    610             if (headerSize > maxHeaderSize) {
    611                 throw new TooLongFrameException(
    612                         "Header block exceeds " + maxHeaderSize);
    613             }
    614
    615             // Try to read name
    616             if (!ensureBytes(nameLength)) {
    617                 decompressed.resetReaderIndex();
    618                 decompressed.discardReadBytes();
    619                 return;
    620             }
    621             byte[] nameBytes = new byte[nameLength];
    622             decompressed.readBytes(nameBytes);
    623             String name = new String(nameBytes, "UTF-8");
    624
    625             // Check for identically named headers
    626             if (spdyHeaderBlock.containsHeader(name)) {
    627                 spdyHeaderBlock.setInvalid();
    628                 return;
    629             }
    630
    631             // Try to read length of value
    632             if (!ensureBytes(lengthFieldSize)) {
    633                 decompressed.resetReaderIndex();
    634                 decompressed.discardReadBytes();
    635                 return;
    636             }
    637             int valueLength = readLengthField();
    638
    639             // Recipients of illegal value fields must issue a stream error
    640             if (valueLength <= 0) {
    641                 spdyHeaderBlock.setInvalid();
    642                 return;
    643             }
    644             headerSize += valueLength;
    645             if (headerSize > maxHeaderSize) {
    646                 throw new TooLongFrameException(
    647                         "Header block exceeds " + maxHeaderSize);
    648             }
    649
    650             // Try to read value
    651             if (!ensureBytes(valueLength)) {
    652                 decompressed.resetReaderIndex();
    653                 decompressed.discardReadBytes();
    654                 return;
    655             }
    656             byte[] valueBytes = new byte[valueLength];
    657             decompressed.readBytes(valueBytes);
    658
    659             // Add Name/Value pair to headers
    660             int index = 0;
    661             int offset = 0;
    662             while (index < valueLength) {
    663                 while (index < valueBytes.length && valueBytes[index] != (byte) 0) {
    664                     index ++;
    665                 }
    666                 if (index < valueBytes.length && valueBytes[index + 1] == (byte) 0) {
    667                     // Received multiple, in-sequence NULL characters
    668                     // Recipients of illegal value fields must issue a stream error
    669                     spdyHeaderBlock.setInvalid();
    670                     return;
    671                 }
    672                 String value = new String(valueBytes, offset, index - offset, "UTF-8");
    673
    674                 try {
    675                     spdyHeaderBlock.addHeader(name, value);
    676                 } catch (IllegalArgumentException e) {
    677                     // Name contains NULL or non-ascii characters
    678                     spdyHeaderBlock.setInvalid();
    679                     return;
    680                 }
    681                 index ++;
    682                 offset = index;
    683             }
    684             numHeaders --;
    685             this.headerSize = headerSize;
    686         }
    687         decompressed = null;
    688     }
    689
    690     private boolean isValidControlFrameHeader() {
    691         switch (type) {
    692         case SPDY_SYN_STREAM_FRAME:
    693             return version < 3 ? length >= 12 : length >= 10;
    694
    695         case SPDY_SYN_REPLY_FRAME:
    696             return version < 3 ? length >= 8 : length >= 4;
    697
    698         case SPDY_RST_STREAM_FRAME:
    699             return flags == 0 && length == 8;
    700
    701         case SPDY_SETTINGS_FRAME:
    702             return length >= 4;
    703
    704         case SPDY_NOOP_FRAME:
    705             return length == 0;
    706
    707         case SPDY_PING_FRAME:
    708             return length == 4;
    709
    710         case SPDY_GOAWAY_FRAME:
    711             return version < 3 ? length == 4 : length == 8;
    712
    713         case SPDY_HEADERS_FRAME:
    714             if (version < 3) {
    715                 return length == 4 || length >= 8;
    716             } else {
    717                 return length >= 4;
    718             }
    719
    720         case SPDY_WINDOW_UPDATE_FRAME:
    721             return length == 8;
    722
    723         case SPDY_CREDENTIAL_FRAME:
    724         default:
    725             return true;
    726         }
    727     }
    728
    729     private boolean willGenerateControlFrame() {
    730         switch (type) {
    731         case SPDY_SYN_STREAM_FRAME:
    732         case SPDY_SYN_REPLY_FRAME:
    733         case SPDY_RST_STREAM_FRAME:
    734         case SPDY_SETTINGS_FRAME:
    735         case SPDY_PING_FRAME:
    736         case SPDY_GOAWAY_FRAME:
    737         case SPDY_HEADERS_FRAME:
    738         case SPDY_WINDOW_UPDATE_FRAME:
    739             return true;
    740
    741         case SPDY_NOOP_FRAME:
    742         case SPDY_CREDENTIAL_FRAME:
    743         default:
    744             return false;
    745         }
    746     }
    747
    748     private void fireInvalidControlFrameException(ChannelHandlerContext ctx) {
    749         String message = "Received invalid control frame";
    750         switch (type) {
    751         case SPDY_SYN_STREAM_FRAME:
    752             message = "Received invalid SYN_STREAM control frame";
    753             break;
    754
    755         case SPDY_SYN_REPLY_FRAME:
    756             message = "Received invalid SYN_REPLY control frame";
    757             break;
    758
    759         case SPDY_RST_STREAM_FRAME:
    760             message = "Received invalid RST_STREAM control frame";
    761             break;
    762
    763         case SPDY_SETTINGS_FRAME:
    764             message = "Received invalid SETTINGS control frame";
    765             break;
    766
    767         case SPDY_NOOP_FRAME:
    768             message = "Received invalid NOOP control frame";
    769             break;
    770
    771         case SPDY_PING_FRAME:
    772             message = "Received invalid PING control frame";
    773             break;
    774
    775         case SPDY_GOAWAY_FRAME:
    776             message = "Received invalid GOAWAY control frame";
    777             break;
    778
    779         case SPDY_HEADERS_FRAME:
    780             message = "Received invalid HEADERS control frame";
    781             break;
    782
    783         case SPDY_WINDOW_UPDATE_FRAME:
    784             message = "Received invalid WINDOW_UPDATE control frame";
    785             break;
    786
    787         case SPDY_CREDENTIAL_FRAME:
    788             message = "Received invalid CREDENTIAL control frame";
    789             break;
    790         }
    791         fireProtocolException(ctx, message);
    792     }
    793
    794     private static void fireProtocolException(ChannelHandlerContext ctx, String message) {
    795         Channels.fireExceptionCaught(ctx, new SpdyProtocolException(message));
    796     }
    797 }