<commit_before>#region License
/*
 * WebHeaderCollection.cs
 *
 * This code is derived from WebHeaderCollection.cs (System.Net) of Mono
 * (http://www.mono-project.com).
 *
 * The MIT License
 *
 * Copyright (c) 2003 Ximian, Inc. (http://www.ximian.com)
 * Copyright (c) 2007 Novell, Inc. (http://www.novell.com)
 * Copyright (c) 2012-2015 sta.blockhead
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#endregion

#region Authors
/*
 * Authors:
 * - <NAME> <<EMAIL>>
 * - <NAME> <<EMAIL>>
 * - <NAME> <<EMAIL>>
 */
#endregion

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Security.Permissions;
using System.Text;

namespace WebSocketSharp.Net
{
  /// <summary>
  /// Provides a collection of the HTTP headers associated with a request or
  /// response.
  /// </summary>
  [Serializable]
  [ComVisible (true)]
  public class WebHeaderCollection : NameValueCollection, ISerializable
  {
    #region Private Fields

    private static readonly Dictionary<string, HttpHeaderInfo> _headers;
    private bool                                               _internallyUsed;
    private HttpHeaderType                                     _state;

    #endregion

    #region Static Constructor

    static WebHeaderCollection ()
    {
      _headers =
        new Dictionary<string, HttpHeaderInfo> (
          StringComparer.InvariantCultureIgnoreCase
        )
        {
          {
            "Accept",
            new HttpHeaderInfo (
              "Accept",
              HttpHeaderType.Request
              | HttpHeaderType.Restricted
              | HttpHeaderType.MultiValue
            )
          },
          {
            "AcceptCharset",
            new HttpHeaderInfo (
              "Accept-Charset",
              HttpHeaderType.Request | HttpHeaderType.MultiValue
            )
          },
          {
            "AcceptEncoding",
            new HttpHeaderInfo (
              "Accept-Encoding",
              HttpHeaderType.Request | HttpHeaderType.MultiValue
            )
          },
          {
            "AcceptLanguage",
            new HttpHeaderInfo (
              "Accept-Language",
              HttpHeaderType.Request | HttpHeaderType.MultiValue
            )
          },
          {
            "AcceptRanges",
            new HttpHeaderInfo (
              "Accept-Ranges",
              HttpHeaderType.Response | HttpHeaderType.MultiValue
            )
          },
          {
            "Age",
            new HttpHeaderInfo (
              "Age",
              HttpHeaderType.Response
            )
          },
          {
            "Allow",
            new HttpHeaderInfo (
              "Allow",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.MultiValue
            )
          },
          {
            "Authorization",
            new HttpHeaderInfo (
              "Authorization",
              HttpHeaderType.Request | HttpHeaderType.MultiValue
            )
          },
          {
            "CacheControl",
            new HttpHeaderInfo (
              "Cache-Control",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.MultiValue
            )
          },
          {
            "Connection",
            new HttpHeaderInfo (
              "Connection",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.Restricted
              | HttpHeaderType.MultiValue
            )
          },
          {
            "ContentEncoding",
            new HttpHeaderInfo (
              "Content-Encoding",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.MultiValue
            )
          },
          {
            "ContentLanguage",
            new HttpHeaderInfo (
              "Content-Language",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.MultiValue
            )
          },
          {
            "ContentLength",
            new HttpHeaderInfo (
              "Content-Length",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.Restricted
            )
          },
          {
            "ContentLocation",
            new HttpHeaderInfo (
              "Content-Location",
              HttpHeaderType.Request | HttpHeaderType.Response
            )
          },
          {
            "ContentMd5",
            new HttpHeaderInfo (
              "Content-MD5",
              HttpHeaderType.Request | HttpHeaderType.Response
            )
          },
          {
            "ContentRange",
            new HttpHeaderInfo (
              "Content-Range",
              HttpHeaderType.Request | HttpHeaderType.Response
            )
          },
          {
            "ContentType",
            new HttpHeaderInfo (
              "Content-Type",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.Restricted
            )
          },
          {
            "Cookie",
            new HttpHeaderInfo (
              "Cookie",
              HttpHeaderType.Request
            )
          },
          {
            "Cookie2",
            new HttpHeaderInfo (
              "Cookie2",
              HttpHeaderType.Request
            )
          },
          {
            "Date",
            new HttpHeaderInfo (
              "Date",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.Restricted
            )
          },
          {
            "Expect",
            new HttpHeaderInfo (
              "Expect",
              HttpHeaderType.Request
              | HttpHeaderType.Restricted
              | HttpHeaderType.MultiValue
            )
          },
          {
            "Expires",
            new HttpHeaderInfo (
              "Expires",
              HttpHeaderType.Request | HttpHeaderType.Response
            )
          },
          {
            "ETag",
            new HttpHeaderInfo (
              "ETag",
              HttpHeaderType.Response
            )
          },
          {
            "From",
            new HttpHeaderInfo (
              "From",
              HttpHeaderType.Request
            )
          },
          {
            "Host",
            new HttpHeaderInfo (
              "Host",
              HttpHeaderType.Request | HttpHeaderType.Restricted
            )
          },
          {
            "IfMatch",
            new HttpHeaderInfo (
              "If-Match",
              HttpHeaderType.Request | HttpHeaderType.MultiValue
            )
          },
          {
            "IfModifiedSince",
            new HttpHeaderInfo (
              "If-Modified-Since",
              HttpHeaderType.Request | HttpHeaderType.Restricted
            )
          },
          {
            "IfNoneMatch",
            new HttpHeaderInfo (
              "If-None-Match",
              HttpHeaderType.Request | HttpHeaderType.MultiValue
            )
          },
          {
            "IfRange",
            new HttpHeaderInfo (
              "If-Range",
              HttpHeaderType.Request
            )
          },
          {
            "IfUnmodifiedSince",
            new HttpHeaderInfo (
              "If-Unmodified-Since",
              HttpHeaderType.Request
            )
          },
          {
            "KeepAlive",
            new HttpHeaderInfo (
              "Keep-Alive",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.MultiValue
            )
          },
          {
            "LastModified",
            new HttpHeaderInfo (
              "Last-Modified",
              HttpHeaderType.Request | HttpHeaderType.Response
            )
          },
          {
            "Location",
            new HttpHeaderInfo (
              "Location",
              HttpHeaderType.Response
            )
          },
          {
            "MaxForwards",
            new HttpHeaderInfo (
              "Max-Forwards",
              HttpHeaderType.Request
            )
          },
          {
            "Pragma",
            new HttpHeaderInfo (
              "Pragma",
              HttpHeaderType.Request | HttpHeaderType.Response
            )
          },
          {
            "ProxyAuthenticate",
            new HttpHeaderInfo (
              "Proxy-Authenticate",
              HttpHeaderType.Response | HttpHeaderType.MultiValue
            )
          },
          {
            "ProxyAuthorization",
            new HttpHeaderInfo (
              "Proxy-Authorization",
              HttpHeaderType.Request
            )
          },
          {
            "ProxyConnection",
            new HttpHeaderInfo (
              "Proxy-Connection",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.Restricted
            )
          },
          {
            "Public",
            new HttpHeaderInfo (
              "Public",
              HttpHeaderType.Response | HttpHeaderType.MultiValue
            )
          },
          {
            "Range",
            new HttpHeaderInfo (
              "Range",
              HttpHeaderType.Request
              | HttpHeaderType.Restricted
              | HttpHeaderType.MultiValue
            )
          },
          {
            "Referer",
            new HttpHeaderInfo (
              "Referer",
              HttpHeaderType.Request | HttpHeaderType.Restricted
            )
          },
          {
            "RetryAfter",
            new HttpHeaderInfo (
              "Retry-After",
              HttpHeaderType.Response
            )
          },
          {
            "SecWebSocketAccept",
            new HttpHeaderInfo (
              "Sec-WebSocket-Accept",
              HttpHeaderType.Response | HttpHeaderType.Restricted
            )
          },
          {
            "SecWebSocketExtensions",
            new HttpHeaderInfo (
              "Sec-WebSocket-Extensions",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.Restricted
              | HttpHeaderType.MultiValueInRequest
            )
          },
          {
            "SecWebSocketKey",
            new HttpHeaderInfo (
              "Sec-WebSocket-Key",
              HttpHeaderType.Request | HttpHeaderType.Restricted
            )
          },
          {
            "SecWebSocketProtocol",
            new HttpHeaderInfo (
              "Sec-WebSocket-Protocol",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.MultiValueInRequest
            )
          },
          {
            "SecWebSocketVersion",
            new HttpHeaderInfo (
              "Sec-WebSocket-Version",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.Restricted
              | HttpHeaderType.MultiValueInResponse
            )
          },
          {
            "Server",
            new HttpHeaderInfo (
              "Server",
              HttpHeaderType.Response
            )
          },
          {
            "SetCookie",
            new HttpHeaderInfo (
              "Set-Cookie",
              HttpHeaderType.Response | HttpHeaderType.MultiValue
            )
          },
          {
            "SetCookie2",
            new HttpHeaderInfo (
              "Set-Cookie2",
              HttpHeaderType.Response | HttpHeaderType.MultiValue
            )
          },
          {
            "Te",
            new HttpHeaderInfo (
              "TE",
              HttpHeaderType.Request
            )
          },
          {
            "Trailer",
            new HttpHeaderInfo (
              "Trailer",
              HttpHeaderType.Request | HttpHeaderType.Response
            )
          },
          {
            "TransferEncoding",
            new HttpHeaderInfo (
              "Transfer-Encoding",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.Restricted
              | HttpHeaderType.MultiValue
            )
          },
          {
            "Translate",
            new HttpHeaderInfo (
              "Translate",
              HttpHeaderType.Request
            )
          },
          {
            "Upgrade",
            new HttpHeaderInfo (
              "Upgrade",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.MultiValue
            )
          },
          {
            "UserAgent",
            new HttpHeaderInfo (
              "User-Agent",
              HttpHeaderType.Request | HttpHeaderType.Restricted
            )
          },
          {
            "Vary",
            new HttpHeaderInfo (
              "Vary",
              HttpHeaderType.Response | HttpHeaderType.MultiValue
            )
          },
          {
            "Via",
            new HttpHeaderInfo (
              "Via",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.MultiValue
            )
          },
          {
            "Warning",
            new HttpHeaderInfo (
              "Warning",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.MultiValue
            )
          },
          {
            "WwwAuthenticate",
            new HttpHeaderInfo (
              "WWW-Authenticate",
              HttpHeaderType.Response
              | HttpHeaderType.Restricted
              | HttpHeaderType.MultiValue
            )
          }
        };
    }

