| File: System\Net\Http\Headers\RetryConditionHeaderValue.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.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; namespace System.Net.Http.Headers { public class RetryConditionHeaderValue : ICloneable { private const long DeltaNotSetTicksSentinel = long.MaxValue; // Only one of date and delta may be set. private readonly DateTimeOffset _date; private readonly TimeSpan _delta; public DateTimeOffset? Date => _delta.Ticks == DeltaNotSetTicksSentinel ? _date : null; public TimeSpan? Delta => _delta.Ticks == DeltaNotSetTicksSentinel ? null : _delta; public RetryConditionHeaderValue(DateTimeOffset date) { _date = date; _delta = new TimeSpan(DeltaNotSetTicksSentinel); } public RetryConditionHeaderValue(TimeSpan delta) { // The amount of seconds for 'delta' must be in the range 0..2^31 ArgumentOutOfRangeException.ThrowIfGreaterThan(delta.TotalSeconds, int.MaxValue); _delta = delta; } private RetryConditionHeaderValue(RetryConditionHeaderValue source) { Debug.Assert(source != null); _delta = source._delta; _date = source._date; } public override string ToString() => _delta.Ticks != DeltaNotSetTicksSentinel ? ((int)_delta.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo) : _date.ToString("r"); public override bool Equals([NotNullWhen(true)] object? obj) => obj is RetryConditionHeaderValue other && _delta == other._delta && _date == other._date; public override int GetHashCode() => HashCode.Combine(_delta, _date); public static RetryConditionHeaderValue Parse(string input) { int index = 0; return (RetryConditionHeaderValue)GenericHeaderParser.RetryConditionParser.ParseValue( input, null, ref index); } public static bool TryParse([NotNullWhen(true)] string? input, [NotNullWhen(true)] out RetryConditionHeaderValue? parsedValue) { int index = 0; parsedValue = null; if (GenericHeaderParser.RetryConditionParser.TryParseValue(input, null, ref index, out object? output)) { parsedValue = (RetryConditionHeaderValue)output!; return true; } return false; } internal static int GetRetryConditionLength(string? input, int startIndex, out object? parsedValue) { Debug.Assert(startIndex >= 0); parsedValue = null; if (string.IsNullOrEmpty(input) || (startIndex >= input.Length)) { return 0; } int current = startIndex; // Caller must remove leading whitespace. DateTimeOffset date = DateTimeOffset.MinValue; int deltaSeconds = -1; // use -1 to indicate that the value was not set. 'delta' values are always >=0 // We either have a timespan or a date/time value. Determine which one we have by looking at the first char. // If it is a number, we have a timespan, otherwise we assume we have a date. char firstChar = input[current]; if (char.IsAsciiDigit(firstChar)) { int deltaStartIndex = current; int deltaLength = HttpRuleParser.GetNumberLength(input, current, false); // The value must be in the range 0..2^31 if ((deltaLength == 0) || (deltaLength > HttpRuleParser.MaxInt32Digits)) { return 0; } current += deltaLength; current += HttpRuleParser.GetWhitespaceLength(input, current); // RetryConditionHeaderValue only allows 1 value. There must be no delimiter/other chars after 'delta' if (current != input.Length) { return 0; } if (!HeaderUtilities.TryParseInt32(input, deltaStartIndex, deltaLength, out deltaSeconds)) { return 0; // int.TryParse() may return 'false' if the value has 10 digits and is > Int32.MaxValue. } } else { if (!HttpDateParser.TryParse(input.AsSpan(current), out date)) { return 0; } // If we got a valid date, then the parser consumed the whole string (incl. trailing whitespace). current = input.Length; } if (deltaSeconds == -1) // we didn't change delta, so we must have found a date. { parsedValue = new RetryConditionHeaderValue(date); } else { parsedValue = new RetryConditionHeaderValue(new TimeSpan(0, 0, deltaSeconds)); } return current - startIndex; } object ICloneable.Clone() { return new RetryConditionHeaderValue(this); } } }