public override bool TryParseValue(string value, object storeValue, ref int index, out object parsedValue)
        {
            parsedValue = null;

            // Some headers support empty/null values. This one doesn't.
            if (string.IsNullOrEmpty(value) || (index == value.Length))
            {
                return(false);
            }

            string dateString = value;

            if (index > 0)
            {
                dateString = value.Substring(index);
            }

            DateTimeOffset date;

            if (!HttpRuleParser.TryStringToDate(dateString, out date))
            {
                return(false);
            }

            index       = value.Length;
            parsedValue = date;
            return(true);
        }
Exemple #2
0
        public void TryStringToDate_UseInvalidDateTimeString_Throw()
        {
            DateTimeOffset result = DateTimeOffset.MinValue;

            Assert.False(HttpRuleParser.TryStringToDate("Sun, 06 Nov 1994 08:49:37 GMT invalid", out result));
            Assert.False(HttpRuleParser.TryStringToDate("Sun, 06 Nov 1994 08:49:37 GMT,", out result));
            Assert.False(HttpRuleParser.TryStringToDate(",Sun, 06 Nov 1994 08:49:37 GMT", out result));
        }
        private static bool TryReadDate(string input, ref int current, out DateTimeOffset?date)
        {
            date = null;

            // Make sure we have at least one whitespace between <text> and <date> (if we have <date>)
            int whitespaceLength = HttpRuleParser.GetWhitespaceLength(input, current);

            current = current + whitespaceLength;

            // Read <date> in '<code> <agent> <text> ["<date>"]'
            if ((current < input.Length) && (input[current] == '"'))
            {
                if (whitespaceLength == 0)
                {
                    return(false); // we have characters after <text> but they were not separated by a whitespace
                }

                current++; // skip opening '"'

                // Find the closing '"'
                int dateStartIndex = current;
                while (current < input.Length)
                {
                    if (input[current] == '"')
                    {
                        break;
                    }
                    current++;
                }

                if ((current == input.Length) || (current == dateStartIndex))
                {
                    return(false); // we couldn't find the closing '"' or we have an empty quoted string.
                }

                DateTimeOffset temp;
                if (!HttpRuleParser.TryStringToDate(input.Substring(dateStartIndex, current - dateStartIndex), out temp))
                {
                    return(false);
                }

                date = temp;

                current++; // skip closing '"'
                current = current + HttpRuleParser.GetWhitespaceLength(input, current);
            }

            return(true);
        }
