public void BeginEncodeHeaders_CacheControlPrivate_NewIndexValue() { Span <byte> buffer = new byte[1024 * 16]; var headers = (IHeaderDictionary) new HttpResponseHeaders(); headers.CacheControl = "private"; var enumerator = new Http2HeadersEnumerator(); enumerator.Initialize(headers); var hpackEncoder = new DynamicHPackEncoder(); Assert.True(HPackHeaderWriter.BeginEncodeHeaders(302, hpackEncoder, enumerator, buffer, out var length)); var result = buffer.Slice(5, length - 5).ToArray(); var hex = BitConverter.ToString(result); Assert.Equal("58-07-70-72-69-76-61-74-65", hex); var statusHeader = GetHeaderEntry(hpackEncoder, 0); Assert.Equal("Cache-Control", statusHeader.Name); Assert.Equal("private", statusHeader.Value); }
public void BeginEncodeHeaders_MaxHeaderTableSizeUpdated_SizeUpdateInHeaders() { Span <byte> buffer = new byte[1024 * 16]; var hpackEncoder = new DynamicHPackEncoder(); hpackEncoder.UpdateMaxHeaderTableSize(100); var enumerator = new Http2HeadersEnumerator(); // First request enumerator.Initialize(new Dictionary <string, StringValues>()); Assert.True(HPackHeaderWriter.BeginEncodeHeaders(hpackEncoder, enumerator, buffer, out var length)); Assert.Equal(2, length); const byte DynamicTableSizeUpdateMask = 0xe0; var integerDecoder = new IntegerDecoder(); Assert.False(integerDecoder.BeginTryDecode((byte)(buffer[0] & ~DynamicTableSizeUpdateMask), prefixLength: 5, out _)); Assert.True(integerDecoder.TryDecode(buffer[1], out var result)); Assert.Equal(100, result); // Second request enumerator.Initialize(new Dictionary <string, StringValues>()); Assert.True(HPackHeaderWriter.BeginEncodeHeaders(hpackEncoder, enumerator, buffer, out length)); Assert.Equal(0, length); }
public void BeginEncodeHeaders_ExcludedHeaders_NotAddedToTable(string headerName, bool neverIndex) { Span <byte> buffer = new byte[1024 * 16]; var headers = new HttpResponseHeaders(); headers.Append(headerName, "1"); var enumerator = new Http2HeadersEnumerator(); enumerator.Initialize(headers); var hpackEncoder = new DynamicHPackEncoder(maxHeaderTableSize: Http2PeerSettings.DefaultHeaderTableSize); Assert.True(HPackHeaderWriter.BeginEncodeHeaders(hpackEncoder, enumerator, buffer, out _)); if (neverIndex) { Assert.Equal(0x10, buffer[0] & 0x10); } else { Assert.Equal(0, buffer[0] & 0x40); } Assert.Empty(GetHeaderEntries(hpackEncoder)); }
public void Initialize_ChangeHeadersSource_EnumeratorUsesNewSource() { var responseHeaders = new HttpResponseHeaders(); responseHeaders.Append("Name1", "Value1"); responseHeaders.Append("Name2", "Value2-1"); responseHeaders.Append("Name2", "Value2-2"); var e = new Http2HeadersEnumerator(); e.Initialize(responseHeaders); Assert.True(e.MoveNext()); Assert.Equal("Name1", e.Current.Key); Assert.Equal("Value1", e.Current.Value); Assert.Equal(-1, e.HPackStaticTableId); Assert.True(e.MoveNext()); Assert.Equal("Name2", e.Current.Key); Assert.Equal("Value2-1", e.Current.Value); Assert.Equal(-1, e.HPackStaticTableId); Assert.True(e.MoveNext()); Assert.Equal("Name2", e.Current.Key); Assert.Equal("Value2-2", e.Current.Value); Assert.Equal(-1, e.HPackStaticTableId); var responseTrailers = new HttpResponseTrailers { HeaderGrpcStatus = "1" }; responseTrailers.Append("Name1", "Value1"); responseTrailers.Append("Name2", "Value2-1"); responseTrailers.Append("Name2", "Value2-2"); e.Initialize(responseTrailers); Assert.True(e.MoveNext()); Assert.Equal("Grpc-Status", e.Current.Key); Assert.Equal("1", e.Current.Value); Assert.Equal(-1, e.HPackStaticTableId); Assert.True(e.MoveNext()); Assert.Equal("Name1", e.Current.Key); Assert.Equal("Value1", e.Current.Value); Assert.Equal(-1, e.HPackStaticTableId); Assert.True(e.MoveNext()); Assert.Equal("Name2", e.Current.Key); Assert.Equal("Value2-1", e.Current.Value); Assert.Equal(-1, e.HPackStaticTableId); Assert.True(e.MoveNext()); Assert.Equal("Name2", e.Current.Key); Assert.Equal("Value2-2", e.Current.Value); Assert.Equal(-1, e.HPackStaticTableId); Assert.False(e.MoveNext()); }
public void CanIterateOverResponseTrailers() { var responseTrailers = new HttpResponseTrailers { ContentLength = 9, HeaderETag = "ETag!" }; responseTrailers.Append("Name1", "Value1"); responseTrailers.Append("Name2", "Value2-1"); responseTrailers.Append("Name2", "Value2-2"); responseTrailers.Append("Name3", "Value3"); var e = new Http2HeadersEnumerator(); e.Initialize(responseTrailers); var headers = GetNormalizedHeaders(e); Assert.Equal(new[] { new KeyValuePair <string, string>("ETag", "ETag!"), new KeyValuePair <string, string>("Name1", "Value1"), new KeyValuePair <string, string>("Name2", "Value2-1"), new KeyValuePair <string, string>("Name2", "Value2-2"), new KeyValuePair <string, string>("Name3", "Value3"), }, headers); }
public void CanIterateOverResponseTrailers() { var responseTrailers = new HttpResponseTrailers { ContentLength = 9, HeaderETag = "ETag!" }; responseTrailers.Append("Name1", "Value1"); responseTrailers.Append("Name2", "Value2-1"); responseTrailers.Append("Name2", "Value2-2"); responseTrailers.Append("Name3", "Value3"); var e = new Http2HeadersEnumerator(); e.Initialize(responseTrailers); var headers = GetNormalizedHeaders(e); Assert.Equal(new[] { CreateHeaderResult(H2StaticTable.ETag, "ETag", "ETag!"), CreateHeaderResult(-1, "Name1", "Value1"), CreateHeaderResult(-1, "Name2", "Value2-1"), CreateHeaderResult(-1, "Name2", "Value2-2"), CreateHeaderResult(-1, "Name3", "Value3"), }, headers); }
public void GlobalSetup() { _knownSingleValueResponseHeaders = new HttpResponseHeaders { HeaderServer = "Value", HeaderDate = "Value", HeaderContentType = "Value", HeaderSetCookie = "Value" }; _knownMultipleValueResponseHeaders = new HttpResponseHeaders { HeaderServer = new StringValues(new[] { "One", "Two" }), HeaderDate = new StringValues(new[] { "One", "Two" }), HeaderContentType = new StringValues(new[] { "One", "Two" }), HeaderSetCookie = new StringValues(new[] { "One", "Two" }) }; _unknownSingleValueResponseHeaders = new HttpResponseHeaders(); _unknownSingleValueResponseHeaders.Append("One", "Value"); _unknownSingleValueResponseHeaders.Append("Two", "Value"); _unknownSingleValueResponseHeaders.Append("Three", "Value"); _unknownSingleValueResponseHeaders.Append("Four", "Value"); _unknownMultipleValueResponseHeaders = new HttpResponseHeaders(); _unknownMultipleValueResponseHeaders.Append("One", new StringValues(new[] { "One", "Two" })); _unknownMultipleValueResponseHeaders.Append("Two", new StringValues(new[] { "One", "Two" })); _unknownMultipleValueResponseHeaders.Append("Three", new StringValues(new[] { "One", "Two" })); _unknownMultipleValueResponseHeaders.Append("Four", new StringValues(new[] { "One", "Two" })); _enumerator = new Http2HeadersEnumerator(); }
public void CanIterateOverResponseHeaders() { var responseHeaders = new HttpResponseHeaders { ContentLength = 9, HeaderAcceptRanges = "AcceptRanges!", HeaderAge = new StringValues(new[] { "1", "2" }), HeaderDate = "Date!" }; responseHeaders.Append("Name1", "Value1"); responseHeaders.Append("Name2", "Value2-1"); responseHeaders.Append("Name2", "Value2-2"); responseHeaders.Append("Name3", "Value3"); var e = new Http2HeadersEnumerator(); e.Initialize(responseHeaders); var headers = GetNormalizedHeaders(e); Assert.Equal(new[] { new KeyValuePair <string, string>("Date", "Date!"), new KeyValuePair <string, string>("Accept-Ranges", "AcceptRanges!"), new KeyValuePair <string, string>("Age", "1"), new KeyValuePair <string, string>("Age", "2"), new KeyValuePair <string, string>("Content-Length", "9"), new KeyValuePair <string, string>("Name1", "Value1"), new KeyValuePair <string, string>("Name2", "Value2-1"), new KeyValuePair <string, string>("Name2", "Value2-2"), new KeyValuePair <string, string>("Name3", "Value3"), }, headers); }
public void CanIterateOverResponseHeaders() { var responseHeaders = (IHeaderDictionary) new HttpResponseHeaders(); responseHeaders.ContentLength = 9; responseHeaders.AcceptRanges = "AcceptRanges!"; responseHeaders.Age = new StringValues(new[] { "1", "2" }); responseHeaders.Date = "Date!"; responseHeaders.GrpcEncoding = "Identity!"; responseHeaders.Append("Name1", "Value1"); responseHeaders.Append("Name2", "Value2-1"); responseHeaders.Append("Name2", "Value2-2"); responseHeaders.Append("Name3", "Value3"); var e = new Http2HeadersEnumerator(); e.Initialize(responseHeaders); var headers = GetNormalizedHeaders(e); Assert.Equal(new[] { CreateHeaderResult(H2StaticTable.Date, "Date", "Date!"), CreateHeaderResult(H2StaticTable.AcceptRanges, "Accept-Ranges", "AcceptRanges!"), CreateHeaderResult(H2StaticTable.Age, "Age", "1"), CreateHeaderResult(H2StaticTable.Age, "Age", "2"), CreateHeaderResult(-1, "Grpc-Encoding", "Identity!"), CreateHeaderResult(H2StaticTable.ContentLength, "Content-Length", "9"), CreateHeaderResult(-1, "Name1", "Value1"), CreateHeaderResult(-1, "Name2", "Value2-1"), CreateHeaderResult(-1, "Name2", "Value2-2"), CreateHeaderResult(-1, "Name3", "Value3"), }, headers); }
public void GlobalSetup() { _http2HeadersEnumerator = new Http2HeadersEnumerator(); _hpackEncoder = new DynamicHPackEncoder(); _buffer = new byte[1024 * 1024]; _knownResponseHeaders = new HttpResponseHeaders(); var knownHeaders = (IHeaderDictionary)_knownResponseHeaders; knownHeaders.Server = "Kestrel"; knownHeaders.ContentType = "application/json"; knownHeaders.Date = "Date!"; knownHeaders.ContentLength = 0; knownHeaders.AcceptRanges = "Ranges!"; knownHeaders.TransferEncoding = "Encoding!"; knownHeaders.Via = "Via!"; knownHeaders.Vary = "Vary!"; knownHeaders.WWWAuthenticate = "Authenticate!"; knownHeaders.LastModified = "Modified!"; knownHeaders.Expires = "Expires!"; knownHeaders.Age = "Age!"; _unknownResponseHeaders = new HttpResponseHeaders(); for (var i = 0; i < 10; i++) { _unknownResponseHeaders.Append("Unknown" + i, "Value" + i); } }
public void GlobalSetup() { _http2HeadersEnumerator = new Http2HeadersEnumerator(); _hpackEncoder = new HPackEncoder(); _buffer = new byte[1024 * 1024]; _knownResponseHeaders = new HttpResponseHeaders { HeaderServer = "Kestrel", HeaderContentType = "application/json", HeaderDate = "Date!", HeaderContentLength = "0", HeaderAcceptRanges = "Ranges!", HeaderTransferEncoding = "Encoding!", HeaderVia = "Via!", HeaderVary = "Vary!", HeaderWWWAuthenticate = "Authenticate!", HeaderLastModified = "Modified!", HeaderExpires = "Expires!", HeaderAge = "Age!" }; _unknownResponseHeaders = new HttpResponseHeaders(); for (var i = 0; i < 10; i++) { _unknownResponseHeaders.Append("Unknown" + i, "Value" + i); } }
private KeyValuePair <string, string>[] GetNormalizedHeaders(Http2HeadersEnumerator enumerator) { var headers = new List <KeyValuePair <string, string> >(); while (enumerator.MoveNext()) { headers.Add(enumerator.Current); } return(headers.ToArray()); }
private static Http2HeadersEnumerator GetHeadersEnumerator(IEnumerable <KeyValuePair <string, string> > headers) { var groupedHeaders = headers .GroupBy(k => k.Key) .ToDictionary(g => g.Key, g => new StringValues(g.Select(gg => gg.Value).ToArray())); var enumerator = new Http2HeadersEnumerator(); enumerator.Initialize(groupedHeaders); return(enumerator); }
public void BeginEncodeHeaders_HeaderExceedHeaderTableSize_NoIndexAndNoHeaderEntry() { Span <byte> buffer = new byte[1024 * 16]; var headers = new HttpResponseHeaders(); headers.Append("x-Custom", new string('!', (int)Http2PeerSettings.DefaultHeaderTableSize)); var enumerator = new Http2HeadersEnumerator(); enumerator.Initialize(headers); var hpackEncoder = new DynamicHPackEncoder(); Assert.True(HPackHeaderWriter.BeginEncodeHeaders(200, hpackEncoder, enumerator, buffer, out var length)); Assert.Empty(GetHeaderEntries(hpackEncoder)); }
/// <summary> /// Begin encoding headers in the first HEADERS frame. /// </summary> public static bool BeginEncodeHeaders(DynamicHPackEncoder hpackEncoder, Http2HeadersEnumerator headersEnumerator, Span <byte> buffer, out int length) { length = 0; if (!hpackEncoder.EnsureDynamicTableSizeUpdate(buffer, out var sizeUpdateLength)) { throw new HPackEncodingException(SR.net_http_hpack_encode_failure); } length += sizeUpdateLength; if (!headersEnumerator.MoveNext()) { return(true); } var done = EncodeHeadersCore(hpackEncoder, headersEnumerator, buffer.Slice(length), throwIfNoneEncoded: true, out var headersLength); length += headersLength; return(done); }
private static bool EncodeHeadersCore(DynamicHPackEncoder hpackEncoder, Http2HeadersEnumerator headersEnumerator, Span <byte> buffer, bool throwIfNoneEncoded, out int length) { var currentLength = 0; do { var staticTableId = headersEnumerator.HPackStaticTableId; var name = headersEnumerator.Current.Key; var value = headersEnumerator.Current.Value; var valueEncoding = ReferenceEquals(headersEnumerator.EncodingSelector, KestrelServerOptions.DefaultHeaderEncodingSelector) ? null : headersEnumerator.EncodingSelector(name); var hint = ResolveHeaderEncodingHint(staticTableId, name); if (!hpackEncoder.EncodeHeader( buffer.Slice(currentLength), staticTableId, hint, name, value, valueEncoding, out var headerLength)) { // If the header wasn't written, and no headers have been written, then the header is too large. // Throw an error to avoid an infinite loop of attempting to write large header. if (currentLength == 0 && throwIfNoneEncoded) { throw new HPackEncodingException(SR.net_http_hpack_encode_failure); } length = currentLength; return(false); } currentLength += headerLength; }while (headersEnumerator.MoveNext()); length = currentLength; return(true); }
public static void WriteStartStream(this PipeWriter writer, int streamId, Http2HeadersEnumerator headers, HPackEncoder hpackEncoder, byte[] headerEncodingBuffer, bool endStream) { var frame = new Http2Frame(); frame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId); var buffer = headerEncodingBuffer.AsSpan(); var done = hpackEncoder.BeginEncode(headers, buffer, out var length); frame.PayloadLength = length; if (done) { frame.HeadersFlags = Http2HeadersFrameFlags.END_HEADERS; } if (endStream) { frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM; } Http2FrameWriter.WriteHeader(frame, writer); writer.Write(buffer.Slice(0, length)); while (!done) { frame.PrepareContinuation(Http2ContinuationFrameFlags.NONE, streamId); done = hpackEncoder.Encode(buffer, out length); frame.PayloadLength = length; if (done) { frame.ContinuationFlags = Http2ContinuationFrameFlags.END_HEADERS; } Http2FrameWriter.WriteHeader(frame, writer); writer.Write(buffer.Slice(0, length)); } }
public void BeginEncodeHeaders_Status302_NewIndexValue() { Span <byte> buffer = new byte[1024 * 16]; var headers = new HttpResponseHeaders(); var enumerator = new Http2HeadersEnumerator(); enumerator.Initialize(headers); var hpackEncoder = new DynamicHPackEncoder(); Assert.True(HPackHeaderWriter.BeginEncodeHeaders(302, hpackEncoder, enumerator, buffer, out var length)); var result = buffer.Slice(0, length).ToArray(); var hex = BitConverter.ToString(result); Assert.Equal("48-03-33-30-32", hex); var statusHeader = GetHeaderEntry(hpackEncoder, 0); Assert.Equal(":status", statusHeader.Name); Assert.Equal("302", statusHeader.Value); }
/// <summary> /// Continue encoding headers in the next HEADERS frame. The enumerator should already have a current value. /// </summary> public static bool ContinueEncodeHeaders(DynamicHPackEncoder hpackEncoder, Http2HeadersEnumerator headersEnumerator, Span <byte> buffer, out int length) { return(EncodeHeadersCore(hpackEncoder, headersEnumerator, buffer, throwIfNoneEncoded: true, out length)); }
/// <summary> /// Begin encoding headers in the first HEADERS frame. /// </summary> public static bool BeginEncodeHeaders(int statusCode, DynamicHPackEncoder hpackEncoder, Http2HeadersEnumerator headersEnumerator, Span <byte> buffer, out int length) { length = 0; if (!hpackEncoder.EnsureDynamicTableSizeUpdate(buffer, out var sizeUpdateLength)) { throw new HPackEncodingException(SR.net_http_hpack_encode_failure); } length += sizeUpdateLength; if (!EncodeStatusHeader(statusCode, hpackEncoder, buffer.Slice(length), out var statusCodeLength)) { throw new HPackEncodingException(SR.net_http_hpack_encode_failure); } length += statusCodeLength; if (!headersEnumerator.MoveNext()) { return(true); } // We're ok with not throwing if no headers were encoded because we've already encoded the status. // There is a small chance that the header will encode if there is no other content in the next HEADERS frame. var done = EncodeHeadersCore(hpackEncoder, headersEnumerator, buffer.Slice(length), throwIfNoneEncoded: false, out var headersLength); length += headersLength; return(done); }
public void BeginEncodeHeaders_MaxHeaderTableSizeExceeded_EvictionsToFit() { // Test follows example https://tools.ietf.org/html/rfc7541#appendix-C.5 Span <byte> buffer = new byte[1024 * 16]; var headers = (IHeaderDictionary) new HttpResponseHeaders(); headers.CacheControl = "private"; headers.Date = "Mon, 21 Oct 2013 20:13:21 GMT"; headers.Location = "https://www.example.com"; var enumerator = new Http2HeadersEnumerator(); var hpackEncoder = new DynamicHPackEncoder(maxHeaderTableSize: 256); // First response enumerator.Initialize(headers); Assert.True(HPackHeaderWriter.BeginEncodeHeaders(302, hpackEncoder, enumerator, buffer, out var length)); var result = buffer.Slice(0, length).ToArray(); var hex = BitConverter.ToString(result); Assert.Equal( "48-03-33-30-32-61-1D-4D-6F-6E-2C-20-32-31-20-4F-" + "63-74-20-32-30-31-33-20-32-30-3A-31-33-3A-32-31-" + "20-47-4D-54-58-07-70-72-69-76-61-74-65-6E-17-68-" + "74-74-70-73-3A-2F-2F-77-77-77-2E-65-78-61-6D-70-" + "6C-65-2E-63-6F-6D", hex); var entries = GetHeaderEntries(hpackEncoder); Assert.Collection(entries, e => { Assert.Equal("Location", e.Name); Assert.Equal("https://www.example.com", e.Value); Assert.Equal(63u, e.Size); }, e => { Assert.Equal("Cache-Control", e.Name); Assert.Equal("private", e.Value); Assert.Equal(52u, e.Size); }, e => { Assert.Equal("Date", e.Name); Assert.Equal("Mon, 21 Oct 2013 20:13:21 GMT", e.Value); Assert.Equal(65u, e.Size); }, e => { Assert.Equal(":status", e.Name); Assert.Equal("302", e.Value); Assert.Equal(42u, e.Size); }); Assert.Equal(222u, hpackEncoder.TableSize); // Second response enumerator.Initialize(headers); Assert.True(HPackHeaderWriter.BeginEncodeHeaders(307, hpackEncoder, enumerator, buffer, out length)); result = buffer.Slice(0, length).ToArray(); hex = BitConverter.ToString(result); Assert.Equal("48-03-33-30-37-C1-C0-BF", hex); entries = GetHeaderEntries(hpackEncoder); Assert.Collection(entries, e => { Assert.Equal(":status", e.Name); Assert.Equal("307", e.Value); Assert.Equal(42u, e.Size); }, e => { Assert.Equal("Location", e.Name); Assert.Equal("https://www.example.com", e.Value); Assert.Equal(63u, e.Size); }, e => { Assert.Equal("Cache-Control", e.Name); Assert.Equal("private", e.Value); Assert.Equal(52u, e.Size); }, e => { Assert.Equal("Date", e.Name); Assert.Equal("Mon, 21 Oct 2013 20:13:21 GMT", e.Value); Assert.Equal(65u, e.Size); }); Assert.Equal(222u, hpackEncoder.TableSize); // Third response headers.Date = "Mon, 21 Oct 2013 20:13:22 GMT"; headers.ContentEncoding = "gzip"; headers.SetCookie = "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"; enumerator.Initialize(headers); Assert.True(HPackHeaderWriter.BeginEncodeHeaders(200, hpackEncoder, enumerator, buffer, out length)); result = buffer.Slice(0, length).ToArray(); hex = BitConverter.ToString(result); Assert.Equal( "88-61-1D-4D-6F-6E-2C-20-32-31-20-4F-63-74-20-32-" + "30-31-33-20-32-30-3A-31-33-3A-32-32-20-47-4D-54-" + "C1-5A-04-67-7A-69-70-C1-1F-28-38-66-6F-6F-3D-41-" + "53-44-4A-4B-48-51-4B-42-5A-58-4F-51-57-45-4F-50-" + "49-55-41-58-51-57-45-4F-49-55-3B-20-6D-61-78-2D-" + "61-67-65-3D-33-36-30-30-3B-20-76-65-72-73-69-6F-" + "6E-3D-31", hex); entries = GetHeaderEntries(hpackEncoder); Assert.Collection(entries, e => { Assert.Equal("Content-Encoding", e.Name); Assert.Equal("gzip", e.Value); Assert.Equal(52u, e.Size); }, e => { Assert.Equal("Date", e.Name); Assert.Equal("Mon, 21 Oct 2013 20:13:22 GMT", e.Value); Assert.Equal(65u, e.Size); }, e => { Assert.Equal(":status", e.Name); Assert.Equal("307", e.Value); Assert.Equal(42u, e.Size); }, e => { Assert.Equal("Location", e.Name); Assert.Equal("https://www.example.com", e.Value); Assert.Equal(63u, e.Size); }); Assert.Equal(222u, hpackEncoder.TableSize); }