context: #region MIT License
  /*
   * Ext.cs
   *  IsPredefinedScheme and MaybeUri methods derived from System.Uri.cs
   *  GetStatusDescription method derived from System.Net.HttpListenerResponse.cs
   *
   * The MIT License
   *
   * Copyright (c) 2010-2013 sta.blockhead
   *
   * System.Uri.cs
   *  (C) 2001 Garrett Rooney
   *  (C) 2003 Ian MacLean
   *  (C) 2003 Ben Maurer
   *  Copyright (C) 2003, 2005, 2009 Novell, Inc. (http://www.novell.com)
   *  Copyright (c) 2009 Stephane Delcroix
   *
   * System.Net.HttpListenerResponse.cs
   *  Copyright (C) 2003, 2005, 2009 Novell, Inc. (http://www.novell.com)
   * 
   * 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
  
  using System;
  using System.Collections.Generic;
  using System.Collections.Specialized;
  using System.IO;
  using System.Linq;
  using System.Net.Sockets;
  using System.Text;
  using WebSocketSharp.Net;
  using WebSocketSharp.Net.WebSockets;
  
  namespace WebSocketSharp {
  
    /// <summary>
    /// Provides a set of static methods for the websocket-sharp.
    /// </summary>
    public static class Ext {
  
      #region Private Methods
  
      private static void times(this ulong n, Action act)
      {
        for (ulong i = 0; i < n; i++)
          act();
      }
  
      private static void times(this ulong n, Action<ulong> act)
      {
        for (ulong i = 0; i < n; i++)
          act(i);
      }
  
      #endregion
  
      #region Public Methods
  
      /// <summary>
      /// Accepts a WebSocket connection by the <see cref="TcpListener"/>.
      /// </summary>
      /// <returns>
      /// A <see cref="TcpListenerWebSocketContext"/> that contains a WebSocket connection.
      /// </returns>
      /// <param name="listener">
      /// A <see cref="TcpListener"/> that provides a TCP connection to accept a WebSocket connection.
      /// </param>
      /// <param name="secure">
      /// A <see cref="bool"/> that indicates a secure connection or not. (<c>true</c> indicates a secure connection.)
      /// </param>
      /// <exception cref="ArgumentNullException">
      /// <paramref name="listener"/> is <see langword="null"/>.
      /// </exception>
      public static TcpListenerWebSocketContext AcceptWebSocket(this TcpListener listener, bool secure)
      {
        if (listener.IsNull())
          throw new ArgumentNullException("listener");
  
        var client = listener.AcceptTcpClient();
        return new TcpListenerWebSocketContext(client, secure);
      }
  
      /// <summary>
      /// Accepts a WebSocket connection asynchronously by the <see cref="TcpListener"/>.
      /// </summary>
      /// <param name="listener">
      /// A <see cref="TcpListener"/> that provides a TCP connection to accept a WebSocket connection.
      /// </param>
      /// <param name="secure">
      /// A <see cref="bool"/> that indicates a secure connection or not. (<c>true</c> indicates a secure connection.)
      /// </param>
      /// <param name="completed">
      /// An Action&lt;TcpListenerWebSocketContext&gt; delegate that contains the method(s) that is called when an asynchronous operation completes.
      /// </param>
      /// <exception cref="ArgumentNullException">
      /// <paramref name="listener"/> is <see langword="null"/>.
      /// </exception>
      public static void AcceptWebSocketAsync(this TcpListener listener, bool secure, Action<TcpListenerWebSocketContext> completed)
      {
        if (listener.IsNull())
          throw new ArgumentNullException("listener");
  
        AsyncCallback callback = (ar) =>
        {
          var client  = listener.EndAcceptTcpClient(ar);
          var context = new TcpListenerWebSocketContext(client, secure);
          completed(context);
        };
  
        listener.BeginAcceptTcpClient(callback, null);
      }
  
      /// <summary>
      /// Emit the specified <see cref="EventHandler"/> delegate if is not <see langword="null"/>.
      /// </summary>
      /// <param name="eventHandler">
      /// An <see cref="EventHandler"/> to emit.
      /// </param>
      /// <param name="sender">
      /// An <see cref="object"/> that emits the <paramref name="eventHandler"/>.
      /// </param>
      /// <param name="e">
      /// An <see cref="EventArgs"/> that contains no event data.
      /// </param>
      public static void Emit(
        this EventHandler eventHandler, object sender, EventArgs e)
      {
        if (!eventHandler.IsNull())
          eventHandler(sender, e);
      }
  
      /// <summary>
      /// Emit the specified <b>EventHandler&lt;TEventArgs&gt;</b> delegate if is not <see langword="null"/>.
      /// </summary>
      /// <param name="eventHandler">
      /// An <b>EventHandler&lt;TEventArgs&gt;</b> to emit.
      /// </param>
      /// <param name="sender">
      /// An <see cref="object"/> that emits the <paramref name="eventHandler"/>.
      /// </param>
      /// <param name="e">
      /// A <b>TEventArgs</b> that contains the event data.
      /// </param>
      /// <typeparam name="TEventArgs">
      /// The type of the event data generated by the event.
      /// </typeparam>
      public static void Emit<TEventArgs>(
        this EventHandler<TEventArgs> eventHandler, object sender, TEventArgs e)
        where TEventArgs : EventArgs
      {
        if (!eventHandler.IsNull())
          eventHandler(sender, e);
      }
  
      /// <summary>
      /// Determines whether the specified <see cref="int"/> equals the specified <see cref="char"/> as <see cref="byte"/>.
      /// And save this specified <see cref="int"/> as <see cref="byte"/> to the specified <strong>List&lt;byte&gt;</strong>.
      /// </summary>
      /// <returns>
      /// <c>true</c> if the <paramref name="value"/> parameter equals the <paramref name="c"/> parameter as <see cref="byte"/>; otherwise, <c>false</c>.
      /// </returns>
      /// <param name="value">
      /// An <see cref="int"/> to compare.
      /// </param>
      /// <param name="c">
      /// A <see cref="char"/> to compare.
      /// </param>
      /// <param name="dest">
      /// A <strong>List&lt;byte&gt;</strong> to save the <paramref name="value"/> as <see cref="byte"/>.
      /// </param>
      /// <exception cref="ArgumentOutOfRangeException">
      /// Is thrown when the <paramref name="value"/> parameter passed to a method is invalid because it is outside the allowable range of values as <see cref="byte"/>.
      /// </exception>
      public static bool EqualsAndSaveTo(this int value, char c, List<byte> dest)
      {
        if (value < 0 || value > 255)
          throw new ArgumentOutOfRangeException("value");
  
        var b = (byte)value;
        dest.Add(b);
  
        return b == Convert.ToByte(c);
      }
  
      /// <summary>
      /// Determines whether the entry with the specified key exists in the specified <see cref="NameValueCollection"/>.
      /// </summary>
      /// <returns>
      /// <c>true</c> if the entry with the <paramref name="name"/> exists in the <paramref name="collection"/>; otherwise, <c>false</c>.
      /// </returns>
      /// <param name="collection">
      /// A <see cref="NameValueCollection"/> that contains the entries.
      /// </param>
      /// <param name="name">
      /// A <see cref="string"/> that contains the key of the entry to find.
      /// </param>
      public static bool Exists(this NameValueCollection collection, string name)
      {
        return collection.IsNull()
               ? false
               : !collection[name].IsNull();
      }
  
      /// <summary>
      /// Determines whether the entry with the specified both key and value exists in the specified <see cref="NameValueCollection"/>.
      /// </summary>
      /// <returns>
      /// <c>true</c> if the entry with the both <paramref name="name"/> and <paramref name="value"/> exists in the <paramref name="collection"/>; otherwise, <c>false</c>.
      /// </returns>
      /// <param name="collection">
      /// A <see cref="NameValueCollection"/> that contains the entries.
      /// </param>
      /// <param name="name">
      /// A <see cref="string"/> that contains the key of the entry to find.
      /// </param>
      /// <param name="value">
      /// A <see cref="string"/> that contains the value of the entry to find.
      /// </param>
      public static bool Exists(this NameValueCollection collection, string name, string value)
      {
        if (collection.IsNull())
          return false;
  
        var values = collection[name];
        if (values.IsNull())
          return false;
  
        foreach (string v in values.Split(','))
          if (String.Compare(v.Trim(), value, true) == 0)
            return true;
  
        return false;
      }
  
      /// <summary>
      /// Gets the absolute path from the specified <see cref="Uri"/>.
      /// </summary>
      /// <returns>
      /// A <see cref="string"/> that contains the absolute path if got successfully; otherwise, <see langword="null"/>.
      /// </returns>
      /// <param name="uri">
      /// A <see cref="Uri"/> that contains the URI to get the absolute path from.
      /// </param>
      public static string GetAbsolutePath(this Uri uri)
      {
        if (uri.IsNull())
          return null;
  
        if (uri.IsAbsoluteUri)
          return uri.AbsolutePath;
  
        var uriString = uri.OriginalString;
        var i = uriString.IndexOf('/');
        if (i != 0)
          return null;
  
        i = uriString.IndexOfAny(new []{'?', '#'});
        return i > 0
               ? uriString.Substring(0, i)
               : uriString;
      }
  
      /// <summary>
      /// Gets the description of the HTTP status code using the specified code.
      /// </summary>
      /// <returns>
      /// A <see cref="string"/> that contains the description of the <paramref name="code"/>.
      /// </returns>
      /// <param name="code">
      /// One of <see cref="WebSocketSharp.Net.HttpStatusCode"/> values that contains the HTTP status code.
      /// </param>
      public static string GetDescription(this HttpStatusCode code)
      {
        return ((int)code).GetStatusDescription();
      }
  
      /// <summary>
      /// Gets the name from the specified <see cref="string"/> that contains a pair of name and value are separated by a separator string.
      /// </summary>
      /// <returns>
      /// A <see cref="string"/> that contains the name if any; otherwise, <c>null</c>.
      /// </returns>
      /// <param name="nameAndValue">
      /// A <see cref="string"/> that contains a pair of name and value are separated by a separator string.
      /// </param>
      /// <param name="separator">
      /// A <see cref="string"/> that contains a separator string.
      /// </param>
      public static string GetName(this string nameAndValue, string separator)
      {
        if (nameAndValue.IsNullOrEmpty())
          return null;
  
        if (separator.IsNullOrEmpty())
          return null;
  
        var i = nameAndValue.IndexOf(separator);
        return i > 0
               ? nameAndValue.Substring(0, i).Trim()
               : null;
      }
  
      /// <summary>
      /// Gets the name and value from the specified <see cref="string"/> that contains a pair of name and value are separated by a separator string.
      /// </summary>
      /// <returns>
      /// A <b>KeyValuePair&lt;string, string&gt;</b> that contains the name and value if any.
      /// </returns>
      /// <param name="nameAndValue">
      /// A <see cref="string"/> that contains a pair of name and value are separated by a separator string.
      /// </param>
      /// <param name="separator">
      /// A <see cref="string"/> that contains a separator string.
      /// </param>
      public static KeyValuePair<string, string> GetNameAndValue(this string nameAndValue, string separator)
      {
        var name  = nameAndValue.GetName(separator);
        var value = nameAndValue.GetValue(separator);
        return !name.IsNull()
               ? new KeyValuePair<string, string>(name, value)
               : new KeyValuePair<string, string>(null, null);
      }
  
      /// <summary>
      /// Gets the description of the HTTP status code using the specified code.
      /// </summary>
      /// <returns>
      /// A <see cref="string"/> that contains the description of the <paramref name="code"/>.
      /// </returns>
      /// <param name="code">
      /// An <see cref="int"/> that contains the HTTP status code.
      /// </param>
      public static string GetStatusDescription(this int code)
      {
        switch (code)
        {
          case 100: return "Continue";
          case 101: return "Switching Protocols";
          case 102: return "Processing";
          case 200: return "OK";
          case 201: return "Created";
          case 202: return "Accepted";
          case 203: return "Non-Authoritative Information";
          case 204: return "No Content";
          case 205: return "Reset Content";
          case 206: return "Partial Content";
          case 207: return "Multi-Status";
          case 300: return "Multiple Choices";
          case 301: return "Moved Permanently";
          case 302: return "Found";
          case 303: return "See Other";
          case 304: return "Not Modified";
          case 305: return "Use Proxy";
          case 307: return "Temporary Redirect";
          case 400: return "Bad Request";
          case 401: return "Unauthorized";
          case 402: return "Payment Required";
          case 403: return "Forbidden";
          case 404: return "Not Found";
          case 405: return "Method Not Allowed";
          case 406: return "Not Acceptable";
          case 407: return "Proxy Authentication Required";
          case 408: return "Request Timeout";
          case 409: return "Conflict";
          case 410: return "Gone";
          case 411: return "Length Required";
          case 412: return "Precondition Failed";
          case 413: return "Request Entity Too Large";
          case 414: return "Request-Uri Too Long";
          case 415: return "Unsupported Media Type";
          case 416: return "Requested Range Not Satisfiable";
          case 417: return "Expectation Failed";
          case 422: return "Unprocessable Entity";
          case 423: return "Locked";
          case 424: return "Failed Dependency";
          case 500: return "Internal Server Error";
          case 501: return "Not Implemented";
          case 502: return "Bad Gateway";
          case 503: return "Service Unavailable";
          case 504: return "Gateway Timeout";
          case 505: return "Http Version Not Supported";
          case 507: return "Insufficient Storage";
        }
  
        return String.Empty;
      }
  
      /// <summary>
      /// Gets the value from the specified <see cref="string"/> that contains a pair of name and value are separated by a separator string.
      /// </summary>
      /// <returns>
      /// A <see cref="string"/> that contains the value if any; otherwise, <c>null</c>.
      /// </returns>
      /// <param name="nameAndValue">
      /// A <see cref="string"/> that contains a pair of name and value are separated by a separator string.
      /// </param>
      /// <param name="separator">
      /// A <see cref="string"/> that contains a separator string.
      /// </param>
      public static string GetValue(this string nameAndValue, string separator)
      {
        if (nameAndValue.IsNullOrEmpty())
          return null;
  
        if (separator.IsNullOrEmpty())
          return null;
  
        var i = nameAndValue.IndexOf(separator);
        return i >= 0 && i < nameAndValue.Length - 1
               ? nameAndValue.Substring(i + 1).Trim()
               : null;
      }
  
      /// <summary>
      /// Determines whether the specified <see cref="string"/> is a <see cref="String.Empty"/>.
      /// </summary>
      /// <returns>
      /// <c>true</c> if the <paramref name="value"/> parameter is a <see cref="String.Empty"/>; otherwise, <c>false</c>.
      /// </returns>
      /// <param name="value">
      /// A <see cref="string"/> to test.
      /// </param>
      public static bool IsEmpty(this string value)
      {
        return value == String.Empty ? true : false;
      }
  
      /// <summary>
      /// Determines whether the specified <see cref="WebSocketSharp.ByteOrder"/> is host (this computer architecture) byte order.
      /// </summary>
      /// <returns>
      /// <c>true</c> if the <paramref name="order"/> parameter is host byte order; otherwise, <c>false</c>.
      /// </returns>
      /// <param name="order">
      /// A <see cref="WebSocketSharp.ByteOrder"/> to test.
      /// </param>
      public static bool IsHostOrder(this ByteOrder order)
      {
        // true : !(true ^ true)  or !(false ^ false)
        // false: !(true ^ false) or !(false ^ true)
        return !(BitConverter.IsLittleEndian ^ (order == ByteOrder.LITTLE));
      }
  
      /// <summary>
      /// Determines whether the specified object is <see langword="null"/>.
      /// </summary>
      /// <returns>
      /// <c>true</c> if the <paramref name="obj"/> parameter is <see langword="null"/>; otherwise, <c>false</c>.
      /// </returns>
      /// <param name="obj">
      /// A <b>class</b> to test.
      /// </param>
      /// <typeparam name="T">
      /// The type of the <paramref name="obj"/> parameter.
      /// </typeparam>
      public static bool IsNull<T>(this T obj)
        where T : class
      {
        return obj == null ? true : false;
      }
  
      /// <summary>
      /// Determines whether the specified object is <see langword="null"/>.
      /// And invokes the specified <see cref="Action"/> delegate if the specified object is <see langword="null"/>.
      /// </summary>
      /// <returns>
      /// <c>true</c> if the <paramref name="obj"/> parameter is <see langword="null"/>; otherwise, <c>false</c>.
      /// </returns>
      /// <param name="obj">
      /// A <b>class</b> to test.
      /// </param>
      /// <param name="act">
      /// An <see cref="Action"/> delegate that contains the method(s) called if the <paramref name="obj"/> is <see langword="null"/>.
      /// </param>
      /// <typeparam name="T">
      /// The type of the <paramref name="obj"/> parameter.
      /// </typeparam>
      public static bool IsNullDo<T>(this T obj, Action act)
        where T : class
      {
        if (obj.IsNull())
        {
          act();
          return true;
        }
  
        return false;
      }
  
      /// <summary>
      /// Determines whether the specified <see cref="string"/> is <see langword="null"/> or <see cref="String.Empty"/>.
      /// </summary>
      /// <returns>
      /// <c>true</c> if the <paramref name="value"/> parameter is <see langword="null"/> or <see cref="String.Empty"/>; otherwise, <c>false</c>.
      /// </returns>
      /// <param name="value">
      /// A <see cref="string"/> to test.
      /// </param>
      public static bool IsNullOrEmpty(this string value)
      {
        return String.IsNullOrEmpty(value);
      }
  
      /// <summary>
      /// Determines whether the specified <see cref="string"/> is predefined scheme.
      /// </summary>
      /// <returns>
      /// <c>true</c> if the <paramref name="scheme"/> parameter is the predefined scheme; otherwise, <c>false</c>.
      /// </returns>
      /// <param name="scheme">
      /// A <see cref="string"/> to test.
      /// </param>
      public static bool IsPredefinedScheme(this string scheme)
      {
        if (scheme.IsNull() && scheme.Length < 2)
          return false;
  
        char c = scheme[0];
        if (c == 'h')
          return (scheme == "http" || scheme == "https");
  
        if (c == 'f')
          return (scheme == "file" || scheme == "ftp");
  
        if (c == 'w')
          return (scheme == "ws" || scheme == "wss");
  
        if (c == 'n')
        {
          c = scheme[1];
          if (c == 'e')
            return (scheme == "news" || scheme == "net.pipe" || scheme == "net.tcp");
  
          if (scheme == "nntp")
            return true;
  
          return false;
        }
  
        if ((c == 'g' && scheme == "gopher") || (c == 'm' && scheme == "mailto"))
          return true;
  
        return false;
      }
  
      /// <summary>
      /// Determines whether the specified <see cref="string"/> is valid absolute path.
      /// </summary>
      /// <returns>
      /// <c>true</c> if the <paramref name="absPath"/> parameter is valid absolute path; otherwise, <c>false</c>.
      /// </returns>
      /// <param name="absPath">
      /// A <see cref="string"/> to test.
      /// </param>
      /// <param name="message">
      /// A <see cref="string"/> that receives a message if the <paramref name="absPath"/> is invalid.
      /// </param>
      public static bool IsValidAbsolutePath(this string absPath, out string message)
      {
        if (absPath.IsNullOrEmpty())
        {
          message = "Must not be null or empty.";
          return false;
        }
  
        var i = absPath.IndexOf('/');
        if (i != 0)
        {
          message = "Not absolute path: " + absPath;
          return false;
        }
  
        i = absPath.IndexOfAny(new []{'?', '#'});
        if (i != -1)
        {
          message = "Must not contain either or both query and fragment components: " + absPath;
          return false;
        }
  
        message = String.Empty;
        return true;
      }
  
      /// <summary>
      /// Determines whether the specified <see cref="string"/> is a URI string.
      /// </summary>
      /// <returns>
      /// <c>true</c> if the <paramref name="uriString"/> parameter is maybe a URI string; otherwise, <c>false</c>.
      /// </returns>
      /// <param name="uriString">
      /// A <see cref="string"/> to test.
      /// </param>
      public static bool MaybeUri(this string uriString)
      {
        if (uriString.IsNullOrEmpty())
          return false;
  
        int p = uriString.IndexOf(':');
        if (p == -1)
          return false;
  
        if (p >= 10)
          return false;
  
        return uriString.Substring(0, p).IsPredefinedScheme();
      }
  
      /// <summary>
      /// Determines whether two specified <see cref="string"/> objects don't have the same value.
      /// </summary>
      /// <returns>
      /// <c>true</c> if the value of <paramref name="expected"/> parameter isn't the same as the value of <paramref name="actual"/> parameter; otherwise, <c>false</c>.
      /// </returns>
      /// <param name="expected">
      /// The first <see cref="string"/> to compare.
      /// </param>
      /// <param name="actual">
      /// The second <see cref="string"/> to compare.
      /// </param>
      /// <param name="ignoreCase">
      /// A <see cref="bool"/> that indicates a case-sensitive or insensitive comparison. (<c>true</c> indicates a case-insensitive comparison.)
      /// </param>
      public static bool NotEqual(this string expected, string actual, bool ignoreCase)
      {
        return String.Compare(expected, actual, ignoreCase) != 0
               ? true
               : false;
      }
  
      /// <summary>
      /// Reads a block of bytes from the specified stream and returns the read data in an array of <see cref="byte"/>.
      /// </summary>
      /// <returns>
      /// An array of <see cref="byte"/> that receives the read data.
      /// </returns>
      /// <param name="stream">
      /// A <see cref="Stream"/> that contains the data to read.
      /// </param>
      /// <param name="length">
      /// An <see cref="int"/> that contains the number of bytes to read.
      /// </param>
      public static byte[] ReadBytes(this Stream stream, int length)
      {
        if (stream.IsNull() || length <= 0)
          return new byte[]{};
  
  
gt:       var buffer  = new byte[length];