Skip to content

Commit 61e1572

Browse files
author
msftbot[bot]
authored
Merge pull request #43437 from dotnet-maestro-bot/merge/release/7.0-to-main
[automated] Merge branch 'release/7.0' => 'main'
2 parents f9f59aa + 7f5cf1f commit 61e1572

File tree

45 files changed

+1121
-387
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1121
-387
lines changed

src/Components/Server/src/Microsoft.AspNetCore.Components.Server.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
<Compile Include="$(RepoRoot)src\SignalR\common\Shared\BinaryMessageParser.cs" LinkBase="BlazorPack" />
7070
<Compile Include="$(RepoRoot)src\SignalR\common\Shared\MemoryBufferWriter.cs" LinkBase="BlazorPack" />
7171
<Compile Include="$(RepoRoot)src\SignalR\common\Protocols.MessagePack\src\Protocol\MessagePackHubProtocolWorker.cs" LinkBase="BlazorPack" />
72+
<Compile Include="$(RepoRoot)src\SignalR\common\Shared\TryGetReturnType.cs" LinkBase="BlazorPack" />
7273

7374
<!-- MessagePack -->
7475
<Compile Include="$(MessagePackRoot)BufferWriter.cs" LinkBase="BlazorPack\MessagePack" />

src/Servers/Kestrel/Core/src/Internal/Http/HttpParser.cs

+180-122
Large diffs are not rendered by default.

src/Servers/Kestrel/Core/src/Internal/KestrelServerImpl.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ private static ServiceContext CreateServiceContext(IOptions<KestrelServerOptions
126126
{
127127
Log = trace,
128128
Scheduler = PipeScheduler.ThreadPool,
129-
HttpParser = new HttpParser<Http1ParsingHandler>(trace.IsEnabled(LogLevel.Information)),
129+
HttpParser = new HttpParser<Http1ParsingHandler>(trace.IsEnabled(LogLevel.Information), serverOptions.DisableHttp1LineFeedTerminators),
130130
SystemClock = heartbeatManager,
131131
DateHeaderValueManager = dateHeaderValueManager,
132132
ConnectionManager = connectionManager,

src/Servers/Kestrel/Core/src/KestrelServerOptions.cs

+20
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core;
2525
/// </summary>
2626
public class KestrelServerOptions
2727
{
28+
internal const string DisableHttp1LineFeedTerminatorsSwitchKey = "Microsoft.AspNetCore.Server.Kestrel.DisableHttp1LineFeedTerminators";
29+
2830
// internal to fast-path header decoding when RequestHeaderEncodingSelector is unchanged.
2931
internal static readonly Func<string, Encoding?> DefaultHeaderEncodingSelector = _ => null;
3032

@@ -175,6 +177,24 @@ internal bool EnableWebTransportAndH3Datagrams
175177
set => _enableWebTransportAndH3Datagrams = value;
176178
}
177179

180+
/// <summary>
181+
/// Internal AppContext switch to toggle whether a request line can end with LF only instead of CR/LF.
182+
/// </summary>
183+
private bool? _disableHttp1LineFeedTerminators;
184+
internal bool DisableHttp1LineFeedTerminators
185+
{
186+
get
187+
{
188+
if (!_disableHttp1LineFeedTerminators.HasValue)
189+
{
190+
_disableHttp1LineFeedTerminators = AppContext.TryGetSwitch(DisableHttp1LineFeedTerminatorsSwitchKey, out var disabled) && disabled;
191+
}
192+
193+
return _disableHttp1LineFeedTerminators.Value;
194+
}
195+
set => _disableHttp1LineFeedTerminators = value;
196+
}
197+
178198
/// <summary>
179199
/// Specifies a configuration Action to run for each newly created endpoint. Calling this again will replace
180200
/// the prior action.

src/Servers/Kestrel/Core/src/Microsoft.AspNetCore.Server.Kestrel.Core.csproj

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<Description>Core components of ASP.NET Core Kestrel cross-platform web server.</Description>
@@ -29,6 +29,7 @@
2929
<Compile Include="$(SharedSourceRoot)runtime\Http3\**\*.cs" Link="Shared\runtime\Http3\%(Filename)%(Extension)" />
3030
<Compile Include="$(SharedSourceRoot)Hpack\**\*.cs" Link="Shared\Hpack\%(Filename)%(Extension)" />
3131
<Compile Include="$(SharedSourceRoot)ServerInfrastructure\**\*.cs" />
32+
<Compile Include="$(SharedSourceRoot)CancellationTokenSourcePool.cs" />
3233
<Compile Include="$(RepoRoot)src\Shared\TaskToApm.cs" Link="Internal\TaskToApm.cs" />
3334
</ItemGroup>
3435

src/Servers/Kestrel/Core/src/Middleware/HttpsConnectionMiddleware.cs

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using Microsoft.AspNetCore.Connections;
1313
using Microsoft.AspNetCore.Connections.Features;
1414
using Microsoft.AspNetCore.Http.Features;
15+
using Microsoft.AspNetCore.Internal;
1516
using Microsoft.AspNetCore.Server.Kestrel.Core;
1617
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
1718
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;

src/Servers/Kestrel/Core/test/HttpParserTests.cs

+261-30
Large diffs are not rendered by default.

src/Servers/Kestrel/shared/test/HttpParsingData.cs

+34-3
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,15 @@ public static IEnumerable<string> RequestLineInvalidData
205205
"CUSTOM / HTTP/1.1a\n",
206206
"CUSTOM / HTTP/1.1a\r\n",
207207
"CUSTOM / HTTP/1.1ab\r\n",
208+
"CUSTOM / H\n",
209+
"CUSTOM / HT\n",
210+
"CUSTOM / HTT\n",
211+
"CUSTOM / HTTP\n",
212+
"CUSTOM / HTTP/\n",
213+
"CUSTOM / HTTP/1\n",
214+
"CUSTOM / HTTP/1.\n",
208215
"CUSTOM / hello\r\n",
216+
"CUSTOM / hello\n",
209217
"CUSTOM ? HTTP/1.1\r\n",
210218
"CUSTOM /a?b=cHTTP/1.1\r\n",
211219
"CUSTOM /a%20bHTTP/1.1\r\n",
@@ -217,6 +225,21 @@ public static IEnumerable<string> RequestLineInvalidData
217225
}
218226
}
219227

