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); } } }