private static bool TryGetLengthLength(string input, ref int current, out int lengthLength)
        {
            lengthLength = 0;

            if (input[current] == '*')
            {
                current++;
            }
            else
            {
                // Parse length value: <length> in '<unit> <from>-<to>/<length>'
                lengthLength = HttpRuleParser.GetNumberLength(input, current, false);

                if ((lengthLength == 0) || (lengthLength > HttpRuleParser.MaxInt64Digits))
                {
                    return(false);
                }

                current = current + lengthLength;
            }

            current = current + HttpRuleParser.GetWhitespaceLength(input, current);
            return(true);
        }
        // Add the given parameter to the list. Remove if date is null.
        private void SetDate(string parameter, DateTimeOffset?date)
        {
            var dateParameter = NameValueHeaderValue.Find(_parameters, parameter);

            if (date == null)
            {
                // Remove parameter
                if (dateParameter != null)
                {
                    _parameters.Remove(dateParameter);
                }
            }
            else
            {
                // Must always be quoted
                var dateString = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", HttpRuleParser.DateToString(date.Value));
                if (dateParameter != null)
                {
                    dateParameter.Value = dateString;
                }
                else
                {
                    Parameters.Add(new NameValueHeaderValue(parameter, dateString));
                }
            }
        }
Exemplo n.º 3
0
 public static string FormatDate(DateTimeOffset dateTime)
 {
     return(HttpRuleParser.DateToString(dateTime));
 }
Exemplo n.º 4
0
 public static bool TryParseDate(string input, out DateTimeOffset result)
 {
     return(HttpRuleParser.TryStringToDate(input, out result));
 }
        private static bool TrySetOptionalTokenList(
            NameValueHeaderValue nameValue,
            ref bool boolField,
            ref ICollection <string> destination)
        {
            Contract.Requires(nameValue != null);

            if (nameValue.Value == null)
            {
                boolField = true;
                return(true);
            }

            // We need the string to be at least 3 chars long: 2x quotes and at least 1 character. Also make sure we
            // have a quoted string. Note that NameValueHeaderValue will never have leading/trailing whitespaces.
            var valueString = nameValue.Value;

            if ((valueString.Length < 3) || (valueString[0] != '\"') || (valueString[valueString.Length - 1] != '\"'))
            {
                return(false);
            }

            // We have a quoted string. Now verify that the string contains a list of valid tokens separated by ','.
            var current            = 1;                      // skip the initial '"' character.
            var maxLength          = valueString.Length - 1; // -1 because we don't want to parse the final '"'.
            var separatorFound     = false;
            var originalValueCount = destination == null ? 0 : destination.Count;

            while (current < maxLength)
            {
                current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(valueString, current, true,
                                                                           out separatorFound);

                if (current == maxLength)
                {
                    break;
                }

                var tokenLength = HttpRuleParser.GetTokenLength(valueString, current);

                if (tokenLength == 0)
                {
                    // We already skipped whitespaces and separators. If we don't have a token it must be an invalid
                    // character.
                    return(false);
                }

                if (destination == null)
                {
                    destination = new ObjectCollection <string>(CheckIsValidTokenAction);
                }

                destination.Add(valueString.Substring(current, tokenLength));

                current = current + tokenLength;
            }

            // After parsing a valid token list, we expect to have at least one value
            if ((destination != null) && (destination.Count > originalValueCount))
            {
                boolField = true;
                return(true);
            }

            return(false);
        }
        private static int GetContentRangeLength(string input, int startIndex, out ContentRangeHeaderValue parsedValue)
        {
            Contract.Requires(startIndex >= 0);

            parsedValue = null;

            if (string.IsNullOrEmpty(input) || (startIndex >= input.Length))
            {
                return(0);
            }

            // Parse the unit string: <unit> in '<unit> <from>-<to>/<length>'
            var unitLength = HttpRuleParser.GetTokenLength(input, startIndex);

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

            var unit            = input.Substring(startIndex, unitLength);
            var current         = startIndex + unitLength;
            var separatorLength = HttpRuleParser.GetWhitespaceLength(input, current);

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

            current = current + separatorLength;

            if (current == input.Length)
            {
                return(0);
            }

            // Read range values <from> and <to> in '<unit> <from>-<to>/<length>'
            var fromStartIndex = current;
            var fromLength     = 0;
            var toStartIndex   = 0;
            var toLength       = 0;

            if (!TryGetRangeLength(input, ref current, out fromLength, out toStartIndex, out toLength))
            {
                return(0);
            }

            // After the range is read we expect the length separator '/'
            if ((current == input.Length) || (input[current] != '/'))
            {
                return(0);
            }

            current++; // Skip '/' separator
            current = current + HttpRuleParser.GetWhitespaceLength(input, current);

            if (current == input.Length)
            {
                return(0);
            }

            // We may not have a length (e.g. 'bytes 1-2/*'). But if we do, parse the length now.
            var lengthStartIndex = current;
            var lengthLength     = 0;

            if (!TryGetLengthLength(input, ref current, out lengthLength))
            {
                return(0);
            }

            if (!TryCreateContentRange(input, unit, fromStartIndex, fromLength, toStartIndex, toLength,
                                       lengthStartIndex, lengthLength, out parsedValue))
            {
                return(0);
            }

            return(current - startIndex);
        }
