Ejemplo n.º 1
0
        internal IEnumerable <KeyValuePair <string, string> > GetHeaderStrings()
        {
            if (headerStore == null)
            {
                yield break;
            }

            foreach (var header in headerStore)
            {
                HeaderStoreItemInfo info = header.Value;

                string[] values      = GetValuesAsStrings(info);
                string   stringValue = string.Empty; // returned if values.Length == 0

                if (values.Length == 1)
                {
                    stringValue = values[0];
                }
                else
                {
                    // Note that if we get multiple values for a header that doesn't support multiple values, we'll
                    // just separate the values using a comma (default separator).
                    string separator = HttpHeaderParser.DefaultSeparator;
                    if ((info.Parser != null) && (info.Parser.SupportsMultipleValues))
                    {
                        separator = info.Parser.Separator;
                    }
                    stringValue = string.Join(separator, values);
                }

                yield return(new KeyValuePair <string, string>(header.Key, stringValue));
            }
        }
Ejemplo n.º 2
0
        private static void AddValueToStoreValue <T>(HeaderStoreItemInfo info, object value,
                                                     ref object currentStoreValue) where T : class
        {
            // If there is no value set yet, then add current item as value (we don't create a list
            // if not required). If 'info.Value' is already assigned then make sure 'info.Value' is a
            // List<T> and append 'item' to the list.
            if (currentStoreValue == null)
            {
                currentStoreValue = value;
            }
            else
            {
                List <T> storeValues = currentStoreValue as List <T>;

                if (storeValues == null)
                {
                    storeValues = new List <T>(2);
                    Contract.Assert(value is T);
                    storeValues.Add(currentStoreValue as T);
                    currentStoreValue = storeValues;
                }
                Contract.Assert(value is T);
                storeValues.Add(value as T);
            }
        }
Ejemplo n.º 3
0
        private HeaderStoreItemInfo GetOrCreateHeaderInfo(string name, bool parseRawValues)
        {
            Contract.Requires((name != null) && (name.Length > 0));
            Contract.Requires(HttpRuleParser.GetTokenLength(name, 0) == name.Length);
            Contract.Ensures(Contract.Result <HeaderStoreItemInfo>() != null);

            HeaderStoreItemInfo result = null;
            bool found = false;

            if (parseRawValues)
            {
                found = TryGetAndParseHeaderInfo(name, out result);
            }
            else
            {
                found = TryGetHeaderInfo(name, out result);
            }

            if (!found)
            {
                result = CreateAndAddHeaderToStore(name);
            }

            return(result);
        }
Ejemplo n.º 4
0
 private void AddHeaderToStore(string name, HeaderStoreItemInfo info)
 {
     if (headerStore == null)
     {
         headerStore = new Dictionary <string, HeaderStoreItemInfo>(
             HeaderUtilities.CaseInsensitiveStringComparer);
     }
     headerStore.Add(name, info);
 }
Ejemplo n.º 5
0
        private bool TryGetAndParseHeaderInfo(string name, out HeaderStoreItemInfo info)
        {
            if (TryGetHeaderInfo(name, out info))
            {
                return(ParseRawHeaderValues(name, info, true));
            }

            return(false);
        }
Ejemplo n.º 6
0
        private HeaderStoreItemInfo CreateAndAddHeaderToStore(string name)
        {
            // If we don't have the header in the store yet, add it now.
            HeaderStoreItemInfo result = new HeaderStoreItemInfo(GetParser(name));

            AddHeaderToStore(name, result);

            return(result);
        }
