public HPackEncoder Encode(string name, string value, HPackFlags flags = HPackFlags.None) { if (name == null) { throw new ArgumentNullException(nameof(name)); } if (value == null) { throw new ArgumentNullException(nameof(value)); } int index = 0; switch (flags & IndexingMask) { case HPackFlags.WithoutIndexing: if (TryGetIndex(name, value, out index)) { return(FinishWrite(EncodeHeader(index, _buffer.Span.Slice(_bufferConsumed)))); } if (TryGetIndex(name, out index)) { name = null; } break; case HPackFlags.NewIndexed: TableEntry newEntry = new TableEntry(name, value); if (TryGetIndex(name, out index)) { name = null; } AddDynamicEntry(newEntry); break; } return(FinishWrite(EncodeHeaderImpl(index, name, value, flags, _buffer.Span.Slice(_bufferConsumed)))); }
private static int EncodeHeaderImpl(int nameIdx, string name, string value, HPackFlags flags, Span <byte> headerBlock) { const HPackFlags IndexingMask = HPackFlags.NeverIndexed | HPackFlags.NewIndexed | HPackFlags.WithoutIndexing; Debug.Assert((nameIdx != 0) != (name != null), $"Only one of {nameof(nameIdx)} or {nameof(name)} can be used."); Debug.Assert(name != null || (flags & HPackFlags.HuffmanEncodeName) == 0, "An indexed name can not be huffman encoded."); byte prefix, prefixMask; switch (flags & IndexingMask) { case HPackFlags.WithoutIndexing: prefix = 0; prefixMask = 0b11110000; break; case HPackFlags.NewIndexed: prefix = 0b01000000; prefixMask = 0b11000000; break; case HPackFlags.NeverIndexed: prefix = 0b00010000; prefixMask = 0b11110000; break; default: throw new Exception("invalid indexing flag"); } int bytesGenerated = EncodeInteger(nameIdx, prefix, prefixMask, headerBlock); if (name != null) { bytesGenerated += EncodeString(name, headerBlock.Slice(bytesGenerated), (flags & HPackFlags.HuffmanEncodeName) != 0); } bytesGenerated += EncodeString(value, headerBlock.Slice(bytesGenerated), (flags & HPackFlags.HuffmanEncodeValue) != 0); return(bytesGenerated); }
/// <summary> /// Encodes a header using a literal name and value. /// </summary> /// <param name="name">A literal name to encode for this header.</param> /// <param name="value">A literal value to encode for this header.</param> /// <param name="headerBlock">A span to write the encoded header to.</param> /// <returns>The number of bytes written to <paramref name="headerBlock"/>.</returns> public static int EncodeHeader(string name, string value, HPackFlags flags, Span <byte> headerBlock) { return(EncodeHeaderImpl(0, name, value, flags, headerBlock)); }
/// <summary> /// Encodes a header using an indexed name and literal value. /// </summary> /// <param name="nameIdx">An index of a header containing the name for this header.</param> /// <param name="value">A literal value to encode for this header.</param> /// <param name="headerBlock">A span to write the encoded header to.</param> /// <returns>The number of bytes written to <paramref name="headerBlock"/>.</returns> public static int EncodeHeader(int nameIdx, string value, HPackFlags flags, Span <byte> headerBlock) { Debug.Assert(nameIdx > 0); return(EncodeHeaderImpl(nameIdx, null, value, flags, headerBlock)); }
static void GenerateSeeds() { Random rng = new Random(0); HPackEncoder.TableEntry[] staticIndexedHeaders = HPackEncoder.s_staticTable.Where(x => x.Value.Length != 0).ToArray(); string[] staticIndexedNames = HPackEncoder.s_staticTable.Select(x => x.Name).Distinct().ToArray(); int seedLen = 1024; for (int i = 0; i < 10; ++i) { Console.WriteLine($"generating seed {i}"); seedLen += rng.Next(1024, 10240); GenerateSeed(enc => { enc.EncodeNewDynamicTableSize(4096); while (enc.BytesWritten < seedLen) { string name, value; HPackFlags flags = HPackFlags.None; int type = rng.Next(5); switch (type) { case 0: Console.WriteLine("fully indexed, static."); HPackEncoder.TableEntry e = staticIndexedHeaders[rng.Next(staticIndexedHeaders.Length)]; (name, value) = (e.Name, e.Value); break; case 1: if (enc.DynamicTableCount == 0 || rng.Next(5) == 0) { Console.WriteLine("new dynamic index."); name = GenerateName(); value = GenerateValue(); flags = HPackFlags.NewIndexed; } else { Console.WriteLine("fully indexed, dynamic."); e = enc.DynamicTable.ElementAt(rng.Next(enc.DynamicTableCount)); (name, value) = (e.Name, e.Value); } break; case 2: Console.WriteLine("indexed name, static."); name = staticIndexedNames[rng.Next(staticIndexedNames.Length)]; value = GenerateValue(); break; case 3: if (enc.DynamicTableCount == 0 || rng.Next(5) == 0) { Console.WriteLine("new dynamic index."); name = GenerateName(); value = GenerateValue(); flags = HPackFlags.NewIndexed; } else { Console.WriteLine("indexed name, dynamic."); e = enc.DynamicTable.ElementAt(rng.Next(enc.DynamicTableCount)); name = e.Name; value = GenerateValue(); } break; case 4: // literal name. Console.WriteLine("literal name."); name = GenerateName(); value = GenerateValue(); break; default: throw new Exception("should never be reached A"); } Debug.Assert(name != null); Debug.Assert(value != null); if (flags != HPackFlags.NewIndexed) { switch (rng.Next(3)) { case 0: flags |= HPackFlags.WithoutIndexing; break; case 1: flags |= HPackFlags.NewIndexed; break; case 2: flags |= HPackFlags.NeverIndexed; break; default: throw new Exception("should never be reached B"); } } if (rng.Next(2) == 0) { flags |= HPackFlags.HuffmanEncodeName; } if (rng.Next(2) == 0) { flags |= HPackFlags.HuffmanEncodeValue; } enc.Encode(name, value, flags); } }); } string GenerateName() => GenerateString(rng.Next(4, 16)); string GenerateValue() => GenerateString(rng.Next(4, 64)); string GenerateString(int len) { char[] buffer = new char[len]; for (int i = 0; i < len; ++i) { buffer[i] = (char)('a' + rng.Next(0, 26)); } return(new string(buffer)); } }