Skip to content

Commit a7c039b

Browse files
authored
Handle spaces after request line #41824 (#41823)
1 parent f6109aa commit a7c039b

File tree

2 files changed

+56
-1
lines changed

2 files changed

+56
-1
lines changed

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,28 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
1616
public class HttpParser<TRequestHandler> : IHttpParser<TRequestHandler> where TRequestHandler : IHttpHeadersHandler, IHttpRequestLineHandler
1717
{
1818
private readonly bool _showErrorDetails;
19+
private readonly bool _allowSpaceAfterRequestLine;
1920

2021
public HttpParser() : this(showErrorDetails: true)
2122
{
2223
}
2324

2425
public HttpParser(bool showErrorDetails)
26+
: this (showErrorDetails, CheckAllowSpaceAfterRequestLine())
27+
{
28+
}
29+
30+
internal HttpParser(bool showErrorDetails, bool allowSpaceAfterRequestLine)
2531
{
2632
_showErrorDetails = showErrorDetails;
33+
_allowSpaceAfterRequestLine = allowSpaceAfterRequestLine;
34+
}
35+
36+
private static bool CheckAllowSpaceAfterRequestLine()
37+
{
38+
// This mitigation is temporary and 6.0 specific, we do not anticipate porting this feature to later versions.
39+
AppContext.TryGetSwitch("Microsoft.AspNetCore.Server.Kestrel.AllowSpaceAfterRequestLine", out var allowSpaceAfterRequestLine);
40+
return allowSpaceAfterRequestLine;
2741
}
2842

2943
// byte types don't have a data type annotation so we pre-cast them; to avoid in-place casts
@@ -48,7 +62,25 @@ public bool ParseRequestLine(TRequestHandler handler, ref SequenceReader<byte> r
4862

4963
if (reader.TryReadTo(out ReadOnlySpan<byte> requestLine, ByteLF, advancePastDelimiter: true))
5064
{
65+
if (_allowSpaceAfterRequestLine)
66+
{
67+
// Skip a space after the request line
68+
if (reader.TryPeek(out byte s) && s == ByteSpace)
69+
{
70+
reader.Advance(1);
71+
}
72+
// Don't parse the request line until we've started receiving headers.
73+
// Need to make sure we skipped an extra space if present.
74+
else if (reader.End)
75+
{
76+
// Reset state so we can try again.
77+
reader.Rewind(requestLine.Length + 1);
78+
return false;
79+
}
80+
}
81+
5182
ParseRequestLine(handler, requestLine);
83+
5284
return true;
5385
}
5486

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,28 @@ public void ParsesRequestLine(
5252
Assert.True(buffer.Slice(examined).IsEmpty);
5353
}
5454

55+
[Fact]
56+
public void ParsesRequestLineWithTrailingSpace()
57+
{
58+
var parser = CreateParser(_nullTrace, allowSpaceAfterRequestLine: true);
59+
var buffer = new ReadOnlySequence<byte>(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\n "));
60+
var requestHandler = new RequestHandler();
61+
62+
Assert.False(ParseRequestLine(parser, requestHandler, buffer.Slice(0, buffer.Length - 1), out var consumed, out var examined));
63+
Assert.True(buffer.Slice(0, consumed).IsEmpty);
64+
Assert.Equal(buffer.Length - 1, buffer.Slice(0, examined).Length);
65+
66+
Assert.True(ParseRequestLine(parser, requestHandler, buffer, out consumed, out examined));
67+
Assert.True(buffer.Slice(consumed).IsEmpty);
68+
Assert.True(buffer.Slice(examined).IsEmpty);
69+
70+
Assert.Equal(HttpMethods.Get, requestHandler.Method);
71+
Assert.Equal("HTTP/1.1", requestHandler.Version);
72+
Assert.Equal("/", requestHandler.RawTarget);
73+
Assert.Equal("/", requestHandler.RawPath);
74+
Assert.Equal("HTTP/1.1", requestHandler.Version);
75+
}
76+
5577
[Theory]
5678
[MemberData(nameof(RequestLineIncompleteData))]
5779
public void ParseRequestLineReturnsFalseWhenGivenIncompleteRequestLines(string requestLine)
@@ -536,7 +558,8 @@ private void VerifyRawHeaders(string rawHeaders, IEnumerable<string> expectedHea
536558
Assert.True(buffer.Slice(reader.Position).IsEmpty);
537559
}
538560

539-
private IHttpParser<RequestHandler> CreateParser(IKestrelTrace log) => new HttpParser<RequestHandler>(log.IsEnabled(LogLevel.Information));
561+
private IHttpParser<RequestHandler> CreateParser(IKestrelTrace log, bool allowSpaceAfterRequestLine = false)
562+
=> new HttpParser<RequestHandler>(log.IsEnabled(LogLevel.Information), allowSpaceAfterRequestLine);
540563

541564
public static IEnumerable<object[]> RequestLineValidData => HttpParsingData.RequestLineValidData;
542565

0 commit comments

Comments
 (0)