#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-2012 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.Net.Sockets; using System.Text; using WebSocketSharp.Net; using WebSocketSharp.Net.Sockets; namespace WebSocketSharp { public delegate void Action(); public delegate void Action(T1 t1, T2 t2); public delegate void Action(T1 t1, T2 t2, T3 t3); public delegate void Action(T1 t1, T2 t2, T3 t3, T4 t4); public delegate TResult Func(T1 t1); /// Provides a set of static methods for the websocket-sharp. /// public static class Ext { #region Private Methods private static void times(ulong n, Action act) { for (ulong i = 0; i < n; i++) act(); } private static void times(ulong n, Action act) { for (ulong i = 0; i < n; i++) act(i); } private static byte[] Reverse(byte[] array) { Array.Reverse(array); return array; } #endregion #region Public Methods /// /// Accept a WebSocket connection by the . /// /// /// A that contains a WebSocket connection. /// /// /// A that contains a TCP connection to accept a WebSocket connection from. /// /// /// A that indicates a secure connection or not. (true indicates a secure connection.) /// /// /// Is thrown when the parameter passed to a method is invalid because it is . /// public static TcpListenerWebSocketContext AcceptWebSocket(TcpClient client, bool secure) { if (client == null) throw new ArgumentNullException("client"); return new TcpListenerWebSocketContext(client, secure); } /// /// Emit the specified delegate if is not . /// /// /// An to emit. /// /// /// An that emits the . /// /// /// An that contains no event data. /// public static void Emit( EventHandler eventHandler, object sender, EventArgs e) { if (eventHandler != null) eventHandler(sender, e); } /// /// Emit the specified delegate if is not . /// /// /// An to emit. /// /// /// An that emits the . /// /// /// An that contains the event data. /// /// /// The type of the event data generated by the event. /// public static void Emit( EventHandler eventHandler, object sender, TEventArgs e) where TEventArgs : EventArgs { if (eventHandler != null) eventHandler(sender, e); } /// /// Determines whether the specified equals the specified as . /// And save this specified as to the specified . /// /// /// true if the parameter equals the parameter as ; otherwise, false. /// /// /// An to compare. /// /// /// A to compare. /// /// /// A to save the as . /// /// /// Is thrown when the parameter passed to a method is invalid because it is outside the allowable range of values as . /// public static bool EqualsAndSaveTo(int value, char c, List dest) { if (value < 0 || value > 255) throw new ArgumentOutOfRangeException("value"); var b = (byte)value; dest.Add(b); return b == Convert.ToByte(c); } /// /// Determines whether the entry with the specified key exists in the specified . /// /// /// true if the entry with the exists in the ; otherwise, false. /// /// /// A that contains the entries. /// /// /// A that contains the key of the entry to find. /// public static bool Exists(NameValueCollection collection, string name) { return collection == null ? false : collection[name] != null; } /// /// Determines whether the entry with the specified both key and value exists in the specified . /// /// /// true if the entry with the both and exists in the ; otherwise, false. /// /// /// A that contains the entries. /// /// /// A that contains the key of the entry to find. /// /// /// A that contains the value of the entry to find. /// public static bool Exists(NameValueCollection collection, string name, string value) { if (collection == null) return false; var values = collection[name]; if (values == null) return false; foreach (string v in values.Split(',')) if (String.Compare(v.Trim(), value, true) == 0) return true; return false; } /// /// Gets the absolute path from the specified . /// /// /// A that contains the absolute path if got successfully; otherwise, . /// /// /// A that contains the URI to get the absolute path from. /// public static string GetAbsolutePath(Uri uri) { if (uri == null) 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; } /// /// Gets the description of the HTTP status code using the specified code. /// /// /// A that contains the description of the . /// /// /// One of values that contains the HTTP status code. /// public static string GetDescription(HttpStatusCode code) { return GetStatusDescription((int)code); } /// /// Gets the name from the specified that contains a pair of name and value are separated by a separator string. /// /// /// A that contains the name if any; otherwise, null. /// /// /// A that contains a pair of name and value are separated by a separator string. /// /// /// A that contains a separator string. /// public static string GetName(string nameAndValue, string separator) { if (IsNullOrEmpty(nameAndValue)) return null; if (IsNullOrEmpty(separator)) return null; var i = nameAndValue.IndexOf(separator); return i > 0 ? nameAndValue.Substring(0, i).Trim() : null; } /// /// Gets the name and value from the specified that contains a pair of name and value are separated by a separator string. /// /// /// A that contains the name and value if any. /// /// /// A that contains a pair of name and value are separated by a separator string. /// /// /// A that contains a separator string. /// public static KeyValuePair GetNameAndValue(string nameAndValue, string separator) { var name = GetName(nameAndValue, separator); var value = GetValue(nameAndValue, separator); return name != null ? new KeyValuePair(name, value) : new KeyValuePair(null, null); } /// /// Gets the description of the HTTP status code using the specified code. /// /// /// A that contains the description of the . /// /// /// An that contains the HTTP status code. /// public static string GetStatusDescription(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; } /// /// Gets the value from the specified that contains a pair of name and value are separated by a separator string. /// /// /// A that contains the value if any; otherwise, null. /// /// /// A that contains a pair of name and value are separated by a separator string. /// /// /// A that contains a separator string. /// public static string GetValue(string nameAndValue, string separator) { if (IsNullOrEmpty(nameAndValue)) return null; if (IsNullOrEmpty(separator)) return null; var i = nameAndValue.IndexOf(separator); return i >= 0 && i < nameAndValue.Length - 1 ? nameAndValue.Substring(i + 1).Trim() : null; } /// /// Determines whether the specified is a . /// /// /// true if the parameter is a ; otherwise, false. /// /// /// A to test. /// public static bool IsEmpty(string value) { return value == String.Empty ? true : false; } /// /// Determines whether the specified is host (this computer architecture) byte order. /// /// /// true if the parameter is host byte order; otherwise, false. /// /// /// A to test. /// public static bool IsHostOrder(ByteOrder order) { // true : !(true ^ true) or !(false ^ false) // false: !(true ^ false) or !(false ^ true) return !(BitConverter.IsLittleEndian ^ (order == ByteOrder.LITTLE)); } /// /// Determines whether the specified object is . /// /// /// true if the parameter is ; otherwise, false. /// /// /// A to test. /// /// /// The type of the parameter. /// public static bool IsNull(T obj) where T : class { return obj == null ? true : false; } /// /// Determines whether the specified object is . /// And invokes the specified delegate if the specified object is . /// /// /// true if the parameter is ; otherwise, false. /// /// /// A to test. /// /// /// An delegate that contains the method(s) called if the is . /// /// /// The type of the parameter. /// public static bool IsNullDo(T obj, Action act) where T : class { if (obj == null) { act(); return true; } return false; } /// /// Determines whether the specified is or . /// /// /// true if the parameter is or ; otherwise, false. /// /// /// A to test. /// public static bool IsNullOrEmpty(string value) { return String.IsNullOrEmpty(value); } /// /// Determines whether the specified is predefined scheme. /// /// /// true if the parameter is the predefined scheme; otherwise, false. /// /// /// A to test. /// public static bool IsPredefinedScheme(string scheme) { if (scheme == null && 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; } /// /// Determines whether the specified is valid absolute path. /// /// /// true if the parameter is valid absolute path; otherwise, false. /// /// /// A to test. /// /// /// A that receives a message if the is invalid. /// public static bool IsValidAbsolutePath(string absPath, out string message) { if (IsNullOrEmpty(absPath)) { 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; } /// /// Determines whether the specified is a URI string. /// /// /// true if the parameter is maybe a URI string; otherwise, false. /// /// /// A to test. /// public static bool MaybeUri(string uriString) { if (IsNullOrEmpty(uriString)) return false; int p = uriString.IndexOf(':'); if (p == -1) return false; if (p >= 10) return false; return IsPredefinedScheme(uriString.Substring(0, p)); } /// /// Determines whether two specified objects don't have the same value. /// /// /// true if the value of parameter isn't the same as the value of parameter; otherwise, false. /// /// /// The first to compare. /// /// /// The second to compare. /// /// /// A that indicates a case-sensitive or insensitive comparison. (true indicates a case-insensitive comparison.) /// public static bool NotEqual(string expected, string actual, bool ignoreCase) { return String.Compare(expected, actual, ignoreCase) != 0 ? true : false; } /// /// Reads a block of bytes from the specified stream and returns the read data in an array of . /// /// /// An array of that receives the read data. /// /// /// A that contains the data to read. /// /// /// An that contains the number of bytes to read. /// public static byte[] ReadBytes(Stream stream, int length) { if (stream == null || length <= 0) return new byte[] { }; var buffer = new byte[length]; var readLen = stream.Read(buffer, 0, length); var temp = 0; while (readLen < length) { temp = stream.Read(buffer, readLen, length - readLen); if (temp < 1) break; readLen += temp; } return readLen == length ? buffer : readLen > 0 ? SubArray(buffer, 0, readLen) : new byte[] { }; } /// /// Reads a block of bytes from the specified stream and returns the read data in an array of . /// /// /// An array of that receives the read data. /// /// /// A that contains the data to read. /// /// /// A that contains the number of bytes to read. /// public static byte[] ReadBytes(Stream stream, long length) { return ReadBytes(stream, length, 1024); } /// /// Reads a block of bytes from the specified stream and returns the read data in an array of . /// /// /// An array of that receives the read data. /// /// /// A that contains the data to read. /// /// /// A that contains the number of bytes to read. /// /// /// An that contains the buffer size in bytes of each internal read. /// public static byte[] ReadBytes(Stream stream, long length, int bufferLength) { if (stream == null || length <= 0) return new byte[] { }; if (bufferLength <= 0) bufferLength = 1024; var count = length / bufferLength; var rem = length % bufferLength; var readData = new List(); var readBuffer = new byte[bufferLength]; long readLen = 0; var tmpLen = 0; Action read = (buffer) => { tmpLen = stream.Read(buffer, 0, buffer.Length); if (tmpLen > 0) { readLen += tmpLen; readData.AddRange(SubArray(buffer, 0, tmpLen)); } }; Times(count, () => { read(readBuffer); }); if (rem > 0) read(new byte[rem]); return readLen > 0 ? readData.ToArray() : new byte[] { }; } /// /// Retrieves a sub-array from the specified . A sub-array starts at the specified element position. /// /// /// An array of T that receives a sub-array, or an empty array of T if any problems with the parameters. /// /// /// An array of T that contains the data to retrieve a sub-array. /// /// /// An that contains the zero-based starting position of a sub-array in the . /// /// /// An that contains the number of elements to retrieve a sub-array. /// /// /// The type of elements in the . /// public static T[] SubArray(T[] array, int startIndex, int length) { if (array == null || array.Length == 0) return new T[] { }; if (startIndex < 0 || length <= 0) return new T[] { }; if (startIndex + length > array.Length) return new T[] { }; if (startIndex == 0 && array.Length == length) return array; T[] subArray = new T[length]; Array.Copy(array, startIndex, subArray, 0, length); return subArray; } /// /// Executes the specified delegate times. /// /// /// An that contains the number of times to execute. /// /// /// An delegate that contains the method(s) to execute. /// public static void Times(int n, Action act) { if (n > 0 && act != null) times((ulong)n, act); } /// /// Executes the specified delegate times. /// /// /// A that contains the number of times to execute. /// /// /// An delegate that contains the method(s) to execute. /// public static void Times(long n, Action act) { if (n > 0 && act != null) times((ulong)n, act); } /// /// Executes the specified delegate times. /// /// /// A that contains the number of times to execute. /// /// /// An delegate that contains the method(s) to execute. /// public static void Times(uint n, Action act) { if (n > 0 && act != null) times((ulong)n, act); } /// /// Executes the specified delegate times. /// /// /// A that contains the number of times to execute. /// /// /// An delegate that contains the method(s) to execute. /// public static void Times(ulong n, Action act) { if (n > 0 && act != null) times((ulong)n, act); } /// /// Executes the specified delegate times. /// /// /// An that contains the number of times to execute. /// /// /// An delegate that contains the method(s) to execute. /// A parameter to pass to this method(s) contains the zero-based count of iteration. /// public static void Times(int n, Action act) { if (n > 0 && act != null) times((ulong)n, act); } /// /// Executes the specified delegate times. /// /// /// A that contains the number of times to execute. /// /// /// An delegate that contains the method(s) to execute. /// A parameter to pass to this method(s) contains the zero-based count of iteration. /// public static void Times(long n, Action act) { if (n > 0 && act != null) times((ulong)n, act); } /// /// Executes the specified delegate times. /// /// /// A that contains the number of times to execute. /// /// /// An delegate that contains the method(s) to execute. /// A parameter to pass to this method(s) contains the zero-based count of iteration. /// public static void Times(uint n, Action act) { if (n > 0 && act != null) times((ulong)n, act); } /// /// Executes the specified delegate times. /// /// /// A that contains the number of times to execute. /// /// /// An delegate that contains the method(s) to execute. /// A parameter to pass to this method(s) contains the zero-based count of iteration. /// public static void Times(ulong n, Action act) { if (n > 0 && act != null) times((ulong)n, act); } /// /// Converts the specified array of to the specified type data. /// /// /// A T converted from the , or a default value of T /// if the is an empty array of /// or if the types of T aren't the , , , /// , , , , /// , , . /// /// /// An array of to convert. /// /// /// A that indicates the byte order of the . /// /// /// The type of the return value. The T must be a value type. /// /// /// Is thrown when the parameter passed to a method is invalid because it is . /// public static T To(byte[] src, ByteOrder srcOrder) where T : struct { if (src == null) throw new ArgumentNullException("src"); if (src.Length == 0) return default(T); var type = typeof(T); var buffer = ToHostOrder(src, srcOrder); if (type == typeof(Boolean)) return (T)(object)BitConverter.ToBoolean(buffer, 0); if (type == typeof(Char)) return (T)(object)BitConverter.ToChar(buffer, 0); if (type == typeof(Double)) return (T)(object)BitConverter.ToDouble(buffer, 0); if (type == typeof(Int16)) return (T)(object)BitConverter.ToInt16(buffer, 0); if (type == typeof(Int32)) return (T)(object)BitConverter.ToInt32(buffer, 0); if (type == typeof(Int64)) return (T)(object)BitConverter.ToInt64(buffer, 0); if (type == typeof(Single)) return (T)(object)BitConverter.ToSingle(buffer, 0); if (type == typeof(UInt16)) return (T)(object)BitConverter.ToUInt16(buffer, 0); if (type == typeof(UInt32)) return (T)(object)BitConverter.ToUInt32(buffer, 0); if (type == typeof(UInt64)) return (T)(object)BitConverter.ToUInt64(buffer, 0); return default(T); } /// /// Converts the specified data to an array of . /// /// /// An array of converted from the . /// /// /// A T to convert. /// /// /// A that indicates the byte order of the return. /// /// /// The type of the . The T must be a value type. /// public static byte[] ToBytes(T value, ByteOrder order) where T : struct { var type = typeof(T); byte[] buffer; if (type == typeof(Boolean)) { buffer = BitConverter.GetBytes((Boolean)(object)value); } else if (type == typeof(Char)) { buffer = BitConverter.GetBytes((Char)(object)value); } else if (type == typeof(Double)) { buffer = BitConverter.GetBytes((Double)(object)value); } else if (type == typeof(Int16)) { buffer = BitConverter.GetBytes((Int16)(object)value); } else if (type == typeof(Int32)) { buffer = BitConverter.GetBytes((Int32)(object)value); } else if (type == typeof(Int64)) { buffer = BitConverter.GetBytes((Int64)(object)value); } else if (type == typeof(Single)) { buffer = BitConverter.GetBytes((Single)(object)value); } else if (type == typeof(UInt16)) { buffer = BitConverter.GetBytes((UInt16)(object)value); } else if (type == typeof(UInt32)) { buffer = BitConverter.GetBytes((UInt32)(object)value); } else if (type == typeof(UInt64)) { buffer = BitConverter.GetBytes((UInt64)(object)value); } else { buffer = new byte[] { }; } return buffer.Length == 0 || IsHostOrder(order) ? buffer : Reverse(buffer); } /// /// Converts the order of the specified array of to the host byte order. /// /// /// An array of converted from the . /// /// /// An array of to convert. /// /// /// A that indicates the byte order of the . /// /// /// Is thrown when the parameter passed to a method is invalid because it is . /// public static byte[] ToHostOrder(byte[] src, ByteOrder srcOrder) { if (src == null) throw new ArgumentNullException("src"); return src.Length == 0 || IsHostOrder(srcOrder) ? src : Reverse(src); } /// /// Converts the specified array to a concatenated the specified separator string /// between each element of this array. /// /// /// A converted from the parameter, or a /// if the length of the is zero. /// /// /// An array of T to convert. /// /// /// A that contains a separator string. /// /// /// The type of elements in the . /// /// /// Is thrown when the parameter passed to a method is invalid because it is . /// public static string ToString(T[] array, string separator) { if (array == null) throw new ArgumentNullException("array"); var len = array.Length; if (len == 0) return String.Empty; if (separator == null) separator = String.Empty; var sb = new StringBuilder(); Times(len - 1, i => sb.AppendFormat("{0}{1}", array[i].ToString(), separator) ); sb.Append(array[len - 1].ToString()); return sb.ToString(); } /// /// Converts the specified to a object. /// /// /// A converted from the parameter, or /// if the is or . /// /// /// A to convert. /// public static Uri ToUri(string uriString) { return IsNullOrEmpty(uriString) ? null : MaybeUri(uriString) ? new Uri(uriString) : new Uri(uriString, UriKind.Relative); } /// /// Tries to create a new WebSocket using the specified . /// /// /// true if the WebSocket was successfully created; otherwise, false. /// /// /// A that contains a WebSocket URI. /// /// /// When this method returns, contains a created WebSocket if the parameter is valid WebSocket URI; otherwise, . /// /// /// When this method returns, contains a error message if the parameter is invalid WebSocket URI; otherwise, String.Empty. /// /// /// Is thrown when the parameter passed to a method is invalid because it is . /// public static bool TryCreateWebSocketUri(string uriString, out Uri result, out string message) { if (uriString == null) throw new ArgumentNullException("uriString"); result = null; if (uriString == String.Empty) { message = "Must not be empty."; return false; } var uri = ToUri(uriString); if (!uri.IsAbsoluteUri) { message = "Not absolute URI: " + uriString; return false; } var scheme = uri.Scheme; if (scheme != "ws" && scheme != "wss") { message = "Unsupported scheme: " + scheme; return false; } var fragment = uri.Fragment; if (!String.IsNullOrEmpty(fragment)) { message = "Must not contain the fragment component: " + uriString; return false; } var port = uri.Port; if (port > 0) { if ((scheme == "ws" && port == 443) || (scheme == "wss" && port == 80)) { message = String.Format( "Invalid pair of scheme and port: {0}, {1}", scheme, port); return false; } } result = uri; message = String.Empty; return true; } /// /// URL-decodes the specified . /// /// /// A that receives a decoded string, or the parameter /// if the is or . /// /// /// A to decode. /// public static string UrlDecode(string s) { return IsNullOrEmpty(s) ? s : HttpUtility.UrlDecode(s); } /// /// URL-encodes the specified . /// /// /// A that receives a encoded string, or the parameter /// if the is or . /// /// /// A to encode. /// public static string UrlEncode(string s) { return IsNullOrEmpty(s) ? s : HttpUtility.UrlEncode(s); } /// /// Writes the specified content data using the specified . /// /// /// A that contains a network stream to write a content data. /// /// /// An array of that contains a content data to write. /// /// /// Is thrown when the parameter passed to a method is invalid because it is . /// public static void WriteContent(HttpListenerResponse response, byte[] content) { if (response == null) throw new ArgumentNullException("response"); if (content == null || content.Length == 0) return; var output = response.OutputStream; response.ContentLength64 = content.Length; output.Write(content, 0, content.Length); output.Close(); } #endregion } }