#515 Make forgiving vs strict header list parsers.

This commit is contained in:
Chris R 2016-02-03 11:03:16 -08:00
parent 77c137fc0a
commit 3e6c7171be
14 changed files with 868 additions and 66 deletions

View File

@ -7,11 +7,6 @@ namespace Microsoft.Net.Http.Headers
{ {
internal class CookieHeaderParser : HttpHeaderParser<CookieHeaderValue> internal class CookieHeaderParser : HttpHeaderParser<CookieHeaderValue>
{ {
// The Cache-Control header is special: It is a header supporting a list of values, but we represent the list
// as _one_ instance of CacheControlHeaderValue. I.e we set 'SupportsMultipleValues' to 'true' since it is
// OK to have multiple Cache-Control headers in a request/response message. However, after parsing all
// Cache-Control headers, only one instance of CacheControlHeaderValue is created (if all headers contain valid
// values, otherwise we may have multiple strings containing the invalid values).
internal CookieHeaderParser(bool supportsMultipleValues) internal CookieHeaderParser(bool supportsMultipleValues)
: base(supportsMultipleValues) : base(supportsMultipleValues)
{ {
@ -49,14 +44,11 @@ namespace Microsoft.Net.Http.Headers
} }
CookieHeaderValue result = null; CookieHeaderValue result = null;
int length = CookieHeaderValue.GetCookieLength(value, current, out result); if (!CookieHeaderValue.TryGetCookieLength(value, ref current, out result))
if (length == 0)
{ {
return false; return false;
} }
current = current + length;
current = GetNextNonEmptyOrWhitespaceIndex(value, current, SupportsMultipleValues, out separatorFound); current = GetNextNonEmptyOrWhitespaceIndex(value, current, SupportsMultipleValues, out separatorFound);
// If we support multiple values and we've not reached the end of the string, then we must have a separator. // If we support multiple values and we've not reached the end of the string, then we must have a separator.
@ -91,6 +83,7 @@ namespace Microsoft.Net.Http.Headers
if (skipEmptyValues) if (skipEmptyValues)
{ {
// Most headers only split on ',', but cookies primarily split on ';'
while ((current < input.Length) && ((input[current] == ',') || (input[current] == ';'))) while ((current < input.Length) && ((input[current] == ',') || (input[current] == ';')))
{ {
current++; // skip delimiter. current++; // skip delimiter.

View File

@ -107,22 +107,31 @@ namespace Microsoft.Net.Http.Headers
return MultipleValueParser.ParseValues(inputs); return MultipleValueParser.ParseValues(inputs);
} }
public static IList<CookieHeaderValue> ParseStrictList(IList<string> inputs)
{
return MultipleValueParser.ParseStrictValues(inputs);
}
public static bool TryParseList(IList<string> inputs, out IList<CookieHeaderValue> parsedValues) public static bool TryParseList(IList<string> inputs, out IList<CookieHeaderValue> parsedValues)
{ {
return MultipleValueParser.TryParseValues(inputs, out parsedValues); return MultipleValueParser.TryParseValues(inputs, out parsedValues);
} }
// name=value; name="value" public static bool TryParseStrictList(IList<string> inputs, out IList<CookieHeaderValue> parsedValues)
internal static int GetCookieLength(string input, int startIndex, out CookieHeaderValue parsedValue)
{ {
Contract.Requires(startIndex >= 0); return MultipleValueParser.TryParseStrictValues(inputs, out parsedValues);
var offset = startIndex; }
// name=value; name="value"
internal static bool TryGetCookieLength(string input, ref int offset, out CookieHeaderValue parsedValue)
{
Contract.Requires(offset >= 0);
parsedValue = null; parsedValue = null;
if (string.IsNullOrEmpty(input) || (offset >= input.Length)) if (string.IsNullOrEmpty(input) || (offset >= input.Length))
{ {
return 0; return false;
} }
var result = new CookieHeaderValue(); var result = new CookieHeaderValue();
@ -135,7 +144,7 @@ namespace Microsoft.Net.Http.Headers
var itemLength = HttpRuleParser.GetTokenLength(input, offset); var itemLength = HttpRuleParser.GetTokenLength(input, offset);
if (itemLength == 0) if (itemLength == 0)
{ {
return 0; return false;
} }
result._name = input.Substring(offset, itemLength); result._name = input.Substring(offset, itemLength);
offset += itemLength; offset += itemLength;
@ -143,36 +152,33 @@ namespace Microsoft.Net.Http.Headers
// = (no spaces) // = (no spaces)
if (!ReadEqualsSign(input, ref offset)) if (!ReadEqualsSign(input, ref offset))
{ {
return 0; return false;
} }
string value;
// value or "quoted value" // value or "quoted value"
itemLength = GetCookieValueLength(input, offset, out value);
// The value may be empty // The value may be empty
result._value = input.Substring(offset, itemLength); result._value = GetCookieValue(input, ref offset);
offset += itemLength;
parsedValue = result; parsedValue = result;
return offset - startIndex; return true;
} }
// cookie-value = *cookie-octet / ( DQUOTE* cookie-octet DQUOTE ) // cookie-value = *cookie-octet / ( DQUOTE* cookie-octet DQUOTE )
// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E // cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
// ; US-ASCII characters excluding CTLs, whitespace DQUOTE, comma, semicolon, and backslash // ; US-ASCII characters excluding CTLs, whitespace DQUOTE, comma, semicolon, and backslash
internal static int GetCookieValueLength(string input, int startIndex, out string value) internal static string GetCookieValue(string input, ref int offset)
{ {
Contract.Requires(input != null); Contract.Requires(input != null);
Contract.Requires(startIndex >= 0); Contract.Requires(offset >= 0);
Contract.Ensures((Contract.Result<int>() >= 0) && (Contract.Result<int>() <= (input.Length - startIndex))); Contract.Ensures((Contract.Result<int>() >= 0) && (Contract.Result<int>() <= (input.Length - offset)));
value = null; var startIndex = offset;
if (startIndex >= input.Length)
if (offset >= input.Length)
{ {
return 0; return string.Empty;
} }
var inQuotes = false; var inQuotes = false;
var offset = startIndex;
if (input[offset] == '"') if (input[offset] == '"')
{ {
@ -195,19 +201,19 @@ namespace Microsoft.Net.Http.Headers
{ {
if (offset == input.Length || input[offset] != '"') if (offset == input.Length || input[offset] != '"')
{ {
return 0; // Missing final quote // Missing final quote
return string.Empty;
} }
offset++; offset++;
} }
int length = offset - startIndex; int length = offset - startIndex;
if (length == 0) if (offset > startIndex)
{ {
return 0; return input.Substring(startIndex, length);
} }
value = input.Substring(startIndex, length); return string.Empty;
return length;
} }
private static bool ReadEqualsSign(string input, ref int offset) private static bool ReadEqualsSign(string input, ref int offset)
@ -252,8 +258,9 @@ namespace Microsoft.Net.Http.Headers
throw new ArgumentNullException(nameof(value)); throw new ArgumentNullException(nameof(value));
} }
string temp; var offset = 0;
if (GetCookieValueLength(value, 0, out temp) != value.Length) var result = GetCookieValue(value, ref offset);
if (result.Length != value.Length)
{ {
throw new ArgumentException("Invalid cookie value: " + value, parameterName); throw new ArgumentException("Invalid cookie value: " + value, parameterName);
} }

View File

@ -129,11 +129,21 @@ namespace Microsoft.Net.Http.Headers
return MultipleValueParser.ParseValues(inputs); return MultipleValueParser.ParseValues(inputs);
} }
public static IList<EntityTagHeaderValue> ParseStrictList(IList<string> inputs)
{
return MultipleValueParser.ParseStrictValues(inputs);
}
public static bool TryParseList(IList<string> inputs, out IList<EntityTagHeaderValue> parsedValues) public static bool TryParseList(IList<string> inputs, out IList<EntityTagHeaderValue> parsedValues)
{ {
return MultipleValueParser.TryParseValues(inputs, out parsedValues); return MultipleValueParser.TryParseValues(inputs, out parsedValues);
} }
public static bool TryParseStrictList(IList<string> inputs, out IList<EntityTagHeaderValue> parsedValues)
{
return MultipleValueParser.TryParseStrictValues(inputs, out parsedValues);
}
internal static int GetEntityTagLength(string input, int startIndex, out EntityTagHeaderValue parsedValue) internal static int GetEntityTagLength(string input, int startIndex, out EntityTagHeaderValue parsedValue)
{ {
Contract.Requires(startIndex >= 0); Contract.Requires(startIndex >= 0);

View File

@ -39,13 +39,23 @@ namespace Microsoft.Net.Http.Headers
T result; T result;
if (!TryParseValue(value, ref index, out result)) if (!TryParseValue(value, ref index, out result))
{ {
throw new FormatException(string.Format(CultureInfo.InvariantCulture, "Invalid value '{0}'.", throw new FormatException(string.Format(CultureInfo.InvariantCulture,
value?.Substring(index) ?? "<null>")); "The header contains invalid values at index {0}: '{1}'", index, value ?? "<null>"));
} }
return result; return result;
} }
public virtual bool TryParseValues(IList<string> values, out IList<T> parsedValues) public virtual bool TryParseValues(IList<string> values, out IList<T> parsedValues)
{
return TryParseValues(values, strict: false, parsedValues: out parsedValues);
}
public virtual bool TryParseStrictValues(IList<string> values, out IList<T> parsedValues)
{
return TryParseValues(values, strict: true, parsedValues: out parsedValues);
}
protected virtual bool TryParseValues(IList<string> values, bool strict, out IList<T> parsedValues)
{ {
Contract.Assert(_supportsMultipleValues); Contract.Assert(_supportsMultipleValues);
// If a parser returns an empty list, it means there was no value, but that's valid (e.g. "Accept: "). The caller // If a parser returns an empty list, it means there was no value, but that's valid (e.g. "Accept: "). The caller
@ -71,10 +81,15 @@ namespace Microsoft.Net.Http.Headers
results.Add(output); results.Add(output);
} }
} }
else else if (strict)
{ {
return false; return false;
} }
else
{
// Skip the invalid values and keep trying.
index++;
}
} }
} }
if (results.Count > 0) if (results.Count > 0)
@ -85,7 +100,17 @@ namespace Microsoft.Net.Http.Headers
return false; return false;
} }
public IList<T> ParseValues(IList<string> values) public virtual IList<T> ParseValues(IList<string> values)
{
return ParseValues(values, strict: false);
}
public virtual IList<T> ParseStrictValues(IList<string> values)
{
return ParseValues(values, strict: true);
}
protected virtual IList<T> ParseValues(IList<string> values, bool strict)
{ {
Contract.Assert(_supportsMultipleValues); Contract.Assert(_supportsMultipleValues);
// If a parser returns an empty list, it means there was no value, but that's valid (e.g. "Accept: "). The caller // If a parser returns an empty list, it means there was no value, but that's valid (e.g. "Accept: "). The caller
@ -110,10 +135,15 @@ namespace Microsoft.Net.Http.Headers
parsedValues.Add(output); parsedValues.Add(output);
} }
} }
else if (strict)
{
throw new FormatException(string.Format(CultureInfo.InvariantCulture,
"The header contains invalid values at index {0}: '{1}'", index, value));
}
else else
{ {
throw new FormatException(string.Format(CultureInfo.InvariantCulture, "Invalid values '{0}'.", // Skip the invalid values and keep trying.
value.Substring(index))); index++;
} }
} }
} }

