| File: Logging\HttpHeadersLogValue.cs | Web Access |
| Project: src\runtime\src\libraries\Microsoft.Extensions.Http\src\Microsoft.Extensions.Http.csproj (Microsoft.Extensions.Http) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections; using System.Collections.Generic; using System.Net.Http.Headers; using System.Text; namespace Microsoft.Extensions.Http.Logging { internal sealed class HttpHeadersLogValue : IReadOnlyList<KeyValuePair<string, object>> { private const string RedactedValue = "*"; private readonly Kind _kind; private readonly Func<string, bool> _shouldRedactHeaderValue; private string? _formatted; private List<KeyValuePair<string, object>>? _values; public HttpHeadersLogValue(Kind kind, HttpHeaders headers, HttpHeaders? contentHeaders, Func<string, bool> shouldRedactHeaderValue) { _kind = kind; _shouldRedactHeaderValue = shouldRedactHeaderValue; Headers = headers; ContentHeaders = contentHeaders; } public HttpHeaders Headers { get; } public HttpHeaders? ContentHeaders { get; } private List<KeyValuePair<string, object>> Values { get { if (_values == null) { var values = new List<KeyValuePair<string, object>>(GetHeaderCount(Headers) + GetHeaderCount(ContentHeaders)); AddHeaders(values, Headers); if (ContentHeaders != null) { AddHeaders(values, ContentHeaders); } _values = values; } return _values; } } public KeyValuePair<string, object> this[int index] { get { if (index < 0 || index >= Count) { throw new IndexOutOfRangeException(); } return Values[index]; } } public int Count => Values.Count; // Enumerate the headers without triggering validation/parsing of the values, so that logging // doesn't alter how the headers are subsequently serialized on the wire. private void AddHeaders(List<KeyValuePair<string, object>> values, HttpHeaders headers) { #if NET foreach (KeyValuePair<string, HeaderStringValues> kvp in headers.NonValidated) #else foreach (KeyValuePair<string, IEnumerable<string>> kvp in headers) #endif { string value = _shouldRedactHeaderValue(kvp.Key) ? RedactedValue #if NET : kvp.Value.ToString(); #else : string.Join(", ", kvp.Value); #endif values.Add(new KeyValuePair<string, object>(kvp.Key, value)); } } private static int GetHeaderCount(HttpHeaders? headers) { #if NET return headers?.NonValidated.Count ?? 0; #else return 0; #endif } public IEnumerator<KeyValuePair<string, object>> GetEnumerator() { return Values.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return Values.GetEnumerator(); } public override string ToString() { if (_formatted == null) { var builder = new StringBuilder(); builder.AppendLine(_kind == Kind.Request ? "Request Headers:" : "Response Headers:"); for (int i = 0; i < Values.Count; i++) { KeyValuePair<string, object> kvp = Values[i]; builder.Append(kvp.Key); builder.Append(": "); builder.AppendLine((string)kvp.Value); } _formatted = builder.ToString(); } return _formatted; } public enum Kind { Request, Response, } } }