Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of WebSocketSharp netstandard v1.0.100
BepInEx/core/WebSocketSharp-netstandard/netstandard2.0/websocket-sharp.dll
Decompiled 2 years ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Net; using System.Net.Security; using System.Net.Sockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security.Authentication; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Security.Permissions; using System.Security.Principal; using System.Text; using System.Threading; using System.Timers; using WebSocketSharp.Net; using WebSocketSharp.Net.WebSockets; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = "")] [assembly: AssemblyCompany("sta")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("sta.blockhead")] [assembly: AssemblyDescription("websocket-sharp provides the WebSocket protocol client and server.\r\n\r\nIt supports:\r\n- RFC 6455\r\n- WebSocket Client and Server\r\n- Per-message Compression extension\r\n- Secure Connection\r\n- HTTP Authentication (Basic/Digest)\r\n- Query String, Origin header and Cookies\r\n- Connecting through the HTTP Proxy server\r\n- .NET 3.5 or later (includes compatible)")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1")] [assembly: AssemblyProduct("websocket-sharp")] [assembly: AssemblyTitle("websocket-sharp")] [assembly: AssemblyVersion("1.0.1.0")] namespace WebSocketSharp { public enum ByteOrder { Little, Big } public class CloseEventArgs : EventArgs { private bool _clean; private PayloadData _payloadData; internal PayloadData PayloadData => _payloadData; public ushort Code => _payloadData.Code; public string Reason => _payloadData.Reason ?? string.Empty; public bool WasClean { get { return _clean; } internal set { _clean = value; } } internal CloseEventArgs() { _payloadData = PayloadData.Empty; } internal CloseEventArgs(ushort code) : this(code, null) { } internal CloseEventArgs(CloseStatusCode code) : this((ushort)code, null) { } internal CloseEventArgs(PayloadData payloadData) { _payloadData = payloadData; } internal CloseEventArgs(ushort code, string reason) { _payloadData = new PayloadData(code, reason); } internal CloseEventArgs(CloseStatusCode code, string reason) : this((ushort)code, reason) { } } public enum CloseStatusCode : ushort { Normal = 1000, Away = 1001, ProtocolError = 1002, UnsupportedData = 1003, Undefined = 1004, NoStatus = 1005, Abnormal = 1006, InvalidData = 1007, PolicyViolation = 1008, TooBig = 1009, MandatoryExtension = 1010, ServerError = 1011, TlsHandshakeFailure = 1015 } public enum CompressionMethod : byte { None, Deflate } public class ErrorEventArgs : EventArgs { private Exception _exception; private string _message; public Exception Exception => _exception; public string Message => _message; internal ErrorEventArgs(string message) : this(message, null) { } internal ErrorEventArgs(string message, Exception exception) { _message = message; _exception = exception; } } public static class Ext { private static readonly byte[] _last = new byte[1]; private static readonly int _retry = 5; private const string _tspecials = "()<>@,;:\\\"/[]?={} \t"; private static byte[] compress(this byte[] data) { if (data.LongLength == 0L) { return data; } using MemoryStream stream = new MemoryStream(data); return stream.compressToArray(); } private static MemoryStream compress(this Stream stream) { MemoryStream memoryStream = new MemoryStream(); if (stream.Length == 0L) { return memoryStream; } stream.Position = 0L; using DeflateStream deflateStream = new DeflateStream(memoryStream, CompressionMode.Compress, leaveOpen: true); stream.CopyTo(deflateStream, 1024); deflateStream.Close(); memoryStream.Write(_last, 0, 1); memoryStream.Position = 0L; return memoryStream; } private static byte[] compressToArray(this Stream stream) { using MemoryStream memoryStream = stream.compress(); memoryStream.Close(); return memoryStream.ToArray(); } private static byte[] decompress(this byte[] data) { if (data.LongLength == 0L) { return data; } using MemoryStream stream = new MemoryStream(data); return stream.decompressToArray(); } private static MemoryStream decompress(this Stream stream) { MemoryStream memoryStream = new MemoryStream(); if (stream.Length == 0L) { return memoryStream; } stream.Position = 0L; using DeflateStream deflateStream = new DeflateStream(stream, CompressionMode.Decompress, leaveOpen: true); deflateStream.CopyTo(memoryStream, 1024); memoryStream.Position = 0L; return memoryStream; } private static byte[] decompressToArray(this Stream stream) { using MemoryStream memoryStream = stream.decompress(); memoryStream.Close(); return memoryStream.ToArray(); } private static void times(this ulong n, Action action) { for (ulong num = 0uL; num < n; num++) { action(); } } internal static byte[] Append(this ushort code, string reason) { byte[] array = code.InternalToByteArray(ByteOrder.Big); if (reason != null && reason.Length > 0) { List<byte> list = new List<byte>(array); list.AddRange(Encoding.UTF8.GetBytes(reason)); array = list.ToArray(); } return array; } internal static void Close(this WebSocketSharp.Net.HttpListenerResponse response, WebSocketSharp.Net.HttpStatusCode code) { response.StatusCode = (int)code; response.OutputStream.Close(); } internal static void CloseWithAuthChallenge(this WebSocketSharp.Net.HttpListenerResponse response, string challenge) { response.Headers.InternalSet("WWW-Authenticate", challenge, response: true); response.Close(WebSocketSharp.Net.HttpStatusCode.Unauthorized); } internal static byte[] Compress(this byte[] data, CompressionMethod method) { if (method != CompressionMethod.Deflate) { return data; } return data.compress(); } internal static Stream Compress(this Stream stream, CompressionMethod method) { if (method != CompressionMethod.Deflate) { return stream; } return stream.compress(); } internal static byte[] CompressToArray(this Stream stream, CompressionMethod method) { if (method != CompressionMethod.Deflate) { return stream.ToByteArray(); } return stream.compressToArray(); } internal static bool Contains<T>(this IEnumerable<T> source, Func<T, bool> condition) { foreach (T item in source) { if (condition(item)) { return true; } } return false; } internal static bool ContainsTwice(this string[] values) { int len = values.Length; int end = len - 1; Func<int, bool> seek = null; seek = delegate(int idx) { if (idx == end) { return false; } string text = values[idx]; for (int i = idx + 1; i < len; i++) { if (values[i] == text) { return true; } } return seek(++idx); }; return seek(0); } internal static T[] Copy<T>(this T[] source, int length) { T[] array = new T[length]; Array.Copy(source, 0, array, 0, length); return array; } internal static T[] Copy<T>(this T[] source, long length) { T[] array = new T[length]; Array.Copy(source, 0L, array, 0L, length); return array; } internal static void CopyTo(this Stream source, Stream destination, int bufferLength) { byte[] buffer = new byte[bufferLength]; int num = 0; while ((num = source.Read(buffer, 0, bufferLength)) > 0) { destination.Write(buffer, 0, num); } } internal static void CopyToAsync(this Stream source, Stream destination, int bufferLength, Action completed, Action<Exception> error) { byte[] buff = new byte[bufferLength]; AsyncCallback callback = null; callback = delegate(IAsyncResult ar) { try { int num = source.EndRead(ar); if (num <= 0) { if (completed != null) { completed(); } } else { destination.Write(buff, 0, num); source.BeginRead(buff, 0, bufferLength, callback, null); } } catch (Exception obj2) { if (error != null) { error(obj2); } } }; try { source.BeginRead(buff, 0, bufferLength, callback, null); } catch (Exception obj) { if (error != null) { error(obj); } } } internal static byte[] Decompress(this byte[] data, CompressionMethod method) { if (method != CompressionMethod.Deflate) { return data; } return data.decompress(); } internal static Stream Decompress(this Stream stream, CompressionMethod method) { if (method != CompressionMethod.Deflate) { return stream; } return stream.decompress(); } internal static byte[] DecompressToArray(this Stream stream, CompressionMethod method) { if (method != CompressionMethod.Deflate) { return stream.ToByteArray(); } return stream.decompressToArray(); } internal static bool EqualsWith(this int value, char c, Action<int> action) { action(value); return value == c; } internal static string GetAbsolutePath(this Uri uri) { if (uri.IsAbsoluteUri) { return uri.AbsolutePath; } string originalString = uri.OriginalString; if (originalString[0] != '/') { return null; } int num = originalString.IndexOfAny(new char[2] { '?', '#' }); if (num <= 0) { return originalString; } return originalString.Substring(0, num); } internal static string GetDnsSafeHost(this Uri uri, bool bracketIPv6) { if (!bracketIPv6 || uri.HostNameType != UriHostNameType.IPv6) { return uri.DnsSafeHost; } return uri.Host; } internal static string GetMessage(this CloseStatusCode code) { return code switch { CloseStatusCode.TlsHandshakeFailure => "An error has occurred during a TLS handshake.", CloseStatusCode.ServerError => "WebSocket server got an internal error.", CloseStatusCode.MandatoryExtension => "WebSocket client didn't receive expected extension(s).", CloseStatusCode.TooBig => "A too big message has been received.", CloseStatusCode.PolicyViolation => "A policy violation has occurred.", CloseStatusCode.InvalidData => "Invalid data has been received.", CloseStatusCode.Abnormal => "An exception has occurred.", CloseStatusCode.UnsupportedData => "Unsupported data has been received.", CloseStatusCode.ProtocolError => "A WebSocket protocol error has occurred.", _ => string.Empty, }; } internal static string GetName(this string nameAndValue, char separator) { int num = nameAndValue.IndexOf(separator); if (num <= 0) { return null; } return nameAndValue.Substring(0, num).Trim(); } internal static string GetValue(this string nameAndValue, char separator) { int num = nameAndValue.IndexOf(separator); if (num <= -1 || num >= nameAndValue.Length - 1) { return null; } return nameAndValue.Substring(num + 1).Trim(); } internal static string GetValue(this string nameAndValue, char separator, bool unquote) { int num = nameAndValue.IndexOf(separator); if (num < 0 || num == nameAndValue.Length - 1) { return null; } string text = nameAndValue.Substring(num + 1).Trim(); if (!unquote) { return text; } return text.Unquote(); } internal static byte[] InternalToByteArray(this ushort value, ByteOrder order) { byte[] bytes = BitConverter.GetBytes(value); if (!order.IsHostOrder()) { Array.Reverse((Array)bytes); } return bytes; } internal static byte[] InternalToByteArray(this ulong value, ByteOrder order) { byte[] bytes = BitConverter.GetBytes(value); if (!order.IsHostOrder()) { Array.Reverse((Array)bytes); } return bytes; } internal static bool IsCompressionExtension(this string value, CompressionMethod method) { return value.StartsWith(method.ToExtensionString()); } internal static bool IsControl(this byte opcode) { if (opcode > 7) { return opcode < 16; } return false; } internal static bool IsControl(this Opcode opcode) { return (int)opcode >= 8; } internal static bool IsData(this byte opcode) { if (opcode != 1) { return opcode == 2; } return true; } internal static bool IsData(this Opcode opcode) { if (opcode != Opcode.Text) { return opcode == Opcode.Binary; } return true; } internal static bool IsPortNumber(this int value) { if (value > 0) { return value < 65536; } return false; } internal static bool IsReserved(this ushort code) { if (code != 1004 && code != 1005 && code != 1006) { return code == 1015; } return true; } internal static bool IsReserved(this CloseStatusCode code) { if (code != CloseStatusCode.Undefined && code != CloseStatusCode.NoStatus && code != CloseStatusCode.Abnormal) { return code == CloseStatusCode.TlsHandshakeFailure; } return true; } internal static bool IsSupported(this byte opcode) { return Enum.IsDefined(typeof(Opcode), opcode); } internal static bool IsText(this string value) { int length = value.Length; for (int i = 0; i < length; i++) { char c = value[i]; if (c < ' ') { if (!Contains("\r\n\t", c)) { return false; } if (c == '\n') { i++; if (i == length) { break; } c = value[i]; if (!Contains(" \t", c)) { return false; } } } else if (c == '\u007f') { return false; } } return true; } internal static bool IsToken(this string value) { foreach (char c in value) { if (c < ' ') { return false; } if (c >= '\u007f') { return false; } if (Contains("()<>@,;:\\\"/[]?={} \t", 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) { byte[] array = new byte[length]; int num = 0; try { int num2 = 0; while (length > 0) { num2 = stream.Read(array, num, length); if (num2 != 0) { num += num2; length -= num2; continue; } break; } } catch { } return array.SubArray(0, num); } internal static byte[] ReadBytes(this Stream stream, long length, int bufferLength) { using MemoryStream memoryStream = new MemoryStream(); try { byte[] buffer = new byte[bufferLength]; int num = 0; while (length > 0) { if (length < bufferLength) { bufferLength = (int)length; } num = stream.Read(buffer, 0, bufferLength); if (num != 0) { memoryStream.Write(buffer, 0, num); length -= num; continue; } break; } } catch { } memoryStream.Close(); return memoryStream.ToArray(); } internal static void ReadBytesAsync(this Stream stream, int length, Action<byte[]> completed, Action<Exception> error) { byte[] buff = new byte[length]; int offset = 0; int retry = 0; AsyncCallback callback = null; callback = delegate(IAsyncResult ar) { try { int num = stream.EndRead(ar); if (num == 0 && retry < _retry) { retry++; stream.BeginRead(buff, offset, length, callback, null); } else if (num == 0 || num == length) { if (completed != null) { completed(buff.SubArray(0, offset + num)); } } else { retry = 0; offset += num; length -= num; stream.BeginRead(buff, offset, length, callback, null); } } catch (Exception obj2) { if (error != null) { error(obj2); } } }; try { stream.BeginRead(buff, offset, length, callback, null); } catch (Exception obj) { if (error != null) { error(obj); } } } internal static void ReadBytesAsync(this Stream stream, long length, int bufferLength, Action<byte[]> completed, Action<Exception> error) { MemoryStream dest = new MemoryStream(); byte[] buff = new byte[bufferLength]; int retry = 0; Action<long> read = null; read = delegate(long len) { if (len < bufferLength) { bufferLength = (int)len; } stream.BeginRead(buff, 0, bufferLength, delegate(IAsyncResult ar) { try { int num = stream.EndRead(ar); if (num > 0) { dest.Write(buff, 0, num); } if (num == 0 && retry < _retry) { int num2 = retry; retry = num2 + 1; read(len); } else if (num == 0 || num == len) { if (completed != null) { dest.Close(); completed(dest.ToArray()); } dest.Dispose(); } else { retry = 0; read(len - num); } } catch (Exception obj2) { dest.Dispose(); if (error != null) { error(obj2); } } }, null); }; try { read(length); } catch (Exception obj) { dest.Dispose(); if (error != null) { error(obj); } } } internal static string RemovePrefix(this string value, params string[] prefixes) { int num = 0; foreach (string text in prefixes) { if (value.StartsWith(text)) { num = text.Length; break; } } if (num <= 0) { return value; } return value.Substring(num); } internal static T[] Reverse<T>(this T[] array) { int num = array.Length; T[] array2 = new T[num]; int num2 = num - 1; for (int i = 0; i <= num2; i++) { array2[i] = array[num2 - i]; } return array2; } internal static IEnumerable<string> SplitHeaderValue(this string value, params char[] separators) { int len = value.Length; string seps = new string(separators); StringBuilder buff = new StringBuilder(32); bool escaped = false; bool quoted = false; for (int i = 0; i < len; i++) { char c = value[i]; switch (c) { case '"': if (escaped) { escaped = !escaped; } else { quoted = !quoted; } break; case '\\': if (i < len - 1 && value[i + 1] == '"') { escaped = true; } break; default: if (Contains(seps, c) && !quoted) { yield return buff.ToString(); buff.Length = 0; continue; } break; } buff.Append(c); } if (buff.Length > 0) { yield return buff.ToString(); } } internal static byte[] ToByteArray(this Stream stream) { using MemoryStream memoryStream = new MemoryStream(); stream.Position = 0L; stream.CopyTo(memoryStream, 1024); memoryStream.Close(); return memoryStream.ToArray(); } internal static CompressionMethod ToCompressionMethod(this string value) { foreach (CompressionMethod value2 in Enum.GetValues(typeof(CompressionMethod))) { if (value2.ToExtensionString() == value) { return value2; } } return CompressionMethod.None; } internal static string ToExtensionString(this CompressionMethod method, params string[] parameters) { if (method == CompressionMethod.None) { return string.Empty; } string text = $"permessage-{method.ToString().ToLower()}"; if (parameters == null || parameters.Length == 0) { return text; } return string.Format("{0}; {1}", text, parameters.ToString("; ")); } internal static IPAddress ToIPAddress(this string value) { if (value == null || value.Length == 0) { return null; } if (IPAddress.TryParse(value, out IPAddress address)) { return address; } try { return Dns.GetHostAddresses(value)[0]; } catch { return null; } } internal static List<TSource> ToList<TSource>(this IEnumerable<TSource> source) { return new List<TSource>(source); } internal static string ToString(this IPAddress address, bool bracketIPv6) { if (!bracketIPv6 || address.AddressFamily != AddressFamily.InterNetworkV6) { return address.ToString(); } return $"[{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) { string text = value.TrimEnd(new char[1] { '/' }); if (text.Length <= 0) { return "/"; } return text; } internal static string TrimSlashOrBackslashFromEnd(this string value) { string text = value.TrimEnd('/', '\\'); if (text.Length <= 0) { return value[0].ToString(); } return text; } internal static bool TryCreateWebSocketUri(this string uriString, out Uri result, out string message) { result = null; message = null; Uri uri = uriString.ToUri(); if (uri == null) { message = "An invalid URI string."; return false; } if (!uri.IsAbsoluteUri) { message = "A relative URI."; return false; } string scheme = uri.Scheme; if (!(scheme == "ws") && !(scheme == "wss")) { message = "The scheme part is not 'ws' or 'wss'."; return false; } int 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(string.Format("{0}://{1}:{2}{3}", 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) { int num = value.IndexOf('"'); if (num < 0) { return value; } int num2 = value.LastIndexOf('"') - num - 1; if (num2 >= 0) { if (num2 != 0) { return value.Substring(num + 1, num2).Replace("\\\"", "\""); } return string.Empty; } return value; } 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 MemoryStream memoryStream = new MemoryStream(bytes); memoryStream.CopyTo(stream, bufferLength); } internal static void WriteBytesAsync(this Stream stream, byte[] bytes, int bufferLength, Action completed, Action<Exception> error) { MemoryStream input = new MemoryStream(bytes); input.CopyToAsync(stream, bufferLength, delegate { if (completed != null) { completed(); } input.Dispose(); }, delegate(Exception ex) { input.Dispose(); if (error != null) { error(ex); } }); } public static bool Contains(this string value, params char[] chars) { if (chars != null && chars.Length != 0) { if (value != null && value.Length != 0) { return value.IndexOfAny(chars) > -1; } return false; } return true; } public static bool Contains(this NameValueCollection collection, string name) { if (collection == null || collection.Count <= 0) { return false; } return collection[name] != null; } public static bool Contains(this NameValueCollection collection, string name, string value) { if (collection == null || collection.Count == 0) { return false; } string text = collection[name]; if (text == null) { return false; } string[] array = text.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { if (array[i].Trim().Equals(value, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } public static void Emit(this EventHandler eventHandler, object sender, EventArgs e) { eventHandler?.Invoke(sender, e); } public static void Emit<TEventArgs>(this EventHandler<TEventArgs> eventHandler, object sender, TEventArgs e) where TEventArgs : EventArgs { eventHandler?.Invoke(sender, e); } public static WebSocketSharp.Net.CookieCollection GetCookies(this NameValueCollection headers, bool response) { string name = (response ? "Set-Cookie" : "Cookie"); if (headers == null || !headers.Contains(name)) { return new WebSocketSharp.Net.CookieCollection(); } return WebSocketSharp.Net.CookieCollection.Parse(headers[name], response); } public static string GetDescription(this WebSocketSharp.Net.HttpStatusCode code) { return ((int)code).GetStatusDescription(); } public static string GetStatusDescription(this int code) { return code switch { 100 => "Continue", 101 => "Switching Protocols", 102 => "Processing", 200 => "OK", 201 => "Created", 202 => "Accepted", 203 => "Non-Authoritative Information", 204 => "No Content", 205 => "Reset Content", 206 => "Partial Content", 207 => "Multi-Status", 300 => "Multiple Choices", 301 => "Moved Permanently", 302 => "Found", 303 => "See Other", 304 => "Not Modified", 305 => "Use Proxy", 307 => "Temporary Redirect", 400 => "Bad Request", 401 => "Unauthorized", 402 => "Payment Required", 403 => "Forbidden", 404 => "Not Found", 405 => "Method Not Allowed", 406 => "Not Acceptable", 407 => "Proxy Authentication Required", 408 => "Request Timeout", 409 => "Conflict", 410 => "Gone", 411 => "Length Required", 412 => "Precondition Failed", 413 => "Request Entity Too Large", 414 => "Request-Uri Too Long", 415 => "Unsupported Media Type", 416 => "Requested Range Not Satisfiable", 417 => "Expectation Failed", 422 => "Unprocessable Entity", 423 => "Locked", 424 => "Failed Dependency", 500 => "Internal Server Error", 501 => "Not Implemented", 502 => "Bad Gateway", 503 => "Service Unavailable", 504 => "Gateway Timeout", 505 => "Http Version Not Supported", 507 => "Insufficient Storage", _ => string.Empty, }; } public static bool IsCloseStatusCode(this ushort value) { if (value > 999) { return value < 5000; } return false; } public static bool IsEnclosedIn(this string value, char c) { if (value != null && value.Length > 1 && value[0] == c) { return value[value.Length - 1] == c; } return false; } public static bool IsHostOrder(this ByteOrder order) { return BitConverter.IsLittleEndian == (order == ByteOrder.Little); } public static bool IsLocal(this IPAddress address) { if (address == null) { return false; } if (address.Equals(IPAddress.Any)) { return true; } if (address.Equals(IPAddress.Loopback)) { return true; } if (Socket.OSSupportsIPv6) { if (address.Equals(IPAddress.IPv6Any)) { return true; } if (address.Equals(IPAddress.IPv6Loopback)) { return true; } } IPAddress[] hostAddresses = Dns.GetHostAddresses(Dns.GetHostName()); foreach (IPAddress obj in hostAddresses) { if (address.Equals(obj)) { return true; } } return false; } public static bool IsNullOrEmpty(this string value) { if (value != null) { return value.Length == 0; } return true; } public static bool IsPredefinedScheme(this string value) { if (value == null || value.Length < 2) { return false; } switch (value[0]) { case 'h': if (!(value == "http")) { return value == "https"; } return true; case 'w': if (!(value == "ws")) { return value == "wss"; } return true; case 'f': if (!(value == "file")) { return value == "ftp"; } return true; case 'g': return value == "gopher"; case 'm': return value == "mailto"; case 'n': { char c = value[1]; if (c != 'e') { return value == "nntp"; } if (!(value == "news") && !(value == "net.pipe")) { return value == "net.tcp"; } return true; } default: return false; } } public static bool IsUpgradeTo(this WebSocketSharp.Net.HttpListenerRequest request, string protocol) { if (request == null) { throw new ArgumentNullException("request"); } if (protocol == null) { throw new ArgumentNullException("protocol"); } if (protocol.Length == 0) { throw new ArgumentException("An empty string.", "protocol"); } if (request.Headers.Contains("Upgrade", protocol)) { return request.Headers.Contains("Connection", "Upgrade"); } return false; } public static bool MaybeUri(this string value) { if (value == null || value.Length == 0) { return false; } int num = value.IndexOf(':'); if (num == -1) { return false; } if (num >= 10) { return false; } return value.Substring(0, num).IsPredefinedScheme(); } public static T[] SubArray<T>(this T[] array, int startIndex, int length) { int num; if (array == null || (num = array.Length) == 0) { return new T[0]; } if (startIndex < 0 || length <= 0 || startIndex + length > num) { return new T[0]; } if (startIndex == 0 && length == num) { return array; } T[] array2 = new T[length]; Array.Copy(array, startIndex, array2, 0, length); return array2; } public static T[] SubArray<T>(this T[] array, long startIndex, long length) { long num; if (array == null || (num = array.LongLength) == 0L) { return new T[0]; } if (startIndex < 0 || length <= 0 || startIndex + length > num) { return new T[0]; } if (startIndex == 0L && length == num) { return array; } T[] array2 = new T[length]; Array.Copy(array, startIndex, array2, 0L, length); return array2; } 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) { times(n, 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<int> action) { if (n > 0 && action != null) { for (int i = 0; i < n; i++) { action(i); } } } public static void Times(this long n, Action<long> action) { if (n > 0 && action != null) { for (long num = 0L; num < n; num++) { action(num); } } } public static void Times(this uint n, Action<uint> action) { if (n != 0 && action != null) { for (uint num = 0u; num < n; num++) { action(num); } } } public static void Times(this ulong n, Action<ulong> action) { if (n != 0 && action != null) { for (ulong num = 0uL; num < n; num++) { action(num); } } } public static T To<T>(this byte[] source, ByteOrder sourceOrder) where T : struct { if (source == null) { throw new ArgumentNullException("source"); } if (source.Length == 0) { return default(T); } Type typeFromHandle = typeof(T); byte[] value = source.ToHostOrder(sourceOrder); if (!(typeFromHandle == typeof(bool))) { if (!(typeFromHandle == typeof(char))) { if (!(typeFromHandle == typeof(double))) { if (!(typeFromHandle == typeof(short))) { if (!(typeFromHandle == typeof(int))) { if (!(typeFromHandle == typeof(long))) { if (!(typeFromHandle == typeof(float))) { if (!(typeFromHandle == typeof(ushort))) { if (!(typeFromHandle == typeof(uint))) { if (!(typeFromHandle == typeof(ulong))) { return default(T); } return (T)(object)BitConverter.ToUInt64(value, 0); } return (T)(object)BitConverter.ToUInt32(value, 0); } return (T)(object)BitConverter.ToUInt16(value, 0); } return (T)(object)BitConverter.ToSingle(value, 0); } return (T)(object)BitConverter.ToInt64(value, 0); } return (T)(object)BitConverter.ToInt32(value, 0); } return (T)(object)BitConverter.ToInt16(value, 0); } return (T)(object)BitConverter.ToDouble(value, 0); } return (T)(object)BitConverter.ToChar(value, 0); } return (T)(object)BitConverter.ToBoolean(value, 0); } public static byte[] ToByteArray<T>(this T value, ByteOrder order) where T : struct { Type typeFromHandle = typeof(T); byte[] array = ((typeFromHandle == typeof(bool)) ? BitConverter.GetBytes((bool)(object)value) : ((!(typeFromHandle == typeof(byte))) ? ((typeFromHandle == typeof(char)) ? BitConverter.GetBytes((char)(object)value) : ((typeFromHandle == typeof(double)) ? BitConverter.GetBytes((double)(object)value) : ((typeFromHandle == typeof(short)) ? BitConverter.GetBytes((short)(object)value) : ((typeFromHandle == typeof(int)) ? BitConverter.GetBytes((int)(object)value) : ((typeFromHandle == typeof(long)) ? BitConverter.GetBytes((long)(object)value) : ((typeFromHandle == typeof(float)) ? BitConverter.GetBytes((float)(object)value) : ((typeFromHandle == typeof(ushort)) ? BitConverter.GetBytes((ushort)(object)value) : ((typeFromHandle == typeof(uint)) ? BitConverter.GetBytes((uint)(object)value) : ((typeFromHandle == typeof(ulong)) ? BitConverter.GetBytes((ulong)(object)value) : WebSocket.EmptyBytes))))))))) : new byte[1] { (byte)(object)value })); if (array.Length > 1 && !order.IsHostOrder()) { Array.Reverse((Array)array); } return array; } public static byte[] ToHostOrder(this byte[] source, ByteOrder sourceOrder) { if (source == null) { throw new ArgumentNullException("source"); } if (source.Length <= 1 || sourceOrder.IsHostOrder()) { return source; } return source.Reverse(); } public static string ToString<T>(this T[] array, string separator) { if (array == null) { throw new ArgumentNullException("array"); } int num = array.Length; if (num == 0) { return string.Empty; } if (separator == null) { separator = string.Empty; } StringBuilder buff = new StringBuilder(64); (num - 1).Times(delegate(int i) { buff.AppendFormat("{0}{1}", array[i].ToString(), separator); }); buff.Append(array[num - 1].ToString()); return buff.ToString(); } public static Uri ToUri(this string value) { Uri.TryCreate(value, value.MaybeUri() ? UriKind.Absolute : UriKind.Relative, out Uri result); return result; } public static string UrlDecode(this string value) { if (value == null || value.Length <= 0) { return value; } return HttpUtility.UrlDecode(value); } public static string UrlEncode(this string value) { if (value == null || value.Length <= 0) { return value; } return HttpUtility.UrlEncode(value); } public static void WriteContent(this WebSocketSharp.Net.HttpListenerResponse response, byte[] content) { if (response == null) { throw new ArgumentNullException("response"); } if (content == null) { throw new ArgumentNullException("content"); } long num = content.LongLength; if (num == 0L) { response.Close(); return; } response.ContentLength64 = num; Stream outputStream = response.OutputStream; if (num <= int.MaxValue) { outputStream.Write(content, 0, (int)num); } else { outputStream.WriteBytes(content, 1024); } outputStream.Close(); } } internal enum Fin : byte { More, Final } internal abstract class HttpBase { private NameValueCollection _headers; private const int _headersMaxLength = 8192; private Version _version; internal byte[] EntityBodyData; protected const string CrLf = "\r\n"; public string EntityBody { get { if (EntityBodyData == null || EntityBodyData.LongLength == 0L) { return string.Empty; } Encoding encoding = null; string text = _headers["Content-Type"]; if (text != null && text.Length > 0) { encoding = HttpUtility.GetEncoding(text); } return (encoding ?? Encoding.UTF8).GetString(EntityBodyData); } } public NameValueCollection Headers => _headers; public Version ProtocolVersion => _version; protected HttpBase(Version version, NameValueCollection headers) { _version = version; _headers = headers; } private static byte[] readEntityBody(Stream stream, string length) { if (!long.TryParse(length, out var result)) { throw new ArgumentException("Cannot be parsed.", "length"); } if (result < 0) { throw new ArgumentOutOfRangeException("length", "Less than zero."); } if (result <= 1024) { if (result <= 0) { return null; } return stream.ReadBytes((int)result); } return stream.ReadBytes(result, 1024); } private static string[] readHeaders(Stream stream, int maxLength) { List<byte> buff = new List<byte>(); int cnt = 0; Action<int> action = delegate(int i) { if (i == -1) { throw new EndOfStreamException("The header cannot be read from the data source."); } buff.Add((byte)i); cnt++; }; bool flag = false; while (cnt < maxLength) { if (stream.ReadByte().EqualsWith('\r', action) && stream.ReadByte().EqualsWith('\n', action) && stream.ReadByte().EqualsWith('\r', action) && stream.ReadByte().EqualsWith('\n', action)) { flag = true; break; } } if (!flag) { throw new WebSocketException("The length of header part is greater than the max length."); } return Encoding.UTF8.GetString(buff.ToArray()).Replace("\r\n ", " ").Replace("\r\n\t", " ") .Split(new string[1] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); } protected static T Read<T>(Stream stream, Func<string[], T> parser, int millisecondsTimeout) where T : HttpBase { bool timeout = false; System.Threading.Timer timer = new System.Threading.Timer(delegate { timeout = true; stream.Close(); }, null, millisecondsTimeout, -1); T val = null; Exception ex = null; try { val = parser(readHeaders(stream, 8192)); string text = val.Headers["Content-Length"]; if (text != null && text.Length > 0) { val.EntityBodyData = readEntityBody(stream, text); } } catch (Exception ex2) { ex = ex2; } finally { timer.Change(-1, -1); timer.Dispose(); } string text2 = (timeout ? "A timeout has occurred while reading an HTTP request/response." : ((ex != null) ? "An exception has occurred while reading an HTTP request/response." : null)); if (text2 != null) { throw new WebSocketException(text2, ex); } return val; } public byte[] ToByteArray() { return Encoding.UTF8.GetBytes(ToString()); } } internal class HttpRequest : HttpBase { private string _method; private string _uri; private bool _websocketRequest; private bool _websocketRequestSet; public AuthenticationResponse AuthenticationResponse { get { string text = base.Headers["Authorization"]; if (text == null || text.Length <= 0) { return null; } return AuthenticationResponse.Parse(text); } } public WebSocketSharp.Net.CookieCollection Cookies => base.Headers.GetCookies(response: false); public string HttpMethod => _method; public bool IsWebSocketRequest { get { if (!_websocketRequestSet) { NameValueCollection headers = base.Headers; _websocketRequest = _method == "GET" && base.ProtocolVersion > WebSocketSharp.Net.HttpVersion.Version10 && headers.Contains("Upgrade", "websocket") && headers.Contains("Connection", "Upgrade"); _websocketRequestSet = true; } return _websocketRequest; } } public string RequestUri => _uri; private HttpRequest(string method, string uri, Version version, NameValueCollection headers) : base(version, headers) { _method = method; _uri = uri; } internal HttpRequest(string method, string uri) : this(method, uri, WebSocketSharp.Net.HttpVersion.Version11, new NameValueCollection()) { base.Headers["User-Agent"] = "websocket-sharp/1.0"; } internal static HttpRequest CreateConnectRequest(Uri uri) { string dnsSafeHost = uri.DnsSafeHost; int port = uri.Port; string text = $"{dnsSafeHost}:{port}"; HttpRequest httpRequest = new HttpRequest("CONNECT", text); httpRequest.Headers["Host"] = ((port == 80) ? dnsSafeHost : text); return httpRequest; } internal static HttpRequest CreateWebSocketRequest(Uri uri) { HttpRequest httpRequest = new HttpRequest("GET", uri.PathAndQuery); NameValueCollection headers = httpRequest.Headers; int port = uri.Port; string scheme = uri.Scheme; headers["Host"] = (((port == 80 && scheme == "ws") || (port == 443 && scheme == "wss")) ? uri.DnsSafeHost : uri.Authority); headers["Upgrade"] = "websocket"; headers["Connection"] = "Upgrade"; return httpRequest; } internal HttpResponse GetResponse(Stream stream, int millisecondsTimeout) { byte[] array = ToByteArray(); stream.Write(array, 0, array.Length); return HttpBase.Read(stream, HttpResponse.Parse, millisecondsTimeout); } internal static HttpRequest Parse(string[] headerParts) { string[] array = headerParts[0].Split(new char[1] { ' ' }, 3); if (array.Length != 3) { throw new ArgumentException("Invalid request line: " + headerParts[0]); } WebSocketSharp.Net.WebHeaderCollection webHeaderCollection = new WebSocketSharp.Net.WebHeaderCollection(); for (int i = 1; i < headerParts.Length; i++) { webHeaderCollection.InternalSet(headerParts[i], response: false); } return new HttpRequest(array[0], array[1], new Version(array[2].Substring(5)), webHeaderCollection); } internal static HttpRequest Read(Stream stream, int millisecondsTimeout) { return HttpBase.Read(stream, Parse, millisecondsTimeout); } public void SetCookies(WebSocketSharp.Net.CookieCollection cookies) { if (cookies == null || cookies.Count == 0) { return; } StringBuilder stringBuilder = new StringBuilder(64); foreach (WebSocketSharp.Net.Cookie item in cookies.Sorted) { if (!item.Expired) { stringBuilder.AppendFormat("{0}; ", item.ToString()); } } int length = stringBuilder.Length; if (length > 2) { stringBuilder.Length = length - 2; base.Headers["Cookie"] = stringBuilder.ToString(); } } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(64); stringBuilder.AppendFormat("{0} {1} HTTP/{2}{3}", _method, _uri, base.ProtocolVersion, "\r\n"); NameValueCollection headers = base.Headers; string[] allKeys = headers.AllKeys; foreach (string text in allKeys) { stringBuilder.AppendFormat("{0}: {1}{2}", text, headers[text], "\r\n"); } stringBuilder.Append("\r\n"); string entityBody = base.EntityBody; if (entityBody.Length > 0) { stringBuilder.Append(entityBody); } return stringBuilder.ToString(); } } internal class HttpResponse : HttpBase { private string _code; private string _reason; public WebSocketSharp.Net.CookieCollection Cookies => base.Headers.GetCookies(response: true); public bool HasConnectionClose => base.Headers.Contains("Connection", "close"); public bool IsProxyAuthenticationRequired => _code == "407"; public bool IsRedirect { get { if (!(_code == "301")) { return _code == "302"; } return true; } } public bool IsUnauthorized => _code == "401"; public bool IsWebSocketResponse { get { NameValueCollection headers = base.Headers; if (base.ProtocolVersion > WebSocketSharp.Net.HttpVersion.Version10 && _code == "101" && headers.Contains("Upgrade", "websocket")) { return headers.Contains("Connection", "Upgrade"); } return false; } } public string Reason => _reason; public string StatusCode => _code; private HttpResponse(string code, string reason, Version version, NameValueCollection headers) : base(version, headers) { _code = code; _reason = reason; } internal HttpResponse(WebSocketSharp.Net.HttpStatusCode code) : this(code, code.GetDescription()) { } internal HttpResponse(WebSocketSharp.Net.HttpStatusCode code, string reason) : this(((int)code).ToString(), reason, WebSocketSharp.Net.HttpVersion.Version11, new NameValueCollection()) { base.Headers["Server"] = "websocket-sharp/1.0"; } internal static HttpResponse CreateCloseResponse(WebSocketSharp.Net.HttpStatusCode code) { HttpResponse httpResponse = new HttpResponse(code); httpResponse.Headers["Connection"] = "close"; return httpResponse; } internal static HttpResponse CreateUnauthorizedResponse(string challenge) { HttpResponse httpResponse = new HttpResponse(WebSocketSharp.Net.HttpStatusCode.Unauthorized); httpResponse.Headers["WWW-Authenticate"] = challenge; return httpResponse; } internal static HttpResponse CreateWebSocketResponse() { HttpResponse httpResponse = new HttpResponse(WebSocketSharp.Net.HttpStatusCode.SwitchingProtocols); NameValueCollection headers = httpResponse.Headers; headers["Upgrade"] = "websocket"; headers["Connection"] = "Upgrade"; return httpResponse; } internal static HttpResponse Parse(string[] headerParts) { string[] array = headerParts[0].Split(new char[1] { ' ' }, 3); if (array.Length != 3) { throw new ArgumentException("Invalid status line: " + headerParts[0]); } WebSocketSharp.Net.WebHeaderCollection webHeaderCollection = new WebSocketSharp.Net.WebHeaderCollection(); for (int i = 1; i < headerParts.Length; i++) { webHeaderCollection.InternalSet(headerParts[i], response: true); } return new HttpResponse(array[1], array[2], new Version(array[0].Substring(5)), webHeaderCollection); } internal static HttpResponse Read(Stream stream, int millisecondsTimeout) { return HttpBase.Read(stream, Parse, millisecondsTimeout); } public void SetCookies(WebSocketSharp.Net.CookieCollection cookies) { if (cookies == null || cookies.Count == 0) { return; } NameValueCollection headers = base.Headers; foreach (WebSocketSharp.Net.Cookie item in cookies.Sorted) { headers.Add("Set-Cookie", item.ToResponseString()); } } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(64); stringBuilder.AppendFormat("HTTP/{0} {1} {2}{3}", base.ProtocolVersion, _code, _reason, "\r\n"); NameValueCollection headers = base.Headers; string[] allKeys = headers.AllKeys; foreach (string text in allKeys) { stringBuilder.AppendFormat("{0}: {1}{2}", text, headers[text], "\r\n"); } stringBuilder.Append("\r\n"); string entityBody = base.EntityBody; if (entityBody.Length > 0) { stringBuilder.Append(entityBody); } return stringBuilder.ToString(); } } public class LogData { private StackFrame _caller; private DateTime _date; private LogLevel _level; private string _message; public StackFrame Caller => _caller; public DateTime Date => _date; public LogLevel Level => _level; public string Message => _message; internal LogData(LogLevel level, StackFrame caller, string message) { _level = level; _caller = caller; _message = message ?? string.Empty; _date = DateTime.Now; } public override string ToString() { string text = $"{_date}|{_level,-5}|"; MethodBase method = _caller.GetMethod(); Type declaringType = method.DeclaringType; string arg = $"{text}{declaringType.Name}.{method.Name}|"; string[] array = _message.Replace("\r\n", "\n").TrimEnd(new char[1] { '\n' }).Split(new char[1] { '\n' }); if (array.Length <= 1) { return $"{arg}{_message}"; } StringBuilder stringBuilder = new StringBuilder($"{arg}{array[0]}\n", 64); string format = $"{{0,{text.Length}}}{{1}}\n"; for (int i = 1; i < array.Length; i++) { stringBuilder.AppendFormat(format, "", array[i]); } stringBuilder.Length--; return stringBuilder.ToString(); } } public class Logger { private volatile string _file; private volatile LogLevel _level; private Action<LogData, string> _output; private object _sync; public string File { get { return _file; } set { lock (_sync) { _file = value; Warn($"The current path to the log file has been changed to {_file}."); } } } public LogLevel Level { get { return _level; } set { lock (_sync) { _level = value; Warn($"The current logging level has been changed to {_level}."); } } } public Action<LogData, string> Output { get { return _output; } set { lock (_sync) { _output = value ?? new Action<LogData, string>(defaultOutput); Warn("The current output action has been changed."); } } } public Logger() : this(LogLevel.Error, null, null) { } public Logger(LogLevel level) : this(level, null, null) { } public Logger(LogLevel level, string file, Action<LogData, string> output) { _level = level; _file = file; _output = output ?? new Action<LogData, string>(defaultOutput); _sync = new object(); } private static void defaultOutput(LogData data, string path) { string value = data.ToString(); Console.WriteLine(value); if (path != null && path.Length > 0) { writeToFile(value, path); } } private void output(string message, LogLevel level) { lock (_sync) { if (_level > level) { return; } LogData logData = null; try { logData = new LogData(level, new StackFrame(2, needFileInfo: true), message); _output(logData, _file); } catch (Exception ex) { logData = new LogData(LogLevel.Fatal, new StackFrame(0, needFileInfo: true), ex.Message); Console.WriteLine(logData.ToString()); } } } private static void writeToFile(string value, string path) { using StreamWriter writer = new StreamWriter(path, append: true); using TextWriter textWriter = TextWriter.Synchronized(writer); textWriter.WriteLine(value); } public void Debug(string message) { if (_level <= LogLevel.Debug) { output(message, LogLevel.Debug); } } public void Error(string message) { if (_level <= LogLevel.Error) { output(message, LogLevel.Error); } } public void Fatal(string message) { output(message, LogLevel.Fatal); } public void Info(string message) { if (_level <= LogLevel.Info) { output(message, LogLevel.Info); } } public void Trace(string message) { if (_level <= LogLevel.Trace) { output(message, LogLevel.Trace); } } public void Warn(string message) { if (_level <= LogLevel.Warn) { output(message, LogLevel.Warn); } } } public enum LogLevel { Trace, Debug, Info, Warn, Error, Fatal } internal enum Mask : byte { Off, On } public class MessageEventArgs : EventArgs { private string _data; private bool _dataSet; private Opcode _opcode; private byte[] _rawData; internal Opcode Opcode => _opcode; public string Data { get { setData(); return _data; } } public bool IsBinary => _opcode == Opcode.Binary; public bool IsPing => _opcode == Opcode.Ping; public bool IsText => _opcode == Opcode.Text; public byte[] RawData { get { setData(); return _rawData; } } internal MessageEventArgs(WebSocketFrame frame) { _opcode = frame.Opcode; _rawData = frame.PayloadData.ApplicationData; } internal MessageEventArgs(Opcode opcode, byte[] rawData) { if ((ulong)rawData.LongLength > PayloadData.MaxLength) { throw new WebSocketException(CloseStatusCode.TooBig); } _opcode = opcode; _rawData = rawData; } private void setData() { if (!_dataSet) { if (_opcode == Opcode.Binary) { _dataSet = true; return; } _data = _rawData.UTF8Decode(); _dataSet = true; } } } internal enum Opcode : byte { Cont = 0, Text = 1, Binary = 2, Close = 8, Ping = 9, Pong = 10 } internal class PayloadData : IEnumerable<byte>, IEnumerable { private ushort _code; private bool _codeSet; private byte[] _data; private long _extDataLength; private long _length; private string _reason; private bool _reasonSet; public static readonly PayloadData Empty; public static readonly ulong MaxLength; internal ushort Code { get { if (!_codeSet) { _code = (ushort)((_length > 1) ? _data.SubArray(0, 2).ToUInt16(ByteOrder.Big) : 1005); _codeSet = true; } return _code; } } internal long ExtensionDataLength { get { return _extDataLength; } set { _extDataLength = value; } } internal bool HasReservedCode { get { if (_length > 1) { return Code.IsReserved(); } return false; } } internal string Reason { get { if (!_reasonSet) { _reason = ((_length > 2) ? _data.SubArray(2L, _length - 2).UTF8Decode() : string.Empty); _reasonSet = true; } return _reason; } } public byte[] ApplicationData { get { if (_extDataLength <= 0) { return _data; } return _data.SubArray(_extDataLength, _length - _extDataLength); } } public byte[] ExtensionData { get { if (_extDataLength <= 0) { return WebSocket.EmptyBytes; } return _data.SubArray(0L, _extDataLength); } } public ulong Length => (ulong)_length; static PayloadData() { Empty = new PayloadData(); MaxLength = 9223372036854775807uL; } internal PayloadData() { _code = 1005; _reason = string.Empty; _data = WebSocket.EmptyBytes; _codeSet = true; _reasonSet = true; } internal PayloadData(byte[] data) : this(data, data.LongLength) { } internal PayloadData(byte[] data, long length) { _data = data; _length = length; } internal PayloadData(ushort code, string reason) { _code = code; _reason = reason ?? string.Empty; _data = code.Append(reason); _length = _data.LongLength; _codeSet = true; _reasonSet = true; } internal void Mask(byte[] key) { for (long num = 0L; num < _length; num++) { _data[num] ^= key[num % 4]; } } public IEnumerator<byte> GetEnumerator() { byte[] data = _data; for (int i = 0; i < data.Length; i++) { yield return data[i]; } } public byte[] ToArray() { return _data; } public override string ToString() { return BitConverter.ToString(_data); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } internal enum Rsv : byte { Off, On } public class WebSocket : IDisposable { private AuthenticationChallenge _authChallenge; private string _base64Key; private bool _client; private Action _closeContext; private CompressionMethod _compression; private WebSocketContext _context; private WebSocketSharp.Net.CookieCollection _cookies; private WebSocketSharp.Net.NetworkCredential _credentials; private bool _emitOnPing; private bool _enableRedirection; private string _extensions; private bool _extensionsRequested; private object _forMessageEventQueue; private object _forPing; private object _forSend; private object _forState; private MemoryStream _fragmentsBuffer; private bool _fragmentsCompressed; private Opcode _fragmentsOpcode; private const string _guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; private Func<WebSocketContext, string> _handshakeRequestChecker; private bool _ignoreExtensions; private bool _inContinuation; private volatile bool _inMessage; private volatile Logger _logger; private static readonly int _maxRetryCountForConnect; private Action<MessageEventArgs> _message; private Queue<MessageEventArgs> _messageEventQueue; private uint _nonceCount; private string _origin; private ManualResetEvent _pongReceived; private bool _preAuth; private string _protocol; private string[] _protocols; private bool _protocolsRequested; private WebSocketSharp.Net.NetworkCredential _proxyCredentials; private Uri _proxyUri; private volatile WebSocketState _readyState; private ManualResetEvent _receivingExited; private int _retryCountForConnect; private bool _secure; private ClientSslConfiguration _sslConfig; private Stream _stream; private TcpClient _tcpClient; private Uri _uri; private const string _version = "13"; private TimeSpan _waitTime; internal static readonly byte[] EmptyBytes; public static int FragmentLength; internal static readonly RandomNumberGenerator RandomNumber; internal WebSocketSharp.Net.CookieCollection CookieCollection => _cookies; internal Func<WebSocketContext, string> CustomHandshakeRequestChecker { get { return _handshakeRequestChecker; } set { _handshakeRequestChecker = value; } } internal bool HasMessage { get { lock (_forMessageEventQueue) { return _messageEventQueue.Count > 0; } } } internal bool IgnoreExtensions { get { return _ignoreExtensions; } set { _ignoreExtensions = value; } } internal bool IsConnected { get { if (_readyState != WebSocketState.Open) { return _readyState == WebSocketState.Closing; } return true; } } public CompressionMethod Compression { get { return _compression; } set { string text = null; if (!_client) { text = "The set operation cannot be used by servers."; throw new InvalidOperationException(text); } if (!canSet(out text)) { _logger.Warn(text); return; } lock (_forState) { if (!canSet(out text)) { _logger.Warn(text); } else { _compression = value; } } } } public IEnumerable<WebSocketSharp.Net.Cookie> Cookies { get { lock (_cookies.SyncRoot) { foreach (WebSocketSharp.Net.Cookie cookie in _cookies) { yield return cookie; } } } } public WebSocketSharp.Net.NetworkCredential Credentials => _credentials; public bool EmitOnPing { get { return _emitOnPing; } set { _emitOnPing = value; } } public bool EnableRedirection { get { return _enableRedirection; } set { string text = null; if (!_client) { text = "The set operation cannot be used by servers."; throw new InvalidOperationException(text); } if (!canSet(out text)) { _logger.Warn(text); return; } lock (_forState) { if (!canSet(out text)) { _logger.Warn(text); } else { _enableRedirection = value; } } } } public string Extensions => _extensions ?? string.Empty; public bool IsAlive => ping(EmptyBytes); public bool IsSecure => _secure; public Logger Log { get { return _logger; } internal set { _logger = value; } } public string Origin { get { return _origin; } set { string text = null; if (!_client) { text = "This instance is not a client."; throw new InvalidOperationException(text); } if (!value.IsNullOrEmpty()) { if (!Uri.TryCreate(value, UriKind.Absolute, out Uri result)) { text = "Not an absolute URI string."; throw new ArgumentException(text, value); } if (result.Segments.Length > 1) { text = "It includes the path segments."; throw new ArgumentException(text, value); } } if (!canSet(out text)) { _logger.Warn(text); return; } lock (_forState) { if (!canSet(out text)) { _logger.Warn(text); return; } _origin = ((!value.IsNullOrEmpty()) ? value.TrimEnd(new char[1] { '/' }) : value); } } } public string Protocol { get { return _protocol ?? string.Empty; } internal set { _protocol = value; } } public WebSocketState ReadyState => _readyState; public ClientSslConfiguration SslConfiguration { get { if (!_client) { throw new InvalidOperationException("This instance is not a client."); } if (!_secure) { throw new InvalidOperationException("This instance does not use a secure connection."); } if (_sslConfig == null) { _sslConfig = new ClientSslConfiguration(_uri.DnsSafeHost); } return _sslConfig; } } public Uri Url { get { if (!_client) { return _context.RequestUri; } return _uri; } } public TimeSpan WaitTime { get { return _waitTime; } set { if (value <= TimeSpan.Zero) { throw new ArgumentOutOfRangeException("value", "Zero or less."); } if (!canSet(out var text)) { _logger.Warn(text); return; } lock (_forState) { if (!canSet(out text)) { _logger.Warn(text); } else { _waitTime = value; } } } } public event EventHandler<CloseEventArgs> OnClose; public event EventHandler<ErrorEventArgs> OnError; public event EventHandler<MessageEventArgs> OnMessage; public event EventHandler OnOpen; static WebSocket() { _maxRetryCountForConnect = 10; EmptyBytes = new byte[0]; FragmentLength = 1016; RandomNumber = new RNGCryptoServiceProvider(); } internal WebSocket(HttpListenerWebSocketContext context, string protocol) { _context = context; _protocol = protocol; _closeContext = context.Close; _logger = context.Log; _message = messages; _secure = context.IsSecureConnection; _stream = context.Stream; _waitTime = TimeSpan.FromSeconds(1.0); init(); } internal WebSocket(TcpListenerWebSocketContext context, string protocol) { _context = context; _protocol = protocol; _closeContext = context.Close; _logger = context.Log; _message = messages; _secure = context.IsSecureConnection; _stream = context.Stream; _waitTime = TimeSpan.FromSeconds(1.0); init(); } public WebSocket(string url, params string[] protocols) { if (url == null) { throw new ArgumentNullException("url"); } if (url.Length == 0) { throw new ArgumentException("An empty string.", "url"); } if (!url.TryCreateWebSocketUri(out _uri, out var text)) { throw new ArgumentException(text, "url"); } if (protocols != null && protocols.Length != 0) { if (!checkProtocols(protocols, out text)) { throw new ArgumentException(text, "protocols"); } _protocols = protocols; } _base64Key = CreateBase64Key(); _client = true; _logger = new Logger(); _message = messagec; _secure = _uri.Scheme == "wss"; _waitTime = TimeSpan.FromSeconds(5.0); init(); } private bool accept() { lock (_forState) { if (!checkIfAvailable(connecting: true, open: false, closing: false, closed: false, out var text)) { _logger.Error(text); error("An error has occurred in accepting.", null); return false; } try { if (!acceptHandshake()) { return false; } _readyState = WebSocketState.Open; } catch (Exception ex) { _logger.Fatal(ex.ToString()); fatal("An exception has occurred while accepting.", ex); return false; } return true; } } private bool acceptHandshake() { _logger.Debug($"A request from {_context.UserEndPoint}:\n{_context}"); if (!checkHandshakeRequest(_context, out var text)) { sendHttpResponse(createHandshakeFailureResponse(WebSocketSharp.Net.HttpStatusCode.BadRequest)); _logger.Fatal(text); fatal("An error has occurred while accepting.", CloseStatusCode.ProtocolError); return false; } if (!customCheckHandshakeRequest(_context, out text)) { sendHttpResponse(createHandshakeFailureResponse(WebSocketSharp.Net.HttpStatusCode.BadRequest)); _logger.Fatal(text); fatal("An error has occurred while accepting.", CloseStatusCode.PolicyViolation); return false; } _base64Key = _context.Headers["Sec-WebSocket-Key"]; if (_protocol != null) { processSecWebSocketProtocolHeader(_context.SecWebSocketProtocols); } if (!_ignoreExtensions) { processSecWebSocketExtensionsClientHeader(_context.Headers["Sec-WebSocket-Extensions"]); } return sendHttpResponse(createHandshakeResponse()); } private bool canSet(out string message) { message = null; if (_readyState == WebSocketState.Open) { message = "The connection has already been established."; return false; } if (_readyState == WebSocketState.Closing) { message = "The connection is closing."; return false; } return true; } private bool checkHandshakeRequest(WebSocketContext context, out string message) { message = null; if (context.RequestUri == null) { message = "Specifies an invalid Request-URI."; return false; } if (!context.IsWebSocketRequest) { message = "Not a WebSocket handshake request."; return false; } NameValueCollection headers = context.Headers; if (!validateSecWebSocketKeyHeader(headers["Sec-WebSocket-Key"])) { message = "Includes no Sec-WebSocket-Key header, or it has an invalid value."; return false; } if (!validateSecWebSocketVersionClientHeader(headers["Sec-WebSocket-Version"])) { message = "Includes no Sec-WebSocket-Version header, or it has an invalid value."; return false; } if (!validateSecWebSocketProtocolClientHeader(headers["Sec-WebSocket-Protocol"])) { message = "Includes an invalid Sec-WebSocket-Protocol header."; return false; } if (!_ignoreExtensions && !validateSecWebSocketExtensionsClientHeader(headers["Sec-WebSocket-Extensions"])) { message = "Includes an invalid Sec-WebSocket-Extensions header."; return false; } return true; } private bool checkHandshakeResponse(HttpResponse response, out string message) { message = null; if (response.IsRedirect) { message = "Indicates the redirection."; return false; } if (response.IsUnauthorized) { message = "Requires the authentication."; return false; } if (!response.IsWebSocketResponse) { message = "Not a WebSocket handshake response."; return false; } NameValueCollection headers = response.Headers; if (!validateSecWebSocketAcceptHeader(headers["Sec-WebSocket-Accept"])) { message = "Includes no Sec-WebSocket-Accept header, or it has an invalid value."; return false; } if (!validateSecWebSocketProtocolServerHeader(headers["Sec-WebSocket-Protocol"])) { message = "Includes no Sec-WebSocket-Protocol header, or it has an invalid value."; return false; } if (!validateSecWebSocketExtensionsServerHeader(headers["Sec-WebSocket-Extensions"])) { message = "Includes an invalid Sec-WebSocket-Extensions header."; return false; } if (!validateSecWebSocketVersionServerHeader(headers["Sec-WebSocket-Version"])) { message = "Includes an invalid Sec-WebSocket-Version header."; return false; } return true; } private bool checkIfAvailable(bool connecting, bool open, bool closing, bool closed, out string message) { message = null; if (!connecting && _readyState == WebSocketState.Connecting) { message = "This operation is not available in: connecting"; return false; } if (!open && _readyState == WebSocketState.Open) { message = "This operation is not available in: open"; return false; } if (!closing && _readyState == WebSocketState.Closing) { message = "This operation is not available in: closing"; return false; } if (!closed && _readyState == WebSocketState.Closed) { message = "This operation is not available in: closed"; return false; } return true; } private bool checkIfAvailable(bool client, bool server, bool connecting, bool open, bool closing, bool closed, out string message) { message = null; if (!client && _client) { message = "This operation is not available in: client"; return false; } if (!server && !_client) { message = "This operation is not available in: server"; return false; } return checkIfAvailable(connecting, open, closing, closed, out message); } private static bool checkParametersForSetCredentials(string username, string password, out string message) { message = null; if (username.IsNullOrEmpty()) { return true; } if (Ext.Contains(username, ':') || !username.IsText()) { message = "'username' contains an invalid character."; return false; } if (password.IsNullOrEmpty()) { return true; } if (!password.IsText()) { message = "'password' contains an invalid character."; return false; } return true; } private static bool checkParametersForSetProxy(string url, string username, string password, out string message) { message = null; if (url.IsNullOrEmpty()) { return true; } if (!Uri.TryCreate(url, UriKind.Absolute, out Uri result) || result.Scheme != "http" || result.Segments.Length > 1) { message = "'url' is an invalid URL."; return false; } if (username.IsNullOrEmpty()) { return true; } if (Ext.Contains(username, ':') || !username.IsText()) { message = "'username' contains an invalid character."; return false; } if (password.IsNullOrEmpty()) { return true; } if (!password.IsText()) { message = "'password' contains an invalid character."; return false; } return true; } private static bool checkProtocols(string[] protocols, out string message) { message = null; Func<string, bool> condition = (string protocol) => protocol.IsNullOrEmpty() || !protocol.IsToken(); if (protocols.Contains(condition)) { message = "It contains a value that is not a token."; return false; } if (protocols.ContainsTwice()) { message = "It contains a value twice."; return false; } return true; } private bool checkReceivedFrame(WebSocketFrame frame, out string message) { message = null; bool isMasked = frame.IsMasked; if (_client && isMasked) { message = "A frame from the server is masked."; return false; } if (!_client && !isMasked) { message = "A frame from a client is not masked."; return false; } if (_inContinuation && frame.IsData) { message = "A data frame has been received while receiving continuation frames."; return false; } if (frame.IsCompressed && _compression == CompressionMethod.None) { message = "A compressed frame has been received without any agreement for it."; return false; } if (frame.Rsv2 == Rsv.On) { message = "The RSV2 of a frame is non-zero without any negotiation for it."; return false; } if (frame.Rsv3 == Rsv.On) { message = "The RSV3 of a frame is non-zero without any negotiation for it."; return false; } return true; } private void close(ushort code, string reason) { if (_readyState == WebSocketState.Closing) { _logger.Info("The closing is already in progress."); return; } if (_readyState == WebSocketState.Closed) { _logger.Info("The connection has already been closed."); return; } if (code == 1005) { close(PayloadData.Empty, send: true, receive: true, received: false); return; } bool receive = !code.IsReserved(); close(new PayloadData(code, reason), receive, receive, received: false); } private void close(PayloadData payloadData, bool send, bool receive, bool received) { lock (_forState) { if (_readyState == WebSocketState.Closing) { _logger.Info("The closing is already in progress."); return; } if (_readyState == WebSocketState.Closed) { _logger.Info("The connection has already been closed."); return; } send = send && _readyState == WebSocketState.Open; receive = send && receive; _readyState = WebSocketState.Closing; } _logger.Trace("Begin closing the connection."); bool wasClean = closeHandshake(payloadData, send, receive, received); releaseResources(); _logger.Trace("End closing the connection."); _readyState = WebSocketState.Closed; CloseEventArgs closeEventArgs = new CloseEventArgs(payloadData); closeEventArgs.WasClean = wasClean; try { this.OnClose.Emit(this, closeEventArgs); } catch (Exception ex) { _logger.Error(ex.ToString()); error("An error has occurred during the OnClose event.", ex); } } private void closeAsync(ushort code, string reason) { if (_readyState == WebSocketState.Closing) { _logger.Info("The closing is already in progress."); return; } if (_readyState == WebSocketState.Closed) { _logger.Info("The connection has already been closed."); return; } if (code == 1005) { closeAsync(PayloadData.Empty, send: true, receive: true, received: false); return; } bool receive = !code.IsReserved(); closeAsync(new PayloadData(code, reason), receive, receive, received: false); } private void closeAsync(PayloadData payloadData, bool send, bool receive, bool received) { Action<PayloadData, bool, bool, bool> closer = close; closer.BeginInvoke(payloadData, send, receive, received, delegate(IAsyncResult ar) { closer.EndInvoke(ar); }, null); } private bool closeHandshake(byte[] frameAsBytes, bool receive, bool received) { bool flag = frameAsBytes != null && sendBytes(frameAsBytes); if (!received && flag && receive && _receivingExited != null) { received = _receivingExited.WaitOne(_waitTime); } bool flag2 = flag && received; _logger.Debug($"Was clean?: {flag2}\n sent: {flag}\n received: {received}"); return flag2; } private bool closeHandshake(PayloadData payloadData, bool send, bool receive, bool received) { bool flag = false; if (send) { WebSocketFrame webSocketFrame = WebSocketFrame.CreateCloseFrame(payloadData, _client); flag = sendBytes(webSocketFrame.ToArray()); if (_client) { webSocketFrame.Unmask(); } } if (!received && flag && receive && _receivingExited != null) { received = _receivingExited.WaitOne(_waitTime); } bool flag2 = flag && received; _logger.Debug($"Was clean?: {flag2}\n sent: {flag}\n received: {received}"); return flag2; } private bool connect() { lock (_forState) { if (!checkIfAvailable(connecting: true, open: false, closing: false, closed: true, out var text)) { _logger.Error(text); error("An error has occurred in connecting.", null); return false; } if (_retryCountForConnect > _maxRetryCountForConnect) { _retryCountForConnect = 0; _logger.Fatal("A series of reconnecting has failed."); return false; } _readyState = WebSocketState.Connecting; try { doHandshake(); } catch (Exception ex) { _retryCountForConnect++; _logger.Fatal(ex.ToString()); fatal("An exception has occurred while connecting.", ex); return false; } _retryCountForConnect = 1; _readyState = WebSocketState.Open; return true; } } private string createExtensions() { StringBuilder stringBuilder = new StringBuilder(80); if (_compression != 0) { string arg = _compression.ToExtensionString("server_no_context_takeover", "client_no_context_takeover"); stringBuilder.AppendFormat("{0}, ", arg); } int length = stringBuilder.Length; if (length > 2) { stringBuilder.Length = length - 2; return stringBuilder.ToString(); } return null; } private HttpResponse createHandshakeFailureResponse(WebSocketSharp.Net.HttpStatusCode code) { HttpResponse httpResponse = HttpResponse.CreateCloseResponse(code); httpResponse.Headers["Sec-WebSocket-Version"] = "13"; return httpResponse; } private HttpRequest createHandshakeRequest() { HttpRequest httpRequest = HttpRequest.CreateWebSocketRequest(_uri); NameValueCollection headers = httpRequest.Headers; if (!_origin.IsNullOrEmpty()) { headers["Origin"] = _origin; } headers["Sec-WebSocket-Key"] = _base64Key; _protocolsRequested = _protocols != null; if (_protocolsRequested) { headers["Sec-WebSocket-Protocol"] = _protocols.ToString(", "); } _extensionsRequested = _compression != CompressionMethod.None; if (_extensionsRequested) { headers["Sec-WebSocket-Extensions"] = createExtensions(); } headers["Sec-WebSocket-Version"] = "13"; AuthenticationResponse authenticationResponse = null; if (_authChallenge != null && _credentials != null) { authenticationResponse = new AuthenticationResponse(_authChallenge, _credentials, _nonceCount); _nonceCount = authenticationResponse.NonceCount; } else if (_preAuth) { authenticationResponse = new AuthenticationResponse(_credentials); } if (authenticationResponse != null) { headers["Authorization"] = authenticationResponse.ToString(); } if (_cookies.Count > 0) { httpRequest.SetCookies(_cookies); } return httpRequest; } private HttpResponse createHandshakeResponse() { HttpResponse httpResponse = HttpResponse.CreateWebSocketResponse(); NameValueCollection headers = httpResponse.Headers; headers["Sec-WebSocket-Accept"] = CreateResponseKey(_base64Key); if (_protocol != null) { headers["Sec-WebSocket-Protocol"] = _protocol; } if (_extensions != null) { headers["Sec-WebSocket-Extensions"] = _extensions; } if (_cookies.Count > 0) { httpResponse.SetCookies(_cookies); } return httpResponse; } private bool customCheckHandshakeRequest(WebSocketContext context, out string message) { message = null; if (_handshakeRequestChecker != null) { return (message = _handshakeRequestChecker(context)) == null; } return true; } private MessageEventArgs dequeueFromMessageEventQueue() { lock (_forMessageEventQueue) { return (_messageEventQueue.Count > 0) ? _messageEventQueue.Dequeue() : null; } } private void doHandshake() { setClientStream(); HttpResponse httpResponse = sendHandshakeRequest(); if (!checkHandshakeResponse(httpResponse, out var text)) { throw new WebSocketException(CloseStatusCode.ProtocolError, text); } if (_protocolsRequested) { _protocol = httpResponse.Headers["Sec-WebSocket-Protocol"]; } if (_extensionsRequested) { processSecWebSocketExtensionsServerHeader(httpResponse.Headers["Sec-WebSocket-Extensions"]); } processCookies(httpResponse.Cookies); } private void enqueueToMessageEventQueue(MessageEventArgs e) { lock (_forMessageEventQueue) { _messageEventQueue.Enqueue(e); } } private void error(string message, Exception exception) { try { this.OnError.Emit(this, new ErrorEventArgs(message, exception)); } catch (Exception ex) { _logger.Error(ex.ToString()); } } private void fatal(string message, Exception exception) { CloseStatusCode code = ((exception is WebSocketException) ? ((WebSocketException)exception).Code : CloseStatusCode.Abnormal); fatal(message, (ushort)code); } private void fatal(string message, ushort code) { PayloadData payloadData = new PayloadData(code, message); close(payloadData, !code.IsReserved(), receive: false, received: false); } private void fatal(string message, CloseStatusCode code) { fatal(message, (ushort)code); } private void init() { _compression = CompressionMethod.None; _cookies = new WebSocketSharp.Net.CookieCollection(); _forPing = new object(); _forSend = new object(); _forState = new object(); _messageEventQueue = new Queue<MessageEventArgs>(); _forMessageEventQueue = ((ICollection)_messageEventQueue).SyncRoot; _readyState = WebSocketState.Connecting; } private void message() { MessageEventArgs obj = null; lock (_forMessageEventQueue) { if (_inMessage || _messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) { return; } _inMessage = true; obj = _messageEventQueue.Dequeue(); } _message(obj); } private void messagec(MessageEventArgs e) { while (true) { try { this.OnMessage.Emit(this, e); } catch (Exception ex) { _logger.Error(ex.ToString()); error("An error has occurred during an OnMessage event.", ex); } lock (_forMessageEventQueue) { if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) { _inMessage = false; break; } e = _messageEventQueue.Dequeue(); } } } private void messages(MessageEventArgs e) { try { this.OnMessage.Emit(this, e); } catch (Exception ex) { _logger.Error(ex.ToString()); error("An error has occurred during an OnMessage event.", ex); } lock (_forMessageEventQueue) { if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) { _inMessage = false; return; } e = _messageEventQueue.Dequeue(); } ThreadPool.QueueUserWorkItem(delegate { messages(e); }); } private void open() { _inMessage = true; startReceiving(); try { this.OnOpen.Emit(this, EventArgs.Empty); } catch (Exception ex) { _logger.Error(ex.ToString()); error("An error has occurred during the OnOpen event.", ex); } MessageEventArgs obj = null; lock (_forMessageEventQueue) { if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) { _inMessage = false; return; } obj = _messageEventQueue.Dequeue(); } _message.BeginInvoke(obj, delegate(IAsyncResult ar) { _message.EndInvoke(ar); }, null); } private bool ping(byte[] data) { if (_readyState != WebSocketState.Open) { return false; } ManualResetEvent pongReceived = _pongReceived; if (pongReceived == null) { return false; } lock (_forPing) { try { pongReceived.Reset(); if (!send(Fin.Final, Opcode.Ping, data, compressed: false)) { return false; } return pongReceived.WaitOne(_waitTime); } catch (ObjectDisposedException) { return false; } } } private bool processCloseFrame(WebSocketFrame frame) { PayloadData payloadData = frame.PayloadData; close(payloadData, !payloadData.HasReservedCode, receive: false, received: true); return false; } private void processCookies(WebSocketSharp.Net.CookieCollection cookies) { if (cookies.Count != 0) { _cookies.SetOrRemove(cookies); } } private bool processDataFrame(WebSocketFrame frame) { enqueueToMessageEventQueue(frame.IsCompressed ? new MessageEventArgs(frame.Opcode, frame.PayloadData.ApplicationData.Decompress(_compression)) : new MessageEventArgs(frame)); return true; } private bool processFragmentFrame(WebSocketFrame frame) { if (!_inContinuation) { if (frame.IsContinuation) { return true; } _fragmentsOpcode = frame.Opcode; _fragmentsCompressed = frame.IsCompressed; _fragmentsBuffer = new MemoryStream(); _inContinuation = true; } _fragmentsBuffer.WriteBytes(frame.PayloadData.ApplicationData, 1024); if (frame.IsFinal) { using (_fragmentsBuffer) { byte[] rawData = (_fragmentsCompressed ? _fragmentsBuffer.DecompressToArray(_compression) : _fragmentsBuffer.ToArray()); enqueueToMessageEventQueue(new MessageEventArgs(_fragmentsOpcode, rawData)); } _fragmentsBuffer = null; _inContinuation = false; } return true; } private bool processPingFrame(WebSocketFrame frame) { _logger.Trace("A ping was received."); WebSocketFrame webSocketFrame = WebSocketFrame.CreatePongFrame(frame.PayloadData, _client); lock (_forState) { if (_readyState != WebSocketState.Open) { _logger.Error("The connection is closing."); return true; } if (!sendBytes(webSocketFrame.ToArray())) { return false; } } _logger.Trace("A pong to this ping has been sent."); if (_emitOnPing) { if (_client) { webSocketFrame.Unmask(); } enqueueToMessageEventQueue(new MessageEventArgs(frame)); } return true; } private bool processPongFrame(WebSocketFrame frame) { _logger.Trace("A pong was received."); try { _pongReceived.Set(); } catch (NullReferenceException ex) { _logger.Error(ex.Message); _logger.Debug(ex.ToString()); return false; } catch (ObjectDisposedException ex2) { _logger.Error(ex2.Message); _logger.Debug(ex2.ToString()); return false; } _logger.Trace("It has been signaled."); return true; } private bool processReceivedFrame(WebSocketFrame frame) { if (!checkReceivedFrame(frame, out var text)) { throw new WebSocketException(CloseStatusCode.ProtocolError, text); } frame.Unmask(); if (!frame.IsFragment) { if (!frame.IsData) { if (!frame.IsPing) { if (!frame.IsPong) { if (!frame.IsClose) { return processUnsupportedFrame(frame); } return processCloseFrame(frame); } return processPongFrame(frame); } return processPingFrame(frame); } return processDataFrame(frame); } return processFragmentFrame(frame); } private void processSecWebSocketExtensionsClientHeader(string value) { if (value == null) { return; } StringBuilder stringBuilder = new StringBuilder(80); bool flag = false; foreach (string item in value.SplitHeaderValue(',')) { string value2 = item.Trim(); if (!flag && value2.IsCompressionExtension(CompressionMethod.Deflate)) { _compression = CompressionMethod.Deflate; stringBuilder.AppendFormat("{0}, ", _compression.ToExtensionString("client_no_context_takeover", "server_no_context_takeover")); flag = true; } } int length = stringBuilder.Length; if (length > 2) { stringBuilder.Length = length - 2; _extensions = stringBuilder.ToString(); } } private void processSecWebSocketExtensionsServerHeader(string value) { if (value == null) { _compression = CompressionMethod.None; } else { _extensions = value; } } private void processSecWebSocketProtocolHeader(IEnumerable<string> values) { if (!values.Contains((string p) => p == _protocol)) { _protocol = null; } } private bool processUnsupportedFrame(WebSocketFrame frame) { _logger.Fatal("An unsupported frame:" + frame.PrintToString(dumped: false)); fatal("There is no way to handle it.", CloseStatusCode.PolicyViolation); return false; } private void releaseClientResources() { if (_stream != null) { _stream.Dispose(); _stream = null; } if (_tcpClient != null) { _tcpClient.Close(); _tcpClient = null; } } private void releaseCommonResources() { if (_fragmentsBuffer != null) { _fragmentsBuffer.Dispose(); _fragmentsBuffer = null; _inContinuation = false; } if (_pongReceived != null) { _pongReceived.Close(); _pongReceived = null; } if (_receivingExited != null) { _receivingExited.Close(); _receivingExited = null; } } private void releaseResources() { if (_client) { releaseClientResources(); } else { releaseServerResources(); } releaseCommonResources(); } private void releaseServerResources() { if (_closeContext != null) { _closeContext(); _closeContext = null; _stream = null; _context = null; } } private bool send(Opcode opcode, Stream stream) { lock (_forSend) { Stream stream2 = stream; bool flag = false; bool flag2 = false; try { if (_compression != 0) { stream = stream.Compress(_compression); flag = true; } flag2 = send(opcode, stream, flag); if (!flag2) { error("A send has been interrupted.", null); } } catch (Exception ex) { _logger.Error(ex.ToString()); error("An error has occurred during a send.", ex); } finally { if (flag) { stream.Dispose(); } stream2.Dispose(); } return flag2; } } private bool send(Opcode opcode, Stream stream, bool compressed) { long length = stream.Length; if (length == 0L) { return send(Fin.Final, opcode, EmptyBytes, compressed: false); } long num = length / FragmentLength; int num2 = (int)(length % FragmentLength); byte[] array = null; switch (num) { case 0L: array = new byte[num2]; if (stream.Read(array, 0, num2) == num2) { return send(Fin.Final, opcode, array, compressed); } return false; case 1L: if (num2 == 0) { array = new byte[FragmentLength]; if (stream.Read(array, 0, FragmentLength) == FragmentLength) { return send(Fin.Final, opcode, array, compressed); } return false; } break; } array = new byte[FragmentLength]; if (stream.Read(array, 0, FragmentLength) != FragmentLength || !send(Fin.More, opcode, array, compressed)) { return false; } long num3 = ((num2 == 0) ? (num - 2) : (num - 1)); for (long num4 = 0L; num4 < num3; num4++) { if (stream.Read(array, 0, FragmentLength) != FragmentLength || !send(Fin.More, Opcode.Cont, array, compressed: false)) { return false; } } if (num2 == 0) { num2 = FragmentLength; } else { array = new byte[num2]; } if (stream.Read(array, 0, num2) == num2) { return send(Fin.Final, Opcode.Cont, array, compressed: false); } return false; } private bool send(Fin fin, Opcode opcode, byte[] data, bool compressed) { lock (_forState) { if (_readyState != WebSocketState.Open) { _logger.Error("The connection is closing."); return false; } WebSocketFrame webSocketFrame = new WebSocketFrame(fin, opcode, data, compressed, _client); return sendBytes(webSocketFrame.ToArray()); } } private void sendAsync(Opcode opcode, Stream stream, Action<bool> completed) { Func<Opcode, Stream, bool> sender = send; sender.BeginInvoke(opcode, stream, delegate(IAsyncResult ar) { try { bool obj = sender.EndInvoke(ar); if (completed != null) { completed(obj); } } catch (Exception ex) { _logger.Error(ex.ToString()); error("An error has occurred during the callback for an async send.", ex); } }, null); } private bool sendBytes(byte[] bytes) { try { _stream.Write(bytes, 0, bytes.Length); } catch (Exception ex) { _logger.Error(ex.Message); _logger.Debug(ex.ToString()); return false; } return true; } private HttpResponse sendHandshakeRequest() { HttpRequest httpRequest = createHandshakeRequest(); HttpResponse httpResponse = sendHttpRequest(httpRequest, 90000); if (httpResponse.IsUnauthorized) { string text = httpResponse.Headers["WWW-Authenticate"]; _logger.Warn($"Received an authentication requirement for '{text}'."); if (text.IsNullOrEmpty()) { _logger.Error("No authentication challenge is specified."); return httpResponse; } _authChallenge = AuthenticationChallenge.Parse(text); if (_authChallenge == null) { _logger.Error("An invalid authentication challenge is specified."); return httpResponse; } if (_credentials != null && (!_preAuth || _authChallenge.Scheme == WebSocketSharp.Net.AuthenticationSchemes.Digest)) { if (httpResponse.HasConnectionClose) { releaseClientResources(); setClientStream(); } AuthenticationResponse authenticationResponse = new AuthenticationResponse(_authChallenge, _credentials, _nonceCount); _nonceCount = authenticationResponse.NonceCount; httpRequest.Headers["Authorization"] = authenticationResponse.ToString(); httpResponse = sendHttpRequest(httpRequest, 15000); } } if (httpResponse.IsRedirect) { string text2 = httpResponse.Headers["Location"]; _logger.Warn($"Received a redirection to '{text2}'."); if (_enableRedirection) { if (text2.IsNullOrEmpty()) { _logger.Error("No url to redirect is located."); return httpResponse; } if (!text2.TryCreateWebSocketUri(out var result, out var text3)) { _logger.Error("An invalid url to redirect is located: " + text3); return httpResponse; } releaseClientResources(); _uri = result; _secure = result.Scheme == "wss"; setClientStream(); return sendHandshakeRequest(); } } return httpResponse; } private HttpResponse sendHttpRequest(HttpRequest request, int millisecondsTimeout) { _logger.Debug("A request to the server:\n" + request.ToString()); HttpResponse response = request.GetResponse(_stream, millisecondsTimeout); _logger.Debug("A response to this request:\n" + response.ToString()); return response; } private bool sendHttpResponse(HttpResponse response) { _logger.Debug("A response to this request:\n" + response.ToString()); return sendBytes(response.ToByteArray()); } private void sendProxyConnectRequest() { HttpRequest httpRequest = HttpRequest.CreateConnectRequest(_uri); HttpResponse httpResponse = sendHttpRequest(httpRequest, 90000); if (httpResponse.IsProxyAuthenticationRequired) { string text = httpResponse.Headers["Proxy-Authenticate"]; _logger.Warn($"Received a proxy authentication requirement for '{text}'."); if (text.IsNullOrEmpty()) { throw new WebSocketException("No proxy authentication challenge is specified."); } AuthenticationChallenge authenticationChallenge = AuthenticationChallenge.Parse(text); if (authenticationChallenge == null) { throw new WebSocketException("An invalid proxy authentication challenge is specified."); } if (_proxyCredentials != null) { if (httpResponse.HasConnectionClose) { releaseClientResources(); _tcpClient = new TcpClient(_proxyUri.DnsSafeHost, _proxyUri.Port); _stream = _tcpClient.GetStream(); } AuthenticationResponse authenticationResponse = new AuthenticationResponse(authenticationChallenge, _proxyCredentials, 0u); httpRequest.Headers["Proxy-Authorization"] = authenticationResponse.ToString(); httpResponse = sendHttpRequest(httpRequest, 15000); } if (httpResponse.IsProxyAuthenticationRequired) { throw new WebSocketException("A proxy authentication is required."); } } if (httpResponse.StatusCode[0] != '2') { throw new WebSocketException("The proxy has failed a connection to the requested host and port."); } } private void setClientStream() { if (_proxyUri != null) { _tcpClient = new TcpClient(_proxyUri.DnsSafeHost, _proxyUri.Port); _stream = _tcpClient.GetStream(); sendProxyConnectRequest(); } else { _tcpClient = new TcpClient(_uri.DnsSafeHost, _uri.Port); _stream = _tcpClient.GetStream(); } if (_secure) { ClientSslConfiguration sslConfiguration = SslConfiguration; string targetHost = sslConfiguration.TargetHost; if (targetHost != _uri.DnsSafeHost) { throw new WebSocketException(CloseStatusCode.TlsHandshakeFailure, "An invalid host name is specified."); } try { SslStream sslStream = new SslStream(_stream, leaveInnerStreamOpen: false, sslConfiguration.ServerCertificateValidationCallback, sslConfiguration.ClientCertificateSelectionCallback); sslStream.AuthenticateAsClient(targetHost, sslConfiguration.ClientCertificates, sslConfiguration.EnabledSslProtocols, sslConfiguration.CheckCertificateRevocation); _stream = sslStream; } catch (Exception innerException) { throw new WebSocketException(CloseStatusCode.TlsHandshakeFailure, innerException); } } } private void startReceiving() { if (_messageEventQueue.Count > 0) { _messageEventQueue.Clear(); } _pongReceived = new ManualResetEvent(initialState: false); _receivingExited = new ManualResetEvent(initialState: false); Action receive = null; receive = delegate { WebSocketFrame.ReadFrameAsync(_stream, unmask: false, delegate(WebSocketFrame frame) { if (!processReceivedFrame(frame) || _readyState == WebSocketState.Closed) { _receivingExited?.Set(); } else { receive(); if (!_inMessage && HasMessage && _readyState == WebSocketState.Open) { message(); } } }, delegate(Exception ex) { _logger.Fatal(ex.ToString()); fatal("An exception has occurred while receiving.", ex); }); }; receive(); } private bool validateSecWebSocketAcceptHeader(string value) { if (value != null) { return value == CreateResponseKey(_base64Key); } return false; } private bool validateSecWebSocketExtensionsClientHeader(string value) { if (value != null) { return value.Length > 0; } return true; } private bool validateSecWebSocketExtensionsServerHeader(string value) { if (value == null) { return true; } if (value.Length == 0) { return false; } if (!_extensionsRequested) { return false; } bool flag = _compression != CompressionMethod.None; foreach (string item in value.SplitHeaderValue(',')) { string text = item.Trim(); if (flag && text.IsCompressionExtension(_compression)) { if (!text.Contains("server_no_context_takeover")) { _logger.Error("The server hasn't sent back 'server_no_context_takeover'."); return false; } if (!text.Contains("client_no_context_takeover")) { _logger.Warn("The server hasn't sent back 'client_no_context_takeover'."); } string method = _compression.ToExtensionString(); if (text.SplitHeaderValue(';').Contains(delegate(string t) { t = t.Trim(); return t != method && t != "server_no_context_takeover" && t != "client_no_context_takeover"; })) { return false; } continue; } return false; } return true; } private bool validateSecWebSocketKeyHeader(string value) { if (value != null) { return value.Length > 0; } return false; } private bool validateSecWebSocketProtocolClientHeader(string value) { if (value != null) { return value.Length > 0; } return true; } private bool validateSecWebSocketProtocolServerHeader(string value) { if (value == null) { return !_protocolsRequested; } if (value.Length == 0) { return false; } if (_protocolsRequested) { return _protocols.Contains((string p) => p == value); } return false; } private bool validateSecWebSocketVersionClientHeader(string value) { if (value != null) { return value == "13"; } return false; } private bool validateSecWebSocketVersionServerHeader(string value) { if (value != null) { return value == "13"; } return true; } internal void Close(HttpResponse response) { _readyState = WebSocketState.Closing