diff --git a/src/Http/Headers/src/CookieHeaderParser.cs b/src/Http/Headers/src/CookieHeaderParser.cs index 2a4cc64f5a15..620d98ee5cae 100644 --- a/src/Http/Headers/src/CookieHeaderParser.cs +++ b/src/Http/Headers/src/CookieHeaderParser.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using Microsoft.Extensions.Primitives; @@ -15,8 +16,9 @@ internal CookieHeaderParser(bool supportsMultipleValues) { } - public bool TryParseValue(StringSegment value, ref int index, out (StringSegment, StringSegment)? parsedValue) + public bool TryParseValue(StringSegment value, ref int index, [NotNullWhen(true)] out StringSegment? parsedName, [NotNullWhen(true)] out StringSegment? parsedValue) { + parsedName = null; parsedValue = null; // If multiple values are supported (i.e. list of values), then accept an empty string: The header may @@ -45,7 +47,7 @@ public bool TryParseValue(StringSegment value, ref int index, out (StringSegment return SupportsMultipleValues; } - if (!CookieHeaderValue.TryGetCookieLength(value, ref current, out var result)) + if (!CookieHeaderValue.TryGetCookieLength(value, ref current, out parsedName, out parsedValue)) { return false; } @@ -59,7 +61,7 @@ public bool TryParseValue(StringSegment value, ref int index, out (StringSegment } index = current; - parsedValue = result; + return true; } @@ -80,16 +82,13 @@ public bool TryParseValues(StringValues values, Dictionary store while (!string.IsNullOrEmpty(value) && index < value.Length) { - (StringSegment, StringSegment)? output; - if (TryParseValue(value, ref index, out output)) + if (TryParseValue(value, ref index, out var parsedName, out var parsedValue)) { // The entry may not contain an actual value, like " , " - if (output != null) + if (parsedName != null && parsedValue != null) { - var cookie = output.Value; - var name = enableCookieNameEncoding ? Uri.UnescapeDataString(cookie.Item1.Value) : cookie.Item1.Value; - var valueString = Uri.UnescapeDataString(cookie.Item2.Value); - store[name] = valueString; + var name = enableCookieNameEncoding ? Uri.UnescapeDataString(parsedName.Value.Value) : parsedName.Value.Value; + store[name] = Uri.UnescapeDataString(parsedValue.Value.Value); hasFoundValue = true; } } @@ -104,21 +103,22 @@ public bool TryParseValues(StringValues values, Dictionary store return hasFoundValue; } - public override bool TryParseValue(StringSegment value, ref int index, out CookieHeaderValue? parsedValue) + public override bool TryParseValue(StringSegment value, ref int index, out CookieHeaderValue? cookieValue) { - parsedValue = null; + cookieValue = null; - if (!TryParseValue(value, ref index, out var stringSegments)) + if (!TryParseValue(value, ref index, out var parsedName, out var parsedValue)) { return false; } - if (stringSegments == null) + if (parsedName == null || parsedValue == null) { - return false; + // Successfully parsed, but no values. + return true; } - parsedValue = new CookieHeaderValue(stringSegments.Value.Item1, stringSegments.Value.Item2); + cookieValue = new CookieHeaderValue(parsedName.Value, parsedValue.Value); return true; } diff --git a/src/Http/Headers/src/CookieHeaderValue.cs b/src/Http/Headers/src/CookieHeaderValue.cs index 88c4fc2683be..7aa478744c1e 100644 --- a/src/Http/Headers/src/CookieHeaderValue.cs +++ b/src/Http/Headers/src/CookieHeaderValue.cs @@ -178,12 +178,12 @@ public static bool TryParseIntoDictionary(StringValues inputs, Dictionary= 0); + parsedName = null; parsedValue = null; - (StringSegment, StringSegment) result = default; if (StringSegment.IsNullOrEmpty(input) || (offset >= input.Length)) { @@ -201,7 +201,7 @@ internal static bool TryGetCookieLength(StringSegment input, ref int offset, [No return false; } - result.Item1 = input.Subsegment(offset, itemLength); + parsedName = input.Subsegment(offset, itemLength); offset += itemLength; // = (no spaces) @@ -212,9 +212,8 @@ internal static bool TryGetCookieLength(StringSegment input, ref int offset, [No // value or "quoted value" // The value may be empty - result.Item2 = GetCookieValue(input, ref offset); + parsedValue = GetCookieValue(input, ref offset); - parsedValue = result; return true; } diff --git a/src/Http/Http/perf/Microbenchmarks/RequestCookieCollectionBenchmarks.cs b/src/Http/Http/perf/Microbenchmarks/RequestCookieCollectionBenchmarks.cs new file mode 100644 index 000000000000..035a367b1324 --- /dev/null +++ b/src/Http/Http/perf/Microbenchmarks/RequestCookieCollectionBenchmarks.cs @@ -0,0 +1,25 @@ +// 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 BenchmarkDotNet.Attributes; +using Microsoft.Extensions.Primitives; + +namespace Microsoft.AspNetCore.Http +{ + public class RequestCookieCollectionBenchmarks + { + private StringValues _cookie; + + [IterationSetup] + public void Setup() + { + _cookie = ".AspNetCore.Cookies=CfDJ8BAklVa9EYREk8_ipRUUYJYhRsleKr485k18s_q5XD6vcRJ-DtowUuLCwwMiY728zRZ3rVFY3DEcXDAQUOTtg1e4tkSIVmYLX38Q6mqdFFyw-8dksclDywe9vnN84cEWvfV0wP3EgOsJGHaND7kTJ47gr7Pc1tLHWOm4Pe7Q1vrT9EkcTMr1Wts3aptBl3bdOLLqjmSdgk-OI7qG7uQGz1OGdnSer6-KLUPBcfXblzs4YCjvwu3bGnM42xLGtkZNIF8izPpyqKkIf7ec6O6LEHMp4gcq86PGHCXHn5NKuNSD"; + } + + [Benchmark] + public void Parse_TypicalCookie() + { + RequestCookieCollection.Parse(_cookie); + } + } +} diff --git a/src/Http/Http/perf/Microbenchmarks/RouteValueDictionaryBenchmark.cs b/src/Http/Http/perf/Microbenchmarks/RouteValueDictionaryBenchmark.cs index 2dfc36afa4cc..76a8659c2a48 100644 --- a/src/Http/Http/perf/Microbenchmarks/RouteValueDictionaryBenchmark.cs +++ b/src/Http/Http/perf/Microbenchmarks/RouteValueDictionaryBenchmark.cs @@ -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; diff --git a/src/Http/Http/src/Properties/AssemblyInfo.cs b/src/Http/Http/src/Properties/AssemblyInfo.cs index 2b8d94f4a543..21cadcc624da 100644 --- a/src/Http/Http/src/Properties/AssemblyInfo.cs +++ b/src/Http/Http/src/Properties/AssemblyInfo.cs @@ -1,3 +1,4 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Http.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Http.MicroBenchmarks, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]