Ejemplo n.º 7
0
        internal bool ContainsParsedValue(string name, object value)
        {
            Contract.Requires((name != null) && (name.Length > 0));
            Contract.Requires(HttpRuleParser.GetTokenLength(name, 0) == name.Length);
            Contract.Requires(value != null);

            if (headerStore == null)
            {
                return(false);
            }

            // If we have a value for this header, then verify if we have a single value. If so, compare that
            // value with 'item'. If we have a list of values, then compare each item in the list with 'item'.
            HeaderStoreItemInfo info = null;

            if (TryGetAndParseHeaderInfo(name, out info))
            {
                Contract.Assert(info.Parser != null, "Can't add parsed value if there is no parser available.");
                Contract.Assert(info.Parser.SupportsMultipleValues,
                                "This method should not be used for single-value headers. Use equality comparer instead.");

                // If there is no entry, just return.
                if (info.ParsedValue == null)
                {
                    return(false);
                }

                List <object> parsedValues = info.ParsedValue as List <object>;

                IEqualityComparer comparer = info.Parser.Comparer;

                if (parsedValues == null)
                {
                    Contract.Assert(info.ParsedValue.GetType() == value.GetType(),
                                    "Stored value does not have the same type as 'value'.");

                    return(AreEqual(value, info.ParsedValue, comparer));
                }
                else
                {
                    foreach (object item in parsedValues)
                    {
                        Contract.Assert(item.GetType() == value.GetType(),
                                        "One of the stored values does not have the same type as 'value'.");

                        if (AreEqual(value, item, comparer))
                        {
                            return(true);
                        }
                    }
                    return(false);
                }
            }

            return(false);
        }
Ejemplo n.º 8
0
        private bool TryGetHeaderInfo(string name, out HeaderStoreItemInfo info)
        {
            if (headerStore == null)
            {
                info = null;
                return(false);
            }

            return(headerStore.TryGetValue(name, out info));
        }
Ejemplo n.º 9
0
 private void PrepareHeaderInfoForAdd(string name, out HeaderStoreItemInfo info, out bool addToStore)
 {
     info       = null;
     addToStore = false;
     if (!TryGetAndParseHeaderInfo(name, out info))
     {
         info       = new HeaderStoreItemInfo(GetParser(name));
         addToStore = true;
     }
 }
Ejemplo n.º 10
0
        private static int GetValueCount(HeaderStoreItemInfo info)
        {
            Contract.Requires(info != null);

            int valueCount = 0;

            UpdateValueCount <string>(info.RawValue, ref valueCount);
            UpdateValueCount <string>(info.InvalidValue, ref valueCount);
            UpdateValueCount <object>(info.ParsedValue, ref valueCount);

            return(valueCount);
        }
Ejemplo n.º 11
0
        internal void AddHeaders(HttpHeaders sourceHeaders)
        {
            Contract.Requires(sourceHeaders != null);
            Contract.Assert(this.parserStore == sourceHeaders.parserStore,
                            "Can only copy headers from an instance with the same header parsers.");

            if (sourceHeaders.headerStore == null)
            {
                return;
            }

            List <string> invalidHeaders = null;

            foreach (var header in sourceHeaders.headerStore)
            {
                // Only add header values if they're not already set on the message. Note that we don't merge
                // collections: If both the default headers and the message have set some values for a certain
                // header, then we don't try to merge the values.
                if ((headerStore == null) || (!headerStore.ContainsKey(header.Key)))
                {
                    HeaderStoreItemInfo sourceInfo = header.Value;

                    // If DefaultRequestHeaders values are copied to multiple messages, it is useful to parse these
                    // default header values only once. This is what we're doing here: By parsing raw headers in
                    // 'sourceHeaders' before copying values to our header store.
                    if (!sourceHeaders.ParseRawHeaderValues(header.Key, sourceInfo, false))
                    {
                        // If after trying to parse source header values no value is left (i.e. all values contain
                        // invalid newline chars), mark this header as 'to-be-deleted' and skip to the next header.
                        if (invalidHeaders == null)
                        {
                            invalidHeaders = new List <string>();
                        }

                        invalidHeaders.Add(header.Key);
                    }
                    else
                    {
                        AddHeaderInfo(header.Key, sourceInfo);
                    }
                }
            }

            if (invalidHeaders != null)
            {
                Contract.Assert(sourceHeaders.headerStore != null);
                foreach (string invalidHeader in invalidHeaders)
                {
                    sourceHeaders.headerStore.Remove(invalidHeader);
                }
            }
        }
