Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,17 @@ public bool ReadHeader(out string headerName, out string headerValue)

if (index > 0)
{
// For compatability, skip past any whitespace before the colon, even though
// the RFC suggests there shouldn't be any.
int headerNameLength = index;
while (index < _span.Length && IsWhiteSpaceLatin1(_span[index]))
{
index++;
}

CheckResponseMsgFormat(index < _span.Length);
CheckResponseMsgFormat(_span[index] == ':');
HeaderBufferSpan headerNameSpan = _span.Substring(0, index);
HeaderBufferSpan headerNameSpan = _span.Substring(0, headerNameLength);
if (!HttpKnownHeaderNames.TryGetHeaderName(_span.Buffer, _span.Length, out headerName))
{
headerName = headerNameSpan.ToString();
Expand Down Expand Up @@ -111,6 +119,19 @@ private static bool ValidHeaderNameChar(byte c)
return c > ' ' && invalidChars.IndexOf((char)c) < 0;
}

internal static bool IsWhiteSpaceLatin1(byte c)
{
// SPACE
// U+0009 = <control> HORIZONTAL TAB
// U+000a = <control> LINE FEED
// U+000b = <control> VERTICAL TAB
// U+000c = <control> FORM FEED
// U+000d = <control> CARRIAGE RETURN
// U+0085 = <control> NEXT LINE
// U+00a0 = NO-BREAK SPACE
return c == ' ' || (c >= '\x0009' && c <= '\x000d') || c == '\x00a0' || c == '\x0085';
}

private unsafe struct HeaderBufferSpan
{
private readonly byte* _pointer;
Expand Down Expand Up @@ -268,18 +289,6 @@ public override string ToString()
{
return Length == 0 ? string.Empty : HttpRuleParser.DefaultHttpEncoding.GetString(_pointer, Length);
}

private static bool IsWhiteSpaceLatin1(byte c)
{
// U+0009 = <control> HORIZONTAL TAB
// U+000a = <control> LINE FEED
// U+000b = <control> VERTICAL TAB
// U+000c = <control> FORM FEED
// U+000d = <control> CARRIAGE RETURN
// U+0085 = <control> NEXT LINE
// U+00a0 = NO-BREAK SPACE
return c == ' ' || (c >= '\x0009' && c <= '\x000d') || c == '\x00a0' || c == '\x0085';
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,7 @@ await LoopbackServer.ReadRequestAndSendResponseAsync(server,
$"HTTP/1.1 200 OK\r\n" +
$"Date: {DateTimeOffset.UtcNow:R}\r\n" +
$"Set-Cookie: {cookie1.Key}={cookie1.Value}; Path=/\r\n" +
$"Set-Cookie: {cookie2.Key}={cookie2.Value}; Path=/\r\n" +
$"Set-Cookie : {cookie2.Key}={cookie2.Value}; Path=/\r\n" + // space before colon to verify header is trimmed and recognized
$"Set-Cookie: {cookie3.Key}={cookie3.Value}; Path=/\r\n" +
"\r\n");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,19 +109,34 @@ public unsafe void ReadStatusLine_ValidStatusCodeLine_ResponseMessageVersionSet(
#endregion

#region Headers
[Theory]
[InlineData("TestHeader", "Test header value", false)]
[InlineData("TestHeader", "Test header value", true)]
[InlineData("TestHeader", "", false)]
[InlineData("TestHeader", "", true)]
[InlineData("Server", "IIS", false)]
[InlineData("Server", "IIS", true)]
public unsafe void ReadHeader_ValidHeaderLine_HeaderReturned(string expectedHeaderName, string expectedHeaderValue, bool spaceAfterColon)
public static IEnumerable<object[]> ReadHeader_ValidHeaderLine_HeaderReturned_MemberData()
{
string headerLine = $"{expectedHeaderName}:{(spaceAfterColon ? " " : "")}{expectedHeaderValue}";
var namesAndValues = new KeyValuePair<string, string>[]
{
new KeyValuePair<string, string>("TestHeader", "Test header value"),
new KeyValuePair<string, string>("TestHeader", ""),
new KeyValuePair<string, string>("Server", "IIS"),
new KeyValuePair<string, string>("Server", "I:I:S"),
};
var whitespaces = new string[] { "", " ", " ", " \t" };

foreach (KeyValuePair<string, string> nameAndValue in namesAndValues)
{
foreach (string beforeColon in whitespaces) // only "" is valid according to the RFC, but we parse more leniently
{
foreach (string afterColon in whitespaces)
{
yield return new object[] { $"{nameAndValue.Key}{beforeColon}:{afterColon}{nameAndValue.Value}", nameAndValue.Key, nameAndValue.Value };
}
}
}
}

[Theory]
[MemberData(nameof(ReadHeader_ValidHeaderLine_HeaderReturned_MemberData))]
public unsafe void ReadHeader_ValidHeaderLine_HeaderReturned(string headerLine, string expectedHeaderName, string expectedHeaderValue)
{
byte[] buffer = headerLine.Select(c => checked((byte)c)).ToArray();

fixed (byte* pBuffer = buffer)
{
var reader = new CurlResponseHeaderReader(new IntPtr(pBuffer), checked((ulong)buffer.Length));
Expand Down