// This file is part of the EonaCat project(s) which is released under the Apache License. // See the LICENSE file or go to https://EonaCat.com/License for full license details. using System; using System.Collections.Generic; using System.Collections.Specialized; using System.IO; using System.IO.Compression; using System.Net.Sockets; using System.Text; namespace EonaCat.Network { public static class Ext { private const string _tspecials = "()<>@,;:\\\"/[]?={} \t"; private const int BUFFER_SIZE = 1024; private static readonly byte[] _last = new byte[] { 0x00 }; private static readonly int _retry = 5; public static bool Contains(this string value, params char[] chars) { return chars == null || chars.Length == 0 || value != null && value.Length != 0 && value.IndexOfAny(chars) > -1; } public static bool Contains(this NameValueCollection collection, string name) { return collection != null && collection.Count > 0 && collection[name] != null; } public static bool Contains(this NameValueCollection collection, string name, string value) { if (collection == null || collection.Count == 0) { return false; } var vals = collection[name]; if (vals == null) { return false; } foreach (var val in vals.Split(',')) { if (val.Trim().Equals(value, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } public static void Emit(this EventHandler eventHandler, object sender, EventArgs e) { if (eventHandler != null) { eventHandler(sender, e); } } public static void Emit( this EventHandler eventHandler, object sender, TEventArgs e) where TEventArgs : EventArgs { if (eventHandler != null) { eventHandler(sender, e); } } public static CookieCollection GetCookies(this NameValueCollection headers, bool response) { var name = response ? "Set-Cookie" : "Cookie"; return headers != null && headers.Contains(name) ? CookieCollection.Parse(headers[name], response) : new CookieCollection(); } public static string GetDescription(this HttpStatusCode code) { return ((int)code).GetStatusDescription(); } 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; } public static bool IsCloseStatusCode(this ushort value) { return value > 999 && value < 5000; } public static bool IsEnclosedIn(this string value, char c) { return value != null && value.Length > 1 && value[0] == c && value[value.Length - 1] == c; } 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)); } /// /// An extension method to determine if an IP address is internal /// Class A Private IP Range: 10.0.0.0 ? 10.255.255.255 /// Class B Private IP Range: 172.16.0.0 ? 172.31.255.255 /// Class C Private IP Range: 192.168.0.0 ? 192.168.255.25 /// /// The IP address that will be tested /// Returns true if the IP is internal, false if it is external public static bool IsInternal(this System.Net.IPAddress address) { byte[] bytes = address.GetAddressBytes(); switch (bytes[0]) { case 10: return true; case 172: return bytes[1] < 32 && bytes[1] >= 16; case 192: return bytes[1] == 168; default: return false; } } public static bool IsLocal(this System.Net.IPAddress address) { if (address == null) { return false; } if (address.Equals(System.Net.IPAddress.Any)) { return true; } if (address.Equals(System.Net.IPAddress.Loopback)) { return true; } if (Socket.OSSupportsIPv6) { if (address.Equals(System.Net.IPAddress.IPv6Any)) { return true; } if (address.Equals(System.Net.IPAddress.IPv6Loopback)) { return true; } } if (address.IsInternal()) return true; var host = System.Net.Dns.GetHostName(); var addrs = System.Net.Dns.GetHostAddresses(host); foreach (var addr in addrs) { if (address.Equals(addr)) { return true; } } return false; } public static bool IsNullOrEmpty(this string value) { return value == null || value.Length == 0; } public static bool IsPredefinedScheme(this string value) { if (value == null || value.Length < 2) { return false; } var c = value[0]; if (c == 'h') { return value == "http" || value == "https"; } if (c == 'w') { return value == "ws" || value == "wss"; } if (c == 'f') { return value == "file" || value == "ftp"; } if (c == 'g') { return value == "gopher"; } if (c == 'm') { return value == "mailto"; } if (c == 'n') { c = value[1]; return c == 'e' ? value == "news" || value == "net.pipe" || value == "net.tcp" : value == "nntp"; } return false; } public static bool IsUpgradeTo(this HttpListenerRequest request, string protocol) { if (request == null) { throw new ArgumentNullException(nameof(request)); } if (protocol == null) { throw new ArgumentNullException(nameof(protocol)); } if (protocol.Length == 0) { throw new ArgumentException("An empty string.", nameof(protocol)); } return request.Headers.Contains("Upgrade", protocol) && request.Headers.Contains("Connection", "Upgrade"); } public static bool MaybeUri(this string value) { if (value == null || value.Length == 0) { return false; } var idx = value.IndexOf(':'); if (idx == -1) { return false; } if (idx >= 10) { return false; } var schm = value.Substring(0, idx); return schm.IsPredefinedScheme(); } public static T[] SubArray(this T[] array, int startIndex, int length) { int len; if (array == null || (len = array.Length) == 0) { return new T[0]; } if (startIndex < 0 || length <= 0 || startIndex + length > len) { return new T[0]; } if (startIndex == 0 && length == len) { return array; } var subArray = new T[length]; Array.Copy(array, startIndex, subArray, 0, length); return subArray; } public static T[] SubArray(this T[] array, long startIndex, long length) { long len; if (array == null || (len = array.LongLength) == 0) { return new T[0]; } if (startIndex < 0 || length <= 0 || startIndex + length > len) { return new T[0]; } if (startIndex == 0 && length == len) { return array; } var subArray = new T[length]; Array.Copy(array, startIndex, subArray, 0, length); return subArray; } public static void Times(this int n, Action action) { if (n > 0 && action != null) { ((ulong)n).times(action); } } public static void Times(this long n, Action action) { if (n > 0 && action != null) { ((ulong)n).times(action); } } public static void Times(this uint n, Action action) { if (n > 0 && action != null) { ((ulong)n).times(action); } } public static void Times(this ulong n, Action action) { if (n > 0 && action != null) { n.times(action); } } public static void Times(this int n, Action action) { if (n > 0 && action != null) { for (int i = 0; i < n; i++) { action(i); } } } public static void Times(this long n, Action action) { if (n > 0 && action != null) { for (long i = 0; i < n; i++) { action(i); } } } public static void Times(this uint n, Action action) { if (n > 0 && action != null) { for (uint i = 0; i < n; i++) { action(i); } } } public static void Times(this ulong n, Action action) { if (n > 0 && action != null) { for (ulong i = 0; i < n; i++) { action(i); } } } public static T To(this byte[] source, ByteOrder sourceOrder) where T : struct { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (source.Length == 0) { return default(T); } var type = typeof(T); var buff = source.ToHostOrder(sourceOrder); return type == typeof(bool) ? (T)(object)BitConverter.ToBoolean(buff, 0) : type == typeof(char) ? (T)(object)BitConverter.ToChar(buff, 0) : type == typeof(double) ? (T)(object)BitConverter.ToDouble(buff, 0) : type == typeof(short) ? (T)(object)BitConverter.ToInt16(buff, 0) : type == typeof(int) ? (T)(object)BitConverter.ToInt32(buff, 0) : type == typeof(long) ? (T)(object)BitConverter.ToInt64(buff, 0) : type == typeof(float) ? (T)(object)BitConverter.ToSingle(buff, 0) : type == typeof(ushort) ? (T)(object)BitConverter.ToUInt16(buff, 0) : type == typeof(uint) ? (T)(object)BitConverter.ToUInt32(buff, 0) : type == typeof(ulong) ? (T)(object)BitConverter.ToUInt64(buff, 0) : default(T); } public static byte[] ToByteArray(this T value, ByteOrder order) where T : struct { var type = typeof(T); var bytes = type == typeof(bool) ? BitConverter.GetBytes((bool)(object)value) : type == typeof(byte) ? new byte[] { (byte)(object)value } : type == typeof(char) ? BitConverter.GetBytes((char)(object)value) : type == typeof(double) ? BitConverter.GetBytes((double)(object)value) : type == typeof(short) ? BitConverter.GetBytes((short)(object)value) : type == typeof(int) ? BitConverter.GetBytes((int)(object)value) : type == typeof(long) ? BitConverter.GetBytes((long)(object)value) : type == typeof(float) ? BitConverter.GetBytes((float)(object)value) : type == typeof(ushort) ? BitConverter.GetBytes((ushort)(object)value) : type == typeof(uint) ? BitConverter.GetBytes((uint)(object)value) : type == typeof(ulong) ? BitConverter.GetBytes((ulong)(object)value) : WSClient.EmptyBytes; if (bytes.Length > 1 && !order.IsHostOrder()) { Array.Reverse(bytes); } return bytes; } public static byte[] ToHostOrder(this byte[] source, ByteOrder sourceOrder) { if (source == null) { throw new ArgumentNullException(nameof(source)); } return source.Length > 1 && !sourceOrder.IsHostOrder() ? source.Reverse() : source; } public static string ToString(this T[] array, string separator) { if (array == null) { throw new ArgumentNullException(nameof(array)); } var len = array.Length; if (len == 0) { return string.Empty; } separator ??= string.Empty; var buff = new StringBuilder(64); (len - 1).Times(i => buff.AppendFormat("{0}{1}", array[i].ToString(), separator)); buff.Append(array[len - 1].ToString()); return buff.ToString(); } public static Uri ToUri(this string value) { Uri.TryCreate( value, value.MaybeUri() ? UriKind.Absolute : UriKind.Relative, out Uri ret ); return ret; } public static string UrlDecode(this string value) { return value != null && value.Length > 0 ? HttpUtility.UrlDecode(value) : value; } public static string UrlEncode(this string value) { return value != null && value.Length > 0 ? HttpUtility.UrlEncode(value) : value; } public static void WriteContent(this HttpListenerResponse response, byte[] content) { if (response == null) { throw new ArgumentNullException(nameof(response)); } if (content == null) { throw new ArgumentNullException(nameof(content)); } var len = content.LongLength; if (len == 0) { response.Close(); return; } response.ContentLength64 = len; var output = response.OutputStream; if (len <= int.MaxValue) { output.Write(content, 0, (int)len); } else { output.WriteBytes(content, BUFFER_SIZE); } output.Close(); } internal static byte[] Append(this ushort code, string reason) { var ret = code.InternalToByteArray(ByteOrder.Big); if (reason != null && reason.Length > 0) { var buff = new List(ret); buff.AddRange(Encoding.UTF8.GetBytes(reason)); ret = buff.ToArray(); } return ret; } internal static void Close(this HttpListenerResponse response, HttpStatusCode code) { response.StatusCode = (int)code; response.OutputStream.Close(); } internal static void CloseWithAuthChallenge( this HttpListenerResponse response, string challenge) { response.Headers.InternalSet("WWW-Authenticate", challenge, true); response.Close(HttpStatusCode.Unauthorized); } internal static byte[] Compress(this byte[] data, CompressionMethod method) { return method == CompressionMethod.Deflate ? data.compress() : data; } internal static Stream Compress(this Stream stream, CompressionMethod method) { return method == CompressionMethod.Deflate ? stream.compress() : stream; } internal static byte[] CompressToArray(this Stream stream, CompressionMethod method) { return method == CompressionMethod.Deflate ? stream.compressToArray() : stream.ToByteArray(); } internal static bool Contains( this IEnumerable source, Func condition ) { foreach (T elm in source) { if (condition(elm)) { return true; } } return false; } internal static bool ContainsTwice(this string[] values) { var len = values.Length; var end = len - 1; Func seek = null; seek = idx => { if (idx == end) { return false; } var val = values[idx]; for (var i = idx + 1; i < len; i++) { if (values[i] == val) { return true; } } return seek(++idx); }; return seek(0); } internal static T[] Copy(this T[] source, int length) { var dest = new T[length]; Array.Copy(source, 0, dest, 0, length); return dest; } internal static T[] Copy(this T[] source, long length) { var dest = new T[length]; Array.Copy(source, 0, dest, 0, length); return dest; } internal static void CopyTo(this Stream source, Stream destination, int bufferLength) { var buff = new byte[bufferLength]; var nread = 0; while ((nread = source.Read(buff, 0, bufferLength)) > 0) { destination.Write(buff, 0, nread); } } internal static void CopyToAsync( this Stream source, Stream destination, int bufferLength, Action completed, Action error) { var buff = new byte[bufferLength]; AsyncCallback callback = null; callback = ar => { try { var nread = source.EndRead(ar); if (nread <= 0) { if (completed != null) { completed(); } return; } destination.Write(buff, 0, nread); source.BeginRead(buff, 0, bufferLength, callback, null); } catch (Exception ex) { if (error != null) { error(ex); } } }; try { source.BeginRead(buff, 0, bufferLength, callback, null); } catch (Exception ex) { if (error != null) { error(ex); } } } internal static byte[] Decompress(this byte[] data, CompressionMethod method) { return method == CompressionMethod.Deflate ? data.decompress() : data; } internal static Stream Decompress(this Stream stream, CompressionMethod method) { return method == CompressionMethod.Deflate ? stream.decompress() : stream; } internal static byte[] DecompressToArray(this Stream stream, CompressionMethod method) { return method == CompressionMethod.Deflate ? stream.decompressToArray() : stream.ToByteArray(); } internal static bool EqualsWith(this int value, char c, Action action) { action(value); return value == c - 0; } internal static string GetAbsolutePath(this Uri uri) { if (uri.IsAbsoluteUri) { return uri.AbsolutePath; } var original = uri.OriginalString; if (original[0] != '/') { return null; } var idx = original.IndexOfAny(new[] { '?', '#' }); return idx > 0 ? original.Substring(0, idx) : original; } internal static string GetDnsSafeHost(this Uri uri, bool bracketIPv6) { return bracketIPv6 && uri.HostNameType == UriHostNameType.IPv6 ? uri.Host : uri.DnsSafeHost; } internal static string GetMessage(this CloseStatusCode code) { return code == CloseStatusCode.ProtocolError ? "A WebSocket protocol error has occurred." : code == CloseStatusCode.UnsupportedData ? "Unsupported data has been received." : code == CloseStatusCode.Abnormal ? "An exception has occurred." : code == CloseStatusCode.InvalidData ? "Invalid data has been received." : code == CloseStatusCode.PolicyViolation ? "A policy violation has occurred." : code == CloseStatusCode.TooBig ? "A too big message has been received." : code == CloseStatusCode.MandatoryExtension ? "WebSocket client didn't receive expected extension(s)." : code == CloseStatusCode.ServerError ? "WebSocket server got an internal error." : code == CloseStatusCode.TlsHandshakeFailure ? "An error has occurred during a TLS handshake." : string.Empty; } internal static string GetName(this string nameAndValue, char separator) { var idx = nameAndValue.IndexOf(separator); return idx > 0 ? nameAndValue.Substring(0, idx).Trim() : null; } internal static string GetValue(this string nameAndValue, char separator) { var idx = nameAndValue.IndexOf(separator); return idx > -1 && idx < nameAndValue.Length - 1 ? nameAndValue.Substring(idx + 1).Trim() : null; } internal static string GetValue(this string nameAndValue, char separator, bool unquote) { var idx = nameAndValue.IndexOf(separator); if (idx < 0 || idx == nameAndValue.Length - 1) { return null; } var val = nameAndValue.Substring(idx + 1).Trim(); return unquote ? val.Unquote() : val; } internal static byte[] InternalToByteArray(this ushort value, ByteOrder order) { var bytes = BitConverter.GetBytes(value); if (!order.IsHostOrder()) { Array.Reverse(bytes); } return bytes; } internal static byte[] InternalToByteArray(this ulong value, ByteOrder order) { var bytes = BitConverter.GetBytes(value); if (!order.IsHostOrder()) { Array.Reverse(bytes); } return bytes; } internal static bool IsCompressionExtension(this string value, CompressionMethod method) { return value.StartsWith(method.ToExtensionString()); } internal static bool IsControl(this byte opcode) { return opcode > 0x7 && opcode < 0x10; } internal static bool IsControl(this OperationCode opcode) { return opcode >= OperationCode.Close; } internal static bool IsData(this byte opcode) { return opcode == 0x1 || opcode == 0x2; } internal static bool IsData(this OperationCode opcode) { return opcode == OperationCode.Text || opcode == OperationCode.Binary; } internal static bool IsPortNumber(this int value) { return value > 0 && value < 65536; } internal static bool IsReserved(this ushort code) { return code == (ushort)CloseStatusCode.Undefined || code == (ushort)CloseStatusCode.NoStatus || code == (ushort)CloseStatusCode.Abnormal || code == (ushort)CloseStatusCode.TlsHandshakeFailure; } internal static bool IsReserved(this CloseStatusCode code) { return code == CloseStatusCode.Undefined || code == CloseStatusCode.NoStatus || code == CloseStatusCode.Abnormal || code == CloseStatusCode.TlsHandshakeFailure; } internal static bool IsSupported(this byte opcode) { return Enum.IsDefined(typeof(OperationCode), opcode); } internal static bool IsText(this string value) { var len = value.Length; for (var i = 0; i < len; i++) { var c = value[i]; if (c < 0x20) { if (!"\r\n\t".Contains(c)) { return false; } if (c == '\n') { i++; if (i == len) { break; } c = value[i]; if (!" \t".Contains(c)) { return false; } } continue; } if (c == 0x7f) { return false; } } return true; } internal static bool IsToken(this string value) { foreach (var c in value) { if (c < 0x20) { return false; } if (c >= 0x7f) { return false; } if (_tspecials.Contains(c)) { return false; } } return true; } internal static string Quote(this string value) { return string.Format("\"{0}\"", value.Replace("\"", "\\\"")); } internal static byte[] ReadBytes(this Stream stream, int length) { var buff = new byte[length]; var offset = 0; try { var nread = 0; while (length > 0) { nread = stream.Read(buff, offset, length); if (nread == 0) { break; } offset += nread; length -= nread; } } catch { } return buff.SubArray(0, offset); } internal static byte[] ReadBytes(this Stream stream, long length, int bufferLength) { using (var dest = new MemoryStream()) { try { var buff = new byte[bufferLength]; var nread = 0; while (length > 0) { if (length < bufferLength) { bufferLength = (int)length; } nread = stream.Read(buff, 0, bufferLength); if (nread == 0) { break; } dest.Write(buff, 0, nread); length -= nread; } } catch { } dest.Close(); return dest.ToArray(); } } internal static void ReadBytesAsync( this Stream stream, int length, Action completed, Action error ) { var buff = new byte[length]; var offset = 0; var retry = 0; AsyncCallback callback = null; callback = ar => { try { var nread = stream.EndRead(ar); if (nread == 0 && retry < _retry) { retry++; stream.BeginRead(buff, offset, length, callback, null); return; } if (nread == length) { if (completed != null) { completed(buff.SubArray(0, offset + nread)); } return; } retry = 0; offset += nread; length -= nread; stream.BeginRead(buff, offset, length, callback, null); } catch (Exception ex) { if (error != null) { error(ex); } } }; try { stream.BeginRead(buff, offset, length, callback, null); } catch (Exception ex) { if (error != null) { error(ex); } } } internal static void ReadBytesAsync( this Stream stream, long length, int bufferLength, Action completed, Action error ) { var dest = new MemoryStream(); var buff = new byte[bufferLength]; var retry = 0; Action read = null; read = len => { if (len < bufferLength) { bufferLength = (int)len; } stream.BeginRead( buff, 0, bufferLength, ar => { try { var nread = stream.EndRead(ar); if (nread > 0) { dest.Write(buff, 0, nread); } if (nread == 0 && retry < _retry) { retry++; read(len); return; } if (nread == 0 || nread == len) { if (completed != null) { dest.Close(); completed(dest.ToArray()); } dest.Dispose(); return; } retry = 0; read(len - nread); } catch (Exception ex) { dest.Dispose(); if (error != null) { error(ex); } } }, null ); }; try { read(length); } catch (Exception ex) { dest.Dispose(); if (error != null) { error(ex); } } } internal static string RemovePrefix(this string value, params string[] prefixes) { var idx = 0; foreach (var prefix in prefixes) { if (value.StartsWith(prefix)) { idx = prefix.Length; break; } } return idx > 0 ? value.Substring(idx) : value; } internal static T[] Reverse(this T[] array) { var len = array.Length; var reverse = new T[len]; var end = len - 1; for (var i = 0; i <= end; i++) { reverse[i] = array[end - i]; } return reverse; } internal static IEnumerable SplitHeaderValue( this string value, params char[] separators) { var len = value.Length; var seps = new string(separators); var buff = new StringBuilder(32); var escaped = false; var quoted = false; for (var i = 0; i < len; i++) { var c = value[i]; if (c == '"') { if (escaped) { escaped = !escaped; } else { quoted = !quoted; } } else if (c == '\\') { if (i < len - 1 && value[i + 1] == '"') { escaped = true; } } else if (seps.Contains(c)) { if (!quoted) { yield return buff.ToString(); buff.Length = 0; continue; } } else { } buff.Append(c); } if (buff.Length > 0) { yield return buff.ToString(); } } internal static byte[] ToByteArray(this Stream stream) { using (var output = new MemoryStream()) { stream.Position = 0; stream.CopyTo(output, BUFFER_SIZE); output.Close(); return output.ToArray(); } } internal static CompressionMethod ToCompressionMethod(this string value) { foreach (CompressionMethod method in Enum.GetValues(typeof(CompressionMethod))) { if (method.ToExtensionString() == value) { return method; } } return CompressionMethod.None; } internal static string ToExtensionString( this CompressionMethod method, params string[] parameters) { if (method == CompressionMethod.None) { return string.Empty; } var m = string.Format("permessage-{0}", method.ToString().ToLower()); if (parameters == null || parameters.Length == 0) { return m; } return string.Format("{0}; {1}", m, parameters.ToString("; ")); } internal static System.Net.IPAddress ToIPAddress(this string value) { if (value == null || value.Length == 0) { return null; } if (System.Net.IPAddress.TryParse(value, out System.Net.IPAddress addr)) { return addr; } try { var addrs = System.Net.Dns.GetHostAddresses(value); return addrs[0]; } catch { return null; } } internal static List ToList(this IEnumerable source) { return new List(source); } internal static string ToString( this System.Net.IPAddress address, bool bracketIPv6 ) { return bracketIPv6 && address.AddressFamily == AddressFamily.InterNetworkV6 ? string.Format("[{0}]", address.ToString()) : address.ToString(); } internal static ushort ToUInt16(this byte[] source, ByteOrder sourceOrder) { return BitConverter.ToUInt16(source.ToHostOrder(sourceOrder), 0); } internal static ulong ToUInt64(this byte[] source, ByteOrder sourceOrder) { return BitConverter.ToUInt64(source.ToHostOrder(sourceOrder), 0); } internal static string TrimSlashFromEnd(this string value) { var ret = value.TrimEnd('/'); return ret.Length > 0 ? ret : "/"; } internal static string TrimSlashOrBackslashFromEnd(this string value) { var ret = value.TrimEnd('/', '\\'); return ret.Length > 0 ? ret : value[0].ToString(); } internal static bool TryCreateWebSocketUri( this string uriString, out Uri result, out string message ) { result = null; message = null; var uri = uriString.ToUri(); if (uri == null) { message = "An invalid URI string."; return false; } if (!uri.IsAbsoluteUri) { message = "A relative URI."; return false; } var scheme = uri.Scheme; if (!(scheme == "ws" || scheme == "wss")) { message = "The scheme part is not 'ws' or 'wss'."; return false; } var port = uri.Port; if (port == 0) { message = "The port part is zero."; return false; } if (uri.Fragment.Length > 0) { message = "It includes the fragment component."; return false; } result = port != -1 ? uri : new Uri( $"{scheme}://{uri.Host}:{(scheme == "ws" ? 80 : 443)}{uri.PathAndQuery}" ); return true; } internal static bool TryGetUTF8DecodedString(this byte[] bytes, out string s) { s = null; try { s = Encoding.UTF8.GetString(bytes); } catch { return false; } return true; } internal static bool TryGetUTF8EncodedBytes(this string s, out byte[] bytes) { bytes = null; try { bytes = Encoding.UTF8.GetBytes(s); } catch { return false; } return true; } internal static bool TryOpenRead( this FileInfo fileInfo, out FileStream fileStream ) { fileStream = null; try { fileStream = fileInfo.OpenRead(); } catch { return false; } return true; } internal static string Unquote(this string value) { var start = value.IndexOf('"'); if (start < 0) { return value; } var end = value.LastIndexOf('"'); var len = end - start - 1; return len < 0 ? value : len == 0 ? string.Empty : value.Substring(start + 1, len).Replace("\\\"", "\""); } internal static string UTF8Decode(this byte[] bytes) { try { return Encoding.UTF8.GetString(bytes); } catch { return null; } } internal static byte[] UTF8Encode(this string s) { return Encoding.UTF8.GetBytes(s); } internal static void WriteBytes(this Stream stream, byte[] bytes, int bufferLength) { using (var input = new MemoryStream(bytes)) { input.CopyTo(stream, bufferLength); } } internal static void WriteBytesAsync( this Stream stream, byte[] bytes, int bufferLength, Action completed, Action error) { var input = new MemoryStream(bytes); input.CopyToAsync( stream, bufferLength, () => { if (completed != null) { completed(); } input.Dispose(); }, ex => { input.Dispose(); if (error != null) { error(ex); } }); } private static byte[] compress(this byte[] data) { if (data.LongLength == 0) { return data; } using (var input = new MemoryStream(data)) { return input.compressToArray(); } } private static MemoryStream compress(this Stream stream) { var output = new MemoryStream(); if (stream.Length == 0) { return output; } stream.Position = 0; using (var ds = new DeflateStream(output, CompressionMode.Compress, true)) { stream.CopyTo(ds, BUFFER_SIZE); ds.Close(); // BFINAL set to 1. output.Write(_last, 0, 1); output.Position = 0; return output; } } private static byte[] compressToArray(this Stream stream) { using (var output = stream.compress()) { output.Close(); return output.ToArray(); } } private static byte[] decompress(this byte[] data) { if (data.LongLength == 0) { return data; } using (var input = new MemoryStream(data)) { return input.decompressToArray(); } } private static MemoryStream decompress(this Stream stream) { var output = new MemoryStream(); if (stream.Length == 0) { return output; } stream.Position = 0; using (var ds = new DeflateStream(stream, CompressionMode.Decompress, true)) { ds.CopyTo(output, BUFFER_SIZE); output.Position = 0; return output; } } private static byte[] decompressToArray(this Stream stream) { using (var output = stream.decompress()) { output.Close(); return output.ToArray(); } } private static void times(this ulong n, Action action) { for (ulong i = 0; i < n; i++) { action(); } } } }