Ejemplo n.º 12
0
        // Since most of the time we just have 1 value, we don't create a List<object> for one value, but we change
        // the return type to 'object'. The caller has to deal with the return type (object vs. List<object>). This
        // is to optimize the most common scenario where a header has only one value.
        internal object GetParsedValues(string name)
        {
            Contract.Requires((name != null) && (name.Length > 0));
            Contract.Requires(HttpRuleParser.GetTokenLength(name, 0) == name.Length);

            HeaderStoreItemInfo info = null;

            if (!TryGetAndParseHeaderInfo(name, out info))
            {
                return(null);
            }

            return(info.ParsedValue);
        }
Ejemplo n.º 13
0
        private static void CloneAndAddValue(HeaderStoreItemInfo destinationInfo, object source)
        {
            // We only have one value. Clone it and assign it to the store.
            ICloneable cloneableValue = source as ICloneable;

            if (cloneableValue != null)
            {
                AddValue(destinationInfo, cloneableValue.Clone(), StoreLocation.Parsed);
            }
            else
            {
                // If it doesn't implement ICloneable, it's a value type or an immutable type like String/Uri.
                AddValue(destinationInfo, source, StoreLocation.Parsed);
            }
        }
Ejemplo n.º 14
0
        internal void AddParsedValue(string name, object value)
        {
            Contract.Requires((name != null) && (name.Length > 0));
            Contract.Requires(HttpRuleParser.GetTokenLength(name, 0) == name.Length);
            Contract.Requires(value != null);

            HeaderStoreItemInfo info = GetOrCreateHeaderInfo(name, true);

            Contract.Assert(info.Parser != null, "Can't add parsed value if there is no parser available.");

            // If the current header has only one value, we can't add another value. The strongly typed property
            // must not call AddParsedValue(), but SetParsedValue(). E.g. for headers like 'Date', 'Host'.
            Contract.Assert(info.CanAddValue, "Header '" + name + "' doesn't support multiple values");

            AddValue(info, value, StoreLocation.Parsed);
        }
Ejemplo n.º 15
0
        public bool Contains(string name)
        {
            CheckHeaderName(name);

            if (headerStore == null)
            {
                return(false);
            }

            // We can't just call headerStore.ContainsKey() since after parsing the value the header may not exist
            // anymore (if the value contains invalid newline chars, we remove the header). So try to parse the
            // header value.
            HeaderStoreItemInfo info = null;

            return(TryGetAndParseHeaderInfo(name, out info));
        }
Ejemplo n.º 16
0
        public void AddWithoutValidation(string name, string value)
        {
            CheckHeaderName(name);

            if (value == null)
            {
                // We allow empty header values. (e.g. "My-Header: "). If the user adds multiple null/empty
                // values, we'll just add them to the collection. This will result in delimiter-only values:
                // E.g. adding two null-strings (or empty, or whitespace-only) results in "My-Header: ,".
                value = string.Empty;
            }

            HeaderStoreItemInfo info = GetOrCreateHeaderInfo(name, false);

            AddValue(info, value, StoreLocation.Raw);
        }
