Unify header rejection messages.

- Log bad headers with escaped non-vchar characters
This commit is contained in:
Cesar Blum Silveira 2017-03-07 17:25:15 -08:00
parent dbcf34388e
commit bb973decb8
7 changed files with 109 additions and 166 deletions

View File

@ -22,20 +22,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel
BadHttpRequestException ex;
switch (reason)
{
case RequestRejectionReason.HeadersCorruptedInvalidHeaderSequence:
ex = new BadHttpRequestException("Headers corrupted, invalid header sequence.", StatusCodes.Status400BadRequest);
break;
case RequestRejectionReason.NoColonCharacterFoundInHeaderLine:
ex = new BadHttpRequestException("No ':' character found in header line.", StatusCodes.Status400BadRequest);
break;
case RequestRejectionReason.WhitespaceIsNotAllowedInHeaderName:
ex = new BadHttpRequestException("Whitespace is not allowed in header name.", StatusCodes.Status400BadRequest);
break;
case RequestRejectionReason.HeaderValueMustNotContainCR:
ex = new BadHttpRequestException("Header value must not contain CR characters.", StatusCodes.Status400BadRequest);
break;
case RequestRejectionReason.HeaderValueLineFoldingNotSupported:
ex = new BadHttpRequestException("Header value line folding not supported.", StatusCodes.Status400BadRequest);
case RequestRejectionReason.InvalidRequestHeadersNoCRLF:
ex = new BadHttpRequestException("Invalid request headers: missing final CRLF in header fields.", StatusCodes.Status400BadRequest);
break;
case RequestRejectionReason.InvalidRequestLine:
ex = new BadHttpRequestException("Invalid request line.", StatusCodes.Status400BadRequest);
@ -58,24 +46,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel
case RequestRejectionReason.ChunkedRequestIncomplete:
ex = new BadHttpRequestException("Chunked request incomplete.", StatusCodes.Status400BadRequest);
break;
case RequestRejectionReason.PathContainsNullCharacters:
ex = new BadHttpRequestException("The path contains null characters.", StatusCodes.Status400BadRequest);
break;
case RequestRejectionReason.InvalidCharactersInHeaderName:
ex = new BadHttpRequestException("Invalid characters in header name.", StatusCodes.Status400BadRequest);
break;
case RequestRejectionReason.NonAsciiOrNullCharactersInInputString:
ex = new BadHttpRequestException("The input string contains non-ASCII or null characters.", StatusCodes.Status400BadRequest);
break;
case RequestRejectionReason.RequestLineTooLong:
ex = new BadHttpRequestException("Request line too long.", StatusCodes.Status414UriTooLong);
break;
case RequestRejectionReason.HeadersExceedMaxTotalSize:
ex = new BadHttpRequestException("Request headers too long.", StatusCodes.Status431RequestHeaderFieldsTooLarge);
break;
case RequestRejectionReason.MissingCRInHeaderLine:
ex = new BadHttpRequestException("No CR character found in header line.", StatusCodes.Status400BadRequest);
break;
case RequestRejectionReason.TooManyHeaders:
ex = new BadHttpRequestException("Request contains too many headers.", StatusCodes.Status431RequestHeaderFieldsTooLarge);
break;
@ -95,7 +74,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel
switch (reason)
{
case RequestRejectionReason.InvalidRequestLine:
ex = new BadHttpRequestException($"Invalid request line: {value}", StatusCodes.Status400BadRequest);
ex = new BadHttpRequestException($"Invalid request line: '{value}'", StatusCodes.Status400BadRequest);
break;
case RequestRejectionReason.InvalidRequestHeader:
ex = new BadHttpRequestException($"Invalid request header: '{value}'", StatusCodes.Status400BadRequest);
break;
case RequestRejectionReason.InvalidContentLength:
ex = new BadHttpRequestException($"Invalid content length: {value}", StatusCodes.Status400BadRequest);

View File

@ -316,11 +316,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
}
// Headers don't end in CRLF line.
RejectRequest(RequestRejectionReason.HeadersCorruptedInvalidHeaderSequence);
}
else if(ch1 == ByteSpace || ch1 == ByteTab)
{
RejectRequest(RequestRejectionReason.WhitespaceIsNotAllowedInHeaderName);
RejectRequest(RequestRejectionReason.InvalidRequestHeadersNoCRLF);
}
// We moved the reader so look ahead 2 bytes so reset both the reader
@ -390,7 +386,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe int FindEndOfName(byte* headerLine, int length)
private unsafe int FindEndOfName(byte* headerLine, int length)
{
var index = 0;
var sawWhitespace = false;
@ -407,14 +403,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
}
}
if (index == length)
if (index == length || sawWhitespace)
{
RejectRequest(RequestRejectionReason.NoColonCharacterFoundInHeaderLine);
}
if (sawWhitespace)
{
RejectRequest(RequestRejectionReason.WhitespaceIsNotAllowedInHeaderName);
RejectRequestHeader(headerLine, length);
}
return index;
}
@ -427,11 +420,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
if (headerLine[valueEnd + 2] != ByteLF)
{
RejectRequest(RequestRejectionReason.HeaderValueMustNotContainCR);
RejectRequestHeader(headerLine, length);
}
if (headerLine[valueEnd + 1] != ByteCR)
{
RejectRequest(RequestRejectionReason.MissingCRInHeaderLine);
RejectRequestHeader(headerLine, length);
}
// Skip colon from value start
@ -446,16 +439,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
}
else if (ch == ByteCR)
{
RejectRequest(RequestRejectionReason.HeaderValueMustNotContainCR);
RejectRequestHeader(headerLine, length);
}
}
// Check for CR in value
var i = valueStart + 1;
if (Contains(headerLine + i, valueEnd - i, ByteCR))
{
RejectRequest(RequestRejectionReason.HeaderValueMustNotContainCR);
RejectRequestHeader(headerLine, length);
}
// Ignore end whitespace
@ -557,9 +549,25 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
Log.IsEnabled(LogLevel.Information) ? span.GetAsciiStringEscaped(MaxRequestLineError) : string.Empty);
}
private unsafe void RejectRequestHeader(byte* headerLine, int length)
{
RejectRequestHeader(new Span<byte>(headerLine, length));
}
private void RejectRequestHeader(Span<byte> span)
{
throw GetRejectRequestHeaderException(span);
}
private BadHttpRequestException GetRejectRequestHeaderException(Span<byte> span)
{
const int MaxRequestHeaderError = 128;
return BadHttpRequestException.GetException(RequestRejectionReason.InvalidRequestHeader,
Log.IsEnabled(LogLevel.Information) ? span.GetAsciiStringEscaped(MaxRequestHeaderError) : string.Empty);
}
public void Reset()
{
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

View File

@ -6,12 +6,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
public enum RequestRejectionReason
{
UnrecognizedHTTPVersion,
HeadersCorruptedInvalidHeaderSequence,
NoColonCharacterFoundInHeaderLine,
WhitespaceIsNotAllowedInHeaderName,
HeaderValueMustNotContainCR,
HeaderValueLineFoldingNotSupported,
InvalidRequestLine,
InvalidRequestHeader,
InvalidRequestHeadersNoCRLF,
MalformedRequestInvalidHeaders,
InvalidContentLength,
MultipleContentLengths,
@ -19,12 +16,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
BadChunkSuffix,
BadChunkSizeData,
ChunkedRequestIncomplete,
PathContainsNullCharacters,
InvalidCharactersInHeaderName,
NonAsciiOrNullCharactersInInputString,
RequestLineTooLong,
HeadersExceedMaxTotalSize,
MissingCRInHeaderLine,
TooManyHeaders,
RequestTimeout,
FinalTransferCodingNotChunked,

View File

@ -133,7 +133,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
foreach (var requestLine in HttpParsingData.RequestLineInvalidData)
{
data.Add(requestLine, $"Invalid request line: {requestLine.Replace("\r", "\\x0D").Replace("\n", "\\x0A")}");
data.Add(requestLine, $"Invalid request line: '{requestLine.Replace("\r", "\\x0D").Replace("\n", "\\x0A")}'");
}
foreach (var requestLine in HttpParsingData.RequestLineWithEncodedNullCharInTargetData)

View File

@ -81,25 +81,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
_pipelineFactory.Dispose();
}
[Fact]
public async Task TakeMessageHeadersThrowsOnHeaderValueWithLineFolding_CharacterNotAvailableOnFirstAttempt()
{
await _input.Writer.WriteAsync(Encoding.ASCII.GetBytes("Header-1: value1\r\n"));
var readableBuffer = (await _input.Reader.ReadAsync()).Buffer;
Assert.False(_frame.TakeMessageHeaders(readableBuffer, out _consumed, out _examined));
_input.Reader.Advance(_consumed, _examined);
await _input.Writer.WriteAsync(Encoding.ASCII.GetBytes(" "));
readableBuffer = (await _input.Reader.ReadAsync()).Buffer;
var exception = Assert.Throws<BadHttpRequestException>(() => _frame.TakeMessageHeaders(readableBuffer, out _consumed, out _examined));
_input.Reader.Advance(_consumed, _examined);
Assert.Equal("Whitespace is not allowed in header name.", exception.Message);
Assert.Equal(StatusCodes.Status400BadRequest, exception.StatusCode);
}
[Fact]
public async Task TakeMessageHeadersThrowsWhenHeadersExceedTotalSizeLimit()
{

View File

@ -108,7 +108,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var exception = Assert.Throws<BadHttpRequestException>(() =>
parser.ParseRequestLine(Mock.Of<IHttpRequestLineHandler>(), buffer, out var consumed, out var examined));
Assert.Equal($"Invalid request line: {requestLine.Replace("\r", "\\x0D").Replace("\n", "\\x0A")}", exception.Message);
Assert.Equal($"Invalid request line: '{requestLine.Replace("\r", "\\x0D").Replace("\n", "\\x0A")}'", exception.Message);
Assert.Equal(StatusCodes.Status400BadRequest, (exception as BadHttpRequestException).StatusCode);
}
@ -306,7 +306,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[MemberData(nameof(RequestHeaderInvalidData))]
public void ParseHeadersThrowsOnInvalidRequestHeaders(string rawHeaders, string expectedExceptionMessage)
{
var parser = CreateParser(Mock.Of<IKestrelTrace>());
var mockTrace = new Mock<IKestrelTrace>();
mockTrace
.Setup(trace => trace.IsEnabled(LogLevel.Information))
.Returns(true);
var parser = CreateParser(mockTrace.Object);
var buffer = ReadableBuffer.Create(Encoding.ASCII.GetBytes(rawHeaders));
var exception = Assert.Throws<BadHttpRequestException>(() =>

View File

@ -203,106 +203,79 @@ namespace Microsoft.AspNetCore.Testing
"8charact",
};
public static IEnumerable<object[]> RequestHeaderInvalidData
public static IEnumerable<object[]> RequestHeaderInvalidData => new[]
{
get
{
// Line folding
var headersWithLineFolding = new[]
{
"Header: line1\r\n line2\r\n\r\n",
"Header: line1\r\n\tline2\r\n\r\n",
"Header: line1\r\n line2\r\n\r\n",
"Header: line1\r\n \tline2\r\n\r\n",
"Header: line1\r\n\t line2\r\n\r\n",
"Header: line1\r\n\t\tline2\r\n\r\n",
"Header: line1\r\n \t\t line2\r\n\r\n",
"Header: line1\r\n \t \t line2\r\n\r\n",
"Header-1: multi\r\n line\r\nHeader-2: value2\r\n\r\n",
"Header-1: value1\r\nHeader-2: multi\r\n line\r\n\r\n",
"Header-1: value1\r\n Header-2: value2\r\n\r\n",
"Header-1: value1\r\n\tHeader-2: value2\r\n\r\n",
};
// Missing CR
new[] { "Header: value\n\r\n", @"Invalid request header: 'Header: value\x0A'" },
new[] { "Header-1: value1\nHeader-2: value2\r\n\r\n", @"Invalid request header: 'Header-1: value1\x0A'" },
new[] { "Header-1: value1\r\nHeader-2: value2\n\r\n", @"Invalid request header: 'Header-2: value2\x0A'" },
// CR in value
var headersWithCRInValue = new[]
{
"Header-1: value1\r\r\n",
"Header-1: val\rue1\r\n",
"Header-1: value1\rHeader-2: value2\r\n\r\n",
"Header-1: value1\r\nHeader-2: value2\r\r\n",
"Header-1: value1\r\nHeader-2: v\ralue2\r\n",
"Header-1: Value__\rVector16________Vector32\r\n",
"Header-1: Value___Vector16\r________Vector32\r\n",
"Header-1: Value___Vector16_______\rVector32\r\n",
"Header-1: Value___Vector16________Vector32\r\r\n",
"Header-1: Value___Vector16________Vector32_\r\r\n",
"Header-1: Value___Vector16________Vector32Value___Vector16_______\rVector32\r\n",
"Header-1: Value___Vector16________Vector32Value___Vector16________Vector32\r\r\n",
"Header-1: Value___Vector16________Vector32Value___Vector16________Vector32_\r\r\n",
};
// Line folding
new[] { "Header: line1\r\n line2\r\n\r\n", @"Invalid request header: ' line2\x0D\x0A'" },
new[] { "Header: line1\r\n\tline2\r\n\r\n", @"Invalid request header: '\x09line2\x0D\x0A'" },
new[] { "Header: line1\r\n line2\r\n\r\n", @"Invalid request header: ' line2\x0D\x0A'" },
new[] { "Header: line1\r\n \tline2\r\n\r\n", @"Invalid request header: ' \x09line2\x0D\x0A'" },
new[] { "Header: line1\r\n\t line2\r\n\r\n", @"Invalid request header: '\x09 line2\x0D\x0A'" },
new[] { "Header: line1\r\n\t\tline2\r\n\r\n", @"Invalid request header: '\x09\x09line2\x0D\x0A'" },
new[] { "Header: line1\r\n \t\t line2\r\n\r\n", @"Invalid request header: ' \x09\x09 line2\x0D\x0A'" },
new[] { "Header: line1\r\n \t \t line2\r\n\r\n", @"Invalid request header: ' \x09 \x09 line2\x0D\x0A'" },
new[] { "Header-1: multi\r\n line\r\nHeader-2: value2\r\n\r\n", @"Invalid request header: ' line\x0D\x0A'" },
new[] { "Header-1: value1\r\nHeader-2: multi\r\n line\r\n\r\n", @"Invalid request header: ' line\x0D\x0A'" },
new[] { "Header-1: value1\r\n Header-2: value2\r\n\r\n", @"Invalid request header: ' Header-2: value2\x0D\x0A'" },
new[] { "Header-1: value1\r\n\tHeader-2: value2\r\n\r\n", @"Invalid request header: '\x09Header-2: value2\x0D\x0A'" },
// Missing colon
var headersWithMissingColon = new[]
{
"Header-1 value1\r\n\r\n",
"Header-1 value1\r\nHeader-2: value2\r\n\r\n",
"Header-1: value1\r\nHeader-2 value2\r\n\r\n",
"\n"
};
// CR in value
new[] { "Header-1: value1\r\r\n", @"Invalid request header: 'Header-1: value1\x0D\x0D\x0A'" },
new[] { "Header-1: val\rue1\r\n", @"Invalid request header: 'Header-1: val\x0Due1\x0D\x0A'" },
new[] { "Header-1: value1\rHeader-2: value2\r\n\r\n", @"Invalid request header: 'Header-1: value1\x0DHeader-2: value2\x0D\x0A'" },
new[] { "Header-1: value1\r\nHeader-2: value2\r\r\n", @"Invalid request header: 'Header-2: value2\x0D\x0D\x0A'" },
new[] { "Header-1: value1\r\nHeader-2: v\ralue2\r\n", @"Invalid request header: 'Header-2: v\x0Dalue2\x0D\x0A'" },
new[] { "Header-1: Value__\rVector16________Vector32\r\n", @"Invalid request header: 'Header-1: Value__\x0DVector16________Vector32\x0D\x0A'" },
new[] { "Header-1: Value___Vector16\r________Vector32\r\n", @"Invalid request header: 'Header-1: Value___Vector16\x0D________Vector32\x0D\x0A'" },
new[] { "Header-1: Value___Vector16_______\rVector32\r\n", @"Invalid request header: 'Header-1: Value___Vector16_______\x0DVector32\x0D\x0A'" },
new[] { "Header-1: Value___Vector16________Vector32\r\r\n", @"Invalid request header: 'Header-1: Value___Vector16________Vector32\x0D\x0D\x0A'" },
new[] { "Header-1: Value___Vector16________Vector32_\r\r\n", @"Invalid request header: 'Header-1: Value___Vector16________Vector32_\x0D\x0D\x0A'" },
new[] { "Header-1: Value___Vector16________Vector32Value___Vector16_______\rVector32\r\n", @"Invalid request header: 'Header-1: Value___Vector16________Vector32Value___Vector16_______\x0DVector32\x0D\x0A'" },
new[] { "Header-1: Value___Vector16________Vector32Value___Vector16________Vector32\r\r\n", @"Invalid request header: 'Header-1: Value___Vector16________Vector32Value___Vector16________Vector32\x0D\x0D\x0A'" },
new[] { "Header-1: Value___Vector16________Vector32Value___Vector16________Vector32_\r\r\n", @"Invalid request header: 'Header-1: Value___Vector16________Vector32Value___Vector16________Vector32_\x0D\x0D\x0A'" },
// Starting with whitespace
var headersStartingWithWhitespace = new[]
{
" Header: value\r\n\r\n",
"\tHeader: value\r\n\r\n",
" Header-1: value1\r\nHeader-2: value2\r\n\r\n",
"\tHeader-1: value1\r\nHeader-2: value2\r\n\r\n",
};
// Missing colon
new[] { "Header-1 value1\r\n\r\n", @"Invalid request header: 'Header-1 value1\x0D\x0A'" },
new[] { "Header-1 value1\r\nHeader-2: value2\r\n\r\n", @"Invalid request header: 'Header-1 value1\x0D\x0A'" },
new[] { "Header-1: value1\r\nHeader-2 value2\r\n\r\n", @"Invalid request header: 'Header-2 value2\x0D\x0A'" },
new[] { "\n", @"Invalid request header: '\x0A'" },
// Whitespace in header name
var headersWithWithspaceInName = new[]
{
"Header : value\r\n\r\n",
"Header\t: value\r\n\r\n",
"Header\r: value\r\n\r\n",
"Header_\rVector16: value\r\n\r\n",
"Header__Vector16\r: value\r\n\r\n",
"Header__Vector16_\r: value\r\n\r\n",
"Header_\rVector16________Vector32: value\r\n\r\n",
"Header__Vector16________Vector32\r: value\r\n\r\n",
"Header__Vector16________Vector32_\r: value\r\n\r\n",
"Header__Vector16________Vector32Header_\rVector16________Vector32: value\r\n\r\n",
"Header__Vector16________Vector32Header__Vector16________Vector32\r: value\r\n\r\n",
"Header__Vector16________Vector32Header__Vector16________Vector32_\r: value\r\n\r\n",
"Header 1: value1\r\nHeader-2: value2\r\n\r\n",
"Header 1 : value1\r\nHeader-2: value2\r\n\r\n",
"Header 1\t: value1\r\nHeader-2: value2\r\n\r\n",
"Header 1\r: value1\r\nHeader-2: value2\r\n\r\n",
"Header-1: value1\r\nHeader 2: value2\r\n\r\n",
"Header-1: value1\r\nHeader-2 : value2\r\n\r\n",
"Header-1: value1\r\nHeader-2\t: value2\r\n\r\n",
};
// Starting with whitespace
new[] { " Header: value\r\n\r\n", @"Invalid request header: ' Header: value\x0D\x0A'" },
new[] { "\tHeader: value\r\n\r\n", @"Invalid request header: '\x09Header: value\x0D\x0A'" },
new[] { " Header-1: value1\r\nHeader-2: value2\r\n\r\n", @"Invalid request header: ' Header-1: value1\x0D\x0A'" },
new[] { "\tHeader-1: value1\r\nHeader-2: value2\r\n\r\n", @"Invalid request header: '\x09Header-1: value1\x0D\x0A'" },
// Headers not ending in CRLF line
var headersNotEndingInCrLfLine = new[]
{
"Header-1: value1\r\nHeader-2: value2\r\n\r\r",
"Header-1: value1\r\nHeader-2: value2\r\n\r ",
"Header-1: value1\r\nHeader-2: value2\r\n\r \n",
};
// Whitespace in header name
new[] { "Header : value\r\n\r\n", @"Invalid request header: 'Header : value\x0D\x0A'" },
new[] { "Header\t: value\r\n\r\n", @"Invalid request header: 'Header\x09: value\x0D\x0A'" },
new[] { "Header\r: value\r\n\r\n", @"Invalid request header: 'Header\x0D: value\x0D\x0A'" },
new[] { "Header_\rVector16: value\r\n\r\n", @"Invalid request header: 'Header_\x0DVector16: value\x0D\x0A'" },
new[] { "Header__Vector16\r: value\r\n\r\n", @"Invalid request header: 'Header__Vector16\x0D: value\x0D\x0A'" },
new[] { "Header__Vector16_\r: value\r\n\r\n", @"Invalid request header: 'Header__Vector16_\x0D: value\x0D\x0A'" },
new[] { "Header_\rVector16________Vector32: value\r\n\r\n", @"Invalid request header: 'Header_\x0DVector16________Vector32: value\x0D\x0A'" },
new[] { "Header__Vector16________Vector32\r: value\r\n\r\n", @"Invalid request header: 'Header__Vector16________Vector32\x0D: value\x0D\x0A'" },
new[] { "Header__Vector16________Vector32_\r: value\r\n\r\n", @"Invalid request header: 'Header__Vector16________Vector32_\x0D: value\x0D\x0A'" },
new[] { "Header__Vector16________Vector32Header_\rVector16________Vector32: value\r\n\r\n", @"Invalid request header: 'Header__Vector16________Vector32Header_\x0DVector16________Vector32: value\x0D\x0A'" },
new[] { "Header__Vector16________Vector32Header__Vector16________Vector32\r: value\r\n\r\n", @"Invalid request header: 'Header__Vector16________Vector32Header__Vector16________Vector32\x0D: value\x0D\x0A'" },
new[] { "Header__Vector16________Vector32Header__Vector16________Vector32_\r: value\r\n\r\n", @"Invalid request header: 'Header__Vector16________Vector32Header__Vector16________Vector32_\x0D: value\x0D\x0A'" },
new[] { "Header 1: value1\r\nHeader-2: value2\r\n\r\n", @"Invalid request header: 'Header 1: value1\x0D\x0A'" },
new[] { "Header 1 : value1\r\nHeader-2: value2\r\n\r\n", @"Invalid request header: 'Header 1 : value1\x0D\x0A'" },
new[] { "Header 1\t: value1\r\nHeader-2: value2\r\n\r\n", @"Invalid request header: 'Header 1\x09: value1\x0D\x0A'" },
new[] { "Header 1\r: value1\r\nHeader-2: value2\r\n\r\n", @"Invalid request header: 'Header 1\x0D: value1\x0D\x0A'" },
new[] { "Header-1: value1\r\nHeader 2: value2\r\n\r\n", @"Invalid request header: 'Header 2: value2\x0D\x0A'" },
new[] { "Header-1: value1\r\nHeader-2 : value2\r\n\r\n", @"Invalid request header: 'Header-2 : value2\x0D\x0A'" },
new[] { "Header-1: value1\r\nHeader-2\t: value2\r\n\r\n", @"Invalid request header: 'Header-2\x09: value2\x0D\x0A'" },
return new[]
{
Tuple.Create(headersWithLineFolding, "Whitespace is not allowed in header name."),
Tuple.Create(headersWithCRInValue, "Header value must not contain CR characters."),
Tuple.Create(headersWithMissingColon, "No ':' character found in header line."),
Tuple.Create(headersStartingWithWhitespace, "Whitespace is not allowed in header name."),
Tuple.Create(headersWithWithspaceInName, "Whitespace is not allowed in header name."),
Tuple.Create(headersNotEndingInCrLfLine, "Headers corrupted, invalid header sequence.")
}
.SelectMany(t => t.Item1.Select(headers => new[] { headers, t.Item2 }));
}
}
// Headers not ending in CRLF line
new[] { "Header-1: value1\r\nHeader-2: value2\r\n\r\r", @"Invalid request headers: missing final CRLF in header fields." },
new[] { "Header-1: value1\r\nHeader-2: value2\r\n\r ", @"Invalid request headers: missing final CRLF in header fields." },
new[] { "Header-1: value1\r\nHeader-2: value2\r\n\r \n", @"Invalid request headers: missing final CRLF in header fields." },
};
}
}