Exemple #4
0
        public void TryStringToDate_UseOfValidDateTimeStringsInDifferentFormats_ParsedCorrectly()
        {
            // We don't need extensive tests, since we let DateTimeOffset do the parsing. This test is just
            // to validate that we use the correct parameters when calling into DateTimeOffset.ToString().

            // RFC1123 date/time value
            DateTimeOffset expected = new DateTimeOffset(1994, 11, 6, 8, 49, 37, TimeSpan.Zero);
            DateTimeOffset result   = DateTimeOffset.MinValue;

            Assert.True(HttpRuleParser.TryStringToDate("Sun, 06 Nov 1994 08:49:37 GMT", out result));
            Assert.Equal(expected, result);
            Assert.True(HttpRuleParser.TryStringToDate("Sun, 06 Nov 1994 08:49:37", out result));
            Assert.Equal(expected, result);
            Assert.True(HttpRuleParser.TryStringToDate("6 Nov 1994 8:49:37 GMT", out result));
            Assert.Equal(expected, result);
            Assert.True(HttpRuleParser.TryStringToDate("6 Nov 1994 8:49:37", out result));
            Assert.Equal(expected, result);
            Assert.True(HttpRuleParser.TryStringToDate("Sun, 06 Nov 94 08:49:37", out result));
            Assert.Equal(expected, result);
            Assert.True(HttpRuleParser.TryStringToDate("6 Nov 94 8:49:37", out result));
            Assert.Equal(expected, result);

            // RFC850 date/time value
            Assert.True(HttpRuleParser.TryStringToDate("Sunday, 06-Nov-94 08:49:37 GMT", out result));
            Assert.Equal(expected, result);
            Assert.True(HttpRuleParser.TryStringToDate("Sunday, 6-Nov-94 8:49:37", out result));
            Assert.Equal(expected, result);

            // ANSI C's asctime() format
            Assert.True(HttpRuleParser.TryStringToDate("Sun Nov  06 08:49:37 1994", out result));
            Assert.Equal(expected, result);
            Assert.True(HttpRuleParser.TryStringToDate("Sun Nov  6 8:49:37 1994", out result));
            Assert.Equal(expected, result);

            // RFC5322 date/time
            expected = new DateTimeOffset(1997, 11, 8, 9, 55, 6, new TimeSpan(-6, 0, 0));
            Assert.True(HttpRuleParser.TryStringToDate("Sat, 08 Nov 1997 09:55:06 -0600", out result));
            Assert.Equal(expected, result);
            expected = new DateTimeOffset(1997, 11, 8, 9, 55, 6, TimeSpan.Zero);
            Assert.True(HttpRuleParser.TryStringToDate("8 Nov 1997 9:55:6", out result));
            Assert.Equal(expected, result);
            expected = new DateTimeOffset(1997, 11, 8, 9, 55, 6, new TimeSpan(2, 0, 0));
            Assert.True(HttpRuleParser.TryStringToDate("Sat, 8 Nov 1997 9:55:6 +0200", out result));
            Assert.Equal(expected, result);
        }
Exemple #5
0
        // Gets a parameter of the given name and attempts to extract a date.
        // Returns null if the parameter is not present or the format is incorrect.
        private DateTimeOffset?GetDate(string parameter)
        {
            NameValueHeaderValue dateParameter = NameValueHeaderValue.Find(_parameters, parameter);
            DateTimeOffset       date;

            if (dateParameter != null)
            {
                string dateString = dateParameter.Value;
                // Should have quotes, remove them.
                if (IsQuoted(dateString))
                {
                    dateString = dateString.Substring(1, dateString.Length - 2);
                }
                if (HttpRuleParser.TryStringToDate(dateString, out date))
                {
                    return(date);
                }
            }
            return(null);
        }
Exemple #6
0
    private static int GetRangeConditionLength(StringSegment input, int startIndex, out RangeConditionHeaderValue?parsedValue)
    {
        Contract.Requires(startIndex >= 0);

        parsedValue = null;

        // Make sure we have at least 2 characters
        if (StringSegment.IsNullOrEmpty(input) || (startIndex + 1 >= input.Length))
        {
            return(0);
        }

        var current = startIndex;

        // Caller must remove leading whitespaces.
        DateTimeOffset       date      = DateTimeOffset.MinValue;
        EntityTagHeaderValue?entityTag = null;

        // Entity tags are quoted strings optionally preceded by "W/". By looking at the first two character we
        // can determine whether the string is en entity tag or a date.
        var firstChar  = input[current];
        var secondChar = input[current + 1];

        if ((firstChar == '\"') || (((firstChar == 'w') || (firstChar == 'W')) && (secondChar == '/')))
        {
            // trailing whitespaces are removed by GetEntityTagLength()
            var entityTagLength = EntityTagHeaderValue.GetEntityTagLength(input, current, out entityTag);

            if (entityTagLength == 0)
            {
                return(0);
            }

            current = current + entityTagLength;

            // RangeConditionHeaderValue only allows 1 value. There must be no delimiter/other chars after an
            // entity tag.
            if (current != input.Length)
            {
                return(0);
            }
        }
        else
        {
            if (!HttpRuleParser.TryStringToDate(input.Subsegment(current), out date))
            {
                return(0);
            }

            // If we got a valid date, then the parser consumed the whole string (incl. trailing whitespaces).
            current = input.Length;
        }

        parsedValue = new RangeConditionHeaderValue();
        if (entityTag == null)
        {
            parsedValue._lastModified = date;
        }
        else
        {
            parsedValue._entityTag = entityTag;
        }

        return(current - startIndex);
    }
        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 ((firstChar >= '0') && (firstChar <= '9'))
            {
                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 = current + deltaLength;
                current = 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.Substring(deltaStartIndex, deltaLength), out deltaSeconds))
                {
                    return(0); // int.TryParse() may return 'false' if the value has 10 digits and is > Int32.MaxValue.
                }
            }
            else
            {
                if (!HttpRuleParser.TryStringToDate(input.Substring(current), out date))
                {
                    return(0);
                }

                // If we got a valid date, then the parser consumed the whole string (incl. trailing whitespace).
                current = input.Length;
            }

            RetryConditionHeaderValue result = new RetryConditionHeaderValue();

            if (deltaSeconds == -1) // we didn't change delta, so we must have found a date.
            {
                result._date = date;
            }
            else
            {
                result._delta = new TimeSpan(0, 0, deltaSeconds);
            }

            parsedValue = result;
            return(current - startIndex);
        }