View File

@ -391,11 +391,21 @@ namespace Microsoft.Net.Http.Headers
return MultipleValueParser.ParseValues(inputs); return MultipleValueParser.ParseValues(inputs);
} }
public static IList<MediaTypeHeaderValue> ParseStrictList(IList<string> inputs)
{
return MultipleValueParser.ParseStrictValues(inputs);
}
public static bool TryParseList(IList<string> inputs, out IList<MediaTypeHeaderValue> parsedValues) public static bool TryParseList(IList<string> inputs, out IList<MediaTypeHeaderValue> parsedValues)
{ {
return MultipleValueParser.TryParseValues(inputs, out parsedValues); return MultipleValueParser.TryParseValues(inputs, out parsedValues);
} }
public static bool TryParseStrictList(IList<string> inputs, out IList<MediaTypeHeaderValue> parsedValues)
{
return MultipleValueParser.TryParseStrictValues(inputs, out parsedValues);
}
private static int GetMediaTypeLength(string input, int startIndex, out MediaTypeHeaderValue parsedValue) private static int GetMediaTypeLength(string input, int startIndex, out MediaTypeHeaderValue parsedValue)
{ {
Contract.Requires(startIndex >= 0); Contract.Requires(startIndex >= 0);

View File

@ -158,11 +158,21 @@ namespace Microsoft.Net.Http.Headers
return MultipleValueParser.ParseValues(input); return MultipleValueParser.ParseValues(input);
} }
public static IList<NameValueHeaderValue> ParseStrictList(IList<string> input)
{
return MultipleValueParser.ParseStrictValues(input);
}
public static bool TryParseList(IList<string> input, out IList<NameValueHeaderValue> parsedValues) public static bool TryParseList(IList<string> input, out IList<NameValueHeaderValue> parsedValues)
{ {
return MultipleValueParser.TryParseValues(input, out parsedValues); return MultipleValueParser.TryParseValues(input, out parsedValues);
} }
public static bool TryParseStrictList(IList<string> input, out IList<NameValueHeaderValue> parsedValues)
{
return MultipleValueParser.TryParseStrictValues(input, out parsedValues);
}
public override string ToString() public override string ToString()
{ {
if (!string.IsNullOrEmpty(_value)) if (!string.IsNullOrEmpty(_value))

View File

@ -156,11 +156,21 @@ namespace Microsoft.Net.Http.Headers
return MultipleValueParser.ParseValues(inputs); return MultipleValueParser.ParseValues(inputs);
} }
public static IList<SetCookieHeaderValue> ParseStrictList(IList<string> inputs)
{
return MultipleValueParser.ParseStrictValues(inputs);
}
public static bool TryParseList(IList<string> inputs, out IList<SetCookieHeaderValue> parsedValues) public static bool TryParseList(IList<string> inputs, out IList<SetCookieHeaderValue> parsedValues)
{ {
return MultipleValueParser.TryParseValues(inputs, out parsedValues); return MultipleValueParser.TryParseValues(inputs, out parsedValues);
} }
public static bool TryParseStrictList(IList<string> inputs, out IList<SetCookieHeaderValue> parsedValues)
{
return MultipleValueParser.TryParseStrictValues(inputs, out parsedValues);
}
// name=value; expires=Sun, 06 Nov 1994 08:49:37 GMT; max-age=86400; domain=domain1; path=path1; secure; httponly // name=value; expires=Sun, 06 Nov 1994 08:49:37 GMT; max-age=86400; domain=domain1; path=path1; secure; httponly
private static int GetSetCookieLength(string input, int startIndex, out SetCookieHeaderValue parsedValue) private static int GetSetCookieLength(string input, int startIndex, out SetCookieHeaderValue parsedValue)
{ {
@ -195,12 +205,9 @@ namespace Microsoft.Net.Http.Headers
return 0; return 0;
} }
string value;
// value or "quoted value" // value or "quoted value"
itemLength = CookieHeaderValue.GetCookieValueLength(input, offset, out value);
// The value may be empty // The value may be empty
result._value = input.Substring(offset, itemLength); result._value = CookieHeaderValue.GetCookieValue(input, ref offset);
offset += itemLength;
// *(';' SP cookie-av) // *(';' SP cookie-av)
while (offset < input.Length) while (offset < input.Length)

