internal static void SetQuality(ICollection <NameValueHeaderValue> parameters, double?value) { Contract.Requires(parameters != null); var qualityParameter = NameValueHeaderValue.Find(parameters, QualityName); if (value.HasValue) { // Note that even if we check the value here, we can't prevent a user from adding an invalid quality // value using Parameters.Add(). Even if we would prevent the user from adding an invalid value // using Parameters.Add() he could always add invalid values using HttpHeaders.AddWithoutValidation(). // So this check is really for convenience to show users that they're trying to add an invalid // value. if ((value < 0) || (value > 1)) { throw new ArgumentOutOfRangeException(nameof(value)); } var qualityString = ((double)value).ToString("0.0##", NumberFormatInfo.InvariantInfo); if (qualityParameter != null) { qualityParameter.Value = qualityString; } else { parameters.Add(new NameValueHeaderValue(QualityName, qualityString)); } } else { // Remove quality parameter if (qualityParameter != null) { parameters.Remove(qualityParameter); } } }
/// <summary> /// Gets a value indicating whether this <see cref="MediaTypeHeaderValue"/> is a subset of /// <paramref name="otherMediaType"/>. A "subset" is defined as the same or a more specific media type /// according to the precedence described in https://www.ietf.org/rfc/rfc2068.txt section 14.1, Accept. /// </summary> /// <param name="otherMediaType">The <see cref="MediaTypeHeaderValue"/> to compare.</param> /// <returns> /// A value indicating whether this <see cref="MediaTypeHeaderValue"/> is a subset of /// <paramref name="otherMediaType"/>. /// </returns> /// <remarks> /// For example "multipart/mixed; boundary=1234" is a subset of "multipart/mixed; boundary=1234", /// "multipart/mixed", "multipart/*", and "*/*" but not "multipart/mixed; boundary=2345" or /// "multipart/message; boundary=1234". /// </remarks> public bool IsSubsetOf(MediaTypeHeaderValue otherMediaType) { if (otherMediaType == null) { return(false); } // PERF: Avoid doing anything here that allocates a substring, this is a very hot path // for content-negotiation. var indexOfSlash = _mediaType.IndexOf('/'); // "text/plain" is a subset of "text/plain", "text/*" and "*/*". "*/*" is a subset only of "*/*". if (string.Compare( strA: _mediaType, indexA: 0, strB: otherMediaType._mediaType, indexB: 0, length: indexOfSlash, comparisonType: StringComparison.OrdinalIgnoreCase) != 0) { if (!otherMediaType.MatchesAllTypes) { return(false); } } else if (string.Compare( strA: MediaType, indexA: indexOfSlash + 1, strB: otherMediaType._mediaType, indexB: indexOfSlash + 1, // We know the Type is equal, so the index of '/' is the same in both strings. length: _mediaType.Length - indexOfSlash, comparisonType: StringComparison.OrdinalIgnoreCase) != 0) { if (!otherMediaType.MatchesAllSubTypes) { return(false); } } // "text/plain; charset=utf-8; level=1" is a subset of "text/plain; charset=utf-8". In turn // "text/plain; charset=utf-8" is a subset of "text/plain". if (otherMediaType._parameters != null && otherMediaType._parameters.Count != 0) { // Make sure all parameters in the potential superset are included locally. Fine to have additional // parameters locally; they make this one more specific. foreach (var parameter in otherMediaType._parameters) { if (string.Equals(parameter.Name, "q", StringComparison.OrdinalIgnoreCase)) { // "q" and later parameters are not involved in media type matching. Quoting the RFC: The first // "q" parameter (if any) separates the media-range parameter(s) from the accept-params. break; } var localParameter = NameValueHeaderValue.Find(_parameters, parameter.Name); if (localParameter == null) { // Not found. return(false); } if (!string.Equals(parameter.Value, localParameter.Value, StringComparison.OrdinalIgnoreCase)) { return(false); } } } return(true); }