    #endregion

    #region Internal Constructors

    internal WebHeaderCollection (HttpHeaderType state, bool internallyUsed)
    {
      _state = state;
      _internallyUsed = internallyUsed;
    }

    #endregion

    #region Protected Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="WebHeaderCollection"/> class
    /// from the specified instances of the <see cref="SerializationInfo"/> and
    /// <see cref="StreamingContext"/> classes.
    /// </summary>
    /// <param name="serializationInfo">
    /// A <see cref="SerializationInfo"/> that contains the serialized
    /// object data.
    /// </param>
    /// <param name="streamingContext">
    /// A <see cref="StreamingContext"/> that specifies the source for
    /// the deserialization.
    /// </param>
    /// <exception cref="ArgumentNullException">
    /// <paramref name="serializationInfo"/> is <see langword="null"/>.
    /// </exception>
    /// <exception cref="ArgumentException">
    /// An element with the specified name is not found in
    /// <paramref name="serializationInfo"/>.
    /// </exception>
    protected WebHeaderCollection (
      SerializationInfo serializationInfo, StreamingContext streamingContext
    )
    {
      if (serializationInfo == null)
        throw new ArgumentNullException ("serializationInfo");

      try {
        _internallyUsed = serializationInfo.GetBoolean ("InternallyUsed");
        _state = (HttpHeaderType) serializationInfo.GetInt32 ("State");

        var cnt = serializationInfo.GetInt32 ("Count");

        for (var i = 0; i < cnt; i++) {
          base.Add (
            serializationInfo.GetString (i.ToString ()),
            serializationInfo.GetString ((cnt + i).ToString ())
          );
        }
      }
      catch (SerializationException ex) {
        throw new ArgumentException (ex.Message, "serializationInfo", ex);
      }
    }

    #endregion

    #region Public Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="WebHeaderCollection"/>
    /// class.
    /// </summary>
    public WebHeaderCollection ()
    {
    }

    #endregion

    #region Internal Properties

    internal HttpHeaderType State {
      get {
        return _state;
      }
    }

    #endregion

    #region Public Properties

    /// <summary>
    /// Gets all header names in the collection.
    /// </summary>
    /// <value>
    /// An array of <see cref="string"/> that contains all header names in
    /// the collection.
    /// </value>
    public override string[] AllKeys {
      get {
        return base.AllKeys;
      }
    }

    /// <summary>
    /// Gets the number of headers in the collection.
    /// </summary>
    /// <value>
    /// An <see cref="int"/> that represents the number of headers in
    /// the collection.
    /// </value>
    public override int Count {
      get {
        return base.Count;
      }
    }

