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);
    }
Example #2
0
    /* https://tools.ietf.org/html/rfc7540#section-6.2
     +-+-------------+-----------------------------------------------+
     |E|                 Stream Dependency? (31)                     |
     +-+-------------+-----------------------------------------------+
     |  Weight? (8)  |
     +-+-------------+-----------------------------------------------+
     |                   Header Block Fragment (*)                 ...
     +---------------------------------------------------------------+
     */
    public Task SendHeadersWithPriorityAsync(int streamId, IEnumerable <KeyValuePair <string, string> > headers, byte priority, int streamDependency, bool endStream)
    {
        var writableBuffer = _pair.Application.Output;

        var frame = new Http2Frame();

        frame.PrepareHeaders(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.PRIORITY, streamId);
        frame.HeadersPriorityWeight   = priority;
        frame.HeadersStreamDependency = streamDependency;

        var extendedHeaderLength = 5; // stream dependency + weight
        var buffer         = _headerEncodingBuffer.AsSpan();
        var extendedHeader = buffer.Slice(0, extendedHeaderLength);

        Bitshifter.WriteUInt31BigEndian(extendedHeader, (uint)streamDependency);
        extendedHeader[4] = priority;
        var payload = buffer.Slice(extendedHeaderLength);

        HPackHeaderWriter.BeginEncodeHeaders(GetHeadersEnumerator(headers), payload, out var length);

        frame.PayloadLength = extendedHeaderLength + length;

        if (endStream)
        {
            frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
        }

        WriteHeader(frame, writableBuffer);
        writableBuffer.Write(buffer.Slice(0, frame.PayloadLength));
        return(FlushAsync(writableBuffer));
    }
    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_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));
    }
Example #5
0
    /* https://tools.ietf.org/html/rfc7540#section-6.2
     +---------------+
     |Pad Length? (8)|
     +-+-------------+-----------------------------------------------+
     |                   Header Block Fragment (*)                 ...
     +---------------------------------------------------------------+
     |                           Padding (*)                       ...
     +---------------------------------------------------------------+
     */
    public Task SendHeadersWithPaddingAsync(int streamId, IEnumerable <KeyValuePair <string, string> > headers, byte padLength, bool endStream)
    {
        var writableBuffer = _pair.Application.Output;

        var frame = new Http2Frame();

        frame.PrepareHeaders(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.PADDED, streamId);
        frame.HeadersPadLength = padLength;

        var extendedHeaderLength = 1; // Padding length field
        var buffer         = _headerEncodingBuffer.AsSpan();
        var extendedHeader = buffer.Slice(0, extendedHeaderLength);

        extendedHeader[0] = padLength;
        var payload = buffer.Slice(extendedHeaderLength, buffer.Length - padLength - extendedHeaderLength);

        HPackHeaderWriter.BeginEncodeHeaders(GetHeadersEnumerator(headers), payload, out var length);
        var padding = buffer.Slice(extendedHeaderLength + length, padLength);

        padding.Clear();

        frame.PayloadLength = extendedHeaderLength + length + padLength;

        if (endStream)
        {
            frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
        }

        WriteHeader(frame, writableBuffer);
        writableBuffer.Write(buffer.Slice(0, frame.PayloadLength));
        return(FlushAsync(writableBuffer));
    }
Example #6
0
    internal async Task <bool> SendContinuationAsync(int streamId, Http2ContinuationFrameFlags flags, IEnumerable <KeyValuePair <string, string> > headers)
    {
        var outputWriter = _pair.Application.Output;
        var frame        = new Http2Frame();

        frame.PrepareContinuation(flags, streamId);
        var buffer = _headerEncodingBuffer.AsMemory();
        var done   = HPackHeaderWriter.BeginEncodeHeaders(GetHeadersEnumerator(headers), buffer.Span, out var length);

        frame.PayloadLength = length;

        WriteHeader(frame, outputWriter);
        await SendAsync(buffer.Span.Slice(0, length));

        return(done);
    }
Example #7
0
    public Task StartStreamAsync(int streamId, IEnumerable <KeyValuePair <string, string> > headers, bool endStream)
    {
        var writableBuffer = _pair.Application.Output;

        var frame = new Http2Frame();

        frame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId);

        var buffer            = _headerEncodingBuffer.AsSpan();
        var headersEnumerator = GetHeadersEnumerator(headers);
        var done = HPackHeaderWriter.BeginEncodeHeaders(headersEnumerator, buffer, out var length);

        frame.PayloadLength = length;

        if (done)
        {
            frame.HeadersFlags = Http2HeadersFrameFlags.END_HEADERS;
        }

        if (endStream)
        {
            frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
        }

        WriteHeader(frame, writableBuffer);
        writableBuffer.Write(buffer.Slice(0, length));

        while (!done)
        {
            frame.PrepareContinuation(Http2ContinuationFrameFlags.NONE, streamId);

            done = HPackHeaderWriter.ContinueEncodeHeaders(headersEnumerator, buffer, out length);
            frame.PayloadLength = length;

            if (done)
            {
                frame.ContinuationFlags = Http2ContinuationFrameFlags.END_HEADERS;
            }

            WriteHeader(frame, writableBuffer);
            writableBuffer.Write(buffer.Slice(0, length));
        }

        return(FlushAsync(writableBuffer));
    }
    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));
    }