Ejemplo n.º 17
0
        public IEnumerator <KeyValuePair <string, IEnumerable <string> > > GetEnumerator()
        {
            if (headerStore == null)
            {
                yield break;
            }

            List <string> invalidHeaders = null;

            foreach (var header in headerStore)
            {
                HeaderStoreItemInfo info = header.Value;

                // Make sure we parse all raw values before returning the result. Note that this has to be
                // done before we calculate the array length (next line): A raw value may contain a list of
                // values.
                if (!ParseRawHeaderValues(header.Key, info, false))
                {
                    // We have an invalid header value (contains invalid newline chars). Mark it as "to-be-deleted"
                    // and skip this header.
                    if (invalidHeaders == null)
                    {
                        invalidHeaders = new List <string>();
                    }
                    invalidHeaders.Add(header.Key);
                }
                else
                {
                    string[] values = GetValuesAsStrings(info);
                    yield return(new KeyValuePair <string, IEnumerable <string> >(header.Key, values));
                }
            }

            // While we were enumerating headers, we also parsed header values. If during parsing it turned out that
            // the header value was invalid (contains invalid newline chars), remove the header from the store after
            // completing the enumeration.
            if (invalidHeaders != null)
            {
                Contract.Assert(headerStore != null);
                foreach (string invalidHeader in invalidHeaders)
                {
                    headerStore.Remove(invalidHeader);
                }
            }
        }
Ejemplo n.º 18
0
        internal void SetParsedValue(string name, object value)
        {
            Contract.Requires((name != null) && (name.Length > 0));
            Contract.Requires(HttpRuleParser.GetTokenLength(name, 0) == name.Length);
            Contract.Requires(value != null);

            // This method will first clear all values. This is used e.g. when setting the 'Date' or 'Host' header.
            // I.e. headers not supporting collections.
            HeaderStoreItemInfo info = GetOrCreateHeaderInfo(name, true);

            Contract.Assert(info.Parser != null, "Can't add parsed value if there is no parser available.");

            info.InvalidValue = null;
            info.ParsedValue  = null;
            info.RawValue     = null;

            AddValue(info, value, StoreLocation.Parsed);
        }
Ejemplo n.º 19
0
        private void AddHeaderInfo(string headerName, HeaderStoreItemInfo sourceInfo)
        {
            HeaderStoreItemInfo destinationInfo = CreateAndAddHeaderToStore(headerName);

            Contract.Assert(sourceInfo.Parser == destinationInfo.Parser,
                            "Expected same parser on both source and destination header store for header '" + headerName +
                            "'.");

            // We have custom header values. The parsed values are strings.
            if (destinationInfo.Parser == null)
            {
                Contract.Assert((sourceInfo.RawValue == null) && (sourceInfo.InvalidValue == null),
                                "No raw or invalid values expected for custom headers.");

                // Custom header values are always stored as string or list of strings.
                destinationInfo.ParsedValue = CloneStringHeaderInfoValues(sourceInfo.ParsedValue);
            }
            else
            {
                // We have a parser, so we have to copy invalid values and clone parsed values.

                // Invalid values are always strings. Strings are immutable. So we only have to clone the
                // collection (if there is one).
                destinationInfo.InvalidValue = CloneStringHeaderInfoValues(sourceInfo.InvalidValue);

                // Now clone and add parsed values (if any).
                if (sourceInfo.ParsedValue != null)
                {
                    List <object> sourceValues = sourceInfo.ParsedValue as List <object>;
                    if (sourceValues == null)
                    {
                        CloneAndAddValue(destinationInfo, sourceInfo.ParsedValue);
                    }
                    else
                    {
                        foreach (object item in sourceValues)
                        {
                            CloneAndAddValue(destinationInfo, item);
                        }
                    }
                }
            }
        }