    /// <summary>
    /// Gets or sets the specified request header.
    /// </summary>
    /// <value>
    /// A <see cref="string"/> that represents the value of the request header.
    /// </value>
    /// <param name="header">
    ///   <para>
    ///   One of the <see cref="HttpRequestHeader"/> enum values.
    ///   </para>
    ///   <para>
    ///   It represents the request header to get or set.
    ///   </para>
    /// </param>
    /// <exception cref="ArgumentException">
    ///   <para>
    ///   <paramref name="header"/> is a restricted header.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   <paramref name="value"/> contains an invalid character.
    ///   </para>
    /// </exception>
    /// <exception cref="ArgumentOutOfRangeException">
    /// The length of <paramref name="value"/> is greater than 65,535
    /// characters.
    /// </exception>
    /// <exception cref="InvalidOperationException">
    /// This instance does not allow the request header.
    /// </exception>
    public string this[HttpRequestHeader header] {
      get {
        var key = header.ToString ();
        var name = getHeaderName (key);

        return Get (name);
      }

      set {
        Add (header, value);
      }
    }

    /// <summary>
    /// Gets or sets the specified response header.
    /// </summary>
    /// <value>
    /// A <see cref="string"/> that represents the value of the response header.
    /// </value>
    /// <param name="header">
    ///   <para>
    ///   One of the <see cref="HttpResponseHeader"/> enum values.
    ///   </para>
    ///   <para>
    ///   It represents the response header to get or set.
    ///   </para>
    /// </param>
    /// <exception cref="ArgumentException">
    ///   <para>
    ///   <paramref name="header"/> is a restricted header.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   <paramref name="value"/> contains an invalid character.
    ///   </para>
    /// </exception>
    /// <exception cref="ArgumentOutOfRangeException">
    /// The length of <paramref name="value"/> is greater than 65,535
    /// characters.
    /// </exception>
    /// <exception cref="InvalidOperationException">
    /// This instance does not allow the response header.
    /// </exception>
    public string this[HttpResponseHeader header] {
      get {
        var key = header.ToString ();
        var name = getHeaderName (key);

        return Get (name);
      }

      set {
        Add (header, value);
      }
    }

    /// <summary>
    /// Gets a collection of header names in the collection.
    /// </summary>
    /// <value>
    /// A <see cref="NameObjectCollectionBase.KeysCollection"/> that contains
    /// all header names in the collection.
    /// </value>
    public override NameObjectCollectionBase.KeysCollection Keys {
      get {
        return base.Keys;
      }
    }

    #endregion

    #region Private Methods

    private void add (string name, string value, HttpHeaderType headerType)
    {
      base.Add (name, value);

      if (_state != HttpHeaderType.Unspecified)
        return;

      if (headerType == HttpHeaderType.Unspecified)
        return;

      _state = headerType;
    }

    private void checkAllowed (HttpHeaderType headerType)
    {
      if (_state == HttpHeaderType.Unspecified)
        return;

      if (headerType == HttpHeaderType.Unspecified)
        return;

      if (headerType != _state) {
        var msg = "This instance does not allow the header.";

        throw new InvalidOperationException (msg);
      }
    }

    private static string checkName (string name)
    {
      if (name == null) {
        var msg = "The name is null.";

        throw new ArgumentNullException ("name", msg);
      }

      if (name.Length == 0) {
        var msg = "The name is an empty string.";

        throw new ArgumentException (msg, "name");
      }

      name = name.Trim ();

      if (name.Length == 0) {
        var msg = "The name is a string of spaces.";

        throw new ArgumentException (msg, "name");
      }

      if (!name.IsToken ()) {
        var msg = "The name contains an invalid character.";

        throw new ArgumentException (msg, "name");
      }

      return name;
    }

    private static string checkName (string name, string paramName)
    {
      if (name == null) {
        var msg = "The name is null.";

        throw new ArgumentNullException (paramName, msg);
      }

      if (name.Length == 0) {
        var msg = "The name is an empty string.";

        throw new ArgumentException (msg, paramName);
      }

      name = name.Trim ();

      if (name.Length == 0) {
        var msg = "The name is a string of spaces.";

        throw new ArgumentException (msg, paramName);
      }

      if (!name.IsToken ()) {
        var msg = "The name contains an invalid character.";

        throw new ArgumentException (msg, paramName);
      }

      return name;
    }

    private void checkRestricted (string name, HttpHeaderType headerType)
    {
      if (_internallyUsed)
        return;

      var res = headerType == HttpHeaderType.Response;

      if (isRestricted (name, res)) {
        var msg = "The header is a restricted header.";

        throw new ArgumentException (msg);
      }
    }

    private static string checkValue (string value)
    {
      if (value == null)
        return String.Empty;

      value = value.Trim ();

      var len = value.Length;

      if (len == 0)
        return value;

      if (len > 65535) {
        var msg = "The length of the value is greater than 65,535 characters.";

        throw new ArgumentOutOfRangeException ("value", msg);
      }

      if (!value.IsText ()) {
        var msg = "The value contains an invalid character.";

        throw new ArgumentException (msg, "value");
      }

      return value;
    }

    private static string checkValue (string value, string paramName)
    {
      if (value == null)
        return String.Empty;

      value = value.Trim ();

      var len = value.Length;

      if (len == 0)
        return value;

      if (len > 65535) {
        var msg = "The length of the value is greater than 65,535 characters.";

        throw new ArgumentOutOfRangeException (paramName, msg);
      }

      if (!value.IsText ()) {
        var msg = "The value contains an invalid character.";

        throw new ArgumentException (msg, paramName);
      }

      return value;
    }

    private static HttpHeaderInfo getHeaderInfo (string name)
    {
      var comparison = StringComparison.InvariantCultureIgnoreCase;

      foreach (var headerInfo in _headers.Values) {
        if (headerInfo.Name.Equals (name, comparison))
          return headerInfo;
      }

      return null;
    }

    private static string getHeaderName (string key)
    {
      HttpHeaderInfo headerInfo;

      return _headers.TryGetValue (key, out headerInfo)
             ? headerInfo.Name
             : null;
    }

    private static HttpHeaderType getHeaderType (string name)
    {
      var headerInfo = getHeaderInfo (name);

      if (headerInfo == null)
        return HttpHeaderType.Unspecified;

      if (headerInfo.IsRequest) {
        return !headerInfo.IsResponse
               ? HttpHeaderType.Request
               : HttpHeaderType.Unspecified;
      }

      return headerInfo.IsResponse
             ? HttpHeaderType.Response
             : HttpHeaderType.Unspecified;
    }

    private static bool isMultiValue (string name, bool response)
    {
      var headerInfo = getHeaderInfo (name);

      return headerInfo != null && headerInfo.IsMultiValue (response);
    }