Example #9
0
        public static void WriteStartStream(this PipeWriter writer, int streamId, Http2HeadersEnumerator headers, byte[] headerEncodingBuffer, bool endStream)
        {
            var frame = new Http2Frame();

            frame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId);

            var buffer = headerEncodingBuffer.AsSpan();
            var done   = HPackHeaderWriter.BeginEncodeHeaders(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 = HPackHeaderWriter.ContinueEncodeHeaders(headers, buffer, out length);
                frame.PayloadLength = length;

                if (done)
                {
                    frame.ContinuationFlags = Http2ContinuationFrameFlags.END_HEADERS;
                }

                Http2FrameWriter.WriteHeader(frame, writer);
                writer.Write(buffer.Slice(0, length));
            }
        }
Example #10
0
        public void EncodesHeadersInSinglePayloadWhenSpaceAvailable(KeyValuePair <string, string>[] headers, byte[] expectedPayload, int?statusCode)
        {
            var payload = new byte[1024];
            var length  = 0;

            if (statusCode.HasValue)
            {
                Assert.True(HPackHeaderWriter.BeginEncodeHeaders(statusCode.Value, GetHeadersEnumerator(headers), payload, out length));
            }
            else
            {
                Assert.True(HPackHeaderWriter.BeginEncodeHeaders(GetHeadersEnumerator(headers), payload, out length));
            }
            Assert.Equal(expectedPayload.Length, length);

            for (var i = 0; i < length; i++)
            {
                Assert.True(expectedPayload[i] == payload[i], $"{expectedPayload[i]} != {payload[i]} at {i} (len {length})");
            }

            Assert.Equal(expectedPayload, new ArraySegment <byte>(payload, 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);
    }
Example #12
0
        public void EncodesHeadersInMultiplePayloadsWhenSpaceNotAvailable(bool exactSize)
        {
            var statusCode = 200;
            var headers    = new[]
            {
                new KeyValuePair <string, string>("date", "Mon, 24 Jul 2017 19:22:30 GMT"),
                new KeyValuePair <string, string>("content-type", "text/html; charset=utf-8"),
                new KeyValuePair <string, string>("server", "Kestrel")
            };

            var expectedStatusCodePayload = new byte[]
            {
                0x88
            };

            var expectedDateHeaderPayload = new byte[]
            {
                0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x1d, 0x4d,
                0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x34, 0x20, 0x4a,
                0x75, 0x6c, 0x20, 0x32, 0x30, 0x31, 0x37, 0x20,
                0x31, 0x39, 0x3a, 0x32, 0x32, 0x3a, 0x33, 0x30,
                0x20, 0x47, 0x4d, 0x54
            };

            var expectedContentTypeHeaderPayload = new byte[]
            {
                0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
                0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x18, 0x74,
                0x65, 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c,
                0x3b, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65,
                0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38
            };

            var expectedServerHeaderPayload = new byte[]
            {
                0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
                0x07, 0x4b, 0x65, 0x73, 0x74, 0x72, 0x65, 0x6c
            };

            Span <byte> payload          = new byte[1024];
            var         offset           = 0;
            var         headerEnumerator = GetHeadersEnumerator(headers);

            // When !exactSize, slices are one byte short of fitting the next header
            var sliceLength = expectedStatusCodePayload.Length + (exactSize ? 0 : expectedDateHeaderPayload.Length - 1);

            Assert.False(HPackHeaderWriter.BeginEncodeHeaders(statusCode, headerEnumerator, payload.Slice(offset, sliceLength), out var length));
            Assert.Equal(expectedStatusCodePayload.Length, length);
            Assert.Equal(expectedStatusCodePayload, payload.Slice(0, length).ToArray());

            offset += length;

            sliceLength = expectedDateHeaderPayload.Length + (exactSize ? 0 : expectedContentTypeHeaderPayload.Length - 1);
            Assert.False(HPackHeaderWriter.ContinueEncodeHeaders(headerEnumerator, payload.Slice(offset, sliceLength), out length));
            Assert.Equal(expectedDateHeaderPayload.Length, length);
            Assert.Equal(expectedDateHeaderPayload, payload.Slice(offset, length).ToArray());

            offset += length;

            sliceLength = expectedContentTypeHeaderPayload.Length + (exactSize ? 0 : expectedServerHeaderPayload.Length - 1);
            Assert.False(HPackHeaderWriter.ContinueEncodeHeaders(headerEnumerator, payload.Slice(offset, sliceLength), out length));
            Assert.Equal(expectedContentTypeHeaderPayload.Length, length);
            Assert.Equal(expectedContentTypeHeaderPayload, payload.Slice(offset, length).ToArray());

            offset += length;

            sliceLength = expectedServerHeaderPayload.Length;
            Assert.True(HPackHeaderWriter.ContinueEncodeHeaders(headerEnumerator, payload.Slice(offset, sliceLength), out length));
            Assert.Equal(expectedServerHeaderPayload.Length, length);
            Assert.Equal(expectedServerHeaderPayload, payload.Slice(offset, length).ToArray());
        }
    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);
    }
Example #14
0
 public void BeginEncodeHeaders_UnknownHeaders_CustomEncoding()
 {
     _knownResponseHeaders.EncodingSelector = _ => Encoding.UTF8;
     _http2HeadersEnumerator.Initialize(_unknownResponseHeaders);
     HPackHeaderWriter.BeginEncodeHeaders(_hpackEncoder, _http2HeadersEnumerator, _buffer, out _);
 }
Example #15
0
 public void BeginEncodeHeaders_UnknownHeaders()
 {
     _http2HeadersEnumerator.Initialize(_unknownResponseHeaders);
     HPackHeaderWriter.BeginEncodeHeaders(_hpackEncoder, _http2HeadersEnumerator, _buffer, out _);
 }