diff --git a/src/Microsoft.Net.Http.Headers/MediaTypeHeaderValue.cs b/src/Microsoft.Net.Http.Headers/MediaTypeHeaderValue.cs
index aebf80e00b..23ed8b0cec 100644
--- a/src/Microsoft.Net.Http.Headers/MediaTypeHeaderValue.cs
+++ b/src/Microsoft.Net.Http.Headers/MediaTypeHeaderValue.cs
@@ -219,6 +219,21 @@ namespace Microsoft.Net.Http.Headers
get { return _isReadOnly; }
}
+ ///
+ /// Gets a value indicating whether this is a subset of
+ /// . 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.
+ ///
+ /// The to compare.
+ ///
+ /// A value indicating whether this is a subset of
+ /// .
+ ///
+ ///
+ /// 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".
+ ///
public bool IsSubsetOf(MediaTypeHeaderValue otherMediaType)
{
if (otherMediaType == null)
@@ -226,6 +241,7 @@ namespace Microsoft.Net.Http.Headers
return false;
}
+ // "text/plain" is a subset of "text/plain", "text/*" and "*/*". "*/*" is a subset only of "*/*".
if (!Type.Equals(otherMediaType.Type, StringComparison.OrdinalIgnoreCase))
{
if (!otherMediaType.MatchesAllTypes)
@@ -241,22 +257,29 @@ namespace Microsoft.Net.Http.Headers
}
}
- if (Parameters != null)
+ // "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)
{
- if (Parameters.Count != 0 && (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)
{
- return false;
- }
-
- // Make sure all parameters listed locally are listed in the other one. The other one may have additional parameters.
- foreach (var param in _parameters)
- {
- var otherParam = NameValueHeaderValue.Find(otherMediaType._parameters, param.Name);
- if (otherParam == null)
+ 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(param.Value, otherParam.Value, StringComparison.OrdinalIgnoreCase))
+
+ if (!string.Equals(parameter.Value, localParameter.Value, StringComparison.OrdinalIgnoreCase))
{
return false;
}
@@ -364,7 +387,7 @@ namespace Microsoft.Net.Http.Headers
return 0;
}
- // Caller must remove leading whitespaces. If not, we'll return 0.
+ // Caller must remove leading whitespace. If not, we'll return 0.
string mediaType = null;
var mediaTypeLength = MediaTypeHeaderValue.GetMediaTypeExpressionLength(input, startIndex, out mediaType);
@@ -432,7 +455,7 @@ namespace Microsoft.Net.Http.Headers
return 0;
}
- // If there are no whitespaces between and in / get the media type using
+ // If there is no whitespace between and in / get the media type using
// one Substring call. Otherwise get substrings for and and combine them.
var mediatTypeLength = current + subtypeLength - startIndex;
if (typeLength + subtypeLength + 1 == mediatTypeLength)
@@ -454,8 +477,8 @@ namespace Microsoft.Net.Http.Headers
throw new ArgumentException("An empty string is not allowed.", parameterName);
}
- // When adding values using strongly typed objects, no leading/trailing LWS (whitespaces) are allowed.
- // Also no LWS between type and subtype are allowed.
+ // When adding values using strongly typed objects, no leading/trailing LWS (whitespace) is allowed.
+ // Also no LWS between type and subtype is allowed.
string tempMediaType;
var mediaTypeLength = GetMediaTypeExpressionLength(mediaType, 0, out tempMediaType);
if ((mediaTypeLength == 0) || (tempMediaType.Length != mediaType.Length))
diff --git a/test/Microsoft.Extensions.BufferedHtmlContent.Test/Microsoft.Extensions.BufferedHtmlContent.Test.xproj b/test/Microsoft.Extensions.BufferedHtmlContent.Test/Microsoft.Extensions.BufferedHtmlContent.Test.xproj
index a091460b76..f0c4c7f35a 100644
--- a/test/Microsoft.Extensions.BufferedHtmlContent.Test/Microsoft.Extensions.BufferedHtmlContent.Test.xproj
+++ b/test/Microsoft.Extensions.BufferedHtmlContent.Test/Microsoft.Extensions.BufferedHtmlContent.Test.xproj
@@ -1,4 +1,4 @@
-
+
14.0
@@ -11,9 +11,11 @@
..\..\artifacts\obj\$(MSBuildProjectName)
..\..\artifacts\bin\$(MSBuildProjectName)\
-
2.0
+
+
+
\ No newline at end of file
diff --git a/test/Microsoft.Net.Http.Headers.Tests/MediaTypeHeaderValueTest.cs b/test/Microsoft.Net.Http.Headers.Tests/MediaTypeHeaderValueTest.cs
index 9ca74a3632..48a4bfab12 100644
--- a/test/Microsoft.Net.Http.Headers.Tests/MediaTypeHeaderValueTest.cs
+++ b/test/Microsoft.Net.Http.Headers.Tests/MediaTypeHeaderValueTest.cs
@@ -542,13 +542,16 @@ namespace Microsoft.Net.Http.Headers
[Theory]
[InlineData("*/*;", "*/*")]
- [InlineData("text/*;", "text/*")]
+ [InlineData("text/*", "text/*")]
+ [InlineData("text/*;", "*/*")]
[InlineData("text/plain;", "text/plain")]
- [InlineData("*/*;", "*/*;charset=utf-8;")]
- [InlineData("text/*;", "*/*;charset=utf-8;")]
- [InlineData("text/plain;", "*/*;charset=utf-8;")]
- [InlineData("text/plain;", "text/*;charset=utf-8;")]
- [InlineData("text/plain;", "text/plain;charset=utf-8;")]
+ [InlineData("text/plain", "text/*")]
+ [InlineData("text/plain;", "*/*")]
+ [InlineData("*/*;missingparam=4", "*/*")]
+ [InlineData("text/*;missingparam=4;", "*/*;")]
+ [InlineData("text/plain;missingparam=4", "*/*;")]
+ [InlineData("text/plain;missingparam=4", "text/*")]
+ [InlineData("text/plain;charset=utf-8", "text/plain;charset=utf-8")]
[InlineData("text/plain;version=v1", "Text/plain;Version=v1")]
[InlineData("text/plain;version=v1", "tExT/plain;version=V1")]
[InlineData("text/plain;version=v1", "TEXT/PLAIN;VERSION=V1")]
@@ -558,26 +561,38 @@ namespace Microsoft.Net.Http.Headers
[InlineData("text/plain;charset=utf-8;foo=bar;q=0.0", "*/*;charset=utf-8;foo=bar;q=0.0")]
public void IsSubsetOf_PositiveCases(string mediaType1, string mediaType2)
{
+ // Arrange
var parsedMediaType1 = MediaTypeHeaderValue.Parse(mediaType1);
var parsedMediaType2 = MediaTypeHeaderValue.Parse(mediaType2);
+ // Act
var isSubset = parsedMediaType1.IsSubsetOf(parsedMediaType2);
+
+ // Assert
Assert.True(isSubset);
}
[Theory]
+ [InlineData("application/html", "text/*")]
+ [InlineData("application/json", "application/html")]
[InlineData("text/plain;version=v1", "text/plain;version=")]
[InlineData("*/*;", "text/plain;charset=utf-8;foo=bar;q=0.0")]
[InlineData("text/*;", "text/plain;charset=utf-8;foo=bar;q=0.0")]
- [InlineData("text/plain;missingparam=4;", "text/plain;charset=utf-8;foo=bar;q=0.0")]
- [InlineData("text/plain;missingparam=4;", "text/*;charset=utf-8;foo=bar;q=0.0")]
- [InlineData("text/plain;missingparam=4;", "*/*;charset=utf-8;foo=bar;q=0.0")]
+ [InlineData("text/*;charset=utf-8;foo=bar;q=0.0", "text/plain;missingparam=4;")]
+ [InlineData("*/*;charset=utf-8;foo=bar;q=0.0", "text/plain;missingparam=4;")]
+ [InlineData("text/plain;charset=utf-8;foo=bar;q=0.0", "text/plain;missingparam=4;")]
+ [InlineData("text/plain;charset=utf-8;foo=bar;q=0.0", "text/*;missingparam=4;")]
+ [InlineData("text/plain;charset=utf-8;foo=bar;q=0.0", "*/*;missingparam=4;")]
public void IsSubsetOf_NegativeCases(string mediaType1, string mediaType2)
{
+ // Arrange
var parsedMediaType1 = MediaTypeHeaderValue.Parse(mediaType1);
var parsedMediaType2 = MediaTypeHeaderValue.Parse(mediaType2);
+ // Act
var isSubset = parsedMediaType1.IsSubsetOf(parsedMediaType2);
+
+ // Assert
Assert.False(isSubset);
}