private void InsertHeader(IHeaderListener headerListener, string name, string value, HpackUtil.IndexType indexType) { AddHeader(headerListener, name, value, indexType == HpackUtil.IndexType.Never); switch (indexType) { case HpackUtil.IndexType.None: case HpackUtil.IndexType.Never: break; case HpackUtil.IndexType.Incremental: dynamicTable.Add(new HttpHeader(name, value)); break; default: throw new Exception("should not reach here"); } }
private void AddHeader(IHeaderListener headerListener, string name, string value, bool sensitive) { if (name.Length == 0) { throw new ArgumentException("name is empty"); } long newSize = headerSize + name.Length + value.Length; if (newSize <= maxHeaderSize) { headerListener.AddHeader(name, value, sensitive); headerSize = (int)newSize; } else { // truncation will be reported during endHeaderBlock headerSize = maxHeaderSize + 1; } }
/// <summary> /// Decode the header block into header fields. /// </summary> /// <param name="input">Input.</param> /// <param name="headerListener">Header listener.</param> public void Decode(BinaryReader input, IHeaderListener headerListener) { while(input.BaseStream.Length - input.BaseStream.Position > 0) { switch(this.state) { case State.READ_HEADER_REPRESENTATION: var b = input.ReadSByte(); if (maxDynamicTableSizeChangeRequired && (b & 0xE0) != 0x20) { // Encoder MUST signal maximum dynamic table size change throw new IOException("max dynamic table size change required"); } if (b < 0) { // Indexed Header Field this.index = b & 0x7F; if (this.index == 0) { throw new IOException("illegal index value (" + this.index + ")"); } else if (this.index == 0x7F) { this.state = State.READ_INDEXED_HEADER; } else { this.IndexHeader(this.index, headerListener); } } else if ((b & 0x40) == 0x40) { // Literal Header Field with Incremental Indexing this.indexType = HpackUtil.IndexType.INCREMENTAL; this.index = b & 0x3F; if (this.index == 0) { this.state = State.READ_LITERAL_HEADER_NAME_LENGTH_PREFIX; } else if (this.index == 0x3F) { this.state = State.READ_INDEXED_HEADER_NAME; } else { // Index was stored as the prefix this.ReadName(this.index); this.state = State.READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX; } } else if ((b & 0x20) == 0x20) { // Dynamic Table Size Update this.index = b & 0x1F; if (this.index == 0x1F) { this.state = State.READ_MAX_DYNAMIC_TABLE_SIZE; } else { this.SetDynamicTableSize(index); this.state = State.READ_HEADER_REPRESENTATION; } } else { // Literal Header Field without Indexing / never Indexed this.indexType = ((b & 0x10) == 0x10) ? HpackUtil.IndexType.NEVER : HpackUtil.IndexType.NONE; this.index = b & 0x0F; if (this.index == 0) { this.state = State.READ_LITERAL_HEADER_NAME_LENGTH_PREFIX; } else if (this.index == 0x0F) { this.state = State.READ_INDEXED_HEADER_NAME; } else { // Index was stored as the prefix this.ReadName(this.index); this.state = State.READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX; } } break; case State.READ_MAX_DYNAMIC_TABLE_SIZE: var maxSize = Decoder.DecodeULE128(input); if (maxSize == -1) { return; } // Check for numerical overflow if (maxSize > int.MaxValue - this.index) { throw new IOException("decompression failure"); } this.SetDynamicTableSize(this.index + maxSize); this.state = State.READ_HEADER_REPRESENTATION; break; case State.READ_INDEXED_HEADER: var headerIndex = Decoder.DecodeULE128(input); if (headerIndex == -1) { return; } // Check for numerical overflow if (headerIndex > int.MaxValue - this.index) { throw new IOException("decompression failure"); } this.IndexHeader(this.index + headerIndex, headerListener); this.state = State.READ_HEADER_REPRESENTATION; break; case State.READ_INDEXED_HEADER_NAME: // Header Name matches an entry in the Header Table var nameIndex = Decoder.DecodeULE128(input); if (nameIndex == -1) { return; } // Check for numerical overflow if (nameIndex > int.MaxValue - this.index) { throw new IOException("decompression failure"); } this.ReadName(this.index + nameIndex); this.state = State.READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX; break; case State.READ_LITERAL_HEADER_NAME_LENGTH_PREFIX: b = input.ReadSByte(); this.huffmanEncoded = (b & 0x80) == 0x80; this.index = b & 0x7F; if (this.index == 0x7f) { this.state = State.READ_LITERAL_HEADER_NAME_LENGTH; } else { this.nameLength = this.index; // Disallow empty names -- they cannot be represented in HTTP/1.x if (this.nameLength == 0) { throw new IOException("decompression failure"); } // Check name length against max header size if (this.ExceedsMaxHeaderSize(this.nameLength)) { if (this.indexType == HpackUtil.IndexType.NONE) { // Name is unused so skip bytes this.name = EMPTY; this.skipLength = this.nameLength; this.state = State.SKIP_LITERAL_HEADER_NAME; break; } // Check name length against max dynamic table size if (this.nameLength + HeaderField.HEADER_ENTRY_OVERHEAD > this.dynamicTable.Capacity) { this.dynamicTable.Clear(); this.name = EMPTY; this.skipLength = this.nameLength; this.state = State.SKIP_LITERAL_HEADER_NAME; break; } } this.state = State.READ_LITERAL_HEADER_NAME; } break; case State.READ_LITERAL_HEADER_NAME_LENGTH: // Header Name is a Literal String this.nameLength = Decoder.DecodeULE128(input); if (this.nameLength == -1) { return; } // Check for numerical overflow if (this.nameLength > int.MaxValue - this.index) { throw new IOException("decompression failure"); } this.nameLength += this.index; // Check name length against max header size if (this.ExceedsMaxHeaderSize(this.nameLength)) { if (this.indexType == HpackUtil.IndexType.NONE) { // Name is unused so skip bytes this.name = EMPTY; this.skipLength = this.nameLength; this.state = State.SKIP_LITERAL_HEADER_NAME; break; } // Check name length against max dynamic table size if (this.nameLength + HeaderField.HEADER_ENTRY_OVERHEAD > this.dynamicTable.Capacity) { this.dynamicTable.Clear(); this.name = EMPTY; this.skipLength = this.nameLength; this.state = State.SKIP_LITERAL_HEADER_NAME; break; } } this.state = State.READ_LITERAL_HEADER_NAME; break; case State.READ_LITERAL_HEADER_NAME: // Wait until entire name is readable if (input.BaseStream.Length - input.BaseStream.Position < this.nameLength) { return; } this.name = this.ReadStringLiteral(input, this.nameLength); this.state = State.READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX; break; case State.SKIP_LITERAL_HEADER_NAME: this.skipLength -= (int)input.BaseStream.Seek(this.skipLength, SeekOrigin.Current); if (this.skipLength < 0) { this.skipLength = 0; } if (this.skipLength == 0) { this.state = State.READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX; } break; case State.READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX: b = input.ReadSByte(); this.huffmanEncoded = (b & 0x80) == 0x80; this.index = b & 0x7F; if (this.index == 0x7f) { this.state = State.READ_LITERAL_HEADER_VALUE_LENGTH; } else { this.valueLength = this.index; // Check new header size against max header size var newHeaderSize1 = (long)((long)this.nameLength + (long)this.valueLength); if (this.ExceedsMaxHeaderSize(newHeaderSize1)) { // truncation will be reported during endHeaderBlock this.headerSize = this.maxHeaderSize + 1; if (this.indexType == HpackUtil.IndexType.NONE) { // Value is unused so skip bytes this.state = State.SKIP_LITERAL_HEADER_VALUE; break; } // Check new header size against max dynamic table size if (newHeaderSize1 + HeaderField.HEADER_ENTRY_OVERHEAD > this.dynamicTable.Capacity) { this.dynamicTable.Clear(); this.state = State.SKIP_LITERAL_HEADER_VALUE; break; } } if (this.valueLength == 0) { this.InsertHeader(headerListener, this.name, EMPTY, this.indexType); this.state = State.READ_HEADER_REPRESENTATION; } else { this.state = State.READ_LITERAL_HEADER_VALUE; } } break; case State.READ_LITERAL_HEADER_VALUE_LENGTH: // Header Value is a Literal String this.valueLength = Decoder.DecodeULE128(input); if (this.valueLength == -1) { return; } // Check for numerical overflow if (this.valueLength > int.MaxValue - this.index) { throw new IOException("decompression failure"); } this.valueLength += this.index; // Check new header size against max header size var newHeaderSize2 = (long)((long)this.nameLength + (long)this.valueLength); if (newHeaderSize2 + this.headerSize > this.maxHeaderSize) { // truncation will be reported during endHeaderBlock this.headerSize = this.maxHeaderSize + 1; if (this.indexType == HpackUtil.IndexType.NONE) { // Value is unused so skip bytes this.state = State.SKIP_LITERAL_HEADER_VALUE; break; } // Check new header size against max dynamic table size if (newHeaderSize2 + HeaderField.HEADER_ENTRY_OVERHEAD > this.dynamicTable.Capacity) { this.dynamicTable.Clear(); this.state = State.SKIP_LITERAL_HEADER_VALUE; break; } } this.state = State.READ_LITERAL_HEADER_VALUE; break; case State.READ_LITERAL_HEADER_VALUE: // Wait until entire value is readable if (input.BaseStream.Length - input.BaseStream.Position < this.valueLength) { return; } var value = this.ReadStringLiteral(input, this.valueLength); this.InsertHeader(headerListener, this.name, value, this.indexType); this.state = State.READ_HEADER_REPRESENTATION; break; case State.SKIP_LITERAL_HEADER_VALUE: this.valueLength -= (int)input.BaseStream.Seek(this.valueLength, SeekOrigin.Current); if (this.valueLength < 0) { this.valueLength = 0; } if (this.valueLength == 0) { this.state = State.READ_HEADER_REPRESENTATION; } break; default: throw new Exception("should not reach here"); } } }
private void InsertHeader(IHeaderListener headerListener, byte[] name, byte[] value, HpackUtil.IndexType indexType) { this.AddHeader(headerListener, name, value, indexType == HpackUtil.IndexType.NEVER); switch(indexType) { case HpackUtil.IndexType.NONE: case HpackUtil.IndexType.NEVER: break; case HpackUtil.IndexType.INCREMENTAL: this.dynamicTable.Add(new HeaderField(name, value)); break; default: throw new Exception("should not reach here"); } }
private void IndexHeader(int index, IHeaderListener headerListener) { if (index <= StaticTable.Length) { var headerField = StaticTable.GetEntry(index); this.AddHeader(headerListener, headerField.Name, headerField.Value, false); } else if (index - StaticTable.Length <= this.dynamicTable.Length()) { var headerField = this.dynamicTable.GetEntry(index - StaticTable.Length); this.AddHeader(headerListener, headerField.Name, headerField.Value, false); } else { throw new IOException("illegal index value (" + index + ")"); } }
private void AddHeader(IHeaderListener headerListener, byte[] name, byte[] value, bool sensitive) { if (name.Length == 0) { throw new ArgumentException("name is empty"); } var newSize = (long)(this.headerSize + name.Length + value.Length); if (newSize <= this.maxHeaderSize) { headerListener.AddHeader(name, value, sensitive); this.headerSize = (int)newSize; } else { // truncation will be reported during endHeaderBlock this.headerSize = this.maxHeaderSize + 1; } }
/// <summary> /// Decode the header block into header fields. /// </summary> /// <param name="input">Input.</param> /// <param name="headerListener">Header listener.</param> public void Decode(BinaryReader input, IHeaderListener headerListener) { while (input.BaseStream.Length - input.BaseStream.Position > 0) { switch (state) { case State.ReadHeaderRepresentation: sbyte b = input.ReadSByte(); if (maxDynamicTableSizeChangeRequired && (b & 0xE0) != 0x20) { // Encoder MUST signal maximum dynamic table size change throw new IOException("max dynamic table size change required"); } if (b < 0) { // Indexed Header Field index = b & 0x7F; if (index == 0) { throw new IOException("illegal index value (" + index + ")"); } else if (index == 0x7F) { state = State.ReadIndexedHeader; } else { IndexHeader(index, headerListener); } } else if ((b & 0x40) == 0x40) { // Literal Header Field with Incremental Indexing indexType = HpackUtil.IndexType.Incremental; index = b & 0x3F; if (index == 0) { state = State.ReadLiteralHeaderNameLengthPrefix; } else if (index == 0x3F) { state = State.ReadIndexedHeaderName; } else { // Index was stored as the prefix ReadName(index); state = State.ReadLiteralHeaderValueLengthPrefix; } } else if ((b & 0x20) == 0x20) { // Dynamic Table Size Update index = b & 0x1F; if (index == 0x1F) { state = State.ReadMaxDynamicTableSize; } else { SetDynamicTableSize(index); state = State.ReadHeaderRepresentation; } } else { // Literal Header Field without Indexing / never Indexed indexType = ((b & 0x10) == 0x10) ? HpackUtil.IndexType.Never : HpackUtil.IndexType.None; index = b & 0x0F; if (index == 0) { state = State.ReadLiteralHeaderNameLengthPrefix; } else if (index == 0x0F) { state = State.ReadIndexedHeaderName; } else { // Index was stored as the prefix ReadName(index); state = State.ReadLiteralHeaderValueLengthPrefix; } } break; case State.ReadMaxDynamicTableSize: int maxSize = decodeULE128(input); if (maxSize == -1) { return; } // Check for numerical overflow if (maxSize > int.MaxValue - index) { throw new IOException("decompression failure"); } SetDynamicTableSize(index + maxSize); state = State.ReadHeaderRepresentation; break; case State.ReadIndexedHeader: int headerIndex = decodeULE128(input); if (headerIndex == -1) { return; } // Check for numerical overflow if (headerIndex > int.MaxValue - index) { throw new IOException("decompression failure"); } IndexHeader(index + headerIndex, headerListener); state = State.ReadHeaderRepresentation; break; case State.ReadIndexedHeaderName: // Header Name matches an entry in the Header Table int nameIndex = decodeULE128(input); if (nameIndex == -1) { return; } // Check for numerical overflow if (nameIndex > int.MaxValue - index) { throw new IOException("decompression failure"); } ReadName(index + nameIndex); state = State.ReadLiteralHeaderValueLengthPrefix; break; case State.ReadLiteralHeaderNameLengthPrefix: b = input.ReadSByte(); huffmanEncoded = (b & 0x80) == 0x80; index = b & 0x7F; if (index == 0x7f) { state = State.ReadLiteralHeaderNameLength; } else { nameLength = index; // Disallow empty names -- they cannot be represented in HTTP/1.x if (nameLength == 0) { throw new IOException("decompression failure"); } // Check name length against max header size if (ExceedsMaxHeaderSize(nameLength)) { if (indexType == HpackUtil.IndexType.None) { // Name is unused so skip bytes name = string.Empty; skipLength = nameLength; state = State.SkipLiteralHeaderName; break; } // Check name length against max dynamic table size if (nameLength + HttpHeader.HttpHeaderOverhead > dynamicTable.Capacity) { dynamicTable.Clear(); name = string.Empty; skipLength = nameLength; state = State.SkipLiteralHeaderName; break; } } state = State.ReadLiteralHeaderName; } break; case State.ReadLiteralHeaderNameLength: // Header Name is a Literal String nameLength = decodeULE128(input); if (nameLength == -1) { return; } // Check for numerical overflow if (nameLength > int.MaxValue - index) { throw new IOException("decompression failure"); } nameLength += index; // Check name length against max header size if (ExceedsMaxHeaderSize(nameLength)) { if (indexType == HpackUtil.IndexType.None) { // Name is unused so skip bytes name = string.Empty; skipLength = nameLength; state = State.SkipLiteralHeaderName; break; } // Check name length against max dynamic table size if (nameLength + HttpHeader.HttpHeaderOverhead > dynamicTable.Capacity) { dynamicTable.Clear(); name = string.Empty; skipLength = nameLength; state = State.SkipLiteralHeaderName; break; } } state = State.ReadLiteralHeaderName; break; case State.ReadLiteralHeaderName: // Wait until entire name is readable if (input.BaseStream.Length - input.BaseStream.Position < nameLength) { return; } name = ReadStringLiteral(input, nameLength); state = State.ReadLiteralHeaderValueLengthPrefix; break; case State.SkipLiteralHeaderName: skipLength -= (int)input.BaseStream.Seek(skipLength, SeekOrigin.Current); if (skipLength < 0) { skipLength = 0; } if (skipLength == 0) { state = State.ReadLiteralHeaderValueLengthPrefix; } break; case State.ReadLiteralHeaderValueLengthPrefix: b = input.ReadSByte(); huffmanEncoded = (b & 0x80) == 0x80; index = b & 0x7F; if (index == 0x7f) { state = State.ReadLiteralHeaderValueLength; } else { valueLength = index; // Check new header size against max header size long newHeaderSize1 = (long)nameLength + valueLength; if (ExceedsMaxHeaderSize(newHeaderSize1)) { // truncation will be reported during endHeaderBlock headerSize = maxHeaderSize + 1; if (indexType == HpackUtil.IndexType.None) { // Value is unused so skip bytes state = State.SkipLiteralHeaderValue; break; } // Check new header size against max dynamic table size if (newHeaderSize1 + HttpHeader.HttpHeaderOverhead > dynamicTable.Capacity) { dynamicTable.Clear(); state = State.SkipLiteralHeaderValue; break; } } if (valueLength == 0) { InsertHeader(headerListener, name, string.Empty, indexType); state = State.ReadHeaderRepresentation; } else { state = State.ReadLiteralHeaderValue; } } break; case State.ReadLiteralHeaderValueLength: // Header Value is a Literal String valueLength = decodeULE128(input); if (valueLength == -1) { return; } // Check for numerical overflow if (valueLength > int.MaxValue - index) { throw new IOException("decompression failure"); } valueLength += index; // Check new header size against max header size long newHeaderSize2 = (long)nameLength + valueLength; if (newHeaderSize2 + headerSize > maxHeaderSize) { // truncation will be reported during endHeaderBlock headerSize = maxHeaderSize + 1; if (indexType == HpackUtil.IndexType.None) { // Value is unused so skip bytes state = State.SkipLiteralHeaderValue; break; } // Check new header size against max dynamic table size if (newHeaderSize2 + HttpHeader.HttpHeaderOverhead > dynamicTable.Capacity) { dynamicTable.Clear(); state = State.SkipLiteralHeaderValue; break; } } state = State.ReadLiteralHeaderValue; break; case State.ReadLiteralHeaderValue: // Wait until entire value is readable if (input.BaseStream.Length - input.BaseStream.Position < valueLength) { return; } var value = ReadStringLiteral(input, valueLength); InsertHeader(headerListener, name, value, indexType); state = State.ReadHeaderRepresentation; break; case State.SkipLiteralHeaderValue: valueLength -= (int)input.BaseStream.Seek(valueLength, SeekOrigin.Current); if (valueLength < 0) { valueLength = 0; } if (valueLength == 0) { state = State.ReadHeaderRepresentation; } break; default: throw new Exception("should not reach here"); } } }
private void IndexHeader(int index, IHeaderListener headerListener) { var headerField = GetHeaderField(index); AddHeader(headerListener, headerField.Name, headerField.Value, false); }
public void SetUp() { this.decoder = new Decoder(MAX_HEADER_SIZE, MAX_HEADER_TABLE_SIZE); //this.mockListener = mock(HeaderListener.class); this.mockListener = new HeaderListener(); }