< Summary

Line coverage
25%
Covered lines: 46
Uncovered lines: 134
Coverable lines: 180
Total lines: 304
Line coverage: 25.5%
Branch coverage
39%
Covered branches: 22
Total branches: 56
Branch coverage: 39.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%110%
.ctor(...)100%110%
.ctor(...)100%110%
.ctor(...)100%110%
ToString()0%220%
Equals(...)0%10100%
GetHashCode()100%110%
Parse(...)100%110%
TryParse(...)0%220%
GetWarningLength(...)58.33%121264%
TryReadAgent(...)100%66100%
TryReadCode(...)90%101083.33%
TryReadDate(...)0%10100%
System.ICloneable.Clone()100%110%
CheckCode(...)100%110%
CheckAgent(...)0%220%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Net.Http/src/System/Net/Http/Headers/WarningHeaderValue.cs

#LineLine coverage
 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
 4using System.Diagnostics;
 5using System.Diagnostics.CodeAnalysis;
 6using System.Globalization;
 7using System.Text;
 8
 9namespace System.Net.Http.Headers
 10{
 11    public class WarningHeaderValue : ICloneable
 12    {
 13        private readonly int _code;
 14        private readonly string _agent;
 15        private readonly string _text;
 16        private readonly DateTimeOffset _date;
 17        private readonly bool _dateHasValue;
 18
 019        public int Code => _code;
 20
 021        public string Agent => _agent;
 22
 023        public string Text => _text;
 24
 025        public DateTimeOffset? Date => _dateHasValue ? _date : null;
 26
 027        private WarningHeaderValue(int code, string agent, string text, DateTimeOffset? date)
 028        {
 29#if DEBUG
 30            // This constructor should only be used with already validated values.
 031            new WarningHeaderValue(code, agent, text);
 32#endif
 33
 034            _code = code;
 035            _agent = agent;
 036            _text = text;
 037            _date = date.GetValueOrDefault();
 038            _dateHasValue = date.HasValue;
 039        }
 40
 041        public WarningHeaderValue(int code, string agent, string text)
 042        {
 043            CheckCode(code);
 044            CheckAgent(agent);
 045            HeaderUtilities.CheckValidQuotedString(text);
 46
 047            _code = code;
 048            _agent = agent;
 049            _text = text;
 050        }
 51
 052        public WarningHeaderValue(int code, string agent, string text, DateTimeOffset date)
 053        {
 054            CheckCode(code);
 055            CheckAgent(agent);
 056            HeaderUtilities.CheckValidQuotedString(text);
 57
 058            _code = code;
 059            _agent = agent;
 060            _text = text;
 061            _date = date;
 062            _dateHasValue = true;
 063        }
 64
 065        private WarningHeaderValue(WarningHeaderValue source)
 066        {
 067            Debug.Assert(source != null);
 68
 069            _code = source._code;
 070            _agent = source._agent;
 071            _text = source._text;
 072            _date = source._date;
 073            _dateHasValue = source._dateHasValue;
 074        }
 75
 76        public override string ToString()
 077        {
 078            var sb = new ValueStringBuilder(stackalloc char[256]);
 79
 80            // Warning codes are always 3 digits according to RFC2616
 081            sb.AppendSpanFormattable(_code, "000", NumberFormatInfo.InvariantInfo);
 82
 083            sb.Append(' ');
 084            sb.Append(_agent);
 085            sb.Append(' ');
 086            sb.Append(_text);
 87
 088            if (_dateHasValue)
 089            {
 090                sb.Append(" \"");
 091                sb.AppendSpanFormattable(_date, "r");
 092                sb.Append('\"');
 093            }
 94
 095            return sb.ToString();
 096        }
 97
 98        public override bool Equals([NotNullWhen(true)] object? obj) =>
 099            obj is WarningHeaderValue other &&
 0100            _code == other._code &&
 0101            // 'agent' is a host/token, i.e. use case-insensitive comparison. Use case-sensitive comparison for 'text'
 0102            // since it is a quoted string.
 0103            string.Equals(_agent, other._agent, StringComparison.OrdinalIgnoreCase) &&
 0104            string.Equals(_text, other._text, StringComparison.Ordinal) &&
 0105            _dateHasValue == other._dateHasValue &&
 0106            _date == other._date;
 107
 108        public override int GetHashCode() =>
 0109            HashCode.Combine(
 0110                _code,
 0111                StringComparer.OrdinalIgnoreCase.GetHashCode(_agent),
 0112                _text,
 0113                _dateHasValue,
 0114                _date);
 115
 116        public static WarningHeaderValue Parse(string input)
 0117        {
 0118            int index = 0;
 0119            return (WarningHeaderValue)GenericHeaderParser.SingleValueWarningParser.ParseValue(input, null, ref index);
 0120        }
 121
 122        public static bool TryParse([NotNullWhen(true)] string? input, [NotNullWhen(true)] out WarningHeaderValue? parse
 0123        {
 0124            int index = 0;
 0125            parsedValue = null;
 126
 0127            if (GenericHeaderParser.SingleValueWarningParser.TryParseValue(input, null, ref index, out object? output))
 0128            {
 0129                parsedValue = (WarningHeaderValue)output!;
 0130                return true;
 131            }
 0132            return false;
 0133        }
 134
 135        internal static int GetWarningLength(string? input, int startIndex, out object? parsedValue)
 460136        {
 460137            Debug.Assert(startIndex >= 0);
 138
 460139            parsedValue = null;
 140
 460141            if (string.IsNullOrEmpty(input) || (startIndex >= input.Length))
 0142            {
 0143                return 0;
 144            }
 145
 146            // Read <code> in '<code> <agent> <text> ["<date>"]'
 460147            int current = startIndex;
 148
 460149            if (!TryReadCode(input, ref current, out int code))
 140150            {
 140151                return 0;
 152            }
 153
 154            // Read <agent> in '<code> <agent> <text> ["<date>"]'
 320155            if (!TryReadAgent(input, ref current, out string? agent))
 164156            {
 164157                return 0;
 158            }
 159
 160            // Read <text> in '<code> <agent> <text> ["<date>"]'
 156161            int textStartIndex = current;
 156162            if (HttpRuleParser.GetQuotedStringLength(input, current, out int textLength) != HttpParseResult.Parsed)
 156163            {
 156164                return 0;
 165            }
 166
 0167            string text = input.Substring(textStartIndex, textLength);
 168
 0169            current += textLength;
 170
 171            // Read <date> in '<code> <agent> <text> ["<date>"]'
 0172            if (!TryReadDate(input, ref current, out DateTimeOffset? date))
 0173            {
 0174                return 0;
 175            }
 176
 0177            parsedValue = new WarningHeaderValue(code, agent, text, date);
 178
 0179            return current - startIndex;
 460180        }
 181
 182        private static bool TryReadAgent(string input, ref int current, [NotNullWhen(true)] out string? agent)
 320183        {
 320184            agent = null;
 185
 320186            int agentLength = HttpRuleParser.GetHostLength(input, current, true);
 320187            if (agentLength == 0)
 124188            {
 124189                return false;
 190            }
 191
 196192            agent = input.Substring(current, agentLength);
 196193            current += agentLength;
 194
 196195            int whitespaceLength = HttpRuleParser.GetWhitespaceLength(input, current);
 196196            current += whitespaceLength;
 197
 198            // At least one whitespace required after <agent>. Also make sure we have characters left for <text>
 196199            if ((whitespaceLength == 0) || (current == input.Length))
 40200            {
 40201                return false;
 202            }
 203
 156204            return true;
 320205        }
 206
 207        private static bool TryReadCode(string input, ref int current, out int code)
 460208        {
 460209            code = 0;
 460210            int codeLength = HttpRuleParser.GetNumberLength(input, current, false);
 211
 212            // code must be a 3 digit value. We accept less digits, but we don't accept more.
 460213            if ((codeLength == 0) || (codeLength > 3))
 32214            {
 32215                return false;
 216            }
 217
 428218            if (!HeaderUtilities.TryParseInt32(input, current, codeLength, out code))
 0219            {
 0220                Debug.Fail("Unable to parse value even though it was parsed as <=3 digits string. Input: '" +
 0221                    input + "', Current: " + current + ", CodeLength: " + codeLength);
 222                return false;
 223            }
 224
 428225            current += codeLength;
 226
 428227            int whitespaceLength = HttpRuleParser.GetWhitespaceLength(input, current);
 428228            current += whitespaceLength;
 229
 230            // Make sure the number is followed by at least one whitespace and that we have characters left to parse.
 428231            if ((whitespaceLength == 0) || (current == input.Length))
 108232            {
 108233                return false;
 234            }
 235
 320236            return true;
 460237        }
 238
 239        private static bool TryReadDate(string input, ref int current, out DateTimeOffset? date)
 0240        {
 0241            date = null;
 242
 243            // Make sure we have at least one whitespace between <text> and <date> (if we have <date>)
 0244            int whitespaceLength = HttpRuleParser.GetWhitespaceLength(input, current);
 0245            current += whitespaceLength;
 246
 247            // Read <date> in '<code> <agent> <text> ["<date>"]'
 0248            if ((current < input.Length) && (input[current] == '"'))
 0249            {
 0250                if (whitespaceLength == 0)
 0251                {
 0252                    return false; // we have characters after <text> but they were not separated by a whitespace
 253                }
 254
 0255                current++; // skip opening '"'
 256
 257                // Find the closing '"'
 0258                int dateStartIndex = current;
 0259                int quote = input.AsSpan(current).IndexOf('"');
 0260                if (quote <= 0) // no quote was found or it was the first character (meaning an empty quoted string)
 0261                {
 0262                    return false;
 263                }
 0264                current += quote;
 265
 0266                if (!HttpDateParser.TryParse(input.AsSpan(dateStartIndex, current - dateStartIndex), out DateTimeOffset 
 0267                {
 0268                    return false;
 269                }
 270
 0271                date = temp;
 272
 0273                current++; // skip closing '"'
 0274                current += HttpRuleParser.GetWhitespaceLength(input, current);
 0275            }
 276
 0277            return true;
 0278        }
 279
 280        object ICloneable.Clone()
 0281        {
 0282            return new WarningHeaderValue(this);
 0283        }
 284
 285        private static void CheckCode(int code)
 0286        {
 0287            ArgumentOutOfRangeException.ThrowIfNegative(code);
 0288            ArgumentOutOfRangeException.ThrowIfGreaterThan(code, 999);
 0289        }
 290
 291        private static void CheckAgent(string agent)
 0292        {
 0293            ArgumentException.ThrowIfNullOrEmpty(agent);
 294
 295            // 'Agent' can either be a host or a token. Since a token is a valid host, we only verify if the value
 296            // is a valid host.
 0297            if (HttpRuleParser.GetHostLength(agent, 0, true) != agent.Length)
 0298            {
 0299                throw new FormatException(SR.Format(SR.net_http_headers_invalid_value, agent));
 300            }
 0301        }
 302    }
 303}
 304