Imported Upstream version 5.10.0.69

Former-commit-id: fc39669a0b707dd3c063977486506b6793da2890
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2018-01-29 19:03:06 +00:00
parent d8f8abd549
commit e2950ec768
6283 changed files with 453847 additions and 91879 deletions

View File

@ -37,9 +37,11 @@
</Compile>
</ItemGroup>
<ItemGroup>
<Reference Include="System.Collections" />
<Reference Include="System.Collections.Specialized" />
<Reference Include="System.Diagnostics.Debug" />
<Reference Include="System.Diagnostics.Tracing" />
<Reference Include="System.Linq" />
<Reference Include="System.Resources.ResourceManager" />
<Reference Include="System.Runtime" />
<Reference Include="System.Runtime.Extensions" />

View File

@ -2,23 +2,28 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
internal class HeaderInfo
namespace System.Net
{
internal readonly bool IsRequestRestricted;
internal readonly bool IsResponseRestricted;
//
// Note that the HeaderName field is not always valid, and should not
// be used after initialization. In particular, the HeaderInfo returned
// for an unknown header will not have the correct header name.
//
internal readonly string HeaderName;
internal readonly bool AllowMultiValues;
internal HeaderInfo(string name, bool requestRestricted, bool responseRestricted, bool multi)
internal class HeaderInfo
{
HeaderName = name;
IsRequestRestricted = requestRestricted;
IsResponseRestricted = responseRestricted;
AllowMultiValues = multi;
internal readonly bool IsRequestRestricted;
internal readonly bool IsResponseRestricted;
internal readonly Func<string, string[]> Parser;
//
// Note that the HeaderName field is not always valid, and should not
// be used after initialization. In particular, the HeaderInfo returned
// for an unknown header will not have the correct header name.
//
internal readonly string HeaderName;
internal readonly bool AllowMultiValues;
internal HeaderInfo(string name, bool requestRestricted, bool responseRestricted, bool multi, Func<string, string[]> parser)
{
HeaderName = name;
IsRequestRestricted = requestRestricted;
IsResponseRestricted = responseRestricted;
Parser = parser;
AllowMultiValues = multi;
}
}
}
}

View File