View File

@ -119,11 +119,21 @@ namespace Microsoft.Net.Http.Headers
return MultipleValueParser.ParseValues(input); return MultipleValueParser.ParseValues(input);
} }
public static IList<StringWithQualityHeaderValue> ParseStrictList(IList<string> input)
{
return MultipleValueParser.ParseStrictValues(input);
}
public static bool TryParseList(IList<string> input, out IList<StringWithQualityHeaderValue> parsedValues) public static bool TryParseList(IList<string> input, out IList<StringWithQualityHeaderValue> parsedValues)
{ {
return MultipleValueParser.TryParseValues(input, out parsedValues); return MultipleValueParser.TryParseValues(input, out parsedValues);
} }
public static bool TryParseStrictList(IList<string> input, out IList<StringWithQualityHeaderValue> parsedValues)
{
return MultipleValueParser.TryParseStrictValues(input, out parsedValues);
}
private static int GetStringWithQualityLength(string input, int startIndex, out StringWithQualityHeaderValue parsedValue) private static int GetStringWithQualityLength(string input, int startIndex, out StringWithQualityHeaderValue parsedValue)
{ {
Contract.Requires(startIndex >= 0); Contract.Requires(startIndex >= 0);

View File

@ -79,6 +79,7 @@ namespace Microsoft.Net.Http.Headers
}; };
} }
} }
public static TheoryData<IList<CookieHeaderValue>, string[]> ListOfCookieHeaderDataSet public static TheoryData<IList<CookieHeaderValue>, string[]> ListOfCookieHeaderDataSet
{ {
get get
@ -111,7 +112,48 @@ namespace Microsoft.Net.Http.Headers
} }
} }
// TODO: [Fact] public static TheoryData<IList<CookieHeaderValue>, string[]> ListWithInvalidCookieHeaderDataSet
{
get
{
var dataset = new TheoryData<IList<CookieHeaderValue>, string[]>();
var header1 = new CookieHeaderValue("name1", "n1=v1&n2=v2&n3=v3");
var validString1 = "name1=n1=v1&n2=v2&n3=v3";
var header2 = new CookieHeaderValue("name2", "value2");
var validString2 = "name2=value2";
var header3 = new CookieHeaderValue("name3", "value3");
var validString3 = "name3=value3";
var invalidString1 = "ipt={\"v\":{\"L\":3},\"pt\":{\"d\":3},ct\":{},\"_t\":44,\"_v\":\"2\"}";
dataset.Add(null, new[] { invalidString1 });
dataset.Add(new[] { header1 }.ToList(), new[] { validString1, invalidString1 });
dataset.Add(new[] { header1 }.ToList(), new[] { validString1, null, "", " ", ";", " , ", invalidString1 });
dataset.Add(new[] { header1 }.ToList(), new[] { invalidString1, null, "", " ", ";", " , ", validString1 });
dataset.Add(new[] { header1 }.ToList(), new[] { validString1 + ", " + invalidString1 });
dataset.Add(new[] { header2 }.ToList(), new[] { invalidString1 + ", " + validString2 });
dataset.Add(new[] { header1 }.ToList(), new[] { invalidString1 + "; " + validString1 });
dataset.Add(new[] { header2 }.ToList(), new[] { validString2 + "; " + invalidString1 });
dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { invalidString1, validString1, validString2, validString3 });
dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { validString1, invalidString1, validString2, validString3 });
dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { validString1, validString2, invalidString1, validString3 });
dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { validString1, validString2, validString3, invalidString1 });
dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { string.Join(",", invalidString1, validString1, validString2, validString3) });
dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { string.Join(",", validString1, invalidString1, validString2, validString3) });
dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { string.Join(",", validString1, validString2, invalidString1, validString3) });
dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { string.Join(",", validString1, validString2, validString3, invalidString1) });
dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { string.Join(";", invalidString1, validString1, validString2, validString3) });
dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { string.Join(";", validString1, invalidString1, validString2, validString3) });
dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { string.Join(";", validString1, validString2, invalidString1, validString3) });
dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { string.Join(";", validString1, validString2, validString3, invalidString1) });
return dataset;
}
}
[Fact]
public void CookieHeaderValue_CtorThrowsOnNullName() public void CookieHeaderValue_CtorThrowsOnNullName()
{ {
Assert.Throws<ArgumentNullException>(() => new CookieHeaderValue(null, "value")); Assert.Throws<ArgumentNullException>(() => new CookieHeaderValue(null, "value"));
@ -182,7 +224,7 @@ namespace Microsoft.Net.Http.Headers
public void CookieHeaderValue_TryParse_AcceptsValidValues(CookieHeaderValue cookie, string expectedValue) public void CookieHeaderValue_TryParse_AcceptsValidValues(CookieHeaderValue cookie, string expectedValue)
{ {
CookieHeaderValue header; CookieHeaderValue header;
bool result = CookieHeaderValue.TryParse(expectedValue, out header); var result = CookieHeaderValue.TryParse(expectedValue, out header);
Assert.True(result); Assert.True(result);
Assert.Equal(cookie, header); Assert.Equal(cookie, header);
@ -201,7 +243,7 @@ namespace Microsoft.Net.Http.Headers
public void CookieHeaderValue_TryParse_RejectsInvalidValues(string value) public void CookieHeaderValue_TryParse_RejectsInvalidValues(string value)
{ {
CookieHeaderValue header; CookieHeaderValue header;
bool result = CookieHeaderValue.TryParse(value, out header); var result = CookieHeaderValue.TryParse(value, out header);
Assert.False(result); Assert.False(result);
} }
@ -215,15 +257,71 @@ namespace Microsoft.Net.Http.Headers
Assert.Equal(cookies, results); Assert.Equal(cookies, results);
} }
[Theory]
[MemberData(nameof(ListOfCookieHeaderDataSet))]
public void CookieHeaderValue_ParseStrictList_AcceptsValidValues(IList<CookieHeaderValue> cookies, string[] input)
{
var results = CookieHeaderValue.ParseStrictList(input);
Assert.Equal(cookies, results);
}
[Theory] [Theory]
[MemberData(nameof(ListOfCookieHeaderDataSet))] [MemberData(nameof(ListOfCookieHeaderDataSet))]
public void CookieHeaderValue_TryParseList_AcceptsValidValues(IList<CookieHeaderValue> cookies, string[] input) public void CookieHeaderValue_TryParseList_AcceptsValidValues(IList<CookieHeaderValue> cookies, string[] input)
{ {
IList<CookieHeaderValue> results; IList<CookieHeaderValue> results;
bool result = CookieHeaderValue.TryParseList(input, out results); var result = CookieHeaderValue.TryParseList(input, out results);
Assert.True(result); Assert.True(result);
Assert.Equal(cookies, results); Assert.Equal(cookies, results);
} }
[Theory]
[MemberData(nameof(ListOfCookieHeaderDataSet))]
public void CookieHeaderValue_TryParseStrictList_AcceptsValidValues(IList<CookieHeaderValue> cookies, string[] input)
{
IList<CookieHeaderValue> results;
var result = CookieHeaderValue.TryParseStrictList(input, out results);
Assert.True(result);
Assert.Equal(cookies, results);
}
[Theory]
[MemberData(nameof(ListWithInvalidCookieHeaderDataSet))]
public void CookieHeaderValue_ParseList_ExcludesInvalidValues(IList<CookieHeaderValue> cookies, string[] input)
{
var results = CookieHeaderValue.ParseList(input);
// ParseList aways returns a list, even if empty. TryParseList may return null (via out).
Assert.Equal(cookies ?? new List<CookieHeaderValue>(), results);
}
[Theory]
[MemberData(nameof(ListWithInvalidCookieHeaderDataSet))]
public void CookieHeaderValue_TryParseList_ExcludesInvalidValues(IList<CookieHeaderValue> cookies, string[] input)
{
IList<CookieHeaderValue> results;
var result = CookieHeaderValue.TryParseList(input, out results);
Assert.Equal(cookies, results);
Assert.Equal(cookies?.Count > 0, result);
}
[Theory]
[MemberData(nameof(ListWithInvalidCookieHeaderDataSet))]
public void CookieHeaderValue_ParseStrictList_ThrowsForAnyInvalidValues(IList<CookieHeaderValue> cookies, string[] input)
{
Assert.Throws<FormatException>(() => CookieHeaderValue.ParseStrictList(input));
}
[Theory]
[MemberData(nameof(ListWithInvalidCookieHeaderDataSet))]
public void CookieHeaderValue_TryParseStrictList_FailsForAnyInvalidValues(IList<CookieHeaderValue> cookies, string[] input)
{
IList<CookieHeaderValue> results;
var result = CookieHeaderValue.TryParseStrictList(input, out results);
Assert.Null(results);
Assert.False(result);
}
} }
} }

