| | | 1 | | // Licensed to the .NET Foundation under one or more agreements. |
| | | 2 | | // The .NET Foundation licenses this file to you under the MIT license. |
| | | 3 | | |
| | | 4 | | using System; |
| | | 5 | | using System.Diagnostics.CodeAnalysis; |
| | | 6 | | |
| | | 7 | | namespace System.Net.Http.Headers |
| | | 8 | | { |
| | | 9 | | // Don't derive from BaseHeaderParser since empty values are not supported. After a ' ' separator a valid value |
| | | 10 | | // must follow. Also leading separators are not allowed. |
| | | 11 | | internal sealed class ProductInfoHeaderParser : HttpHeaderParser |
| | | 12 | | { |
| | | 13 | | // Unlike most other headers, User-Agent and Server use whitespace as separators |
| | | 14 | | private const string separator = " "; |
| | | 15 | | |
| | 1 | 16 | | internal static readonly ProductInfoHeaderParser SingleValueParser = new ProductInfoHeaderParser(false); |
| | 1 | 17 | | internal static readonly ProductInfoHeaderParser MultipleValueParser = new ProductInfoHeaderParser(true); |
| | | 18 | | |
| | | 19 | | private ProductInfoHeaderParser(bool supportsMultipleValues) |
| | 2 | 20 | | : base(supportsMultipleValues, separator) |
| | 2 | 21 | | { |
| | 2 | 22 | | } |
| | | 23 | | |
| | | 24 | | public override bool TryParseValue([NotNullWhen(true)] string? value, object? storeValue, ref int index, [NotNul |
| | 38771 | 25 | | { |
| | 38771 | 26 | | parsedValue = null; |
| | | 27 | | |
| | 38771 | 28 | | if (string.IsNullOrEmpty(value) || (index == value.Length)) |
| | 12 | 29 | | { |
| | 12 | 30 | | return false; |
| | | 31 | | } |
| | | 32 | | |
| | | 33 | | // Skip leading whitespace |
| | 38759 | 34 | | int current = index + HttpRuleParser.GetWhitespaceLength(value, index); |
| | | 35 | | |
| | 38759 | 36 | | if (current == value.Length) |
| | 2 | 37 | | { |
| | 2 | 38 | | return false; // whitespace-only values are not valid |
| | | 39 | | } |
| | | 40 | | |
| | 38757 | 41 | | int length = ProductInfoHeaderValue.GetProductInfoLength(value, current, out ProductInfoHeaderValue? result) |
| | | 42 | | |
| | 38757 | 43 | | if (length == 0) |
| | 608 | 44 | | { |
| | 608 | 45 | | return false; |
| | | 46 | | } |
| | | 47 | | |
| | | 48 | | // GetProductInfoLength() already skipped trailing whitespace. No need to do it here again. |
| | 38149 | 49 | | current += length; |
| | | 50 | | |
| | | 51 | | // If we have more values, make sure we saw a whitespace before. Values like "product/1.0(comment)" are |
| | | 52 | | // invalid since there must be a whitespace between the product and the comment value. |
| | 38149 | 53 | | if (current < value.Length) |
| | 36931 | 54 | | { |
| | | 55 | | // Note that for \r\n to be a valid whitespace, it must be followed by a space/tab. I.e. it's enough if |
| | | 56 | | // we check whether the char before the next value is space/tab. |
| | 36931 | 57 | | char lastSeparatorChar = value[current - 1]; |
| | 36931 | 58 | | if ((lastSeparatorChar != ' ') && (lastSeparatorChar != '\t')) |
| | 158 | 59 | | { |
| | 158 | 60 | | return false; |
| | | 61 | | } |
| | 36773 | 62 | | } |
| | | 63 | | |
| | | 64 | | // Separators for "User-Agent" and "Server" headers are whitespace. This is different from most other header |
| | | 65 | | // where comma/semicolon is used as separator. |
| | 37991 | 66 | | index = current; |
| | 37991 | 67 | | parsedValue = result!; |
| | 37991 | 68 | | return true; |
| | 38771 | 69 | | } |
| | | 70 | | } |
| | | 71 | | } |