    private static bool isRestricted (string name, bool response)
    {
      var headerInfo = getHeaderInfo (name);

      return headerInfo != null && headerInfo.IsRestricted (response);
    }

    private void set (string name, string value, HttpHeaderType headerType)
    {
      base.Set (name, value);

      if (_state != HttpHeaderType.Unspecified)
        return;

      if (headerType == HttpHeaderType.Unspecified)
        return;

      _state = headerType;
    }

    #endregion

    #region Internal Methods

    internal void InternalRemove (string name)
    {
      base.Remove (name);
    }

    internal void InternalSet (string header, bool response)
    {
      var idx = header.IndexOf (':');

      if (idx == -1) {
        var msg = "It does not contain a colon character.";

        throw new ArgumentException (msg, "header");
      }

      var name = header.Substring (0, idx);
      var val = idx < header.Length - 1
                ? header.Substring (idx + 1)
                : String.Empty;

      InternalSet (name, val, response);
    }

    internal void InternalSet (string name, string value, bool response)
    {
      value = checkValue (value);

      if (isMultiValue (name, response)) {
        base.Add (name, value);

        return;
      }

      base.Set (name, value);
    }

    internal string ToStringMultiValue (bool response)
    {
      var cnt = Count;

      if (cnt == 0)
        return "\r\n";

      var buff = new StringBuilder ();

      for (var i = 0; i < cnt; i++) {
        var name = GetKey (i);

        if (isMultiValue (name, response)) {
          foreach (var val in GetValues (i))
            buff.AppendFormat ("{0}: {1}\r\n", name, val);

          continue;
        }

        buff.AppendFormat ("{0}: {1}\r\n", name, Get (i));
      }

      buff.Append ("\r\n");

      return buff.ToString ();
    }

    #endregion

    #region Protected Methods

    /// <summary>
    /// Adds a header to the collection without checking if the header is on
    /// the restricted header list.
    /// </summary>
    /// <param name="headerName">
    /// A <see cref="string"/> that specifies the name of the header to add.
    /// </param>
    /// <param name="headerValue">
    /// A <see cref="string"/> that specifies the value of the header to add.
    /// </param>
    /// <exception cref="ArgumentNullException">
    /// <paramref name="headerName"/> is <see langword="null"/>.
    /// </exception>
    /// <exception cref="ArgumentException">
    ///   <para>
    ///   <paramref name="headerName"/> is an empty string.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   <paramref name="headerName"/> is a string of spaces.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   <paramref name="headerName"/> contains an invalid character.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   <paramref name="headerValue"/> contains an invalid character.
    ///   </para>
    /// </exception>
    /// <exception cref="ArgumentOutOfRangeException">
    /// The length of <paramref name="headerValue"/> is greater than 65,535
    /// characters.
    /// </exception>
    /// <exception cref="InvalidOperationException">
    /// This instance does not allow the header.
    /// </exception>
    protected void AddWithoutValidate (string headerName, string headerValue)
    {
      headerName = checkName (headerName, "headerName");
      headerValue = checkValue (headerValue, "headerValue");

      var headerType = getHeaderType (headerName);

      checkAllowed (headerType);
      add (headerName, headerValue, headerType);
    }

    #endregion

    #region Public Methods

    /// <summary>
    /// Adds the specified header to the collection.
    /// </summary>
    /// <param name="header">
    /// A <see cref="string"/> that specifies the header to add,
    /// with the name and value separated by a colon character (':').
    /// </param>
    /// <exception cref="ArgumentNullException">
    /// <paramref name="header"/> is <see langword="null"/>.
    /// </exception>
    /// <exception cref="ArgumentException">
    ///   <para>
    ///   <paramref name="header"/> is an empty string.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   <paramref name="header"/> does not contain a colon character.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   The name part of <paramref name="header"/> is an empty string.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   The name part of <paramref name="header"/> is a string of spaces.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   The name part of <paramref name="header"/> contains an invalid
    ///   character.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   The value part of <paramref name="header"/> contains an invalid
    ///   character.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   <paramref name="header"/> is a restricted header.
    ///   </para>
    /// </exception>
    /// <exception cref="ArgumentOutOfRangeException">
    /// The length of the value part of <paramref name="header"/> is greater
    /// than 65,535 characters.
    /// </exception>
    /// <exception cref="InvalidOperationException">
    /// This instance does not allow the header.
    /// </exception>
    public void Add (string header)
    {
      if (header == null)
        throw new ArgumentNullException ("header");

      var len = header.Length;

      if (len == 0) {
        var msg = "An empty string.";

        throw new ArgumentException (msg, "header");
      }

      var idx = header.IndexOf (':');

      if (idx == -1) {
        var msg = "It does not contain a colon character.";

        throw new ArgumentException (msg, "header");
      }

      var name = header.Substring (0, idx);
      var val = idx < len - 1
                ? header.Substring (idx + 1)
                : String.Empty;

      name = checkName (name, "header");
      val = checkValue (val, "header");

      var headerType = getHeaderType (name);

      checkRestricted (name, headerType);
      checkAllowed (headerType);
      add (name, val, headerType);
    }

    /// <summary>
    /// Adds the specified request header with the specified value to
    /// the collection.
    /// </summary>
    /// <param name="header">
    ///   <para>
    ///   One of the <see cref="HttpRequestHeader"/> enum values.
    ///   </para>
    ///   <para>
    ///   It specifies the request header to add.
    ///   </para>
    /// </param>
    /// <param name="value">
    /// A <see cref="string"/> that specifies the value of the header to add.
    /// </param>
    /// <exception cref="ArgumentException">
    ///   <para>
    ///   <paramref name="header"/> is a restricted header.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   <paramref name="value"/> contains an invalid character.
    ///   </para>
    /// </exception>
    /// <exception cref="ArgumentOutOfRangeException">
    /// The length of <paramref name="value"/> is greater than 65,535
    /// characters.
    /// </exception>
    /// <exception cref="InvalidOperationException">
    /// This instance does not allow the request header.
    /// </exception>
    public void Add (HttpRequestHeader header, string value)
    {
      value = checkValue (value);

      var key = header.ToString ();
      var name = getHeaderName (key);

      checkRestricted (name, HttpHeaderType.Request);
      checkAllowed (HttpHeaderType.Request);
      add (name, value, HttpHeaderType.Request);
    }