View File

@ -212,6 +212,39 @@ namespace Microsoft.Net.Http.Headers
Assert.Equal(expectedResults, results); Assert.Equal(expectedResults, results);
} }
[Fact]
public void ParseStrictList_SetOfValidValueStrings_ParsedCorrectly()
{
var inputs = new[]
{
"",
"\"tag\"",
"",
" \"tag\" ",
"\r\n \"tag\"\r\n ",
"\"tag会\"",
"\"tag\",\"tag\"",
"\"tag\", \"tag\"",
"W/\"tag\"",
};
IList<EntityTagHeaderValue> results = EntityTagHeaderValue.ParseStrictList(inputs);
var expectedResults = new[]
{
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag会\""),
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag\"", true),
}.ToList();
Assert.Equal(expectedResults, results);
}
[Fact] [Fact]
public void TryParseList_SetOfValidValueStrings_ParsedCorrectly() public void TryParseList_SetOfValidValueStrings_ParsedCorrectly()
{ {
@ -246,7 +279,40 @@ namespace Microsoft.Net.Http.Headers
} }
[Fact] [Fact]
public void ParseList_WithSomeInvlaidValues_Throws() public void TryParseStrictList_SetOfValidValueStrings_ParsedCorrectly()
{
var inputs = new[]
{
"",
"\"tag\"",
"",
" \"tag\" ",
"\r\n \"tag\"\r\n ",
"\"tag会\"",
"\"tag\",\"tag\"",
"\"tag\", \"tag\"",
"W/\"tag\"",
};
IList<EntityTagHeaderValue> results;
Assert.True(EntityTagHeaderValue.TryParseStrictList(inputs, out results));
var expectedResults = new[]
{
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag会\""),
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag\"", true),
}.ToList();
Assert.Equal(expectedResults, results);
}
[Fact]
public void ParseList_WithSomeInvlaidValues_ExcludesInvalidValues()
{ {
var inputs = new[] var inputs = new[]
{ {
@ -260,11 +326,41 @@ namespace Microsoft.Net.Http.Headers
"\"tag\", \"tag\"", "\"tag\", \"tag\"",
"W/\"tag\"", "W/\"tag\"",
}; };
Assert.Throws<FormatException>(() => EntityTagHeaderValue.ParseList(inputs)); var results = EntityTagHeaderValue.ParseList(inputs);
var expectedResults = new[]
{
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag会\""),
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag\"", true),
}.ToList();
Assert.Equal(expectedResults, results);
} }
[Fact] [Fact]
public void TryParseList_WithSomeInvlaidValues_ReturnsFalse() public void ParseStrictList_WithSomeInvlaidValues_Throws()
{
var inputs = new[]
{
"",
"\"tag\", tag, \"tag\"",
"tag, \"tag\"",
"",
" \"tag ",
"\r\n tag\"\r\n ",
"\"tag会\"",
"\"tag\", \"tag\"",
"W/\"tag\"",
};
Assert.Throws<FormatException>(() => EntityTagHeaderValue.ParseStrictList(inputs));
}
[Fact]
public void TryParseList_WithSomeInvlaidValues_ExcludesInvalidValues()
{ {
var inputs = new[] var inputs = new[]
{ {
@ -279,7 +375,38 @@ namespace Microsoft.Net.Http.Headers
"W/\"tag\"", "W/\"tag\"",
}; };
IList<EntityTagHeaderValue> results; IList<EntityTagHeaderValue> results;
Assert.False(EntityTagHeaderValue.TryParseList(inputs, out results)); Assert.True(EntityTagHeaderValue.TryParseList(inputs, out results));
var expectedResults = new[]
{
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag会\""),
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag\""),
new EntityTagHeaderValue("\"tag\"", true),
}.ToList();
Assert.Equal(expectedResults, results);
}
[Fact]
public void TryParseStrictList_WithSomeInvlaidValues_ReturnsFalse()
{
var inputs = new[]
{
"",
"\"tag\", tag, \"tag\"",
"tag, \"tag\"",
"",
" \"tag ",
"\r\n tag\"\r\n ",
"\"tag会\"",
"\"tag\", \"tag\"",
"W/\"tag\"",
};
IList<EntityTagHeaderValue> results;
Assert.False(EntityTagHeaderValue.TryParseStrictList(inputs, out results));
} }
private void CheckValidParse(string input, EntityTagHeaderValue expectedResult) private void CheckValidParse(string input, EntityTagHeaderValue expectedResult)

