File: runtime\Http2\HPackIntegerTest.cs
Web Access
Project: src\src\Shared\test\Shared.Tests\Microsoft.AspNetCore.Shared.Tests.csproj (Microsoft.AspNetCore.Shared.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.Net.Http.HPack;
using Xunit;
 
namespace System.Net.Http.Unit.Tests.HPack
{
    public class HPackIntegerTest
    {
        [Theory]
        [MemberData(nameof(IntegerCodecExactSamples))]
        public void HPack_IntegerEncode(int value, int bits, byte[] expectedResult)
        {
            Span<byte> actualResult = new byte[64];
            bool success = IntegerEncoder.Encode(value, bits, actualResult, out int bytesWritten);
 
            Assert.True(success);
            Assert.Equal(expectedResult.Length, bytesWritten);
            Assert.True(actualResult.Slice(0, bytesWritten).SequenceEqual(expectedResult));
        }
 
        [Theory]
        [MemberData(nameof(IntegerCodecExactSamples))]
        public void HPack_IntegerEncode_ShortBuffer(int value, int bits, byte[] expectedResult)
        {
            Span<byte> actualResult = new byte[expectedResult.Length - 1];
            bool success = IntegerEncoder.Encode(value, bits, actualResult, out int bytesWritten);
 
            Assert.False(success);
        }
 
        [Theory]
        [MemberData(nameof(IntegerCodecExactSamples))]
        public void HPack_IntegerDecode(int expectedResult, int bits, byte[] encoded)
        {
            IntegerDecoder integerDecoder = new IntegerDecoder();
 
            bool finished = integerDecoder.BeginTryDecode(encoded[0], bits, out int actualResult);
 
            int i = 1;
            for (; !finished && i < encoded.Length; ++i)
            {
                finished = integerDecoder.TryDecode(encoded[i], out actualResult);
            }
 
            Assert.True(finished);
            Assert.Equal(encoded.Length, i);
 
            Assert.Equal(expectedResult, actualResult);
        }
 
        [Fact]
        public void IntegerEncoderDecoderRoundtrips()
        {
            IntegerDecoder decoder = new IntegerDecoder();
            Span<byte> integerBytes = stackalloc byte[5];
 
            for (int i = 0; i < 2048; ++i)
            {
                for (int prefixLength = 1; prefixLength <= 8; ++prefixLength)
                {
                    integerBytes.Clear();
                    Assert.True(IntegerEncoder.Encode(i, prefixLength, integerBytes, out int length));
 
                    bool decodeResult = decoder.BeginTryDecode(integerBytes[0], prefixLength, out int intResult);
 
                    for (int j = 1; j < length; j++)
                    {
                        Assert.False(decodeResult);
                        decodeResult = decoder.TryDecode(integerBytes[j], out intResult);
                    }
 
                    Assert.True(decodeResult);
                    Assert.Equal(i, intResult);
                }
            }
        }
 
        public static IEnumerable<object[]> IntegerCodecExactSamples()
        {
            yield return new object[] { 10, 5, new byte[] { 0x0A } };
            yield return new object[] { 1337, 5, new byte[] { 0x1F, 0x9A, 0x0A } };
            yield return new object[] { 42, 8, new byte[] { 0x2A } };
            yield return new object[] { 7, 3, new byte[] { 0x7, 0x0 } };
            yield return new object[] { int.MaxValue, 1, new byte[] { 0x01, 0xfe, 0xff, 0xff, 0xff, 0x07 } };
            yield return new object[] { int.MaxValue, 8, new byte[] { 0xff, 0x80, 0xfe, 0xff, 0xff, 0x07 } };
        }
 
        [Theory]
        [MemberData(nameof(IntegerData_OverMax))]
        public void IntegerDecode_Throws_IfMaxExceeded(int prefixLength, byte[] octets)
        {
            var decoder = new IntegerDecoder();
            var result = decoder.BeginTryDecode(octets[0], prefixLength, out var intResult);
 
            for (var j = 1; j < octets.Length - 1; j++)
            {
                Assert.False(decoder.TryDecode(octets[j], out intResult));
            }
 
            Assert.Throws<HPackDecodingException>(() => decoder.TryDecode(octets[octets.Length - 1], out intResult));
        }
 
        public static TheoryData<int, byte[]> IntegerData_OverMax
        {
            get
            {
                var data = new TheoryData<int, byte[]>();
 
                data.Add(1, new byte[] { 0x01, 0xff, 0xff, 0xff, 0xff, 0x07 }); // Int32.MaxValue + 1
                data.Add(1, new byte[] { 0x01, 0xff, 0xff, 0xff, 0xff, 0x08 }); // MSB exceeds maximum
                data.Add(1, new byte[] { 0x01, 0xff, 0xff, 0xff, 0xff, 0x80 }); // Undefined since continuation bit set
 
                data.Add(7, new byte[] { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x08 }); // 1 bit too large
                data.Add(7, new byte[] { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F });
                data.Add(7, new byte[] { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80 }); // A continuation byte (0x80) where the byte after it would be too large.
                data.Add(7, new byte[] { 0x7F, 0xFF, 0x00 }); // Encoded with 1 byte too many.
 
                data.Add(8, new byte[] { 0xff, 0x81, 0xfe, 0xff, 0xff, 0x07 }); // Int32.MaxValue + 1
                data.Add(8, new byte[] { 0xff, 0x81, 0xfe, 0xff, 0xff, 0x08 }); // MSB exceeds maximum
                data.Add(8, new byte[] { 0xff, 0x81, 0xfe, 0xff, 0xff, 0x80 }); // Undefined since continuation bit set
 
                return data;
            }
        }
    }
}