Skip to content
This repository was archived by the owner on Nov 20, 2018. It is now read-only.

HeaderUtilities enhancements #738

Merged
merged 1 commit into from
Dec 9, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Microsoft.AspNetCore.Http.Abstractions/HostString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public int? Port
{
return null;
}

return p;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,18 @@ public static void SetHeaderJoined(IHeaderDictionary headers, string key, String
// Quote items that contain comas and are not already quoted.
private static string QuoteIfNeeded(string value)
{
if (!string.IsNullOrWhiteSpace(value) &&
value.Contains(',') &&
if (!string.IsNullOrWhiteSpace(value) &&
value.Contains(',') &&
(value[0] != '"' || value[value.Length - 1] != '"'))
{
{
return $"\"{value}\"";
}
return value;
}

private static string DeQuote(string value)
{
if (!string.IsNullOrWhiteSpace(value) &&
if (!string.IsNullOrWhiteSpace(value) &&
(value.Length > 1 && value[0] == '"' && value[value.Length - 1] == '"'))
{
value = value.Substring(1, value.Length - 2);
Expand Down
5 changes: 2 additions & 3 deletions src/Microsoft.AspNetCore.Http/Internal/ParsingHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -411,12 +411,11 @@ private static string DeQuote(string value)
throw new ArgumentNullException(nameof(headers));
}

const NumberStyles styles = NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite;
long value;
var rawValue = headers[HeaderNames.ContentLength];
if (rawValue.Count == 1 &&
!string.IsNullOrWhiteSpace(rawValue[0]) &&
long.TryParse(rawValue[0], styles, CultureInfo.InvariantCulture, out value))
HeaderUtilities.TryParseInt64(new StringSegment(rawValue[0]).Trim(), out value))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should do SetContentLength while we're at it, but I don't know if we have any optimized code for this yet. Kestrel has one but it's for hex.

Copy link
Contributor Author

@JunTaoLuo JunTaoLuo Dec 6, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified the FormatInt64 implementation.

As a reference, formatting long num = 1234567890; to "1234567890" for 30000000 iterations:
Old: 2027ms using num.ToString(CultureInfo.InvariantCulture)
New: 1023ms using FormatInt64(num)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{
return value;
}
Expand All @@ -433,7 +432,7 @@ public static void SetContentLength(IHeaderDictionary headers, long? value)

if (value.HasValue)
{
headers[HeaderNames.ContentLength] = value.Value.ToString(CultureInfo.InvariantCulture);
headers[HeaderNames.ContentLength] = HeaderUtilities.FormatInt64(value.Value);
}
else
{
Expand Down
155 changes: 106 additions & 49 deletions src/Microsoft.Net.Http.Headers/CacheControlHeaderValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@ namespace Microsoft.Net.Http.Headers
{
public class CacheControlHeaderValue
{
private const string MaxAgeString = "max-age";
private const string MaxStaleString = "max-stale";
private const string MinFreshString = "min-fresh";
private const string MustRevalidateString = "must-revalidate";
private const string NoCacheString = "no-cache";
private const string NoStoreString = "no-store";
private const string NoTransformString = "no-transform";
private const string OnlyIfCachedString = "only-if-cached";
private const string PrivateString = "private";
private const string ProxyRevalidateString = "proxy-revalidate";
private const string PublicString = "public";
private const string SharedMaxAgeString = "s-maxage";
public static readonly string PublicString = "public";
public static readonly string PrivateString = "private";
public static readonly string MaxAgeString = "max-age";
public static readonly string SharedMaxAgeString = "s-maxage";
public static readonly string NoCacheString = "no-cache";
public static readonly string NoStoreString = "no-store";
public static readonly string MaxStaleString = "max-stale";
public static readonly string MinFreshString = "min-fresh";
public static readonly string NoTransformString = "no-transform";
public static readonly string OnlyIfCachedString = "only-if-cached";
public static readonly string MustRevalidateString = "must-revalidate";
public static readonly string ProxyRevalidateString = "proxy-revalidate";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have we learned nothing from #712!? I actually prefer const as you can see if you read the PR comments, but we should be consistent and use static readonly instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently we were using these values in a switch statement and they need to be const for that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if David will be OK with this because of the switch statement. Like I said, I'm fine with consts given we don't mess up the values, but I would like consistency.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified the switch statement. These strings are now public static readonly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👎 This regressed the code readability and maintainability. These constants aren't worth it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also avoiding a ToLower() which is required since switch on strings are case-sensitive. I'm seeing a one percent performance increase in ResponseCaching with it. Also it's code within a private method, so I'm not too concerned about its readability.


// The Cache-Control header is special: It is a header supporting a list of values, but we represent the list
// as _one_ instance of CacheControlHeaderValue. I.e we set 'SupportsMultipleValues' to 'true' since it is
Expand Down Expand Up @@ -394,63 +394,120 @@ private static bool TrySetCacheControlValues(
CacheControlHeaderValue cc,
List<NameValueHeaderValue> nameValueList)
{
foreach (NameValueHeaderValue nameValue in nameValueList)
for (var i = 0; i < nameValueList.Count; i++)
{
var nameValue = nameValueList[i];
var name = nameValue.Name;
var success = true;
string name = nameValue.Name.ToLowerInvariant();

switch (name)
switch (name.Length)
{
case NoCacheString:
success = TrySetOptionalTokenList(nameValue, ref cc._noCache, ref cc._noCacheHeaders);
break;

case NoStoreString:
success = TrySetTokenOnlyValue(nameValue, ref cc._noStore);
break;

case MaxAgeString:
success = TrySetTimeSpan(nameValue, ref cc._maxAge);
break;

case MaxStaleString:
success = ((nameValue.Value == null) || TrySetTimeSpan(nameValue, ref cc._maxStaleLimit));
if (success)
case 6:
if (string.Equals(PublicString, name, StringComparison.OrdinalIgnoreCase))
{
cc._maxStale = true;
success = TrySetTokenOnlyValue(nameValue, ref cc._public);
}
else
{
goto default;
}
break;

case MinFreshString:
success = TrySetTimeSpan(nameValue, ref cc._minFresh);
break;

case NoTransformString:
success = TrySetTokenOnlyValue(nameValue, ref cc._noTransform);
case 7:
if (string.Equals(MaxAgeString, name, StringComparison.OrdinalIgnoreCase))
{
success = TrySetTimeSpan(nameValue, ref cc._maxAge);
}
else if(string.Equals(PrivateString, name, StringComparison.OrdinalIgnoreCase))
{
success = TrySetOptionalTokenList(nameValue, ref cc._private, ref cc._privateHeaders);
}
else
{
goto default;
}
break;

case OnlyIfCachedString:
success = TrySetTokenOnlyValue(nameValue, ref cc._onlyIfCached);
case 8:
if (string.Equals(NoCacheString, name, StringComparison.OrdinalIgnoreCase))
{
success = TrySetOptionalTokenList(nameValue, ref cc._noCache, ref cc._noCacheHeaders);
}
else if (string.Equals(NoStoreString, name, StringComparison.OrdinalIgnoreCase))
{
success = TrySetTokenOnlyValue(nameValue, ref cc._noStore);
}
else if (string.Equals(SharedMaxAgeString, name, StringComparison.OrdinalIgnoreCase))
{
success = TrySetTimeSpan(nameValue, ref cc._sharedMaxAge);
}
else
{
goto default;
}
break;

case PublicString:
success = TrySetTokenOnlyValue(nameValue, ref cc._public);
case 9:
if (string.Equals(MaxStaleString, name, StringComparison.OrdinalIgnoreCase))
{
success = ((nameValue.Value == null) || TrySetTimeSpan(nameValue, ref cc._maxStaleLimit));
if (success)
{
cc._maxStale = true;
}
}
else if (string.Equals(MinFreshString, name, StringComparison.OrdinalIgnoreCase))
{
success = TrySetTimeSpan(nameValue, ref cc._minFresh);
}
else
{
goto default;
}
break;

case PrivateString:
success = TrySetOptionalTokenList(nameValue, ref cc._private, ref cc._privateHeaders);
case 12:
if (string.Equals(NoTransformString, name, StringComparison.OrdinalIgnoreCase))
{
success = TrySetTokenOnlyValue(nameValue, ref cc._noTransform);
}
else
{
goto default;
}
break;

case MustRevalidateString:
success = TrySetTokenOnlyValue(nameValue, ref cc._mustRevalidate);
case 14:
if (string.Equals(OnlyIfCachedString, name, StringComparison.OrdinalIgnoreCase))
{
success = TrySetTokenOnlyValue(nameValue, ref cc._onlyIfCached);
}
else
{
goto default;
}
break;

case ProxyRevalidateString:
success = TrySetTokenOnlyValue(nameValue, ref cc._proxyRevalidate);
case 15:
if (string.Equals(MustRevalidateString, name, StringComparison.OrdinalIgnoreCase))
{
success = TrySetTokenOnlyValue(nameValue, ref cc._mustRevalidate);
}
else
{
goto default;
}
break;

case SharedMaxAgeString:
success = TrySetTimeSpan(nameValue, ref cc._sharedMaxAge);
case 16:
if (string.Equals(ProxyRevalidateString, name, StringComparison.OrdinalIgnoreCase))
{
success = TrySetTokenOnlyValue(nameValue, ref cc._proxyRevalidate);
}
else
{
goto default;
}
break;

default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,13 @@ public long? Size
get
{
var sizeParameter = NameValueHeaderValue.Find(_parameters, SizeString);
ulong value;
long value;
if (sizeParameter != null)
{
string sizeString = sizeParameter.Value;
if (UInt64.TryParse(sizeString, NumberStyles.Integer, CultureInfo.InvariantCulture, out value))
var sizeString = sizeParameter.Value;
if (HeaderUtilities.TryParseInt64(sizeString, out value))
{
return (long)value;
return value;
}
}
return null;
Expand Down
Loading