View File

@ -497,6 +497,24 @@ namespace Microsoft.Net.Http.Headers
Assert.Equal(expectedResults, results); Assert.Equal(expectedResults, results);
} }
[Fact]
public void ParseStrictList_SetOfValidValueStrings_ReturnsValues()
{
var inputs = new[] { "text/html,application/xhtml+xml,", "application/xml;q=0.9,image/webp,*/*;q=0.8" };
var results = MediaTypeHeaderValue.ParseStrictList(inputs);
var expectedResults = new[]
{
new MediaTypeHeaderValue("text/html"),
new MediaTypeHeaderValue("application/xhtml+xml"),
new MediaTypeHeaderValue("application/xml", 0.9),
new MediaTypeHeaderValue("image/webp"),
new MediaTypeHeaderValue("*/*", 0.8),
}.ToList();
Assert.Equal(expectedResults, results);
}
[Fact] [Fact]
public void TryParseList_SetOfValidValueStrings_ReturnsTrue() public void TryParseList_SetOfValidValueStrings_ReturnsTrue()
{ {
@ -517,18 +535,60 @@ namespace Microsoft.Net.Http.Headers
} }
[Fact] [Fact]
public void ParseList_WithSomeInvlaidValues_Throws() public void TryParseStrictList_SetOfValidValueStrings_ReturnsTrue()
{
var inputs = new[] { "text/html,application/xhtml+xml,", "application/xml;q=0.9,image/webp,*/*;q=0.8" };
IList<MediaTypeHeaderValue> results;
Assert.True(MediaTypeHeaderValue.TryParseStrictList(inputs, out results));
var expectedResults = new[]
{
new MediaTypeHeaderValue("text/html"),
new MediaTypeHeaderValue("application/xhtml+xml"),
new MediaTypeHeaderValue("application/xml", 0.9),
new MediaTypeHeaderValue("image/webp"),
new MediaTypeHeaderValue("*/*", 0.8),
}.ToList();
Assert.Equal(expectedResults, results);
}
[Fact]
public void ParseList_WithSomeInvlaidValues_IgnoresInvalidValues()
{ {
var inputs = new[] var inputs = new[]
{ {
"text/html,application/xhtml+xml, ignore-this, ignore/this", "text/html,application/xhtml+xml, ignore-this, ignore/this",
"application/xml;q=0.9,image/webp,*/*;q=0.8" "application/xml;q=0.9,image/webp,*/*;q=0.8"
}; };
Assert.Throws<FormatException>(() => MediaTypeHeaderValue.ParseList(inputs)); var results = MediaTypeHeaderValue.ParseList(inputs);
var expectedResults = new[]
{
new MediaTypeHeaderValue("text/html"),
new MediaTypeHeaderValue("application/xhtml+xml"),
new MediaTypeHeaderValue("ignore/this"),
new MediaTypeHeaderValue("application/xml", 0.9),
new MediaTypeHeaderValue("image/webp"),
new MediaTypeHeaderValue("*/*", 0.8),
}.ToList();
Assert.Equal(expectedResults, results);
} }
[Fact] [Fact]
public void TryParseList_WithSomeInvlaidValues_ReturnsFalse() public void ParseStrictList_WithSomeInvlaidValues_Throws()
{
var inputs = new[]
{
"text/html,application/xhtml+xml, ignore-this, ignore/this",
"application/xml;q=0.9,image/webp,*/*;q=0.8"
};
Assert.Throws<FormatException>(() => MediaTypeHeaderValue.ParseStrictList(inputs));
}
[Fact]
public void TryParseList_WithSomeInvlaidValues_IgnoresInvalidValues()
{ {
var inputs = new[] var inputs = new[]
{ {
@ -537,7 +597,32 @@ namespace Microsoft.Net.Http.Headers
"application/xml;q=0 4" "application/xml;q=0 4"
}; };
IList<MediaTypeHeaderValue> results; IList<MediaTypeHeaderValue> results;
Assert.False(MediaTypeHeaderValue.TryParseList(inputs, out results)); Assert.True(MediaTypeHeaderValue.TryParseList(inputs, out results));
var expectedResults = new[]
{
new MediaTypeHeaderValue("text/html"),
new MediaTypeHeaderValue("application/xhtml+xml"),
new MediaTypeHeaderValue("ignore/this"),
new MediaTypeHeaderValue("application/xml", 0.9),
new MediaTypeHeaderValue("image/webp"),
new MediaTypeHeaderValue("*/*", 0.8),
}.ToList();
Assert.Equal(expectedResults, results);
}
[Fact]
public void TryParseStrictList_WithSomeInvlaidValues_ReturnsFalse()
{
var inputs = new[]
{
"text/html,application/xhtml+xml, ignore-this, ignore/this",
"application/xml;q=0.9,image/webp,*/*;q=0.8",
"application/xml;q=0 4"
};
IList<MediaTypeHeaderValue> results;
Assert.False(MediaTypeHeaderValue.TryParseStrictList(inputs, out results));
} }
[Theory] [Theory]

View File

