<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="10.0.0-preview.5.25277.114" />

W3CPropagator

using System.Collections.Generic; using System.Collections.ObjectModel; using System.Runtime.CompilerServices; using System.Text; namespace System.Diagnostics { internal sealed class W3CPropagator : DistributedContextPropagator { private const int MaxBaggageEntriesToEmit = 64; private const int MaxBaggageEncodedLength = 8192; private const int MaxTraceStateEncodedLength = 256; private const char Equal = '='; private const char Percent = '%'; private const char Replacement = '�'; private static ulong[] s_invalidBaggageKeyCharsMask = new ulong[2] { 18158675351536926719, 2882303762456641537 }; private static ulong[] ValidTraceStateKeyCharsMask = new ulong[2] { 180319906955264, 576460745860972545 }; private static ulong[] s_traceStateValueMask = new ulong[2] { 16140883463719878656, 9223372036854775807 }; private static ulong[] s_baggageOctet = new ulong[2] { 17870265566011326464, 9223372036586340351 }; private static ulong[] s_traceParentMask = new ulong[2] { 287948901175001088, 541165879296 }; internal static DistributedContextPropagator Instance { get; } = new W3CPropagator(); public override IReadOnlyCollection<string> Fields { get; } = new ReadOnlyCollection<string>(new string[4] { "traceparent", "tracestate", "baggage", "Correlation-Context" }); public override void Inject(Activity activity, object carrier, PropagatorSetterCallback setter) { if (activity != null && setter != null && activity.IdFormat == ActivityIdFormat.W3C) { string id = activity.Id; if (id != null) { setter(carrier, "traceparent", id); string traceStateString = activity.TraceStateString; if (traceStateString != null && traceStateString.Length > 0) InjectTraceState(traceStateString, carrier, setter); InjectW3CBaggage(carrier, activity.Baggage, setter); } } } public override void ExtractTraceIdAndState(object carrier, PropagatorGetterCallback getter, out string traceId, out string traceState) { if (getter == null) { traceId = null; traceState = null; } else { getter(carrier, "traceparent", out traceId, out IEnumerable<string> fieldValues); if (IsInvalidTraceParent(traceId)) traceId = null; getter(carrier, "tracestate", out string fieldValue, out fieldValues); traceState = ValidateTraceState(fieldValue); } } public override IEnumerable<KeyValuePair<string, string>> ExtractBaggage(object carrier, PropagatorGetterCallback getter) { if (getter == null) return null; getter(carrier, "baggage", out string fieldValue, out IEnumerable<string> fieldValues); if (fieldValue == null) getter(carrier, "Correlation-Context", out fieldValue, out fieldValues); TryExtractBaggage(fieldValue, out IEnumerable<KeyValuePair<string, string>> baggage); return baggage; } internal static bool TryExtractBaggage(string baggageString, out IEnumerable<KeyValuePair<string, string>> baggage) { baggage = null; List<KeyValuePair<string, string>> list = null; if (string.IsNullOrEmpty(baggageString)) return true; ReadOnlySpan<char> readOnlySpan = MemoryExtensions.AsSpan(baggageString); do { int num = readOnlySpan.IndexOf(','); ReadOnlySpan<char> span = (num >= 0) ? readOnlySpan.Slice(0, num) : readOnlySpan; int num2 = span.IndexOf('='); if (num2 <= 0 || num2 >= span.Length - 1) break; ReadOnlySpan<char> keySpan = span.Slice(0, num2); ReadOnlySpan<char> valueSpan = span.Slice(num2 + 1); if (TryDecodeBaggageKey(keySpan, out string key) && TryDecodeBaggageValue(valueSpan, out string value)) { if (list == null) list = new List<KeyValuePair<string, string>>(); list.Add(new KeyValuePair<string, string>(key, value)); } readOnlySpan = ((num >= 0) ? readOnlySpan.Slice(num + 1) : ReadOnlySpan<char>.Empty); } while (readOnlySpan.Length > 0); list?.Reverse(); baggage = list; return list != null; } internal static string ValidateTraceState(string traceState) { if (string.IsNullOrEmpty(traceState)) return null; int i; int num2; for (i = 0; i < traceState.Length; i += num2) { ReadOnlySpan<char> readOnlySpan = MemoryExtensions.AsSpan(traceState, i); int num = readOnlySpan.IndexOf(','); ReadOnlySpan<char> span = (num >= 0) ? readOnlySpan.Slice(0, num) : readOnlySpan; num2 = span.Length + ((num >= 0) ? 1 : 0); if (i + num2 > 256) break; int num3 = span.IndexOf('='); if (num3 <= 0 || num3 >= span.Length - 1 || IsInvalidTraceStateKey(Trim(span.Slice(0, num3))) || IsInvalidTraceStateValue(TrimSpaceOnly(span.Slice(num3 + 1)))) break; } if (i > 0) { if (traceState[i - 1] == ',') i--; if (i > 0) { if (i < traceState.Length) return MemoryExtensions.AsSpan(traceState, 0, i).ToString(); return traceState; } } return null; } internal static void InjectTraceState(string traceState, object carrier, PropagatorSetterCallback setter) { string text = ValidateTraceState(traceState); if (text != null) setter(carrier, "tracestate", text); } internal unsafe static void InjectW3CBaggage(object carrier, IEnumerable<KeyValuePair<string, string>> baggage, PropagatorSetterCallback setter) { using (IEnumerator<KeyValuePair<string, string>> enumerator = baggage.GetEnumerator()) { if (enumerator.MoveNext()) { Span<char> initialBuffer = new Span<char>(stackalloc byte[512], 256); System.Text.ValueStringBuilder vsb = new System.Text.ValueStringBuilder(initialBuffer); int num = 0; int num2 = 0; do { KeyValuePair<string, string> current = enumerator.Current; if (EncodeBaggageKey(MemoryExtensions.AsSpan(current.Key), ref vsb)) { vsb.Append(' '); vsb.Append('='); vsb.Append(' '); if (!string.IsNullOrEmpty(current.Value)) EncodeBaggageValue(MemoryExtensions.AsSpan(current.Value), ref vsb); vsb.Append(", "); num++; if (vsb.Length < 8192) num2 = vsb.Length; } } while (enumerator.MoveNext() && num < 64 && vsb.Length < 8192); if (num2 - 2 > 0) setter(carrier, "baggage", vsb.AsSpan(0, num2 - 2).ToString()); vsb.Dispose(); } } } private static bool TryDecodeBaggageKey(ReadOnlySpan<char> keySpan, out string key) { key = null; keySpan = Trim(keySpan); if (keySpan.IsEmpty || IsInvalidBaggageKey(keySpan)) return false; key = keySpan.ToString(); return true; } private unsafe static bool TryDecodeBaggageValue(ReadOnlySpan<char> valueSpan, out string value) { value = null; valueSpan = Trim(valueSpan); Span<char> initialBuffer = new Span<char>(stackalloc byte[256], 128); System.Text.ValueStringBuilder valueStringBuilder = new System.Text.ValueStringBuilder(initialBuffer); try { for (int i = 0; i < valueSpan.Length; i++) { char c = valueSpan[i]; if (c > '') return false; if (c != '%') valueStringBuilder.Append(c); else { if (!TryDecodeEscapedByte(valueSpan.Slice(i), out byte value2)) return false; if (value2 <= 127) { valueStringBuilder.Append((char)value2); i += 2; } else if ((uint)(value2 - 194) <= 29) { byte value3; if (i + 5 >= valueSpan.Length || valueSpan[i + 3] != '%' || !TryDecodeEscapedByte(valueSpan.Slice(i + 3), out value3) || (value3 & 192) != 128) { valueStringBuilder.Append('�'); i += 2; } else { valueStringBuilder.Append((char)(((value2 & 31) << 6) | (value3 & 63))); i += 5; } } else if ((uint)(value2 - 224) <= 15) { byte value4; if (i + 8 >= valueSpan.Length || valueSpan[i + 3] != '%' || valueSpan[i + 6] != '%' || !TryDecodeEscapedByte(valueSpan.Slice(i + 3), out value4) || !TryDecodeEscapedByte(valueSpan.Slice(i + 6), out byte value5) || (value2 == 224 && value4 < 160) || (value2 == 237 && value4 >= 160)) { valueStringBuilder.Append('�'); i += 2; } else { valueStringBuilder.Append((char)(((value2 & 15) << 12) | ((value4 & 63) << 6) | (value5 & 63))); i += 8; } } else if ((uint)(value2 - 240) <= 4) { byte value8; byte value7; byte value6; if (i + 11 >= valueSpan.Length || valueSpan[i + 3] != '%' || valueSpan[i + 6] != '%' || valueSpan[i + 9] != '%' || !TryDecodeEscapedByte(valueSpan.Slice(i + 3), out value6) || !TryDecodeEscapedByte(valueSpan.Slice(i + 6), out value7) || !TryDecodeEscapedByte(valueSpan.Slice(i + 9), out value8) || (value6 & 192) != 128 || (value7 & 192) != 128 || (value8 & 192) != 128) { valueStringBuilder.Append('�'); i += 2; } else { int num = ((value2 & 7) << 18) | ((value6 & 63) << 12) | ((value7 & 63) << 6) | (value8 & 63); if (num < 65536 || num > 1114111) { valueStringBuilder.Append('�'); i += 2; } else { num -= 65536; valueStringBuilder.Append((char)((num >> 10) + 55296)); valueStringBuilder.Append((char)((num & 1023) + 56320)); i += 11; } } } else { valueStringBuilder.Append('�'); i += 2; } } } value = valueStringBuilder.ToString(); return true; } finally { valueStringBuilder.Dispose(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryDecodeEscapedByte(ReadOnlySpan<char> span, out byte value) { if (span.Length < 3 || !TryDecodeHexDigit(span[1], out byte value2) || !TryDecodeHexDigit(span[2], out byte value3)) { value = 0; return false; } value = (byte)((value2 << 4) + value3); return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryDecodeHexDigit(char c, out byte value) { value = (byte)System.HexConverter.FromChar(c); return value != 255; } private static bool IsInvalidBaggageKey(ReadOnlySpan<char> span) { ReadOnlySpan<char> readOnlySpan = span; foreach (char c in readOnlySpan) { if (c >= '' || ((long)s_invalidBaggageKeyCharsMask[(int)c >> 6] & (1 << (int)c)) != 0) return true; } return false; } private static bool IsInvalidTraceStateKey(ReadOnlySpan<char> key) { if (key.IsEmpty || key[0] < 'a' || key[0] > 'z') return true; ReadOnlySpan<char> readOnlySpan = key; foreach (char c in readOnlySpan) { if (c >= '' || ((long)ValidTraceStateKeyCharsMask[(int)c >> 6] & (1 << (int)c)) == 0) return true; } return false; } private static bool IsInvalidTraceStateValue(ReadOnlySpan<char> value) { if (value.IsEmpty) return true; ReadOnlySpan<char> readOnlySpan = value; foreach (char c in readOnlySpan) { if (c >= '' || ((long)s_traceStateValueMask[(int)c >> 6] & (1 << (int)c)) == 0) return true; } return false; } internal static bool EncodeBaggageKey(ReadOnlySpan<char> key, ref System.Text.ValueStringBuilder vsb) { key = Trim(key); if (key.IsEmpty || IsInvalidBaggageKey(key)) return false; vsb.Append(key); return true; } internal static void EncodeBaggageValue(ReadOnlySpan<char> value, ref System.Text.ValueStringBuilder vsb) { value = Trim(value); for (int i = 0; i < value.Length; i++) { char c = value[i]; if (!NeedToEscapeBaggageValueCharacter(c)) vsb.Append(c); else if ((uint)c <= 127) { EmitEscapedByte((byte)c, ref vsb); } else if ((uint)c <= 2047) { EmitEscapedByte((byte)((uint)(c + 12288) >> 6), ref vsb); EmitEscapedByte((byte)((c & 63) + 128), ref vsb); } else if (char.IsSurrogate(c)) { if (i < value.Length - 1 && char.IsSurrogatePair(c, value[i + 1])) { int num = char.ConvertToUtf32(c, value[i + 1]); EmitEscapedByte((byte)((uint)(num + 62914560) >> 18), ref vsb); EmitEscapedByte((byte)(((uint)(num & 258048) >> 12) + 128), ref vsb); EmitEscapedByte((byte)(((uint)(num & 4032) >> 6) + 128), ref vsb); EmitEscapedByte((byte)((num & 63) + 128), ref vsb); i++; } else { EmitEscapedByte(239, ref vsb); EmitEscapedByte(191, ref vsb); EmitEscapedByte(189, ref vsb); } } else { EmitEscapedByte((byte)(c + 917504 >> 12), ref vsb); EmitEscapedByte((byte)(((uint)(c & 4032) >> 6) + 128), ref vsb); EmitEscapedByte((byte)((c & 63) + 128), ref vsb); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool NeedToEscapeBaggageValueCharacter(char c) { if (c >= '') return true; return ((long)s_baggageOctet[(int)c >> 6] & (1 << (int)c)) == 0; } private static bool IsInvalidTraceParent(string traceParent) { if (string.IsNullOrEmpty(traceParent) || traceParent.Length != 55) return true; if (traceParent[0] == 'f' || traceParent[1] == 'f' || IsInvalidTraceParentCharacter(traceParent[0]) || IsInvalidTraceParentCharacter(traceParent[1])) return true; if (traceParent[2] != '-' || traceParent[35] != '-' || traceParent[52] != '-') return true; bool flag = true; for (int i = 3; i < 35; i++) { if (IsInvalidTraceParentCharacter(traceParent[i])) return true; flag &= (traceParent[i] == '0'); } if (flag) return true; flag = true; for (int j = 36; j < 52; j++) { if (IsInvalidTraceParentCharacter(traceParent[j])) return true; flag &= (traceParent[j] == '0'); } if (flag) return true; if (IsInvalidTraceParentCharacter(traceParent[53]) || IsInvalidTraceParentCharacter(traceParent[54])) return true; return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsInvalidTraceParentCharacter(char c) { if (c >= '') return true; return ((long)s_traceParentMask[(int)c >> 6] & (1 << (int)c)) == 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void EmitEscapedByte(byte b, ref System.Text.ValueStringBuilder vsb) { vsb.Append('%'); vsb.Append("0123456789ABCDEF"[(b >> 4) & 15]); vsb.Append("0123456789ABCDEF"[b & 15]); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan<char> TrimSpaceOnly(ReadOnlySpan<char> span) { return span.Trim(' '); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan<char> Trim(ReadOnlySpan<char> span) { return span.Trim(MemoryExtensions.AsSpan(" \t")); } } }