Ejemplo n.º 20
0
        private static void AddValue(HeaderStoreItemInfo info, object value, StoreLocation location)
        {
            // Since we have the same pattern for all three store locations (raw, invalid, parsed), we use
            // this helper method to deal with adding values:
            // - if 'null' just set the store property to 'value'
            // - if 'List<T>' append 'value' to the end of the list
            // - if 'T', i.e. we have already a value stored (but no list), create a list, add the stored value
            //   to the list and append 'value' at the end of the newly created list.

            Contract.Assert((info.Parser != null) || ((info.Parser == null) && (value.GetType() == typeof(string))),
                            "If no parser is defined, then the value must be string.");

            object currentStoreValue = null;

            switch (location)
            {
            case StoreLocation.Raw:
                currentStoreValue = info.RawValue;
                AddValueToStoreValue <string>(info, value, ref currentStoreValue);
                info.RawValue = currentStoreValue;
                break;

            case StoreLocation.Invalid:
                currentStoreValue = info.InvalidValue;
                AddValueToStoreValue <string>(info, value, ref currentStoreValue);
                info.InvalidValue = currentStoreValue;
                break;

            case StoreLocation.Parsed:
                Contract.Assert((value == null) || (!(value is List <object>)),
                                "Header value types must not derive from List<object> since this type is used internally to store " +
                                "lists of values. So we would not be able to distinguish between a single value and a list of values.");
                currentStoreValue = info.ParsedValue;
                AddValueToStoreValue <object>(info, value, ref currentStoreValue);
                info.ParsedValue = currentStoreValue;
                break;

            default:
                Contract.Assert(false, "Unknown StoreLocation value: " + location.ToString());
                break;
            }
        }
Ejemplo n.º 21
0
        private bool ParseRawHeaderValues(string name, HeaderStoreItemInfo info, bool removeEmptyHeader)
        {
            Contract.Ensures(info.RawValue == null);

            // Unlike TryGetHeaderInfo() this method tries to parse all non-validated header values (if any)
            // before returning to the caller.
            if (info.RawValue != null)
            {
                List <string> rawValues = info.RawValue as List <string>;

                if (rawValues == null)
                {
                    ParseSingleRawHeaderValue(name, info);
                }
                else
                {
                    ParseMultipleRawHeaderValues(name, info, rawValues);
                }

                // At this point all values are either in info.ParsedValue, info.InvalidValue, or were removed since they
                // contain invalid newline chars. Reset RawValue.
                info.RawValue = null;

                // During parsing, we removed tha value since it contains invalid newline chars. Return false to indicate that
                // this is an empty header. If the caller specified to remove empty headers, we'll remove the header before
                // returning.
                if ((info.InvalidValue == null) && (info.ParsedValue == null))
                {
                    if (removeEmptyHeader)
                    {
                        // After parsing the raw value, no value is left because all values contain invalid newline
                        // chars.
                        Contract.Assert(headerStore != null);
                        headerStore.Remove(name);
                    }
                    return(false);
                }
            }

            return(true);
        }
Ejemplo n.º 22
0
        public bool TryGetValues(string name, out IEnumerable <string> values)
        {
            CheckHeaderName(name);

            if (headerStore == null)
            {
                values = null;
                return(false);
            }

            HeaderStoreItemInfo info = null;

            if (TryGetAndParseHeaderInfo(name, out info))
            {
                values = GetValuesAsStrings(info);
                return(true);
            }

            values = null;
            return(false);
        }
Ejemplo n.º 23
0
        private static void ParseSingleRawHeaderValue(string name, HeaderStoreItemInfo info)
        {
            string rawValue = info.RawValue as string;

            Contract.Assert(rawValue != null, "RawValue must either be List<string> or string.");

            if (info.Parser == null)
            {
                if (!ContainsInvalidNewLine(rawValue, name))
                {
                    info.ParsedValue = info.RawValue;
                }
            }
            else
            {
                if (!TryParseAndAddRawHeaderValue(name, info, rawValue))
                {
                    // TODO log that we couldn't add invalid header value
                }
            }
        }
Ejemplo n.º 24
0
        private static string[] GetValuesAsStrings(HeaderStoreItemInfo info)
        {
            Contract.Ensures(Contract.Result <string[]>() != null);

            int length = GetValueCount(info);

            string[] values = new string[length];

            if (length > 0)
            {
                int currentIndex = 0;

                ReadStoreValues <string>(values, info.RawValue, null, ref currentIndex);
                ReadStoreValues <object>(values, info.ParsedValue, info.Parser, ref currentIndex);

                // Set parser parameter to 'null' for invalid values: The invalid values is always a string so we
                // don't need the parser to "serialize" the value to a string.
                ReadStoreValues <string>(values, info.InvalidValue, null, ref currentIndex);
            }
            return(values);
        }