@ -364,6 +364,39 @@ namespace Microsoft.Net.Http.Headers
Assert.Equal(expectedResults, results); Assert.Equal(expectedResults, results);
} }
[Fact]
public void ParseStrictList_SetOfValidValueStrings_ParsedCorrectly()
{
var inputs = new[]
{
"",
"name=value1",
"",
" name = value2 ",
"\r\n name =value3\r\n ",
"name=\"value 4\"",
"name=\"value会5\"",
"name=value6,name=value7",
"name=\"value 8\", name= \"value 9\"",
};
var results = NameValueHeaderValue.ParseStrictList(inputs);
var expectedResults = new[]
{
new NameValueHeaderValue("name", "value1"),
new NameValueHeaderValue("name", "value2"),
new NameValueHeaderValue("name", "value3"),
new NameValueHeaderValue("name", "\"value 4\""),
new NameValueHeaderValue("name", "\"value会5\""),
new NameValueHeaderValue("name", "value6"),
new NameValueHeaderValue("name", "value7"),
new NameValueHeaderValue("name", "\"value 8\""),
new NameValueHeaderValue("name", "\"value 9\""),
}.ToList();
Assert.Equal(expectedResults, results);
}
[Fact] [Fact]
public void TryParseList_SetOfValidValueStrings_ParsedCorrectly() public void TryParseList_SetOfValidValueStrings_ParsedCorrectly()
{ {
@ -399,7 +432,41 @@ namespace Microsoft.Net.Http.Headers
} }
[Fact] [Fact]
public void ParseList_WithSomeInvlaidValues_Throws() public void TryParseStrictList_SetOfValidValueStrings_ParsedCorrectly()
{
var inputs = new[]
{
"",
"name=value1",
"",
" name = value2 ",
"\r\n name =value3\r\n ",
"name=\"value 4\"",
"name=\"value会5\"",
"name=value6,name=value7",
"name=\"value 8\", name= \"value 9\"",
};
IList<NameValueHeaderValue> results;
Assert.True(NameValueHeaderValue.TryParseStrictList(inputs, out results));
var expectedResults = new[]
{
new NameValueHeaderValue("name", "value1"),
new NameValueHeaderValue("name", "value2"),
new NameValueHeaderValue("name", "value3"),
new NameValueHeaderValue("name", "\"value 4\""),
new NameValueHeaderValue("name", "\"value会5\""),
new NameValueHeaderValue("name", "value6"),
new NameValueHeaderValue("name", "value7"),
new NameValueHeaderValue("name", "\"value 8\""),
new NameValueHeaderValue("name", "\"value 9\""),
}.ToList();
Assert.Equal(expectedResults, results);
}
[Fact]
public void ParseList_WithSomeInvlaidValues_ExcludesInvalidValues()
{ {
var inputs = new[] var inputs = new[]
{ {
@ -413,11 +480,47 @@ namespace Microsoft.Net.Http.Headers
"name8=value8,name9=value9", "name8=value8,name9=value9",
"name10=\"value 10\", name11= \"value 11\"", "name10=\"value 10\", name11= \"value 11\"",
}; };
Assert.Throws<FormatException>(() => NameValueHeaderValue.ParseList(inputs)); var results = NameValueHeaderValue.ParseList(inputs);
var expectedResults = new[]
{
new NameValueHeaderValue("name1", "value1"),
new NameValueHeaderValue("name2"),
new NameValueHeaderValue("name3", "3"),
new NameValueHeaderValue("a"),
new NameValueHeaderValue("name4", "value4"),
new NameValueHeaderValue("b"),
new NameValueHeaderValue("6"),
new NameValueHeaderValue("name7", "\"value会7\""),
new NameValueHeaderValue("name8", "value8"),
new NameValueHeaderValue("name9", "value9"),
new NameValueHeaderValue("name10", "\"value 10\""),
new NameValueHeaderValue("name11", "\"value 11\""),
}.ToList();
Assert.Equal(expectedResults, results);
} }
[Fact] [Fact]
public void TryParseList_WithSomeInvlaidValues_ReturnsFalse() public void ParseStrictList_WithSomeInvlaidValues_Throws()
{
var inputs = new[]
{
"",
"name1=value1",
"name2",
" name3 = 3, value a",
"name4 =value4, name5 = value5 b",
"name6=\"value 6",
"name7=\"value会7\"",
"name8=value8,name9=value9",
"name10=\"value 10\", name11= \"value 11\"",
};
Assert.Throws<FormatException>(() => NameValueHeaderValue.ParseStrictList(inputs));
}
[Fact]
public void TryParseList_WithSomeInvlaidValues_ExcludesInvalidValues()
{ {
var inputs = new[] var inputs = new[]
{ {
@ -432,7 +535,44 @@ namespace Microsoft.Net.Http.Headers
"name10=\"value 10\", name11= \"value 11\"", "name10=\"value 10\", name11= \"value 11\"",
}; };
IList<NameValueHeaderValue> results; IList<NameValueHeaderValue> results;
Assert.False(NameValueHeaderValue.TryParseList(inputs, out results)); Assert.True(NameValueHeaderValue.TryParseList(inputs, out results));
var expectedResults = new[]
{
new NameValueHeaderValue("name1", "value1"),
new NameValueHeaderValue("name2"),
new NameValueHeaderValue("name3", "3"),
new NameValueHeaderValue("a"),
new NameValueHeaderValue("name4", "value4"),
new NameValueHeaderValue("b"),
new NameValueHeaderValue("6"),
new NameValueHeaderValue("name7", "\"value会7\""),
new NameValueHeaderValue("name8", "value8"),
new NameValueHeaderValue("name9", "value9"),
new NameValueHeaderValue("name10", "\"value 10\""),
new NameValueHeaderValue("name11", "\"value 11\""),
}.ToList();
Assert.Equal(expectedResults, results);
}
[Fact]
public void TryParseStrictList_WithSomeInvlaidValues_ReturnsFalse()
{
var inputs = new[]
{
"",
"name1=value1",
"name2",
" name3 = 3, value a",
"name4 =value4, name5 = value5 b",
"name6=\"value 6",
"name7=\"value会7\"",
"name8=value8,name9=value9",
"name10=\"value 10\", name11= \"value 11\"",
};
IList<NameValueHeaderValue> results;
Assert.False(NameValueHeaderValue.TryParseStrictList(inputs, out results));
} }
#region Helper methods #region Helper methods

View File

