From 0080d0e9f6765ee516673c55d10e2ca1d8b20f95 Mon Sep 17 00:00:00 2001 From: Steve Gordon Date: Wed, 13 Mar 2024 13:18:30 -0700 Subject: [PATCH] Fix bug when handling of multiple cookie entries with the same name --- .../Filters/RequestCookieExtractionFilter.cs | 2 +- src/Elastic.Apm/Helpers/CookieHeaderParser.cs | 16 ++++++++-- .../HelpersTests/CookieHeaderParserTests.cs | 31 +++++++++++-------- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/Elastic.Apm/Filters/RequestCookieExtractionFilter.cs b/src/Elastic.Apm/Filters/RequestCookieExtractionFilter.cs index a68fa2d0a..92ec639de 100644 --- a/src/Elastic.Apm/Filters/RequestCookieExtractionFilter.cs +++ b/src/Elastic.Apm/Filters/RequestCookieExtractionFilter.cs @@ -14,7 +14,7 @@ namespace Elastic.Apm.Filters /// internal class RequestCookieExtractionFilter { - private static readonly WildcardMatcher[] CookieMatcher = new WildcardMatcher[] { new WildcardMatcher.VerbatimMatcher("Cookie", true) }; + private static readonly WildcardMatcher[] CookieMatcher = [new WildcardMatcher.VerbatimMatcher("Cookie", true)]; public static IError Filter(IError error) { diff --git a/src/Elastic.Apm/Helpers/CookieHeaderParser.cs b/src/Elastic.Apm/Helpers/CookieHeaderParser.cs index 7e4b27ad5..15c470580 100644 --- a/src/Elastic.Apm/Helpers/CookieHeaderParser.cs +++ b/src/Elastic.Apm/Helpers/CookieHeaderParser.cs @@ -42,7 +42,11 @@ public static Dictionary ParseCookies(string cookieHeader) var trimmed = cookieValue.Trim(); var parts = trimmed.Split('='); - if (parts.Length == 2 && !string.IsNullOrEmpty(parts[0]) && !string.IsNullOrEmpty(parts[1])) + // Fow now, we store only the first value for a given key. This aligns to our nodeJS agent behavior. + if (parts.Length == 2 + && !string.IsNullOrEmpty(parts[0]) + && !cookies.ContainsKey(parts[0]) + && !string.IsNullOrEmpty(parts[1])) { cookies.Add(parts[0], parts[1]); } @@ -51,7 +55,7 @@ public static Dictionary ParseCookies(string cookieHeader) return cookies; #else var span = cookieHeader.AsSpan(); - + while (span.Length > 0) { var foundComma = true; @@ -75,8 +79,14 @@ public static Dictionary ParseCookies(string cookieHeader) var keyString = key.ToString(); var valueString = value.ToString(); - if (!string.IsNullOrEmpty(keyString) && !string.IsNullOrEmpty(valueString)) + // Fow now, we store only the first value for a given key. This aligns to our nodeJS agent behavior. +#if NETSTANDARD2_0 + if (!string.IsNullOrEmpty(keyString) && !cookies.ContainsKey(keyString) && !string.IsNullOrEmpty(valueString)) cookies.Add(keyString, valueString); +#else + if (!string.IsNullOrEmpty(keyString) && !string.IsNullOrEmpty(valueString)) + cookies.TryAdd(keyString, valueString); +#endif } span = span.Slice(foundComma ? separatorIndex + 1 : span.Length); diff --git a/test/Elastic.Apm.Tests/HelpersTests/CookieHeaderParserTests.cs b/test/Elastic.Apm.Tests/HelpersTests/CookieHeaderParserTests.cs index 37506ea10..588b4b2f2 100644 --- a/test/Elastic.Apm.Tests/HelpersTests/CookieHeaderParserTests.cs +++ b/test/Elastic.Apm.Tests/HelpersTests/CookieHeaderParserTests.cs @@ -26,31 +26,36 @@ public void ParsesHeadersCorrectly(string input, Dictionary expe result.Should().Contain(expected); } - public static IEnumerable TestData() => new[] { - new object[] { null, null }, - new object[] { "", null }, - new object[] { "Key1=Value1", new Dictionary() { { "Key1", "Value1" }} }, - new object[] { "Key1=Value1,Key2=Value2", new Dictionary() + public static IEnumerable TestData() => [ + [null, null], + ["", null], + ["Key1=Value1", new Dictionary() { { "Key1", "Value1" }}], + [ "Key1=Value1,Key2=Value2", new Dictionary() { { "Key1", "Value1" }, { "Key2", "Value2" } - }}, - new object[] { "Key1=Value1,Key2=Value2,Key3=Value3", new Dictionary() + }], + [ "Key1=Value1,Key2=Value2,Key3=Value3", new Dictionary() { { "Key1", "Value1" }, { "Key2", "Value2" }, { "Key3", "Value3" } - }}, - new object[] { "Key1=Value1; Key2=Value2", new Dictionary() + }], + [ "Key1=Value1; Key2=Value2", new Dictionary() { { "Key1", "Value1" }, { "Key2", "Value2" } - }}, - new object[] { "Key1=Value1; Key2=Value2; Key3=Value3", new Dictionary() + }], + [ "Key1=Value1; Key2=Value2; Key3=Value3", new Dictionary() { { "Key1", "Value1" }, { "Key2", "Value2" }, { "Key3", "Value3" } - }} - }; + }], + [ "Key1=Value1; Key2=Value2; Key1=Value3", new Dictionary() + { + { "Key1", "Value1" }, + { "Key2", "Value2" } + }], + ]; }