Ejemplo n.º 25
0
 private static void ParseMultipleRawHeaderValues(string name, HeaderStoreItemInfo info, List <string> rawValues)
 {
     if (info.Parser == null)
     {
         foreach (string rawValue in rawValues)
         {
             if (!ContainsInvalidNewLine(rawValue, name))
             {
                 AddValue(info, rawValue, StoreLocation.Parsed);
             }
         }
     }
     else
     {
         foreach (string rawValue in rawValues)
         {
             if (!TryParseAndAddRawHeaderValue(name, info, rawValue))
             {
                 // TODO log that we couldn't add invalid header value
             }
         }
     }
 }
Ejemplo n.º 26
0
        private void ParseAndAddValue(string name, HeaderStoreItemInfo info, string value)
        {
            Contract.Requires(info != null);

            if (info.Parser == null)
            {
                // If we don't have a parser for the header, we consider the value valid if it doesn't contains
                // invalid newline characters. We add the values as "parsed value". Note that we allow empty values.
                CheckInvalidNewLine(value);
                AddValue(info, value == null ? string.Empty : value, StoreLocation.Parsed);
                return;
            }

            // If the header only supports 1 value, we can add the current value only if there is no
            // value already set.
            if (!info.CanAddValue)
            {
                throw new FormatException(string.Format("Cannot add value because header '{0}' does not support multiple values.", name));
            }

            int    index       = 0;
            object parsedValue = info.Parser.ParseValue(value, info.ParsedValue, ref index);

            // The raw string only represented one value (which was successfully parsed). Add the value and return.
            // If value is null we still have to first call ParseValue() to allow the parser to decide whether null is
            // a valid value. If it is (i.e. no exception thrown), we set the parsed value (if any) and return.
            if ((value == null) || (index == value.Length))
            {
                // If the returned value is null, then it means the header accepts empty values. I.e. we don't throw
                // but we don't add 'null' to the store either.
                if (parsedValue != null)
                {
                    AddValue(info, parsedValue, StoreLocation.Parsed);
                }
                return;
            }
            Contract.Assert(index < value.Length, "Parser must return an index value within the string length.");

            // If we successfully parsed a value, but there are more left to read, store the results in a temp
            // list. Only when all values are parsed successfully write the list to the store.
            List <object> parsedValues = new List <object>();

            if (parsedValue != null)
            {
                parsedValues.Add(parsedValue);
            }

            while (index < value.Length)
            {
                parsedValue = info.Parser.ParseValue(value, info.ParsedValue, ref index);
                if (parsedValue != null)
                {
                    parsedValues.Add(parsedValue);
                }
            }

            // All values were parsed correctly. Copy results to the store.
            foreach (object item in parsedValues)
            {
                AddValue(info, item, StoreLocation.Parsed);
            }
        }
