Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Commit a37fa83

Browse files
authored
Fixed a parser bug found when trying out the array pool (#2450)
- When using the array pool, we get terrible block density and as a result the header parser was failing. - This fixes the case where the parser needed to skip 2 blocks at the end (which is unrealistic). Comparing the current index to the reader index is incorrect since we may end up at the same index in another segment.
1 parent 7382198 commit a37fa83

File tree

2 files changed

+54
-4
lines changed

2 files changed

+54
-4
lines changed

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ public unsafe bool ParseHeaders(TRequestHandler handler, in ReadOnlySequence<byt
213213
var index = reader.CurrentSegmentIndex;
214214
int ch1;
215215
int ch2;
216+
var readAhead = false;
216217

217218
// Fast path, we're still looking at the same span
218219
if (remaining >= 2)
@@ -229,6 +230,8 @@ public unsafe bool ParseHeaders(TRequestHandler handler, in ReadOnlySequence<byt
229230
// Possibly split across spans
230231
ch1 = reader.Read();
231232
ch2 = reader.Read();
233+
234+
readAhead = true;
232235
}
233236

234237
if (ch1 == ByteCR)
@@ -244,7 +247,7 @@ public unsafe bool ParseHeaders(TRequestHandler handler, in ReadOnlySequence<byt
244247
{
245248
// If we got 2 bytes from the span directly so skip ahead 2 so that
246249
// the reader's state matches what we expect
247-
if (index == reader.CurrentSegmentIndex)
250+
if (!readAhead)
248251
{
249252
reader.Advance(2);
250253
}
@@ -259,7 +262,7 @@ public unsafe bool ParseHeaders(TRequestHandler handler, in ReadOnlySequence<byt
259262

260263
// We moved the reader so look ahead 2 bytes so reset both the reader
261264
// and the index
262-
if (index != reader.CurrentSegmentIndex)
265+
if (readAhead)
263266
{
264267
reader = start;
265268
index = reader.CurrentSegmentIndex;

test/Kestrel.Core.Tests/HttpParserTests.cs

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ public void ParsesRequestLine(
2727
string expectedMethod,
2828
string expectedRawTarget,
2929
string expectedRawPath,
30-
// This warns that theory methods should use all of their parameters,
31-
// but this method is using a shared data collection with Http1ConnectionTests.TakeStartLineSetsHttpProtocolProperties and others.
30+
// This warns that theory methods should use all of their parameters,
31+
// but this method is using a shared data collection with Http1ConnectionTests.TakeStartLineSetsHttpProtocolProperties and others.
3232
#pragma warning disable xUnit1026
3333
string expectedDecodedPath,
3434
string expectedQueryString,
@@ -386,6 +386,30 @@ public void ParseRequestLineSplitBufferWithoutNewLineDoesNotUpdateConsumed()
386386
Assert.Equal(buffer.End, examined);
387387
}
388388

389+
[Fact]
390+
public void ParseHeadersWithGratuitouslySplitBuffers()
391+
{
392+
var parser = CreateParser(_nullTrace);
393+
var buffer = BytePerSegmentTestSequenceFactory.Instance.CreateWithContent("Host:\r\nConnection: keep-alive\r\n\r\n");
394+
395+
var requestHandler = new RequestHandler();
396+
var result = parser.ParseHeaders(requestHandler, buffer, out var consumed, out var examined, out _);
397+
398+
Assert.True(result);
399+
}
400+
401+
[Fact]
402+
public void ParseHeadersWithGratuitouslySplitBuffers2()
403+
{
404+
var parser = CreateParser(_nullTrace);
405+
var buffer = BytePerSegmentTestSequenceFactory.Instance.CreateWithContent("A:B\r\nB: C\r\n\r\n");
406+
407+
var requestHandler = new RequestHandler();
408+
var result = parser.ParseHeaders(requestHandler, buffer, out var consumed, out var examined, out _);
409+
410+
Assert.True(result);
411+
}
412+
389413
private void VerifyHeader(
390414
string headerName,
391415
string rawHeaderValue,
@@ -469,5 +493,28 @@ public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> targe
469493
PathEncoded = pathEncoded;
470494
}
471495
}
496+
497+
// Doesn't put empty blocks inbetween every byte
498+
internal class BytePerSegmentTestSequenceFactory : ReadOnlySequenceFactory
499+
{
500+
public static ReadOnlySequenceFactory Instance { get; } = new HttpParserTests.BytePerSegmentTestSequenceFactory();
501+
502+
public override ReadOnlySequence<byte> CreateOfSize(int size)
503+
{
504+
return CreateWithContent(new byte[size]);
505+
}
506+
507+
public override ReadOnlySequence<byte> CreateWithContent(byte[] data)
508+
{
509+
var segments = new List<byte[]>();
510+
511+
foreach (var b in data)
512+
{
513+
segments.Add(new[] { b });
514+
}
515+
516+
return CreateSegments(segments.ToArray());
517+
}
518+
}
472519
}
473520
}

0 commit comments

Comments
 (0)