< Summary

Class:Azure.Core.Http.Multipart.MediaTypeHeaderValue
Assembly:Azure.Storage.Blobs.Batch
File(s):C:\Git\azure-sdk-for-net\sdk\storage\Azure.Storage.Blobs.Batch\src\Shared\MediaTypeHeaderValue.cs
Covered lines:0
Uncovered lines:186
Coverable lines:186
Total lines:740
Line coverage:0% (0 of 186)
Covered branches:0
Total branches:98
Branch coverage:0% (0 of 98)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.cctor()-0%100%
.ctor()-0%100%
.ctor(...)-0%100%
.ctor(...)-0%100%
get_Charset()-0%0%
set_Charset(...)-0%0%
get_Encoding()-0%0%
set_Encoding(...)-0%0%
get_Boundary()-0%0%
set_Boundary(...)-0%0%
get_Parameters()-0%0%
get_Quality()-0%100%
set_Quality(...)-0%100%
get_MediaType()-0%100%
set_MediaType(...)-0%100%
get_Type()-0%100%
get_SubType()-0%100%
get_SubTypeWithoutSuffix()-0%0%
get_Suffix()-0%0%
get_Facets()-0%100%
get_MatchesAllTypes()-0%100%
get_MatchesAllSubTypes()-0%100%
get_MatchesAllSubTypesWithoutSuffix()-0%100%
get_IsReadOnly()-0%100%
IsSubsetOf(...)-0%0%
Copy()-0%0%
CopyAsReadOnly()-0%0%
ToString()-0%100%
Equals(...)-0%0%
GetHashCode()-0%100%
Parse(...)-0%100%
TryParse(...)-0%100%
ParseList(...)-0%100%
ParseStrictList(...)-0%100%
TryParseList(...)-0%100%
TryParseStrictList(...)-0%100%
GetMediaTypeLength(...)-0%0%
GetMediaTypeExpressionLength(...)-0%0%
CheckMediaTypeFormat(...)-0%0%
MatchesType(...)-0%0%
MatchesSubtype(...)-0%0%
MatchesSubtypeWithoutSuffix(...)-0%0%
MatchesEitherSubtypeOrSuffix(...)-0%0%
MatchesParameters(...)-0%0%
MatchesSubtypeSuffix(...)-0%100%

File(s)