    /// <summary>
    /// Adds the specified response header with the specified value to
    /// the collection.
    /// </summary>
    /// <param name="header">
    ///   <para><commit_msg>[Modify] Replace it<commit_after>#region License
/*
 * WebHeaderCollection.cs
 *
 * This code is derived from WebHeaderCollection.cs (System.Net) of Mono
 * (http://www.mono-project.com).
 *
 * The MIT License
 *
 * Copyright (c) 2003 Ximian, Inc. (http://www.ximian.com)
 * Copyright (c) 2007 Novell, Inc. (http://www.novell.com)
 * Copyright (c) 2012-2015 sta.blockhead
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#endregion

#region Authors
/*
 * Authors:
 * - <NAME> <loz<EMAIL>>
 * - <NAME> <<EMAIL>>
 * - <NAME> <<EMAIL>>
 */
#endregion

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Security.Permissions;
using System.Text;

namespace WebSocketSharp.Net
{
  /// <summary>
  /// Provides a collection of the HTTP headers associated with a request or
  /// response.
  /// </summary>
  [Serializable]
  [ComVisible (true)]
  public class WebHeaderCollection : NameValueCollection, ISerializable
  {
    #region Private Fields

    private static readonly Dictionary<string, HttpHeaderInfo> _headers;
    private bool                                               _internallyUsed;
    private HttpHeaderType                                     _state;

    #endregion

    #region Static Constructor

    static WebHeaderCollection ()
    {
      _headers =
        new Dictionary<string, HttpHeaderInfo> (
          StringComparer.InvariantCultureIgnoreCase
        )
        {
          {
            "Accept",
            new HttpHeaderInfo (
              "Accept",
              HttpHeaderType.Request
              | HttpHeaderType.Restricted
              | HttpHeaderType.MultiValue
            )
          },
          {
            "AcceptCharset",
            new HttpHeaderInfo (
              "Accept-Charset",
              HttpHeaderType.Request | HttpHeaderType.MultiValue
            )
          },
          {
            "AcceptEncoding",
            new HttpHeaderInfo (
              "Accept-Encoding",
              HttpHeaderType.Request | HttpHeaderType.MultiValue
            )
          },
          {
            "AcceptLanguage",
            new HttpHeaderInfo (
              "Accept-Language",
              HttpHeaderType.Request | HttpHeaderType.MultiValue
            )
          },
          {
            "AcceptRanges",
            new HttpHeaderInfo (
              "Accept-Ranges",
              HttpHeaderType.Response | HttpHeaderType.MultiValue
            )
          },
          {
            "Age",
            new HttpHeaderInfo (
              "Age",
              HttpHeaderType.Response
            )
          },
          {
            "Allow",
            new HttpHeaderInfo (
              "Allow",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.MultiValue
            )
          },
          {
            "Authorization",
            new HttpHeaderInfo (
              "Authorization",
              HttpHeaderType.Request | HttpHeaderType.MultiValue
            )
          },
          {
            "CacheControl",
            new HttpHeaderInfo (
              "Cache-Control",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.MultiValue
            )
          },
          {
            "Connection",
            new HttpHeaderInfo (
              "Connection",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.Restricted
              | HttpHeaderType.MultiValue
            )
          },
          {
            "ContentEncoding",
            new HttpHeaderInfo (
              "Content-Encoding",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.MultiValue
            )
          },
          {
            "ContentLanguage",
            new HttpHeaderInfo (
              "Content-Language",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.MultiValue
            )
          },
          {
            "ContentLength",
            new HttpHeaderInfo (
              "Content-Length",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.Restricted
            )
          },
          {
            "ContentLocation",
            new HttpHeaderInfo (
              "Content-Location",
              HttpHeaderType.Request | HttpHeaderType.Response
            )
          },
          {
            "ContentMd5",
            new HttpHeaderInfo (
              "Content-MD5",
              HttpHeaderType.Request | HttpHeaderType.Response
            )
          },
          {
            "ContentRange",
            new HttpHeaderInfo (
              "Content-Range",
              HttpHeaderType.Request | HttpHeaderType.Response
            )
          },
          {
            "ContentType",
            new HttpHeaderInfo (
              "Content-Type",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.Restricted
            )
          },
          {
            "Cookie",
            new HttpHeaderInfo (
              "Cookie",
              HttpHeaderType.Request
            )
          },
          {
            "Cookie2",
            new HttpHeaderInfo (
              "Cookie2",
              HttpHeaderType.Request
            )
          },
          {
            "Date",
            new HttpHeaderInfo (
              "Date",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.Restricted
            )
          },
          {
            "Expect",
            new HttpHeaderInfo (
              "Expect",
              HttpHeaderType.Request
              | HttpHeaderType.Restricted
              | HttpHeaderType.MultiValue
            )
          },
          {
            "Expires",
            new HttpHeaderInfo (
              "Expires",
              HttpHeaderType.Request | HttpHeaderType.Response
            )
          },
          {
            "ETag",
            new HttpHeaderInfo (
              "ETag",
              HttpHeaderType.Response
            )
          },
          {
            "From",
            new HttpHeaderInfo (
              "From",
              HttpHeaderType.Request
            )
          },
          {
            "Host",
            new HttpHeaderInfo (
              "Host",
              HttpHeaderType.Request | HttpHeaderType.Restricted
            )
          },
          {
            "IfMatch",
            new HttpHeaderInfo (
              "If-Match",
              HttpHeaderType.Request | HttpHeaderType.MultiValue
            )
          },
          {
            "IfModifiedSince",
            new HttpHeaderInfo (
              "If-Modified-Since",
              HttpHeaderType.Request | HttpHeaderType.Restricted
            )
          },
          {
            "IfNoneMatch",
            new HttpHeaderInfo (
              "If-None-Match",
              HttpHeaderType.Request | HttpHeaderType.MultiValue
            )
          },
          {
            "IfRange",
            new HttpHeaderInfo (
              "If-Range",
              HttpHeaderType.Request
            )
          },
          {
            "IfUnmodifiedSince",
            new HttpHeaderInfo (
              "If-Unmodified-Since",
              HttpHeaderType.Request
            )
          },
          {
            "KeepAlive",
            new HttpHeaderInfo (
              "Keep-Alive",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.MultiValue
            )
          },
          {
            "LastModified",
            new HttpHeaderInfo (
              "Last-Modified",
              HttpHeaderType.Request | HttpHeaderType.Response
            )
          },
          {
            "Location",
            new HttpHeaderInfo (
              "Location",
              HttpHeaderType.Response
            )
          },
          {
            "MaxForwards",
            new HttpHeaderInfo (
              "Max-Forwards",
              HttpHeaderType.Request
            )
          },
          {
            "Pragma",
            new HttpHeaderInfo (
              "Pragma",
              HttpHeaderType.Request | HttpHeaderType.Response
            )
          },
          {
            "ProxyAuthenticate",
            new HttpHeaderInfo (
              "Proxy-Authenticate",
              HttpHeaderType.Response | HttpHeaderType.MultiValue
            )
          },
          {
            "ProxyAuthorization",
            new HttpHeaderInfo (
              "Proxy-Authorization",
              HttpHeaderType.Request
            )
          },
          {
            "ProxyConnection",
            new HttpHeaderInfo (
              "Proxy-Connection",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.Restricted
            )
          },
          {
            "Public",
            new HttpHeaderInfo (
              "Public",
              HttpHeaderType.Response | HttpHeaderType.MultiValue
            )
          },
          {
            "Range",
            new HttpHeaderInfo (
              "Range",
              HttpHeaderType.Request
              | HttpHeaderType.Restricted
              | HttpHeaderType.MultiValue
            )
          },
          {
            "Referer",
            new HttpHeaderInfo (
              "Referer",
              HttpHeaderType.Request | HttpHeaderType.Restricted
            )
          },
          {
            "RetryAfter",
            new HttpHeaderInfo (
              "Retry-After",
              HttpHeaderType.Response
            )
          },
          {
            "SecWebSocketAccept",
            new HttpHeaderInfo (
              "Sec-WebSocket-Accept",
              HttpHeaderType.Response | HttpHeaderType.Restricted
            )
          },
          {
            "SecWebSocketExtensions",
            new HttpHeaderInfo (
              "Sec-WebSocket-Extensions",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.Restricted
              | HttpHeaderType.MultiValueInRequest
            )
          },
          {
            "SecWebSocketKey",
            new HttpHeaderInfo (
              "Sec-WebSocket-Key",
              HttpHeaderType.Request | HttpHeaderType.Restricted
            )
          },
          {
            "SecWebSocketProtocol",
            new HttpHeaderInfo (
              "Sec-WebSocket-Protocol",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.MultiValueInRequest
            )
          },
          {
            "SecWebSocketVersion",
            new HttpHeaderInfo (
              "Sec-WebSocket-Version",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.Restricted
              | HttpHeaderType.MultiValueInResponse
            )
          },
          {
            "Server",
            new HttpHeaderInfo (
              "Server",
              HttpHeaderType.Response
            )
          },
          {
            "SetCookie",
            new HttpHeaderInfo (
              "Set-Cookie",
              HttpHeaderType.Response | HttpHeaderType.MultiValue
            )
          },
          {
            "SetCookie2",
            new HttpHeaderInfo (
              "Set-Cookie2",
              HttpHeaderType.Response | HttpHeaderType.MultiValue
            )
          },
          {
            "Te",
            new HttpHeaderInfo (
              "TE",
              HttpHeaderType.Request
            )
          },
          {
            "Trailer",
            new HttpHeaderInfo (
              "Trailer",
              HttpHeaderType.Request | HttpHeaderType.Response
            )
          },
          {
            "TransferEncoding",
            new HttpHeaderInfo (
              "Transfer-Encoding",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.Restricted
              | HttpHeaderType.MultiValue
            )
          },
          {
            "Translate",
            new HttpHeaderInfo (
              "Translate",
              HttpHeaderType.Request
            )
          },
          {
            "Upgrade",
            new HttpHeaderInfo (
              "Upgrade",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.MultiValue
            )
          },
          {
            "UserAgent",
            new HttpHeaderInfo (
              "User-Agent",
              HttpHeaderType.Request | HttpHeaderType.Restricted
            )
          },
          {
            "Vary",
            new HttpHeaderInfo (
              "Vary",
              HttpHeaderType.Response | HttpHeaderType.MultiValue
            )
          },
          {
            "Via",
            new HttpHeaderInfo (
              "Via",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.MultiValue
            )
          },
          {
            "Warning",
            new HttpHeaderInfo (
              "Warning",
              HttpHeaderType.Request
              | HttpHeaderType.Response
              | HttpHeaderType.MultiValue
            )
          },
          {
            "WwwAuthenticate",
            new HttpHeaderInfo (
              "WWW-Authenticate",
              HttpHeaderType.Response
              | HttpHeaderType.Restricted
              | HttpHeaderType.MultiValue
            )
          }
        };
    }