Ejemplo n.º 27
0
        private static bool TryParseAndAddRawHeaderValue(string name, HeaderStoreItemInfo info, string value)
        {
            Contract.Requires(info != null);
            Contract.Requires(info.Parser != null);
            Contract.Requires(value != null, "Trying to parse 'null': Raw values should never be 'null'.");

            // Values are added as 'invalid' if we either can't parse the value OR if we already have a value
            // and the current header doesn't support multiple values: e.g. trying to add a date/time value
            // to the 'Date' header if we already have a date/time value will result in the second value being
            // added to the 'invalid' header values.
            if (!info.CanAddValue)
            {
                AddValue(info, value, StoreLocation.Invalid);
                return(false);
            }

            int    index       = 0;
            object parsedValue = null;

            if (info.Parser.TryParseValue(value, info.ParsedValue, ref index, out parsedValue))
            {
                // The raw string only represented one value (which was successfully parsed). Add the value and return.
                if (index == value.Length)
                {
                    if (parsedValue != null)
                    {
                        AddValue(info, parsedValue, StoreLocation.Parsed);
                    }
                    return(true);
                }
                Contract.Assert(index < value.Length, "Parser must return an index value within the string length.");

                // If we successfully parsed a value, but there are more left to read, store the results in a temp
                // list. Only when all values are parsed successfully write the list to the store.
                List <object> parsedValues = new List <object>();
                if (parsedValue != null)
                {
                    parsedValues.Add(parsedValue);
                }

                while (index < value.Length)
                {
                    if (info.Parser.TryParseValue(value, info.ParsedValue, ref index, out parsedValue))
                    {
                        if (parsedValue != null)
                        {
                            parsedValues.Add(parsedValue);
                        }
                    }
                    else
                    {
                        if (!ContainsInvalidNewLine(value, name))
                        {
                            AddValue(info, value, StoreLocation.Invalid);
                        }
                        return(false);
                    }
                }

                // All values were parsed correctly. Copy results to the store.
                foreach (object item in parsedValues)
                {
                    AddValue(info, item, StoreLocation.Parsed);
                }
                return(true);
            }

            if (!ContainsInvalidNewLine(value, name))
            {
                AddValue(info, value, StoreLocation.Invalid);
            }
            return(false);
        }
Ejemplo n.º 28
0
        internal bool RemoveParsedValue(string name, object value)
        {
            Contract.Requires((name != null) && (name.Length > 0));
            Contract.Requires(HttpRuleParser.GetTokenLength(name, 0) == name.Length);
            Contract.Requires(value != null);

            if (headerStore == null)
            {
                return(false);
            }

            // If we have a value for this header, then verify if we have a single value. If so, compare that
            // value with 'item'. If we have a list of values, then remove 'item' from the list.
            HeaderStoreItemInfo info = null;

            if (TryGetAndParseHeaderInfo(name, out info))
            {
                Contract.Assert(info.Parser != null, "Can't add parsed value if there is no parser available.");
                Contract.Assert(info.Parser.SupportsMultipleValues,
                                "This method should not be used for single-value headers. Use Remove(string) instead.");

                bool result = false;

                // If there is no entry, just return.
                if (info.ParsedValue == null)
                {
                    return(false);
                }

                IEqualityComparer comparer = info.Parser.Comparer;

                List <object> parsedValues = info.ParsedValue as List <object>;
                if (parsedValues == null)
                {
                    Contract.Assert(info.ParsedValue.GetType() == value.GetType(),
                                    "Stored value does not have the same type as 'value'.");

                    if (AreEqual(value, info.ParsedValue, comparer))
                    {
                        info.ParsedValue = null;
                        result           = true;
                    }
                }
                else
                {
                    foreach (object item in parsedValues)
                    {
                        Contract.Assert(item.GetType() == value.GetType(),
                                        "One of the stored values does not have the same type as 'value'.");

                        if (AreEqual(value, item, comparer))
                        {
                            // Remove 'item' rather than 'value', since the 'comparer' may consider two values
                            // equal even though the default obj.Equals() may not (e.g. if 'comparer' does
                            // case-insentive comparison for strings, but string.Equals() is case-sensitive).
                            result = parsedValues.Remove(item);
                            break;
                        }
                    }

                    // If we removed the last item in a list, remove the list.
                    if (parsedValues.Count == 0)
                    {
                        info.ParsedValue = null;
                    }
                }

                // If there is no value for the header left, remove the header.
                if (info.IsEmpty)
                {
                    bool headerRemoved = Remove(name);
                    Contract.Assert(headerRemoved, "Existing header '" + name + "' couldn't be removed.");
                }

                return(result);
            }
            return(false);
        }