private static bool TrySetOptionalTokenList(NameValueHeaderValue nameValue, ref bool boolField,
                                                    ref ObjectCollection <string>?destination)
        {
            Debug.Assert(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 whitespace.
            string 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 ','.
            int  current            = 1;                      // skip the initial '"' character.
            int  maxLength          = valueString.Length - 1; // -1 because we don't want to parse the final '"'.
            bool separatorFound     = false;
            int  originalValueCount = destination == null ? 0 : destination.Count;

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

                if (current == maxLength)
                {
                    break;
                }

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

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

                destination ??= new ObjectCollection <string>(s_checkIsValidToken);
                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 bool TryGetParametersEndIndex(string input, ref int parseEndIndex, ref int parameterEndIndex)
        {
            Debug.Assert(parseEndIndex < input.Length, "Expected string to have at least 1 char");
            Debug.Assert(input[parseEndIndex] == ',');

            int current = parseEndIndex;

            do
            {
                current++;                   // skip ',' delimiter

                bool separatorFound = false; // ignore value returned by GetNextNonEmptyOrWhitespaceIndex()
                current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(input, current, true, out separatorFound);
                if (current == input.Length)
                {
                    return(true);
                }

                // Now we have to determine if after ',' we have a list of <name>=<value> pairs that are part of
                // the auth scheme parameters OR if we have another auth scheme. Either way, after ',' we expect a
                // valid token that is either the <name> in a <name>=<value> pair OR <scheme> of another scheme.
                int tokenLength = HttpRuleParser.GetTokenLength(input, current);
                if (tokenLength == 0)
                {
                    return(false);
                }

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

                // If we reached the end of the string or the token is followed by anything but '=', then the parsed
                // token is another scheme name. The string representing parameters ends before the token (e.g.
                // "Digest a=b, c=d, NTLM": return scheme "Digest" with parameters string "a=b, c=d").
                if ((current == input.Length) || (input[current] != '='))
                {
                    return(true);
                }

                current++; // skip '=' delimiter
                current = current + HttpRuleParser.GetWhitespaceLength(input, current);
                int valueLength = NameValueHeaderValue.GetValueLength(input, current);

                // After '<name>=' we expect a valid <value> (either token or quoted string)
                if (valueLength == 0)
                {
                    return(false);
                }

                // Update parameter end index, since we just parsed a valid <name>=<value> pair that is part of the
                // parameters string.
                current           = current + valueLength;
                parameterEndIndex = current - 1; // -1 because 'current' already points to the char after <value>
                current           = current + HttpRuleParser.GetWhitespaceLength(input, current);
                parseEndIndex     = current;     // this essentially points to parameterEndIndex + whitespace + next char
            } while ((current < input.Length) && (input[current] == ','));

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

            // If multiple values are supported (i.e. list of values), then accept an empty string: The header may
            // be added multiple times to the request/response message. E.g.
            //  Accept: text/xml; q=1
            //  Accept:
            //  Accept: text/plain; q=0.2
            if (string.IsNullOrEmpty(value) || (index == value.Length))
            {
                return(SupportsMultipleValues);
            }

            bool separatorFound = false;
            int  current        = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(value, index, SupportsMultipleValues,
                                                                                   out separatorFound);

            if (separatorFound && !SupportsMultipleValues)
            {
                return(false); // leading separators not allowed if we don't support multiple values.
            }

            if (current == value.Length)
            {
                if (SupportsMultipleValues)
                {
                    index = current;
                }
                return(SupportsMultipleValues);
            }

            object result = null;
            int    length = GetParsedValueLength(value, current, storeValue, out result);

            if (length == 0)
            {
                return(false);
            }

            current = current + length;
            current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(value, current, SupportsMultipleValues,
                                                                       out separatorFound);

            // If we support multiple values and we've not reached the end of the string, then we must have a separator.
            if ((separatorFound && !SupportsMultipleValues) || (!separatorFound && (current < value.Length)))
            {
                return(false);
            }

            index       = current;
            parsedValue = result;
            return(true);
        }
        // Returns the length of a range list. E.g. "1-2, 3-4, 5-6" adds 3 ranges to 'rangeCollection'. Note that empty
        // list segments are allowed, e.g. ",1-2, , 3-4,,".
        internal static int GetRangeItemListLength(string input, int startIndex,
                                                   ICollection <RangeItemHeaderValue> rangeCollection)
        {
            Debug.Assert(rangeCollection != null);
            Debug.Assert(startIndex >= 0);
            Contract.Ensures((Contract.Result <int>() == 0) || (rangeCollection.Count > 0),
                             "If we can parse the string, then we expect to have at least one range item.");

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

            // Empty segments are allowed, so skip all delimiter-only segments (e.g. ", ,").
            bool separatorFound = false;
            int  current        = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(input, startIndex, true, out separatorFound);

            // It's OK if we didn't find leading separator characters. Ignore 'separatorFound'.

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

            RangeItemHeaderValue range = null;

            while (true)
            {
                int rangeLength = GetRangeItemLength(input, current, out range);

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

                rangeCollection.Add(range);

                current = current + rangeLength;
                current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(input, current, true, out separatorFound);

                // If the string is not consumed, we must have a delimiter, otherwise the string is not a valid
                // range list.
                if ((current < input.Length) && !separatorFound)
                {
                    return(0);
                }

                if (current == input.Length)
                {
                    return(current - startIndex);
                }
            }
        }
        // Returns the length of a range list. E.g. "1-2, 3-4, 5-6" adds 3 ranges to 'rangeCollection'. Note that empty
        // list segments are allowed, e.g. ",1-2, , 3-4,,".
        internal static int GetRangeItemListLength(string?input, int startIndex,
                                                   ICollection <RangeItemHeaderValue> rangeCollection)
        {
            Debug.Assert(rangeCollection != null);
            Debug.Assert(startIndex >= 0);

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

            // Empty segments are allowed, so skip all delimiter-only segments (e.g. ", ,").
            int current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(input, startIndex, true, out _);

            // It's OK if we didn't find leading separator characters. Ignore 'separatorFound'.

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

            RangeItemHeaderValue?range;

            while (true)
            {
                int rangeLength = GetRangeItemLength(input, current, out range);

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

                rangeCollection.Add(range !);

                current += rangeLength;
                current  = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(input, current, true, out bool separatorFound);

                // If the string is not consumed, we must have a delimiter, otherwise the string is not a valid
                // range list.
                if ((current < input.Length) && !separatorFound)
                {
                    return(0);
                }

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