@@ -219,13 +219,29 @@ public bool IsReadOnly
219
219
get { return _isReadOnly ; }
220
220
}
221
221
222
+ /// <summary>
223
+ /// Gets a value indicating whether this <see cref="MediaTypeHeaderValue"/> is a subset of
224
+ /// <paramref name="otherMediaType"/>. A "subset" is defined as the same or a more specific media type
225
+ /// according to the precedence described in https://www.ietf.org/rfc/rfc2068.txt section 14.1, Accept.
226
+ /// </summary>
227
+ /// <param name="otherMediaType">The <see cref="MediaTypeHeaderValue"/> to compare.</param>
228
+ /// <returns>
229
+ /// A value indicating whether this <see cref="MediaTypeHeaderValue"/> is a subset of
230
+ /// <paramref name="otherMediaType"/>.
231
+ /// </returns>
232
+ /// <remarks>
233
+ /// For example "multipart/mixed; boundary=1234" is a subset of "multipart/mixed; boundary=1234",
234
+ /// "multipart/mixed", "multipart/*", and "*/*" but not "multipart/mixed; boundary=2345" or
235
+ /// "multipart/message; boundary=1234".
236
+ /// </remarks>
222
237
public bool IsSubsetOf ( MediaTypeHeaderValue otherMediaType )
223
238
{
224
239
if ( otherMediaType == null )
225
240
{
226
241
return false ;
227
242
}
228
243
244
+ // "text/plain" is a subset of "text/plain", "text/*" and "*/*". "*/*" is a subset only of "*/*".
229
245
if ( ! Type . Equals ( otherMediaType . Type , StringComparison . OrdinalIgnoreCase ) )
230
246
{
231
247
if ( ! otherMediaType . MatchesAllTypes )
@@ -241,22 +257,29 @@ public bool IsSubsetOf(MediaTypeHeaderValue otherMediaType)
241
257
}
242
258
}
243
259
244
- if ( Parameters != null )
260
+ // "text/plain; charset=utf-8; level=1" is a subset of "text/plain; charset=utf-8". In turn
261
+ // "text/plain; charset=utf-8" is a subset of "text/plain".
262
+ if ( otherMediaType . _parameters != null && otherMediaType . _parameters . Count != 0 )
245
263
{
246
- if ( Parameters . Count != 0 && ( otherMediaType . Parameters == null || otherMediaType . Parameters . Count == 0 ) )
264
+ // Make sure all parameters in the potential superset are included locally. Fine to have additional
265
+ // parameters locally; they make this one more specific.
266
+ foreach ( var parameter in otherMediaType . _parameters )
247
267
{
248
- return false ;
249
- }
268
+ if ( string . Equals ( parameter . Name , "q" , StringComparison . OrdinalIgnoreCase ) )
269
+ {
270
+ // "q" and later parameters are not involved in media type matching. Quoting the RFC: The first
271
+ // "q" parameter (if any) separates the media-range parameter(s) from the accept-params.
272
+ break ;
273
+ }
250
274
251
- // Make sure all parameters listed locally are listed in the other one. The other one may have additional parameters.
252
- foreach ( var param in _parameters )
253
- {
254
- var otherParam = NameValueHeaderValue . Find ( otherMediaType . _parameters , param . Name ) ;
255
- if ( otherParam == null )
275
+ var localParameter = NameValueHeaderValue . Find ( _parameters , parameter . Name ) ;
276
+ if ( localParameter == null )
256
277
{
278
+ // Not found.
257
279
return false ;
258
280
}
259
- if ( ! string . Equals ( param . Value , otherParam . Value , StringComparison . OrdinalIgnoreCase ) )
281
+
282
+ if ( ! string . Equals ( parameter . Value , localParameter . Value , StringComparison . OrdinalIgnoreCase ) )
260
283
{
261
284
return false ;
262
285
}
@@ -364,7 +387,7 @@ private static int GetMediaTypeLength(string input, int startIndex, out MediaTyp
364
387
return 0 ;
365
388
}
366
389
367
- // Caller must remove leading whitespaces . If not, we'll return 0.
390
+ // Caller must remove leading whitespace . If not, we'll return 0.
368
391
string mediaType = null ;
369
392
var mediaTypeLength = MediaTypeHeaderValue . GetMediaTypeExpressionLength ( input , startIndex , out mediaType ) ;
370
393
@@ -432,7 +455,7 @@ private static int GetMediaTypeExpressionLength(string input, int startIndex, ou
432
455
return 0 ;
433
456
}
434
457
435
- // If there are no whitespaces between <type> and <subtype> in <type>/<subtype> get the media type using
458
+ // If there is no whitespace between <type> and <subtype> in <type>/<subtype> get the media type using
436
459
// one Substring call. Otherwise get substrings for <type> and <subtype> and combine them.
437
460
var mediatTypeLength = current + subtypeLength - startIndex ;
438
461
if ( typeLength + subtypeLength + 1 == mediatTypeLength )
@@ -454,8 +477,8 @@ private static void CheckMediaTypeFormat(string mediaType, string parameterName)
454
477
throw new ArgumentException ( "An empty string is not allowed." , parameterName ) ;
455
478
}
456
479
457
- // When adding values using strongly typed objects, no leading/trailing LWS (whitespaces) are allowed.
458
- // Also no LWS between type and subtype are allowed.
480
+ // When adding values using strongly typed objects, no leading/trailing LWS (whitespace) is allowed.
481
+ // Also no LWS between type and subtype is allowed.
459
482
string tempMediaType ;
460
483
var mediaTypeLength = GetMediaTypeExpressionLength ( mediaType , 0 , out tempMediaType ) ;
461
484
if ( ( mediaTypeLength == 0 ) || ( tempMediaType . Length != mediaType . Length ) )
0 commit comments