C:\Git\azure-sdk-for-net\sdk\storage\Azure.Storage.Blobs.Batch\src\Shared\MediaTypeHeaderValue.cs

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License.
 3
 4// Copied from https://github.com/aspnet/AspNetCore/tree/master/src/Http/Headers/src
 5
 6using System;
 7using System.Collections.Generic;
 8using System.Diagnostics.Contracts;
 9using System.Globalization;
 10using System.Linq;
 11using System.Text;
 12
 13#pragma warning disable IDE0008 // Use explicit type
 14#pragma warning disable IDE0017 // initialization can be simplified
 15#pragma warning disable IDE0019 // Use pattern matching
 16#pragma warning disable IDE0032 // Use auto property
 17#pragma warning disable IDE0034 // Default expression can be simplified
 18#pragma warning disable IDE0054 // Use compound assignment
 19#pragma warning disable IDE0059 // Unnecessary assignment
 20#pragma warning disable IDE1006 // Missing s_ prefix
 21
 22namespace Azure.Core.Http.Multipart
 23{
 24    /// <summary>
 25    /// Representation of the media type header. See <see href="https://tools.ietf.org/html/rfc6838">Media Type Specific
 26    /// </summary>
 27    internal class MediaTypeHeaderValue
 28    {
 29        private const string BoundaryString = "boundary";
 30        private const string CharsetString = "charset";
 31        private const string MatchesAllString = "*/*";
 32        private const string QualityString = "q";
 33        private const string WildcardString = "*";
 34
 35        private const char ForwardSlashCharacter = '/';
 36        private const char PeriodCharacter = '.';
 37        private const char PlusCharacter = '+';
 38
 039        private static readonly char[] PeriodCharacterArray = new char[] { PeriodCharacter };
 40
 041        private static readonly HttpHeaderParser<MediaTypeHeaderValue> SingleValueParser
 042            = new GenericHeaderParser<MediaTypeHeaderValue>(false, GetMediaTypeLength);
 043        private static readonly HttpHeaderParser<MediaTypeHeaderValue> MultipleValueParser
 044            = new GenericHeaderParser<MediaTypeHeaderValue>(true, GetMediaTypeLength);
 45
 46        // Use a collection instead of a dictionary since we may have multiple parameters with the same name.
 47        private ObjectCollection<NameValueHeaderValue> _parameters;
 48        private StringSegment _mediaType;
 49        private bool _isReadOnly;
 50
 051        private MediaTypeHeaderValue()
 52        {
 53            // Used by the parser to create a new instance of this type.
 054        }
 55
 56        /// <summary>
 57        /// Initializes a <see cref="MediaTypeHeaderValue"/> instance.
 58        /// </summary>
 59        /// <param name="mediaType">A <see cref="StringSegment"/> representation of a media type.
 60        /// The text provided must be a single media type without parameters. </param>
 061        public MediaTypeHeaderValue(StringSegment mediaType)
 62        {
 063            CheckMediaTypeFormat(mediaType, nameof(mediaType));
 064            _mediaType = mediaType;
 065        }
 66
 67        /// <summary>
 68        /// Initializes a <see cref="MediaTypeHeaderValue"/> instance.
 69        /// </summary>
 70        /// <param name="mediaType">A <see cref="StringSegment"/> representation of a media type.
 71        /// The text provided must be a single media type without parameters. </param>
 72        /// <param name="quality">The <see cref="double"/> with the quality of the media type.</param>
 73        public MediaTypeHeaderValue(StringSegment mediaType, double quality)
 074            : this(mediaType)
 75        {
 076            Quality = quality;
 077        }
 78
 79        /// <summary>
 80        /// Gets or sets the value of the charset parameter. Returns <see cref="StringSegment.Empty"/>
 81        /// if there is no charset.
 82        /// </summary>
 83        public StringSegment Charset
 84        {
 85            get
 86            {
 087                return NameValueHeaderValue.Find(_parameters, CharsetString)?.Value.Value;
 88            }
 89            set
 90            {
 091                HeaderUtilities.ThrowIfReadOnly(IsReadOnly);
 92                // We don't prevent a user from setting whitespace-only charsets. Like we can't prevent a user from
 93                // setting a non-existing charset.
 094                var charsetParameter = NameValueHeaderValue.Find(_parameters, CharsetString);
 095                if (StringSegment.IsNullOrEmpty(value))
 96                {
 97                    // Remove charset parameter
 098                    if (charsetParameter != null)
 99                    {
 0100                        Parameters.Remove(charsetParameter);
 101                    }
 102                }
 103                else
 104                {
 0105                    if (charsetParameter != null)
 106                    {
 0107                        charsetParameter.Value = value;
 108                    }
 109                    else
 110                    {
 0111                        Parameters.Add(new NameValueHeaderValue(CharsetString, value));
 112                    }
 113                }
 0114            }
 115        }
 116
 117        /// <summary>
 118        /// Gets or sets the value of the Encoding parameter. Setting the Encoding will set
 119        /// the <see cref="Charset"/> to <see cref="Encoding.WebName"/>.
 120        /// </summary>
 121        public Encoding Encoding
 122        {
 123            get
 124            {
 0125                var charset = Charset;
 0126                if (!StringSegment.IsNullOrEmpty(charset))
 127                {
 128                    try
 129                    {
 0130                        return Encoding.GetEncoding(charset.Value);
 131                    }
 0132                    catch (ArgumentException)
 133                    {
 134                        // Invalid or not supported
 0135                    }
 136                }
 0137                return null;
 0138            }
 139            set
 140            {
 0141                HeaderUtilities.ThrowIfReadOnly(IsReadOnly);
 0142                if (value == null)
 143                {
 0144                    Charset = null;
 145                }
 146                else
 147                {
 0148                    Charset = value.WebName;
 149                }
 0150            }
 151        }
 152
 153        /// <summary>
 154        /// Gets or sets the value of the boundary parameter. Returns <see cref="StringSegment.Empty"/>
 155        /// if there is no boundary.
 156        /// </summary>
 157        public StringSegment Boundary
 158        {
 159            get
 160            {
 0161                return NameValueHeaderValue.Find(_parameters, BoundaryString)?.Value ?? default(StringSegment);
 162            }
 163            set
 164            {
 0165                HeaderUtilities.ThrowIfReadOnly(IsReadOnly);
 0166                var boundaryParameter = NameValueHeaderValue.Find(_parameters, BoundaryString);
 0167                if (StringSegment.IsNullOrEmpty(value))
 168                {
 169                    // Remove charset parameter
 0170                    if (boundaryParameter != null)
 171                    {
 0172                        Parameters.Remove(boundaryParameter);
 173                    }
 174                }
 175                else
 176                {
 0177                    if (boundaryParameter != null)
 178                    {
 0179                        boundaryParameter.Value = value;
 180                    }
 181                    else
 182                    {
 0183                        Parameters.Add(new NameValueHeaderValue(BoundaryString, value));
 184                    }
 185                }
 0186            }
 187        }
 188
 189        /// <summary>
 190        /// Gets or sets the media type's parameters. Returns an empty <see cref="IList{T}"/>
 191        /// if there are no parameters.
 192        /// </summary>
 193        public IList<NameValueHeaderValue> Parameters
 194        {
 195            get
 196            {
 0197                if (_parameters == null)
 198                {
 0199                    if (IsReadOnly)
 200                    {
 0201                        _parameters = ObjectCollection<NameValueHeaderValue>.EmptyReadOnlyCollection;
 202                    }
 203                    else
 204                    {
 0205                        _parameters = new ObjectCollection<NameValueHeaderValue>();
 206                    }
 207                }
 0208                return _parameters;
 209            }
 210        }
 211
 212        /// <summary>
 213        /// Gets or sets the value of the quality parameter. Returns null
 214        /// if there is no quality.
 215        /// </summary>
 216        public double? Quality
 217        {
 0218            get { return HeaderUtilities.GetQuality(_parameters); }
 219            set
 220            {
 0221                HeaderUtilities.ThrowIfReadOnly(IsReadOnly);
 0222                HeaderUtilities.SetQuality(Parameters, value);
 0223            }
 224        }
 225
 226        /// <summary>
 227        /// Gets or sets the value of the media type. Returns <see cref="StringSegment.Empty"/>
 228        /// if there is no media type.
 229        /// </summary>
 230        /// <example>
 231        /// For the media type <c>"application/json"</c>, the property gives the value
 232        /// <c>"application/json"</c>.
 233        /// </example>
 234        public StringSegment MediaType
 235        {
 0236            get { return _mediaType; }
 237            set
 238            {
 0239                HeaderUtilities.ThrowIfReadOnly(IsReadOnly);
 0240                CheckMediaTypeFormat(value, nameof(value));
 0241                _mediaType = value;
 0242            }
 243        }
 244
 245        /// <summary>
 246        /// Gets the type of the <see cref="MediaTypeHeaderValue"/>.
 247        /// </summary>
 248        /// <example>
 249        /// For the media type <c>"application/json"</c>, the property gives the value <c>"application"</c>.
 250        /// </example>
 251        /// <remarks>See <see href="https://tools.ietf.org/html/rfc6838#section-4.2">Naming Requirements</see> for more 
 252        public StringSegment Type
 253        {
 254            get
 255            {
 0256                return _mediaType.Subsegment(0, _mediaType.IndexOf(ForwardSlashCharacter));
 257            }
 258        }
 259
 260        /// <summary>
 261        /// Gets the subtype of the <see cref="MediaTypeHeaderValue"/>.
 262        /// </summary>
 263        /// <example>
 264        /// For the media type <c>"application/vnd.example+json"</c>, the property gives the value
 265        /// <c>"vnd.example+json"</c>.
 266        /// </example>
 267        /// <remarks>See <see href="https://tools.ietf.org/html/rfc6838#section-4.2">Naming Requirements</see> for more 
 268        public StringSegment SubType
 269        {
 270            get
 271            {
 0272                return _mediaType.Subsegment(_mediaType.IndexOf(ForwardSlashCharacter) + 1);
 273            }
 274        }
 275
 276        /// <summary>
 277        /// Gets subtype of the <see cref="MediaTypeHeaderValue"/>, excluding any structured syntax suffix. Returns <see
 278        /// if there is no subtype without suffix.
 279        /// </summary>
 280        /// <example>
 281        /// For the media type <c>"application/vnd.example+json"</c>, the property gives the value
 282        /// <c>"vnd.example"</c>.
 283        /// </example>
 284        public StringSegment SubTypeWithoutSuffix
 285        {
 286            get
 287            {
 0288                var subType = SubType;
 0289                var startOfSuffix = subType.LastIndexOf(PlusCharacter);
 0290                if (startOfSuffix == -1)
 291                {
 0292                    return subType;
 293                }
 294                else
 295                {
 0296                    return subType.Subsegment(0, startOfSuffix);
 297                }
 298            }
 299        }
 300
 301        /// <summary>
 302        /// Gets the structured syntax suffix of the <see cref="MediaTypeHeaderValue"/> if it has one.
 303        /// See <see href="https://tools.ietf.org/html/rfc6838#section-4.8">The RFC documentation on structured syntaxes
 304        /// </summary>
 305        /// <example>
 306        /// For the media type <c>"application/vnd.example+json"</c>, the property gives the value
 307        /// <c>"json"</c>.
 308        /// </example>
 309        public StringSegment Suffix
 310        {
 311            get
 312            {
 0313                var subType = SubType;
 0314                var startOfSuffix = subType.LastIndexOf(PlusCharacter);
 0315                if (startOfSuffix == -1)
 316                {
 0317                    return default(StringSegment);
 318                }
 319                else
 320                {
 0321                    return subType.Subsegment(startOfSuffix + 1);
 322                }
 323            }
 324        }
 325
 326        /// <summary>
 327        /// Get a <see cref="IList{T}"/> of facets of the <see cref="MediaTypeHeaderValue"/>. Facets are a
 328        /// period separated list of StringSegments in the <see cref="SubTypeWithoutSuffix"/>.
 329        /// See <see href="https://tools.ietf.org/html/rfc6838#section-3">The RFC documentation on facets.</see>
 330        /// </summary>
 331        /// <example>
 332        /// For the media type <c>"application/vnd.example+json"</c>, the property gives the value:
 333        /// <c>{"vnd", "example"}</c>
 334        /// </example>
 335        public IEnumerable<StringSegment> Facets
 336        {
 337            get
 338            {
 0339                return SubTypeWithoutSuffix.Split(PeriodCharacterArray);
 340            }
 341        }
 342
 343        /// <summary>
 344        /// Gets whether this <see cref="MediaTypeHeaderValue"/> matches all types.
 345        /// </summary>
 0346        public bool MatchesAllTypes => MediaType.Equals(MatchesAllString, StringComparison.Ordinal);
 347
 348        /// <summary>
 349        /// Gets whether this <see cref="MediaTypeHeaderValue"/> matches all subtypes.
 350        /// </summary>
 351        /// <example>
 352        /// For the media type <c>"application/*"</c>, this property is <c>true</c>.
 353        /// </example>
 354        /// <example>
 355        /// For the media type <c>"application/json"</c>, this property is <c>false</c>.
 356        /// </example>
 0357        public bool MatchesAllSubTypes => SubType.Equals(WildcardString, StringComparison.Ordinal);
 358
 359        /// <summary>
 360        /// Gets whether this <see cref="MediaTypeHeaderValue"/> matches all subtypes, ignoring any structured syntax su
 361        /// </summary>
 362        /// <example>
 363        /// For the media type <c>"application/*+json"</c>, this property is <c>true</c>.
 364        /// </example>
 365        /// <example>
 366        /// For the media type <c>"application/vnd.example+json"</c>, this property is <c>false</c>.
 367        /// </example>
 368        public bool MatchesAllSubTypesWithoutSuffix =>
 0369            SubTypeWithoutSuffix.Equals(WildcardString, StringComparison.OrdinalIgnoreCase);
 370
 371        /// <summary>
 372        /// Gets whether the <see cref="MediaTypeHeaderValue"/> is readonly.
 373        /// </summary>
 374        public bool IsReadOnly
 375        {
 0376            get { return _isReadOnly; }
 377        }
 378
 379        /// <summary>
 380        /// Gets a value indicating whether this <see cref="MediaTypeHeaderValue"/> is a subset of
 381        /// <paramref name="otherMediaType"/>. A "subset" is defined as the same or a more specific media type
 382        /// according to the precedence described in https://www.ietf.org/rfc/rfc2068.txt section 14.1, Accept.
 383        /// </summary>
 384        /// <param name="otherMediaType">The <see cref="MediaTypeHeaderValue"/> to compare.</param>
 385        /// <returns>
 386        /// A value indicating whether this <see cref="MediaTypeHeaderValue"/> is a subset of
 387        /// <paramref name="otherMediaType"/>.
 388        /// </returns>
 389        /// <remarks>
 390        /// For example "multipart/mixed; boundary=1234" is a subset of "multipart/mixed; boundary=1234",
 391        /// "multipart/mixed", "multipart/*", and "*/*" but not "multipart/mixed; boundary=2345" or
 392        /// "multipart/message; boundary=1234".
 393        /// </remarks>
 394        public bool IsSubsetOf(MediaTypeHeaderValue otherMediaType)
 395        {
 0396            if (otherMediaType == null)
 397            {
 0398                return false;
 399            }
 400
 401            // "text/plain" is a subset of "text/plain", "text/*" and "*/*". "*/*" is a subset only of "*/*".
 0402            return MatchesType(otherMediaType) &&
 0403                MatchesSubtype(otherMediaType) &&
 0404                MatchesParameters(otherMediaType);
 405        }
 406
 407        /// <summary>
 408        /// Performs a deep copy of this object and all of it's NameValueHeaderValue sub components,
 409        /// while avoiding the cost of re-validating the components.
 410        /// </summary>
 411        /// <returns>A deep copy.</returns>
 412        public MediaTypeHeaderValue Copy()
 413        {
 0414            var other = new MediaTypeHeaderValue();
 0415            other._mediaType = _mediaType;
 416
 0417            if (_parameters != null)
 418            {
 0419                other._parameters = new ObjectCollection<NameValueHeaderValue>(
 0420                    _parameters.Select(item => item.Copy()));
 421            }
 0422            return other;
 423        }
 424
 425        /// <summary>
 426        /// Performs a deep copy of this object and all of it's NameValueHeaderValue sub components,
 427        /// while avoiding the cost of re-validating the components. This copy is read-only.
 428        /// </summary>
 429        /// <returns>A deep, read-only, copy.</returns>
 430        public MediaTypeHeaderValue CopyAsReadOnly()
 431        {
 0432            if (IsReadOnly)
 433            {
 0434                return this;
 435            }
 436
 0437            var other = new MediaTypeHeaderValue();
 0438            other._mediaType = _mediaType;
 0439            if (_parameters != null)
 440            {
 0441                other._parameters = new ObjectCollection<NameValueHeaderValue>(
 0442                    _parameters.Select(item => item.CopyAsReadOnly()), isReadOnly: true);
 443            }
 0444            other._isReadOnly = true;
 0445            return other;
 446        }
 447
 448        public override string ToString()
 449        {
 0450            var builder = new StringBuilder();
 0451            builder.Append(_mediaType.AsSpan());
 0452            NameValueHeaderValue.ToString(_parameters, separator: ';', leadingSeparator: true, destination: builder);
 0453            return builder.ToString();
 454        }
 455
 456        public override bool Equals(object obj)
 457        {
 0458            var other = obj as MediaTypeHeaderValue;
 459
 0460            if (other == null)
 461            {
 0462                return false;
 463            }
 464
 0465            return _mediaType.Equals(other._mediaType, StringComparison.OrdinalIgnoreCase) &&
 0466                HeaderUtilities.AreEqualCollections(_parameters, other._parameters);
 467        }
 468
 469        public override int GetHashCode()
 470        {
 471            // The media-type string is case-insensitive.
 0472            return StringSegmentComparer.OrdinalIgnoreCase.GetHashCode(_mediaType) ^ NameValueHeaderValue.GetHashCode(_p
 473        }
 474
 475        /// <summary>
 476        /// Takes a media type and parses it into the <see cref="MediaTypeHeaderValue" /> and its associated parameters.
 477        /// </summary>
 478        /// <param name="input">The <see cref="StringSegment"/> with the media type.</param>
 479        /// <returns>The parsed <see cref="MediaTypeHeaderValue"/>.</returns>
 480        public static MediaTypeHeaderValue Parse(StringSegment input)
 481        {
 0482            var index = 0;
 0483            return SingleValueParser.ParseValue(input, ref index);
 484        }
 485
 486        /// <summary>
 487        /// Takes a media type, which can include parameters, and parses it into the <see cref="MediaTypeHeaderValue" />
 488        /// </summary>
 489        /// <param name="input">The <see cref="StringSegment"/> with the media type. The media type constructed here mus
 490        /// <param name="parsedValue">The parsed <see cref="MediaTypeHeaderValue"/></param>
 491        /// <returns>True if the value was successfully parsed.</returns>
 492        public static bool TryParse(StringSegment input, out MediaTypeHeaderValue parsedValue)
 493        {
 0494            var index = 0;
 0495            return SingleValueParser.TryParseValue(input, ref index, out parsedValue);
 496        }
 497
 498        /// <summary>
 499        /// Takes an <see cref="IList{T}"/> of <see cref="string"/> and parses it into the <see cref="MediaTypeHeaderVal
 500        /// </summary>
 501        /// <param name="inputs">A list of media types</param>
 502        /// <returns>The parsed <see cref="MediaTypeHeaderValue"/>.</returns>
 503        public static IList<MediaTypeHeaderValue> ParseList(IList<string> inputs)
 504        {
 0505            return MultipleValueParser.ParseValues(inputs);
 506        }
 507
 508        /// <summary>
 509        /// Takes an <see cref="IList{T}"/> of <see cref="string"/> and parses it into the <see cref="MediaTypeHeaderVal
 510        /// Throws if there is invalid data in a string.
 511        /// </summary>
 512        /// <param name="inputs">A list of media types</param>
 513        /// <returns>The parsed <see cref="MediaTypeHeaderValue"/>.</returns>
 514        public static IList<MediaTypeHeaderValue> ParseStrictList(IList<string> inputs)
 515        {
 0516            return MultipleValueParser.ParseStrictValues(inputs);
 517        }
 518
 519        /// <summary>
 520        /// Takes an <see cref="IList{T}"/> of <see cref="string"/> and parses it into the <see cref="MediaTypeHeaderVal
 521        /// </summary>
 522        /// <param name="inputs">A list of media types</param>
 523        /// <param name="parsedValues">The parsed <see cref="MediaTypeHeaderValue"/>.</param>
 524        /// <returns>True if the value was successfully parsed.</returns>
 525        public static bool TryParseList(IList<string> inputs, out IList<MediaTypeHeaderValue> parsedValues)
 526        {
 0527            return MultipleValueParser.TryParseValues(inputs, out parsedValues);
 528        }
 529
 530        /// <summary>
 531        /// Takes an <see cref="IList{T}"/> of <see cref="string"/> and parses it into the <see cref="MediaTypeHeaderVal
 532        /// </summary>
 533        /// <param name="inputs">A list of media types</param>
 534        /// <param name="parsedValues">The parsed <see cref="MediaTypeHeaderValue"/>.</param>
 535        /// <returns>True if the value was successfully parsed.</returns>
 536        public static bool TryParseStrictList(IList<string> inputs, out IList<MediaTypeHeaderValue> parsedValues)
 537        {
 0538            return MultipleValueParser.TryParseStrictValues(inputs, out parsedValues);
 539        }
 540
 541        private static int GetMediaTypeLength(StringSegment input, int startIndex, out MediaTypeHeaderValue parsedValue)
 542        {
 543            Contract.Requires(startIndex >= 0);
 544
 0545            parsedValue = null;
 546
 0547            if (StringSegment.IsNullOrEmpty(input) || (startIndex >= input.Length))
 548            {
 0549                return 0;
 550            }
 551
 552            // Caller must remove leading whitespace. If not, we'll return 0.
 0553            var mediaTypeLength = MediaTypeHeaderValue.GetMediaTypeExpressionLength(input, startIndex, out var mediaType
 554
 0555            if (mediaTypeLength == 0)
 556            {
 0557                return 0;
 558            }
 559
 0560            var current = startIndex + mediaTypeLength;
 0561            current = current + HttpRuleParser.GetWhitespaceLength(input, current);
 0562            MediaTypeHeaderValue mediaTypeHeader = null;
 563
 564            // If we're not done and we have a parameter delimiter, then we have a list of parameters.
 0565            if ((current < input.Length) && (input[current] == ';'))
 566            {
 0567                mediaTypeHeader = new MediaTypeHeaderValue();
 0568                mediaTypeHeader._mediaType = mediaType;
 569
 0570                current++; // skip delimiter.
 0571                var parameterLength = NameValueHeaderValue.GetNameValueListLength(input, current, ';',
 0572                    mediaTypeHeader.Parameters);
 573
 0574                parsedValue = mediaTypeHeader;
 0575                return current + parameterLength - startIndex;
 576            }
 577
 578            // We have a media type without parameters.
 0579            mediaTypeHeader = new MediaTypeHeaderValue();
 0580            mediaTypeHeader._mediaType = mediaType;
 0581            parsedValue = mediaTypeHeader;
 0582            return current - startIndex;
 583        }
 584
 585        private static int GetMediaTypeExpressionLength(StringSegment input, int startIndex, out StringSegment mediaType
 586        {
 587            Contract.Requires((input != null) && (input.Length > 0) && (startIndex < input.Length));
 588
 589            // This method just parses the "type/subtype" string, it does not parse parameters.
 0590            mediaType = null;
 591
 592            // Parse the type, i.e. <type> in media type string "<type>/<subtype>; param1=value1; param2=value2"
 0593            var typeLength = HttpRuleParser.GetTokenLength(input, startIndex);
 594
 0595            if (typeLength == 0)
 596            {
 0597                return 0;
 598            }
 599
 0600            var current = startIndex + typeLength;
 0601            current = current + HttpRuleParser.GetWhitespaceLength(input, current);
 602
 603            // Parse the separator between type and subtype
 0604            if ((current >= input.Length) || (input[current] != '/'))
 605            {
 0606                return 0;
 607            }
 0608            current++; // skip delimiter.
 0609            current = current + HttpRuleParser.GetWhitespaceLength(input, current);
 610
 611            // Parse the subtype, i.e. <subtype> in media type string "<type>/<subtype>; param1=value1; param2=value2"
 0612            var subtypeLength = HttpRuleParser.GetTokenLength(input, current);
 613
 0614            if (subtypeLength == 0)
 615            {
 0616                return 0;
 617            }
 618
 619            // If there is no whitespace between <type> and <subtype> in <type>/<subtype> get the media type using
 620            // one Substring call. Otherwise get substrings for <type> and <subtype> and combine them.
 0621            var mediaTypeLength = current + subtypeLength - startIndex;
 0622            if (typeLength + subtypeLength + 1 == mediaTypeLength)
 623            {
 0624                mediaType = input.Subsegment(startIndex, mediaTypeLength);
 625            }
 626            else
 627            {
 0628                mediaType = input.Substring(startIndex, typeLength) + ForwardSlashCharacter + input.Substring(current, s
 629            }
 630
 0631            return mediaTypeLength;
 632        }
 633
 634        private static void CheckMediaTypeFormat(StringSegment mediaType, string parameterName)
 635        {
 0636            if (StringSegment.IsNullOrEmpty(mediaType))
 637            {
 0638                throw new ArgumentException("An empty string is not allowed.", parameterName);
 639            }
 640
 641            // When adding values using strongly typed objects, no leading/trailing LWS (whitespace) is allowed.
 642            // Also no LWS between type and subtype is allowed.
 0643            var mediaTypeLength = GetMediaTypeExpressionLength(mediaType, 0, out var tempMediaType);
 0644            if ((mediaTypeLength == 0) || (tempMediaType.Length != mediaType.Length))
 645            {
 0646                throw new FormatException(string.Format(CultureInfo.InvariantCulture, "Invalid media type '{0}'.", media
 647            }
 0648        }
 649
 650        private bool MatchesType(MediaTypeHeaderValue set)
 651        {
 0652            return set.MatchesAllTypes ||
 0653                set.Type.Equals(Type, StringComparison.OrdinalIgnoreCase);
 654        }
 655
 656        private bool MatchesSubtype(MediaTypeHeaderValue set)
 657        {
 0658            if (set.MatchesAllSubTypes)
 659            {
 0660                return true;
 661            }
 662
 0663            if (set.Suffix.HasValue)
 664            {
 0665                if (Suffix.HasValue)
 666                {
 0667                    return MatchesSubtypeWithoutSuffix(set) && MatchesSubtypeSuffix(set);
 668                }
 669                else
 670                {
 0671                    return false;
 672                }
 673            }
 674            else
 675            {
 676                // If this subtype or suffix matches the subtype of the set,
 677                // it is considered a subtype.
 678                // Ex: application/json > application/val+json
 0679                return MatchesEitherSubtypeOrSuffix(set);
 680            }
 681        }
 682
 683        private bool MatchesSubtypeWithoutSuffix(MediaTypeHeaderValue set)
 684        {
 0685            return set.MatchesAllSubTypesWithoutSuffix ||
 0686                set.SubTypeWithoutSuffix.Equals(SubTypeWithoutSuffix, StringComparison.OrdinalIgnoreCase);
 687        }
 688
 689        private bool MatchesEitherSubtypeOrSuffix(MediaTypeHeaderValue set)
 690        {
 0691            return set.SubType.Equals(SubType, StringComparison.OrdinalIgnoreCase) ||
 0692                set.SubType.Equals(Suffix, StringComparison.OrdinalIgnoreCase);
 693        }
 694
 695        private bool MatchesParameters(MediaTypeHeaderValue set)
 696        {
 0697            if (set._parameters != null && set._parameters.Count != 0)
 698            {
 699                // Make sure all parameters in the potential superset are included locally. Fine to have additional
 700                // parameters locally; they make this one more specific.
 0701                foreach (var parameter in set._parameters)
 702                {
 0703                    if (parameter.Name.Equals(WildcardString, StringComparison.OrdinalIgnoreCase))
 704                    {
 705                        // A parameter named "*" has no effect on media type matching, as it is only used as an indicati
 706                        // that the entire media type string should be treated as a wildcard.
 707                        continue;
 708                    }
 709
 0710                    if (parameter.Name.Equals(QualityString, StringComparison.OrdinalIgnoreCase))
 711                    {
 712                        // "q" and later parameters are not involved in media type matching. Quoting the RFC: The first
 713                        // "q" parameter (if any) separates the media-range parameter(s) from the accept-params.
 0714                        break;
 715                    }
 716
 0717                    var localParameter = NameValueHeaderValue.Find(_parameters, parameter.Name);
 0718                    if (localParameter == null)
 719                    {
 720                        // Not found.
 0721                        return false;
 722                    }
 723
 0724                    if (!StringSegment.Equals(parameter.Value, localParameter.Value, StringComparison.OrdinalIgnoreCase)
 725                    {
 0726                        return false;
 727                    }
 728                }
 729            }
 0730            return true;
 0731        }
 732
 733        private bool MatchesSubtypeSuffix(MediaTypeHeaderValue set)
 734        {
 735            // We don't have support for wildcards on suffixes alone (e.g., "application/entity+*")
 736            // because there's no clear use case for it.
 0737            return set.Suffix.Equals(Suffix, StringComparison.OrdinalIgnoreCase);
 738        }
 739    }
 740}