228+
// This list is valid in quirk mode
229+
public static IEnumerable<string> RequestLineInvalidDataLineFeedTerminator
230+
{
231+
get
232+
{
233+
return new[]
234+
{
235+
"GET / HTTP/1.0\n",
236+
"GET / HTTP/1.1\n",
237+
"CUSTOM / HTTP/1.0\n",
238+
"CUSTOM / HTTP/1.1\n",
239+
};
240+
}
241+
}
242+
220243
// Bad HTTP Methods (invalid according to RFC)
221244
public static IEnumerable<string> MethodWithNonTokenCharData
222245
{
@@ -364,13 +387,19 @@ public static IEnumerable<string> TargetWithNullCharData
364387
"8charact",
365388
};
366389

367-
public static IEnumerable<object[]> RequestHeaderInvalidData => new[]
390+
public static IEnumerable<object[]> RequestHeaderInvalidDataLineFeedTerminator => new[]
368391
{
369392
// Missing CR
370393
new[] { "Header: value\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header: value\x0A") },
371394
new[] { "Header-1: value1\nHeader-2: value2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-1: value1\x0A") },
372395
new[] { "Header-1: value1\r\nHeader-2: value2\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-2: value2\x0A") },
373396

397+
// Empty header name
398+
new[] { ":a\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@":a\x0A") },
399+
};
400+
401+
public static IEnumerable<object[]> RequestHeaderInvalidData => new[]
402+
{
374403
// Line folding
375404
new[] { "Header: line1\r\n line2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@" line2\x0D\x0A") },
376405
new[] { "Header: line1\r\n\tline2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"\x09line2\x0D\x0A") },
@@ -404,7 +433,7 @@ public static IEnumerable<string> TargetWithNullCharData
404433
new[] { "Header-1 value1\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-1 value1\x0D\x0A") },
405434
new[] { "Header-1 value1\r\nHeader-2: value2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-1 value1\x0D\x0A") },
406435
new[] { "Header-1: value1\r\nHeader-2 value2\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-2 value2\x0D\x0A") },
407-
new[] { "\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"\x0A") },
436+
new[] { "HeaderValue1\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"HeaderValue1\x0D\x0A") },
408437

409438
// Starting with whitespace
410439
new[] { " Header: value\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@" Header: value\x0D\x0A") },
@@ -435,11 +464,13 @@ public static IEnumerable<string> TargetWithNullCharData
435464

436465
// Headers not ending in CRLF line
437466
new[] { "Header-1: value1\r\nHeader-2: value2\r\n\r\r", CoreStrings.BadRequest_InvalidRequestHeadersNoCRLF },
438-
new[] { "Header-1: value1\r\nHeader-2: value2\r\n\r ", CoreStrings.BadRequest_InvalidRequestHeadersNoCRLF },
467+
new[] { "Header-1: value1\r\nHeader-2: value2\r\n\r ", CoreStrings.BadRequest_InvalidRequestHeadersNoCRLF },
439468
new[] { "Header-1: value1\r\nHeader-2: value2\r\n\r \n", CoreStrings.BadRequest_InvalidRequestHeadersNoCRLF },
469+
new[] { "Header-1: value1\r\nHeader-2\t: value2 \n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@"Header-2\x09: value2 \x0A") },
440470

441471
// Empty header name
442472
new[] { ": value\r\n\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@": value\x0D\x0A") },
473+
new[] { ":a\r\n", CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(@":a\x0D\x0A") },
443474
};
444475

445476
public static TheoryData<string, string> HostHeaderData

src/Servers/Kestrel/shared/test/TestServiceContext.cs

+7-7
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,17 @@ internal class TestServiceContext : ServiceContext
1717
{
1818
public TestServiceContext()
1919
{
20-
Initialize(NullLoggerFactory.Instance, CreateLoggingTrace(NullLoggerFactory.Instance));
20+
Initialize(NullLoggerFactory.Instance, CreateLoggingTrace(NullLoggerFactory.Instance), false);
2121
}
2222

23-
public TestServiceContext(ILoggerFactory loggerFactory)
23+
public TestServiceContext(ILoggerFactory loggerFactory, bool disableHttp1LineFeedTerminators = true)
2424
{
25-
Initialize(loggerFactory, CreateLoggingTrace(loggerFactory));
25+
Initialize(loggerFactory, CreateLoggingTrace(loggerFactory), disableHttp1LineFeedTerminators);
2626
}
2727

28-
public TestServiceContext(ILoggerFactory loggerFactory, KestrelTrace kestrelTrace)
28+
public TestServiceContext(ILoggerFactory loggerFactory, KestrelTrace kestrelTrace, bool disableHttp1LineFeedTerminators = true)
2929
{
30-
Initialize(loggerFactory, kestrelTrace);
30+
Initialize(loggerFactory, kestrelTrace, disableHttp1LineFeedTerminators);
3131
}
3232

3333
private static KestrelTrace CreateLoggingTrace(ILoggerFactory loggerFactory)
@@ -49,7 +49,7 @@ public void InitializeHeartbeat()
4949
SystemClock = heartbeatManager;
5050
}
5151

52-
private void Initialize(ILoggerFactory loggerFactory, KestrelTrace kestrelTrace)
52+
private void Initialize(ILoggerFactory loggerFactory, KestrelTrace kestrelTrace, bool disableHttp1LineFeedTerminators)
5353
{
5454
LoggerFactory = loggerFactory;
5555
Log = kestrelTrace;
@@ -58,7 +58,7 @@ private void Initialize(ILoggerFactory loggerFactory, KestrelTrace kestrelTrace)
5858
SystemClock = MockSystemClock;
5959
DateHeaderValueManager = new DateHeaderValueManager();
6060
ConnectionManager = new ConnectionManager(Log, ResourceCounter.Unlimited);
61-
HttpParser = new HttpParser<Http1ParsingHandler>(Log.IsEnabled(LogLevel.Information));
61+
HttpParser = new HttpParser<Http1ParsingHandler>(Log.IsEnabled(LogLevel.Information), disableHttp1LineFeedTerminators);
6262
ServerOptions = new KestrelServerOptions
6363
{
6464
AddServerHeader = false

src/Servers/Kestrel/test/InMemory.FunctionalTests/BadHttpRequestTests.cs

+2
Original file line numberDiff line numberDiff line change
@@ -524,5 +524,7 @@ public static TheoryData<string, string> InvalidRequestLineData
524524

525525
public static IEnumerable<object[]> InvalidRequestHeaderData => HttpParsingData.RequestHeaderInvalidData;
526526

527+
public static IEnumerable<object[]> InvalidRequestHeaderDataLineFeedTerminator => HttpParsingData.RequestHeaderInvalidDataLineFeedTerminator;
528+
527529
public static TheoryData<string, string> InvalidHostHeaderData => HttpParsingData.HostHeaderInvalidData;
528530
}

src/Servers/Kestrel/test/InMemory.FunctionalTests/RequestTests.cs

+43
Original file line numberDiff line numberDiff line change
@@ -2284,6 +2284,49 @@ await connection.Receive(
22842284
}
22852285
}
22862286

2287+
[Fact]
2288+
public async Task SingleLineFeedIsSupportedAnywhere()
2289+
{
2290+
// Exercises all combinations of LF and CRLF as line separators.
2291+
// Uses a bit mask for all the possible combinations.
2292+
2293+
var lines = new[]
2294+
{
2295+
$"GET / HTTP/1.1",
2296+
"Content-Length: 0",
2297+
$"Host: localhost",
2298+
"",
2299+
};
2300+
2301+
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory, disableHttp1LineFeedTerminators: false)))
2302+
{
2303+
var mask = Math.Pow(2, lines.Length) - 1;
2304+
2305+
for (var m = 0; m <= mask; m++)
2306+
{
2307+
using (var client = server.CreateConnection())
2308+
{
2309+
var sb = new StringBuilder();
2310+
2311+
for (var pos = 0; pos < lines.Length; pos++)
2312+
{
2313+
sb.Append(lines[pos]);
2314+
var separator = (m & (1 << pos)) != 0 ? "\n" : "\r\n";
2315+
sb.Append(separator);
2316+
}
2317+
2318+
var text = sb.ToString();
2319+
var writer = new StreamWriter(client.Stream, Encoding.GetEncoding("iso-8859-1"));
2320+
await writer.WriteAsync(text).ConfigureAwait(false);
2321+
await writer.FlushAsync().ConfigureAwait(false);
2322+
await client.Stream.FlushAsync().ConfigureAwait(false);
2323+
2324+
await client.Receive("HTTP/1.1 200");
2325+
}
2326+
}
2327+
}
2328+
}
2329+
22872330
public static TheoryData<string, string> HostHeaderData => HttpParsingData.HostHeaderData;
22882331

22892332
private class IntAsClass

src/Servers/Kestrel/Core/src/Internal/CancellationTokenSourcePool.cs renamed to src/Shared/CancellationTokenSourcePool.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
using System.Collections.Concurrent;
55

6-
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
6+
namespace Microsoft.AspNetCore.Internal;
77

88
internal sealed class CancellationTokenSourcePool
99
{

src/SignalR/clients/java/signalr/core/src/main/java/com/microsoft/signalr/CallbackMap.java

+23-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,12 @@ public InvocationHandler put(String target, Object action, Type... types) {
3131
}
3232
}
3333
}
34+
methodHandlers = new ArrayList<>(methodHandlers);
3435
methodHandlers.add(handler);
36+
37+
// replace List in handlers map
38+
handlers.remove(target);
39+
handlers.put(target, methodHandlers);
3540
return handler;
3641
} finally {
3742
lock.unlock();
@@ -41,7 +46,7 @@ public InvocationHandler put(String target, Object action, Type... types) {
4146
public List<InvocationHandler> get(String key) {
4247
try {
4348
lock.lock();
44-
return handlers.get(key);
49+
return this.handlers.get(key);
4550
} finally {
4651
lock.unlock();
4752
}
@@ -55,4 +60,21 @@ public void remove(String key) {
5560
lock.unlock();
5661
}
5762
}
63+
64+
public void remove(String key, InvocationHandler handler) {
65+
try {
66+
lock.lock();
67+
List<InvocationHandler> handlers = this.handlers.get(key);
68+
if (handlers != null) {
69+
handlers = new ArrayList<>(handlers);
70+
handlers.remove(handler);
71+
72+
// replace List in handlers map
73+
this.handlers.remove(key);
74+
this.handlers.put(key, handlers);
75+
}
76+
} finally {
77+
lock.unlock();
78+
}
79+
}
5880
}

src/SignalR/clients/java/signalr/core/src/main/java/com/microsoft/signalr/Subscription.java

+1-4
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@ public class Subscription {
2323
* Removes the client method handler represented by this subscription.
2424
*/
2525
public void unsubscribe() {
26-
List<InvocationHandler> handler = this.handlers.get(target);
27-
if (handler != null) {
28-
handler.remove(this.handler);
29-
}
26+
this.handlers.remove(this.target, this.handler);
3027
}
3128
}

src/SignalR/clients/ts/FunctionalTests/Startup.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<
232232
{
233233
try
234234
{
235-
var result = await hubContext.Clients.Client(id).InvokeAsync<int>("Result");
235+
var result = await hubContext.Clients.Client(id).InvokeAsync<int>("Result", cancellationToken: default);
236236
return result.ToString(CultureInfo.InvariantCulture);
237237
}
238238
catch (Exception ex)

src/SignalR/common/Protocols.Json/src/Microsoft.AspNetCore.SignalR.Protocols.Json.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<Compile Include="$(SignalRSharedSourceRoot)Utf8BufferTextReader.cs" Link="Utf8BufferTextReader.cs" />
1818
<Compile Include="$(SignalRSharedSourceRoot)Utf8BufferTextWriter.cs" Link="Utf8BufferTextWriter.cs" />
1919
<Compile Include="$(SignalRSharedSourceRoot)ReusableUtf8JsonWriter.cs" Link="ReusableUtf8JsonWriter.cs" />
20+
<Compile Include="$(SignalRSharedSourceRoot)TryGetReturnType.cs" Link="TryGetReturnType.cs" />
2021
</ItemGroup>
2122

2223
<ItemGroup>

src/SignalR/common/Protocols.Json/src/Protocol/JsonHubProtocol.cs

+19-4
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,16 @@ public ReadOnlyMemory<byte> GetMessageBytes(HubMessage message)
230230
else
231231
{
232232
// If we have an invocation id already we can parse the end result
233-
var returnType = binder.GetReturnType(invocationId);
234-
result = BindType(ref reader, input, returnType);
233+
var returnType = ProtocolHelper.TryGetReturnType(binder, invocationId);
234+
if (returnType is null)
235+
{
236+
reader.Skip();
237+
result = null;
238+
}
239+
else
240+
{
241+
result = BindType(ref reader, input, returnType);
242+
}
235243
}
236244
}
237245
else if (reader.ValueTextEquals(ItemPropertyNameBytes.EncodedUtf8Bytes))
@@ -408,8 +416,15 @@ public ReadOnlyMemory<byte> GetMessageBytes(HubMessage message)
408416

409417
if (hasResultToken)
410418
{
411-
var returnType = binder.GetReturnType(invocationId);
412-
result = BindType(ref resultToken, input, returnType);
419+
var returnType = ProtocolHelper.TryGetReturnType(binder, invocationId);
420+
if (returnType is null)
421+
{
422+
result = null;
423+
}
424+
else
425+
{
426+
result = BindType(ref resultToken, input, returnType);
427+
}
413428
}
414429

415430
message = BindCompletionMessage(invocationId, error, result, hasResult);

src/SignalR/common/Protocols.MessagePack/src/Microsoft.AspNetCore.SignalR.Protocols.MessagePack.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<Compile Include="$(SignalRSharedSourceRoot)BinaryMessageFormatter.cs" Link="BinaryMessageFormatter.cs" />
1313
<Compile Include="$(SignalRSharedSourceRoot)BinaryMessageParser.cs" Link="BinaryMessageParser.cs" />
1414
<Compile Include="$(SignalRSharedSourceRoot)MemoryBufferWriter.cs" Link="Internal\MemoryBufferWriter.cs" />
15+
<Compile Include="$(SignalRSharedSourceRoot)TryGetReturnType.cs" Link="TryGetReturnType.cs" />
1516
</ItemGroup>
1617

1718
<ItemGroup>

0 commit comments

Comments
 (0)