    #endregion

    #region Internal Constructors

    internal WebHeaderCollection (HttpHeaderType state, bool internallyUsed)
    {
      _state = state;
      _internallyUsed = internallyUsed;
    }

    #endregion

    #region Protected Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="WebHeaderCollection"/> class
    /// from the specified instances of the <see cref="SerializationInfo"/> and
    /// <see cref="StreamingContext"/> classes.
    /// </summary>
    /// <param name="serializationInfo">
    /// A <see cref="SerializationInfo"/> that contains the serialized
    /// object data.
    /// </param>
    /// <param name="streamingContext">
    /// A <see cref="StreamingContext"/> that specifies the source for
    /// the deserialization.
    /// </param>
    /// <exception cref="ArgumentNullException">
    /// <paramref name="serializationInfo"/> is <see langword="null"/>.
    /// </exception>
    /// <exception cref="ArgumentException">
    /// An element with the specified name is not found in
    /// <paramref name="serializationInfo"/>.
    /// </exception>
    protected WebHeaderCollection (
      SerializationInfo serializationInfo, StreamingContext streamingContext
    )
    {
      if (serializationInfo == null)
        throw new ArgumentNullException ("serializationInfo");

      try {
        _internallyUsed = serializationInfo.GetBoolean ("InternallyUsed");
        _state = (HttpHeaderType) serializationInfo.GetInt32 ("State");

        var cnt = serializationInfo.GetInt32 ("Count");

        for (var i = 0; i < cnt; i++) {
          base.Add (
            serializationInfo.GetString (i.ToString ()),
            serializationInfo.GetString ((cnt + i).ToString ())
          );
        }
      }
      catch (SerializationException ex) {
        throw new ArgumentException (ex.Message, "serializationInfo", ex);
      }
    }

    #endregion

    #region Public Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="WebHeaderCollection"/>
    /// class.
    /// </summary>
    public WebHeaderCollection ()
    {
    }

    #endregion

    #region Internal Properties

    internal HttpHeaderType State {
      get {
        return _state;
      }
    }

    #endregion

    #region Public Properties

    /// <summary>
    /// Gets all header names in the collection.
    /// </summary>
    /// <value>
    /// An array of <see cref="string"/> that contains all header names in
    /// the collection.
    /// </value>
    public override string[] AllKeys {
      get {
        return base.AllKeys;
      }
    }

    /// <summary>
    /// Gets the number of headers in the collection.
    /// </summary>
    /// <value>
    /// An <see cref="int"/> that represents the number of headers in
    /// the collection.
    /// </value>
    public override int Count {
      get {
        return base.Count;
      }
    }

