Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/aspnetcore.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHao Kung <HaoK@users.noreply.github.com>2022-03-11 19:51:57 +0300
committerGitHub <noreply@github.com>2022-03-11 19:51:57 +0300
commitac41f6340fe9121168aba10e6fb7006f293f31fd (patch)
tree5b53895d07cecf95dccd6553af5699e52fc5bc7b
parentca15c1b691e44c35767212d96d0402d33e50f1a9 (diff)
parent7ea70a5218cb7ab4cf0d836bd39df4904d50e813 (diff)
Merge branch 'main' into haok/r2haok/r2
-rw-r--r--src/Components/Components/src/Routing/QueryParameterValueSupplier.cs3
-rw-r--r--src/Http/WebUtilities/src/FormPipeReader.cs24
-rw-r--r--src/Http/WebUtilities/src/Resources.resx9
-rw-r--r--src/Http/WebUtilities/test/FormPipeReaderTests.cs37
-rw-r--r--src/Servers/HttpSys/src/Microsoft.AspNetCore.Server.HttpSys.csproj1
-rw-r--r--src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.cs1
-rw-r--r--src/Servers/Kestrel/Core/src/Internal/Http/HttpHeaders.cs1
-rw-r--r--src/Servers/Kestrel/Core/src/Internal/Http/HttpParser.cs1
-rw-r--r--src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs1
-rw-r--r--src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs1
-rw-r--r--src/Servers/Kestrel/Core/src/Internal/KestrelServerImpl.cs1
-rw-r--r--src/Servers/Kestrel/Core/test/HttpResponseHeadersTests.cs12
-rw-r--r--src/Shared/HttpSys/RequestProcessing/HeaderCollection.cs8
-rw-r--r--src/Shared/ServerInfrastructure/HttpCharacters.cs (renamed from src/Servers/Kestrel/Core/src/Internal/Infrastructure/HttpCharacters.cs)10
14 files changed, 95 insertions, 15 deletions
diff --git a/src/Components/Components/src/Routing/QueryParameterValueSupplier.cs b/src/Components/Components/src/Routing/QueryParameterValueSupplier.cs
index 76d8f09d55..40ae03126c 100644
--- a/src/Components/Components/src/Routing/QueryParameterValueSupplier.cs
+++ b/src/Components/Components/src/Routing/QueryParameterValueSupplier.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Buffers;
+using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Microsoft.AspNetCore.Components.Reflection;
@@ -15,7 +16,7 @@ internal sealed class QueryParameterValueSupplier
{
public static void ClearCache() => _cacheByType.Clear();
- private static readonly Dictionary<Type, QueryParameterValueSupplier?> _cacheByType = new();
+ private static readonly ConcurrentDictionary<Type, QueryParameterValueSupplier?> _cacheByType = new();
// These two arrays contain the same number of entries, and their corresponding positions refer to each other.
// Holding the info like this means we can use Array.BinarySearch with less custom implementation.
diff --git a/src/Http/WebUtilities/src/FormPipeReader.cs b/src/Http/WebUtilities/src/FormPipeReader.cs
index fb7325349a..845ad7bf6b 100644
--- a/src/Http/WebUtilities/src/FormPipeReader.cs
+++ b/src/Http/WebUtilities/src/FormPipeReader.cs
@@ -3,6 +3,7 @@
using System.Buffers;
using System.Diagnostics;
+using System.Globalization;
using System.IO.Pipelines;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -249,8 +250,8 @@ public class FormPipeReader
{
if (!isFinalBlock)
{
- // Don't buffer indefinitely
- if ((uint)(sequenceReader.Consumed - consumedBytes) > (uint)KeyLengthLimit + (uint)ValueLengthLimit)
+ // +2 to account for '&' and '='
+ if ((sequenceReader.Length - consumedBytes) > (long)KeyLengthLimit + (long)ValueLengthLimit + 2)
{
ThrowKeyOrValueTooLargeException();
}
@@ -314,17 +315,30 @@ public class FormPipeReader
private void ThrowKeyOrValueTooLargeException()
{
- throw new InvalidDataException($"Form key length limit {KeyLengthLimit} or value length limit {ValueLengthLimit} exceeded.");
+ throw new InvalidDataException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ Resources.FormPipeReader_KeyOrValueTooLarge,
+ KeyLengthLimit,
+ ValueLengthLimit));
}
private void ThrowKeyTooLargeException()
{
- throw new InvalidDataException($"Form key length limit {KeyLengthLimit} exceeded.");
+ throw new InvalidDataException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ Resources.FormPipeReader_KeyTooLarge,
+ KeyLengthLimit));
}
private void ThrowValueTooLargeException()
{
- throw new InvalidDataException($"Form value length limit {ValueLengthLimit} exceeded.");
+ throw new InvalidDataException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ Resources.FormPipeReader_ValueTooLarge,
+ ValueLengthLimit));
}
[SkipLocalsInit]
diff --git a/src/Http/WebUtilities/src/Resources.resx b/src/Http/WebUtilities/src/Resources.resx
index a32d2db5cc..d055be3bad 100644
--- a/src/Http/WebUtilities/src/Resources.resx
+++ b/src/Http/WebUtilities/src/Resources.resx
@@ -117,6 +117,15 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
+ <data name="FormPipeReader_KeyOrValueTooLarge" xml:space="preserve">
+ <value>Form key length limit {0} or value length limit {1} exceeded.</value>
+ </data>
+ <data name="FormPipeReader_KeyTooLarge" xml:space="preserve">
+ <value>Form key length limit {0} exceeded.</value>
+ </data>
+ <data name="FormPipeReader_ValueTooLarge" xml:space="preserve">
+ <value>Form value length limit {0} exceeded.</value>
+ </data>
<data name="HttpRequestStreamReader_StreamNotReadable" xml:space="preserve">
<value>The stream must support reading.</value>
</data>
diff --git a/src/Http/WebUtilities/test/FormPipeReaderTests.cs b/src/Http/WebUtilities/test/FormPipeReaderTests.cs
index 1b3b88c594..dac33ec25a 100644
--- a/src/Http/WebUtilities/test/FormPipeReaderTests.cs
+++ b/src/Http/WebUtilities/test/FormPipeReaderTests.cs
@@ -2,6 +2,7 @@
// 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;
@@ -209,6 +210,30 @@ public class FormPipeReaderTests
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")]
@@ -610,4 +635,16 @@ public class FormPipeReaderTests
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);
+ }
}
diff --git a/src/Servers/HttpSys/src/Microsoft.AspNetCore.Server.HttpSys.csproj b/src/Servers/HttpSys/src/Microsoft.AspNetCore.Server.HttpSys.csproj
index dff0c5861c..07f2d99eed 100644
--- a/src/Servers/HttpSys/src/Microsoft.AspNetCore.Server.HttpSys.csproj
+++ b/src/Servers/HttpSys/src/Microsoft.AspNetCore.Server.HttpSys.csproj
@@ -18,6 +18,7 @@
<Compile Include="$(SharedSourceRoot)HttpSys\**\*.cs" />
<Compile Include="$(SharedSourceRoot)Buffers.MemoryPool\*.cs" LinkBase="MemoryPool" />
<Compile Include="$(SharedSourceRoot)ServerInfrastructure\StringUtilities.cs" LinkBase="ServerInfrastructure\StringUtilities.cs" />
+ <Compile Include="$(SharedSourceRoot)ServerInfrastructure\HttpCharacters.cs" LinkBase="ServerInfrastructure\HttpCharacters.cs" />
<Compile Include="$(SharedSourceRoot)TaskToApm.cs" />
</ItemGroup>
diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.cs b/src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.cs
index 230cbc3f90..64762913a8 100644
--- a/src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.cs
+++ b/src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.cs
@@ -7,6 +7,7 @@ using System.Globalization;
using System.IO.Pipelines;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Features;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/HttpHeaders.cs b/src/Servers/Kestrel/Core/src/Internal/Http/HttpHeaders.cs
index 95f7e72378..5d6682cf71 100644
--- a/src/Servers/Kestrel/Core/src/Internal/Http/HttpHeaders.cs
+++ b/src/Servers/Kestrel/Core/src/Internal/Http/HttpHeaders.cs
@@ -8,7 +8,6 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/HttpParser.cs b/src/Servers/Kestrel/Core/src/Internal/Http/HttpParser.cs
index 80ec7fd95b..658990cc85 100644
--- a/src/Servers/Kestrel/Core/src/Internal/Http/HttpParser.cs
+++ b/src/Servers/Kestrel/Core/src/Internal/Http/HttpParser.cs
@@ -5,6 +5,7 @@ using System.Buffers;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs
index 9c94e933bd..54f4e14a0c 100644
--- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs
+++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs
@@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.Extensions.Primitives;
+using HttpCharacters = Microsoft.AspNetCore.Http.HttpCharacters;
using HttpMethods = Microsoft.AspNetCore.Http.HttpMethods;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
diff --git a/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs b/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs
index 35a272cfcc..462e56533e 100644
--- a/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs
+++ b/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs
@@ -20,6 +20,7 @@ using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using HttpMethod = Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpMethod;
using HttpMethods = Microsoft.AspNetCore.Http.HttpMethods;
+using HttpCharacters = Microsoft.AspNetCore.Http.HttpCharacters;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3;
diff --git a/src/Servers/Kestrel/Core/src/Internal/KestrelServerImpl.cs b/src/Servers/Kestrel/Core/src/Internal/KestrelServerImpl.cs
index 578c0310d4..8a37f6d6d2 100644
--- a/src/Servers/Kestrel/Core/src/Internal/KestrelServerImpl.cs
+++ b/src/Servers/Kestrel/Core/src/Internal/KestrelServerImpl.cs
@@ -7,6 +7,7 @@ using System.Linq;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
diff --git a/src/Servers/Kestrel/Core/test/HttpResponseHeadersTests.cs b/src/Servers/Kestrel/Core/test/HttpResponseHeadersTests.cs
index 893a1fd3ed..aaca50f66e 100644
--- a/src/Servers/Kestrel/Core/test/HttpResponseHeadersTests.cs
+++ b/src/Servers/Kestrel/Core/test/HttpResponseHeadersTests.cs
@@ -159,6 +159,18 @@ public class HttpResponseHeadersTests
});
}
+ [Fact]
+ public void AddingTabCharactersToHeaderPropertyWorks()
+ {
+ var responseHeaders = (IHeaderDictionary)new HttpResponseHeaders();
+
+ // Known special header
+ responseHeaders.Allow = "Da\tta";
+
+ // Unknown header fallback
+ responseHeaders.Accept = "Da\tta";
+ }
+
[Theory]
[InlineData("\r\nData")]
[InlineData("\0Data")]
diff --git a/src/Shared/HttpSys/RequestProcessing/HeaderCollection.cs b/src/Shared/HttpSys/RequestProcessing/HeaderCollection.cs
index 687e55921f..cfcf13ae2a 100644
--- a/src/Shared/HttpSys/RequestProcessing/HeaderCollection.cs
+++ b/src/Shared/HttpSys/RequestProcessing/HeaderCollection.cs
@@ -272,12 +272,10 @@ internal class HeaderCollection : IHeaderDictionary
{
if (headerCharacters != null)
{
- foreach (var ch in headerCharacters)
+ var invalid = HttpCharacters.IndexOfInvalidFieldValueChar(headerCharacters);
+ if (invalid >= 0)
{
- if (ch < 0x20)
- {
- throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Invalid control character in header: 0x{0:X2}", (byte)ch));
- }
+ throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Invalid control character in header: 0x{0:X2}", headerCharacters[invalid]));
}
}
}
diff --git a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/HttpCharacters.cs b/src/Shared/ServerInfrastructure/HttpCharacters.cs
index d344dabeda..9892ed6fdb 100644
--- a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/HttpCharacters.cs
+++ b/src/Shared/ServerInfrastructure/HttpCharacters.cs
@@ -3,7 +3,7 @@
using System.Runtime.CompilerServices;
-namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
+namespace Microsoft.AspNetCore.Http;
internal static class HttpCharacters
{
@@ -107,6 +107,9 @@ internal static class HttpCharacters
{
// field-value https://tools.ietf.org/html/rfc7230#section-3.2
var fieldValue = new bool[_tableSize];
+
+ fieldValue[0x9] = true; // HTAB
+
for (var c = 0x20; c <= 0x7e; c++) // VCHAR and SP
{
fieldValue[c] = true;
@@ -182,7 +185,8 @@ internal static class HttpCharacters
return -1;
}
- // Disallows control characters and anything more than 0x7F
+ // Follows field-value rules in https://tools.ietf.org/html/rfc7230#section-3.2
+ // Disallows characters > 0x7E.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOfInvalidFieldValueChar(string s)
{
@@ -200,7 +204,7 @@ internal static class HttpCharacters
return -1;
}
- // Disallows control characters but allows extended characters > 0x7F
+ // Follows field-value rules for chars <= 0x7F. Allows extended characters > 0x7F.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOfInvalidFieldValueCharExtended(string s)
{