#758 Convert the header parsers to use StringSegment
This commit is contained in:
parent
e8123db21e
commit
2ce2d8b6c5
|
|
@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.Http.Extensions
|
|||
{
|
||||
return string.Empty;
|
||||
}
|
||||
return HeaderUtilities.RemoveQuotes(mediaType.Boundary);
|
||||
return HeaderUtilities.RemoveQuotes(mediaType.Boundary).ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http.Internal;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace Microsoft.AspNetCore.Http.Features
|
||||
|
|
@ -293,14 +294,14 @@ namespace Microsoft.AspNetCore.Http.Features
|
|||
{
|
||||
// Content-Disposition: form-data; name="key";
|
||||
return contentDisposition != null && contentDisposition.DispositionType.Equals("form-data")
|
||||
&& string.IsNullOrEmpty(contentDisposition.FileName) && string.IsNullOrEmpty(contentDisposition.FileNameStar);
|
||||
&& StringSegment.IsNullOrEmpty(contentDisposition.FileName) && StringSegment.IsNullOrEmpty(contentDisposition.FileNameStar);
|
||||
}
|
||||
|
||||
private bool HasFileContentDisposition(ContentDispositionHeaderValue contentDisposition)
|
||||
{
|
||||
// Content-Disposition: form-data; name="myfile1"; filename="Misc 002.jpg"
|
||||
return contentDisposition != null && contentDisposition.DispositionType.Equals("form-data")
|
||||
&& (!string.IsNullOrEmpty(contentDisposition.FileName) || !string.IsNullOrEmpty(contentDisposition.FileNameStar));
|
||||
&& (!StringSegment.IsNullOrEmpty(contentDisposition.FileName) || !StringSegment.IsNullOrEmpty(contentDisposition.FileNameStar));
|
||||
}
|
||||
|
||||
// Content-Type: multipart/form-data; boundary="----WebKitFormBoundarymx2fSWqWSd0OxQqq"
|
||||
|
|
@ -308,7 +309,7 @@ namespace Microsoft.AspNetCore.Http.Features
|
|||
private static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit)
|
||||
{
|
||||
var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary);
|
||||
if (string.IsNullOrWhiteSpace(boundary))
|
||||
if (StringSegment.IsNullOrEmpty(boundary))
|
||||
{
|
||||
throw new InvalidDataException("Missing content-type boundary.");
|
||||
}
|
||||
|
|
@ -316,7 +317,7 @@ namespace Microsoft.AspNetCore.Http.Features
|
|||
{
|
||||
throw new InvalidDataException($"Multipart boundary length limit {lengthLimit} exceeded.");
|
||||
}
|
||||
return boundary;
|
||||
return boundary.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,8 +75,8 @@ namespace Microsoft.AspNetCore.Http.Internal
|
|||
for (var i = 0; i < cookies.Count; i++)
|
||||
{
|
||||
var cookie = cookies[i];
|
||||
var name = Uri.UnescapeDataString(cookie.Name);
|
||||
var value = Uri.UnescapeDataString(cookie.Value);
|
||||
var name = Uri.UnescapeDataString(cookie.Name.Value);
|
||||
var value = Uri.UnescapeDataString(cookie.Value.Value);
|
||||
store[name] = value;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,11 +39,11 @@ namespace Microsoft.AspNetCore.WebUtilities
|
|||
Section = section;
|
||||
_contentDispositionHeader = header;
|
||||
|
||||
Name = HeaderUtilities.RemoveQuotes(_contentDispositionHeader.Name) ?? string.Empty;
|
||||
Name = HeaderUtilities.RemoveQuotes(_contentDispositionHeader.Name).ToString();
|
||||
FileName = HeaderUtilities.RemoveQuotes(
|
||||
_contentDispositionHeader.FileNameStar ??
|
||||
_contentDispositionHeader.FileName ??
|
||||
string.Empty);
|
||||
_contentDispositionHeader.FileNameStar.HasValue ?
|
||||
_contentDispositionHeader.FileNameStar :
|
||||
_contentDispositionHeader.FileName).ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ namespace Microsoft.AspNetCore.WebUtilities
|
|||
|
||||
Section = section;
|
||||
_contentDispositionHeader = header;
|
||||
Name = HeaderUtilities.RemoveQuotes(_contentDispositionHeader.Name);
|
||||
Name = HeaderUtilities.RemoveQuotes(_contentDispositionHeader.Name).ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Net.Http.Headers
|
||||
{
|
||||
internal abstract class BaseHeaderParser<T> : HttpHeaderParser<T>
|
||||
|
|
@ -10,9 +12,9 @@ namespace Microsoft.Net.Http.Headers
|
|||
{
|
||||
}
|
||||
|
||||
protected abstract int GetParsedValueLength(string value, int startIndex, out T parsedValue);
|
||||
protected abstract int GetParsedValueLength(StringSegment value, int startIndex, out T parsedValue);
|
||||
|
||||
public sealed override bool TryParseValue(string value, ref int index, out T parsedValue)
|
||||
public sealed override bool TryParseValue(StringSegment value, ref int index, out T parsedValue)
|
||||
{
|
||||
parsedValue = default(T);
|
||||
|
||||
|
|
@ -21,7 +23,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
// Accept: text/xml; q=1
|
||||
// Accept:
|
||||
// Accept: text/plain; q=0.2
|
||||
if (string.IsNullOrEmpty(value) || (index == value.Length))
|
||||
if (StringSegment.IsNullOrEmpty(value) || (index == value.Length))
|
||||
{
|
||||
return SupportsMultipleValues;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics.Contracts;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Net.Http.Headers
|
||||
{
|
||||
|
|
@ -32,10 +33,10 @@ namespace Microsoft.Net.Http.Headers
|
|||
private static readonly HttpHeaderParser<CacheControlHeaderValue> Parser
|
||||
= new GenericHeaderParser<CacheControlHeaderValue>(true, GetCacheControlLength);
|
||||
|
||||
private static readonly Action<string> CheckIsValidTokenAction = CheckIsValidToken;
|
||||
private static readonly Action<StringSegment> CheckIsValidTokenAction = CheckIsValidToken;
|
||||
|
||||
private bool _noCache;
|
||||
private ICollection<string> _noCacheHeaders;
|
||||
private ICollection<StringSegment> _noCacheHeaders;
|
||||
private bool _noStore;
|
||||
private TimeSpan? _maxAge;
|
||||
private TimeSpan? _sharedMaxAge;
|
||||
|
|
@ -46,7 +47,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
private bool _onlyIfCached;
|
||||
private bool _public;
|
||||
private bool _private;
|
||||
private ICollection<string> _privateHeaders;
|
||||
private ICollection<StringSegment> _privateHeaders;
|
||||
private bool _mustRevalidate;
|
||||
private bool _proxyRevalidate;
|
||||
private IList<NameValueHeaderValue> _extensions;
|
||||
|
|
@ -62,13 +63,13 @@ namespace Microsoft.Net.Http.Headers
|
|||
set { _noCache = value; }
|
||||
}
|
||||
|
||||
public ICollection<string> NoCacheHeaders
|
||||
public ICollection<StringSegment> NoCacheHeaders
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_noCacheHeaders == null)
|
||||
{
|
||||
_noCacheHeaders = new ObjectCollection<string>(CheckIsValidTokenAction);
|
||||
_noCacheHeaders = new ObjectCollection<StringSegment>(CheckIsValidTokenAction);
|
||||
}
|
||||
return _noCacheHeaders;
|
||||
}
|
||||
|
|
@ -134,13 +135,13 @@ namespace Microsoft.Net.Http.Headers
|
|||
set { _private = value; }
|
||||
}
|
||||
|
||||
public ICollection<string> PrivateHeaders
|
||||
public ICollection<StringSegment> PrivateHeaders
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_privateHeaders == null)
|
||||
{
|
||||
_privateHeaders = new ObjectCollection<string>(CheckIsValidTokenAction);
|
||||
_privateHeaders = new ObjectCollection<StringSegment>(CheckIsValidTokenAction);
|
||||
}
|
||||
return _privateHeaders;
|
||||
}
|
||||
|
|
@ -259,13 +260,13 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
|
||||
if (!HeaderUtilities.AreEqualCollections(_noCacheHeaders, other._noCacheHeaders,
|
||||
StringComparer.OrdinalIgnoreCase))
|
||||
StringSegmentComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!HeaderUtilities.AreEqualCollections(_privateHeaders, other._privateHeaders,
|
||||
StringComparer.OrdinalIgnoreCase))
|
||||
StringSegmentComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -299,7 +300,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
{
|
||||
foreach (var noCacheHeader in _noCacheHeaders)
|
||||
{
|
||||
result = result ^ StringComparer.OrdinalIgnoreCase.GetHashCode(noCacheHeader);
|
||||
result = result ^ StringSegmentComparer.OrdinalIgnoreCase.GetHashCode(noCacheHeader);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -307,7 +308,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
{
|
||||
foreach (var privateHeader in _privateHeaders)
|
||||
{
|
||||
result = result ^ StringComparer.OrdinalIgnoreCase.GetHashCode(privateHeader);
|
||||
result = result ^ StringSegmentComparer.OrdinalIgnoreCase.GetHashCode(privateHeader);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -322,7 +323,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return result;
|
||||
}
|
||||
|
||||
public static CacheControlHeaderValue Parse(string input)
|
||||
public static CacheControlHeaderValue Parse(StringSegment input)
|
||||
{
|
||||
int index = 0;
|
||||
// Cache-Control is unusual because there are no required values so the parser will succeed for an empty string, but still return null.
|
||||
|
|
@ -334,7 +335,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return result;
|
||||
}
|
||||
|
||||
public static bool TryParse(string input, out CacheControlHeaderValue parsedValue)
|
||||
public static bool TryParse(StringSegment input, out CacheControlHeaderValue parsedValue)
|
||||
{
|
||||
int index = 0;
|
||||
// Cache-Control is unusual because there are no required values so the parser will succeed for an empty string, but still return null.
|
||||
|
|
@ -346,13 +347,13 @@ namespace Microsoft.Net.Http.Headers
|
|||
return false;
|
||||
}
|
||||
|
||||
private static int GetCacheControlLength(string input, int startIndex, out CacheControlHeaderValue parsedValue)
|
||||
private static int GetCacheControlLength(StringSegment input, int startIndex, out CacheControlHeaderValue parsedValue)
|
||||
{
|
||||
Contract.Requires(startIndex >= 0);
|
||||
|
||||
parsedValue = null;
|
||||
|
||||
if (string.IsNullOrEmpty(input) || (startIndex >= input.Length))
|
||||
if (StringSegment.IsNullOrEmpty(input) || (startIndex >= input.Length))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -403,7 +404,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
switch (name.Length)
|
||||
{
|
||||
case 6:
|
||||
if (string.Equals(PublicString, name, StringComparison.OrdinalIgnoreCase))
|
||||
if (StringSegment.Equals(PublicString, name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
success = TrySetTokenOnlyValue(nameValue, ref cc._public);
|
||||
}
|
||||
|
|
@ -414,11 +415,11 @@ namespace Microsoft.Net.Http.Headers
|
|||
break;
|
||||
|
||||
case 7:
|
||||
if (string.Equals(MaxAgeString, name, StringComparison.OrdinalIgnoreCase))
|
||||
if (StringSegment.Equals(MaxAgeString, name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
success = TrySetTimeSpan(nameValue, ref cc._maxAge);
|
||||
}
|
||||
else if(string.Equals(PrivateString, name, StringComparison.OrdinalIgnoreCase))
|
||||
else if(StringSegment.Equals(PrivateString, name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
success = TrySetOptionalTokenList(nameValue, ref cc._private, ref cc._privateHeaders);
|
||||
}
|
||||
|
|
@ -429,15 +430,15 @@ namespace Microsoft.Net.Http.Headers
|
|||
break;
|
||||
|
||||
case 8:
|
||||
if (string.Equals(NoCacheString, name, StringComparison.OrdinalIgnoreCase))
|
||||
if (StringSegment.Equals(NoCacheString, name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
success = TrySetOptionalTokenList(nameValue, ref cc._noCache, ref cc._noCacheHeaders);
|
||||
}
|
||||
else if (string.Equals(NoStoreString, name, StringComparison.OrdinalIgnoreCase))
|
||||
else if (StringSegment.Equals(NoStoreString, name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
success = TrySetTokenOnlyValue(nameValue, ref cc._noStore);
|
||||
}
|
||||
else if (string.Equals(SharedMaxAgeString, name, StringComparison.OrdinalIgnoreCase))
|
||||
else if (StringSegment.Equals(SharedMaxAgeString, name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
success = TrySetTimeSpan(nameValue, ref cc._sharedMaxAge);
|
||||
}
|
||||
|
|
@ -448,7 +449,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
break;
|
||||
|
||||
case 9:
|
||||
if (string.Equals(MaxStaleString, name, StringComparison.OrdinalIgnoreCase))
|
||||
if (StringSegment.Equals(MaxStaleString, name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
success = ((nameValue.Value == null) || TrySetTimeSpan(nameValue, ref cc._maxStaleLimit));
|
||||
if (success)
|
||||
|
|
@ -456,7 +457,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
cc._maxStale = true;
|
||||
}
|
||||
}
|
||||
else if (string.Equals(MinFreshString, name, StringComparison.OrdinalIgnoreCase))
|
||||
else if (StringSegment.Equals(MinFreshString, name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
success = TrySetTimeSpan(nameValue, ref cc._minFresh);
|
||||
}
|
||||
|
|
@ -467,7 +468,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
break;
|
||||
|
||||
case 12:
|
||||
if (string.Equals(NoTransformString, name, StringComparison.OrdinalIgnoreCase))
|
||||
if (StringSegment.Equals(NoTransformString, name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
success = TrySetTokenOnlyValue(nameValue, ref cc._noTransform);
|
||||
}
|
||||
|
|
@ -478,7 +479,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
break;
|
||||
|
||||
case 14:
|
||||
if (string.Equals(OnlyIfCachedString, name, StringComparison.OrdinalIgnoreCase))
|
||||
if (StringSegment.Equals(OnlyIfCachedString, name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
success = TrySetTokenOnlyValue(nameValue, ref cc._onlyIfCached);
|
||||
}
|
||||
|
|
@ -489,7 +490,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
break;
|
||||
|
||||
case 15:
|
||||
if (string.Equals(MustRevalidateString, name, StringComparison.OrdinalIgnoreCase))
|
||||
if (StringSegment.Equals(MustRevalidateString, name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
success = TrySetTokenOnlyValue(nameValue, ref cc._mustRevalidate);
|
||||
}
|
||||
|
|
@ -500,7 +501,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
break;
|
||||
|
||||
case 16:
|
||||
if (string.Equals(ProxyRevalidateString, name, StringComparison.OrdinalIgnoreCase))
|
||||
if (StringSegment.Equals(ProxyRevalidateString, name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
success = TrySetTokenOnlyValue(nameValue, ref cc._proxyRevalidate);
|
||||
}
|
||||
|
|
@ -538,7 +539,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
private static bool TrySetOptionalTokenList(
|
||||
NameValueHeaderValue nameValue,
|
||||
ref bool boolField,
|
||||
ref ICollection<string> destination)
|
||||
ref ICollection<StringSegment> destination)
|
||||
{
|
||||
Contract.Requires(nameValue != null);
|
||||
|
||||
|
|
@ -582,10 +583,10 @@ namespace Microsoft.Net.Http.Headers
|
|||
|
||||
if (destination == null)
|
||||
{
|
||||
destination = new ObjectCollection<string>(CheckIsValidTokenAction);
|
||||
destination = new ObjectCollection<StringSegment>(CheckIsValidTokenAction);
|
||||
}
|
||||
|
||||
destination.Add(valueString.Substring(current, tokenLength));
|
||||
destination.Add(valueString.Subsegment(current, tokenLength));
|
||||
|
||||
current = current + tokenLength;
|
||||
}
|
||||
|
|
@ -637,10 +638,10 @@ namespace Microsoft.Net.Http.Headers
|
|||
sb.Append(value);
|
||||
}
|
||||
|
||||
private static void AppendValues(StringBuilder sb, IEnumerable<string> values)
|
||||
private static void AppendValues(StringBuilder sb, IEnumerable<StringSegment> values)
|
||||
{
|
||||
var first = true;
|
||||
foreach (string value in values)
|
||||
foreach (var value in values)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
|
|
@ -655,7 +656,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
}
|
||||
|
||||
private static void CheckIsValidToken(string item)
|
||||
private static void CheckIsValidToken(StringSegment item)
|
||||
{
|
||||
HeaderUtilities.CheckValidToken(item, nameof(item));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ using System.Buffers;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Net.Http.Headers
|
||||
{
|
||||
|
|
@ -20,26 +22,28 @@ namespace Microsoft.Net.Http.Headers
|
|||
private const string ModificationDateString = "modification-date";
|
||||
private const string ReadDateString = "read-date";
|
||||
private const string SizeString = "size";
|
||||
private static readonly char[] QuestionMark = new char[] { '?' };
|
||||
private static readonly char[] SingleQuote = new char[] { '\'' };
|
||||
|
||||
private static readonly HttpHeaderParser<ContentDispositionHeaderValue> Parser
|
||||
= new GenericHeaderParser<ContentDispositionHeaderValue>(false, GetDispositionTypeLength);
|
||||
|
||||
// Use list instead of dictionary since we may have multiple parameters with the same name.
|
||||
private ObjectCollection<NameValueHeaderValue> _parameters;
|
||||
private string _dispositionType;
|
||||
private StringSegment _dispositionType;
|
||||
|
||||
private ContentDispositionHeaderValue()
|
||||
{
|
||||
// Used by the parser to create a new instance of this type.
|
||||
}
|
||||
|
||||
public ContentDispositionHeaderValue(string dispositionType)
|
||||
public ContentDispositionHeaderValue(StringSegment dispositionType)
|
||||
{
|
||||
CheckDispositionTypeFormat(dispositionType, "dispositionType");
|
||||
_dispositionType = dispositionType;
|
||||
}
|
||||
|
||||
public string DispositionType
|
||||
public StringSegment DispositionType
|
||||
{
|
||||
get { return _dispositionType; }
|
||||
set
|
||||
|
|
@ -63,19 +67,19 @@ namespace Microsoft.Net.Http.Headers
|
|||
|
||||
// Helpers to access specific parameters in the list
|
||||
|
||||
public string Name
|
||||
public StringSegment Name
|
||||
{
|
||||
get { return GetName(NameString); }
|
||||
set { SetName(NameString, value); }
|
||||
}
|
||||
|
||||
public string FileName
|
||||
public StringSegment FileName
|
||||
{
|
||||
get { return GetName(FileNameString); }
|
||||
set { SetName(FileNameString, value); }
|
||||
}
|
||||
|
||||
public string FileNameStar
|
||||
public StringSegment FileNameStar
|
||||
{
|
||||
get { return GetName(FileNameStarString); }
|
||||
set { SetName(FileNameStarString, value); }
|
||||
|
|
@ -146,9 +150,9 @@ namespace Microsoft.Net.Http.Headers
|
|||
/// Sets both FileName and FileNameStar using encodings appropriate for HTTP headers.
|
||||
/// </summary>
|
||||
/// <param name="fileName"></param>
|
||||
public void SetHttpFileName(string fileName)
|
||||
public void SetHttpFileName(StringSegment fileName)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(fileName))
|
||||
if (!StringSegment.IsNullOrEmpty(fileName))
|
||||
{
|
||||
FileName = Sanatize(fileName);
|
||||
}
|
||||
|
|
@ -164,7 +168,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
/// The FileNameStar paraemter is removed.
|
||||
/// </summary>
|
||||
/// <param name="fileName"></param>
|
||||
public void SetMimeFileName(string fileName)
|
||||
public void SetMimeFileName(StringSegment fileName)
|
||||
{
|
||||
FileNameStar = null;
|
||||
FileName = fileName;
|
||||
|
|
@ -184,42 +188,41 @@ namespace Microsoft.Net.Http.Headers
|
|||
return false;
|
||||
}
|
||||
|
||||
return (string.Compare(_dispositionType, other._dispositionType, StringComparison.OrdinalIgnoreCase) == 0) &&
|
||||
return _dispositionType.Equals(other._dispositionType, StringComparison.OrdinalIgnoreCase) &&
|
||||
HeaderUtilities.AreEqualCollections(_parameters, other._parameters);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
// The dispositionType string is case-insensitive.
|
||||
return StringComparer.OrdinalIgnoreCase.GetHashCode(_dispositionType) ^ NameValueHeaderValue.GetHashCode(_parameters);
|
||||
return StringSegmentComparer.OrdinalIgnoreCase.GetHashCode(_dispositionType) ^ NameValueHeaderValue.GetHashCode(_parameters);
|
||||
}
|
||||
|
||||
public static ContentDispositionHeaderValue Parse(string input)
|
||||
public static ContentDispositionHeaderValue Parse(StringSegment input)
|
||||
{
|
||||
var index = 0;
|
||||
return Parser.ParseValue(input, ref index);
|
||||
}
|
||||
|
||||
public static bool TryParse(string input, out ContentDispositionHeaderValue parsedValue)
|
||||
public static bool TryParse(StringSegment input, out ContentDispositionHeaderValue parsedValue)
|
||||
{
|
||||
var index = 0;
|
||||
return Parser.TryParseValue(input, ref index, out parsedValue);
|
||||
}
|
||||
|
||||
private static int GetDispositionTypeLength(string input, int startIndex, out ContentDispositionHeaderValue parsedValue)
|
||||
private static int GetDispositionTypeLength(StringSegment input, int startIndex, out ContentDispositionHeaderValue parsedValue)
|
||||
{
|
||||
Contract.Requires(startIndex >= 0);
|
||||
|
||||
parsedValue = null;
|
||||
|
||||
if (string.IsNullOrEmpty(input) || (startIndex >= input.Length))
|
||||
if (StringSegment.IsNullOrEmpty(input) || (startIndex >= input.Length))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Caller must remove leading whitespaces. If not, we'll return 0.
|
||||
string dispositionType = null;
|
||||
var dispositionTypeLength = GetDispositionTypeExpressionLength(input, startIndex, out dispositionType);
|
||||
var dispositionTypeLength = GetDispositionTypeExpressionLength(input, startIndex, out var dispositionType);
|
||||
|
||||
if (dispositionTypeLength == 0)
|
||||
{
|
||||
|
|
@ -247,7 +250,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return current - startIndex;
|
||||
}
|
||||
|
||||
private static int GetDispositionTypeExpressionLength(string input, int startIndex, out string dispositionType)
|
||||
private static int GetDispositionTypeExpressionLength(StringSegment input, int startIndex, out StringSegment dispositionType)
|
||||
{
|
||||
Contract.Requires((input != null) && (input.Length > 0) && (startIndex < input.Length));
|
||||
|
||||
|
|
@ -263,20 +266,19 @@ namespace Microsoft.Net.Http.Headers
|
|||
return 0;
|
||||
}
|
||||
|
||||
dispositionType = input.Substring(startIndex, typeLength);
|
||||
dispositionType = input.Subsegment(startIndex, typeLength);
|
||||
return typeLength;
|
||||
}
|
||||
|
||||
private static void CheckDispositionTypeFormat(string dispositionType, string parameterName)
|
||||
private static void CheckDispositionTypeFormat(StringSegment dispositionType, string parameterName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(dispositionType))
|
||||
if (StringSegment.IsNullOrEmpty(dispositionType))
|
||||
{
|
||||
throw new ArgumentException("An empty string is not allowed.", parameterName);
|
||||
}
|
||||
|
||||
// When adding values using strongly typed objects, no leading/trailing LWS (whitespaces) are allowed.
|
||||
string tempDispositionType;
|
||||
var dispositionTypeLength = GetDispositionTypeExpressionLength(dispositionType, 0, out tempDispositionType);
|
||||
var dispositionTypeLength = GetDispositionTypeExpressionLength(dispositionType, 0, out var tempDispositionType);
|
||||
if ((dispositionTypeLength == 0) || (tempDispositionType.Length != dispositionType.Length))
|
||||
{
|
||||
throw new FormatException(string.Format(CultureInfo.InvariantCulture,
|
||||
|
|
@ -291,11 +293,11 @@ namespace Microsoft.Net.Http.Headers
|
|||
var dateParameter = NameValueHeaderValue.Find(_parameters, parameter);
|
||||
if (dateParameter != null)
|
||||
{
|
||||
string dateString = dateParameter.Value;
|
||||
var dateString = dateParameter.Value;
|
||||
// Should have quotes, remove them.
|
||||
if (IsQuoted(dateString))
|
||||
{
|
||||
dateString = dateString.Substring(1, dateString.Length - 2);
|
||||
dateString = dateString.Subsegment(1, dateString.Length - 2);
|
||||
}
|
||||
DateTimeOffset date;
|
||||
if (HttpRuleParser.TryStringToDate(dateString, out date))
|
||||
|
|
@ -357,17 +359,17 @@ namespace Microsoft.Net.Http.Headers
|
|||
return result;
|
||||
}
|
||||
// May not have been encoded
|
||||
return nameParameter.Value;
|
||||
return nameParameter.Value.ToString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Add/update the given parameter in the list, encoding if necessary.
|
||||
// Remove if value is null/Empty
|
||||
private void SetName(string parameter, string value)
|
||||
private void SetName(StringSegment parameter, StringSegment value)
|
||||
{
|
||||
var nameParameter = NameValueHeaderValue.Find(_parameters, parameter);
|
||||
if (string.IsNullOrEmpty(value))
|
||||
if (StringSegment.IsNullOrEmpty(value))
|
||||
{
|
||||
// Remove parameter
|
||||
if (nameParameter != null)
|
||||
|
|
@ -377,7 +379,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
else
|
||||
{
|
||||
var processedValue = string.Empty;
|
||||
var processedValue = StringSegment.Empty;
|
||||
if (parameter.EndsWith("*", StringComparison.Ordinal))
|
||||
{
|
||||
processedValue = Encode5987(value);
|
||||
|
|
@ -399,14 +401,14 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
|
||||
// Returns input for decoding failures, as the content might not be encoded
|
||||
private string EncodeAndQuoteMime(string input)
|
||||
private StringSegment EncodeAndQuoteMime(StringSegment input)
|
||||
{
|
||||
var result = input;
|
||||
var needsQuotes = false;
|
||||
// Remove bounding quotes, they'll get re-added later
|
||||
if (IsQuoted(result))
|
||||
{
|
||||
result = result.Substring(1, result.Length - 2);
|
||||
result = result.Subsegment(1, result.Length - 2);
|
||||
needsQuotes = true;
|
||||
}
|
||||
|
||||
|
|
@ -423,8 +425,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
if (needsQuotes)
|
||||
{
|
||||
// '\' and '"' must be escaped in a quoted string
|
||||
result = result.Replace(@"\", @"\\");
|
||||
result = result.Replace(@"""", @"\""");
|
||||
result = result.ToString().Replace(@"\", @"\\").Replace(@"""", @"\""");
|
||||
// Re-add quotes "value"
|
||||
result = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", result);
|
||||
}
|
||||
|
|
@ -432,7 +433,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
|
||||
// Replaces characters not suitable for HTTP headers with '_' rather than MIME encoding them.
|
||||
private string Sanatize(string input)
|
||||
private StringSegment Sanatize(StringSegment input)
|
||||
{
|
||||
var result = input;
|
||||
|
||||
|
|
@ -455,7 +456,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
|
||||
// Returns true if the value starts and ends with a quote
|
||||
private bool IsQuoted(string value)
|
||||
private bool IsQuoted(StringSegment value)
|
||||
{
|
||||
Contract.Assert(value != null);
|
||||
|
||||
|
|
@ -464,13 +465,13 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
|
||||
// tspecials are required to be in a quoted string. Only non-ascii needs to be encoded.
|
||||
private bool RequiresEncoding(string input)
|
||||
private bool RequiresEncoding(StringSegment input)
|
||||
{
|
||||
Contract.Assert(input != null);
|
||||
|
||||
foreach (char c in input)
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
if ((int)c > 0x7f)
|
||||
if ((int)input[i] > 0x7f)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
@ -479,15 +480,23 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
|
||||
// Encode using MIME encoding
|
||||
private string EncodeMime(string input)
|
||||
private unsafe string EncodeMime(StringSegment input)
|
||||
{
|
||||
var buffer = Encoding.UTF8.GetBytes(input);
|
||||
var encodedName = Convert.ToBase64String(buffer);
|
||||
return string.Format(CultureInfo.InvariantCulture, "=?utf-8?B?{0}?=", encodedName);
|
||||
fixed (char* chars = input.Buffer)
|
||||
{
|
||||
var byteCount = Encoding.UTF8.GetByteCount(chars + input.Offset, input.Length);
|
||||
var buffer = new byte[byteCount];
|
||||
fixed (byte* bytes = buffer)
|
||||
{
|
||||
Encoding.UTF8.GetBytes(chars + input.Offset, input.Length, bytes, byteCount);
|
||||
}
|
||||
var encodedName = Convert.ToBase64String(buffer);
|
||||
return "=?utf-8?B?" + encodedName + "?=";
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to decode MIME encoded strings
|
||||
private bool TryDecodeMime(string input, out string output)
|
||||
private bool TryDecodeMime(StringSegment input, out string output)
|
||||
{
|
||||
Contract.Assert(input != null);
|
||||
|
||||
|
|
@ -498,9 +507,11 @@ namespace Microsoft.Net.Http.Headers
|
|||
{
|
||||
return false;
|
||||
}
|
||||
var parts = processedInput.Split('?');
|
||||
|
||||
var parts = processedInput.Split(QuestionMark).ToArray();
|
||||
// "=, encodingName, encodingType, encodedData, ="
|
||||
if (parts.Length != 5 || parts[0] != "\"=" || parts[4] != "=\"" || parts[2].ToLowerInvariant() != "b")
|
||||
if (parts.Length != 5 || parts[0] != "\"=" || parts[4] != "=\""
|
||||
|| !parts[2].Equals("b", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Not encoded.
|
||||
// This does not support multi-line encoding.
|
||||
|
|
@ -510,8 +521,8 @@ namespace Microsoft.Net.Http.Headers
|
|||
|
||||
try
|
||||
{
|
||||
var encoding = Encoding.GetEncoding(parts[1]);
|
||||
var bytes = Convert.FromBase64String(parts[3]);
|
||||
var encoding = Encoding.GetEncoding(parts[1].ToString());
|
||||
var bytes = Convert.FromBase64String(parts[3].ToString());
|
||||
output = encoding.GetString(bytes, 0, bytes.Length);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -528,11 +539,12 @@ namespace Microsoft.Net.Http.Headers
|
|||
|
||||
// Encode a string using RFC 5987 encoding
|
||||
// encoding'lang'PercentEncodedSpecials
|
||||
private string Encode5987(string input)
|
||||
private string Encode5987(StringSegment input)
|
||||
{
|
||||
var builder = new StringBuilder("UTF-8\'\'");
|
||||
foreach (char c in input)
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
var c = input[i];
|
||||
// attr-char = ALPHA / DIGIT / "!" / "#" / "$" / "&" / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
|
||||
// ; token except ( "*" / "'" / "%" )
|
||||
if (c > 0x7F) // Encodes as multiple utf-8 bytes
|
||||
|
|
@ -569,10 +581,11 @@ namespace Microsoft.Net.Http.Headers
|
|||
|
||||
// Attempt to decode using RFC 5987 encoding.
|
||||
// encoding'language'my%20string
|
||||
private bool TryDecode5987(string input, out string output)
|
||||
private bool TryDecode5987(StringSegment input, out string output)
|
||||
{
|
||||
output = null;
|
||||
var parts = input.Split('\'');
|
||||
|
||||
var parts = input.Split(SingleQuote).ToArray();
|
||||
if (parts.Length != 3)
|
||||
{
|
||||
return false;
|
||||
|
|
@ -582,7 +595,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
byte[] unescapedBytes = null;
|
||||
try
|
||||
{
|
||||
var encoding = Encoding.GetEncoding(parts[0]);
|
||||
var encoding = Encoding.GetEncoding(parts[0].ToString());
|
||||
|
||||
var dataString = parts[2];
|
||||
unescapedBytes = ArrayPool<byte>.Shared.Rent(dataString.Length);
|
||||
|
|
@ -629,7 +642,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return true;
|
||||
}
|
||||
|
||||
private static bool IsHexEncoding(string pattern, int index)
|
||||
private static bool IsHexEncoding(StringSegment pattern, int index)
|
||||
{
|
||||
if ((pattern.Length - index) < 3)
|
||||
{
|
||||
|
|
@ -661,7 +674,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return true;
|
||||
}
|
||||
|
||||
private static byte HexUnescape(string pattern, ref int index)
|
||||
private static byte HexUnescape(StringSegment pattern, ref int index)
|
||||
{
|
||||
if ((index < 0) || (index >= pattern.Length))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Net.Http.Headers
|
||||
{
|
||||
|
|
@ -23,7 +24,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
|
||||
return header.DispositionType.Equals("form-data")
|
||||
&& (!string.IsNullOrEmpty(header.FileName) || !string.IsNullOrEmpty(header.FileNameStar));
|
||||
&& (!StringSegment.IsNullOrEmpty(header.FileName) || !StringSegment.IsNullOrEmpty(header.FileNameStar));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -39,7 +40,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
|
||||
return header.DispositionType.Equals("form-data")
|
||||
&& string.IsNullOrEmpty(header.FileName) && string.IsNullOrEmpty(header.FileNameStar);
|
||||
&& StringSegment.IsNullOrEmpty(header.FileName) && StringSegment.IsNullOrEmpty(header.FileNameStar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Diagnostics.Contracts;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Net.Http.Headers
|
||||
{
|
||||
|
|
@ -13,7 +14,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
private static readonly HttpHeaderParser<ContentRangeHeaderValue> Parser
|
||||
= new GenericHeaderParser<ContentRangeHeaderValue>(false, GetContentRangeLength);
|
||||
|
||||
private string _unit;
|
||||
private StringSegment _unit;
|
||||
private long? _from;
|
||||
private long? _to;
|
||||
private long? _length;
|
||||
|
|
@ -77,7 +78,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
_unit = HeaderUtilities.BytesUnit;
|
||||
}
|
||||
|
||||
public string Unit
|
||||
public StringSegment Unit
|
||||
{
|
||||
get { return _unit; }
|
||||
set
|
||||
|
|
@ -122,12 +123,12 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
|
||||
return ((_from == other._from) && (_to == other._to) && (_length == other._length) &&
|
||||
(string.Compare(_unit, other._unit, StringComparison.OrdinalIgnoreCase) == 0));
|
||||
StringSegment.Equals(_unit, other._unit, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var result = StringComparer.OrdinalIgnoreCase.GetHashCode(_unit);
|
||||
var result = StringSegmentComparer.OrdinalIgnoreCase.GetHashCode(_unit);
|
||||
|
||||
if (HasRange)
|
||||
{
|
||||
|
|
@ -144,7 +145,8 @@ namespace Microsoft.Net.Http.Headers
|
|||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder(_unit);
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(_unit);
|
||||
sb.Append(' ');
|
||||
|
||||
if (HasRange)
|
||||
|
|
@ -171,25 +173,25 @@ namespace Microsoft.Net.Http.Headers
|
|||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static ContentRangeHeaderValue Parse(string input)
|
||||
public static ContentRangeHeaderValue Parse(StringSegment input)
|
||||
{
|
||||
var index = 0;
|
||||
return Parser.ParseValue(input, ref index);
|
||||
}
|
||||
|
||||
public static bool TryParse(string input, out ContentRangeHeaderValue parsedValue)
|
||||
public static bool TryParse(StringSegment input, out ContentRangeHeaderValue parsedValue)
|
||||
{
|
||||
var index = 0;
|
||||
return Parser.TryParseValue(input, ref index, out parsedValue);
|
||||
}
|
||||
|
||||
private static int GetContentRangeLength(string input, int startIndex, out ContentRangeHeaderValue parsedValue)
|
||||
private static int GetContentRangeLength(StringSegment input, int startIndex, out ContentRangeHeaderValue parsedValue)
|
||||
{
|
||||
Contract.Requires(startIndex >= 0);
|
||||
|
||||
parsedValue = null;
|
||||
|
||||
if (string.IsNullOrEmpty(input) || (startIndex >= input.Length))
|
||||
if (StringSegment.IsNullOrEmpty(input) || (startIndex >= input.Length))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -202,7 +204,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return 0;
|
||||
}
|
||||
|
||||
var unit = input.Substring(startIndex, unitLength);
|
||||
var unit = input.Subsegment(startIndex, unitLength);
|
||||
var current = startIndex + unitLength;
|
||||
var separatorLength = HttpRuleParser.GetWhitespaceLength(input, current);
|
||||
|
||||
|
|
@ -259,7 +261,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return current - startIndex;
|
||||
}
|
||||
|
||||
private static bool TryGetLengthLength(string input, ref int current, out int lengthLength)
|
||||
private static bool TryGetLengthLength(StringSegment input, ref int current, out int lengthLength)
|
||||
{
|
||||
lengthLength = 0;
|
||||
|
||||
|
|
@ -284,7 +286,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return true;
|
||||
}
|
||||
|
||||
private static bool TryGetRangeLength(string input, ref int current, out int fromLength, out int toStartIndex, out int toLength)
|
||||
private static bool TryGetRangeLength(StringSegment input, ref int current, out int fromLength, out int toStartIndex, out int toLength)
|
||||
{
|
||||
fromLength = 0;
|
||||
toStartIndex = 0;
|
||||
|
|
@ -341,8 +343,8 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
|
||||
private static bool TryCreateContentRange(
|
||||
string input,
|
||||
string unit,
|
||||
StringSegment input,
|
||||
StringSegment unit,
|
||||
int fromStartIndex,
|
||||
int fromLength,
|
||||
int toStartIndex,
|
||||
|
|
@ -354,13 +356,13 @@ namespace Microsoft.Net.Http.Headers
|
|||
parsedValue = null;
|
||||
|
||||
long from = 0;
|
||||
if ((fromLength > 0) && !HeaderUtilities.TryParseNonNegativeInt64(input.Substring(fromStartIndex, fromLength), out from))
|
||||
if ((fromLength > 0) && !HeaderUtilities.TryParseNonNegativeInt64(input.Subsegment(fromStartIndex, fromLength), out from))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
long to = 0;
|
||||
if ((toLength > 0) && !HeaderUtilities.TryParseNonNegativeInt64(input.Substring(toStartIndex, toLength), out to))
|
||||
if ((toLength > 0) && !HeaderUtilities.TryParseNonNegativeInt64(input.Subsegment(toStartIndex, toLength), out to))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -372,7 +374,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
|
||||
long length = 0;
|
||||
if ((lengthLength > 0) && !HeaderUtilities.TryParseNonNegativeInt64(input.Substring(lengthStartIndex, lengthLength),
|
||||
if ((lengthLength > 0) && !HeaderUtilities.TryParseNonNegativeInt64(input.Subsegment(lengthStartIndex, lengthLength),
|
||||
out length))
|
||||
{
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Diagnostics.Contracts;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Net.Http.Headers
|
||||
{
|
||||
|
|
@ -12,7 +13,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
{
|
||||
}
|
||||
|
||||
public sealed override bool TryParseValue(string value, ref int index, out CookieHeaderValue parsedValue)
|
||||
public sealed override bool TryParseValue(StringSegment value, ref int index, out CookieHeaderValue parsedValue)
|
||||
{
|
||||
parsedValue = null;
|
||||
|
||||
|
|
@ -21,7 +22,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
// Accept: text/xml; q=1
|
||||
// Accept:
|
||||
// Accept: text/plain; q=0.2
|
||||
if (string.IsNullOrEmpty(value) || (index == value.Length))
|
||||
if (StringSegment.IsNullOrEmpty(value) || (index == value.Length))
|
||||
{
|
||||
return SupportsMultipleValues;
|
||||
}
|
||||
|
|
@ -62,7 +63,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return true;
|
||||
}
|
||||
|
||||
private static int GetNextNonEmptyOrWhitespaceIndex(string input, int startIndex, bool skipEmptyValues, out bool separatorFound)
|
||||
private static int GetNextNonEmptyOrWhitespaceIndex(StringSegment input, int startIndex, bool skipEmptyValues, out bool separatorFound)
|
||||
{
|
||||
Contract.Requires(input != null);
|
||||
Contract.Requires(startIndex <= input.Length); // it's OK if index == value.Length.
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Net.Http.Headers
|
||||
{
|
||||
|
|
@ -14,16 +15,16 @@ namespace Microsoft.Net.Http.Headers
|
|||
private static readonly CookieHeaderParser SingleValueParser = new CookieHeaderParser(supportsMultipleValues: false);
|
||||
private static readonly CookieHeaderParser MultipleValueParser = new CookieHeaderParser(supportsMultipleValues: true);
|
||||
|
||||
private string _name;
|
||||
private string _value;
|
||||
private StringSegment _name;
|
||||
private StringSegment _value;
|
||||
|
||||
private CookieHeaderValue()
|
||||
{
|
||||
// Used by the parser to create a new instance of this type.
|
||||
}
|
||||
|
||||
public CookieHeaderValue(string name)
|
||||
: this(name, string.Empty)
|
||||
public CookieHeaderValue(StringSegment name)
|
||||
: this(name, StringSegment.Empty)
|
||||
{
|
||||
if (name == null)
|
||||
{
|
||||
|
|
@ -31,7 +32,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
}
|
||||
|
||||
public CookieHeaderValue(string name, string value)
|
||||
public CookieHeaderValue(StringSegment name, StringSegment value)
|
||||
{
|
||||
if (name == null)
|
||||
{
|
||||
|
|
@ -47,7 +48,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
Value = value;
|
||||
}
|
||||
|
||||
public string Name
|
||||
public StringSegment Name
|
||||
{
|
||||
get { return _name; }
|
||||
set
|
||||
|
|
@ -57,7 +58,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
}
|
||||
|
||||
public string Value
|
||||
public StringSegment Value
|
||||
{
|
||||
get { return _value; }
|
||||
set
|
||||
|
|
@ -79,24 +80,13 @@ namespace Microsoft.Net.Http.Headers
|
|||
return header.ToString();
|
||||
}
|
||||
|
||||
private static void AppendSegment(StringBuilder builder, string name, string value)
|
||||
{
|
||||
builder.Append("; ");
|
||||
builder.Append(name);
|
||||
if (value != null)
|
||||
{
|
||||
builder.Append("=");
|
||||
builder.Append(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static CookieHeaderValue Parse(string input)
|
||||
public static CookieHeaderValue Parse(StringSegment input)
|
||||
{
|
||||
var index = 0;
|
||||
return SingleValueParser.ParseValue(input, ref index);
|
||||
}
|
||||
|
||||
public static bool TryParse(string input, out CookieHeaderValue parsedValue)
|
||||
public static bool TryParse(StringSegment input, out CookieHeaderValue parsedValue)
|
||||
{
|
||||
var index = 0;
|
||||
return SingleValueParser.TryParseValue(input, ref index, out parsedValue);
|
||||
|
|
@ -123,13 +113,13 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
|
||||
// name=value; name="value"
|
||||
internal static bool TryGetCookieLength(string input, ref int offset, out CookieHeaderValue parsedValue)
|
||||
internal static bool TryGetCookieLength(StringSegment input, ref int offset, out CookieHeaderValue parsedValue)
|
||||
{
|
||||
Contract.Requires(offset >= 0);
|
||||
|
||||
parsedValue = null;
|
||||
|
||||
if (string.IsNullOrEmpty(input) || (offset >= input.Length))
|
||||
if (StringSegment.IsNullOrEmpty(input) || (offset >= input.Length))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -146,7 +136,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
{
|
||||
return false;
|
||||
}
|
||||
result._name = input.Substring(offset, itemLength);
|
||||
result._name = input.Subsegment(offset, itemLength);
|
||||
offset += itemLength;
|
||||
|
||||
// = (no spaces)
|
||||
|
|
@ -166,7 +156,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
// cookie-value = *cookie-octet / ( DQUOTE* cookie-octet DQUOTE )
|
||||
// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
|
||||
// ; US-ASCII characters excluding CTLs, whitespace DQUOTE, comma, semicolon, and backslash
|
||||
internal static string GetCookieValue(string input, ref int offset)
|
||||
internal static StringSegment GetCookieValue(StringSegment input, ref int offset)
|
||||
{
|
||||
Contract.Requires(input != null);
|
||||
Contract.Requires(offset >= 0);
|
||||
|
|
@ -176,7 +166,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
|
||||
if (offset >= input.Length)
|
||||
{
|
||||
return string.Empty;
|
||||
return StringSegment.Empty;
|
||||
}
|
||||
var inQuotes = false;
|
||||
|
||||
|
|
@ -202,7 +192,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
if (offset == input.Length || input[offset] != '"')
|
||||
{
|
||||
// Missing final quote
|
||||
return string.Empty;
|
||||
return StringSegment.Empty;
|
||||
}
|
||||
offset++;
|
||||
}
|
||||
|
|
@ -210,13 +200,13 @@ namespace Microsoft.Net.Http.Headers
|
|||
int length = offset - startIndex;
|
||||
if (offset > startIndex)
|
||||
{
|
||||
return input.Substring(startIndex, length);
|
||||
return input.Subsegment(startIndex, length);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
return StringSegment.Empty;
|
||||
}
|
||||
|
||||
private static bool ReadEqualsSign(string input, ref int offset)
|
||||
private static bool ReadEqualsSign(StringSegment input, ref int offset)
|
||||
{
|
||||
// = (no spaces)
|
||||
if (offset >= input.Length || input[offset] != '=')
|
||||
|
|
@ -238,7 +228,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return !(c == '"' || c == ',' || c == ';' || c == '\\');
|
||||
}
|
||||
|
||||
internal static void CheckNameFormat(string name, string parameterName)
|
||||
internal static void CheckNameFormat(StringSegment name, string parameterName)
|
||||
{
|
||||
if (name == null)
|
||||
{
|
||||
|
|
@ -251,7 +241,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
}
|
||||
|
||||
internal static void CheckValueFormat(string value, string parameterName)
|
||||
internal static void CheckValueFormat(StringSegment value, string parameterName)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
|
|
@ -275,8 +265,8 @@ namespace Microsoft.Net.Http.Headers
|
|||
return false;
|
||||
}
|
||||
|
||||
return string.Equals(_name, other._name, StringComparison.OrdinalIgnoreCase)
|
||||
&& string.Equals(_value, other._value, StringComparison.OrdinalIgnoreCase);
|
||||
return StringSegment.Equals(_name, other._name, StringComparison.OrdinalIgnoreCase)
|
||||
&& StringSegment.Equals(_value, other._value, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Contracts;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Net.Http.Headers
|
||||
{
|
||||
|
|
@ -22,7 +23,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
|
||||
private static EntityTagHeaderValue AnyType;
|
||||
|
||||
private string _tag;
|
||||
private StringSegment _tag;
|
||||
private bool _isWeak;
|
||||
|
||||
private EntityTagHeaderValue()
|
||||
|
|
@ -30,20 +31,20 @@ namespace Microsoft.Net.Http.Headers
|
|||
// Used by the parser to create a new instance of this type.
|
||||
}
|
||||
|
||||
public EntityTagHeaderValue(string tag)
|
||||
public EntityTagHeaderValue(StringSegment tag)
|
||||
: this(tag, false)
|
||||
{
|
||||
}
|
||||
|
||||
public EntityTagHeaderValue(string tag, bool isWeak)
|
||||
public EntityTagHeaderValue(StringSegment tag, bool isWeak)
|
||||
{
|
||||
if (string.IsNullOrEmpty(tag))
|
||||
if (StringSegment.IsNullOrEmpty(tag))
|
||||
{
|
||||
throw new ArgumentException("An empty string is not allowed.", nameof(tag));
|
||||
}
|
||||
|
||||
int length = 0;
|
||||
if (!isWeak && string.Equals(tag, "*", StringComparison.Ordinal))
|
||||
if (!isWeak && StringSegment.Equals(tag, "*", StringComparison.Ordinal))
|
||||
{
|
||||
// * is valid, but W/* isn't.
|
||||
_tag = tag;
|
||||
|
|
@ -74,7 +75,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
}
|
||||
|
||||
public string Tag
|
||||
public StringSegment Tag
|
||||
{
|
||||
get { return _tag; }
|
||||
}
|
||||
|
|
@ -88,9 +89,9 @@ namespace Microsoft.Net.Http.Headers
|
|||
{
|
||||
if (_isWeak)
|
||||
{
|
||||
return "W/" + _tag;
|
||||
return "W/" + _tag.ToString();
|
||||
}
|
||||
return _tag;
|
||||
return _tag.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -112,7 +113,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
|
||||
// Since the tag is a quoted-string we treat it case-sensitive.
|
||||
return _isWeak == other._isWeak && string.Equals(_tag, other._tag, StringComparison.Ordinal);
|
||||
return _isWeak == other._isWeak && StringSegment.Equals(_tag, other._tag, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
|
|
@ -139,21 +140,21 @@ namespace Microsoft.Net.Http.Headers
|
|||
|
||||
if (useStrongComparison)
|
||||
{
|
||||
return !IsWeak && !other.IsWeak && string.Equals(Tag, other.Tag, StringComparison.Ordinal);
|
||||
return !IsWeak && !other.IsWeak && StringSegment.Equals(Tag, other.Tag, StringComparison.Ordinal);
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Equals(Tag, other.Tag, StringComparison.Ordinal);
|
||||
return StringSegment.Equals(Tag, other.Tag, StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
public static EntityTagHeaderValue Parse(string input)
|
||||
public static EntityTagHeaderValue Parse(StringSegment input)
|
||||
{
|
||||
var index = 0;
|
||||
return SingleValueParser.ParseValue(input, ref index);
|
||||
}
|
||||
|
||||
public static bool TryParse(string input, out EntityTagHeaderValue parsedValue)
|
||||
public static bool TryParse(StringSegment input, out EntityTagHeaderValue parsedValue)
|
||||
{
|
||||
var index = 0;
|
||||
return SingleValueParser.TryParseValue(input, ref index, out parsedValue);
|
||||
|
|
@ -179,13 +180,13 @@ namespace Microsoft.Net.Http.Headers
|
|||
return MultipleValueParser.TryParseStrictValues(inputs, out parsedValues);
|
||||
}
|
||||
|
||||
internal static int GetEntityTagLength(string input, int startIndex, out EntityTagHeaderValue parsedValue)
|
||||
internal static int GetEntityTagLength(StringSegment input, int startIndex, out EntityTagHeaderValue parsedValue)
|
||||
{
|
||||
Contract.Requires(startIndex >= 0);
|
||||
|
||||
parsedValue = null;
|
||||
|
||||
if (string.IsNullOrEmpty(input) || (startIndex >= input.Length))
|
||||
if (StringSegment.IsNullOrEmpty(input) || (startIndex >= input.Length))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -235,7 +236,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
else
|
||||
{
|
||||
parsedValue._tag = input.Substring(tagStartIndex, tagLength);
|
||||
parsedValue._tag = input.Subsegment(tagStartIndex, tagLength);
|
||||
parsedValue._isWeak = isWeak;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Net.Http.Headers
|
||||
{
|
||||
internal sealed class GenericHeaderParser<T> : BaseHeaderParser<T>
|
||||
{
|
||||
internal delegate int GetParsedValueLengthDelegate(string value, int startIndex, out T parsedValue);
|
||||
internal delegate int GetParsedValueLengthDelegate(StringSegment value, int startIndex, out T parsedValue);
|
||||
|
||||
private GetParsedValueLengthDelegate _getParsedValueLength;
|
||||
|
||||
|
|
@ -22,7 +23,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
_getParsedValueLength = getParsedValueLength;
|
||||
}
|
||||
|
||||
protected override int GetParsedValueLength(string value, int startIndex, out T parsedValue)
|
||||
protected override int GetParsedValueLength(StringSegment value, int startIndex, out T parsedValue)
|
||||
{
|
||||
return _getParsedValueLength(value, startIndex, out parsedValue);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,9 +72,9 @@ namespace Microsoft.Net.Http.Headers
|
|||
return null;
|
||||
}
|
||||
|
||||
internal static void CheckValidToken(string value, string parameterName)
|
||||
internal static void CheckValidToken(StringSegment value, string parameterName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
if (StringSegment.IsNullOrEmpty(value))
|
||||
{
|
||||
throw new ArgumentException("An empty string is not allowed.", parameterName);
|
||||
}
|
||||
|
|
@ -85,21 +85,6 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
}
|
||||
|
||||
internal static void CheckValidQuotedString(string value, string parameterName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
throw new ArgumentException("An empty string is not allowed.", parameterName);
|
||||
}
|
||||
|
||||
int length;
|
||||
if ((HttpRuleParser.GetQuotedStringLength(value, 0, out length) != HttpParseResult.Parsed) ||
|
||||
(length != value.Length)) // no trailing spaces allowed
|
||||
{
|
||||
throw new FormatException(string.Format(CultureInfo.InvariantCulture, "Invalid quoted string '{0}'.", value));
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool AreEqualCollections<T>(ICollection<T> x, ICollection<T> y)
|
||||
{
|
||||
return AreEqualCollections(x, y, null);
|
||||
|
|
@ -167,7 +152,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
|
||||
internal static int GetNextNonEmptyOrWhitespaceIndex(
|
||||
string input,
|
||||
StringSegment input,
|
||||
int startIndex,
|
||||
bool skipEmptyValues,
|
||||
out bool separatorFound)
|
||||
|
|
@ -368,11 +353,6 @@ namespace Microsoft.Net.Http.Headers
|
|||
return false;
|
||||
}
|
||||
|
||||
internal static bool TryParseNonNegativeInt32(string value, out int result)
|
||||
{
|
||||
return TryParseNonNegativeInt32(new StringSegment(value), out result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to convert a string representation of a positive number to its 64-bit signed integer equivalent.
|
||||
/// A return value indicates whether the conversion succeeded or failed.
|
||||
|
|
@ -388,12 +368,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
/// result will be overwritten.
|
||||
/// </param>
|
||||
/// <returns><code>true</code> if parsing succeeded; otherwise, <code>false</code>.</returns>
|
||||
public static bool TryParseNonNegativeInt64(string value, out long result)
|
||||
{
|
||||
return TryParseNonNegativeInt64(new StringSegment(value), out result);
|
||||
}
|
||||
|
||||
internal static unsafe bool TryParseNonNegativeInt32(StringSegment value, out int result)
|
||||
public static unsafe bool TryParseNonNegativeInt32(StringSegment value, out int result)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value.Buffer) || value.Length == 0)
|
||||
{
|
||||
|
|
@ -483,7 +458,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
// Strict and fast RFC7231 5.3.1 Quality value parser (and without memory allocation)
|
||||
// See https://tools.ietf.org/html/rfc7231#section-5.3.1
|
||||
// Check is made to verify if the value is between 0 and 1 (and it returns False if the check fails).
|
||||
internal static bool TryParseQualityDouble(string input, int startIndex, out double quality, out int length)
|
||||
internal static bool TryParseQualityDouble(StringSegment input, int startIndex, out double quality, out int length)
|
||||
{
|
||||
quality = 0;
|
||||
length = 0;
|
||||
|
|
@ -602,7 +577,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return new string(charBuffer, position, _int64MaxStringLength - position);
|
||||
}
|
||||
|
||||
public static bool TryParseDate(string input, out DateTimeOffset result)
|
||||
public static bool TryParseDate(StringSegment input, out DateTimeOffset result)
|
||||
{
|
||||
return HttpRuleParser.TryStringToDate(input, out result);
|
||||
}
|
||||
|
|
@ -617,11 +592,11 @@ namespace Microsoft.Net.Http.Headers
|
|||
return dateTime.ToRfc1123String(quoted);
|
||||
}
|
||||
|
||||
public static string RemoveQuotes(string input)
|
||||
public static StringSegment RemoveQuotes(StringSegment input)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(input) && input.Length >= 2 && input[0] == '"' && input[input.Length - 1] == '"')
|
||||
if (!StringSegment.IsNullOrEmpty(input) && input.Length >= 2 && input[0] == '"' && input[input.Length - 1] == '"')
|
||||
{
|
||||
input = input.Substring(1, input.Length - 2);
|
||||
input = input.Subsegment(1, input.Length - 2);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Globalization;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Net.Http.Headers
|
||||
{
|
||||
|
|
@ -26,9 +27,9 @@ namespace Microsoft.Net.Http.Headers
|
|||
// pointing to the next non-whitespace character after a delimiter. E.g. if called with a start index of 0
|
||||
// for string "value , second_value", then after the call completes, 'index' must point to 's', i.e. the first
|
||||
// non-whitespace after the separator ','.
|
||||
public abstract bool TryParseValue(string value, ref int index, out T parsedValue);
|
||||
public abstract bool TryParseValue(StringSegment value, ref int index, out T parsedValue);
|
||||
|
||||
public T ParseValue(string value, ref int index)
|
||||
public T ParseValue(StringSegment value, ref int index)
|
||||
{
|
||||
// Index may be value.Length (e.g. both 0). This may be allowed for some headers (e.g. Accept but not
|
||||
// allowed by others (e.g. Content-Length). The parser has to decide if this is valid or not.
|
||||
|
|
@ -40,7 +41,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
if (!TryParseValue(value, ref index, out result))
|
||||
{
|
||||
throw new FormatException(string.Format(CultureInfo.InvariantCulture,
|
||||
"The header contains invalid values at index {0}: '{1}'", index, value ?? "<null>"));
|
||||
"The header contains invalid values at index {0}: '{1}'", index, value.Value ?? "<null>"));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Diagnostics.Contracts;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Net.Http.Headers
|
||||
{
|
||||
|
|
@ -89,7 +90,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
|
||||
[Pure]
|
||||
internal static int GetTokenLength(string input, int startIndex)
|
||||
internal static int GetTokenLength(StringSegment input, int startIndex)
|
||||
{
|
||||
Contract.Requires(input != null);
|
||||
Contract.Ensures((Contract.Result<int>() >= 0) && (Contract.Result<int>() <= (input.Length - startIndex)));
|
||||
|
|
@ -112,7 +113,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return input.Length - startIndex;
|
||||
}
|
||||
|
||||
internal static int GetWhitespaceLength(string input, int startIndex)
|
||||
internal static int GetWhitespaceLength(StringSegment input, int startIndex)
|
||||
{
|
||||
Contract.Requires(input != null);
|
||||
Contract.Ensures((Contract.Result<int>() >= 0) && (Contract.Result<int>() <= (input.Length - startIndex)));
|
||||
|
|
@ -156,7 +157,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return input.Length - startIndex;
|
||||
}
|
||||
|
||||
internal static int GetNumberLength(string input, int startIndex, bool allowDecimal)
|
||||
internal static int GetNumberLength(StringSegment input, int startIndex, bool allowDecimal)
|
||||
{
|
||||
Contract.Requires(input != null);
|
||||
Contract.Requires((startIndex >= 0) && (startIndex < input.Length));
|
||||
|
|
@ -201,7 +202,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return current - startIndex;
|
||||
}
|
||||
|
||||
internal static HttpParseResult GetQuotedStringLength(string input, int startIndex, out int length)
|
||||
internal static HttpParseResult GetQuotedStringLength(StringSegment input, int startIndex, out int length)
|
||||
{
|
||||
var nestedCount = 0;
|
||||
return GetExpressionLength(input, startIndex, '"', '"', false, ref nestedCount, out length);
|
||||
|
|
@ -209,7 +210,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
|
||||
// quoted-pair = "\" CHAR
|
||||
// CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||
internal static HttpParseResult GetQuotedPairLength(string input, int startIndex, out int length)
|
||||
internal static HttpParseResult GetQuotedPairLength(StringSegment input, int startIndex, out int length)
|
||||
{
|
||||
Contract.Requires(input != null);
|
||||
Contract.Requires((startIndex >= 0) && (startIndex < input.Length));
|
||||
|
|
@ -237,8 +238,8 @@ namespace Microsoft.Net.Http.Headers
|
|||
|
||||
// Try the various date formats in the order listed above.
|
||||
// We should accept a wide verity of common formats, but only output RFC 1123 style dates.
|
||||
internal static bool TryStringToDate(string input, out DateTimeOffset result) =>
|
||||
DateTimeOffset.TryParseExact(input, DateFormats, DateTimeFormatInfo.InvariantInfo,
|
||||
internal static bool TryStringToDate(StringSegment input, out DateTimeOffset result) =>
|
||||
DateTimeOffset.TryParseExact(input.ToString(), DateFormats, DateTimeFormatInfo.InvariantInfo,
|
||||
DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeUniversal, out result);
|
||||
|
||||
// TEXT = <any OCTET except CTLs, but including LWS>
|
||||
|
|
@ -253,7 +254,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
// comments, resulting in a stack overflow exception. In addition having more than 1 nested comment (if any)
|
||||
// is unusual.
|
||||
private static HttpParseResult GetExpressionLength(
|
||||
string input,
|
||||
StringSegment input,
|
||||
int startIndex,
|
||||
char openChar,
|
||||
char closeChar,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.Diagnostics.Contracts;
|
|||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Net.Http.Headers
|
||||
{
|
||||
|
|
@ -22,7 +23,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
|
||||
// Use a collection instead of a dictionary since we may have multiple parameters with the same name.
|
||||
private ObjectCollection<NameValueHeaderValue> _parameters;
|
||||
private string _mediaType;
|
||||
private StringSegment _mediaType;
|
||||
private bool _isReadOnly;
|
||||
|
||||
private MediaTypeHeaderValue()
|
||||
|
|
@ -30,23 +31,23 @@ namespace Microsoft.Net.Http.Headers
|
|||
// Used by the parser to create a new instance of this type.
|
||||
}
|
||||
|
||||
public MediaTypeHeaderValue(string mediaType)
|
||||
public MediaTypeHeaderValue(StringSegment mediaType)
|
||||
{
|
||||
CheckMediaTypeFormat(mediaType, "mediaType");
|
||||
_mediaType = mediaType;
|
||||
}
|
||||
|
||||
public MediaTypeHeaderValue(string mediaType, double quality)
|
||||
public MediaTypeHeaderValue(StringSegment mediaType, double quality)
|
||||
: this(mediaType)
|
||||
{
|
||||
Quality = quality;
|
||||
}
|
||||
|
||||
public string Charset
|
||||
public StringSegment Charset
|
||||
{
|
||||
get
|
||||
{
|
||||
return NameValueHeaderValue.Find(_parameters, CharsetString)?.Value;
|
||||
return NameValueHeaderValue.Find(_parameters, CharsetString)?.Value.Value;
|
||||
}
|
||||
set
|
||||
{
|
||||
|
|
@ -54,7 +55,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
// We don't prevent a user from setting whitespace-only charsets. Like we can't prevent a user from
|
||||
// setting a non-existing charset.
|
||||
var charsetParameter = NameValueHeaderValue.Find(_parameters, CharsetString);
|
||||
if (string.IsNullOrEmpty(value))
|
||||
if (StringSegment.IsNullOrEmpty(value))
|
||||
{
|
||||
// Remove charset parameter
|
||||
if (charsetParameter != null)
|
||||
|
|
@ -81,11 +82,11 @@ namespace Microsoft.Net.Http.Headers
|
|||
get
|
||||
{
|
||||
var charset = Charset;
|
||||
if (!string.IsNullOrWhiteSpace(charset))
|
||||
if (!StringSegment.IsNullOrEmpty(charset))
|
||||
{
|
||||
try
|
||||
{
|
||||
return Encoding.GetEncoding(charset);
|
||||
return Encoding.GetEncoding(charset.Value);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
|
|
@ -108,17 +109,17 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
}
|
||||
|
||||
public string Boundary
|
||||
public StringSegment Boundary
|
||||
{
|
||||
get
|
||||
{
|
||||
return NameValueHeaderValue.Find(_parameters, BoundaryString)?.Value;
|
||||
return NameValueHeaderValue.Find(_parameters, BoundaryString)?.Value ?? default(StringSegment);
|
||||
}
|
||||
set
|
||||
{
|
||||
HeaderUtilities.ThrowIfReadOnly(IsReadOnly);
|
||||
var boundaryParameter = NameValueHeaderValue.Find(_parameters, BoundaryString);
|
||||
if (string.IsNullOrEmpty(value))
|
||||
if (StringSegment.IsNullOrEmpty(value))
|
||||
{
|
||||
// Remove charset parameter
|
||||
if (boundaryParameter != null)
|
||||
|
|
@ -169,7 +170,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
}
|
||||
|
||||
public string MediaType
|
||||
public StringSegment MediaType
|
||||
{
|
||||
get { return _mediaType; }
|
||||
set
|
||||
|
|
@ -180,19 +181,19 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
}
|
||||
|
||||
public string Type
|
||||
public StringSegment Type
|
||||
{
|
||||
get
|
||||
{
|
||||
return _mediaType.Substring(0, _mediaType.IndexOf('/'));
|
||||
return _mediaType.Subsegment(0, _mediaType.IndexOf('/'));
|
||||
}
|
||||
}
|
||||
|
||||
public string SubType
|
||||
public StringSegment SubType
|
||||
{
|
||||
get
|
||||
{
|
||||
return _mediaType.Substring(_mediaType.IndexOf('/') + 1);
|
||||
return _mediaType.Subsegment(_mediaType.IndexOf('/') + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -214,7 +215,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
{
|
||||
get
|
||||
{
|
||||
return string.Compare(_mediaType, _mediaType.IndexOf('/') + 1, "*", 0, 1, StringComparison.Ordinal) == 0;
|
||||
return SubType.Equals("*", StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -245,31 +246,15 @@ namespace Microsoft.Net.Http.Headers
|
|||
return false;
|
||||
}
|
||||
|
||||
// PERF: Avoid doing anything here that allocates a substring, this is a very hot path
|
||||
// for content-negotiation.
|
||||
var indexOfSlash = _mediaType.IndexOf('/');
|
||||
|
||||
// "text/plain" is a subset of "text/plain", "text/*" and "*/*". "*/*" is a subset only of "*/*".
|
||||
if (string.Compare(
|
||||
strA: _mediaType,
|
||||
indexA: 0,
|
||||
strB: otherMediaType._mediaType,
|
||||
indexB: 0,
|
||||
length: indexOfSlash,
|
||||
comparisonType: StringComparison.OrdinalIgnoreCase) != 0)
|
||||
if (!Type.Equals(otherMediaType.Type, comparisonType: StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!otherMediaType.MatchesAllTypes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (string.Compare(
|
||||
strA: MediaType,
|
||||
indexA: indexOfSlash + 1,
|
||||
strB: otherMediaType._mediaType,
|
||||
indexB: indexOfSlash + 1, // We know the Type is equal, so the index of '/' is the same in both strings.
|
||||
length: _mediaType.Length - indexOfSlash,
|
||||
comparisonType: StringComparison.OrdinalIgnoreCase) != 0)
|
||||
else if (!SubType.Equals(otherMediaType.SubType, comparisonType: StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!otherMediaType.MatchesAllSubTypes)
|
||||
{
|
||||
|
|
@ -285,7 +270,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
// parameters locally; they make this one more specific.
|
||||
foreach (var parameter in otherMediaType._parameters)
|
||||
{
|
||||
if (string.Equals(parameter.Name, "q", StringComparison.OrdinalIgnoreCase))
|
||||
if (parameter.Name.Equals("q", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// "q" and later parameters are not involved in media type matching. Quoting the RFC: The first
|
||||
// "q" parameter (if any) separates the media-range parameter(s) from the accept-params.
|
||||
|
|
@ -299,7 +284,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!string.Equals(parameter.Value, localParameter.Value, StringComparison.OrdinalIgnoreCase))
|
||||
if (!StringSegment.Equals(parameter.Value, localParameter.Value, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -352,7 +337,10 @@ namespace Microsoft.Net.Http.Headers
|
|||
|
||||
public override string ToString()
|
||||
{
|
||||
return _mediaType + NameValueHeaderValue.ToString(_parameters, ';', true);
|
||||
var builder = new StringBuilder();
|
||||
builder.Append(_mediaType);
|
||||
NameValueHeaderValue.ToString(_parameters, separator: ';', leadingSeparator: true, destination: builder);
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
|
|
@ -364,23 +352,23 @@ namespace Microsoft.Net.Http.Headers
|
|||
return false;
|
||||
}
|
||||
|
||||
return (string.Compare(_mediaType, other._mediaType, StringComparison.OrdinalIgnoreCase) == 0) &&
|
||||
return _mediaType.Equals(other._mediaType, StringComparison.OrdinalIgnoreCase) &&
|
||||
HeaderUtilities.AreEqualCollections(_parameters, other._parameters);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
// The media-type string is case-insensitive.
|
||||
return StringComparer.OrdinalIgnoreCase.GetHashCode(_mediaType) ^ NameValueHeaderValue.GetHashCode(_parameters);
|
||||
return StringSegmentComparer.OrdinalIgnoreCase.GetHashCode(_mediaType) ^ NameValueHeaderValue.GetHashCode(_parameters);
|
||||
}
|
||||
|
||||
public static MediaTypeHeaderValue Parse(string input)
|
||||
public static MediaTypeHeaderValue Parse(StringSegment input)
|
||||
{
|
||||
var index = 0;
|
||||
return SingleValueParser.ParseValue(input, ref index);
|
||||
}
|
||||
|
||||
public static bool TryParse(string input, out MediaTypeHeaderValue parsedValue)
|
||||
public static bool TryParse(StringSegment input, out MediaTypeHeaderValue parsedValue)
|
||||
{
|
||||
var index = 0;
|
||||
return SingleValueParser.TryParseValue(input, ref index, out parsedValue);
|
||||
|
|
@ -406,20 +394,19 @@ namespace Microsoft.Net.Http.Headers
|
|||
return MultipleValueParser.TryParseStrictValues(inputs, out parsedValues);
|
||||
}
|
||||
|
||||
private static int GetMediaTypeLength(string input, int startIndex, out MediaTypeHeaderValue parsedValue)
|
||||
private static int GetMediaTypeLength(StringSegment input, int startIndex, out MediaTypeHeaderValue parsedValue)
|
||||
{
|
||||
Contract.Requires(startIndex >= 0);
|
||||
|
||||
parsedValue = null;
|
||||
|
||||
if (string.IsNullOrEmpty(input) || (startIndex >= input.Length))
|
||||
if (StringSegment.IsNullOrEmpty(input) || (startIndex >= input.Length))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Caller must remove leading whitespace. If not, we'll return 0.
|
||||
string mediaType = null;
|
||||
var mediaTypeLength = MediaTypeHeaderValue.GetMediaTypeExpressionLength(input, startIndex, out mediaType);
|
||||
var mediaTypeLength = MediaTypeHeaderValue.GetMediaTypeExpressionLength(input, startIndex, out var mediaType);
|
||||
|
||||
if (mediaTypeLength == 0)
|
||||
{
|
||||
|
|
@ -451,7 +438,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return current - startIndex;
|
||||
}
|
||||
|
||||
private static int GetMediaTypeExpressionLength(string input, int startIndex, out string mediaType)
|
||||
private static int GetMediaTypeExpressionLength(StringSegment input, int startIndex, out StringSegment mediaType)
|
||||
{
|
||||
Contract.Requires((input != null) && (input.Length > 0) && (startIndex < input.Length));
|
||||
|
||||
|
|
@ -490,7 +477,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
var mediaTypeLength = current + subtypeLength - startIndex;
|
||||
if (typeLength + subtypeLength + 1 == mediaTypeLength)
|
||||
{
|
||||
mediaType = input.Substring(startIndex, mediaTypeLength);
|
||||
mediaType = input.Subsegment(startIndex, mediaTypeLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -500,17 +487,16 @@ namespace Microsoft.Net.Http.Headers
|
|||
return mediaTypeLength;
|
||||
}
|
||||
|
||||
private static void CheckMediaTypeFormat(string mediaType, string parameterName)
|
||||
private static void CheckMediaTypeFormat(StringSegment mediaType, string parameterName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(mediaType))
|
||||
if (StringSegment.IsNullOrEmpty(mediaType))
|
||||
{
|
||||
throw new ArgumentException("An empty string is not allowed.", parameterName);
|
||||
}
|
||||
|
||||
// When adding values using strongly typed objects, no leading/trailing LWS (whitespace) is allowed.
|
||||
// Also no LWS between type and subtype is allowed.
|
||||
string tempMediaType;
|
||||
var mediaTypeLength = GetMediaTypeExpressionLength(mediaType, 0, out tempMediaType);
|
||||
var mediaTypeLength = GetMediaTypeExpressionLength(mediaType, 0, out var tempMediaType);
|
||||
if ((mediaTypeLength == 0) || (tempMediaType.Length != mediaType.Length))
|
||||
{
|
||||
throw new FormatException(string.Format(CultureInfo.InvariantCulture, "Invalid media type '{0}'.", mediaType));
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Net.Http.Headers
|
||||
{
|
||||
|
|
@ -18,8 +19,8 @@ namespace Microsoft.Net.Http.Headers
|
|||
internal static readonly HttpHeaderParser<NameValueHeaderValue> MultipleValueParser
|
||||
= new GenericHeaderParser<NameValueHeaderValue>(true, GetNameValueLength);
|
||||
|
||||
private string _name;
|
||||
private string _value;
|
||||
private StringSegment _name;
|
||||
private StringSegment _value;
|
||||
private bool _isReadOnly;
|
||||
|
||||
private NameValueHeaderValue()
|
||||
|
|
@ -27,12 +28,12 @@ namespace Microsoft.Net.Http.Headers
|
|||
// Used by the parser to create a new instance of this type.
|
||||
}
|
||||
|
||||
public NameValueHeaderValue(string name)
|
||||
public NameValueHeaderValue(StringSegment name)
|
||||
: this(name, null)
|
||||
{
|
||||
}
|
||||
|
||||
public NameValueHeaderValue(string name, string value)
|
||||
public NameValueHeaderValue(StringSegment name, StringSegment value)
|
||||
{
|
||||
CheckNameValueFormat(name, value);
|
||||
|
||||
|
|
@ -40,12 +41,12 @@ namespace Microsoft.Net.Http.Headers
|
|||
_value = value;
|
||||
}
|
||||
|
||||
public string Name
|
||||
public StringSegment Name
|
||||
{
|
||||
get { return _name; }
|
||||
}
|
||||
|
||||
public string Value
|
||||
public StringSegment Value
|
||||
{
|
||||
get { return _value; }
|
||||
set
|
||||
|
|
@ -90,9 +91,9 @@ namespace Microsoft.Net.Http.Headers
|
|||
{
|
||||
Contract.Assert(_name != null);
|
||||
|
||||
var nameHashCode = StringComparer.OrdinalIgnoreCase.GetHashCode(_name);
|
||||
var nameHashCode = StringSegmentComparer.OrdinalIgnoreCase.GetHashCode(_name);
|
||||
|
||||
if (!string.IsNullOrEmpty(_value))
|
||||
if (!StringSegment.IsNullOrEmpty(_value))
|
||||
{
|
||||
// If we have a quoted-string, then just use the hash code. If we have a token, convert to lowercase
|
||||
// and retrieve the hash code.
|
||||
|
|
@ -101,7 +102,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return nameHashCode ^ _value.GetHashCode();
|
||||
}
|
||||
|
||||
return nameHashCode ^ StringComparer.OrdinalIgnoreCase.GetHashCode(_value);
|
||||
return nameHashCode ^ StringSegmentComparer.OrdinalIgnoreCase.GetHashCode(_value);
|
||||
}
|
||||
|
||||
return nameHashCode;
|
||||
|
|
@ -116,7 +117,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return false;
|
||||
}
|
||||
|
||||
if (string.Compare(_name, other._name, StringComparison.OrdinalIgnoreCase) != 0)
|
||||
if (!_name.Equals(other._name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -125,29 +126,29 @@ namespace Microsoft.Net.Http.Headers
|
|||
// case-sensitive comparison. The RFC doesn't mention how to compare quoted-strings outside the "Expect"
|
||||
// header. We treat all quoted-strings the same: case-sensitive comparison.
|
||||
|
||||
if (string.IsNullOrEmpty(_value))
|
||||
if (StringSegment.IsNullOrEmpty(_value))
|
||||
{
|
||||
return string.IsNullOrEmpty(other._value);
|
||||
return StringSegment.IsNullOrEmpty(other._value);
|
||||
}
|
||||
|
||||
if (_value[0] == '"')
|
||||
{
|
||||
// We have a quoted string, so we need to do case-sensitive comparison.
|
||||
return (string.CompareOrdinal(_value, other._value) == 0);
|
||||
return (_value.Equals(other._value, StringComparison.Ordinal));
|
||||
}
|
||||
else
|
||||
{
|
||||
return (string.Compare(_value, other._value, StringComparison.OrdinalIgnoreCase) == 0);
|
||||
return (_value.Equals(other._value, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
|
||||
public static NameValueHeaderValue Parse(string input)
|
||||
public static NameValueHeaderValue Parse(StringSegment input)
|
||||
{
|
||||
var index = 0;
|
||||
return SingleValueParser.ParseValue(input, ref index);
|
||||
}
|
||||
|
||||
public static bool TryParse(string input, out NameValueHeaderValue parsedValue)
|
||||
public static bool TryParse(StringSegment input, out NameValueHeaderValue parsedValue)
|
||||
{
|
||||
var index = 0;
|
||||
return SingleValueParser.TryParseValue(input, ref index, out parsedValue);
|
||||
|
|
@ -175,11 +176,11 @@ namespace Microsoft.Net.Http.Headers
|
|||
|
||||
public override string ToString()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_value))
|
||||
if (!StringSegment.IsNullOrEmpty(_value))
|
||||
{
|
||||
return _name + "=" + _value;
|
||||
}
|
||||
return _name;
|
||||
return _name.ToString();
|
||||
}
|
||||
|
||||
internal static void ToString(
|
||||
|
|
@ -202,7 +203,12 @@ namespace Microsoft.Net.Http.Headers
|
|||
destination.Append(separator);
|
||||
destination.Append(' ');
|
||||
}
|
||||
destination.Append(values[i].ToString());
|
||||
destination.Append(values[i].Name);
|
||||
if (!StringSegment.IsNullOrEmpty(values[i].Value))
|
||||
{
|
||||
destination.Append('=');
|
||||
destination.Append(values[i].Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -235,14 +241,14 @@ namespace Microsoft.Net.Http.Headers
|
|||
return result;
|
||||
}
|
||||
|
||||
private static int GetNameValueLength(string input, int startIndex, out NameValueHeaderValue parsedValue)
|
||||
private static int GetNameValueLength(StringSegment input, int startIndex, out NameValueHeaderValue parsedValue)
|
||||
{
|
||||
Contract.Requires(input != null);
|
||||
Contract.Requires(startIndex >= 0);
|
||||
|
||||
parsedValue = null;
|
||||
|
||||
if (string.IsNullOrEmpty(input) || (startIndex >= input.Length))
|
||||
if (StringSegment.IsNullOrEmpty(input) || (startIndex >= input.Length))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -256,7 +262,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return 0;
|
||||
}
|
||||
|
||||
var name = input.Substring(startIndex, nameLength);
|
||||
var name = input.Subsegment(startIndex, nameLength);
|
||||
var current = startIndex + nameLength;
|
||||
current = current + HttpRuleParser.GetWhitespaceLength(input, current);
|
||||
|
||||
|
|
@ -280,7 +286,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
// Use parameterless ctor to avoid double-parsing of name and value, i.e. skip public ctor validation.
|
||||
parsedValue = new NameValueHeaderValue();
|
||||
parsedValue._name = name;
|
||||
parsedValue._value = input.Substring(current, valueLength);
|
||||
parsedValue._value = input.Subsegment(current, valueLength);
|
||||
current = current + valueLength;
|
||||
current = current + HttpRuleParser.GetWhitespaceLength(input, current); // skip whitespaces
|
||||
return current - startIndex;
|
||||
|
|
@ -289,7 +295,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
// Returns the length of a name/value list, separated by 'delimiter'. E.g. "a=b, c=d, e=f" adds 3
|
||||
// name/value pairs to 'nameValueCollection' if 'delimiter' equals ','.
|
||||
internal static int GetNameValueListLength(
|
||||
string input,
|
||||
StringSegment input,
|
||||
int startIndex,
|
||||
char delimiter,
|
||||
IList<NameValueHeaderValue> nameValueCollection)
|
||||
|
|
@ -297,7 +303,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
Contract.Requires(nameValueCollection != null);
|
||||
Contract.Requires(startIndex >= 0);
|
||||
|
||||
if ((string.IsNullOrEmpty(input)) || (startIndex >= input.Length))
|
||||
if ((StringSegment.IsNullOrEmpty(input)) || (startIndex >= input.Length))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -330,7 +336,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
}
|
||||
|
||||
public static NameValueHeaderValue Find(IList<NameValueHeaderValue> values, string name)
|
||||
public static NameValueHeaderValue Find(IList<NameValueHeaderValue> values, StringSegment name)
|
||||
{
|
||||
Contract.Requires((name != null) && (name.Length > 0));
|
||||
|
||||
|
|
@ -342,7 +348,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
for (var i = 0; i < values.Count; i++)
|
||||
{
|
||||
var value = values[i];
|
||||
if (string.Compare(value.Name, name, StringComparison.OrdinalIgnoreCase) == 0)
|
||||
if (value.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
|
@ -350,7 +356,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return null;
|
||||
}
|
||||
|
||||
internal static int GetValueLength(string input, int startIndex)
|
||||
internal static int GetValueLength(StringSegment input, int startIndex)
|
||||
{
|
||||
Contract.Requires(input != null);
|
||||
|
||||
|
|
@ -373,16 +379,16 @@ namespace Microsoft.Net.Http.Headers
|
|||
return valueLength;
|
||||
}
|
||||
|
||||
private static void CheckNameValueFormat(string name, string value)
|
||||
private static void CheckNameValueFormat(StringSegment name, StringSegment value)
|
||||
{
|
||||
HeaderUtilities.CheckValidToken(name, nameof(name));
|
||||
CheckValueFormat(value);
|
||||
}
|
||||
|
||||
private static void CheckValueFormat(string value)
|
||||
private static void CheckValueFormat(StringSegment value)
|
||||
{
|
||||
// Either value is null/empty or a valid token/quoted string
|
||||
if (!(string.IsNullOrEmpty(value) || (GetValueLength(value, 0) == value.Length)))
|
||||
if (!(StringSegment.IsNullOrEmpty(value) || (GetValueLength(value, 0) == value.Length)))
|
||||
{
|
||||
throw new FormatException(string.Format(System.Globalization.CultureInfo.InvariantCulture, "The header value is invalid: '{0}'", value));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
// type to throw if 'null' gets added. Collection<T> internally uses List<T> which comes at some cost. In addition
|
||||
// Collection<T>.Add() calls List<T>.InsertItem() which is an O(n) operation (compared to O(1) for List<T>.Add()).
|
||||
// This type is only used for very small collections (1-2 items) to keep the impact of using Collection<T> small.
|
||||
internal class ObjectCollection<T> : Collection<T> where T : class
|
||||
internal class ObjectCollection<T> : Collection<T>
|
||||
{
|
||||
internal static readonly Action<T> DefaultValidator = CheckNotNull;
|
||||
internal static readonly ObjectCollection<T> EmptyReadOnlyCollection
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Net.Http.Headers
|
||||
{
|
||||
|
|
@ -85,26 +86,26 @@ namespace Microsoft.Net.Http.Headers
|
|||
return _entityTag.GetHashCode();
|
||||
}
|
||||
|
||||
public static RangeConditionHeaderValue Parse(string input)
|
||||
public static RangeConditionHeaderValue Parse(StringSegment input)
|
||||
{
|
||||
var index = 0;
|
||||
return Parser.ParseValue(input, ref index);
|
||||
}
|
||||
|
||||
public static bool TryParse(string input, out RangeConditionHeaderValue parsedValue)
|
||||
public static bool TryParse(StringSegment input, out RangeConditionHeaderValue parsedValue)
|
||||
{
|
||||
var index = 0;
|
||||
return Parser.TryParseValue(input, ref index, out parsedValue);
|
||||
}
|
||||
|
||||
private static int GetRangeConditionLength(string input, int startIndex, out RangeConditionHeaderValue parsedValue)
|
||||
private static int GetRangeConditionLength(StringSegment input, int startIndex, out RangeConditionHeaderValue parsedValue)
|
||||
{
|
||||
Contract.Requires(startIndex >= 0);
|
||||
|
||||
parsedValue = null;
|
||||
|
||||
// Make sure we have at least 2 characters
|
||||
if (string.IsNullOrEmpty(input) || (startIndex + 1 >= input.Length))
|
||||
if (StringSegment.IsNullOrEmpty(input) || (startIndex + 1 >= input.Length))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -141,7 +142,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
else
|
||||
{
|
||||
if (!HttpRuleParser.TryStringToDate(input.Substring(current), out date))
|
||||
if (!HttpRuleParser.TryStringToDate(input.Subsegment(current), out date))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Net.Http.Headers
|
||||
{
|
||||
|
|
@ -13,7 +14,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
private static readonly HttpHeaderParser<RangeHeaderValue> Parser
|
||||
= new GenericHeaderParser<RangeHeaderValue>(false, GetRangeLength);
|
||||
|
||||
private string _unit;
|
||||
private StringSegment _unit;
|
||||
private ICollection<RangeItemHeaderValue> _ranges;
|
||||
|
||||
public RangeHeaderValue()
|
||||
|
|
@ -28,7 +29,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
Ranges.Add(new RangeItemHeaderValue(from, to));
|
||||
}
|
||||
|
||||
public string Unit
|
||||
public StringSegment Unit
|
||||
{
|
||||
get { return _unit; }
|
||||
set
|
||||
|
|
@ -52,7 +53,8 @@ namespace Microsoft.Net.Http.Headers
|
|||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder(_unit);
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(_unit);
|
||||
sb.Append('=');
|
||||
|
||||
var first = true;
|
||||
|
|
@ -84,13 +86,13 @@ namespace Microsoft.Net.Http.Headers
|
|||
return false;
|
||||
}
|
||||
|
||||
return (string.Compare(_unit, other._unit, StringComparison.OrdinalIgnoreCase) == 0) &&
|
||||
return StringSegment.Equals(_unit, other._unit, StringComparison.OrdinalIgnoreCase) &&
|
||||
HeaderUtilities.AreEqualCollections(Ranges, other.Ranges);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var result = StringComparer.OrdinalIgnoreCase.GetHashCode(_unit);
|
||||
var result = StringSegmentComparer.OrdinalIgnoreCase.GetHashCode(_unit);
|
||||
|
||||
foreach (var range in Ranges)
|
||||
{
|
||||
|
|
@ -100,25 +102,25 @@ namespace Microsoft.Net.Http.Headers
|
|||
return result;
|
||||
}
|
||||
|
||||
public static RangeHeaderValue Parse(string input)
|
||||
public static RangeHeaderValue Parse(StringSegment input)
|
||||
{
|
||||
var index = 0;
|
||||
return Parser.ParseValue(input, ref index);
|
||||
}
|
||||
|
||||
public static bool TryParse(string input, out RangeHeaderValue parsedValue)
|
||||
public static bool TryParse(StringSegment input, out RangeHeaderValue parsedValue)
|
||||
{
|
||||
var index = 0;
|
||||
return Parser.TryParseValue(input, ref index, out parsedValue);
|
||||
}
|
||||
|
||||
private static int GetRangeLength(string input, int startIndex, out RangeHeaderValue parsedValue)
|
||||
private static int GetRangeLength(StringSegment input, int startIndex, out RangeHeaderValue parsedValue)
|
||||
{
|
||||
Contract.Requires(startIndex >= 0);
|
||||
|
||||
parsedValue = null;
|
||||
|
||||
if (string.IsNullOrEmpty(input) || (startIndex >= input.Length))
|
||||
if (StringSegment.IsNullOrEmpty(input) || (startIndex >= input.Length))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -132,7 +134,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
|
||||
RangeHeaderValue result = new RangeHeaderValue();
|
||||
result._unit = input.Substring(startIndex, unitLength);
|
||||
result._unit = input.Subsegment(startIndex, unitLength);
|
||||
var current = startIndex + unitLength;
|
||||
current = current + HttpRuleParser.GetWhitespaceLength(input, current);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Globalization;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Net.Http.Headers
|
||||
{
|
||||
|
|
@ -87,7 +88,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
// Returns the length of a range list. E.g. "1-2, 3-4, 5-6" adds 3 ranges to 'rangeCollection'. Note that empty
|
||||
// list segments are allowed, e.g. ",1-2, , 3-4,,".
|
||||
internal static int GetRangeItemListLength(
|
||||
string input,
|
||||
StringSegment input,
|
||||
int startIndex,
|
||||
ICollection<RangeItemHeaderValue> rangeCollection)
|
||||
{
|
||||
|
|
@ -96,7 +97,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
Contract.Ensures((Contract.Result<int>() == 0) || (rangeCollection.Count > 0),
|
||||
"If we can parse the string, then we expect to have at least one range item.");
|
||||
|
||||
if ((string.IsNullOrEmpty(input)) || (startIndex >= input.Length))
|
||||
if ((StringSegment.IsNullOrEmpty(input)) || (startIndex >= input.Length))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -140,7 +141,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
}
|
||||
|
||||
internal static int GetRangeItemLength(string input, int startIndex, out RangeItemHeaderValue parsedValue)
|
||||
internal static int GetRangeItemLength(StringSegment input, int startIndex, out RangeItemHeaderValue parsedValue)
|
||||
{
|
||||
Contract.Requires(startIndex >= 0);
|
||||
|
||||
|
|
@ -148,7 +149,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
|
||||
parsedValue = null;
|
||||
|
||||
if (string.IsNullOrEmpty(input) || (startIndex >= input.Length))
|
||||
if (StringSegment.IsNullOrEmpty(input) || (startIndex >= input.Length))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -202,14 +203,14 @@ namespace Microsoft.Net.Http.Headers
|
|||
|
||||
// Try convert first value to int64
|
||||
long from = 0;
|
||||
if ((fromLength > 0) && !HeaderUtilities.TryParseNonNegativeInt64(input.Substring(fromStartIndex, fromLength), out from))
|
||||
if ((fromLength > 0) && !HeaderUtilities.TryParseNonNegativeInt64(input.Subsegment(fromStartIndex, fromLength), out from))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Try convert second value to int64
|
||||
long to = 0;
|
||||
if ((toLength > 0) && !HeaderUtilities.TryParseNonNegativeInt64(input.Substring(toStartIndex, toLength), out to))
|
||||
if ((toLength > 0) && !HeaderUtilities.TryParseNonNegativeInt64(input.Subsegment(toStartIndex, toLength), out to))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,20 +31,20 @@ namespace Microsoft.Net.Http.Headers
|
|||
private static readonly HttpHeaderParser<SetCookieHeaderValue> MultipleValueParser
|
||||
= new GenericHeaderParser<SetCookieHeaderValue>(true, GetSetCookieLength);
|
||||
|
||||
private string _name;
|
||||
private string _value;
|
||||
private StringSegment _name;
|
||||
private StringSegment _value;
|
||||
|
||||
private SetCookieHeaderValue()
|
||||
{
|
||||
// Used by the parser to create a new instance of this type.
|
||||
}
|
||||
|
||||
public SetCookieHeaderValue(string name)
|
||||
: this(name, string.Empty)
|
||||
public SetCookieHeaderValue(StringSegment name)
|
||||
: this(name, StringSegment.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
public SetCookieHeaderValue(string name, string value)
|
||||
public SetCookieHeaderValue(StringSegment name, StringSegment value)
|
||||
{
|
||||
if (name == null)
|
||||
{
|
||||
|
|
@ -60,7 +60,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
Value = value;
|
||||
}
|
||||
|
||||
public string Name
|
||||
public StringSegment Name
|
||||
{
|
||||
get { return _name; }
|
||||
set
|
||||
|
|
@ -70,7 +70,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
}
|
||||
|
||||
public string Value
|
||||
public StringSegment Value
|
||||
{
|
||||
get { return _value; }
|
||||
set
|
||||
|
|
@ -84,10 +84,9 @@ namespace Microsoft.Net.Http.Headers
|
|||
|
||||
public TimeSpan? MaxAge { get; set; }
|
||||
|
||||
public string Domain { get; set; }
|
||||
public StringSegment Domain { get; set; }
|
||||
|
||||
// TODO: PathString?
|
||||
public string Path { get; set; }
|
||||
public StringSegment Path { get; set; }
|
||||
|
||||
public bool Secure { get; set; }
|
||||
|
||||
|
|
@ -186,7 +185,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static void AppendSegment(ref InplaceStringBuilder builder, string name, string value)
|
||||
private static void AppendSegment(ref InplaceStringBuilder builder, StringSegment name, StringSegment value)
|
||||
{
|
||||
builder.Append(SeparatorToken);
|
||||
builder.Append(name);
|
||||
|
|
@ -247,7 +246,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
}
|
||||
|
||||
private static void AppendSegment(StringBuilder builder, string name, string value)
|
||||
private static void AppendSegment(StringBuilder builder, StringSegment name, StringSegment value)
|
||||
{
|
||||
builder.Append("; ");
|
||||
builder.Append(name);
|
||||
|
|
@ -258,13 +257,13 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
}
|
||||
|
||||
public static SetCookieHeaderValue Parse(string input)
|
||||
public static SetCookieHeaderValue Parse(StringSegment input)
|
||||
{
|
||||
var index = 0;
|
||||
return SingleValueParser.ParseValue(input, ref index);
|
||||
}
|
||||
|
||||
public static bool TryParse(string input, out SetCookieHeaderValue parsedValue)
|
||||
public static bool TryParse(StringSegment input, out SetCookieHeaderValue parsedValue)
|
||||
{
|
||||
var index = 0;
|
||||
return SingleValueParser.TryParseValue(input, ref index, out parsedValue);
|
||||
|
|
@ -291,14 +290,14 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
|
||||
// name=value; expires=Sun, 06 Nov 1994 08:49:37 GMT; max-age=86400; domain=domain1; path=path1; secure; samesite={Strict|Lax}; httponly
|
||||
private static int GetSetCookieLength(string input, int startIndex, out SetCookieHeaderValue parsedValue)
|
||||
private static int GetSetCookieLength(StringSegment input, int startIndex, out SetCookieHeaderValue parsedValue)
|
||||
{
|
||||
Contract.Requires(startIndex >= 0);
|
||||
var offset = startIndex;
|
||||
|
||||
parsedValue = null;
|
||||
|
||||
if (string.IsNullOrEmpty(input) || (offset >= input.Length))
|
||||
if (StringSegment.IsNullOrEmpty(input) || (offset >= input.Length))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -315,7 +314,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
result._name = input.Substring(offset, itemLength);
|
||||
result._name = input.Subsegment(offset, itemLength);
|
||||
offset += itemLength;
|
||||
|
||||
// = (no spaces)
|
||||
|
|
@ -352,11 +351,11 @@ namespace Microsoft.Net.Http.Headers
|
|||
// Trailing ';' or leading into garbage. Let the next parser fail.
|
||||
break;
|
||||
}
|
||||
var token = input.Substring(offset, itemLength);
|
||||
var token = input.Subsegment(offset, itemLength);
|
||||
offset += itemLength;
|
||||
|
||||
// expires-av = "Expires=" sane-cookie-date
|
||||
if (string.Equals(token, ExpiresToken, StringComparison.OrdinalIgnoreCase))
|
||||
if (StringSegment.Equals(token, ExpiresToken, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// = (no spaces)
|
||||
if (!ReadEqualsSign(input, ref offset))
|
||||
|
|
@ -373,7 +372,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
result.Expires = expirationDate;
|
||||
}
|
||||
// max-age-av = "Max-Age=" non-zero-digit *DIGIT
|
||||
else if (string.Equals(token, MaxAgeToken, StringComparison.OrdinalIgnoreCase))
|
||||
else if (StringSegment.Equals(token, MaxAgeToken, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// = (no spaces)
|
||||
if (!ReadEqualsSign(input, ref offset))
|
||||
|
|
@ -386,7 +385,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
var numberString = input.Substring(offset, itemLength);
|
||||
var numberString = input.Subsegment(offset, itemLength);
|
||||
long maxAge;
|
||||
if (!HeaderUtilities.TryParseNonNegativeInt64(numberString, out maxAge))
|
||||
{
|
||||
|
|
@ -398,7 +397,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
// domain-av = "Domain=" domain-value
|
||||
// domain-value = <subdomain> ; defined in [RFC1034], Section 3.5, as enhanced by [RFC1123], Section 2.1
|
||||
else if (string.Equals(token, DomainToken, StringComparison.OrdinalIgnoreCase))
|
||||
else if (StringSegment.Equals(token, DomainToken, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// = (no spaces)
|
||||
if (!ReadEqualsSign(input, ref offset))
|
||||
|
|
@ -410,7 +409,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
// path-av = "Path=" path-value
|
||||
// path-value = <any CHAR except CTLs or ";">
|
||||
else if (string.Equals(token, PathToken, StringComparison.OrdinalIgnoreCase))
|
||||
else if (StringSegment.Equals(token, PathToken, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// = (no spaces)
|
||||
if (!ReadEqualsSign(input, ref offset))
|
||||
|
|
@ -421,13 +420,13 @@ namespace Microsoft.Net.Http.Headers
|
|||
result.Path = ReadToSemicolonOrEnd(input, ref offset);
|
||||
}
|
||||
// secure-av = "Secure"
|
||||
else if (string.Equals(token, SecureToken, StringComparison.OrdinalIgnoreCase))
|
||||
else if (StringSegment.Equals(token, SecureToken, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
result.Secure = true;
|
||||
}
|
||||
// samesite-av = "SameSite" / "SameSite=" samesite-value
|
||||
// samesite-value = "Strict" / "Lax"
|
||||
else if (string.Equals(token, SameSiteToken, StringComparison.OrdinalIgnoreCase))
|
||||
else if (StringSegment.Equals(token, SameSiteToken, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!ReadEqualsSign(input, ref offset))
|
||||
{
|
||||
|
|
@ -437,7 +436,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
{
|
||||
var enforcementMode = ReadToSemicolonOrEnd(input, ref offset);
|
||||
|
||||
if (string.Equals(enforcementMode, SameSiteLaxToken, StringComparison.OrdinalIgnoreCase))
|
||||
if (StringSegment.Equals(enforcementMode, SameSiteLaxToken, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
result.SameSite = SameSiteMode.Lax;
|
||||
}
|
||||
|
|
@ -448,7 +447,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
}
|
||||
// httponly-av = "HttpOnly"
|
||||
else if (string.Equals(token, HttpOnlyToken, StringComparison.OrdinalIgnoreCase))
|
||||
else if (StringSegment.Equals(token, HttpOnlyToken, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
result.HttpOnly = true;
|
||||
}
|
||||
|
|
@ -463,7 +462,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return offset - startIndex;
|
||||
}
|
||||
|
||||
private static bool ReadEqualsSign(string input, ref int offset)
|
||||
private static bool ReadEqualsSign(StringSegment input, ref int offset)
|
||||
{
|
||||
// = (no spaces)
|
||||
if (offset >= input.Length || input[offset] != '=')
|
||||
|
|
@ -474,7 +473,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return true;
|
||||
}
|
||||
|
||||
private static string ReadToSemicolonOrEnd(string input, ref int offset)
|
||||
private static StringSegment ReadToSemicolonOrEnd(StringSegment input, ref int offset)
|
||||
{
|
||||
var end = input.IndexOf(';', offset);
|
||||
if (end < 0)
|
||||
|
|
@ -483,7 +482,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
end = input.Length;
|
||||
}
|
||||
var itemLength = end - offset;
|
||||
var result = input.Substring(offset, itemLength);
|
||||
var result = input.Subsegment(offset, itemLength);
|
||||
offset += itemLength;
|
||||
return result;
|
||||
}
|
||||
|
|
@ -497,12 +496,12 @@ namespace Microsoft.Net.Http.Headers
|
|||
return false;
|
||||
}
|
||||
|
||||
return string.Equals(_name, other._name, StringComparison.OrdinalIgnoreCase)
|
||||
&& string.Equals(_value, other._value, StringComparison.OrdinalIgnoreCase)
|
||||
return StringSegment.Equals(_name, other._name, StringComparison.OrdinalIgnoreCase)
|
||||
&& StringSegment.Equals(_value, other._value, StringComparison.OrdinalIgnoreCase)
|
||||
&& Expires.Equals(other.Expires)
|
||||
&& MaxAge.Equals(other.MaxAge)
|
||||
&& string.Equals(Domain, other.Domain, StringComparison.OrdinalIgnoreCase)
|
||||
&& string.Equals(Path, other.Path, StringComparison.OrdinalIgnoreCase)
|
||||
&& StringSegment.Equals(Domain, other.Domain, StringComparison.OrdinalIgnoreCase)
|
||||
&& StringSegment.Equals(Path, other.Path, StringComparison.OrdinalIgnoreCase)
|
||||
&& Secure == other.Secure
|
||||
&& SameSite == other.SameSite
|
||||
&& HttpOnly == other.HttpOnly;
|
||||
|
|
@ -510,12 +509,12 @@ namespace Microsoft.Net.Http.Headers
|
|||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return StringComparer.OrdinalIgnoreCase.GetHashCode(_name)
|
||||
^ StringComparer.OrdinalIgnoreCase.GetHashCode(_value)
|
||||
return StringSegmentComparer.OrdinalIgnoreCase.GetHashCode(_name)
|
||||
^ StringSegmentComparer.OrdinalIgnoreCase.GetHashCode(_value)
|
||||
^ (Expires.HasValue ? Expires.GetHashCode() : 0)
|
||||
^ (MaxAge.HasValue ? MaxAge.GetHashCode() : 0)
|
||||
^ (Domain != null ? StringComparer.OrdinalIgnoreCase.GetHashCode(Domain) : 0)
|
||||
^ (Path != null ? StringComparer.OrdinalIgnoreCase.GetHashCode(Path) : 0)
|
||||
^ (Domain != null ? StringSegmentComparer.OrdinalIgnoreCase.GetHashCode(Domain) : 0)
|
||||
^ (Path != null ? StringSegmentComparer.OrdinalIgnoreCase.GetHashCode(Path) : 0)
|
||||
^ Secure.GetHashCode()
|
||||
^ SameSite.GetHashCode()
|
||||
^ HttpOnly.GetHashCode();
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Globalization;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Net.Http.Headers
|
||||
{
|
||||
|
|
@ -15,7 +16,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
private static readonly HttpHeaderParser<StringWithQualityHeaderValue> MultipleValueParser
|
||||
= new GenericHeaderParser<StringWithQualityHeaderValue>(true, GetStringWithQualityLength);
|
||||
|
||||
private string _value;
|
||||
private StringSegment _value;
|
||||
private double? _quality;
|
||||
|
||||
private StringWithQualityHeaderValue()
|
||||
|
|
@ -23,14 +24,14 @@ namespace Microsoft.Net.Http.Headers
|
|||
// Used by the parser to create a new instance of this type.
|
||||
}
|
||||
|
||||
public StringWithQualityHeaderValue(string value)
|
||||
public StringWithQualityHeaderValue(StringSegment value)
|
||||
{
|
||||
HeaderUtilities.CheckValidToken(value, nameof(value));
|
||||
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public StringWithQualityHeaderValue(string value, double quality)
|
||||
public StringWithQualityHeaderValue(StringSegment value, double quality)
|
||||
{
|
||||
HeaderUtilities.CheckValidToken(value, nameof(value));
|
||||
|
||||
|
|
@ -43,7 +44,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
_quality = quality;
|
||||
}
|
||||
|
||||
public string Value
|
||||
public StringSegment Value
|
||||
{
|
||||
get { return _value; }
|
||||
}
|
||||
|
|
@ -60,7 +61,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return _value + "; q=" + _quality.Value.ToString("0.0##", NumberFormatInfo.InvariantInfo);
|
||||
}
|
||||
|
||||
return _value;
|
||||
return _value.ToString();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
|
|
@ -72,7 +73,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return false;
|
||||
}
|
||||
|
||||
if (string.Compare(_value, other._value, StringComparison.OrdinalIgnoreCase) != 0)
|
||||
if (!StringSegment.Equals(_value, other._value, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -92,7 +93,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var result = StringComparer.OrdinalIgnoreCase.GetHashCode(_value);
|
||||
var result = StringSegmentComparer.OrdinalIgnoreCase.GetHashCode(_value);
|
||||
|
||||
if (_quality.HasValue)
|
||||
{
|
||||
|
|
@ -102,13 +103,13 @@ namespace Microsoft.Net.Http.Headers
|
|||
return result;
|
||||
}
|
||||
|
||||
public static StringWithQualityHeaderValue Parse(string input)
|
||||
public static StringWithQualityHeaderValue Parse(StringSegment input)
|
||||
{
|
||||
var index = 0;
|
||||
return SingleValueParser.ParseValue(input, ref index);
|
||||
}
|
||||
|
||||
public static bool TryParse(string input, out StringWithQualityHeaderValue parsedValue)
|
||||
public static bool TryParse(StringSegment input, out StringWithQualityHeaderValue parsedValue)
|
||||
{
|
||||
var index = 0;
|
||||
return SingleValueParser.TryParseValue(input, ref index, out parsedValue);
|
||||
|
|
@ -134,13 +135,13 @@ namespace Microsoft.Net.Http.Headers
|
|||
return MultipleValueParser.TryParseStrictValues(input, out parsedValues);
|
||||
}
|
||||
|
||||
private static int GetStringWithQualityLength(string input, int startIndex, out StringWithQualityHeaderValue parsedValue)
|
||||
private static int GetStringWithQualityLength(StringSegment input, int startIndex, out StringWithQualityHeaderValue parsedValue)
|
||||
{
|
||||
Contract.Requires(startIndex >= 0);
|
||||
|
||||
parsedValue = null;
|
||||
|
||||
if (string.IsNullOrEmpty(input) || (startIndex >= input.Length))
|
||||
if (StringSegment.IsNullOrEmpty(input) || (startIndex >= input.Length))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -154,7 +155,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
}
|
||||
|
||||
StringWithQualityHeaderValue result = new StringWithQualityHeaderValue();
|
||||
result._value = input.Substring(startIndex, valueLength);
|
||||
result._value = input.Subsegment(startIndex, valueLength);
|
||||
var current = startIndex + valueLength;
|
||||
current = current + HttpRuleParser.GetWhitespaceLength(input, current);
|
||||
|
||||
|
|
@ -177,7 +178,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
return current - startIndex;
|
||||
}
|
||||
|
||||
private static bool TryReadQuality(string input, StringWithQualityHeaderValue result, ref int index)
|
||||
private static bool TryReadQuality(StringSegment input, StringWithQualityHeaderValue result, ref int index)
|
||||
{
|
||||
var current = index;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Net.Http.Headers
|
||||
{
|
||||
|
|
@ -64,13 +65,13 @@ namespace Microsoft.Net.Http.Headers
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (!string.Equals(stringWithQuality1.Value, stringWithQuality2.Value, StringComparison.OrdinalIgnoreCase))
|
||||
if (!StringSegment.Equals(stringWithQuality1.Value, stringWithQuality2.Value, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.Equals(stringWithQuality1.Value, "*", StringComparison.Ordinal))
|
||||
if (StringSegment.Equals(stringWithQuality1.Value, "*", StringComparison.Ordinal))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (string.Equals(stringWithQuality2.Value, "*", StringComparison.Ordinal))
|
||||
else if (StringSegment.Equals(stringWithQuality2.Value, "*", StringComparison.Ordinal))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ namespace Microsoft.Net.Http.Headers
|
|||
var contentDisposition = new ContentDispositionHeaderValue("inline");
|
||||
Assert.Equal("inline", contentDisposition.DispositionType);
|
||||
Assert.Equal(0, contentDisposition.Parameters.Count);
|
||||
Assert.Null(contentDisposition.Name);
|
||||
Assert.Null(contentDisposition.FileName);
|
||||
Assert.Null(contentDisposition.Name.Value);
|
||||
Assert.Null(contentDisposition.FileName.Value);
|
||||
Assert.Null(contentDisposition.CreationDate);
|
||||
Assert.Null(contentDisposition.ModificationDate);
|
||||
Assert.Null(contentDisposition.ReadDate);
|
||||
|
|
@ -81,7 +81,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
Assert.Equal("name", contentDisposition.Parameters.First().Name);
|
||||
|
||||
contentDisposition.Name = null;
|
||||
Assert.Null(contentDisposition.Name);
|
||||
Assert.Null(contentDisposition.Name.Value);
|
||||
Assert.Equal(0, contentDisposition.Parameters.Count);
|
||||
contentDisposition.Name = null; // It's OK to set it again to null; no exception.
|
||||
}
|
||||
|
|
@ -103,7 +103,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
Assert.Equal("NAME", contentDisposition.Parameters.First().Name);
|
||||
|
||||
contentDisposition.Parameters.Remove(name);
|
||||
Assert.Null(contentDisposition.Name);
|
||||
Assert.Null(contentDisposition.Name.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -123,7 +123,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
Assert.Equal("FILENAME", contentDisposition.Parameters.First().Name);
|
||||
|
||||
contentDisposition.Parameters.Remove(fileName);
|
||||
Assert.Null(contentDisposition.FileName);
|
||||
Assert.Null(contentDisposition.FileName.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -138,7 +138,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
Assert.Equal("\"=?utf-8?B?RmlsZcODTmFtZS5iYXQ=?=\"", contentDisposition.Parameters.First().Value);
|
||||
|
||||
contentDisposition.Parameters.Remove(contentDisposition.Parameters.First());
|
||||
Assert.Null(contentDisposition.FileName);
|
||||
Assert.Null(contentDisposition.FileName.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -160,7 +160,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
Assert.Equal("FILENAME", contentDisposition.Parameters.First().Name);
|
||||
|
||||
contentDisposition.Parameters.Remove(fileName);
|
||||
Assert.Null(contentDisposition.FileName);
|
||||
Assert.Null(contentDisposition.FileName.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -173,7 +173,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
contentDisposition.Parameters.Add(fileNameStar);
|
||||
Assert.Equal(1, contentDisposition.Parameters.Count);
|
||||
Assert.Equal("FILENAME*", contentDisposition.Parameters.First().Name);
|
||||
Assert.Null(contentDisposition.FileNameStar); // Decode failure
|
||||
Assert.Null(contentDisposition.FileNameStar.Value); // Decode failure
|
||||
|
||||
contentDisposition.FileNameStar = "new_name";
|
||||
Assert.Equal("new_name", contentDisposition.FileNameStar);
|
||||
|
|
@ -182,7 +182,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
Assert.Equal("UTF-8\'\'new_name", contentDisposition.Parameters.First().Value);
|
||||
|
||||
contentDisposition.Parameters.Remove(fileNameStar);
|
||||
Assert.Null(contentDisposition.FileNameStar);
|
||||
Assert.Null(contentDisposition.FileNameStar.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -197,7 +197,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
Assert.Equal("UTF-8\'\'File%C3%83Name.bat", contentDisposition.Parameters.First().Value);
|
||||
|
||||
contentDisposition.Parameters.Remove(contentDisposition.Parameters.First());
|
||||
Assert.Null(contentDisposition.FileNameStar);
|
||||
Assert.Null(contentDisposition.FileNameStar.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -211,7 +211,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
Assert.Equal(1, contentDisposition.Parameters.Count);
|
||||
Assert.Equal("FILENAME*", contentDisposition.Parameters.First().Name);
|
||||
Assert.Equal("utf-99'lang'File%CZName.bat", contentDisposition.Parameters.First().Value);
|
||||
Assert.Null(contentDisposition.FileNameStar); // Decode failure
|
||||
Assert.Null(contentDisposition.FileNameStar.Value); // Decode failure
|
||||
|
||||
contentDisposition.FileNameStar = "new_name";
|
||||
Assert.Equal("new_name", contentDisposition.FileNameStar);
|
||||
|
|
@ -219,7 +219,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
Assert.Equal("FILENAME*", contentDisposition.Parameters.First().Name);
|
||||
|
||||
contentDisposition.Parameters.Remove(fileNameStar);
|
||||
Assert.Null(contentDisposition.FileNameStar);
|
||||
Assert.Null(contentDisposition.FileNameStar.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
var mediaType = new MediaTypeHeaderValue("text/plain");
|
||||
Assert.Equal("text/plain", mediaType.MediaType);
|
||||
Assert.Equal(0, mediaType.Parameters.Count);
|
||||
Assert.Null(mediaType.Charset);
|
||||
Assert.Null(mediaType.Charset.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -70,7 +70,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
var mediaType0 = new MediaTypeHeaderValue("text/plain");
|
||||
var mediaType1 = mediaType0.Copy();
|
||||
Assert.NotSame(mediaType0, mediaType1);
|
||||
Assert.Same(mediaType0.MediaType, mediaType1.MediaType);
|
||||
Assert.Same(mediaType0.MediaType.Value, mediaType1.MediaType.Value);
|
||||
Assert.NotSame(mediaType0.Parameters, mediaType1.Parameters);
|
||||
Assert.Equal(mediaType0.Parameters.Count, mediaType1.Parameters.Count);
|
||||
}
|
||||
|
|
@ -81,7 +81,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
var mediaType0 = new MediaTypeHeaderValue("text/plain");
|
||||
var mediaType1 = mediaType0.CopyAsReadOnly();
|
||||
Assert.NotSame(mediaType0, mediaType1);
|
||||
Assert.Same(mediaType0.MediaType, mediaType1.MediaType);
|
||||
Assert.Same(mediaType0.MediaType.Value, mediaType1.MediaType.Value);
|
||||
Assert.NotSame(mediaType0.Parameters, mediaType1.Parameters);
|
||||
Assert.Equal(mediaType0.Parameters.Count, mediaType1.Parameters.Count);
|
||||
|
||||
|
|
@ -97,14 +97,14 @@ namespace Microsoft.Net.Http.Headers
|
|||
mediaType0.Parameters.Add(new NameValueHeaderValue("name", "value"));
|
||||
var mediaType1 = mediaType0.Copy();
|
||||
Assert.NotSame(mediaType0, mediaType1);
|
||||
Assert.Same(mediaType0.MediaType, mediaType1.MediaType);
|
||||
Assert.Same(mediaType0.MediaType.Value, mediaType1.MediaType.Value);
|
||||
Assert.NotSame(mediaType0.Parameters, mediaType1.Parameters);
|
||||
Assert.Equal(mediaType0.Parameters.Count, mediaType1.Parameters.Count);
|
||||
var pair0 = mediaType0.Parameters.First();
|
||||
var pair1 = mediaType1.Parameters.First();
|
||||
Assert.NotSame(pair0, pair1);
|
||||
Assert.Same(pair0.Name, pair1.Name);
|
||||
Assert.Same(pair0.Value, pair1.Value);
|
||||
Assert.Same(pair0.Name.Value, pair1.Name.Value);
|
||||
Assert.Same(pair0.Value.Value, pair1.Value.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -116,7 +116,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
Assert.NotSame(mediaType0, mediaType1);
|
||||
Assert.False(mediaType0.IsReadOnly);
|
||||
Assert.True(mediaType1.IsReadOnly);
|
||||
Assert.Same(mediaType0.MediaType, mediaType1.MediaType);
|
||||
Assert.Same(mediaType0.MediaType.Value, mediaType1.MediaType.Value);
|
||||
|
||||
Assert.NotSame(mediaType0.Parameters, mediaType1.Parameters);
|
||||
Assert.False(mediaType0.Parameters.IsReadOnly);
|
||||
|
|
@ -131,8 +131,8 @@ namespace Microsoft.Net.Http.Headers
|
|||
Assert.NotSame(pair0, pair1);
|
||||
Assert.False(pair0.IsReadOnly);
|
||||
Assert.True(pair1.IsReadOnly);
|
||||
Assert.Same(pair0.Name, pair1.Name);
|
||||
Assert.Same(pair0.Value, pair1.Value);
|
||||
Assert.Same(pair0.Name.Value, pair1.Name.Value);
|
||||
Assert.Same(pair0.Value.Value, pair1.Value.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -144,7 +144,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
var mediaType2 = mediaType1.Copy();
|
||||
|
||||
Assert.NotSame(mediaType2, mediaType1);
|
||||
Assert.Same(mediaType2.MediaType, mediaType1.MediaType);
|
||||
Assert.Same(mediaType2.MediaType.Value, mediaType1.MediaType.Value);
|
||||
Assert.True(mediaType1.IsReadOnly);
|
||||
Assert.False(mediaType2.IsReadOnly);
|
||||
Assert.NotSame(mediaType2.Parameters, mediaType1.Parameters);
|
||||
|
|
@ -154,8 +154,8 @@ namespace Microsoft.Net.Http.Headers
|
|||
Assert.NotSame(pair2, pair1);
|
||||
Assert.True(pair1.IsReadOnly);
|
||||
Assert.False(pair2.IsReadOnly);
|
||||
Assert.Same(pair2.Name, pair1.Name);
|
||||
Assert.Same(pair2.Value, pair1.Value);
|
||||
Assert.Same(pair2.Name.Value, pair1.Name.Value);
|
||||
Assert.Same(pair2.Value.Value, pair1.Value.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -178,7 +178,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
Assert.Equal("charset", mediaType.Parameters.First().Name);
|
||||
|
||||
mediaType.Charset = null;
|
||||
Assert.Null(mediaType.Charset);
|
||||
Assert.Null(mediaType.Charset.Value);
|
||||
Assert.Equal(0, mediaType.Parameters.Count);
|
||||
mediaType.Charset = null; // It's OK to set it again to null; no exception.
|
||||
}
|
||||
|
|
@ -200,7 +200,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
Assert.Equal("CHARSET", mediaType.Parameters.First().Name);
|
||||
|
||||
mediaType.Parameters.Remove(charset);
|
||||
Assert.Null(mediaType.Charset);
|
||||
Assert.Null(mediaType.Charset.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
|
@ -65,14 +65,14 @@ namespace Microsoft.Net.Http.Headers
|
|||
var pair0 = new NameValueHeaderValue("name");
|
||||
var pair1 = pair0.Copy();
|
||||
Assert.NotSame(pair0, pair1);
|
||||
Assert.Same(pair0.Name, pair1.Name);
|
||||
Assert.Null(pair0.Value);
|
||||
Assert.Null(pair1.Value);
|
||||
Assert.Same(pair0.Name.Value, pair1.Name.Value);
|
||||
Assert.Null(pair0.Value.Value);
|
||||
Assert.Null(pair1.Value.Value);
|
||||
|
||||
// Change one value and verify the other is unchanged.
|
||||
pair0.Value = "othervalue";
|
||||
Assert.Equal("othervalue", pair0.Value);
|
||||
Assert.Null(pair1.Value);
|
||||
Assert.Null(pair1.Value.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -81,16 +81,16 @@ namespace Microsoft.Net.Http.Headers
|
|||
var pair0 = new NameValueHeaderValue("name");
|
||||
var pair1 = pair0.CopyAsReadOnly();
|
||||
Assert.NotSame(pair0, pair1);
|
||||
Assert.Same(pair0.Name, pair1.Name);
|
||||
Assert.Null(pair0.Value);
|
||||
Assert.Null(pair1.Value);
|
||||
Assert.Same(pair0.Name.Value, pair1.Name.Value);
|
||||
Assert.Null(pair0.Value.Value);
|
||||
Assert.Null(pair1.Value.Value);
|
||||
Assert.False(pair0.IsReadOnly);
|
||||
Assert.True(pair1.IsReadOnly);
|
||||
|
||||
// Change one value and verify the other is unchanged.
|
||||
pair0.Value = "othervalue";
|
||||
Assert.Equal("othervalue", pair0.Value);
|
||||
Assert.Null(pair1.Value);
|
||||
Assert.Null(pair1.Value.Value);
|
||||
Assert.Throws<InvalidOperationException>(() => { pair1.Value = "othervalue"; });
|
||||
}
|
||||
|
||||
|
|
@ -100,8 +100,8 @@ namespace Microsoft.Net.Http.Headers
|
|||
var pair0 = new NameValueHeaderValue("name", "value");
|
||||
var pair1 = pair0.Copy();
|
||||
Assert.NotSame(pair0, pair1);
|
||||
Assert.Same(pair0.Name, pair1.Name);
|
||||
Assert.Same(pair0.Value, pair1.Value);
|
||||
Assert.Same(pair0.Name.Value, pair1.Name.Value);
|
||||
Assert.Same(pair0.Value.Value, pair1.Value.Value);
|
||||
|
||||
// Change one value and verify the other is unchanged.
|
||||
pair0.Value = "othervalue";
|
||||
|
|
@ -115,8 +115,8 @@ namespace Microsoft.Net.Http.Headers
|
|||
var pair0 = new NameValueHeaderValue("name", "value");
|
||||
var pair1 = pair0.CopyAsReadOnly();
|
||||
Assert.NotSame(pair0, pair1);
|
||||
Assert.Same(pair0.Name, pair1.Name);
|
||||
Assert.Same(pair0.Value, pair1.Value);
|
||||
Assert.Same(pair0.Name.Value, pair1.Name.Value);
|
||||
Assert.Same(pair0.Value.Value, pair1.Value.Value);
|
||||
Assert.False(pair0.IsReadOnly);
|
||||
Assert.True(pair1.IsReadOnly);
|
||||
|
||||
|
|
@ -134,8 +134,8 @@ namespace Microsoft.Net.Http.Headers
|
|||
var pair1 = pair0.CopyAsReadOnly();
|
||||
var pair2 = pair1.Copy();
|
||||
Assert.NotSame(pair0, pair1);
|
||||
Assert.Same(pair0.Name, pair1.Name);
|
||||
Assert.Same(pair0.Value, pair1.Value);
|
||||
Assert.Same(pair0.Name.Value, pair1.Name.Value);
|
||||
Assert.Same(pair0.Value.Value, pair1.Value.Value);
|
||||
|
||||
// Change one value and verify the other is unchanged.
|
||||
pair2.Value = "othervalue";
|
||||
|
|
|
|||
Loading…
Reference in New Issue