    /// <summary>
    /// Gets or sets the specified request header.
    /// </summary>
    /// <value>
    /// A <see cref="string"/> that represents the value of the request header.
    /// </value>
    /// <param name="header">
    ///   <para>
    ///   One of the <see cref="HttpRequestHeader"/> enum values.
    ///   </para>
    ///   <para>
    ///   It represents the request header to get or set.
    ///   </para>
    /// </param>
    /// <exception cref="ArgumentException">
    ///   <para>
    ///   <paramref name="header"/> is a restricted header.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   <paramref name="value"/> contains an invalid character.
    ///   </para>
    /// </exception>
    /// <exception cref="ArgumentOutOfRangeException">
    /// The length of <paramref name="value"/> is greater than 65,535
    /// characters.
    /// </exception>
    /// <exception cref="InvalidOperationException">
    /// This instance does not allow the request header.
    /// </exception>
    public string this[HttpRequestHeader header] {
      get {
        var key = header.ToString ();
        var name = getHeaderName (key);

        return Get (name);
      }

      set {
        Add (header, value);
      }
    }

    /// <summary>
    /// Gets or sets the specified response header.
    /// </summary>
    /// <value>
    /// A <see cref="string"/> that represents the value of the response header.
    /// </value>
    /// <param name="header">
    ///   <para>
    ///   One of the <see cref="HttpResponseHeader"/> enum values.
    ///   </para>
    ///   <para>
    ///   It represents the response header to get or set.
    ///   </para>
    /// </param>
    /// <exception cref="ArgumentException">
    ///   <para>
    ///   <paramref name="header"/> is a restricted header.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   <paramref name="value"/> contains an invalid character.
    ///   </para>
    /// </exception>
    /// <exception cref="ArgumentOutOfRangeException">
    /// The length of <paramref name="value"/> is greater than 65,535
    /// characters.
    /// </exception>
    /// <exception cref="InvalidOperationException">
    /// This instance does not allow the response header.
    /// </exception>
    public string this[HttpResponseHeader header] {
      get {
        var key = header.ToString ();
        var name = getHeaderName (key);

        return Get (name);
      }

      set {
        Add (header, value);
      }
    }

    /// <summary>
    /// Gets a collection of header names in the collection.
    /// </summary>
    /// <value>
    /// A <see cref="NameObjectCollectionBase.KeysCollection"/> that contains
    /// all header names in the collection.
    /// </value>
    public override NameObjectCollectionBase.KeysCollection Keys {
      get {
        return base.Keys;
      }
    }

    #endregion

    #region Private Methods

    private void add (string name, string value, HttpHeaderType headerType)
    {
      base.Add (name, value);

      if (_state != HttpHeaderType.Unspecified)
        return;

      if (headerType == HttpHeaderType.Unspecified)
        return;

      _state = headerType;
    }

    private void checkAllowed (HttpHeaderType headerType)
    {
      if (_state == HttpHeaderType.Unspecified)
        return;

      if (headerType == HttpHeaderType.Unspecified)
        return;

      if (headerType != _state) {
        var msg = "This instance does not allow the header.";

        throw new InvalidOperationException (msg);
      }
    }

    private static string checkName (string name)
    {
      if (name == null) {
        var msg = "The name is null.";

        throw new ArgumentNullException ("name", msg);
      }

      if (name.Length == 0) {
        var msg = "The name is an empty string.";

        throw new ArgumentException (msg, "name");
      }

      name = name.Trim ();

      if (name.Length == 0) {
        var msg = "The name is a string of spaces.";

        throw new ArgumentException (msg, "name");
      }

      if (!name.IsToken ()) {
        var msg = "The name contains an invalid character.";

        throw new ArgumentException (msg, "name");
      }

      return name;
    }

    private static string checkName (string name, string paramName)
    {
      if (name == null) {
        var msg = "The name is null.";

        throw new ArgumentNullException (paramName, msg);
      }

      if (name.Length == 0) {
        var msg = "The name is an empty string.";

        throw new ArgumentException (msg, paramName);
      }

      name = name.Trim ();

      if (name.Length == 0) {
        var msg = "The name is a string of spaces.";

        throw new ArgumentException (msg, paramName);
      }

      if (!name.IsToken ()) {
        var msg = "The name contains an invalid character.";

        throw new ArgumentException (msg, paramName);
      }

      return name;
    }

    private void checkRestricted (string name, HttpHeaderType headerType)
    {
      if (_internallyUsed)
        return;

      var res = headerType == HttpHeaderType.Response;

      if (isRestricted (name, res)) {
        var msg = "The header is a restricted header.";

        throw new ArgumentException (msg);
      }
    }

    private static string checkValue (string value)
    {
      if (value == null)
        return String.Empty;

      value = value.Trim ();

      var len = value.Length;

      if (len == 0)
        return value;

      if (len > 65535) {
        var msg = "The length of the value is greater than 65,535 characters.";

        throw new ArgumentOutOfRangeException ("value", msg);
      }

      if (!value.IsText ()) {
        var msg = "The value contains an invalid character.";

        throw new ArgumentException (msg, "value");
      }

      return value;
    }

    private static string checkValue (string value, string paramName)
    {
      if (value == null)
        return String.Empty;

      value = value.Trim ();

      var len = value.Length;

      if (len == 0)
        return value;

      if (len > 65535) {
        var msg = "The length of the value is greater than 65,535 characters.";

        throw new ArgumentOutOfRangeException (paramName, msg);
      }

      if (!value.IsText ()) {
        var msg = "The value contains an invalid character.";

        throw new ArgumentException (msg, paramName);
      }

      return value;
    }

    private static HttpHeaderInfo getHeaderInfo (string name)
    {
      var comparison = StringComparison.InvariantCultureIgnoreCase;

      foreach (var headerInfo in _headers.Values) {
        if (headerInfo.Name.Equals (name, comparison))
          return headerInfo;
      }

      return null;
    }

    private static string getHeaderName (string key)
    {
      HttpHeaderInfo headerInfo;

      return _headers.TryGetValue (key, out headerInfo)
             ? headerInfo.Name
             : null;
    }

    private static HttpHeaderType getHeaderType (string name)
    {
      var headerInfo = getHeaderInfo (name);

      if (headerInfo == null)
        return HttpHeaderType.Unspecified;

      if (headerInfo.IsRequest) {
        return !headerInfo.IsResponse
               ? HttpHeaderType.Request
               : HttpHeaderType.Unspecified;
      }

      return headerInfo.IsResponse
             ? HttpHeaderType.Response
             : HttpHeaderType.Unspecified;
    }

    private static bool isMultiValue (string name, bool response)
    {
      var headerInfo = getHeaderInfo (name);

      return headerInfo != null && headerInfo.IsMultiValue (response);
    }

    private static bool isRestricted (string name, bool response)
    {
      var headerInfo = getHeaderInfo (name);

      return headerInfo != null && headerInfo.IsRestricted (response);
    }