Exemple #8
0
 public static bool TryParseDate(StringSegment input, out DateTimeOffset result)
 {
     return(HttpRuleParser.TryStringToDate(input, out result));
 }
Exemple #9
0
    // name=value; expires=Sun, 06 Nov 1994 08:49:37 GMT; max-age=86400; domain=domain1; path=path1; secure; samesite={Strict|Lax|None}; httponly
    private static int GetSetCookieLength(StringSegment input, int startIndex, out SetCookieHeaderValue?parsedValue)
    {
        Contract.Requires(startIndex >= 0);
        var offset = startIndex;

        parsedValue = null;

        if (StringSegment.IsNullOrEmpty(input) || (offset >= input.Length))
        {
            return(0);
        }

        var result = new SetCookieHeaderValue();

        // The caller should have already consumed any leading whitespace, commas, etc..

        // Name=value;

        // Name
        var itemLength = HttpRuleParser.GetTokenLength(input, offset);

        if (itemLength == 0)
        {
            return(0);
        }
        result._name = input.Subsegment(offset, itemLength);
        offset      += itemLength;

        // = (no spaces)
        if (!ReadEqualsSign(input, ref offset))
        {
            return(0);
        }

        // value or "quoted value"
        // The value may be empty
        result._value = CookieHeaderParserShared.GetCookieValue(input, ref offset);

        // *(';' SP cookie-av)
        while (offset < input.Length)
        {
            if (input[offset] == ',')
            {
                // Divider between headers
                break;
            }
            if (input[offset] != ';')
            {
                // Expecting a ';' between parameters
                return(0);
            }
            offset++;

            offset += HttpRuleParser.GetWhitespaceLength(input, offset);

            //  cookie-av = expires-av / max-age-av / domain-av / path-av / secure-av / samesite-av / httponly-av / extension-av
            itemLength = HttpRuleParser.GetTokenLength(input, offset);
            if (itemLength == 0)
            {
                // Trailing ';' or leading into garbage. Let the next parser fail.
                break;
            }
            var token = input.Subsegment(offset, itemLength);
            offset += itemLength;

            //  expires-av = "Expires=" sane-cookie-date
            if (StringSegment.Equals(token, ExpiresToken, StringComparison.OrdinalIgnoreCase))
            {
                // = (no spaces)
                if (!ReadEqualsSign(input, ref offset))
                {
                    return(0);
                }
                // We don't want to include comma, becouse date may contain it (eg. Sun, 06 Nov...)
                var            dateString = ReadToSemicolonOrEnd(input, ref offset, includeComma: false);
                DateTimeOffset expirationDate;
                if (!HttpRuleParser.TryStringToDate(dateString, out expirationDate))
                {
                    // Invalid expiration date, abort
                    return(0);
                }
                result.Expires = expirationDate;
            }
            // max-age-av = "Max-Age=" non-zero-digit *DIGIT
            else if (StringSegment.Equals(token, MaxAgeToken, StringComparison.OrdinalIgnoreCase))
            {
                // = (no spaces)
                if (!ReadEqualsSign(input, ref offset))
                {
                    return(0);
                }

                itemLength = HttpRuleParser.GetNumberLength(input, offset, allowDecimal: false);
                if (itemLength == 0)
                {
                    return(0);
                }
                var  numberString = input.Subsegment(offset, itemLength);
                long maxAge;
                if (!HeaderUtilities.TryParseNonNegativeInt64(numberString, out maxAge))
                {
                    // Invalid expiration date, abort
                    return(0);
                }
                result.MaxAge = TimeSpan.FromSeconds(maxAge);
                offset       += itemLength;
            }
            // domain-av = "Domain=" domain-value
            // domain-value = <subdomain> ; defined in [RFC1034], Section 3.5, as enhanced by [RFC1123], Section 2.1
            else if (StringSegment.Equals(token, DomainToken, StringComparison.OrdinalIgnoreCase))
            {
                // = (no spaces)
                if (!ReadEqualsSign(input, ref offset))
                {
                    return(0);
                }
                // We don't do any detailed validation on the domain.
                result.Domain = ReadToSemicolonOrEnd(input, ref offset);
            }
            // path-av = "Path=" path-value
            // path-value = <any CHAR except CTLs or ";">
            else if (StringSegment.Equals(token, PathToken, StringComparison.OrdinalIgnoreCase))
            {
                // = (no spaces)
                if (!ReadEqualsSign(input, ref offset))
                {
                    return(0);
                }
                // We don't do any detailed validation on the path.
                result.Path = ReadToSemicolonOrEnd(input, ref offset);
            }
            // secure-av = "Secure"
            else if (StringSegment.Equals(token, SecureToken, StringComparison.OrdinalIgnoreCase))
            {
                result.Secure = true;
            }
            // samesite-av = "SameSite=" samesite-value
            // samesite-value = "Strict" / "Lax" / "None"
            else if (StringSegment.Equals(token, SameSiteToken, StringComparison.OrdinalIgnoreCase))
            {
                if (!ReadEqualsSign(input, ref offset))
                {
                    result.SameSite = SameSiteMode.Unspecified;
                }
                else
                {
                    var enforcementMode = ReadToSemicolonOrEnd(input, ref offset);

                    if (StringSegment.Equals(enforcementMode, SameSiteStrictToken, StringComparison.OrdinalIgnoreCase))
                    {
                        result.SameSite = SameSiteMode.Strict;
                    }
                    else if (StringSegment.Equals(enforcementMode, SameSiteLaxToken, StringComparison.OrdinalIgnoreCase))
                    {
                        result.SameSite = SameSiteMode.Lax;
                    }
                    else if (StringSegment.Equals(enforcementMode, SameSiteNoneToken, StringComparison.OrdinalIgnoreCase))
                    {
                        result.SameSite = SameSiteMode.None;
                    }
                    else
                    {
                        result.SameSite = SameSiteMode.Unspecified;
                    }
                }
            }
            // httponly-av = "HttpOnly"
            else if (StringSegment.Equals(token, HttpOnlyToken, StringComparison.OrdinalIgnoreCase))
            {
                result.HttpOnly = true;
            }
            // extension-av = <any CHAR except CTLs or ";">
            else
            {
                var tokenStart = offset - itemLength;
                ReadToSemicolonOrEnd(input, ref offset, includeComma: true);
                result.Extensions.Add(input.Subsegment(tokenStart, offset - tokenStart));
            }
        }

        parsedValue = result;
        return(offset - startIndex);
    }