@ -95,6 +95,7 @@ namespace Microsoft.Net.Http.Headers
}; };
} }
} }
public static TheoryData<IList<SetCookieHeaderValue>, string[]> ListOfSetCookieHeaderDataSet public static TheoryData<IList<SetCookieHeaderValue>, string[]> ListOfSetCookieHeaderDataSet
{ {
get get
@ -141,7 +142,73 @@ namespace Microsoft.Net.Http.Headers
} }
} }
// TODO: [Fact] public static TheoryData<IList<SetCookieHeaderValue>, string[]> ListWithInvalidSetCookieHeaderDataSet
{
get
{
var dataset = new TheoryData<IList<SetCookieHeaderValue>, string[]>();
var header1 = new SetCookieHeaderValue("name1", "n1=v1&n2=v2&n3=v3")
{
Domain = "domain1",
Expires = new DateTimeOffset(1994, 11, 6, 8, 49, 37, TimeSpan.Zero),
HttpOnly = true,
MaxAge = TimeSpan.FromDays(1),
Path = "path1",
Secure = true
};
var string1 = "name1=n1=v1&n2=v2&n3=v3; expires=Sun, 06 Nov 1994 08:49:37 GMT; max-age=86400; domain=domain1; path=path1; secure; httponly";
var header2 = new SetCookieHeaderValue("name2", "value2");
var string2 = "name2=value2";
var header3 = new SetCookieHeaderValue("name3", "value3")
{
MaxAge = TimeSpan.FromDays(1),
};
var string3 = "name3=value3; max-age=86400";
var header4 = new SetCookieHeaderValue("name4", "value4")
{
Domain = "domain1",
Expires = new DateTimeOffset(1994, 11, 6, 8, 49, 37, TimeSpan.Zero),
};
var string4 = "name4=value4; expires=Sun, 06 Nov 1994 08:49:37 GMT; domain=domain1;";
var invalidString1 = "ipt={\"v\":{\"L\":3},\"pt:{\"d\":3},\"ct\":{},\"_t\":44,\"_v\":\"2\"}";
var invalidHeader2a = new SetCookieHeaderValue("expires", "Sun");
var invalidHeader2b = new SetCookieHeaderValue("domain", "domain1");
var invalidString2 = "ipt={\"v\":{\"L\":3},\"pt\":{d\":3},\"ct\":{},\"_t\":44,\"_v\":\"2\"}; expires=Sun, 06 Nov 1994 08:49:37 GMT; domain=domain1";
var invalidHeader3 = new SetCookieHeaderValue("domain", "domain1")
{
Expires = new DateTimeOffset(1994, 11, 6, 8, 49, 37, TimeSpan.Zero),
};
var invalidString3 = "ipt={\"v\":{\"L\":3},\"pt\":{\"d:3},\"ct\":{},\"_t\":44,\"_v\":\"2\"}; domain=domain1; expires=Sun, 06 Nov 1994 08:49:37 GMT";
dataset.Add(null, new[] { invalidString1 });
dataset.Add(new[] { invalidHeader2a, invalidHeader2b }.ToList(), new[] { invalidString2 });
dataset.Add(new[] { invalidHeader3 }.ToList(), new[] { invalidString3 });
dataset.Add(new[] { header1 }.ToList(), new[] { string1, invalidString1 });
dataset.Add(new[] { header1 }.ToList(), new[] { invalidString1, null, "", " ", ",", " , ", string1 });
dataset.Add(new[] { header1 }.ToList(), new[] { string1 + ", " + invalidString1 });
dataset.Add(new[] { header1 }.ToList(), new[] { invalidString1 + ", " + string1 });
dataset.Add(new[] { header1, header2, header3, header4 }.ToList(), new[] { invalidString1, string1, string2, string3, string4 });
dataset.Add(new[] { header1, header2, header3, header4 }.ToList(), new[] { string1, invalidString1, string2, string3, string4 });
dataset.Add(new[] { header1, header2, header3, header4 }.ToList(), new[] { string1, string2, invalidString1, string3, string4 });
dataset.Add(new[] { header1, header2, header3, header4 }.ToList(), new[] { string1, string2, string3, invalidString1, string4 });
dataset.Add(new[] { header1, header2, header3, header4 }.ToList(), new[] { string1, string2, string3, string4, invalidString1 });
dataset.Add(new[] { header1, header2, header3, header4 }.ToList(), new[] { string.Join(",", invalidString1, string1, string2, string3, string4) });
dataset.Add(new[] { header1, header2, header3, header4 }.ToList(), new[] { string.Join(",", string1, invalidString1, string2, string3, string4) });
dataset.Add(new[] { header1, header2, header3, header4 }.ToList(), new[] { string.Join(",", string1, string2, invalidString1, string3, string4) });
dataset.Add(new[] { header1, header2, header3, header4 }.ToList(), new[] { string.Join(",", string1, string2, string3, invalidString1, string4) });
dataset.Add(new[] { header1, header2, header3, header4 }.ToList(), new[] { string.Join(",", string1, string2, string3, string4, invalidString1) });
return dataset;
}
}
[Fact]
public void SetCookieHeaderValue_CtorThrowsOnNullName() public void SetCookieHeaderValue_CtorThrowsOnNullName()
{ {
Assert.Throws<ArgumentNullException>(() => new SetCookieHeaderValue(null, "value")); Assert.Throws<ArgumentNullException>(() => new SetCookieHeaderValue(null, "value"));
@ -255,5 +322,61 @@ namespace Microsoft.Net.Http.Headers
Assert.Equal(cookies, results); Assert.Equal(cookies, results);
} }
[Theory]
[MemberData(nameof(ListOfSetCookieHeaderDataSet))]
public void SetCookieHeaderValue_ParseStrictList_AcceptsValidValues(IList<SetCookieHeaderValue> cookies, string[] input)
{
var results = SetCookieHeaderValue.ParseStrictList(input);
Assert.Equal(cookies, results);
}
[Theory]
[MemberData(nameof(ListOfSetCookieHeaderDataSet))]
public void SetCookieHeaderValue_TryParseStrictList_AcceptsValidValues(IList<SetCookieHeaderValue> cookies, string[] input)
{
IList<SetCookieHeaderValue> results;
bool result = SetCookieHeaderValue.TryParseStrictList(input, out results);
Assert.True(result);
Assert.Equal(cookies, results);
}
[Theory]
[MemberData(nameof(ListWithInvalidSetCookieHeaderDataSet))]
public void SetCookieHeaderValue_ParseList_ExcludesInvalidValues(IList<SetCookieHeaderValue> cookies, string[] input)
{
var results = SetCookieHeaderValue.ParseList(input);
// ParseList aways returns a list, even if empty. TryParseList may return null (via out).
Assert.Equal(cookies ?? new List<SetCookieHeaderValue>(), results);
}
[Theory]
[MemberData(nameof(ListWithInvalidSetCookieHeaderDataSet))]
public void SetCookieHeaderValue_TryParseList_ExcludesInvalidValues(IList<SetCookieHeaderValue> cookies, string[] input)
{
IList<SetCookieHeaderValue> results;
bool result = SetCookieHeaderValue.TryParseList(input, out results);
Assert.Equal(cookies, results);
Assert.Equal(cookies?.Count > 0, result);
}
[Theory]
[MemberData(nameof(ListWithInvalidSetCookieHeaderDataSet))]
public void SetCookieHeaderValue_ParseStrictList_ThrowsForAnyInvalidValues(IList<SetCookieHeaderValue> cookies, string[] input)
{
Assert.Throws<FormatException>(() => SetCookieHeaderValue.ParseStrictList(input));
}
[Theory]
[MemberData(nameof(ListWithInvalidSetCookieHeaderDataSet))]
public void SetCookieHeaderValue_TryParseStrictList_FailsForAnyInvalidValues(IList<SetCookieHeaderValue> cookies, string[] input)
{
IList<SetCookieHeaderValue> results;
bool result = SetCookieHeaderValue.TryParseStrictList(input, out results);
Assert.Null(results);
Assert.False(result);
}
} }
} }

View File