    private void set (string name, string value, HttpHeaderType headerType)
    {
      base.Set (name, value);

      if (_state != HttpHeaderType.Unspecified)
        return;

      if (headerType == HttpHeaderType.Unspecified)
        return;

      _state = headerType;
    }

    #endregion

    #region Internal Methods

    internal void InternalRemove (string name)
    {
      base.Remove (name);
    }

    internal void InternalSet (string header, bool response)
    {
      var idx = header.IndexOf (':');

      if (idx == -1) {
        var msg = "It does not contain a colon character.";

        throw new ArgumentException (msg, "header");
      }

      var name = header.Substring (0, idx);
      var val = idx < header.Length - 1
                ? header.Substring (idx + 1)
                : String.Empty;

      InternalSet (name, val, response);
    }

    internal void InternalSet (string name, string value, bool response)
    {
      value = checkValue (value);

      if (isMultiValue (name, response)) {
        base.Add (name, value);

        return;
      }

      base.Set (name, value);
    }

    internal string ToStringMultiValue (bool response)
    {
      var cnt = Count;

      if (cnt == 0)
        return "\r\n";

      var buff = new StringBuilder ();

      for (var i = 0; i < cnt; i++) {
        var name = GetKey (i);

        if (isMultiValue (name, response)) {
          foreach (var val in GetValues (i))
            buff.AppendFormat ("{0}: {1}\r\n", name, val);

          continue;
        }

        buff.AppendFormat ("{0}: {1}\r\n", name, Get (i));
      }

      buff.Append ("\r\n");

      return buff.ToString ();
    }

    #endregion

    #region Protected Methods

    /// <summary>
    /// Adds a header to the collection without checking if the header is on
    /// the restricted header list.
    /// </summary>
    /// <param name="headerName">
    /// A <see cref="string"/> that specifies the name of the header to add.
    /// </param>
    /// <param name="headerValue">
    /// A <see cref="string"/> that specifies the value of the header to add.
    /// </param>
    /// <exception cref="ArgumentNullException">
    /// <paramref name="headerName"/> is <see langword="null"/>.
    /// </exception>
    /// <exception cref="ArgumentException">
    ///   <para>
    ///   <paramref name="headerName"/> is an empty string.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   <paramref name="headerName"/> is a string of spaces.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   <paramref name="headerName"/> contains an invalid character.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   <paramref name="headerValue"/> contains an invalid character.
    ///   </para>
    /// </exception>
    /// <exception cref="ArgumentOutOfRangeException">
    /// The length of <paramref name="headerValue"/> is greater than 65,535
    /// characters.
    /// </exception>
    /// <exception cref="InvalidOperationException">
    /// This instance does not allow the header.
    /// </exception>
    protected void AddWithoutValidate (string headerName, string headerValue)
    {
      headerName = checkName (headerName, "headerName");
      headerValue = checkValue (headerValue, "headerValue");

      var headerType = getHeaderType (headerName);

      checkAllowed (headerType);
      add (headerName, headerValue, headerType);
    }

    #endregion

    #region Public Methods

    /// <summary>
    /// Adds the specified header to the collection.
    /// </summary>
    /// <param name="header">
    /// A <see cref="string"/> that specifies the header to add,
    /// with the name and value separated by a colon character (':').
    /// </param>
    /// <exception cref="ArgumentNullException">
    /// <paramref name="header"/> is <see langword="null"/>.
    /// </exception>
    /// <exception cref="ArgumentException">
    ///   <para>
    ///   <paramref name="header"/> is an empty string.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   <paramref name="header"/> does not contain a colon character.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   The name part of <paramref name="header"/> is an empty string.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   The name part of <paramref name="header"/> is a string of spaces.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   The name part of <paramref name="header"/> contains an invalid
    ///   character.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   The value part of <paramref name="header"/> contains an invalid
    ///   character.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   <paramref name="header"/> is a restricted header.
    ///   </para>
    /// </exception>
    /// <exception cref="ArgumentOutOfRangeException">
    /// The length of the value part of <paramref name="header"/> is greater
    /// than 65,535 characters.
    /// </exception>
    /// <exception cref="InvalidOperationException">
    /// This instance does not allow the header.
    /// </exception>
    public void Add (string header)
    {
      if (header == null)
        throw new ArgumentNullException ("header");

      var len = header.Length;

      if (len == 0) {
        var msg = "An empty string.";

        throw new ArgumentException (msg, "header");
      }

      var idx = header.IndexOf (':');

      if (idx == -1) {
        var msg = "It does not contain a colon character.";

        throw new ArgumentException (msg, "header");
      }

      var name = header.Substring (0, idx);
      var val = idx < len - 1
                ? header.Substring (idx + 1)
                : String.Empty;

      name = checkName (name, "header");
      val = checkValue (val, "header");

      var headerType = getHeaderType (name);

      checkRestricted (name, headerType);
      checkAllowed (headerType);
      add (name, val, headerType);
    }

    /// <summary>
    /// Adds the specified request header with the specified value to
    /// the collection.
    /// </summary>
    /// <param name="header">
    ///   <para>
    ///   One of the <see cref="HttpRequestHeader"/> enum values.
    ///   </para>
    ///   <para>
    ///   It specifies the request header to add.
    ///   </para>
    /// </param>
    /// <param name="value">
    /// A <see cref="string"/> that specifies the value of the header to add.
    /// </param>
    /// <exception cref="ArgumentException">
    ///   <para>
    ///   <paramref name="header"/> is a restricted header.
    ///   </para>
    ///   <para>
    ///   -or-
    ///   </para>
    ///   <para>
    ///   <paramref name="value"/> contains an invalid character.
    ///   </para>
    /// </exception>
    /// <exception cref="ArgumentOutOfRangeException">
    /// The length of <paramref name="value"/> is greater than 65,535
    /// characters.
    /// </exception>
    /// <exception cref="InvalidOperationException">
    /// This instance does not allow the request header.
    /// </exception>
    public void Add (HttpRequestHeader header, string value)
    {
      value = checkValue (value, "value");

      var key = header.ToString ();
      var name = getHeaderName (key);

      checkRestricted (name, HttpHeaderType.Request);
      checkAllowed (HttpHeaderType.Request);
      add (name, value, HttpHeaderType.Request);
    }

    /// <summary>
    /// Adds the specified response header with the specified value to
    /// the collection.
    /// </summary>
    /// <param name="header">
    ///   <para>
{'fraction_non_alphanumeric': 0.1110305648430334, 'fraction_numerical': 0.002751723525756724, 'mean_word_length': 1.45603517186251, 'pattern_counts': {'":': 0, '<': 451, '<?xml version=': 0, '>': 447, 'https://': 0, 'lorem ipsum': 0, 'www.': 6, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2482831', 'n_tokens_mistral': 17584, 'n_tokens_neox': 17085, 'n_words': 4532}