private State _state = State.Ready;
private byte[]? _headerName;
+ private int _headerStaticIndex;
private int _stringIndex;
private int _stringLength;
private int _headerNameLength;
private void ProcessHeaderValue(ReadOnlySpan<byte> data, IHttpHeadersHandler handler)
{
- ReadOnlySpan<byte> headerNameSpan = _headerNameRange == null
- ? new Span<byte>(_headerName, 0, _headerNameLength)
- : data.Slice(_headerNameRange.GetValueOrDefault().start, _headerNameRange.GetValueOrDefault().length);
-
ReadOnlySpan<byte> headerValueSpan = _headerValueRange == null
- ? new Span<byte>(_headerValueOctets, 0, _headerValueLength)
+ ? _headerValueOctets.AsSpan(0, _headerValueLength)
: data.Slice(_headerValueRange.GetValueOrDefault().start, _headerValueRange.GetValueOrDefault().length);
- handler.OnHeader(headerNameSpan, headerValueSpan);
-
- _headerNameRange = null;
- _headerValueRange = null;
+ if (_headerStaticIndex > 0)
+ {
+ handler.OnStaticIndexedHeader(_headerStaticIndex, headerValueSpan);
- if (_index)
+ if (_index)
+ {
+ _dynamicTable.Insert(H2StaticTable.Get(_headerStaticIndex - 1).Name, headerValueSpan);
+ }
+ }
+ else
{
- _dynamicTable.Insert(headerNameSpan, headerValueSpan);
+ ReadOnlySpan<byte> headerNameSpan = _headerNameRange == null
+ ? _headerName.AsSpan(0, _headerNameLength)
+ : data.Slice(_headerNameRange.GetValueOrDefault().start, _headerNameRange.GetValueOrDefault().length);
+
+ handler.OnHeader(headerNameSpan, headerValueSpan);
+
+ if (_index)
+ {
+ _dynamicTable.Insert(headerNameSpan, headerValueSpan);
+ }
}
+
+ _headerStaticIndex = 0;
+ _headerNameRange = null;
+ _headerValueRange = null;
}
public void CompleteDecode()
private void OnIndexedHeaderField(int index, IHttpHeadersHandler handler)
{
- ref readonly HeaderField header = ref GetHeader(index);
- handler.OnHeader(header.Name, header.Value);
+ if (index <= H2StaticTable.Count)
+ {
+ handler.OnStaticIndexedHeader(index);
+ }
+ else
+ {
+ ref readonly HeaderField header = ref GetDynamicHeader(index);
+ handler.OnHeader(header.Name, header.Value);
+ }
+
_state = State.Ready;
}
private void OnIndexedHeaderName(int index)
{
- _headerName = GetHeader(index).Name;
- _headerNameLength = _headerName.Length;
+ if (index <= H2StaticTable.Count)
+ {
+ _headerStaticIndex = index;
+ }
+ else
+ {
+ _headerName = GetDynamicHeader(index).Name;
+ _headerNameLength = _headerName.Length;
+ }
_state = State.HeaderValueLength;
}
return (b & HuffmanMask) != 0;
}
- private ref readonly HeaderField GetHeader(int index)
+ private ref readonly HeaderField GetDynamicHeader(int index)
{
try
{
- return ref index <= H2StaticTable.Count
- ? ref H2StaticTable.Get(index - 1)
- : ref _dynamicTable[index - H2StaticTable.Count - 1];
+ return ref _dynamicTable[index - H2StaticTable.Count - 1];
}
catch (IndexOutOfRangeException)
{
}
[Fact]
- public void DecodesIndexedHeaderField_StaticTable()
+ public void DecodesIndexedHeaderField_StaticTableWithValue()
{
_decoder.Decode(_indexedHeaderStatic, endHeaders: true, handler: _handler);
Assert.Equal("GET", _handler.DecodedHeaders[":method"]);
+
+ Assert.Equal(":method", _handler.DecodedStaticHeaders[H2StaticTable.MethodGet].Key);
+ Assert.Equal("GET", _handler.DecodedStaticHeaders[H2StaticTable.MethodGet].Value);
+ }
+
+ [Fact]
+ public void DecodesIndexedHeaderField_StaticTableWithoutValue()
+ {
+ byte[] encoded = _literalHeaderFieldWithIndexingIndexedName
+ .Concat(_headerValue)
+ .ToArray();
+
+ _decoder.Decode(encoded, endHeaders: true, handler: _handler);
+ Assert.Equal(_headerValueString, _handler.DecodedHeaders[_userAgentString]);
+
+ Assert.Equal(_userAgentString, _handler.DecodedStaticHeaders[H2StaticTable.UserAgent].Key);
+ Assert.Equal(_headerValueString, _handler.DecodedStaticHeaders[H2StaticTable.UserAgent].Value);
}
[Fact]
public class TestHttpHeadersHandler : IHttpHeadersHandler
{
public Dictionary<string, string> DecodedHeaders { get; } = new Dictionary<string, string>();
+ public Dictionary<int, KeyValuePair<string, string>> DecodedStaticHeaders { get; } = new Dictionary<int, KeyValuePair<string, string>>();
void IHttpHeadersHandler.OnHeader(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
{
void IHttpHeadersHandler.OnStaticIndexedHeader(int index)
{
- // Not yet implemented for HPACK.
- throw new NotImplementedException();
+ ref readonly HeaderField entry = ref H2StaticTable.Get(index - 1);
+ ((IHttpHeadersHandler)this).OnHeader(entry.Name, entry.Value);
+ DecodedStaticHeaders[index] = new KeyValuePair<string, string>(Encoding.ASCII.GetString(entry.Name), Encoding.ASCII.GetString(entry.Value));
}
void IHttpHeadersHandler.OnStaticIndexedHeader(int index, ReadOnlySpan<byte> value)
{
- // Not yet implemented for HPACK.
- throw new NotImplementedException();
+ byte[] name = H2StaticTable.Get(index - 1).Name;
+ ((IHttpHeadersHandler)this).OnHeader(name, value);
+ DecodedStaticHeaders[index] = new KeyValuePair<string, string>(Encoding.ASCII.GetString(name), Encoding.ASCII.GetString(value));
}
void IHttpHeadersHandler.OnHeadersComplete(bool endStream) { }
using System.Diagnostics;
using System.IO;
using System.Net.Http.Headers;
+using System.Net.Http.HPack;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Text;
void IHttpHeadersHandler.OnStaticIndexedHeader(int index)
{
// TODO: https://github.com/dotnet/runtime/issues/1505
- Debug.Fail("Currently unused by HPACK, this should never be called.");
+ ref readonly HeaderField entry = ref H2StaticTable.Get(index - 1);
+ OnHeader(entry.Name, entry.Value);
}
void IHttpHeadersHandler.OnStaticIndexedHeader(int index, ReadOnlySpan<byte> value)
{
// TODO: https://github.com/dotnet/runtime/issues/1505
- Debug.Fail("Currently unused by HPACK, this should never be called.");
+ OnHeader(H2StaticTable.Get(index - 1).Name, value);
}
public void OnHeader(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
public void OnStaticIndexedHeader(int index)
{
- throw new NotImplementedException();
+ ref readonly HeaderField entry = ref H2StaticTable.Get(index - 1);
+ OnHeader(entry.Name, entry.Value);
}
public void OnStaticIndexedHeader(int index, ReadOnlySpan<byte> value)
{
- throw new NotImplementedException();
+ OnHeader(H2StaticTable.Get(index - 1).Name, value);
}
}
}