@ -233,6 +233,43 @@ namespace Microsoft.Net.Http.Headers
Assert.Equal(expectedResults, results); Assert.Equal(expectedResults, results);
} }
[Fact]
public void ParseStrictList_SetOfValidValueStrings_ParsedCorrectly()
{
var inputs = new[]
{
"",
"text1",
"text2,",
"textA,textB",
"text3;q=0.5",
"text4;q=0.5,",
" text5 ; q = 0.50 ",
"\r\n text6 ; q = 0.05 ",
"text7,text8;q=0.5",
" text9 , text10 ; q = 0.5 ",
};
IList<StringWithQualityHeaderValue> results = StringWithQualityHeaderValue.ParseStrictList(inputs);
var expectedResults = new[]
{
new StringWithQualityHeaderValue("text1"),
new StringWithQualityHeaderValue("text2"),
new StringWithQualityHeaderValue("textA"),
new StringWithQualityHeaderValue("textB"),
new StringWithQualityHeaderValue("text3", 0.5),
new StringWithQualityHeaderValue("text4", 0.5),
new StringWithQualityHeaderValue("text5", 0.5),
new StringWithQualityHeaderValue("text6", 0.05),
new StringWithQualityHeaderValue("text7"),
new StringWithQualityHeaderValue("text8", 0.5),
new StringWithQualityHeaderValue("text9"),
new StringWithQualityHeaderValue("text10", 0.5),
}.ToList();
Assert.Equal(expectedResults, results);
}
[Fact] [Fact]
public void TryParseList_SetOfValidValueStrings_ParsedCorrectly() public void TryParseList_SetOfValidValueStrings_ParsedCorrectly()
{ {
@ -272,7 +309,45 @@ namespace Microsoft.Net.Http.Headers
} }
[Fact] [Fact]
public void ParseList_WithSomeInvlaidValues_Throws() public void TryParseStrictList_SetOfValidValueStrings_ParsedCorrectly()
{
var inputs = new[]
{
"",
"text1",
"text2,",
"textA,textB",
"text3;q=0.5",
"text4;q=0.5,",
" text5 ; q = 0.50 ",
"\r\n text6 ; q = 0.05 ",
"text7,text8;q=0.5",
" text9 , text10 ; q = 0.5 ",
};
IList<StringWithQualityHeaderValue> results;
Assert.True(StringWithQualityHeaderValue.TryParseStrictList(inputs, out results));
var expectedResults = new[]
{
new StringWithQualityHeaderValue("text1"),
new StringWithQualityHeaderValue("text2"),
new StringWithQualityHeaderValue("textA"),
new StringWithQualityHeaderValue("textB"),
new StringWithQualityHeaderValue("text3", 0.5),
new StringWithQualityHeaderValue("text4", 0.5),
new StringWithQualityHeaderValue("text5", 0.5),
new StringWithQualityHeaderValue("text6", 0.05),
new StringWithQualityHeaderValue("text7"),
new StringWithQualityHeaderValue("text8", 0.5),
new StringWithQualityHeaderValue("text9"),
new StringWithQualityHeaderValue("text10", 0.5),
}.ToList();
Assert.Equal(expectedResults, results);
}
[Fact]
public void ParseList_WithSomeInvlaidValues_IgnoresInvalidValues()
{ {
var inputs = new[] var inputs = new[]
{ {
@ -288,11 +363,49 @@ namespace Microsoft.Net.Http.Headers
"text7,text8;q=0.5", "text7,text8;q=0.5",
" text9 , text10 ; q = 0.5 ", " text9 , text10 ; q = 0.5 ",
}; };
Assert.Throws<FormatException>(() => StringWithQualityHeaderValue.ParseList(inputs)); var results = StringWithQualityHeaderValue.ParseList(inputs);
var expectedResults = new[]
{
new StringWithQualityHeaderValue("text1"),
new StringWithQualityHeaderValue("1"),
new StringWithQualityHeaderValue("text2"),
new StringWithQualityHeaderValue("text3", 0.5),
new StringWithQualityHeaderValue("text4", 0.5),
new StringWithQualityHeaderValue("stuff"),
new StringWithQualityHeaderValue("text5", 0.5),
new StringWithQualityHeaderValue("text6", 0.05),
new StringWithQualityHeaderValue("text7"),
new StringWithQualityHeaderValue("text8", 0.5),
new StringWithQualityHeaderValue("text9"),
new StringWithQualityHeaderValue("text10", 0.5),
}.ToList();
Assert.Equal(expectedResults, results);
} }
[Fact] [Fact]
public void TryParseList_WithSomeInvlaidValues_ReturnsFalse() public void ParseStrictList_WithSomeInvlaidValues_Throws()
{
var inputs = new[]
{
"",
"text1",
"text 1",
"text2",
"\"text 2\",",
"text3;q=0.5",
"text4;q=0.5, extra stuff",
" text5 ; q = 0.50 ",
"\r\n text6 ; q = 0.05 ",
"text7,text8;q=0.5",
" text9 , text10 ; q = 0.5 ",
};
Assert.Throws<FormatException>(() => StringWithQualityHeaderValue.ParseStrictList(inputs));
}
[Fact]
public void TryParseList_WithSomeInvlaidValues_IgnoresInvalidValues()
{ {
var inputs = new[] var inputs = new[]
{ {
@ -309,7 +422,46 @@ namespace Microsoft.Net.Http.Headers
" text9 , text10 ; q = 0.5 ", " text9 , text10 ; q = 0.5 ",
}; };
IList<StringWithQualityHeaderValue> results; IList<StringWithQualityHeaderValue> results;
Assert.False(StringWithQualityHeaderValue.TryParseList(inputs, out results)); Assert.True(StringWithQualityHeaderValue.TryParseList(inputs, out results));
var expectedResults = new[]
{
new StringWithQualityHeaderValue("text1"),
new StringWithQualityHeaderValue("1"),
new StringWithQualityHeaderValue("text2"),
new StringWithQualityHeaderValue("text3", 0.5),
new StringWithQualityHeaderValue("text4", 0.5),
new StringWithQualityHeaderValue("stuff"),
new StringWithQualityHeaderValue("text5", 0.5),
new StringWithQualityHeaderValue("text6", 0.05),
new StringWithQualityHeaderValue("text7"),
new StringWithQualityHeaderValue("text8", 0.5),
new StringWithQualityHeaderValue("text9"),
new StringWithQualityHeaderValue("text10", 0.5),
}.ToList();
Assert.Equal(expectedResults, results);
}
[Fact]
public void TryParseStrictList_WithSomeInvlaidValues_ReturnsFalse()
{
var inputs = new[]
{
"",
"text1",
"text 1",
"text2",
"\"text 2\",",
"text3;q=0.5",
"text4;q=0.5, extra stuff",
" text5 ; q = 0.50 ",
"\r\n text6 ; q = 0.05 ",
"text7,text8;q=0.5",
" text9 , text10 ; q = 0.5 ",
};
IList<StringWithQualityHeaderValue> results;
Assert.False(StringWithQualityHeaderValue.TryParseStrictList(inputs, out results));
} }
#region Helper methods #region Helper methods