| File: System\Net\Http\Headers\HttpHeaderParser.cs | Web Access |
| Project: src\runtime\src\libraries\System.Net.Http\src\System.Net.Http.csproj (System.Net.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.Collections; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Text; namespace System.Net.Http.Headers { internal abstract class HttpHeaderParser { public const string DefaultSeparator = ", "; public static readonly byte[] DefaultSeparatorBytes = ", "u8.ToArray(); public bool SupportsMultipleValues { get; } public string Separator { get; } public byte[] SeparatorBytes { get; } // If ValueType implements Equals() as required, there is no need to provide a comparer. A comparer is needed // e.g. if we want to compare strings using case-insensitive comparison. public virtual IEqualityComparer? Comparer => null; protected HttpHeaderParser(bool supportsMultipleValues) { SupportsMultipleValues = supportsMultipleValues; Separator = DefaultSeparator; SeparatorBytes = DefaultSeparatorBytes; } protected HttpHeaderParser(bool supportsMultipleValues, string separator) : this(supportsMultipleValues) { Debug.Assert(!string.IsNullOrEmpty(separator)); Debug.Assert(Ascii.IsValid(separator)); if (supportsMultipleValues) { Separator = separator; SeparatorBytes = Encoding.ASCII.GetBytes(separator); } } // If a parser supports multiple values, a call to ParseValue/TryParseValue should return a value for 'index' // pointing to the next non-whitespace character after a delimiter. E.g. if called with a start index of 0 // for string "value , second_value", then after the call completes, 'index' must point to 's', i.e. the first // non-whitespace after the separator ','. public abstract bool TryParseValue(string? value, object? storeValue, ref int index, [NotNullWhen(true)] out object? parsedValue); public object ParseValue(string? value, object? storeValue, ref int index) { // Index may be value.Length (e.g. both 0). This may be allowed for some headers (e.g. Accept but not // allowed by others (e.g. Content-Length). The parser has to decide if this is valid or not. Debug.Assert((value == null) || ((index >= 0) && (index <= value.Length))); // If a parser returns 'null', it means there was no value, but that's valid (e.g. "Accept: "). The caller // can ignore the value. if (!TryParseValue(value, storeValue, ref index, out object? result)) { throw new FormatException(SR.Format(System.Globalization.CultureInfo.InvariantCulture, SR.net_http_headers_invalid_value, value == null ? "<null>" : value.Substring(index))); } return result; } // If ValueType is a custom header value type (e.g. NameValueHeaderValue) it already implements ToString() correctly. // However for existing types like int, byte[], DateTimeOffset we can't override ToString(). Therefore the // parser provides a ToString() virtual method that can be overridden by derived types to correctly serialize // values (e.g. byte[] to Base64 encoded string). public virtual string? ToString(object value) { Debug.Assert(value != null); return value.ToString(); } } }