Exemplo n.º 7
0
        private static int GetRangeConditionLength(string input, int startIndex, out RangeConditionHeaderValue parsedValue)
        {
            Contract.Requires(startIndex >= 0);

            parsedValue = null;

            // Make sure we have at least 2 characters
            if (string.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.Substring(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);
        }
Exemplo n.º 8
0
        // name=value; expires=Sun, 06 Nov 1994 08:49:37 GMT; max-age=86400; domain=domain1; path=path1; secure; httponly
        private static int GetSetCookieLength(string input, int startIndex, out SetCookieHeaderValue parsedValue)
        {
            Contract.Requires(startIndex >= 0);
            var offset = startIndex;

            parsedValue = null;

            if (string.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.Substring(offset, itemLength);
            offset      += itemLength;

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

            // value or "quoted value"
            // The value may be empty
            result._value = CookieHeaderValue.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 / 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.Substring(offset, itemLength);
                offset += itemLength;

                //  expires-av = "Expires=" sane-cookie-date
                if (string.Equals(token, ExpiresToken, StringComparison.OrdinalIgnoreCase))
                {
                    // = (no spaces)
                    if (!ReadEqualsSign(input, ref offset))
                    {
                        return(0);
                    }
                    var            dateString = ReadToSemicolonOrEnd(input, ref offset);
                    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 (string.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.Substring(offset, itemLength);
                    long maxAge;
                    if (!HeaderUtilities.TryParseInt64(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 (string.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 (string.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 (string.Equals(token, SecureToken, StringComparison.OrdinalIgnoreCase))
                {
                    result.Secure = true;
                }
                // httponly-av = "HttpOnly"
                else if (string.Equals(token, HttpOnlyToken, StringComparison.OrdinalIgnoreCase))
                {
                    result.HttpOnly = true;
                }
                // extension-av = <any CHAR except CTLs or ";">
                else
                {
                    // TODO: skip it? Store it in a list?
                }
            }

            parsedValue = result;
            return(offset - startIndex);
        }
Exemplo n.º 9
0
        internal static int GetRangeItemLength(string input, int startIndex, out RangeItemHeaderValue parsedValue)
        {
            Contract.Requires(startIndex >= 0);

            // This parser parses number ranges: e.g. '1-2', '1-', '-2'.

            parsedValue = null;

            if (string.IsNullOrEmpty(input) || (startIndex >= input.Length))
            {
                return(0);
            }

            // Caller must remove leading whitespaces. If not, we'll return 0.
            var current = startIndex;

            // Try parse the first value of a value pair.
            var fromStartIndex = current;
            var fromLength     = HttpRuleParser.GetNumberLength(input, current, false);

            if (fromLength > HttpRuleParser.MaxInt64Digits)
            {
                return(0);
            }

            current = current + fromLength;
            current = current + HttpRuleParser.GetWhitespaceLength(input, current);

            // Afer the first value, the '-' character must follow.
            if ((current == input.Length) || (input[current] != '-'))
            {
                // We need a '-' character otherwise this can't be a valid range.
                return(0);
            }

            current++; // skip the '-' character
            current = current + HttpRuleParser.GetWhitespaceLength(input, current);

            var toStartIndex = current;
            var toLength     = 0;

            // If we didn't reach the end of the string, try parse the second value of the range.
            if (current < input.Length)
            {
                toLength = HttpRuleParser.GetNumberLength(input, current, false);

                if (toLength > HttpRuleParser.MaxInt64Digits)
                {
                    return(0);
                }

                current = current + toLength;
                current = current + HttpRuleParser.GetWhitespaceLength(input, current);
            }

            if ((fromLength == 0) && (toLength == 0))
            {
                return(0); // At least one value must be provided in order to be a valid range.
            }

            // Try convert first value to int64
            long from = 0;

            if ((fromLength > 0) && !HeaderUtilities.TryParseInt64(input.Substring(fromStartIndex, fromLength), out from))
            {
                return(0);
            }

            // Try convert second value to int64
            long to = 0;

            if ((toLength > 0) && !HeaderUtilities.TryParseInt64(input.Substring(toStartIndex, toLength), out to))
            {
                return(0);
            }

            // 'from' must not be greater than 'to'
            if ((fromLength > 0) && (toLength > 0) && (from > to))
            {
                return(0);
            }

            parsedValue = new RangeItemHeaderValue((fromLength == 0 ? (long?)null : (long?)from),
                                                   (toLength == 0 ? (long?)null : (long?)to));
            return(current - startIndex);
        }
Exemplo n.º 10
0
        internal static int GetEntityTagLength(string input, int startIndex, out EntityTagHeaderValue parsedValue)
        {
            Contract.Requires(startIndex >= 0);

            parsedValue = null;

            if (string.IsNullOrEmpty(input) || (startIndex >= input.Length))
            {
                return(0);
            }

            // Caller must remove leading whitespaces. If not, we'll return 0.
            var isWeak  = false;
            var current = startIndex;

            var firstChar = input[startIndex];

            if (firstChar == '*')
            {
                // We have '*' value, indicating "any" ETag.
                parsedValue = Any;
                current++;
            }
            else
            {
                // The RFC defines 'W/' as prefix, but we'll be flexible and also accept lower-case 'w'.
                if ((firstChar == 'W') || (firstChar == 'w'))
                {
                    current++;
                    // We need at least 3 more chars: the '/' character followed by two quotes.
                    if ((current + 2 >= input.Length) || (input[current] != '/'))
                    {
                        return(0);
                    }
                    isWeak = true;
                    current++; // we have a weak-entity tag.
                    current = current + HttpRuleParser.GetWhitespaceLength(input, current);
                }

                var tagStartIndex = current;
                var tagLength     = 0;
                if (HttpRuleParser.GetQuotedStringLength(input, current, out tagLength) != HttpParseResult.Parsed)
                {
                    return(0);
                }

                parsedValue = new EntityTagHeaderValue();
                if (tagLength == input.Length)
                {
                    // Most of the time we'll have strong ETags without leading/trailing whitespaces.
                    Contract.Assert(startIndex == 0);
                    Contract.Assert(!isWeak);
                    parsedValue._tag    = input;
                    parsedValue._isWeak = false;
                }
                else
                {
                    parsedValue._tag    = input.Substring(tagStartIndex, tagLength);
                    parsedValue._isWeak = isWeak;
                }

                current = current + tagLength;
            }
            current = current + HttpRuleParser.GetWhitespaceLength(input, current);

            return(current - startIndex);
        }