/
githubmirror
/
aspnetcore
Обзор
Документация
Войти
/
githubmirror
/
aspnetcore
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Http/WebUtilities/test/FormPipeReaderTests.cs
650 строк
25 KB
William Godbe
Fix denial of service in ASP.NET Core via FormPipeReader (#40648)
11 мар 2022, 09:29
Не верифицирован
11 мар 2022, 09:29
7915086
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; using System.Globalization; using System.IO.Pipelines; using System.Text; using Microsoft.Extensions.Primitives; namespace Microsoft.AspNetCore.WebUtilities; public class FormPipeReaderTests { [Fact] public async Task ReadFormAsync_EmptyKeyAtEndAllowed() { var bodyPipe = await MakePipeReader("=bar"); var formCollection = await ReadFormAsync(new FormPipeReader(bodyPipe)); Assert.Equal("bar", formCollection[""].ToString()); } [Fact] public async Task ReadFormAsync_EmptyKeyWithAdditionalEntryAllowed() { var bodyPipe = await MakePipeReader("=bar&baz=2"); var formCollection = await ReadFormAsync(new FormPipeReader(bodyPipe)); Assert.Equal("bar", formCollection[""].ToString()); Assert.Equal("2", formCollection["baz"].ToString()); } [Fact] public async Task ReadFormAsync_EmptyValueAtEndAllowed() { var bodyPipe = await MakePipeReader("foo="); var formCollection = await ReadFormAsync(new FormPipeReader(bodyPipe)); Assert.Equal("", formCollection["foo"].ToString()); } [Fact] public async Task ReadFormAsync_EmptyValueWithoutEqualsAtEndAllowed() { var bodyPipe = await MakePipeReader("foo"); var formCollection = await ReadFormAsync(new FormPipeReader(bodyPipe)); Assert.Equal("", formCollection["foo"].ToString()); } [Fact] public async Task ReadFormAsync_EmptyValueWithAdditionalEntryAllowed() { var bodyPipe = await MakePipeReader("foo=&baz=2"); var formCollection = await ReadFormAsync(new FormPipeReader(bodyPipe)); Assert.Equal("", formCollection["foo"].ToString()); Assert.Equal("2", formCollection["baz"].ToString()); } [Fact] public async Task ReadFormAsync_EmptyValueWithoutEqualsWithAdditionalEntryAllowed() { var bodyPipe = await MakePipeReader("foo&baz=2"); var formCollection = await ReadFormAsync(new FormPipeReader(bodyPipe)); Assert.Equal("", formCollection["foo"].ToString()); Assert.Equal("2", formCollection["baz"].ToString()); } [Fact] public async Task ReadFormAsync_ValueContainsInvalidCharacters_Throw() { var bodyPipe = await MakePipeReader("%00"); var exception = await Assert.ThrowsAsync<InvalidDataException>( () => ReadFormAsync(new FormPipeReader(bodyPipe))); Assert.Equal("The form value contains invalid characters.", exception.Message); Assert.IsType<InvalidOperationException>(exception.InnerException); } [Fact] public async Task ReadFormAsync_ValueCountLimitMet_Success() { var bodyPipe = await MakePipeReader("foo=1&bar=2&baz=3"); var formCollection = await ReadFormAsync(new FormPipeReader(bodyPipe) { ValueCountLimit = 3 }); Assert.Equal("1", formCollection["foo"].ToString()); Assert.Equal("2", formCollection["bar"].ToString()); Assert.Equal("3", formCollection["baz"].ToString()); Assert.Equal(3, formCollection.Count); } [Fact] public async Task ReadFormAsync_ValueCountLimitExceeded_Throw() { var content = "foo=1&baz=2&bar=3&baz=4&baf=5"; var bodyPipe = await MakePipeReader(content); var exception = await Assert.ThrowsAsync<InvalidDataException>( () => ReadFormAsync(new FormPipeReader(bodyPipe) { ValueCountLimit = 3 })); Assert.Equal("Form value count limit 3 exceeded.", exception.Message); // The body pipe is still readable and has not advanced. var readResult = await bodyPipe.ReadAsync(); Assert.Equal(Encoding.UTF8.GetBytes(content), readResult.Buffer.ToArray()); } [Fact] public async Task ReadFormAsync_ValueCountLimitExceededSameKey_Throw() { var content = "baz=1&baz=2&baz=3&baz=4"; var bodyPipe = await MakePipeReader(content); var exception = await Assert.ThrowsAsync<InvalidDataException>( () => ReadFormAsync(new FormPipeReader(bodyPipe) { ValueCountLimit = 3 })); Assert.Equal("Form value count limit 3 exceeded.", exception.Message); // The body pipe is still readable and has not advanced. var readResult = await bodyPipe.ReadAsync(); Assert.Equal(Encoding.UTF8.GetBytes(content), readResult.Buffer.ToArray()); } [Fact] public async Task ReadFormAsync_KeyLengthLimitMet_Success() { var bodyPipe = await MakePipeReader("fooooooooo=1&bar=2&baz=3&baz=4"); var formCollection = await ReadFormAsync(new FormPipeReader(bodyPipe) { KeyLengthLimit = 10 }); Assert.Equal("1", formCollection["fooooooooo"].ToString()); Assert.Equal("2", formCollection["bar"].ToString()); Assert.Equal("3,4", formCollection["baz"].ToString()); Assert.Equal(3, formCollection.Count); } [Fact] public async Task ReadFormAsync_KeyLengthLimitExceeded_Throw() { var content = "foo=1&baz12345678=2"; var bodyPipe = await MakePipeReader(content); var exception = await Assert.ThrowsAsync<InvalidDataException>( () => ReadFormAsync(new FormPipeReader(bodyPipe) { KeyLengthLimit = 10 })); Assert.Equal("Form key length limit 10 exceeded.", exception.Message); // The body pipe is still readable and has not advanced. var readResult = await bodyPipe.ReadAsync(); Assert.Equal(Encoding.UTF8.GetBytes(content), readResult.Buffer.ToArray()); } [Fact] public async Task ReadFormAsync_ValueLengthLimitMet_Success() { var bodyPipe = await MakePipeReader("foo=1&bar=1234567890&baz=3&baz=4"); var formCollection = await ReadFormAsync(new FormPipeReader(bodyPipe) { ValueLengthLimit = 10 }); Assert.Equal("1", formCollection["foo"].ToString()); Assert.Equal("1234567890", formCollection["bar"].ToString()); Assert.Equal("3,4", formCollection["baz"].ToString()); Assert.Equal(3, formCollection.Count); } [Fact] public async Task ReadFormAsync_ValueLengthLimitExceeded_Throw() { var content = "foo=1&baz=12345678901"; var bodyPipe = await MakePipeReader(content); var exception = await Assert.ThrowsAsync<InvalidDataException>( () => ReadFormAsync(new FormPipeReader(bodyPipe) { ValueLengthLimit = 10 })); Assert.Equal("Form value length limit 10 exceeded.", exception.Message); // The body pipe is still readable and has not advanced. var readResult = await bodyPipe.ReadAsync(); Assert.Equal(Encoding.UTF8.GetBytes(content), readResult.Buffer.ToArray()); } [Fact] public async Task ReadFormAsync_ValueLengthLimitExceededAcrossBufferBoundary_Throw() { Pipe bodyPipe = new Pipe(); var content1 = "foo=1&baz=1234567890"; var content2 = "1"; await bodyPipe.Writer.WriteAsync(Encoding.UTF8.GetBytes(content1)); await bodyPipe.Writer.FlushAsync(); var readTask = Assert.ThrowsAsync<InvalidDataException>( () => ReadFormAsync(new FormPipeReader(bodyPipe.Reader) { ValueLengthLimit = 10 })); await bodyPipe.Writer.WriteAsync(Encoding.UTF8.GetBytes(content2)); bodyPipe.Writer.Complete(); var exception = await readTask; Assert.Equal("Form value length limit 10 exceeded.", exception.Message); // The body pipe is still readable and has not advanced. var readResult = await bodyPipe.Reader.ReadAsync(); Assert.Equal(Encoding.UTF8.GetBytes("baz=12345678901"), readResult.Buffer.ToArray()); } [Fact] public void ReadFormAsync_ChunkedDataNoDelimiter_ThrowsEarly() { var bytes = CreateBytes_NoDelimiter(10 * 1024); var readOnlySequence = ReadOnlySequenceFactory.SegmentPerByteFactory.CreateWithContent(bytes); KeyValueAccumulator accumulator = default; var valueLengthLimit = 1024; var keyLengthLimit = 10; var formReader = new FormPipeReader(null!) { ValueLengthLimit = valueLengthLimit, KeyLengthLimit = keyLengthLimit }; var exception = Assert.Throws<InvalidDataException>( () => formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: false)); // Make sure that FormPipeReader throws an exception after hitting KeyLengthLimit + ValueLengthLimit, // Rather than after reading the entire request. Assert.Equal(string.Format( CultureInfo.CurrentCulture, Resources.FormPipeReader_KeyOrValueTooLarge, keyLengthLimit, valueLengthLimit), exception.Message); } // https://en.wikipedia.org/wiki/Percent-encoding [Theory] [InlineData("++=hello", " ", "hello")] [InlineData("a=1+1", "a", "1 1")] [InlineData("%22%25%2D%2E%3C%3E%5C%5E%5F%60%7B%7C%7D%7E=%22%25%2D%2E%3C%3E%5C%5E%5F%60%7B%7C%7D%7E", "\"%-.<>\\^_`{|}~", "\"%-.<>\\^_`{|}~")] [InlineData("a=%41", "a", "A")] // ascii encoded hex [InlineData("a=%C3%A1", "a", "\u00e1")] // utf8 code points [InlineData("a=%u20AC", "a", "%u20AC")] // utf16 not supported public async Task ReadForm_Decoding(string formData, string key, string expectedValue) { var bodyPipe = await MakePipeReader(text: formData); var form = await ReadFormAsync(new FormPipeReader(bodyPipe)); Assert.Equal(expectedValue, form[key]); } [Theory] [MemberData(nameof(Encodings))] public void TryParseFormValues_SingleSegmentWorks(Encoding encoding) { var readOnlySequence = new ReadOnlySequence<byte>(encoding.GetBytes("foo=bar&baz=boo")); KeyValueAccumulator accumulator = default; var formReader = new FormPipeReader(null!, encoding); formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: true); Assert.True(readOnlySequence.IsEmpty); Assert.Equal(2, accumulator.KeyCount); var dict = accumulator.GetResults(); Assert.Equal("bar", dict["foo"]); Assert.Equal("boo", dict["baz"]); } [Theory] [MemberData(nameof(Encodings))] public void TryParseFormValues_Works(Encoding encoding) { var readOnlySequence = ReadOnlySequenceFactory.SingleSegmentFactory.CreateWithContent(encoding.GetBytes("foo=bar&baz=boo&t=")); KeyValueAccumulator accumulator = default; var formReader = new FormPipeReader(null!, encoding); formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: true); Assert.True(readOnlySequence.IsEmpty); Assert.Equal(3, accumulator.KeyCount); var dict = accumulator.GetResults(); Assert.Equal("bar", dict["foo"]); Assert.Equal("boo", dict["baz"]); Assert.Equal("", dict["t"]); } [Theory] [MemberData(nameof(Encodings))] public void TryParseFormValues_LimitsCanBeLarge(Encoding encoding) { var readOnlySequence = ReadOnlySequenceFactory.SingleSegmentFactory.CreateWithContent(encoding.GetBytes("foo=bar&baz=boo&t=")); KeyValueAccumulator accumulator = default; var formReader = new FormPipeReader(null!, encoding); formReader.KeyLengthLimit = int.MaxValue; formReader.ValueLengthLimit = int.MaxValue; formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: false); formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: true); Assert.True(readOnlySequence.IsEmpty); Assert.Equal(3, accumulator.KeyCount); var dict = accumulator.GetResults(); Assert.Equal("bar", dict["foo"]); Assert.Equal("boo", dict["baz"]); Assert.Equal("", dict["t"]); } [Theory] [MemberData(nameof(Encodings))] public void TryParseFormValues_SplitAcrossSegmentsWorks(Encoding encoding) { var readOnlySequence = ReadOnlySequenceFactory.SegmentPerByteFactory.CreateWithContent(encoding.GetBytes("foo=bar&baz=boo&t=")); KeyValueAccumulator accumulator = default; var formReader = new FormPipeReader(null!, encoding); formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: true); Assert.True(readOnlySequence.IsEmpty); Assert.Equal(3, accumulator.KeyCount); var dict = accumulator.GetResults(); Assert.Equal("bar", dict["foo"]); Assert.Equal("boo", dict["baz"]); Assert.Equal("", dict["t"]); } [Theory] [MemberData(nameof(Encodings))] public void TryParseFormValues_SplitAcrossSegmentsWorks_LimitsCanBeLarge(Encoding encoding) { var readOnlySequence = ReadOnlySequenceFactory.SegmentPerByteFactory.CreateWithContent(encoding.GetBytes("foo=bar&baz=boo&t=")); KeyValueAccumulator accumulator = default; var formReader = new FormPipeReader(null!, encoding); formReader.KeyLengthLimit = int.MaxValue; formReader.ValueLengthLimit = int.MaxValue; formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: false); formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: true); Assert.True(readOnlySequence.IsEmpty); Assert.Equal(3, accumulator.KeyCount); var dict = accumulator.GetResults(); Assert.Equal("bar", dict["foo"]); Assert.Equal("boo", dict["baz"]); Assert.Equal("", dict["t"]); } [Theory] [MemberData(nameof(Encodings))] public void TryParseFormValues_MultiSegmentWithArrayPoolAcrossSegmentsWorks(Encoding encoding) { var readOnlySequence = ReadOnlySequenceFactory.SegmentPerByteFactory.CreateWithContent(encoding.GetBytes("foo=bar&baz=bo" + new string('a', 128))); KeyValueAccumulator accumulator = default; var formReader = new FormPipeReader(null!, encoding); formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: true); Assert.True(readOnlySequence.IsEmpty); Assert.Equal(2, accumulator.KeyCount); var dict = accumulator.GetResults(); Assert.Equal("bar", dict["foo"]); Assert.Equal("bo" + new string('a', 128), dict["baz"]); } [Theory] [MemberData(nameof(Encodings))] public void TryParseFormValues_MultiSegmentSplitAcrossSegmentsWithPlusesWorks(Encoding encoding) { var readOnlySequence = ReadOnlySequenceFactory.SegmentPerByteFactory.CreateWithContent(encoding.GetBytes("+++=+++&++++=++++&+=")); KeyValueAccumulator accumulator = default; var formReader = new FormPipeReader(null!, encoding); formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: true); Assert.True(readOnlySequence.IsEmpty); Assert.Equal(3, accumulator.KeyCount); var dict = accumulator.GetResults(); Assert.Equal(" ", dict[" "]); Assert.Equal(" ", dict[" "]); Assert.Equal("", dict[" "]); } [Theory] [MemberData(nameof(Encodings))] public void TryParseFormValues_DecodedPlusesWorks(Encoding encoding) { var readOnlySequence = ReadOnlySequenceFactory.SingleSegmentFactory.CreateWithContent(encoding.GetBytes("++%2B=+++%2B&++++=++++&+=")); KeyValueAccumulator accumulator = default; var formReader = new FormPipeReader(null!, encoding); formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: true); Assert.True(readOnlySequence.IsEmpty); Assert.Equal(3, accumulator.KeyCount); var dict = accumulator.GetResults(); Assert.Equal(" ", dict[" "]); Assert.Equal(" +", dict[" +"]); Assert.Equal("", dict[" "]); } [Theory] [MemberData(nameof(Encodings))] public void TryParseFormValues_SplitAcrossSegmentsThatNeedDecodingWorks(Encoding encoding) { var readOnlySequence = ReadOnlySequenceFactory.SegmentPerByteFactory.CreateWithContent(encoding.GetBytes("\"%-.<>\\^_`{|}~=\"%-.<>\\^_`{|}~&\"%-.<>\\^_`{|}=wow")); KeyValueAccumulator accumulator = default; var formReader = new FormPipeReader(null!, encoding); formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: true); Assert.True(readOnlySequence.IsEmpty); Assert.Equal(2, accumulator.KeyCount); var dict = accumulator.GetResults(); Assert.Equal("\"%-.<>\\^_`{|}~", dict["\"%-.<>\\^_`{|}~"]); Assert.Equal("wow", dict["\"%-.<>\\^_`{|}"]); } [Fact] public void TryParseFormValues_MultiSegmentFastPathWorks() { var readOnlySequence = ReadOnlySequenceFactory.CreateSegments(Encoding.UTF8.GetBytes("foo=bar&"), Encoding.UTF8.GetBytes("baz=boo")); KeyValueAccumulator accumulator = default; var formReader = new FormPipeReader(null!); formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: true); Assert.True(readOnlySequence.IsEmpty); Assert.Equal(2, accumulator.KeyCount); var dict = accumulator.GetResults(); Assert.Equal("bar", dict["foo"]); Assert.Equal("boo", dict["baz"]); } [Fact] public void TryParseFormValues_ExceedKeyLengthThrows() { var readOnlySequence = ReadOnlySequenceFactory.SingleSegmentFactory.CreateWithContent(Encoding.UTF8.GetBytes("foo=bar&baz=boo&t=")); KeyValueAccumulator accumulator = default; var formReader = new FormPipeReader(null!); formReader.KeyLengthLimit = 2; var exception = Assert.Throws<InvalidDataException>(() => formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: true)); Assert.Equal("Form key length limit 2 exceeded.", exception.Message); } [Fact] public void TryParseFormValues_ExceedKeyLengthThrowsInSplitSegment() { var readOnlySequence = ReadOnlySequenceFactory.CreateSegments(Encoding.UTF8.GetBytes("fo=bar&ba"), Encoding.UTF8.GetBytes("z=boo&t=")); KeyValueAccumulator accumulator = default; var formReader = new FormPipeReader(null!); formReader.KeyLengthLimit = 2; var exception = Assert.Throws<InvalidDataException>(() => formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: true)); Assert.Equal("Form key length limit 2 exceeded.", exception.Message); } [Fact] public void TryParseFormValues_ExceedValueLengthThrows() { var readOnlySequence = ReadOnlySequenceFactory.CreateSegments(Encoding.UTF8.GetBytes("foo=bar&baz=boo&t=")); KeyValueAccumulator accumulator = default; var formReader = new FormPipeReader(null!); formReader.ValueLengthLimit = 2; var exception = Assert.Throws<InvalidDataException>(() => formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: true)); Assert.Equal("Form value length limit 2 exceeded.", exception.Message); } [Fact] public void TryParseFormValues_ExceedValueLengthThrowsInSplitSegment() { var readOnlySequence = ReadOnlySequenceFactory.CreateSegments(Encoding.UTF8.GetBytes("foo=ba&baz=bo"), Encoding.UTF8.GetBytes("o&t=")); KeyValueAccumulator accumulator = default; var formReader = new FormPipeReader(null!); formReader.ValueLengthLimit = 2; var exception = Assert.Throws<InvalidDataException>(() => formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: true)); Assert.Equal("Form value length limit 2 exceeded.", exception.Message); } [Fact] public void TryParseFormValues_ExceedKeyLengthThrowsInSplitSegmentEnd() { var readOnlySequence = ReadOnlySequenceFactory.CreateSegments(Encoding.UTF8.GetBytes("foo=ba&baz=bo"), Encoding.UTF8.GetBytes("o&asdfasdfasd=")); KeyValueAccumulator accumulator = default; var formReader = new FormPipeReader(null!); formReader.KeyLengthLimit = 10; var exception = Assert.Throws<InvalidDataException>(() => formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: true)); Assert.Equal("Form key length limit 10 exceeded.", exception.Message); } [Fact] public void TryParseFormValues_ExceedValueLengthThrowsInSplitSegmentEnd() { var readOnlySequence = ReadOnlySequenceFactory.CreateSegments(Encoding.UTF8.GetBytes("foo=ba&baz=bo"), Encoding.UTF8.GetBytes("o&t=asdfasdfasd")); KeyValueAccumulator accumulator = default; var formReader = new FormPipeReader(null!); formReader.ValueLengthLimit = 10; var exception = Assert.Throws<InvalidDataException>(() => formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: true)); Assert.Equal("Form value length limit 10 exceeded.", exception.Message); } [Fact] public async Task ResetPipeWorks() { // Same test that is in the benchmark var pipe = new Pipe(); var bytes = Encoding.UTF8.GetBytes("foo=bar&baz=boo"); for (var i = 0; i < 1000; i++) { pipe.Writer.Write(bytes); pipe.Writer.Complete(); var formReader = new FormPipeReader(pipe.Reader); await formReader.ReadFormAsync(); pipe.Reader.Complete(); pipe.Reset(); } } [Theory] [MemberData(nameof(IncompleteFormKeys))] public void ParseFormWithIncompleteKeyWhenIsFinalBlockSucceeds(ReadOnlySequence<byte> readOnlySequence) { KeyValueAccumulator accumulator = default; var formReader = new FormPipeReader(null!) { KeyLengthLimit = 3 }; formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: true); Assert.True(readOnlySequence.IsEmpty); IDictionary<string, StringValues> values = accumulator.GetResults(); Assert.Contains("fo", values); Assert.Equal("bar", values["fo"]); Assert.Contains("ba", values); Assert.Equal("", values["ba"]); } [Theory] [MemberData(nameof(IncompleteFormValues))] public void ParseFormWithIncompleteValueWhenIsFinalBlockSucceeds(ReadOnlySequence<byte> readOnlySequence) { KeyValueAccumulator accumulator = default; var formReader = new FormPipeReader(null!) { ValueLengthLimit = 3 }; formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: true); Assert.True(readOnlySequence.IsEmpty); IDictionary<string, StringValues> values = accumulator.GetResults(); Assert.Contains("fo", values); Assert.Equal("bar", values["fo"]); Assert.Contains("b", values); Assert.Equal("", values["b"]); } [Fact] public async Task ReadFormAsync_AccumulatesEmptyKeys() { var bodyPipe = await MakePipeReader("&&&"); var formCollection = await ReadFormAsync(new FormPipeReader(bodyPipe)); Assert.Single(formCollection); } public static TheoryData<ReadOnlySequence<byte>> IncompleteFormKeys => new TheoryData<ReadOnlySequence<byte>> { { ReadOnlySequenceFactory.CreateSegments(Encoding.UTF8.GetBytes("fo=bar&b"), Encoding.UTF8.GetBytes("a")) }, { new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes("fo=bar&ba")) } }; public static TheoryData<ReadOnlySequence<byte>> IncompleteFormValues => new TheoryData<ReadOnlySequence<byte>> { { ReadOnlySequenceFactory.CreateSegments(Encoding.UTF8.GetBytes("fo=bar&b"), Encoding.UTF8.GetBytes("=")) }, { new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes("fo=bar&b=")) } }; public static TheoryData<Encoding> Encodings => new TheoryData<Encoding> { { Encoding.UTF8 }, { Encoding.UTF32 }, { Encoding.ASCII }, { Encoding.Unicode } }; internal virtual Task<Dictionary<string, StringValues>> ReadFormAsync(FormPipeReader reader) { return reader.ReadFormAsync(); } private static async Task<PipeReader> MakePipeReader(string text) { var formContent = Encoding.UTF8.GetBytes(text); Pipe bodyPipe = new Pipe(); await bodyPipe.Writer.WriteAsync(formContent); // Complete the writer so the reader will complete after processing all data. bodyPipe.Writer.Complete(); return bodyPipe.Reader; } private static byte[] CreateBytes_NoDelimiter(int n) { //Create the bytes of "key=vvvvvvvv....", of length n var keyValue = new char[n]; Array.Fill(keyValue, 'v'); keyValue[0] = 'k'; keyValue[1] = 'e'; keyValue[2] = 'y'; keyValue[3] = '='; return Encoding.UTF8.GetBytes(keyValue); } }