@ -3,77 +3,141 @@
// See the LICENSE file in the project root for more information.
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
namespace System.Net
{
internal class HeaderInfoTable
{
private static Hashtable HeaderHashTable;
private static HeaderInfo UnknownHeaderInfo = new HeaderInfo(string.Empty, false, false, false);
private static readonly Func<string, string[]> s_singleParser = value => new[] { value };
private static readonly Func<string, string[]> s_multiParser = value => ParseValueHelper(value, isSetCookie: false);
private static readonly Func<string, string[]> s_setCookieParser = value => ParseValueHelper(value, isSetCookie: true);
private static readonly HeaderInfo s_unknownHeaderInfo = new HeaderInfo(string.Empty, false, false, false, s_singleParser);
private static readonly Hashtable s_headerHashTable;
private static string[] ParseValueHelper(string value, bool isSetCookie)
{
// RFC 6265: (for Set-Cookie header)
// If the name-value-pair string lacks a %x3D ("=") character, ignore the set-cookie-string entirely.
if (isSetCookie && (value.IndexOf('=') < 0)) return Array.Empty<string>();
var tempStringCollection = new List<string>();
bool inquote = false;
int startIndex = 0;
int length = 0;
for (int i = 0; i < value.Length; i++)
{
if (value[i] == '\"')
{
inquote = !inquote;
}
else if ((value[i] == ',') && !inquote)
{
string singleValue = value.SubstringTrim(startIndex, length);
if (!isSetCookie || !IsDuringExpiresAttributeParsing(singleValue))
{
tempStringCollection.Add(singleValue);
startIndex = i + 1;
length = 0;
continue;
}
}
length++;
}
// Now add the last of the header values to the string table.
if (startIndex < value.Length && length > 0)
{
tempStringCollection.Add(value.SubstringTrim(startIndex, length));
}
return tempStringCollection.ToArray();
}
// This method is to check if we are in the middle of parsing the Expires attribute
// for Set-Cookie header. It needs to check two conditions: 1. If current attribute
// is Expires. 2. Have we finished parsing it yet. Because the Expires attribute
// will contain exactly one comma, no comma means we are still parsing it.
private static bool IsDuringExpiresAttributeParsing(string singleValue)
{
// Current cookie doesn't contain any attributes.
if (singleValue.IndexOf(';') < 0) return false;
string lastElement = singleValue.Split(';').Last();
bool noComma = lastElement.IndexOf(',') < 0;
string lastAttribute = lastElement.Split('=')[0].Trim();
bool isExpires = string.Equals(lastAttribute, "Expires", StringComparison.OrdinalIgnoreCase);
return (isExpires && noComma);
}
static HeaderInfoTable()
{
HeaderInfo[] InfoArray = new HeaderInfo[]
{
new HeaderInfo(HttpKnownHeaderNames.Age, false, false, false ),
new HeaderInfo(HttpKnownHeaderNames.Allow, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.Accept, true, false, true ),
new HeaderInfo(HttpKnownHeaderNames.Authorization, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.AcceptRanges, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.AcceptCharset, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.AcceptEncoding, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.AcceptLanguage, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.Cookie, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.Connection, true, false, true ),
new HeaderInfo(HttpKnownHeaderNames.ContentMD5, false, false, false ),
new HeaderInfo(HttpKnownHeaderNames.ContentType, true, false, false ),
new HeaderInfo(HttpKnownHeaderNames.CacheControl, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.ContentRange, false, false, false ),
new HeaderInfo(HttpKnownHeaderNames.ContentLength, true, true, false ),
new HeaderInfo(HttpKnownHeaderNames.ContentEncoding, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.ContentLanguage, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.ContentLocation, false, false, false ),
new HeaderInfo(HttpKnownHeaderNames.Date, true, false, false ),
new HeaderInfo(HttpKnownHeaderNames.ETag, false, false, false ),
new HeaderInfo(HttpKnownHeaderNames.Expect, true, false, true ),
new HeaderInfo(HttpKnownHeaderNames.Expires, false, false, false ),
new HeaderInfo(HttpKnownHeaderNames.From, false, false, false ),
new HeaderInfo(HttpKnownHeaderNames.Host, true, false, false ),
new HeaderInfo(HttpKnownHeaderNames.IfMatch, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.IfRange, false, false, false ),
new HeaderInfo(HttpKnownHeaderNames.IfNoneMatch, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.IfModifiedSince, true, false, false ),
new HeaderInfo(HttpKnownHeaderNames.IfUnmodifiedSince, false, false, false ),
new HeaderInfo(HttpKnownHeaderNames.KeepAlive, false, true, false ),
new HeaderInfo(HttpKnownHeaderNames.Location, false, false, false ),
new HeaderInfo(HttpKnownHeaderNames.LastModified, false, false, false ),
new HeaderInfo(HttpKnownHeaderNames.MaxForwards, false, false, false ),
new HeaderInfo(HttpKnownHeaderNames.Pragma, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.ProxyAuthenticate, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.ProxyAuthorization, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.ProxyConnection, true, false, true ),
new HeaderInfo(HttpKnownHeaderNames.Range, true, false, true ),
new HeaderInfo(HttpKnownHeaderNames.Referer, true, false, false ),
new HeaderInfo(HttpKnownHeaderNames.RetryAfter, false, false, false ),
new HeaderInfo(HttpKnownHeaderNames.Server, false, false, false ),
new HeaderInfo(HttpKnownHeaderNames.SetCookie, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.SetCookie2, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.TE, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.Trailer, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.TransferEncoding, true, true, true ),
new HeaderInfo(HttpKnownHeaderNames.Upgrade, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.UserAgent, true, false, false ),
new HeaderInfo(HttpKnownHeaderNames.Via, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.Vary, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.Warning, false, false, true ),
new HeaderInfo(HttpKnownHeaderNames.WWWAuthenticate, false, true, true ),
new HeaderInfo(HttpKnownHeaderNames.Age, false, false, false, s_singleParser),
new HeaderInfo(HttpKnownHeaderNames.Allow, false, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.Accept, true, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.Authorization, false, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.AcceptRanges, false, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.AcceptCharset, false, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.AcceptEncoding, false, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.AcceptLanguage, false, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.Cookie, false, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.Connection, true, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.ContentMD5, false, false, false, s_singleParser),
new HeaderInfo(HttpKnownHeaderNames.ContentType, true, false, false, s_singleParser),
new HeaderInfo(HttpKnownHeaderNames.CacheControl, false, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.ContentRange, false, false, false, s_singleParser),
new HeaderInfo(HttpKnownHeaderNames.ContentLength, true, true, false, s_singleParser),
new HeaderInfo(HttpKnownHeaderNames.ContentEncoding, false, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.ContentLanguage, false, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.ContentLocation, false, false, false, s_singleParser),
new HeaderInfo(HttpKnownHeaderNames.Date, true, false, false, s_singleParser),
new HeaderInfo(HttpKnownHeaderNames.ETag, false, false, false, s_singleParser),
new HeaderInfo(HttpKnownHeaderNames.Expect, true, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.Expires, false, false, false, s_singleParser),
new HeaderInfo(HttpKnownHeaderNames.From, false, false, false, s_singleParser),
new HeaderInfo(HttpKnownHeaderNames.Host, true, false, false, s_singleParser),
new HeaderInfo(HttpKnownHeaderNames.IfMatch, false, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.IfRange, false, false, false, s_singleParser),
new HeaderInfo(HttpKnownHeaderNames.IfNoneMatch, false, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.IfModifiedSince, true, false, false, s_singleParser),
new HeaderInfo(HttpKnownHeaderNames.IfUnmodifiedSince, false, false, false, s_singleParser),
new HeaderInfo(HttpKnownHeaderNames.KeepAlive, false, true, false, s_singleParser),
new HeaderInfo(HttpKnownHeaderNames.Location, false, false, false, s_singleParser),
new HeaderInfo(HttpKnownHeaderNames.LastModified, false, false, false, s_singleParser),
new HeaderInfo(HttpKnownHeaderNames.MaxForwards, false, false, false, s_singleParser),
new HeaderInfo(HttpKnownHeaderNames.Pragma, false, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.ProxyAuthenticate, false, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.ProxyAuthorization, false, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.ProxyConnection, true, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.Range, true, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.Referer, true, false, false, s_singleParser),
new HeaderInfo(HttpKnownHeaderNames.RetryAfter, false, false, false, s_singleParser),
new HeaderInfo(HttpKnownHeaderNames.Server, false, false, false, s_singleParser),
new HeaderInfo(HttpKnownHeaderNames.SetCookie, false, false, true, s_setCookieParser),
new HeaderInfo(HttpKnownHeaderNames.SetCookie2, false, false, true, s_setCookieParser),
new HeaderInfo(HttpKnownHeaderNames.TE, false, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.Trailer, false, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.TransferEncoding, true, true, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.Upgrade, false, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.UserAgent, true, false, false, s_singleParser),
new HeaderInfo(HttpKnownHeaderNames.Via, false, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.Vary, false, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.Warning, false, false, true, s_multiParser),
new HeaderInfo(HttpKnownHeaderNames.WWWAuthenticate, false, true, true, s_singleParser)
};
HeaderHashTable = new Hashtable(InfoArray.Length * 2, CaseInsensitiveAscii.StaticInstance);
s_headerHashTable = new Hashtable(InfoArray.Length * 2, CaseInsensitiveAscii.StaticInstance);
for (int i = 0; i < InfoArray.Length; i++)
{
HeaderHashTable[InfoArray[i].HeaderName] = InfoArray[i];
s_headerHashTable[InfoArray[i].HeaderName] = InfoArray[i];
}
}
@ -81,10 +145,10 @@ namespace System.Net
{
get
{
HeaderInfo tempHeaderInfo = (HeaderInfo)HeaderHashTable[name];
HeaderInfo tempHeaderInfo = (HeaderInfo)s_headerHashTable[name];
if (tempHeaderInfo == null)
{
return UnknownHeaderInfo;
return s_unknownHeaderInfo;
}
return tempHeaderInfo;
}

View File

@ -228,9 +228,71 @@ namespace System.Net
return InnerCollection.GetValues(index);
}
// GetValues
// Routine Description:
// This method takes a header name and returns a string array representing
// the individual values for that headers. For example, if the headers
// contained the line Accept: text/plain, text/html then
// GetValues("Accept") would return an array of two strings: "text/plain"
// and "text/html".
// Arguments:
// header - Name of the header.
// Return Value:
// string[] - array of parsed string objects
public override string[] GetValues(string header)
{
return InnerCollection.GetValues(header);
// First get the information about the header and the values for
// the header.
HeaderInfo info = HeaderInfo[header];
string[] values = InnerCollection.GetValues(header);
// If we have no information about the header or it doesn't allow
// multiple values, just return the values.
if (info == null || values == null || !info.AllowMultiValues)
{
return values;
}
// Here we have a multi value header. We need to go through
// each entry in the multi values array, and if an entry itself
// has multiple values we'll need to combine those in.
//
// We do some optimazation here, where we try not to copy the
// values unless there really is one that have multiple values.
string[] tempValues;
List<string> valueList = null;
for (int i = 0; i < values.Length; i++)
{
// Parse this value header.
tempValues = info.Parser(values[i]);
// If we don't have an array list yet, see if this
// value has multiple values.
if (valueList == null)
{
// If it's not empty, replace valueList.
// Because for invalid WebRequest headers, we will return empty
// valueList instead of the default NameValueCollection.GetValues().
if (tempValues != null)
{
// It does, so we need to create an array list that
// represents the Values, then trim out this one and
// the ones after it that haven't been parsed yet.
valueList = new List<string>(values);
valueList.RemoveRange(i, values.Length - i);
valueList.AddRange(tempValues);
}
}
else
{
// We already have an List, so just add the values.
valueList.AddRange(tempValues);
}
}
// See if we have an List. If we don't, just return the values.
// Otherwise convert the List to a string array and return that.
if (valueList != null)
{
return valueList.ToArray();
}
return values;
}
public override string GetKey(int index)

View File

@ -529,6 +529,108 @@ namespace System.Net.Tests
AssertExtensions.Throws<ArgumentException>(paramName, () => headers.Add(header));
}
private const string HeaderType = "Set-Cookie";
private const string Cookie1 = "locale=en; path=/; expires=Fri, 05 Oct 2018 06:28:57 -0000";
private const string Cookie2 = "uuid=123abc; path=/; expires=Fri, 05 Oct 2018 06:28:57 -0000; secure; HttpOnly";
private const string Cookie3 = "country=US; path=/; expires=Fri, 05 Oct 2018 06:28:57 -0000";
private const string Cookie4 = "m_session=session1; path=/; expires=Sun, 08 Oct 2017 00:28:57 -0000; secure; HttpOnly";
private const string Cookie1NoAttribute = "locale=en";
private const string Cookie2NoAttribute = "uuid=123abc";
private const string Cookie3NoAttribute = "country=US";
private const string Cookie4NoAttribute = "m_session=session1";
private const string CookieInvalid = "helloWorld";
[Fact]
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")]
public void GetValues_MultipleSetCookieHeadersWithExpiresAttribute_Success()
{
WebHeaderCollection w = new WebHeaderCollection();
w.Add(HeaderType, Cookie1);
w.Add(HeaderType, Cookie2);
w.Add(HeaderType, Cookie3);
w.Add(HeaderType, Cookie4);
string[] values = w.GetValues(HeaderType);
Assert.Equal(4, values.Length);
Assert.Equal(Cookie1, values[0]);
Assert.Equal(Cookie2, values[1]);
Assert.Equal(Cookie3, values[2]);
Assert.Equal(Cookie4, values[3]);
}
[Fact]
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")]
public void GetValues_SingleSetCookieHeaderWithMultipleCookiesWithExpiresAttribute_Success()
{
WebHeaderCollection w = new WebHeaderCollection();
w.Add(HeaderType, Cookie1 + "," + Cookie2 + "," + Cookie3 + "," + Cookie4);
string[] values = w.GetValues(HeaderType);
Assert.Equal(4, values.Length);
Assert.Equal(Cookie1, values[0]);
Assert.Equal(Cookie2, values[1]);
Assert.Equal(Cookie3, values[2]);
Assert.Equal(Cookie4, values[3]);
}
[Fact]
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")]
public void GetValues_MultipleSetCookieHeadersWithNoAttribute_Success()
{
WebHeaderCollection w = new WebHeaderCollection();
w.Add(HeaderType, Cookie1NoAttribute);
w.Add(HeaderType, Cookie2NoAttribute);
w.Add(HeaderType, Cookie3NoAttribute);
w.Add(HeaderType, Cookie4NoAttribute);
string[] values = w.GetValues(HeaderType);
Assert.Equal(4, values.Length);
Assert.Equal(Cookie1NoAttribute, values[0]);
Assert.Equal(Cookie2NoAttribute, values[1]);
Assert.Equal(Cookie3NoAttribute, values[2]);
Assert.Equal(Cookie4NoAttribute, values[3]);
}
[Fact]
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")]
public void GetValues_SingleSetCookieHeaderWithMultipleCookiesWithNoAttribute_Success()
{
WebHeaderCollection w = new WebHeaderCollection();
w.Add(HeaderType, Cookie1NoAttribute + "," + Cookie2NoAttribute + "," + Cookie3NoAttribute + "," + Cookie4NoAttribute);
string[] values = w.GetValues(HeaderType);
Assert.Equal(4, values.Length);
Assert.Equal(Cookie1NoAttribute, values[0]);
Assert.Equal(Cookie2NoAttribute, values[1]);
Assert.Equal(Cookie3NoAttribute, values[2]);
Assert.Equal(Cookie4NoAttribute, values[3]);
}
[Fact]
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")]
public void GetValues_InvalidSetCookieHeader_Success()
{
WebHeaderCollection w = new WebHeaderCollection();
w.Add(HeaderType, CookieInvalid);
string[] values = w.GetValues(HeaderType);
Assert.Equal(0, values.Length);
}
[Fact]
public void GetValues_MultipleValuesHeader_Success()
{
WebHeaderCollection w = new WebHeaderCollection();
string headerType = "Accept";
w.Add(headerType, "text/plain, text/html");
string[] values = w.GetValues(headerType);
Assert.Equal(2, values.Length);
Assert.Equal("text/plain", values[0]);
Assert.Equal("text/html", values[1]);
}
[Fact]
public void HttpRequestHeader_Add_Rmemove_Success()
{