diff --git a/src/libraries/System.Net.Http/src/Resources/Strings.resx b/src/libraries/System.Net.Http/src/Resources/Strings.resx
index 501ca3c8f9740..a6972e36a6be5 100644
--- a/src/libraries/System.Net.Http/src/Resources/Strings.resx
+++ b/src/libraries/System.Net.Http/src/Resources/Strings.resx
@@ -157,7 +157,7 @@
Invalid range. At least one of the two parameters must not be null.
- New-line characters in header values must be followed by a white-space character.
+ New-line characters are not allowed in header values.Cannot write more bytes to the buffer than the configured maximum buffer size: {0}.
@@ -217,7 +217,7 @@
The field cannot be longer than {0} characters.
- Value for header '{0}' contains invalid new-line characters. Value: '{1}'.
+ Value for header '{0}' contains new-line characters. Value: '{1}'.The 'q' value is invalid: '{0}'.
diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/AuthenticationHeaderValue.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/AuthenticationHeaderValue.cs
index 2b6538028e17b..a3197527d80a6 100644
--- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/AuthenticationHeaderValue.cs
+++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/AuthenticationHeaderValue.cs
@@ -38,6 +38,7 @@ public AuthenticationHeaderValue(string scheme)
public AuthenticationHeaderValue(string scheme, string? parameter)
{
HeaderUtilities.CheckValidToken(scheme, nameof(scheme));
+ HttpHeaders.CheckContainsNewLine(parameter);
_scheme = scheme;
_parameter = parameter;
}
diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/GenericHeaderParser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/GenericHeaderParser.cs
index 106c7b3ee96c3..c6ec5e5b0b766 100644
--- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/GenericHeaderParser.cs
+++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/GenericHeaderParser.cs
@@ -119,7 +119,7 @@ private static int ParseMultipleEntityTags(string value, int startIndex, out obj
///
private static int ParseWithoutValidation(string value, int startIndex, out object? parsedValue)
{
- if (HttpRuleParser.ContainsInvalidNewLine(value, startIndex))
+ if (HttpRuleParser.ContainsNewLine(value, startIndex))
{
parsedValue = null;
return 0;
diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpHeaders.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpHeaders.cs
index d52a92875a6a8..c31e628159db0 100644
--- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpHeaders.cs
+++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpHeaders.cs
@@ -221,7 +221,7 @@ internal bool TryGetValues(HeaderDescriptor descriptor, [NotNullWhen(true)] out
internal bool Contains(HeaderDescriptor descriptor)
{
// We can't just call headerStore.ContainsKey() since after parsing the value the header may not exist
- // anymore (if the value contains invalid newline chars, we remove the header). So try to parse the
+ // anymore (if the value contains newline chars, we remove the header). So try to parse the
// header value.
return _headerStore != null && TryGetAndParseHeaderInfo(descriptor, out _);
}
@@ -318,7 +318,7 @@ private IEnumerator>> GetEnumeratorCore
// values.
if (!ParseRawHeaderValues(descriptor, info, removeEmptyHeader: false))
{
- // We have an invalid header value (contains invalid newline chars). Delete it.
+ // We have an invalid header value (contains newline chars). Delete it.
_headerStore.Remove(descriptor);
}
else
@@ -726,18 +726,17 @@ private bool ParseRawHeaderValues(HeaderDescriptor descriptor, HeaderStoreItemIn
}
// At this point all values are either in info.ParsedValue, info.InvalidValue, or were removed since they
- // contain invalid newline chars. Reset RawValue.
+ // contain newline chars. Reset RawValue.
info.RawValue = null;
- // During parsing, we removed the value since it contains invalid newline chars. Return false to indicate that
+ // During parsing, we removed the value since it contains newline chars. Return false to indicate that
// this is an empty header. If the caller specified to remove empty headers, we'll remove the header before
// returning.
if ((info.InvalidValue == null) && (info.ParsedValue == null))
{
if (removeEmptyHeader)
{
- // After parsing the raw value, no value is left because all values contain invalid newline
- // chars.
+ // After parsing the raw value, no value is left because all values contain newline chars.
Debug.Assert(_headerStore != null);
_headerStore.Remove(descriptor);
}
@@ -754,7 +753,7 @@ private static void ParseMultipleRawHeaderValues(HeaderDescriptor descriptor, He
{
foreach (string rawValue in rawValues)
{
- if (!ContainsInvalidNewLine(rawValue, descriptor.Name))
+ if (!ContainsNewLine(rawValue, descriptor.Name))
{
AddParsedValue(info, rawValue);
}
@@ -779,7 +778,7 @@ private static void ParseSingleRawHeaderValue(HeaderDescriptor descriptor, Heade
if (descriptor.Parser == null)
{
- if (!ContainsInvalidNewLine(rawValue, descriptor.Name))
+ if (!ContainsNewLine(rawValue, descriptor.Name))
{
AddParsedValue(info, rawValue);
}
@@ -868,7 +867,7 @@ private static bool TryParseAndAddRawHeaderValue(HeaderDescriptor descriptor, He
}
else
{
- if (!ContainsInvalidNewLine(value, descriptor.Name) && addWhenInvalid)
+ if (!ContainsNewLine(value, descriptor.Name) && addWhenInvalid)
{
AddInvalidValue(info, value);
}
@@ -885,7 +884,7 @@ private static bool TryParseAndAddRawHeaderValue(HeaderDescriptor descriptor, He
}
Debug.Assert(value != null);
- if (!ContainsInvalidNewLine(value, descriptor.Name) && addWhenInvalid)
+ if (!ContainsNewLine(value, descriptor.Name) && addWhenInvalid)
{
AddInvalidValue(info, value ?? string.Empty);
}
@@ -973,8 +972,8 @@ private void ParseAndAddValue(HeaderDescriptor descriptor, HeaderStoreItemInfo i
if (descriptor.Parser == null)
{
// If we don't have a parser for the header, we consider the value valid if it doesn't contains
- // invalid newline characters. We add the values as "parsed value". Note that we allow empty values.
- CheckInvalidNewLine(value);
+ // newline characters. We add the values as "parsed value". Note that we allow empty values.
+ CheckContainsNewLine(value);
AddParsedValue(info, value ?? string.Empty);
return;
}
@@ -1077,22 +1076,22 @@ private bool TryGetHeaderDescriptor(string name, out HeaderDescriptor descriptor
return false;
}
- private static void CheckInvalidNewLine(string? value)
+ internal static void CheckContainsNewLine(string? value)
{
if (value == null)
{
return;
}
- if (HttpRuleParser.ContainsInvalidNewLine(value))
+ if (HttpRuleParser.ContainsNewLine(value))
{
throw new FormatException(SR.net_http_headers_no_newlines);
}
}
- private static bool ContainsInvalidNewLine(string value, string name)
+ private static bool ContainsNewLine(string value, string name)
{
- if (HttpRuleParser.ContainsInvalidNewLine(value))
+ if (HttpRuleParser.ContainsNewLine(value))
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, SR.Format(SR.net_http_log_headers_no_newlines, name, value));
return true;
diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpRequestHeaders.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpRequestHeaders.cs
index f3035cc6687b3..1731a954bf0a8 100644
--- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpRequestHeaders.cs
+++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpRequestHeaders.cs
@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
namespace System.Net.Http.Headers
{
@@ -102,6 +101,8 @@ public string? From
value = null;
}
+ CheckContainsNewLine(value);
+
SetOrRemoveParsedValue(KnownHeaders.From.Descriptor, value);
}
}
diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/MediaTypeHeaderParser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/MediaTypeHeaderParser.cs
index cd9884eff1678..e8d6e5c15a33a 100644
--- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/MediaTypeHeaderParser.cs
+++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/MediaTypeHeaderParser.cs
@@ -7,7 +7,6 @@ namespace System.Net.Http.Headers
{
internal sealed class MediaTypeHeaderParser : BaseHeaderParser
{
- private readonly bool _supportsMultipleValues;
private readonly Func _mediaTypeCreator;
internal static readonly MediaTypeHeaderParser SingleValueParser = new MediaTypeHeaderParser(false, CreateMediaType);
@@ -18,8 +17,6 @@ private MediaTypeHeaderParser(bool supportsMultipleValues, Func
+ throw new FormatException(SR.Format(System.Globalization.CultureInfo.InvariantCulture, SR.net_http_headers_invalid_value, value));
}
private static NameValueHeaderValue CreateNameValue()
diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpRuleParser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpRuleParser.cs
index 72fed31d31863..2e7390099282b 100644
--- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpRuleParser.cs
+++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpRuleParser.cs
@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
-using System.Globalization;
using System.Text;
namespace System.Net.Http
@@ -141,20 +140,6 @@ internal static int GetWhitespaceLength(string input, int startIndex)
continue;
}
- if (c == '\r')
- {
- // If we have a #13 char, it must be followed by #10 and then at least one SP or HT.
- if ((current + 2 < input.Length) && (input[current + 1] == '\n'))
- {
- char spaceOrTab = input[current + 2];
- if ((spaceOrTab == ' ') || (spaceOrTab == '\t'))
- {
- current += 3;
- continue;
- }
- }
- }
-
return current - startIndex;
}
@@ -162,42 +147,9 @@ internal static int GetWhitespaceLength(string input, int startIndex)
return input.Length - startIndex;
}
- internal static bool ContainsInvalidNewLine(string value)
+ internal static bool ContainsNewLine(string value, int startIndex = 0)
{
- return ContainsInvalidNewLine(value, 0);
- }
-
- internal static bool ContainsInvalidNewLine(string value, int startIndex)
- {
- // Search for newlines followed by non-whitespace: This is not allowed in any header (be it a known or
- // custom header). E.g. "value\r\nbadformat: header" is invalid. However "value\r\n goodformat: header"
- // is valid: newlines followed by whitespace are allowed in header values.
- int current = startIndex;
- while (current < value.Length)
- {
- if (value[current] == '\r')
- {
- int char10Index = current + 1;
- if ((char10Index < value.Length) && (value[char10Index] == '\n'))
- {
- current = char10Index + 1;
-
- if (current == value.Length)
- {
- return true; // We have a string terminating with \r\n. This is invalid.
- }
-
- char c = value[current];
- if ((c != ' ') && (c != '\t'))
- {
- return true;
- }
- }
- }
- current++;
- }
-
- return false;
+ return value.AsSpan(startIndex).IndexOfAny('\r', '\n') != -1;
}
internal static int GetNumberLength(string input, int startIndex, bool allowDecimal)
@@ -331,7 +283,7 @@ internal static HttpParseResult GetQuotedPairLength(string input, int startIndex
}
// TEXT =
- // LWS = [CRLF] 1*( SP | HT )
+ // LWS = SP | HT
// CTL =
//
// Since we don't really care about the content of a quoted string or comment, we're more tolerant and
@@ -370,8 +322,15 @@ private static HttpParseResult GetExpressionLength(string input, int startIndex,
continue;
}
+ char c = input[current];
+
+ if (c == '\r' || c == '\n')
+ {
+ return HttpParseResult.InvalidFormat;
+ }
+
// If we support nested expressions and we find an open-char, then parse the nested expressions.
- if (supportsNesting && (input[current] == openChar))
+ if (supportsNesting && (c == openChar))
{
// Check if we exceeded the number of nested calls.
if (nestedCount > MaxNestedCount)
diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/AltSvcHeaderParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/AltSvcHeaderParserTest.cs
index 3349ba6215f3a..c40eef9eba321 100644
--- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/AltSvcHeaderParserTest.cs
+++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/AltSvcHeaderParserTest.cs
@@ -1,14 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using System.Net.Http.Headers;
using Xunit;
-using Xunit.Sdk;
namespace System.Net.Http.Tests
{
diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/ByteArrayHeaderParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/ByteArrayHeaderParserTest.cs
index d5c7147ded0be..8a28534b47962 100644
--- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/ByteArrayHeaderParserTest.cs
+++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/ByteArrayHeaderParserTest.cs
@@ -1,10 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.Collections.Generic;
using System.Net.Http.Headers;
-using System.Text;
using Xunit;
diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/ContentDispositionHeaderValueTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/ContentDispositionHeaderValueTest.cs
index be4e0e65001e6..cb44f49b76781 100644
--- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/ContentDispositionHeaderValueTest.cs
+++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/ContentDispositionHeaderValueTest.cs
@@ -1,11 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
-using System.Text;
using Xunit;
@@ -487,8 +485,8 @@ public void GetDispositionTypeLength_DifferentValidScenarios_AllReturnNonZero()
Assert.Equal("custom", value.Parameters.ElementAt(0).Name);
Assert.Null(value.Parameters.ElementAt(0).Value);
- Assert.Equal(40, ContentDispositionHeaderValue.GetDispositionTypeLength(
- "inline ; custom =\r\n \"x\" ; name = myName , next", 0, out result));
+ Assert.Equal(38, ContentDispositionHeaderValue.GetDispositionTypeLength(
+ "inline ; custom = \"x\" ; name = myName , next", 0, out result));
value = (ContentDispositionHeaderValue)result;
Assert.Equal("inline", value.DispositionType);
Assert.Equal("myName", value.Name);
@@ -535,14 +533,14 @@ public void GetDispositionTypeLength_DifferentInvalidScenarios_AllReturnZero()
public void Parse_SetOfValidValueStrings_ParsedCorrectly()
{
ContentDispositionHeaderValue expected = new ContentDispositionHeaderValue("inline");
- CheckValidParse("\r\n inline ", expected);
+ CheckValidParse(" inline ", expected);
CheckValidParse("inline", expected);
// We don't have to test all possible input strings, since most of the pieces are handled by other parsers.
// The purpose of this test is to verify that these other parsers are combined correctly to build a
// Content-Disposition parser.
expected.Name = "myName";
- CheckValidParse("\r\n inline ; name = myName ", expected);
+ CheckValidParse(" inline ; name = myName ", expected);
CheckValidParse(" inline;name=myName", expected);
expected.Name = null;
@@ -565,35 +563,7 @@ public void Parse_SetOfInvalidValueStrings_Throws()
CheckInvalidParse("inline; name=myName,");
CheckInvalidParse("inline; name=my\u4F1AName");
CheckInvalidParse("inline/");
- }
-
- [Fact]
- public void TryParse_SetOfValidValueStrings_ParsedCorrectly()
- {
- ContentDispositionHeaderValue expected = new ContentDispositionHeaderValue("inline");
- CheckValidTryParse("\r\n inline ", expected);
- CheckValidTryParse("inline", expected);
-
- // We don't have to test all possible input strings, since most of the pieces are handled by other parsers.
- // The purpose of this test is to verify that these other parsers are combined correctly to build a
- // Content-Disposition parser.
- expected.Name = "myName";
- CheckValidTryParse("\r\n inline ; name = myName ", expected);
- CheckValidTryParse(" inline;name=myName", expected);
- }
-
- [Fact]
- public void TryParse_SetOfInvalidValueStrings_ReturnsFalse()
- {
- CheckInvalidTryParse("");
- CheckInvalidTryParse(" ");
- CheckInvalidTryParse(null);
- CheckInvalidTryParse("inline\u4F1A");
- CheckInvalidTryParse("inline ,");
- CheckInvalidTryParse("inline,");
- CheckInvalidTryParse("inline; name=myName ,");
- CheckInvalidTryParse("inline; name=myName,");
- CheckInvalidTryParse("text/");
+ CheckInvalidParse(" inline ; \r name = myName ");
}
#region Tests from HenrikN
@@ -1209,24 +1179,16 @@ private void CheckValidParse(string input, ContentDispositionHeaderValue expecte
{
ContentDispositionHeaderValue result = ContentDispositionHeaderValue.Parse(input);
Assert.Equal(expectedResult, result);
- }
-
- private void CheckInvalidParse(string input)
- {
- Assert.Throws(() => { ContentDispositionHeaderValue.Parse(input); });
- }
- private void CheckValidTryParse(string input, ContentDispositionHeaderValue expectedResult)
- {
- ContentDispositionHeaderValue result = null;
Assert.True(ContentDispositionHeaderValue.TryParse(input, out result), input);
Assert.Equal(expectedResult, result);
}
- private void CheckInvalidTryParse(string input)
+ private void CheckInvalidParse(string input)
{
- ContentDispositionHeaderValue result = null;
- Assert.False(ContentDispositionHeaderValue.TryParse(input, out result), input);
+ Assert.Throws(() => { ContentDispositionHeaderValue.Parse(input); });
+
+ Assert.False(ContentDispositionHeaderValue.TryParse(input, out ContentDispositionHeaderValue result), input);
Assert.Null(result);
}
diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/DateHeaderParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/DateHeaderParserTest.cs
index 89ce57593e47e..ebb2efe606884 100644
--- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/DateHeaderParserTest.cs
+++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/DateHeaderParserTest.cs
@@ -1,11 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Net.Http.Headers;
-using System.Text;
using Xunit;
diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/EntityTagHeaderValueTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/EntityTagHeaderValueTest.cs
index 290c53decc668..644c6cb1581a2 100644
--- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/EntityTagHeaderValueTest.cs
+++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/EntityTagHeaderValueTest.cs
@@ -1,11 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Net.Http.Headers;
-using System.Text;
using Xunit;
@@ -140,7 +136,7 @@ public void GetEntityTagLength_DifferentValidScenarios_AllReturnNonZero()
// Note that even if after a valid tag & whitespace there are invalid characters, GetEntityTagLength()
// will return the length of the valid tag and ignore the invalid characters at the end. It is the callers
// responsibility to consider the whole string invalid if after a valid ETag there are invalid chars.
- Assert.Equal(11, EntityTagHeaderValue.GetEntityTagLength("\"tag\" \r\n !!", 0, out result));
+ Assert.Equal(9, EntityTagHeaderValue.GetEntityTagLength("\"tag\" !!", 0, out result));
Assert.Equal("\"tag\"", result.Tag);
Assert.False(result.IsWeak);
@@ -198,7 +194,6 @@ public void Parse_SetOfValidValueStrings_ParsedCorrectly()
{
CheckValidParse("\"tag\"", new EntityTagHeaderValue("\"tag\""));
CheckValidParse(" \"tag\" ", new EntityTagHeaderValue("\"tag\""));
- CheckValidParse("\r\n \"tag\"\r\n ", new EntityTagHeaderValue("\"tag\""));
CheckValidParse("\"tag\"", new EntityTagHeaderValue("\"tag\""));
CheckValidParse("\"tag\u4F1A\"", new EntityTagHeaderValue("\"tag\u4F1A\""));
CheckValidParse("W/\"tag\"", new EntityTagHeaderValue("\"tag\"", true));
@@ -217,32 +212,8 @@ public void Parse_SetOfInvalidValueStrings_Throws()
CheckInvalidParse("\"tag\" \"tag2\"");
CheckInvalidParse("/\"tag\"");
CheckInvalidParse("*"); // "any" is not allowed as ETag value.
- }
-
- [Fact]
- public void TryParse_SetOfValidValueStrings_ParsedCorrectly()
- {
- CheckValidTryParse("\"tag\"", new EntityTagHeaderValue("\"tag\""));
- CheckValidTryParse(" \"tag\" ", new EntityTagHeaderValue("\"tag\""));
- CheckValidTryParse("\r\n \"tag\"\r\n ", new EntityTagHeaderValue("\"tag\""));
- CheckValidTryParse("\"tag\"", new EntityTagHeaderValue("\"tag\""));
- CheckValidTryParse("\"tag\u4F1A\"", new EntityTagHeaderValue("\"tag\u4F1A\""));
- CheckValidTryParse("W/\"tag\"", new EntityTagHeaderValue("\"tag\"", true));
- }
-
- [Fact]
- public void TryParse_SetOfInvalidValueStrings_ReturnsFalse()
- {
- CheckInvalidTryParse(null);
- CheckInvalidTryParse(string.Empty);
- CheckInvalidTryParse(" ");
- CheckInvalidTryParse(" !");
- CheckInvalidTryParse("tag\" !");
- CheckInvalidTryParse("!\"tag\"");
- CheckInvalidTryParse("\"tag\",");
- CheckInvalidTryParse("\"tag\" \"tag2\"");
- CheckInvalidTryParse("/\"tag\"");
- CheckInvalidTryParse("*"); // "any" is not allowed as ETag value.
+ CheckInvalidParse("\r\n \"tag\"\r\n ");
+ CheckInvalidParse("\"tag\" \r\n !!");
}
#region Helper methods
@@ -251,24 +222,16 @@ private void CheckValidParse(string input, EntityTagHeaderValue expectedResult)
{
EntityTagHeaderValue result = EntityTagHeaderValue.Parse(input);
Assert.Equal(expectedResult, result);
- }
-
- private void CheckInvalidParse(string input)
- {
- Assert.Throws(() => { EntityTagHeaderValue.Parse(input); });
- }
- private void CheckValidTryParse(string input, EntityTagHeaderValue expectedResult)
- {
- EntityTagHeaderValue result = null;
Assert.True(EntityTagHeaderValue.TryParse(input, out result));
Assert.Equal(expectedResult, result);
}
- private void CheckInvalidTryParse(string input)
+ private void CheckInvalidParse(string input)
{
- EntityTagHeaderValue result = null;
- Assert.False(EntityTagHeaderValue.TryParse(input, out result));
+ Assert.Throws(() => { EntityTagHeaderValue.Parse(input); });
+
+ Assert.False(EntityTagHeaderValue.TryParse(input, out EntityTagHeaderValue result));
Assert.Null(result);
}
diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/EntityTagParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/EntityTagParserTest.cs
index 938fce5615eed..3549295f2d403 100644
--- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/EntityTagParserTest.cs
+++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/EntityTagParserTest.cs
@@ -1,11 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Net.Http.Headers;
-using System.Text;
using Xunit;
@@ -34,8 +30,8 @@ public void TryParse_SetOfValidValueStrings_ParsedCorrectly()
CheckValidParsedValue(" * ,", 1, EntityTagHeaderValue.Any, 5, true);
CheckValidParsedValue(" \"tag\" ", 0, new EntityTagHeaderValue("\"tag\""), 7, false);
CheckValidParsedValue(" \"tag\" ,", 0, new EntityTagHeaderValue("\"tag\""), 8, true);
- CheckValidParsedValue("\r\n \"tag\"\r\n ", 0, new EntityTagHeaderValue("\"tag\""), 11, false);
- CheckValidParsedValue("\r\n \"tag\"\r\n , ", 0, new EntityTagHeaderValue("\"tag\""), 14, true);
+ CheckValidParsedValue(" \"tag\" ", 0, new EntityTagHeaderValue("\"tag\""), 7, false);
+ CheckValidParsedValue(" \"tag\" , ", 0, new EntityTagHeaderValue("\"tag\""), 10, true);
CheckValidParsedValue("!\"tag\"", 1, new EntityTagHeaderValue("\"tag\""), 6, false);
CheckValidParsedValue("!\"tag\"", 1, new EntityTagHeaderValue("\"tag\""), 6, true);
CheckValidParsedValue("//\"tag\u4F1A\"", 2, new EntityTagHeaderValue("\"tag\u4F1A\""), 8, false);
@@ -62,6 +58,8 @@ public void TryParse_SetOfInvalidValueStrings_ReturnsFalse()
CheckInvalidParsedValue("\"tag\" \"tag2\"", 0, false);
CheckInvalidParsedValue("W/\"tag\"", 1, false);
CheckInvalidParsedValue("*", 0, false); // "any" is not allowed as ETag value.
+ CheckInvalidParsedValue("\r\n \"tag\"\r\n ", 0, false);
+ CheckInvalidParsedValue("\r\n \"tag\"\r\n , ", 0, false);
}
#region Helper methods
diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/HostParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/HostParserTest.cs
index 91eaa0760a8ff..c149c59170ebd 100644
--- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/HostParserTest.cs
+++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/HostParserTest.cs
@@ -22,7 +22,6 @@ public void TryParse_SetOfValidValueStrings_ParsedCorrectly()
{
CheckValidParsedValue("host", 0, "host", 4);
CheckValidParsedValue(" host ", 0, "host", 6);
- CheckValidParsedValue("\r\n host\r\n ", 0, "host", 10);
CheckValidParsedValue("!host", 1, "host", 5);
CheckValidParsedValue("!host:50", 1, "host:50", 8);
CheckValidParsedValue("//host\u4F1A", 2, "host\u4F1A", 7);
@@ -54,6 +53,7 @@ public void TryParse_SetOfInvalidValueStrings_ReturnsFalse()
CheckInvalidParsedValue("host ,", 0);
CheckInvalidParsedValue("/", 0);
CheckInvalidParsedValue(" , ", 0);
+ CheckInvalidParsedValue(" host\r\n ", 0);
}
#region Helper methods
diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/MailAddressParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/MailAddressParserTest.cs
deleted file mode 100644
index 917c6e0b35b14..0000000000000
--- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/MailAddressParserTest.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net.Mail;
-using System.Net.Http.Headers;
-using System.Text;
-
-using Xunit;
-
-namespace System.Net.Http.Tests
-{
- public class MailAddressParserTest
- {
- [Fact]
- public void Properties_ReadValues_MatchExpectation()
- {
- HttpHeaderParser parser = GenericHeaderParser.MailAddressParser;
- Assert.False(parser.SupportsMultipleValues);
- Assert.Null(parser.Comparer);
- }
-
- [Fact]
- public void TryParse_SetOfValidValueStrings_ParsedCorrectly()
- {
- // We don't need to validate all possible date values, since they're already tested MailAddress.
- // Just make sure the parser calls MailAddressParser with correct parameters (like startIndex must be
- // honored).
-
- // Note that we still have trailing whitespace since we don't do the parsing of the email address.
- CheckValidParsedValue("!! info@example.com ", 2, "info@example.com ", 27);
- CheckValidParsedValue("\r\n \"My name\" info@example.com", 0,
- "\"My name\" info@example.com", 29);
- }
-
- [Fact]
- public void TryParse_SetOfInvalidValueStrings_ReturnsFalse()
- {
- CheckInvalidParsedValue("[info@example.com", 0);
- CheckInvalidParsedValue("info@example.com\r\nother", 0);
- CheckInvalidParsedValue("info@example.com\r\n other", 0);
- CheckInvalidParsedValue("info@example.com\r\n", 0);
- CheckInvalidParsedValue("info@example.com,", 0);
- CheckInvalidParsedValue("\r\ninfo@example.com", 0);
- CheckInvalidParsedValue(null, 0);
- CheckInvalidParsedValue(string.Empty, 0);
- CheckInvalidParsedValue(" ", 2);
- }
-
- #region Helper methods
-
- private void CheckValidParsedValue(string input, int startIndex, string expectedResult,
- int expectedIndex)
- {
- HttpHeaderParser parser = GenericHeaderParser.MailAddressParser;
- object result = null;
- Assert.True(parser.TryParseValue(input, null, ref startIndex, out result),
- string.Format("TryParse returned false: {0}", input));
- Assert.Equal(expectedIndex, startIndex);
- Assert.Equal(expectedResult, result);
- }
-
- private void CheckInvalidParsedValue(string input, int startIndex)
- {
- HttpHeaderParser parser = GenericHeaderParser.MailAddressParser;
- object result = null;
- int newIndex = startIndex;
- Assert.False(parser.TryParseValue(input, null, ref newIndex, out result),
- string.Format("TryParse returned true: {0}", input));
- Assert.Null(result);
- Assert.Equal(startIndex, newIndex);
- }
- #endregion
- }
-}
diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/NameValueParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/NameValueParserTest.cs
index 71604d4385d28..967cac75c5533 100644
--- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/NameValueParserTest.cs
+++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/NameValueParserTest.cs
@@ -1,11 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Net.Http.Headers;
-using System.Text;
using Xunit;
@@ -50,6 +46,12 @@ public void TryParse_SetOfInvalidValueStrings_ReturnsFalse()
CheckInvalidParsedValue("name value", 0);
CheckInvalidParsedValue("name=,value", 0);
CheckInvalidParsedValue("\u4F1A", 0);
+ CheckInvalidParsedValue("name=\nvalue", 0);
+ CheckInvalidParsedValue("name=val\nue", 0);
+ CheckInvalidParsedValue("name=value\n", 0);
+ CheckInvalidParsedValue("\"name=\nvalue\"", 0);
+ CheckInvalidParsedValue("\"name=val\nue\"", 0);
+ CheckInvalidParsedValue("\"name=value\n\"", 0);
}
#region Helper methods
diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/NameValueWithParametersParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/NameValueWithParametersParserTest.cs
index 130927ad55b81..916302a1bba9d 100644
--- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/NameValueWithParametersParserTest.cs
+++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/NameValueWithParametersParserTest.cs
@@ -1,11 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Net.Http.Headers;
-using System.Text;
using Xunit;
@@ -52,9 +48,9 @@ public void Parse_InvalidValue_Throw()
public void TryParse_SetOfValidValueStrings_ParsedCorrectly()
{
NameValueWithParametersHeaderValue expected = new NameValueWithParametersHeaderValue("custom");
- CheckValidParsedValue("\r\n custom ", 0, expected, 11);
+ CheckValidParsedValue(" custom ", 0, expected, 9);
CheckValidParsedValue("custom", 0, expected, 6);
- CheckValidParsedValue(",, ,\r\n custom , chunked", 0, expected, 17);
+ CheckValidParsedValue(",, , custom , chunked", 0, expected, 15);
// Note that even if the whole string is invalid, the first "Expect" value is valid. When the parser
// gets called again using the result-index (9), then it fails: I.e. we have 1 valid "Expect" value
@@ -65,7 +61,7 @@ public void TryParse_SetOfValidValueStrings_ParsedCorrectly()
// The purpose of this test is to verify that these other parsers are combined correctly to build a
// transfer-coding parser.
expected.Parameters.Add(new NameValueHeaderValue("name", "value"));
- CheckValidParsedValue("\r\n custom ; name = value ", 0, expected, 28);
+ CheckValidParsedValue(" custom ; name = value ", 0, expected, 26);
CheckValidParsedValue(" custom;name=value", 2, expected, 19);
CheckValidParsedValue(" custom ; name=value", 2, expected, 21);
@@ -81,6 +77,12 @@ public void TryParse_SetOfInvalidValueStrings_ReturnsFalse()
CheckInvalidParsedValue("custom\u4F1A", 0);
CheckInvalidParsedValue("custom; name=value;", 0);
CheckInvalidParsedValue("custom; name1=value1; name2=value2;", 0);
+ CheckInvalidParsedValue("\r\n custom ", 0);
+ CheckInvalidParsedValue(",, ,\r\n custom , chunked", 0);
+ CheckInvalidParsedValue("\r\n custom ; name = value ", 0);
+ CheckInvalidParsedValue("custom; name=value\r", 0);
+ CheckInvalidParsedValue("custom; name=value;\r", 0);
+ CheckInvalidParsedValue("custom; name=value;\r foo=bar", 0);
}
#region Helper methods
diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/StringWithQualityParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/StringWithQualityParserTest.cs
index ef1265db99676..177d773686fc5 100644
--- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/StringWithQualityParserTest.cs
+++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/StringWithQualityParserTest.cs
@@ -26,12 +26,12 @@ public void TryParse_SetOfValidValueStrings_ParsedCorrectly()
{
CheckValidParsedValue("text", 0, new StringWithQualityHeaderValue("text"), 4);
CheckValidParsedValue("text,", 0, new StringWithQualityHeaderValue("text"), 5);
- CheckValidParsedValue("\r\n text ; q = 0.5, next_text ", 0, new StringWithQualityHeaderValue("text", 0.5), 19);
+ CheckValidParsedValue(" text ; q = 0.5, next_text ", 0, new StringWithQualityHeaderValue("text", 0.5), 17);
CheckValidParsedValue(" text,next_text ", 2, new StringWithQualityHeaderValue("text"), 7);
CheckValidParsedValue(" ,, text, , ,next", 0, new StringWithQualityHeaderValue("text"), 13);
CheckValidParsedValue(" ,, text, , ,", 0, new StringWithQualityHeaderValue("text"), 13);
- CheckValidParsedValue(", \r\n text \r\n ; \r\n q = 0.123", 0,
- new StringWithQualityHeaderValue("text", 0.123), 27);
+ CheckValidParsedValue(", text ; q = 0.123", 0,
+ new StringWithQualityHeaderValue("text", 0.123), 21);
CheckValidParsedValue(null, 0, null, 0);
CheckValidParsedValue(string.Empty, 0, null, 0);
@@ -52,6 +52,8 @@ public void TryParse_SetOfInvalidValueStrings_ReturnsFalse()
CheckInvalidParsedValue("t;q\u4F1A=1", 0);
CheckInvalidParsedValue("t y", 0);
CheckInvalidParsedValue("t;q=1 y", 0);
+ CheckInvalidParsedValue("\r\n text ; q = 0.5, next_text ", 0);
+ CheckInvalidParsedValue(", \r\n text \r\n ; \r\n q = 0.123", 0);
}
#region Helper methods
diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/TokenListParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/TokenListParserTest.cs
index d6d1dd15c8b28..3500fb9503cff 100644
--- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/TokenListParserTest.cs
+++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/TokenListParserTest.cs
@@ -22,7 +22,7 @@ public void TryParse_SetOfValidValueStrings_ParsedCorrectly()
{
CheckValidParsedValue("text", 0, "text", 4);
CheckValidParsedValue("text,", 0, "text", 5);
- CheckValidParsedValue("\r\n text , next_text ", 0, "text", 10);
+ CheckValidParsedValue(" text , next_text ", 0, "text", 8);
CheckValidParsedValue(" text,next_text ", 2, "text", 7);
CheckValidParsedValue(" ,, text, , ,next", 0, "text", 13);
CheckValidParsedValue(" ,, text, , ,", 0, "text", 13);
@@ -39,6 +39,7 @@ public void TryParse_SetOfInvalidValueStrings_ReturnsFalse()
CheckInvalidParsedValue("te\u00E4xt", 0);
CheckInvalidParsedValue("text\u4F1A", 0);
CheckInvalidParsedValue("\u4F1A", 0);
+ CheckInvalidParsedValue("\r\n text , next_text ", 0);
}
#region Helper methods
diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/ViaParserTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/ViaParserTest.cs
index 330b7305dec16..155e5b30997ac 100644
--- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/ViaParserTest.cs
+++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/GenericHeaderParserTest/ViaParserTest.cs
@@ -1,11 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Net.Http.Headers;
-using System.Text;
using Xunit;
@@ -29,8 +25,8 @@ public void Properties_ReadValues_MatchExpectation()
public void TryParse_SetOfValidValueStrings_ParsedCorrectly()
{
CheckValidParsedValue("X , , 1.1 host, ,next", 1, new ViaHeaderValue("1.1", "host"), 19);
- CheckValidParsedValue("X HTTP / x11 192.168.0.1\r\n (comment) , ,next", 1,
- new ViaHeaderValue("x11", "192.168.0.1", "HTTP", "(comment)"), 44);
+ CheckValidParsedValue("X HTTP / x11 192.168.0.1 (comment) , ,next", 1,
+ new ViaHeaderValue("x11", "192.168.0.1", "HTTP", "(comment)"), 42);
CheckValidParsedValue(" ,HTTP/1.1 [::1]", 0, new ViaHeaderValue("1.1", "[::1]", "HTTP"), 16);
CheckValidParsedValue("1.1 host", 0, new ViaHeaderValue("1.1", "host"), 8);
@@ -54,6 +50,7 @@ public void TryParse_SetOfInvalidValueStrings_ReturnsFalse()
CheckInvalidParsedValue("\u4F1A", 0);
CheckInvalidParsedValue("HTTP/test [::1]:80\r(comment)", 0);
CheckInvalidParsedValue("HTTP/test [::1]:80\n(comment)", 0);
+ CheckInvalidParsedValue("X HTTP / x11 192.168.0.1 (comment) , ,next", 0);
}
#region Helper methods
diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/HeaderUtilitiesTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/HeaderUtilitiesTest.cs
index cece26a7c5ff2..64a29bda743dd 100644
--- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/HeaderUtilitiesTest.cs
+++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/HeaderUtilitiesTest.cs
@@ -1,11 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Net.Http.Headers;
-using System.Text;
using Xunit;
@@ -69,7 +65,7 @@ public void AreEqualCollections_UseSetOfNotEqualCollections_ReturnsFalse()
[Fact]
public void GetNextNonEmptyOrWhitespaceIndex_UseDifferentInput_MatchExpectation()
{
- CheckGetNextNonEmptyOrWhitespaceIndex("x , , ,, ,\t\r\n , ,x", 1, true, 18, true);
+ CheckGetNextNonEmptyOrWhitespaceIndex("x , , ,, ,\t , ,x", 1, true, 16, true);
CheckGetNextNonEmptyOrWhitespaceIndex("x , , ", 1, false, 4, true); // stop at the second ','
CheckGetNextNonEmptyOrWhitespaceIndex("x , , ", 1, true, 8, true);
CheckGetNextNonEmptyOrWhitespaceIndex(" x", 0, true, 1, false);
diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpContentHeadersTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpContentHeadersTest.cs
index 6191eb254f3f9..b50b28b56fd1b 100644
--- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpContentHeadersTest.cs
+++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpContentHeadersTest.cs
@@ -1,12 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http.Headers;
-using System.Text;
using System.Threading.Tasks;
using Xunit;
@@ -72,7 +69,7 @@ public void ContentLength_SetCustomValue_TryComputeLengthNotInvoked()
public void ContentLength_UseAddMethod_AddedValueCanBeRetrievedUsingProperty()
{
_headers = new HttpContentHeaders(new ComputeLengthHttpContent(() => { throw new ShouldNotBeInvokedException(); }));
- _headers.TryAddWithoutValidation(HttpKnownHeaderNames.ContentLength, " 68 \r\n ");
+ _headers.TryAddWithoutValidation(HttpKnownHeaderNames.ContentLength, " 68 ");
Assert.Equal(68, _headers.ContentLength);
}
diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpHeadersTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpHeadersTest.cs
index f2a4367c70463..f56f85ac5bd0d 100644
--- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpHeadersTest.cs
+++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/HttpHeadersTest.cs
@@ -329,60 +329,44 @@ public void TryAddWithoutValidation_AddMultipleValueStringToSingleValueHeaders_M
Assert.Equal(1, headers.Parser.TryParseValueCallCount);
}
- [Fact]
- public void TryAddWithoutValidation_AddValueContainingNewLine_NewLineFollowedByWhitespaceIsOKButNewLineFollowedByNonWhitespaceIsRejected()
+ [Theory]
+ [MemberData(nameof(HeaderValuesWithNewLines))]
+ public void TryAddWithoutValidation_AddValueContainingNewLine_Rejected(string headerValue)
{
- MockHeaders headers = new MockHeaders();
-
- // The header parser rejects both of the following values. Both values contain new line chars. According
- // to the RFC, LWS supports newlines followed by whitespace. I.e. the first value gets rejected by the
- // parser, but added to the list of invalid values.
- headers.TryAddWithoutValidation(headers.Descriptor, invalidHeaderValue + "\r\n other: value"); // OK, LWS is allowed
- Assert.Equal(1, headers.Count());
- Assert.Equal(1, headers.First().Value.Count());
- Assert.Equal(invalidHeaderValue + "\r\n other: value", headers.First().Value.First());
- Assert.Equal(1, headers.Parser.TryParseValueCallCount);
+ var headers = new HttpRequestHeaders();
// This value is considered invalid (newline char followed by non-whitespace). However, since
// TryAddWithoutValidation() only causes the header value to be analyzed when it gets actually accessed, no
// exception is thrown. Instead the value is discarded and a warning is logged.
- headers.Clear();
- headers.TryAddWithoutValidation(headers.Descriptor, invalidHeaderValue + "\r\nother:value");
- Assert.False(headers.Contains(headers.Descriptor));
- Assert.Equal(0, headers.Count());
+ headers.TryAddWithoutValidation("foo", headerValue);
- // Adding newline followed by whitespace to a custom header is OK.
- headers.Clear();
- headers.TryAddWithoutValidation("custom", "value\r\n other: value"); // OK, LWS is allowed
- Assert.Equal(1, headers.Count());
- Assert.Equal(1, headers.First().Value.Count());
- Assert.Equal("value\r\n other: value", headers.First().Value.First());
+ Assert.Equal(1, headers.NonValidated.Count);
+ Assert.Equal(headerValue, headers.NonValidated["foo"].ToString());
- // Adding newline followed by non-whitespace chars is invalid. The value is discarded and a warning is
- // logged.
- headers.Clear();
- headers.TryAddWithoutValidation("custom", "value\r\nother: value");
- Assert.False(headers.Contains("custom"));
+ Assert.False(headers.Contains("foo"));
Assert.Equal(0, headers.Count());
- // Also ending a value with newline is invalid. Verify that valid values are added.
- headers.Clear();
- headers.Parser.TryParseValueCallCount = 0;
- headers.TryAddWithoutValidation(headers.Descriptor, rawPrefix + "\rvalid");
- headers.TryAddWithoutValidation(headers.Descriptor, invalidHeaderValue + "\r\n");
- headers.TryAddWithoutValidation(headers.Descriptor, rawPrefix + "\n," + invalidHeaderValue + "\r\nother");
- Assert.Equal(1, headers.Count());
- Assert.Equal(1, headers.First().Value.Count());
- Assert.Equal(parsedPrefix + "\rvalid", headers.First().Value.First());
- Assert.Equal(4, headers.Parser.TryParseValueCallCount);
+ // Accessing the header forces parsing and the invalid value is removed
+ Assert.Equal(0, headers.NonValidated.Count);
+
headers.Clear();
- headers.TryAddWithoutValidation("custom", "value\r\ninvalid");
- headers.TryAddWithoutValidation("custom", "value\r\n valid");
- headers.TryAddWithoutValidation("custom", "validvalue, invalid\r\nvalue");
+ headers.TryAddWithoutValidation("foo", new[] { "valid", headerValue });
+
+ Assert.Equal(1, headers.NonValidated.Count);
+ HeaderStringValues values = headers.NonValidated["foo"];
+ Assert.Equal(2, values.Count);
+ Assert.Equal(new[] { "valid", headerValue }, values);
+
Assert.Equal(1, headers.Count());
Assert.Equal(1, headers.First().Value.Count());
- Assert.Equal("value\r\n valid", headers.First().Value.First());
+ Assert.Equal("valid", headers.First().Value.First());
+
+ // Accessing the header forces parsing and the invalid value is removed
+ Assert.Equal(1, headers.NonValidated.Count);
+ values = headers.NonValidated["foo"];
+ Assert.Equal(1, values.Count);
+ Assert.Equal("valid", values.ToString());
}
[Fact]
@@ -811,22 +795,23 @@ public void Add_SingleAddHeadersWithDifferentCasing_ConsideredTheSameHeader()
Assert.Equal(3, headers.GetValues("CuStOm-HeAdEr").Count());
}
- [Fact]
- public void Add_AddValueContainingNewLine_NewLineFollowedByWhitespaceIsOKButNewLineFollowedByNonWhitespaceIsRejected()
+ [Theory]
+ [MemberData(nameof(HeaderValuesWithNewLines))]
+ public void Add_AddValueContainingNewLine_Rejected(string headerValue)
{
- MockHeaders headers = new MockHeaders();
+ var headers = new HttpRequestHeaders();
- headers.Clear();
- headers.Add("custom", "value\r");
- Assert.Equal(1, headers.Count());
- Assert.Equal(1, headers.First().Value.Count());
- Assert.Equal("value\r", headers.First().Value.First());
+ Assert.Throws(() => headers.Add("foo", headerValue));
+ Assert.Equal(0, headers.Count());
+ Assert.Equal(0, headers.NonValidated.Count);
headers.Clear();
- Assert.Throws(() => { headers.Add("custom", new string[] { "valid\n", "invalid\r\nother" }); });
+ Assert.Throws(() => headers.Add("foo", new[] { "valid", headerValue }));
Assert.Equal(1, headers.Count());
Assert.Equal(1, headers.First().Value.Count());
- Assert.Equal("valid\n", headers.First().Value.First());
+ Assert.Equal("valid", headers.First().Value.First());
+ Assert.Equal(1, headers.NonValidated.Count);
+ Assert.Equal("valid", headers.NonValidated["foo"].ToString());
}
[Fact]
@@ -996,11 +981,11 @@ public void RemoveParsedValue_AddTwoValuesToKnownHeaderAndCompareWithValueThatDi
}
[Fact]
- public void RemoveParsedValue_FirstAddInvalidNewlineCharsValueThenCallRemoveParsedValue_HeaderRemoved()
+ public void RemoveParsedValue_FirstAddNewlineCharsValueThenCallRemoveParsedValue_HeaderRemoved()
{
MockHeaders headers = new MockHeaders();
- // Add header value with invalid newline chars.
+ // Add header value with newline chars.
headers.TryAddWithoutValidation(headers.Descriptor, invalidHeaderValue + "\r\ninvalid");
Assert.Equal(0, headers.Parser.TryParseValueCallCount);
@@ -1011,11 +996,11 @@ public void RemoveParsedValue_FirstAddInvalidNewlineCharsValueThenCallRemovePars
}
[Fact]
- public void RemoveParsedValue_FirstAddInvalidNewlineCharsValueThenAddValidValueThenCallAddParsedValue_HeaderRemoved()
+ public void RemoveParsedValue_FirstAddNewlineCharsValueThenAddValidValueThenCallAddParsedValue_HeaderRemoved()
{
MockHeaders headers = new MockHeaders();
- // Add header value with invalid newline chars.
+ // Add header value with newline chars.
headers.TryAddWithoutValidation(headers.Descriptor, invalidHeaderValue + "\r\ninvalid");
headers.TryAddWithoutValidation(headers.Descriptor, rawPrefix + "1");
@@ -1407,11 +1392,11 @@ public void GetParsedValues_AddInvalidValueToHeader_HeaderGetsRemovedAndNullRetu
}
[Fact]
- public void GetParsedValues_GetParsedValuesForKnownHeaderWithInvalidNewlineChars_ReturnsNull()
+ public void GetParsedValues_GetParsedValuesForKnownHeaderWithNewlineChars_ReturnsNull()
{
MockHeaders headers = new MockHeaders();
- // Add header value with invalid newline chars.
+ // Add header value with newline chars.
headers.TryAddWithoutValidation(headers.Descriptor, invalidHeaderValue + "\r\ninvalid");
Assert.Equal(0, headers.Parser.TryParseValueCallCount);
@@ -1436,7 +1421,6 @@ public void NonValidated_SetValidAndInvalidHeaderValues_AllHeaderValuesReturned(
MockHeaderParser parser = new MockHeaderParser("---");
MockHeaders headers = new MockHeaders(parser);
- // Add header value with invalid newline chars.
headers.Add(headers.Descriptor, rawPrefix + "1");
headers.TryAddWithoutValidation(headers.Descriptor, "value2,value3");
headers.TryAddWithoutValidation(headers.Descriptor, invalidHeaderValue);
@@ -1464,7 +1448,6 @@ public void NonValidated_SetMultipleHeaders_AllHeaderValuesReturned()
MockHeaderParser parser = new MockHeaderParser(true);
MockHeaders headers = new MockHeaders(parser);
- // Add header value with invalid newline chars.
headers.Add(headers.Descriptor, rawPrefix + "1");
headers.Add("header2", "value2");
headers.Add("header3", (string)null);
@@ -1622,12 +1605,12 @@ public void Contains_CallContainsForExistingHeader_ReturnsTrue()
Assert.True(headers.Contains(headers.Descriptor));
// Contains() should trigger parsing of values added with TryAddWithoutValidation(): If the value was invalid,
- // i.e. contains invalid newline chars, then the header will be removed from the collection.
+ // i.e. contains newline chars, then the header will be removed from the collection.
Assert.Equal(1, headers.Parser.TryParseValueCallCount);
}
[Fact]
- public void Contains_AddValuesWithInvalidNewlineChars_HeadersGetRemovedWhenCallingContains()
+ public void Contains_AddValuesWithNewlineChars_HeadersGetRemovedWhenCallingContains()
{
MockHeaders headers = new MockHeaders();
@@ -1794,11 +1777,11 @@ public void AddParsedValue_UseDifferentAddMethods_AllValuesAddedCorrectly()
}
[Fact]
- public void AddParsedValue_FirstAddInvalidNewlineCharsValueThenCallAddParsedValue_ParsedValueAdded()
+ public void AddParsedValue_FirstAddNewlineCharsValueThenCallAddParsedValue_ParsedValueAdded()
{
MockHeaders headers = new MockHeaders();
- // Add header value with invalid newline chars.
+ // Add header value with newline chars.
headers.TryAddWithoutValidation(headers.Descriptor, invalidHeaderValue + "\r\ninvalid");
Assert.Equal(0, headers.Parser.TryParseValueCallCount);
@@ -1811,11 +1794,11 @@ public void AddParsedValue_FirstAddInvalidNewlineCharsValueThenCallAddParsedValu
}
[Fact]
- public void AddParsedValue_FirstAddInvalidNewlineCharsValueThenAddValidValueThenCallAddParsedValue_ParsedValueAdded()
+ public void AddParsedValue_FirstAddNewlineCharsValueThenAddValidValueThenCallAddParsedValue_ParsedValueAdded()
{
MockHeaders headers = new MockHeaders();
- // Add header value with invalid newline chars.
+ // Add header value with newline chars.
headers.TryAddWithoutValidation(headers.Descriptor, invalidHeaderValue + "\r\ninvalid");
headers.TryAddWithoutValidation(headers.Descriptor, rawPrefix + "0");
@@ -2212,6 +2195,16 @@ public static IEnumerable