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(".NETFramework,Version=v4.5", FrameworkDisplayName = ".NET Framework 4.5")] [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 list = new List(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(this IEnumerable source, Func 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 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(this T[] source, int length) { T[] array = new T[length]; Array.Copy(source, 0, array, 0, length); return array; } internal static T[] Copy(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 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 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 completed, Action 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 completed, Action error) { MemoryStream dest = new MemoryStream(); byte[] buff = new byte[bufferLength]; int retry = 0; Action 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(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 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 ToList(this IEnumerable source) { return new List(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 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(this EventHandler 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(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(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 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 num = 0L; num < n; num++) { action(num); } } } public static void Times(this uint n, Action action) { if (n != 0 && action != null) { for (uint num = 0u; num < n; num++) { action(num); } } } public static void Times(this ulong n, Action action) { if (n != 0 && action != null) { for (ulong num = 0uL; num < n; num++) { action(num); } } } public static T To(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(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(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 buff = new List(); int cnt = 0; Action 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(Stream stream, Func 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 _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 Output { get { return _output; } set { lock (_sync) { _output = value ?? new Action(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 output) { _level = level; _file = file; _output = output ?? new Action(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, 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 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 _handshakeRequestChecker; private bool _ignoreExtensions; private bool _inContinuation; private volatile bool _inMessage; private volatile Logger _logger; private static readonly int _maxRetryCountForConnect; private Action _message; private Queue _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 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 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 OnClose; public event EventHandler OnError; public event EventHandler 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 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 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(); _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 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 completed) { Func 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; sendHttpResponse(response); releaseServerResources(); _readyState = WebSocketState.Closed; } internal void Close(WebSocketSharp.Net.HttpStatusCode code) { Close(createHandshakeFailureResponse(code)); } internal void Close(PayloadData payloadData, byte[] frameAsBytes) { 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; } _readyState = WebSocketState.Closing; } _logger.Trace("Begin closing the connection."); bool flag = frameAsBytes != null && sendBytes(frameAsBytes); bool flag2 = flag && _receivingExited != null && _receivingExited.WaitOne(_waitTime); bool flag3 = flag && flag2; _logger.Debug($"Was clean?: {flag3}\n sent: {flag}\n received: {flag2}"); releaseServerResources(); releaseCommonResources(); _logger.Trace("End closing the connection."); _readyState = WebSocketState.Closed; CloseEventArgs closeEventArgs = new CloseEventArgs(payloadData); closeEventArgs.WasClean = flag3; try { this.OnClose.Emit(this, closeEventArgs); } catch (Exception ex) { _logger.Error(ex.ToString()); } } internal static string CreateBase64Key() { byte[] array = new byte[16]; RandomNumber.GetBytes(array); return Convert.ToBase64String(array); } internal static string CreateResponseKey(string base64Key) { StringBuilder stringBuilder = new StringBuilder(base64Key, 64); stringBuilder.Append("258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); return Convert.ToBase64String(new SHA1CryptoServiceProvider().ComputeHash(stringBuilder.ToString().UTF8Encode())); } internal void InternalAccept() { try { if (!acceptHandshake()) { return; } _readyState = WebSocketState.Open; } catch (Exception ex) { _logger.Fatal(ex.ToString()); fatal("An exception has occurred while accepting.", ex); return; } open(); } internal bool Ping(byte[] frameAsBytes, TimeSpan timeout) { if (_readyState != WebSocketState.Open) { return false; } ManualResetEvent pongReceived = _pongReceived; if (pongReceived == null) { return false; } lock (_forPing) { try { pongReceived.Reset(); lock (_forState) { if (_readyState != WebSocketState.Open) { return false; } if (!sendBytes(frameAsBytes)) { return false; } } return pongReceived.WaitOne(timeout); } catch (ObjectDisposedException) { return false; } } } internal void Send(Opcode opcode, byte[] data, Dictionary cache) { lock (_forSend) { lock (_forState) { if (_readyState != WebSocketState.Open) { _logger.Error("The connection is closing."); return; } if (!cache.TryGetValue(_compression, out var value)) { value = new WebSocketFrame(Fin.Final, opcode, data.Compress(_compression), _compression != CompressionMethod.None, mask: false).ToArray(); cache.Add(_compression, value); } sendBytes(value); } } } internal void Send(Opcode opcode, Stream stream, Dictionary cache) { lock (_forSend) { if (!cache.TryGetValue(_compression, out var value)) { value = stream.Compress(_compression); cache.Add(_compression, value); } else { value.Position = 0L; } send(opcode, value, _compression != CompressionMethod.None); } } public void Accept() { if (!checkIfAvailable(client: false, server: true, connecting: true, open: false, closing: false, closed: false, out var text)) { _logger.Error(text); error("An error has occurred in accepting.", null); } else if (accept()) { open(); } } public void AcceptAsync() { if (!checkIfAvailable(client: false, server: true, connecting: true, open: false, closing: false, closed: false, out var text)) { _logger.Error(text); error("An error has occurred in accepting.", null); return; } Func acceptor = accept; acceptor.BeginInvoke(delegate(IAsyncResult ar) { if (acceptor.EndInvoke(ar)) { open(); } }, null); } public void Close() { close(1005, string.Empty); } public void Close(ushort code) { if (!code.IsCloseStatusCode()) { string text = "Less than 1000 or greater than 4999."; throw new ArgumentOutOfRangeException("code", text); } if (_client && code == 1011) { throw new ArgumentException("1011 cannot be used.", "code"); } if (!_client && code == 1010) { throw new ArgumentException("1010 cannot be used.", "code"); } close(code, string.Empty); } public void Close(CloseStatusCode code) { if (_client && code == CloseStatusCode.ServerError) { throw new ArgumentException("ServerError cannot be used.", "code"); } if (!_client && code == CloseStatusCode.MandatoryExtension) { throw new ArgumentException("MandatoryExtension cannot be used.", "code"); } close((ushort)code, string.Empty); } public void Close(ushort code, string reason) { if (!code.IsCloseStatusCode()) { string text = "Less than 1000 or greater than 4999."; throw new ArgumentOutOfRangeException("code", text); } if (_client && code == 1011) { throw new ArgumentException("1011 cannot be used.", "code"); } if (!_client && code == 1010) { throw new ArgumentException("1010 cannot be used.", "code"); } if (reason.IsNullOrEmpty()) { close(code, string.Empty); return; } if (code == 1005) { throw new ArgumentException("1005 cannot be used.", "code"); } if (!reason.TryGetUTF8EncodedBytes(out var bytes)) { throw new ArgumentException("It could not be UTF-8-encoded.", "reason"); } if (bytes.Length > 123) { string text2 = "Its size is greater than 123 bytes."; throw new ArgumentOutOfRangeException("reason", text2); } close(code, reason); } public void Close(CloseStatusCode code, string reason) { if (_client && code == CloseStatusCode.ServerError) { throw new ArgumentException("ServerError cannot be used.", "code"); } if (!_client && code == CloseStatusCode.MandatoryExtension) { throw new ArgumentException("MandatoryExtension cannot be used.", "code"); } if (reason.IsNullOrEmpty()) { close((ushort)code, string.Empty); return; } if (code == CloseStatusCode.NoStatus) { throw new ArgumentException("NoStatus cannot be used.", "code"); } if (!reason.TryGetUTF8EncodedBytes(out var bytes)) { throw new ArgumentException("It could not be UTF-8-encoded.", "reason"); } if (bytes.Length > 123) { string text = "Its size is greater than 123 bytes."; throw new ArgumentOutOfRangeException("reason", text); } close((ushort)code, reason); } public void CloseAsync() { closeAsync(1005, string.Empty); } public void CloseAsync(ushort code) { if (!code.IsCloseStatusCode()) { string text = "Less than 1000 or greater than 4999."; throw new ArgumentOutOfRangeException("code", text); } if (_client && code == 1011) { throw new ArgumentException("1011 cannot be used.", "code"); } if (!_client && code == 1010) { throw new ArgumentException("1010 cannot be used.", "code"); } closeAsync(code, string.Empty); } public void CloseAsync(CloseStatusCode code) { if (_client && code == CloseStatusCode.ServerError) { throw new ArgumentException("ServerError cannot be used.", "code"); } if (!_client && code == CloseStatusCode.MandatoryExtension) { throw new ArgumentException("MandatoryExtension cannot be used.", "code"); } closeAsync((ushort)code, string.Empty); } public void CloseAsync(ushort code, string reason) { if (!code.IsCloseStatusCode()) { string text = "Less than 1000 or greater than 4999."; throw new ArgumentOutOfRangeException("code", text); } if (_client && code == 1011) { throw new ArgumentException("1011 cannot be used.", "code"); } if (!_client && code == 1010) { throw new ArgumentException("1010 cannot be used.", "code"); } if (reason.IsNullOrEmpty()) { closeAsync(code, string.Empty); return; } if (code == 1005) { throw new ArgumentException("1005 cannot be used.", "code"); } if (!reason.TryGetUTF8EncodedBytes(out var bytes)) { throw new ArgumentException("It could not be UTF-8-encoded.", "reason"); } if (bytes.Length > 123) { string text2 = "Its size is greater than 123 bytes."; throw new ArgumentOutOfRangeException("reason", text2); } closeAsync(code, reason); } public void CloseAsync(CloseStatusCode code, string reason) { if (_client && code == CloseStatusCode.ServerError) { throw new ArgumentException("ServerError cannot be used.", "code"); } if (!_client && code == CloseStatusCode.MandatoryExtension) { throw new ArgumentException("MandatoryExtension cannot be used.", "code"); } if (reason.IsNullOrEmpty()) { closeAsync((ushort)code, string.Empty); return; } if (code == CloseStatusCode.NoStatus) { throw new ArgumentException("NoStatus cannot be used.", "code"); } if (!reason.TryGetUTF8EncodedBytes(out var bytes)) { throw new ArgumentException("It could not be UTF-8-encoded.", "reason"); } if (bytes.Length > 123) { string text = "Its size is greater than 123 bytes."; throw new ArgumentOutOfRangeException("reason", text); } closeAsync((ushort)code, reason); } public void Connect() { if (!checkIfAvailable(client: true, server: false, connecting: true, open: false, closing: false, closed: true, out var text)) { _logger.Error(text); error("An error has occurred in connecting.", null); } else if (connect()) { open(); } } public void ConnectAsync() { if (!checkIfAvailable(client: true, server: false, connecting: true, open: false, closing: false, closed: true, out var text)) { _logger.Error(text); error("An error has occurred in connecting.", null); return; } Func connector = connect; connector.BeginInvoke(delegate(IAsyncResult ar) { if (connector.EndInvoke(ar)) { open(); } }, null); } public bool Ping() { return ping(EmptyBytes); } public bool Ping(string message) { if (message.IsNullOrEmpty()) { return ping(EmptyBytes); } if (!message.TryGetUTF8EncodedBytes(out var bytes)) { throw new ArgumentException("It could not be UTF-8-encoded.", "message"); } if (bytes.Length > 125) { string text = "Its size is greater than 125 bytes."; throw new ArgumentOutOfRangeException("message", text); } return ping(bytes); } public void Send(byte[] data) { if (_readyState != WebSocketState.Open) { throw new InvalidOperationException("The current state of the connection is not Open."); } if (data == null) { throw new ArgumentNullException("data"); } send(Opcode.Binary, new MemoryStream(data)); } public void Send(FileInfo fileInfo) { if (_readyState != WebSocketState.Open) { throw new InvalidOperationException("The current state of the connection is not Open."); } if (fileInfo == null) { throw new ArgumentNullException("fileInfo"); } if (!fileInfo.Exists) { throw new ArgumentException("The file does not exist.", "fileInfo"); } if (!fileInfo.TryOpenRead(out var fileStream)) { throw new ArgumentException("The file could not be opened.", "fileInfo"); } send(Opcode.Binary, fileStream); } public void Send(string data) { if (_readyState != WebSocketState.Open) { throw new InvalidOperationException("The current state of the connection is not Open."); } if (data == null) { throw new ArgumentNullException("data"); } if (!data.TryGetUTF8EncodedBytes(out var bytes)) { throw new ArgumentException("It could not be UTF-8-encoded.", "data"); } send(Opcode.Text, new MemoryStream(bytes)); } public void Send(Stream stream, int length) { if (_readyState != WebSocketState.Open) { throw new InvalidOperationException("The current state of the connection is not Open."); } if (stream == null) { throw new ArgumentNullException("stream"); } if (!stream.CanRead) { throw new ArgumentException("It cannot be read.", "stream"); } if (length < 1) { throw new ArgumentException("Less than 1.", "length"); } byte[] array = stream.ReadBytes(length); int num = array.Length; if (num == 0) { throw new ArgumentException("No data could be read from it.", "stream"); } if (num < length) { _logger.Warn($"Only {num} byte(s) of data could be read from the stream."); } send(Opcode.Binary, new MemoryStream(array)); } public void SendAsync(byte[] data, Action completed) { if (_readyState != WebSocketState.Open) { throw new InvalidOperationException("The current state of the connection is not Open."); } if (data == null) { throw new ArgumentNullException("data"); } sendAsync(Opcode.Binary, new MemoryStream(data), completed); } public void SendAsync(FileInfo fileInfo, Action completed) { if (_readyState != WebSocketState.Open) { throw new InvalidOperationException("The current state of the connection is not Open."); } if (fileInfo == null) { throw new ArgumentNullException("fileInfo"); } if (!fileInfo.Exists) { throw new ArgumentException("The file does not exist.", "fileInfo"); } if (!fileInfo.TryOpenRead(out var fileStream)) { throw new ArgumentException("The file could not be opened.", "fileInfo"); } sendAsync(Opcode.Binary, fileStream, completed); } public void SendAsync(string data, Action completed) { if (_readyState != WebSocketState.Open) { throw new InvalidOperationException("The current state of the connection is not Open."); } if (data == null) { throw new ArgumentNullException("data"); } if (!data.TryGetUTF8EncodedBytes(out var bytes)) { throw new ArgumentException("It could not be UTF-8-encoded.", "data"); } sendAsync(Opcode.Text, new MemoryStream(bytes), completed); } public void SendAsync(Stream stream, int length, Action completed) { if (_readyState != WebSocketState.Open) { throw new InvalidOperationException("The current state of the connection is not Open."); } if (stream == null) { throw new ArgumentNullException("stream"); } if (!stream.CanRead) { throw new ArgumentException("It cannot be read.", "stream"); } if (length < 1) { throw new ArgumentException("Less than 1.", "length"); } byte[] array = stream.ReadBytes(length); int num = array.Length; if (num == 0) { throw new ArgumentException("No data could be read from it.", "stream"); } if (num < length) { _logger.Warn($"Only {num} byte(s) of data could be read from the stream."); } sendAsync(Opcode.Binary, new MemoryStream(array), completed); } public void SetCookie(WebSocketSharp.Net.Cookie cookie) { if (!checkIfAvailable(client: true, server: false, connecting: true, open: false, closing: false, closed: true, out var text)) { _logger.Error(text); error("An error has occurred in setting a cookie.", null); return; } if (cookie == null) { _logger.Error("'cookie' is null."); error("An error has occurred in setting a cookie.", null); return; } lock (_forState) { if (!checkIfAvailable(connecting: true, open: false, closing: false, closed: true, out text)) { _logger.Error(text); error("An error has occurred in setting a cookie.", null); return; } lock (_cookies.SyncRoot) { _cookies.SetOrRemove(cookie); } } } public void SetCredentials(string username, string password, bool preAuth) { if (!checkIfAvailable(client: true, server: false, connecting: true, open: false, closing: false, closed: true, out var text)) { _logger.Error(text); error("An error has occurred in setting the credentials.", null); return; } if (!checkParametersForSetCredentials(username, password, out text)) { _logger.Error(text); error("An error has occurred in setting the credentials.", null); return; } lock (_forState) { if (!checkIfAvailable(connecting: true, open: false, closing: false, closed: true, out text)) { _logger.Error(text); error("An error has occurred in setting the credentials.", null); } else if (username.IsNullOrEmpty()) { _logger.Warn("The credentials are initialized."); _credentials = null; _preAuth = false; } else { _credentials = new WebSocketSharp.Net.NetworkCredential(username, password, _uri.PathAndQuery); _preAuth = preAuth; } } } public void SetProxy(string url, string username, string password) { if (!checkIfAvailable(client: true, server: false, connecting: true, open: false, closing: false, closed: true, out var text)) { _logger.Error(text); error("An error has occurred in setting the proxy.", null); return; } if (!checkParametersForSetProxy(url, username, password, out text)) { _logger.Error(text); error("An error has occurred in setting the proxy.", null); return; } lock (_forState) { if (!checkIfAvailable(connecting: true, open: false, closing: false, closed: true, out text)) { _logger.Error(text); error("An error has occurred in setting the proxy.", null); return; } if (url.IsNullOrEmpty()) { _logger.Warn("The url and credentials for the proxy are initialized."); _proxyUri = null; _proxyCredentials = null; return; } _proxyUri = new Uri(url); if (username.IsNullOrEmpty()) { _logger.Warn("The credentials for the proxy are initialized."); _proxyCredentials = null; } else { _proxyCredentials = new WebSocketSharp.Net.NetworkCredential(username, password, $"{_uri.DnsSafeHost}:{_uri.Port}"); } } } void IDisposable.Dispose() { close(1001, string.Empty); } } public class WebSocketException : Exception { private CloseStatusCode _code; public CloseStatusCode Code => _code; internal WebSocketException() : this(CloseStatusCode.Abnormal, null, null) { } internal WebSocketException(Exception innerException) : this(CloseStatusCode.Abnormal, null, innerException) { } internal WebSocketException(string message) : this(CloseStatusCode.Abnormal, message, null) { } internal WebSocketException(CloseStatusCode code) : this(code, null, null) { } internal WebSocketException(string message, Exception innerException) : this(CloseStatusCode.Abnormal, message, innerException) { } internal WebSocketException(CloseStatusCode code, Exception innerException) : this(code, null, innerException) { } internal WebSocketException(CloseStatusCode code, string message) : this(code, message, null) { } internal WebSocketException(CloseStatusCode code, string message, Exception innerException) : base(message ?? code.GetMessage(), innerException) { _code = code; } } internal class WebSocketFrame : IEnumerable, IEnumerable { private byte[] _extPayloadLength; private Fin _fin; private Mask _mask; private byte[] _maskingKey; private Opcode _opcode; private PayloadData _payloadData; private byte _payloadLength; private Rsv _rsv1; private Rsv _rsv2; private Rsv _rsv3; internal static readonly byte[] EmptyPingBytes; internal int ExtendedPayloadLengthCount { get { if (_payloadLength >= 126) { if (_payloadLength != 126) { return 8; } return 2; } return 0; } } internal ulong FullPayloadLength { get { if (_payloadLength >= 126) { if (_payloadLength != 126) { return _extPayloadLength.ToUInt64(ByteOrder.Big); } return _extPayloadLength.ToUInt16(ByteOrder.Big); } return _payloadLength; } } public byte[] ExtendedPayloadLength => _extPayloadLength; public Fin Fin => _fin; public bool IsBinary => _opcode == Opcode.Binary; public bool IsClose => _opcode == Opcode.Close; public bool IsCompressed => _rsv1 == Rsv.On; public bool IsContinuation => _opcode == Opcode.Cont; public bool IsControl => (int)_opcode >= 8; public bool IsData { get { if (_opcode != Opcode.Text) { return _opcode == Opcode.Binary; } return true; } } public bool IsFinal => _fin == Fin.Final; public bool IsFragment { get { if (_fin != 0) { return _opcode == Opcode.Cont; } return true; } } public bool IsMasked => _mask == Mask.On; public bool IsPing => _opcode == Opcode.Ping; public bool IsPong => _opcode == Opcode.Pong; public bool IsText => _opcode == Opcode.Text; public ulong Length => (ulong)(2L + (long)(_extPayloadLength.Length + _maskingKey.Length)) + _payloadData.Length; public Mask Mask => _mask; public byte[] MaskingKey => _maskingKey; public Opcode Opcode => _opcode; public PayloadData PayloadData => _payloadData; public byte PayloadLength => _payloadLength; public Rsv Rsv1 => _rsv1; public Rsv Rsv2 => _rsv2; public Rsv Rsv3 => _rsv3; static WebSocketFrame() { EmptyPingBytes = CreatePingFrame(mask: false).ToArray(); } private WebSocketFrame() { } internal WebSocketFrame(Opcode opcode, PayloadData payloadData, bool mask) : this(Fin.Final, opcode, payloadData, compressed: false, mask) { } internal WebSocketFrame(Fin fin, Opcode opcode, byte[] data, bool compressed, bool mask) : this(fin, opcode, new PayloadData(data), compressed, mask) { } internal WebSocketFrame(Fin fin, Opcode opcode, PayloadData payloadData, bool compressed, bool mask) { _fin = fin; _rsv1 = ((opcode.IsData() && compressed) ? Rsv.On : Rsv.Off); _rsv2 = Rsv.Off; _rsv3 = Rsv.Off; _opcode = opcode; ulong length = payloadData.Length; if (length < 126) { _payloadLength = (byte)length; _extPayloadLength = WebSocket.EmptyBytes; } else if (length < 65536) { _payloadLength = 126; _extPayloadLength = ((ushort)length).InternalToByteArray(ByteOrder.Big); } else { _payloadLength = 127; _extPayloadLength = length.InternalToByteArray(ByteOrder.Big); } if (mask) { _mask = Mask.On; _maskingKey = createMaskingKey(); payloadData.Mask(_maskingKey); } else { _mask = Mask.Off; _maskingKey = WebSocket.EmptyBytes; } _payloadData = payloadData; } private static byte[] createMaskingKey() { byte[] array = new byte[4]; WebSocket.RandomNumber.GetBytes(array); return array; } private static string dump(WebSocketFrame frame) { ulong length = frame.Length; long num = (long)(length / 4); int num2 = (int)(length % 4); int num3; string arg5; if (num < 10000) { num3 = 4; arg5 = "{0,4}"; } else if (num < 65536) { num3 = 4; arg5 = "{0,4:X}"; } else if (num < 4294967296L) { num3 = 8; arg5 = "{0,8:X}"; } else { num3 = 16; arg5 = "{0,16:X}"; } string arg6 = $"{{0,{num3}}}"; string format = string.Format("\n{0} 01234567 89ABCDEF 01234567 89ABCDEF\n{0}+--------+--------+--------+--------+\\n", arg6); string lineFmt = $"{arg5}|{{1,8}} {{2,8}} {{3,8}} {{4,8}}|\n"; string format2 = $"{arg6}+--------+--------+--------+--------+"; StringBuilder output = new StringBuilder(64); Action action = ((Func>)delegate { long lineCnt = 0L; return delegate(string arg1, string arg2, string arg3, string arg4) { output.AppendFormat(lineFmt, ++lineCnt, arg1, arg2, arg3, arg4); }; })(); output.AppendFormat(format, string.Empty); byte[] array = frame.ToArray(); for (long num4 = 0L; num4 <= num; num4++) { long num5 = num4 * 4; if (num4 < num) { action(Convert.ToString(array[num5], 2).PadLeft(8, '0'), Convert.ToString(array[num5 + 1], 2).PadLeft(8, '0'), Convert.ToString(array[num5 + 2], 2).PadLeft(8, '0'), Convert.ToString(array[num5 + 3], 2).PadLeft(8, '0')); } else if (num2 > 0) { action(Convert.ToString(array[num5], 2).PadLeft(8, '0'), (num2 >= 2) ? Convert.ToString(array[num5 + 1], 2).PadLeft(8, '0') : string.Empty, (num2 == 3) ? Convert.ToString(array[num5 + 2], 2).PadLeft(8, '0') : string.Empty, string.Empty); } } output.AppendFormat(format2, string.Empty); return output.ToString(); } private static string print(WebSocketFrame frame) { byte payloadLength = frame._payloadLength; string text = ((payloadLength > 125) ? frame.FullPayloadLength.ToString() : string.Empty); string text2 = BitConverter.ToString(frame._maskingKey); string text3 = ((payloadLength == 0) ? string.Empty : ((payloadLength > 125) ? "---" : ((frame.IsText && !frame.IsFragment && !frame.IsMasked && !frame.IsCompressed) ? frame._payloadData.ApplicationData.UTF8Decode() : frame._payloadData.ToString()))); return $"\n FIN: {frame._fin}\n RSV1: {frame._rsv1}\n RSV2: {frame._rsv2}\n RSV3: {frame._rsv3}\n Opcode: {frame._opcode}\n MASK: {frame._mask}\n Payload Length: {payloadLength}\nExtended Payload Length: {text}\n Masking Key: {text2}\n Payload Data: {text3}"; } private static WebSocketFrame processHeader(byte[] header) { if (header.Length != 2) { throw new WebSocketException("The header of a frame cannot be read from the stream."); } Fin fin = (((header[0] & 0x80) == 128) ? Fin.Final : Fin.More); Rsv rsv = (((header[0] & 0x40) == 64) ? Rsv.On : Rsv.Off); Rsv rsv2 = (((header[0] & 0x20) == 32) ? Rsv.On : Rsv.Off); Rsv rsv3 = (((header[0] & 0x10) == 16) ? Rsv.On : Rsv.Off); byte opcode = (byte)(header[0] & 0xFu); Mask mask = (((header[1] & 0x80) == 128) ? Mask.On : Mask.Off); byte b = (byte)(header[1] & 0x7Fu); string text = ((!opcode.IsSupported()) ? "An unsupported opcode." : ((!opcode.IsData() && rsv == Rsv.On) ? "A non data frame is compressed." : ((opcode.IsControl() && fin == Fin.More) ? "A control frame is fragmented." : ((opcode.IsControl() && b > 125) ? "A control frame has a long payload length." : null)))); if (text != null) { throw new WebSocketException(CloseStatusCode.ProtocolError, text); } return new WebSocketFrame { _fin = fin, _rsv1 = rsv, _rsv2 = rsv2, _rsv3 = rsv3, _opcode = (Opcode)opcode, _mask = mask, _payloadLength = b }; } private static WebSocketFrame readExtendedPayloadLength(Stream stream, WebSocketFrame frame) { int extendedPayloadLengthCount = frame.ExtendedPayloadLengthCount; if (extendedPayloadLengthCount == 0) { frame._extPayloadLength = WebSocket.EmptyBytes; return frame; } byte[] array = stream.ReadBytes(extendedPayloadLengthCount); if (array.Length != extendedPayloadLengthCount) { throw new WebSocketException("The extended payload length of a frame cannot be read from the stream."); } frame._extPayloadLength = array; return frame; } private static void readExtendedPayloadLengthAsync(Stream stream, WebSocketFrame frame, Action completed, Action error) { int len = frame.ExtendedPayloadLengthCount; if (len == 0) { frame._extPayloadLength = WebSocket.EmptyBytes; completed(frame); return; } stream.ReadBytesAsync(len, delegate(byte[] bytes) { if (bytes.Length != len) { throw new WebSocketException("The extended payload length of a frame cannot be read from the stream."); } frame._extPayloadLength = bytes; completed(frame); }, error); } private static WebSocketFrame readHeader(Stream stream) { return processHeader(stream.ReadBytes(2)); } private static void readHeaderAsync(Stream stream, Action completed, Action error) { stream.ReadBytesAsync(2, delegate(byte[] bytes) { completed(processHeader(bytes)); }, error); } private static WebSocketFrame readMaskingKey(Stream stream, WebSocketFrame frame) { int num = (frame.IsMasked ? 4 : 0); if (num == 0) { frame._maskingKey = WebSocket.EmptyBytes; return frame; } byte[] array = stream.ReadBytes(num); if (array.Length != num) { throw new WebSocketException("The masking key of a frame cannot be read from the stream."); } frame._maskingKey = array; return frame; } private static void readMaskingKeyAsync(Stream stream, WebSocketFrame frame, Action completed, Action error) { int len = (frame.IsMasked ? 4 : 0); if (len == 0) { frame._maskingKey = WebSocket.EmptyBytes; completed(frame); return; } stream.ReadBytesAsync(len, delegate(byte[] bytes) { if (bytes.Length != len) { throw new WebSocketException("The masking key of a frame cannot be read from the stream."); } frame._maskingKey = bytes; completed(frame); }, error); } private static WebSocketFrame readPayloadData(Stream stream, WebSocketFrame frame) { ulong fullPayloadLength = frame.FullPayloadLength; if (fullPayloadLength == 0L) { frame._payloadData = PayloadData.Empty; return frame; } if (fullPayloadLength > PayloadData.MaxLength) { throw new WebSocketException(CloseStatusCode.TooBig, "A frame has a long payload length."); } long num = (long)fullPayloadLength; byte[] array = ((frame._payloadLength < 127) ? stream.ReadBytes((int)fullPayloadLength) : stream.ReadBytes(num, 1024)); if (array.LongLength != num) { throw new WebSocketException("The payload data of a frame cannot be read from the stream."); } frame._payloadData = new PayloadData(array, num); return frame; } private static void readPayloadDataAsync(Stream stream, WebSocketFrame frame, Action completed, Action error) { ulong fullPayloadLength = frame.FullPayloadLength; if (fullPayloadLength == 0L) { frame._payloadData = PayloadData.Empty; completed(frame); return; } if (fullPayloadLength > PayloadData.MaxLength) { throw new WebSocketException(CloseStatusCode.TooBig, "A frame has a long payload length."); } long llen = (long)fullPayloadLength; Action completed2 = delegate(byte[] bytes) { if (bytes.LongLength != llen) { throw new WebSocketException("The payload data of a frame cannot be read from the stream."); } frame._payloadData = new PayloadData(bytes, llen); completed(frame); }; if (frame._payloadLength < 127) { stream.ReadBytesAsync((int)fullPayloadLength, completed2, error); } else { stream.ReadBytesAsync(llen, 1024, completed2, error); } } internal static WebSocketFrame CreateCloseFrame(PayloadData payloadData, bool mask) { return new WebSocketFrame(Fin.Final, Opcode.Close, payloadData, compressed: false, mask); } internal static WebSocketFrame CreatePingFrame(bool mask) { return new WebSocketFrame(Fin.Final, Opcode.Ping, PayloadData.Empty, compressed: false, mask); } internal static WebSocketFrame CreatePingFrame(byte[] data, bool mask) { return new WebSocketFrame(Fin.Final, Opcode.Ping, new PayloadData(data), compressed: false, mask); } internal static WebSocketFrame CreatePongFrame(PayloadData payloadData, bool mask) { return new WebSocketFrame(Fin.Final, Opcode.Pong, payloadData, compressed: false, mask); } internal static WebSocketFrame ReadFrame(Stream stream, bool unmask) { WebSocketFrame webSocketFrame = readHeader(stream); readExtendedPayloadLength(stream, webSocketFrame); readMaskingKey(stream, webSocketFrame); readPayloadData(stream, webSocketFrame); if (unmask) { webSocketFrame.Unmask(); } return webSocketFrame; } internal static void ReadFrameAsync(Stream stream, bool unmask, Action completed, Action error) { readHeaderAsync(stream, delegate(WebSocketFrame frame) { readExtendedPayloadLengthAsync(stream, frame, delegate(WebSocketFrame frame1) { readMaskingKeyAsync(stream, frame1, delegate(WebSocketFrame frame2) { readPayloadDataAsync(stream, frame2, delegate(WebSocketFrame frame3) { if (unmask) { frame3.Unmask(); } completed(frame3); }, error); }, error); }, error); }, error); } internal void Unmask() { if (_mask != 0) { _mask = Mask.Off; _payloadData.Mask(_maskingKey); _maskingKey = WebSocket.EmptyBytes; } } public IEnumerator GetEnumerator() { byte[] array = ToArray(); for (int i = 0; i < array.Length; i++) { yield return array[i]; } } public void Print(bool dumped) { Console.WriteLine(dumped ? dump(this) : print(this)); } public string PrintToString(bool dumped) { if (!dumped) { return print(this); } return dump(this); } public byte[] ToArray() { using MemoryStream memoryStream = new MemoryStream(); int fin = (int)_fin; fin = (fin << 1) + (int)_rsv1; fin = (fin << 1) + (int)_rsv2; fin = (fin << 1) + (int)_rsv3; fin = (fin << 4) + (int)_opcode; fin = (fin << 1) + (int)_mask; fin = (fin << 7) + _payloadLength; memoryStream.Write(((ushort)fin).InternalToByteArray(ByteOrder.Big), 0, 2); if (_payloadLength > 125) { memoryStream.Write(_extPayloadLength, 0, (_payloadLength == 126) ? 2 : 8); } if (_mask == Mask.On) { memoryStream.Write(_maskingKey, 0, 4); } if (_payloadLength > 0) { byte[] array = _payloadData.ToArray(); if (_payloadLength < 127) { memoryStream.Write(array, 0, array.Length); } else { memoryStream.WriteBytes(array, 1024); } } memoryStream.Close(); return memoryStream.ToArray(); } public override string ToString() { return BitConverter.ToString(ToArray()); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } public enum WebSocketState : ushort { Connecting, Open, Closing, Closed } } namespace WebSocketSharp.Server { public class HttpRequestEventArgs : EventArgs { private WebSocketSharp.Net.HttpListenerContext _context; private string _docRootPath; public WebSocketSharp.Net.HttpListenerRequest Request => _context.Request; public WebSocketSharp.Net.HttpListenerResponse Response => _context.Response; public IPrincipal User => _context.User; internal HttpRequestEventArgs(WebSocketSharp.Net.HttpListenerContext context, string documentRootPath) { _context = context; _docRootPath = documentRootPath; } private string createFilePath(string childPath) { childPath = childPath.TrimStart('/', '\\'); return new StringBuilder(_docRootPath, 32).AppendFormat("/{0}", childPath).ToString().Replace('\\', '/'); } private static bool tryReadFile(string path, out byte[] contents) { contents = null; if (!File.Exists(path)) { return false; } try { contents = File.ReadAllBytes(path); } catch { return false; } return true; } public byte[] ReadFile(string path) { if (path == null) { throw new ArgumentNullException("path"); } if (path.Length == 0) { throw new ArgumentException("An empty string.", "path"); } if (path.IndexOf("..") > -1) { throw new ArgumentException("It contains '..'.", "path"); } tryReadFile(createFilePath(path), out var contents); return contents; } public bool TryReadFile(string path, out byte[] contents) { if (path == null) { throw new ArgumentNullException("path"); } if (path.Length == 0) { throw new ArgumentException("An empty string.", "path"); } if (path.IndexOf("..") > -1) { throw new ArgumentException("It contains '..'.", "path"); } return tryReadFile(createFilePath(path), out contents); } } public class HttpServer { private IPAddress _address; private string _docRootPath; private string _hostname; private WebSocketSharp.Net.HttpListener _listener; private Logger _log; private int _port; private Thread _receiveThread; private bool _secure; private WebSocketServiceManager _services; private volatile ServerState _state; private object _sync; public IPAddress Address => _address; public WebSocketSharp.Net.AuthenticationSchemes AuthenticationSchemes { get { return _listener.AuthenticationSchemes; } set { if (!canSet(out var message)) { _log.Warn(message); return; } lock (_sync) { if (!canSet(out message)) { _log.Warn(message); } else { _listener.AuthenticationSchemes = value; } } } } public string DocumentRootPath { get { return _docRootPath; } set { if (value == null) { throw new ArgumentNullException("value"); } if (value.Length == 0) { throw new ArgumentException("An empty string.", "value"); } value = value.TrimSlashOrBackslashFromEnd(); string text = null; try { text = Path.GetFullPath(value); } catch (Exception innerException) { throw new ArgumentException("An invalid path string.", "value", innerException); } if (value == "/") { throw new ArgumentException("An absolute root.", "value"); } if (value == "\\") { throw new ArgumentException("An absolute root.", "value"); } if (value.Length == 2 && value[1] == ':') { throw new ArgumentException("An absolute root.", "value"); } if (text == "/") { throw new ArgumentException("An absolute root.", "value"); } text = text.TrimSlashOrBackslashFromEnd(); if (text.Length == 2 && text[1] == ':') { throw new ArgumentException("An absolute root.", "value"); } if (!canSet(out var message)) { _log.Warn(message); return; } lock (_sync) { if (!canSet(out message)) { _log.Warn(message); } else { _docRootPath = value; } } } } public bool IsListening => _state == ServerState.Start; public bool IsSecure => _secure; public bool KeepClean { get { return _services.KeepClean; } set { _services.KeepClean = value; } } public Logger Log => _log; public int Port => _port; public string Realm { get { return _listener.Realm; } set { if (!canSet(out var message)) { _log.Warn(message); return; } lock (_sync) { if (!canSet(out message)) { _log.Warn(message); } else { _listener.Realm = value; } } } } public bool ReuseAddress { get { return _listener.ReuseAddress; } set { if (!canSet(out var message)) { _log.Warn(message); return; } lock (_sync) { if (!canSet(out message)) { _log.Warn(message); } else { _listener.ReuseAddress = value; } } } } public ServerSslConfiguration SslConfiguration { get { if (!_secure) { throw new InvalidOperationException("This instance does not provide secure connections."); } return _listener.SslConfiguration; } } public Func UserCredentialsFinder { get { return _listener.UserCredentialsFinder; } set { if (!canSet(out var message)) { _log.Warn(message); return; } lock (_sync) { if (!canSet(out message)) { _log.Warn(message); } else { _listener.UserCredentialsFinder = value; } } } } public TimeSpan WaitTime { get { return _services.WaitTime; } set { _services.WaitTime = value; } } public WebSocketServiceManager WebSocketServices => _services; public event EventHandler OnConnect; public event EventHandler OnDelete; public event EventHandler OnGet; public event EventHandler OnHead; public event EventHandler OnOptions; public event EventHandler OnPatch; public event EventHandler OnPost; public event EventHandler OnPut; public event EventHandler OnTrace; public HttpServer() { init("*", IPAddress.Any, 80, secure: false); } public HttpServer(int port) : this(port, port == 443) { } public HttpServer(string url) { if (url == null) { throw new ArgumentNullException("url"); } if (url.Length == 0) { throw new ArgumentException("An empty string.", "url"); } if (!tryCreateUri(url, out var result, out var message)) { throw new ArgumentException(message, "url"); } string dnsSafeHost = result.GetDnsSafeHost(bracketIPv6: true); IPAddress iPAddress = dnsSafeHost.ToIPAddress(); if (iPAddress == null) { message = "The host part could not be converted to an IP address."; throw new ArgumentException(message, "url"); } if (!iPAddress.IsLocal()) { message = "The IP address of the host is not a local IP address."; throw new ArgumentException(message, "url"); } init(dnsSafeHost, iPAddress, result.Port, result.Scheme == "https"); } public HttpServer(int port, bool secure) { if (!port.IsPortNumber()) { string message = "Less than 1 or greater than 65535."; throw new ArgumentOutOfRangeException("port", message); } init("*", IPAddress.Any, port, secure); } public HttpServer(IPAddress address, int port) : this(address, port, port == 443) { } public HttpServer(IPAddress address, int port, bool secure) { if (address == null) { throw new ArgumentNullException("address"); } if (!address.IsLocal()) { throw new ArgumentException("Not a local IP address.", "address"); } if (!port.IsPortNumber()) { string message = "Less than 1 or greater than 65535."; throw new ArgumentOutOfRangeException("port", message); } init(address.ToString(bracketIPv6: true), address, port, secure); } private void abort() { lock (_sync) { if (_state != ServerState.Start) { return; } _state = ServerState.ShuttingDown; } try { try { _services.Stop(1006, string.Empty); } finally { _listener.Abort(); } } catch { } _state = ServerState.Stop; } private bool canSet(out string message) { message = null; if (_state == ServerState.Start) { message = "The server has already started."; return false; } if (_state == ServerState.ShuttingDown) { message = "The server is shutting down."; return false; } return true; } private bool checkCertificate(out string message) { message = null; bool flag = _listener.SslConfiguration.ServerCertificate != null; string certificateFolderPath = _listener.CertificateFolderPath; bool flag2 = EndPointListener.CertificateExists(_port, certificateFolderPath); if (flag && flag2) { _log.Warn("A server certificate associated with the port is used."); return true; } if (!(flag || flag2)) { message = "There is no server certificate for secure connections."; return false; } return true; } private string createFilePath(string childPath) { childPath = childPath.TrimStart('/', '\\'); return new StringBuilder(_docRootPath, 32).AppendFormat("/{0}", childPath).ToString().Replace('\\', '/'); } private static WebSocketSharp.Net.HttpListener createListener(string hostname, int port, bool secure) { WebSocketSharp.Net.HttpListener httpListener = new WebSocketSharp.Net.HttpListener(); string arg = (secure ? "https" : "http"); string uriPrefix = $"{arg}://{hostname}:{port}/"; httpListener.Prefixes.Add(uriPrefix); return httpListener; } private void init(string hostname, IPAddress address, int port, bool secure) { _hostname = hostname; _address = address; _port = port; _secure = secure; _docRootPath = "./Public"; _listener = createListener(_hostname, _port, _secure); _log = _listener.Log; _services = new WebSocketServiceManager(_log); _sync = new object(); } private void processRequest(WebSocketSharp.Net.HttpListenerContext context) { EventHandler eventHandler = context.Request.HttpMethod switch { "PATCH" => this.OnPatch, "CONNECT" => this.OnConnect, "TRACE" => this.OnTrace, "OPTIONS" => this.OnOptions, "DELETE" => this.OnDelete, "PUT" => this.OnPut, "POST" => this.OnPost, "HEAD" => this.OnHead, "GET" => this.OnGet, _ => null, }; if (eventHandler != null) { eventHandler(this, new HttpRequestEventArgs(context, _docRootPath)); } else { context.Response.StatusCode = 501; } context.Response.Close(); } private void processRequest(HttpListenerWebSocketContext context) { string absolutePath = context.RequestUri.AbsolutePath; if (!_services.InternalTryGetServiceHost(absolutePath, out var host)) { context.Close(WebSocketSharp.Net.HttpStatusCode.NotImplemented); } else { host.StartSession(context); } } private void receiveRequest() { while (true) { WebSocketSharp.Net.HttpListenerContext ctx = null; try { ctx = _listener.GetContext(); ThreadPool.QueueUserWorkItem(delegate { try { if (ctx.Request.IsUpgradeTo("websocket")) { processRequest(ctx.AcceptWebSocket(null)); } else { processRequest(ctx); } } catch (Exception ex4) { _log.Fatal(ex4.Message); _log.Debug(ex4.ToString()); ctx.Connection.Close(force: true); } }); } catch (WebSocketSharp.Net.HttpListenerException) { _log.Info("The underlying listener is stopped."); break; } catch (InvalidOperationException) { _log.Info("The underlying listener is stopped."); break; } catch (Exception ex3) { _log.Fatal(ex3.Message); _log.Debug(ex3.ToString()); if (ctx != null) { ctx.Connection.Close(force: true); } break; } } if (_state != ServerState.ShuttingDown) { abort(); } } private void start() { if (_state == ServerState.Start) { _log.Info("The server has already started."); return; } if (_state == ServerState.ShuttingDown) { _log.Warn("The server is shutting down."); return; } lock (_sync) { if (_state == ServerState.Start) { _log.Info("The server has already started."); return; } if (_state == ServerState.ShuttingDown) { _log.Warn("The server is shutting down."); return; } _services.Start(); try { startReceiving(); } catch { _services.Stop(1011, string.Empty); throw; } _state = ServerState.Start; } } private void startReceiving() { try { _listener.Start(); } catch (Exception innerException) { throw new InvalidOperationException("The underlying listener has failed to start.", innerException); } _receiveThread = new Thread(receiveRequest); _receiveThread.IsBackground = true; _receiveThread.Start(); } private void stop(ushort code, string reason) { if (_state == ServerState.Ready) { _log.Info("The server is not started."); return; } if (_state == ServerState.ShuttingDown) { _log.Info("The server is shutting down."); return; } if (_state == ServerState.Stop) { _log.Info("The server has already stopped."); return; } lock (_sync) { if (_state == ServerState.ShuttingDown) { _log.Info("The server is shutting down."); return; } if (_state == ServerState.Stop) { _log.Info("The server has already stopped."); return; } _state = ServerState.ShuttingDown; } try { bool flag = false; try { _services.Stop(code, reason); } catch { flag = true; throw; } finally { try { stopReceiving(5000); } catch { if (!flag) { throw; } } } } finally { _state = ServerState.Stop; } } private void stopReceiving(int millisecondsTimeout) { _listener.Stop(); _receiveThread.Join(millisecondsTimeout); } private static bool tryCreateUri(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 == "http") && !(scheme == "https")) { message = "The scheme part is not 'http' or 'https'."; return false; } if (uri.PathAndQuery != "/") { message = "It includes either or both path and query components."; return false; } if (uri.Fragment.Length > 0) { message = "It includes the fragment component."; return false; } if (uri.Port == 0) { message = "The port part is zero."; return false; } result = uri; return true; } [Obsolete("This method will be removed. Use added one instead.")] public void AddWebSocketService(string path, Func creator) where TBehavior : WebSocketBehavior { if (path == null) { throw new ArgumentNullException("path"); } if (creator == null) { throw new ArgumentNullException("creator"); } if (path.Length == 0) { throw new ArgumentException("An empty string.", "path"); } if (path[0] != '/') { throw new ArgumentException("Not an absolute path.", "path"); } if (path.IndexOfAny(new char[2] { '?', '#' }) > -1) { throw new ArgumentException("It includes either or both query and fragment components.", "path"); } _services.Add(path, creator); } public void AddWebSocketService(string path) where TBehaviorWithNew : WebSocketBehavior, new() { _services.AddService(path, null); } public void AddWebSocketService(string path, Action initializer) where TBehaviorWithNew : WebSocketBehavior, new() { _services.AddService(path, initializer); } [Obsolete("This method will be removed.")] public byte[] GetFile(string path) { if (path == null) { throw new ArgumentNullException("path"); } if (path.Length == 0) { throw new ArgumentException("An empty string.", "path"); } if (path.IndexOf("..") > -1) { throw new ArgumentException("It contains '..'.", "path"); } path = createFilePath(path); if (!File.Exists(path)) { return null; } return File.ReadAllBytes(path); } public bool RemoveWebSocketService(string path) { return _services.RemoveService(path); } public void Start() { if (_secure && !checkCertificate(out var message)) { throw new InvalidOperationException(message); } start(); } public void Stop() { stop(1005, string.Empty); } public void Stop(ushort code, string reason) { if (!code.IsCloseStatusCode()) { string message = "Less than 1000 or greater than 4999."; throw new ArgumentOutOfRangeException("code", message); } if (code == 1010) { throw new ArgumentException("1010 cannot be used.", "code"); } if (!reason.IsNullOrEmpty()) { if (code == 1005) { throw new ArgumentException("1005 cannot be used.", "code"); } if (!reason.TryGetUTF8EncodedBytes(out var bytes)) { throw new ArgumentException("It could not be UTF-8-encoded.", "reason"); } if (bytes.Length > 123) { string message2 = "Its size is greater than 123 bytes."; throw new ArgumentOutOfRangeException("reason", message2); } } stop(code, reason); } public void Stop(CloseStatusCode code, string reason) { if (code == CloseStatusCode.MandatoryExtension) { throw new ArgumentException("MandatoryExtension cannot be used.", "code"); } if (!reason.IsNullOrEmpty()) { if (code == CloseStatusCode.NoStatus) { throw new ArgumentException("NoStatus cannot be used.", "code"); } if (!reason.TryGetUTF8EncodedBytes(out var bytes)) { throw new ArgumentException("It could not be UTF-8-encoded.", "reason"); } if (bytes.Length > 123) { string message = "Its size is greater than 123 bytes."; throw new ArgumentOutOfRangeException("reason", message); } } stop((ushort)code, reason); } } public interface IWebSocketSession { WebSocketContext Context { get; } string ID { get; } string Protocol { get; } DateTime StartTime { get; } WebSocketState State { get; } } internal enum ServerState { Ready, Start, ShuttingDown, Stop } public abstract class WebSocketBehavior : IWebSocketSession { private WebSocketContext _context; private Func _cookiesValidator; private bool _emitOnPing; private string _id; private bool _ignoreExtensions; private Func _originValidator; private string _protocol; private WebSocketSessionManager _sessions; private DateTime _startTime; private WebSocket _websocket; protected Logger Log { get { if (_websocket == null) { return null; } return _websocket.Log; } } protected WebSocketSessionManager Sessions => _sessions; public WebSocketContext Context => _context; public Func CookiesValidator { get { return _cookiesValidator; } set { _cookiesValidator = value; } } public bool EmitOnPing { get { if (_websocket == null) { return _emitOnPing; } return _websocket.EmitOnPing; } set { if (_websocket != null) { _websocket.EmitOnPing = value; } else { _emitOnPing = value; } } } public string ID => _id; public bool IgnoreExtensions { get { return _ignoreExtensions; } set { _ignoreExtensions = value; } } public Func OriginValidator { get { return _originValidator; } set { _originValidator = value; } } public string Protocol { get { string protocol; if (_websocket == null) { protocol = _protocol; if (protocol == null) { return string.Empty; } } else { protocol = _websocket.Protocol; } return protocol; } set { if (State == WebSocketState.Connecting && (value == null || (value.Length != 0 && value.IsToken()))) { _protocol = value; } } } public DateTime StartTime => _startTime; public WebSocketState State { get { if (_websocket == null) { return WebSocketState.Connecting; } return _websocket.ReadyState; } } protected WebSocketBehavior() { _startTime = DateTime.MaxValue; } private string checkHandshakeRequest(WebSocketContext context) { if (_originValidator == null || _originValidator(context.Origin)) { if (_cookiesValidator == null || _cookiesValidator(context.CookieCollection, context.WebSocket.CookieCollection)) { return null; } return "Includes no cookie, or an invalid cookie exists."; } return "Includes no Origin header, or it has an invalid value."; } private void onClose(object sender, CloseEventArgs e) { if (_id != null) { _sessions.Remove(_id); OnClose(e); } } private void onError(object sender, ErrorEventArgs e) { OnError(e); } private void onMessage(object sender, MessageEventArgs e) { OnMessage(e); } private void onOpen(object sender, EventArgs e) { _id = _sessions.Add(this); if (_id == null) { _websocket.Close(CloseStatusCode.Away); return; } _startTime = DateTime.Now; OnOpen(); } internal void Start(WebSocketContext context, WebSocketSessionManager sessions) { if (_websocket != null) { _websocket.Log.Error("A session instance cannot be reused."); context.WebSocket.Close(WebSocketSharp.Net.HttpStatusCode.ServiceUnavailable); return; } _context = context; _sessions = sessions; _websocket = context.WebSocket; _websocket.CustomHandshakeRequestChecker = checkHandshakeRequest; _websocket.EmitOnPing = _emitOnPing; _websocket.IgnoreExtensions = _ignoreExtensions; _websocket.Protocol = _protocol; TimeSpan waitTime = sessions.WaitTime; if (waitTime != _websocket.WaitTime) { _websocket.WaitTime = waitTime; } _websocket.OnOpen += onOpen; _websocket.OnMessage += onMessage; _websocket.OnError += onError; _websocket.OnClose += onClose; _websocket.InternalAccept(); } protected void Error(string message, Exception exception) { if (message != null && message.Length > 0) { OnError(new ErrorEventArgs(message, exception)); } } protected virtual void OnClose(CloseEventArgs e) { } protected virtual void OnError(ErrorEventArgs e) { } protected virtual void OnMessage(MessageEventArgs e) { } protected virtual void OnOpen() { } protected void Send(byte[] data) { if (_websocket != null) { _websocket.Send(data); } } protected void Send(FileInfo file) { if (_websocket != null) { _websocket.Send(file); } } protected void Send(string data) { if (_websocket != null) { _websocket.Send(data); } } protected void SendAsync(byte[] data, Action completed) { if (_websocket != null) { _websocket.SendAsync(data, completed); } } protected void SendAsync(FileInfo file, Action completed) { if (_websocket != null) { _websocket.SendAsync(file, completed); } } protected void SendAsync(string data, Action completed) { if (_websocket != null) { _websocket.SendAsync(data, completed); } } protected void SendAsync(Stream stream, int length, Action completed) { if (_websocket != null) { _websocket.SendAsync(stream, length, completed); } } } public class WebSocketServer { private IPAddress _address; private bool _allowForwardedRequest; private WebSocketSharp.Net.AuthenticationSchemes _authSchemes; private static readonly string _defaultRealm; private bool _dnsStyle; private string _hostname; private TcpListener _listener; private Logger _log; private int _port; private string _realm; private string _realmInUse; private Thread _receiveThread; private bool _reuseAddress; private bool _secure; private WebSocketServiceManager _services; private ServerSslConfiguration _sslConfig; private ServerSslConfiguration _sslConfigInUse; private volatile ServerState _state; private object _sync; private Func _userCredFinder; public IPAddress Address => _address; public bool AllowForwardedRequest { get { return _allowForwardedRequest; } set { if (!canSet(out var message)) { _log.Warn(message); return; } lock (_sync) { if (!canSet(out message)) { _log.Warn(message); } else { _allowForwardedRequest = value; } } } } public WebSocketSharp.Net.AuthenticationSchemes AuthenticationSchemes { get { return _authSchemes; } set { if (!canSet(out var message)) { _log.Warn(message); return; } lock (_sync) { if (!canSet(out message)) { _log.Warn(message); } else { _authSchemes = value; } } } } public bool IsListening => _state == ServerState.Start; public bool IsSecure => _secure; public bool KeepClean { get { return _services.KeepClean; } set { _services.KeepClean = value; } } public Logger Log => _log; public int Port => _port; public string Realm { get { return _realm; } set { if (!canSet(out var message)) { _log.Warn(message); return; } lock (_sync) { if (!canSet(out message)) { _log.Warn(message); } else { _realm = value; } } } } public bool ReuseAddress { get { return _reuseAddress; } set { if (!canSet(out var message)) { _log.Warn(message); return; } lock (_sync) { if (!canSet(out message)) { _log.Warn(message); } else { _reuseAddress = value; } } } } public ServerSslConfiguration SslConfiguration { get { if (!_secure) { throw new InvalidOperationException("This instance does not provide secure connections."); } return getSslConfiguration(); } } public Func UserCredentialsFinder { get { return _userCredFinder; } set { if (!canSet(out var message)) { _log.Warn(message); return; } lock (_sync) { if (!canSet(out message)) { _log.Warn(message); } else { _userCredFinder = value; } } } } public TimeSpan WaitTime { get { return _services.WaitTime; } set { _services.WaitTime = value; } } public WebSocketServiceManager WebSocketServices => _services; static WebSocketServer() { _defaultRealm = "SECRET AREA"; } public WebSocketServer() { IPAddress any = IPAddress.Any; init(any.ToString(), any, 80, secure: false); } public WebSocketServer(int port) : this(port, port == 443) { } public WebSocketServer(string url) { if (url == null) { throw new ArgumentNullException("url"); } if (url.Length == 0) { throw new ArgumentException("An empty string.", "url"); } if (!tryCreateUri(url, out var result, out var message)) { throw new ArgumentException(message, "url"); } string dnsSafeHost = result.DnsSafeHost; IPAddress iPAddress = dnsSafeHost.ToIPAddress(); if (iPAddress == null) { message = "The host part could not be converted to an IP address."; throw new ArgumentException(message, "url"); } if (!iPAddress.IsLocal()) { message = "The IP address of the host is not a local IP address."; throw new ArgumentException(message, "url"); } init(dnsSafeHost, iPAddress, result.Port, result.Scheme == "wss"); } public WebSocketServer(int port, bool secure) { if (!port.IsPortNumber()) { string message = "Less than 1 or greater than 65535."; throw new ArgumentOutOfRangeException("port", message); } IPAddress any = IPAddress.Any; init(any.ToString(), any, port, secure); } public WebSocketServer(IPAddress address, int port) : this(address, port, port == 443) { } public WebSocketServer(IPAddress address, int port, bool secure) { if (address == null) { throw new ArgumentNullException("address"); } if (!address.IsLocal()) { throw new ArgumentException("Not a local IP address.", "address"); } if (!port.IsPortNumber()) { string message = "Less than 1 or greater than 65535."; throw new ArgumentOutOfRangeException("port", message); } init(address.ToString(), address, port, secure); } private void abort() { lock (_sync) { if (_state != ServerState.Start) { return; } _state = ServerState.ShuttingDown; } try { try { _listener.Stop(); } finally { _services.Stop(1006, string.Empty); } } catch { } _state = ServerState.Stop; } private bool canSet(out string message) { message = null; if (_state == ServerState.Start) { message = "The server has already started."; return false; } if (_state == ServerState.ShuttingDown) { message = "The server is shutting down."; return false; } return true; } private bool checkHostNameForRequest(string name) { if (_dnsStyle && Uri.CheckHostName(name) == UriHostNameType.Dns) { return name == _hostname; } return true; } private static bool checkSslConfiguration(ServerSslConfiguration configuration, out string message) { message = null; if (configuration.ServerCertificate == null) { message = "There is no server certificate for secure connections."; return false; } return true; } private string getRealm() { string realm = _realm; if (realm == null || realm.Length <= 0) { return _defaultRealm; } return realm; } private ServerSslConfiguration getSslConfiguration() { if (_sslConfig == null) { _sslConfig = new ServerSslConfiguration(); } return _sslConfig; } private void init(string hostname, IPAddress address, int port, bool secure) { _hostname = hostname; _address = address; _port = port; _secure = secure; _authSchemes = WebSocketSharp.Net.AuthenticationSchemes.Anonymous; _dnsStyle = Uri.CheckHostName(hostname) == UriHostNameType.Dns; _listener = new TcpListener(address, port); _log = new Logger(); _services = new WebSocketServiceManager(_log); _sync = new object(); } private void processRequest(TcpListenerWebSocketContext context) { Uri requestUri = context.RequestUri; if (requestUri == null) { context.Close(WebSocketSharp.Net.HttpStatusCode.BadRequest); return; } if (!_allowForwardedRequest) { if (requestUri.Port != _port) { context.Close(WebSocketSharp.Net.HttpStatusCode.BadRequest); return; } if (!checkHostNameForRequest(requestUri.DnsSafeHost)) { context.Close(WebSocketSharp.Net.HttpStatusCode.NotFound); return; } } if (!_services.InternalTryGetServiceHost(requestUri.AbsolutePath, out var host)) { context.Close(WebSocketSharp.Net.HttpStatusCode.NotImplemented); } else { host.StartSession(context); } } private void receiveRequest() { while (true) { TcpClient cl = null; try { cl = _listener.AcceptTcpClient(); ThreadPool.QueueUserWorkItem(delegate { try { TcpListenerWebSocketContext tcpListenerWebSocketContext = new TcpListenerWebSocketContext(cl, null, _secure, _sslConfigInUse, _log); if (tcpListenerWebSocketContext.Authenticate(_authSchemes, _realmInUse, _userCredFinder)) { processRequest(tcpListenerWebSocketContext); } } catch (Exception ex3) { _log.Error(ex3.Message); _log.Debug(ex3.ToString()); cl.Close(); } }); } catch (SocketException ex) { if (_state == ServerState.ShuttingDown) { _log.Info("The underlying listener is stopped."); break; } _log.Fatal(ex.Message); _log.Debug(ex.ToString()); break; } catch (Exception ex2) { _log.Fatal(ex2.Message); _log.Debug(ex2.ToString()); if (cl != null) { cl.Close(); } break; } } if (_state != ServerState.ShuttingDown) { abort(); } } private void start(ServerSslConfiguration sslConfig) { if (_state == ServerState.Start) { _log.Info("The server has already started."); return; } if (_state == ServerState.ShuttingDown) { _log.Warn("The server is shutting down."); return; } lock (_sync) { if (_state == ServerState.Start) { _log.Info("The server has already started."); return; } if (_state == ServerState.ShuttingDown) { _log.Warn("The server is shutting down."); return; } _sslConfigInUse = sslConfig; _realmInUse = getRealm(); _services.Start(); try { startReceiving(); } catch { _services.Stop(1011, string.Empty); throw; } _state = ServerState.Start; } } private void startReceiving() { if (_reuseAddress) { _listener.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, optionValue: true); } try { _listener.Start(); } catch (Exception innerException) { throw new InvalidOperationException("The underlying listener has failed to start.", innerException); } _receiveThread = new Thread(receiveRequest); _receiveThread.IsBackground = true; _receiveThread.Start(); } private void stop(ushort code, string reason) { if (_state == ServerState.Ready) { _log.Info("The server is not started."); return; } if (_state == ServerState.ShuttingDown) { _log.Info("The server is shutting down."); return; } if (_state == ServerState.Stop) { _log.Info("The server has already stopped."); return; } lock (_sync) { if (_state == ServerState.ShuttingDown) { _log.Info("The server is shutting down."); return; } if (_state == ServerState.Stop) { _log.Info("The server has already stopped."); return; } _state = ServerState.ShuttingDown; } try { bool flag = false; try { stopReceiving(5000); } catch { flag = true; throw; } finally { try { _services.Stop(code, reason); } catch { if (!flag) { throw; } } } } finally { _state = ServerState.Stop; } } private void stopReceiving(int millisecondsTimeout) { try { _listener.Stop(); } catch (Exception innerException) { throw new InvalidOperationException("The underlying listener has failed to stop.", innerException); } _receiveThread.Join(millisecondsTimeout); } private static bool tryCreateUri(string uriString, out Uri result, out string message) { if (!uriString.TryCreateWebSocketUri(out result, out message)) { return false; } if (result.PathAndQuery != "/") { result = null; message = "It includes either or both path and query components."; return false; } return true; } [Obsolete("This method will be removed. Use added one instead.")] public void AddWebSocketService(string path, Func creator) where TBehavior : WebSocketBehavior { if (path == null) { throw new ArgumentNullException("path"); } if (creator == null) { throw new ArgumentNullException("creator"); } if (path.Length == 0) { throw new ArgumentException("An empty string.", "path"); } if (path[0] != '/') { throw new ArgumentException("Not an absolute path.", "path"); } if (path.IndexOfAny(new char[2] { '?', '#' }) > -1) { throw new ArgumentException("It includes either or both query and fragment components.", "path"); } _services.Add(path, creator); } public void AddWebSocketService(string path) where TBehaviorWithNew : WebSocketBehavior, new() { _services.AddService(path, null); } public void AddWebSocketService(string path, Action initializer) where TBehaviorWithNew : WebSocketBehavior, new() { _services.AddService(path, initializer); } public bool RemoveWebSocketService(string path) { return _services.RemoveService(path); } public void Start() { ServerSslConfiguration serverSslConfiguration = null; if (_secure) { serverSslConfiguration = new ServerSslConfiguration(getSslConfiguration()); if (!checkSslConfiguration(serverSslConfiguration, out var message)) { throw new InvalidOperationException(message); } } start(serverSslConfiguration); } public void Stop() { stop(1005, string.Empty); } public void Stop(ushort code, string reason) { if (!code.IsCloseStatusCode()) { string message = "Less than 1000 or greater than 4999."; throw new ArgumentOutOfRangeException("code", message); } if (code == 1010) { throw new ArgumentException("1010 cannot be used.", "code"); } if (!reason.IsNullOrEmpty()) { if (code == 1005) { throw new ArgumentException("1005 cannot be used.", "code"); } if (!reason.TryGetUTF8EncodedBytes(out var bytes)) { throw new ArgumentException("It could not be UTF-8-encoded.", "reason"); } if (bytes.Length > 123) { string message2 = "Its size is greater than 123 bytes."; throw new ArgumentOutOfRangeException("reason", message2); } } stop(code, reason); } public void Stop(CloseStatusCode code, string reason) { if (code == CloseStatusCode.MandatoryExtension) { throw new ArgumentException("MandatoryExtension cannot be used.", "code"); } if (!reason.IsNullOrEmpty()) { if (code == CloseStatusCode.NoStatus) { throw new ArgumentException("NoStatus cannot be used.", "code"); } if (!reason.TryGetUTF8EncodedBytes(out var bytes)) { throw new ArgumentException("It could not be UTF-8-encoded.", "reason"); } if (bytes.Length > 123) { string message = "Its size is greater than 123 bytes."; throw new ArgumentOutOfRangeException("reason", message); } } stop((ushort)code, reason); } } public abstract class WebSocketServiceHost { private Logger _log; private string _path; private WebSocketSessionManager _sessions; internal ServerState State => _sessions.State; protected Logger Log => _log; public bool KeepClean { get { return _sessions.KeepClean; } set { _sessions.KeepClean = value; } } public string Path => _path; public WebSocketSessionManager Sessions => _sessions; public abstract Type BehaviorType { get; } public TimeSpan WaitTime { get { return _sessions.WaitTime; } set { _sessions.WaitTime = value; } } protected WebSocketServiceHost(string path, Logger log) { _path = path; _log = log; _sessions = new WebSocketSessionManager(log); } internal void Start() { _sessions.Start(); } internal void StartSession(WebSocketContext context) { CreateSession().Start(context, _sessions); } internal void Stop(ushort code, string reason) { _sessions.Stop(code, reason); } protected abstract WebSocketBehavior CreateSession(); } internal class WebSocketServiceHost : WebSocketServiceHost where TBehavior : WebSocketBehavior { private Func _creator; public override Type BehaviorType => typeof(TBehavior); internal WebSocketServiceHost(string path, Func creator, Logger log) : this(path, creator, (Action)null, log) { } internal WebSocketServiceHost(string path, Func creator, Action initializer, Logger log) : base(path, log) { _creator = createCreator(creator, initializer); } private Func createCreator(Func creator, Action initializer) { if (initializer == null) { return creator; } return delegate { TBehavior val = creator(); initializer(val); return val; }; } protected override WebSocketBehavior CreateSession() { return _creator(); } } public class WebSocketServiceManager { private volatile bool _clean; private Dictionary _hosts; private Logger _log; private volatile ServerState _state; private object _sync; private TimeSpan _waitTime; public int Count { get { lock (_sync) { return _hosts.Count; } } } public IEnumerable Hosts { get { lock (_sync) { return _hosts.Values.ToList(); } } } public WebSocketServiceHost this[string path] { get { if (path == null) { throw new ArgumentNullException("path"); } if (path.Length == 0) { throw new ArgumentException("An empty string.", "path"); } if (path[0] != '/') { throw new ArgumentException("Not an absolute path.", "path"); } if (path.IndexOfAny(new char[2] { '?', '#' }) > -1) { throw new ArgumentException("It includes either or both query and fragment components.", "path"); } InternalTryGetServiceHost(path, out var host); return host; } } public bool KeepClean { get { return _clean; } set { if (!canSet(out var message)) { _log.Warn(message); return; } lock (_sync) { if (!canSet(out message)) { _log.Warn(message); return; } foreach (WebSocketServiceHost value2 in _hosts.Values) { value2.KeepClean = value; } _clean = value; } } } public IEnumerable Paths { get { lock (_sync) { return _hosts.Keys.ToList(); } } } [Obsolete("This property will be removed.")] public int SessionCount { get { int num = 0; foreach (WebSocketServiceHost host in Hosts) { if (_state != ServerState.Start) { break; } num += host.Sessions.Count; } return num; } } public TimeSpan WaitTime { get { return _waitTime; } set { if (value <= TimeSpan.Zero) { throw new ArgumentOutOfRangeException("value", "Zero or less."); } if (!canSet(out var message)) { _log.Warn(message); return; } lock (_sync) { if (!canSet(out message)) { _log.Warn(message); return; } foreach (WebSocketServiceHost value2 in _hosts.Values) { value2.WaitTime = value; } _waitTime = value; } } } internal WebSocketServiceManager(Logger log) { _log = log; _clean = true; _hosts = new Dictionary(); _state = ServerState.Ready; _sync = ((ICollection)_hosts).SyncRoot; _waitTime = TimeSpan.FromSeconds(1.0); } private void broadcast(Opcode opcode, byte[] data, Action completed) { Dictionary dictionary = new Dictionary(); try { foreach (WebSocketServiceHost host in Hosts) { if (_state != ServerState.Start) { _log.Error("The server is shutting down."); break; } host.Sessions.Broadcast(opcode, data, dictionary); } completed?.Invoke(); } catch (Exception ex) { _log.Error(ex.Message); _log.Debug(ex.ToString()); } finally { dictionary.Clear(); } } private void broadcast(Opcode opcode, Stream stream, Action completed) { Dictionary dictionary = new Dictionary(); try { foreach (WebSocketServiceHost host in Hosts) { if (_state != ServerState.Start) { _log.Error("The server is shutting down."); break; } host.Sessions.Broadcast(opcode, stream, dictionary); } completed?.Invoke(); } catch (Exception ex) { _log.Error(ex.Message); _log.Debug(ex.ToString()); } finally { foreach (Stream value in dictionary.Values) { value.Dispose(); } dictionary.Clear(); } } private void broadcastAsync(Opcode opcode, byte[] data, Action completed) { ThreadPool.QueueUserWorkItem(delegate { broadcast(opcode, data, completed); }); } private void broadcastAsync(Opcode opcode, Stream stream, Action completed) { ThreadPool.QueueUserWorkItem(delegate { broadcast(opcode, stream, completed); }); } private Dictionary> broadping(byte[] frameAsBytes, TimeSpan timeout) { Dictionary> dictionary = new Dictionary>(); foreach (WebSocketServiceHost host in Hosts) { if (_state != ServerState.Start) { _log.Error("The server is shutting down."); break; } Dictionary value = host.Sessions.Broadping(frameAsBytes, timeout); dictionary.Add(host.Path, value); } return dictionary; } private bool canSet(out string message) { message = null; if (_state == ServerState.Start) { message = "The server has already started."; return false; } if (_state == ServerState.ShuttingDown) { message = "The server is shutting down."; return false; } return true; } internal void Add(string path, Func creator) where TBehavior : WebSocketBehavior { path = HttpUtility.UrlDecode(path).TrimSlashFromEnd(); lock (_sync) { if (_hosts.TryGetValue(path, out var value)) { throw new ArgumentException("Already in use.", "path"); } value = new WebSocketServiceHost(path, creator, null, _log); if (!_clean) { value.KeepClean = false; } if (_waitTime != value.WaitTime) { value.WaitTime = _waitTime; } if (_state == ServerState.Start) { value.Start(); } _hosts.Add(path, value); } } internal bool InternalTryGetServiceHost(string path, out WebSocketServiceHost host) { path = HttpUtility.UrlDecode(path).TrimSlashFromEnd(); lock (_sync) { return _hosts.TryGetValue(path, out host); } } internal void Start() { lock (_sync) { foreach (WebSocketServiceHost value in _hosts.Values) { value.Start(); } _state = ServerState.Start; } } internal void Stop(ushort code, string reason) { lock (_sync) { _state = ServerState.ShuttingDown; foreach (WebSocketServiceHost value in _hosts.Values) { value.Stop(code, reason); } _state = ServerState.Stop; } } public void AddService(string path, Action initializer) where TBehavior : WebSocketBehavior, new() { if (path == null) { throw new ArgumentNullException("path"); } if (path.Length == 0) { throw new ArgumentException("An empty string.", "path"); } if (path[0] != '/') { throw new ArgumentException("Not an absolute path.", "path"); } if (path.IndexOfAny(new char[2] { '?', '#' }) > -1) { throw new ArgumentException("It includes either or both query and fragment components.", "path"); } path = HttpUtility.UrlDecode(path).TrimSlashFromEnd(); lock (_sync) { if (_hosts.TryGetValue(path, out var value)) { throw new ArgumentException("Already in use.", "path"); } value = new WebSocketServiceHost(path, () => new TBehavior(), initializer, _log); if (!_clean) { value.KeepClean = false; } if (_waitTime != value.WaitTime) { value.WaitTime = _waitTime; } if (_state == ServerState.Start) { value.Start(); } _hosts.Add(path, value); } } [Obsolete("This method will be removed.")] public void Broadcast(byte[] data) { if (_state != ServerState.Start) { throw new InvalidOperationException("The current state of the manager is not Start."); } if (data == null) { throw new ArgumentNullException("data"); } if (data.LongLength <= WebSocket.FragmentLength) { broadcast(Opcode.Binary, data, null); } else { broadcast(Opcode.Binary, new MemoryStream(data), null); } } [Obsolete("This method will be removed.")] public void Broadcast(string data) { if (_state != ServerState.Start) { throw new InvalidOperationException("The current state of the manager is not Start."); } if (data == null) { throw new ArgumentNullException("data"); } if (!data.TryGetUTF8EncodedBytes(out var bytes)) { throw new ArgumentException("It could not be UTF-8-encoded.", "data"); } if (bytes.LongLength <= WebSocket.FragmentLength) { broadcast(Opcode.Text, bytes, null); } else { broadcast(Opcode.Text, new MemoryStream(bytes), null); } } [Obsolete("This method will be removed.")] public void BroadcastAsync(byte[] data, Action completed) { if (_state != ServerState.Start) { throw new InvalidOperationException("The current state of the manager is not Start."); } if (data == null) { throw new ArgumentNullException("data"); } if (data.LongLength <= WebSocket.FragmentLength) { broadcastAsync(Opcode.Binary, data, completed); } else { broadcastAsync(Opcode.Binary, new MemoryStream(data), completed); } } [Obsolete("This method will be removed.")] public void BroadcastAsync(string data, Action completed) { if (_state != ServerState.Start) { throw new InvalidOperationException("The current state of the manager is not Start."); } if (data == null) { throw new ArgumentNullException("data"); } if (!data.TryGetUTF8EncodedBytes(out var bytes)) { throw new ArgumentException("It could not be UTF-8-encoded.", "data"); } if (bytes.LongLength <= WebSocket.FragmentLength) { broadcastAsync(Opcode.Text, bytes, completed); } else { broadcastAsync(Opcode.Text, new MemoryStream(bytes), completed); } } [Obsolete("This method will be removed.")] public void BroadcastAsync(Stream stream, int length, Action completed) { if (_state != ServerState.Start) { throw new InvalidOperationException("The current state of the manager is not Start."); } if (stream == null) { throw new ArgumentNullException("stream"); } if (!stream.CanRead) { throw new ArgumentException("It cannot be read.", "stream"); } if (length < 1) { throw new ArgumentException("Less than 1.", "length"); } byte[] array = stream.ReadBytes(length); int num = array.Length; if (num == 0) { throw new ArgumentException("No data could be read from it.", "stream"); } if (num < length) { _log.Warn($"Only {num} byte(s) of data could be read from the stream."); } if (num <= WebSocket.FragmentLength) { broadcastAsync(Opcode.Binary, array, completed); } else { broadcastAsync(Opcode.Binary, new MemoryStream(array), completed); } } [Obsolete("This method will be removed.")] public Dictionary> Broadping() { if (_state != ServerState.Start) { throw new InvalidOperationException("The current state of the manager is not Start."); } return broadping(WebSocketFrame.EmptyPingBytes, _waitTime); } [Obsolete("This method will be removed.")] public Dictionary> Broadping(string message) { if (_state != ServerState.Start) { throw new InvalidOperationException("The current state of the manager is not Start."); } if (message.IsNullOrEmpty()) { return broadping(WebSocketFrame.EmptyPingBytes, _waitTime); } if (!message.TryGetUTF8EncodedBytes(out var bytes)) { throw new ArgumentException("It could not be UTF-8-encoded.", "message"); } if (bytes.Length > 125) { string message2 = "Its size is greater than 125 bytes."; throw new ArgumentOutOfRangeException("message", message2); } WebSocketFrame webSocketFrame = WebSocketFrame.CreatePingFrame(bytes, mask: false); return broadping(webSocketFrame.ToArray(), _waitTime); } public void Clear() { List list = null; lock (_sync) { list = _hosts.Values.ToList(); _hosts.Clear(); } foreach (WebSocketServiceHost item in list) { if (item.State == ServerState.Start) { item.Stop(1001, string.Empty); } } } public bool RemoveService(string path) { if (path == null) { throw new ArgumentNullException("path"); } if (path.Length == 0) { throw new ArgumentException("An empty string.", "path"); } if (path[0] != '/') { throw new ArgumentException("Not an absolute path.", "path"); } if (path.IndexOfAny(new char[2] { '?', '#' }) > -1) { throw new ArgumentException("It includes either or both query and fragment components.", "path"); } path = HttpUtility.UrlDecode(path).TrimSlashFromEnd(); WebSocketServiceHost value; lock (_sync) { if (!_hosts.TryGetValue(path, out value)) { return false; } _hosts.Remove(path); } if (value.State == ServerState.Start) { value.Stop(1001, string.Empty); } return true; } public bool TryGetServiceHost(string path, out WebSocketServiceHost host) { if (path == null) { throw new ArgumentNullException("path"); } if (path.Length == 0) { throw new ArgumentException("An empty string.", "path"); } if (path[0] != '/') { throw new ArgumentException("Not an absolute path.", "path"); } if (path.IndexOfAny(new char[2] { '?', '#' }) > -1) { throw new ArgumentException("It includes either or both query and fragment components.", "path"); } return InternalTryGetServiceHost(path, out host); } } public class WebSocketSessionManager { private volatile bool _clean; private object _forSweep; private Logger _log; private Dictionary _sessions; private volatile ServerState _state; private volatile bool _sweeping; private System.Timers.Timer _sweepTimer; private object _sync; private TimeSpan _waitTime; internal ServerState State => _state; public IEnumerable ActiveIDs { get { foreach (KeyValuePair item in broadping(WebSocketFrame.EmptyPingBytes)) { if (item.Value) { yield return item.Key; } } } } public int Count { get { lock (_sync) { return _sessions.Count; } } } public IEnumerable IDs { get { if (_state != ServerState.Start) { return Enumerable.Empty(); } lock (_sync) { if (_state != ServerState.Start) { return Enumerable.Empty(); } return _sessions.Keys.ToList(); } } } public IEnumerable InactiveIDs { get { foreach (KeyValuePair item in broadping(WebSocketFrame.EmptyPingBytes)) { if (!item.Value) { yield return item.Key; } } } } public IWebSocketSession this[string id] { get { if (id == null) { throw new ArgumentNullException("id"); } if (id.Length == 0) { throw new ArgumentException("An empty string.", "id"); } tryGetSession(id, out var session); return session; } } public bool KeepClean { get { return _clean; } set { if (!canSet(out var message)) { _log.Warn(message); return; } lock (_sync) { if (!canSet(out message)) { _log.Warn(message); } else { _clean = value; } } } } public IEnumerable Sessions { get { if (_state != ServerState.Start) { return Enumerable.Empty(); } lock (_sync) { if (_state != ServerState.Start) { return Enumerable.Empty(); } return _sessions.Values.ToList(); } } } public TimeSpan WaitTime { get { return _waitTime; } set { if (value <= TimeSpan.Zero) { throw new ArgumentOutOfRangeException("value", "Zero or less."); } if (!canSet(out var message)) { _log.Warn(message); return; } lock (_sync) { if (!canSet(out message)) { _log.Warn(message); } else { _waitTime = value; } } } } internal WebSocketSessionManager(Logger log) { _log = log; _clean = true; _forSweep = new object(); _sessions = new Dictionary(); _state = ServerState.Ready; _sync = ((ICollection)_sessions).SyncRoot; _waitTime = TimeSpan.FromSeconds(1.0); setSweepTimer(60000.0); } private void broadcast(Opcode opcode, byte[] data, Action completed) { Dictionary dictionary = new Dictionary(); try { foreach (IWebSocketSession session in Sessions) { if (_state != ServerState.Start) { _log.Error("The service is shutting down."); break; } session.Context.WebSocket.Send(opcode, data, dictionary); } completed?.Invoke(); } catch (Exception ex) { _log.Error(ex.Message); _log.Debug(ex.ToString()); } finally { dictionary.Clear(); } } private void broadcast(Opcode opcode, Stream stream, Action completed) { Dictionary dictionary = new Dictionary(); try { foreach (IWebSocketSession session in Sessions) { if (_state != ServerState.Start) { _log.Error("The service is shutting down."); break; } session.Context.WebSocket.Send(opcode, stream, dictionary); } completed?.Invoke(); } catch (Exception ex) { _log.Error(ex.Message); _log.Debug(ex.ToString()); } finally { foreach (Stream value in dictionary.Values) { value.Dispose(); } dictionary.Clear(); } } private void broadcastAsync(Opcode opcode, byte[] data, Action completed) { ThreadPool.QueueUserWorkItem(delegate { broadcast(opcode, data, completed); }); } private void broadcastAsync(Opcode opcode, Stream stream, Action completed) { ThreadPool.QueueUserWorkItem(delegate { broadcast(opcode, stream, completed); }); } private Dictionary broadping(byte[] frameAsBytes) { Dictionary dictionary = new Dictionary(); foreach (IWebSocketSession session in Sessions) { if (_state != ServerState.Start) { _log.Error("The service is shutting down."); break; } bool value = session.Context.WebSocket.Ping(frameAsBytes, _waitTime); dictionary.Add(session.ID, value); } return dictionary; } private bool canSet(out string message) { message = null; if (_state == ServerState.Start) { message = "The service has already started."; return false; } if (_state == ServerState.ShuttingDown) { message = "The service is shutting down."; return false; } return true; } private static string createID() { return Guid.NewGuid().ToString("N"); } private void setSweepTimer(double interval) { _sweepTimer = new System.Timers.Timer(interval); _sweepTimer.Elapsed += delegate { Sweep(); }; } private void stop(PayloadData payloadData, bool send) { byte[] frameAsBytes = (send ? WebSocketFrame.CreateCloseFrame(payloadData, mask: false).ToArray() : null); lock (_sync) { _state = ServerState.ShuttingDown; _sweepTimer.Enabled = false; foreach (IWebSocketSession item in _sessions.Values.ToList()) { item.Context.WebSocket.Close(payloadData, frameAsBytes); } _state = ServerState.Stop; } } private bool tryGetSession(string id, out IWebSocketSession session) { session = null; if (_state != ServerState.Start) { return false; } lock (_sync) { if (_state != ServerState.Start) { return false; } return _sessions.TryGetValue(id, out session); } } internal string Add(IWebSocketSession session) { lock (_sync) { if (_state != ServerState.Start) { return null; } string text = createID(); _sessions.Add(text, session); return text; } } internal void Broadcast(Opcode opcode, byte[] data, Dictionary cache) { foreach (IWebSocketSession session in Sessions) { if (_state != ServerState.Start) { _log.Error("The service is shutting down."); break; } session.Context.WebSocket.Send(opcode, data, cache); } } internal void Broadcast(Opcode opcode, Stream stream, Dictionary cache) { foreach (IWebSocketSession session in Sessions) { if (_state != ServerState.Start) { _log.Error("The service is shutting down."); break; } session.Context.WebSocket.Send(opcode, stream, cache); } } internal Dictionary Broadping(byte[] frameAsBytes, TimeSpan timeout) { Dictionary dictionary = new Dictionary(); foreach (IWebSocketSession session in Sessions) { if (_state != ServerState.Start) { _log.Error("The service is shutting down."); break; } bool value = session.Context.WebSocket.Ping(frameAsBytes, timeout); dictionary.Add(session.ID, value); } return dictionary; } internal bool Remove(string id) { lock (_sync) { return _sessions.Remove(id); } } internal void Start() { lock (_sync) { _sweepTimer.Enabled = _clean; _state = ServerState.Start; } } internal void Stop(ushort code, string reason) { if (code == 1005) { stop(PayloadData.Empty, send: true); } else { stop(new PayloadData(code, reason), !code.IsReserved()); } } public void Broadcast(byte[] data) { if (_state != ServerState.Start) { throw new InvalidOperationException("The current state of the manager is not Start."); } if (data == null) { throw new ArgumentNullException("data"); } if (data.LongLength <= WebSocket.FragmentLength) { broadcast(Opcode.Binary, data, null); } else { broadcast(Opcode.Binary, new MemoryStream(data), null); } } public void Broadcast(string data) { if (_state != ServerState.Start) { throw new InvalidOperationException("The current state of the manager is not Start."); } if (data == null) { throw new ArgumentNullException("data"); } if (!data.TryGetUTF8EncodedBytes(out var bytes)) { throw new ArgumentException("It could not be UTF-8-encoded.", "data"); } if (bytes.LongLength <= WebSocket.FragmentLength) { broadcast(Opcode.Text, bytes, null); } else { broadcast(Opcode.Text, new MemoryStream(bytes), null); } } public void Broadcast(Stream stream, int length) { if (_state != ServerState.Start) { throw new InvalidOperationException("The current state of the manager is not Start."); } if (stream == null) { throw new ArgumentNullException("stream"); } if (!stream.CanRead) { throw new ArgumentException("It cannot be read.", "stream"); } if (length < 1) { throw new ArgumentException("Less than 1.", "length"); } byte[] array = stream.ReadBytes(length); int num = array.Length; if (num == 0) { throw new ArgumentException("No data could be read from it.", "stream"); } if (num < length) { _log.Warn($"Only {num} byte(s) of data could be read from the stream."); } if (num <= WebSocket.FragmentLength) { broadcast(Opcode.Binary, array, null); } else { broadcast(Opcode.Binary, new MemoryStream(array), null); } } public void BroadcastAsync(byte[] data, Action completed) { if (_state != ServerState.Start) { throw new InvalidOperationException("The current state of the manager is not Start."); } if (data == null) { throw new ArgumentNullException("data"); } if (data.LongLength <= WebSocket.FragmentLength) { broadcastAsync(Opcode.Binary, data, completed); } else { broadcastAsync(Opcode.Binary, new MemoryStream(data), completed); } } public void BroadcastAsync(string data, Action completed) { if (_state != ServerState.Start) { throw new InvalidOperationException("The current state of the manager is not Start."); } if (data == null) { throw new ArgumentNullException("data"); } if (!data.TryGetUTF8EncodedBytes(out var bytes)) { throw new ArgumentException("It could not be UTF-8-encoded.", "data"); } if (bytes.LongLength <= WebSocket.FragmentLength) { broadcastAsync(Opcode.Text, bytes, completed); } else { broadcastAsync(Opcode.Text, new MemoryStream(bytes), completed); } } public void BroadcastAsync(Stream stream, int length, Action completed) { if (_state != ServerState.Start) { throw new InvalidOperationException("The current state of the manager is not Start."); } if (stream == null) { throw new ArgumentNullException("stream"); } if (!stream.CanRead) { throw new ArgumentException("It cannot be read.", "stream"); } if (length < 1) { throw new ArgumentException("Less than 1.", "length"); } byte[] array = stream.ReadBytes(length); int num = array.Length; if (num == 0) { throw new ArgumentException("No data could be read from it.", "stream"); } if (num < length) { _log.Warn($"Only {num} byte(s) of data could be read from the stream."); } if (num <= WebSocket.FragmentLength) { broadcastAsync(Opcode.Binary, array, completed); } else { broadcastAsync(Opcode.Binary, new MemoryStream(array), completed); } } [Obsolete("This method will be removed.")] public Dictionary Broadping() { if (_state != ServerState.Start) { throw new InvalidOperationException("The current state of the manager is not Start."); } return Broadping(WebSocketFrame.EmptyPingBytes, _waitTime); } [Obsolete("This method will be removed.")] public Dictionary Broadping(string message) { if (_state != ServerState.Start) { throw new InvalidOperationException("The current state of the manager is not Start."); } if (message.IsNullOrEmpty()) { return Broadping(WebSocketFrame.EmptyPingBytes, _waitTime); } if (!message.TryGetUTF8EncodedBytes(out var bytes)) { throw new ArgumentException("It could not be UTF-8-encoded.", "message"); } if (bytes.Length > 125) { string message2 = "Its size is greater than 125 bytes."; throw new ArgumentOutOfRangeException("message", message2); } WebSocketFrame webSocketFrame = WebSocketFrame.CreatePingFrame(bytes, mask: false); return Broadping(webSocketFrame.ToArray(), _waitTime); } public void CloseSession(string id) { if (!TryGetSession(id, out var session)) { throw new InvalidOperationException("The session could not be found."); } session.Context.WebSocket.Close(); } public void CloseSession(string id, ushort code, string reason) { if (!TryGetSession(id, out var session)) { throw new InvalidOperationException("The session could not be found."); } session.Context.WebSocket.Close(code, reason); } public void CloseSession(string id, CloseStatusCode code, string reason) { if (!TryGetSession(id, out var session)) { throw new InvalidOperationException("The session could not be found."); } session.Context.WebSocket.Close(code, reason); } public bool PingTo(string id) { if (!TryGetSession(id, out var session)) { throw new InvalidOperationException("The session could not be found."); } return session.Context.WebSocket.Ping(); } public bool PingTo(string message, string id) { if (!TryGetSession(id, out var session)) { throw new InvalidOperationException("The session could not be found."); } return session.Context.WebSocket.Ping(message); } public void SendTo(byte[] data, string id) { if (!TryGetSession(id, out var session)) { throw new InvalidOperationException("The session could not be found."); } session.Context.WebSocket.Send(data); } public void SendTo(string data, string id) { if (!TryGetSession(id, out var session)) { throw new InvalidOperationException("The session could not be found."); } session.Context.WebSocket.Send(data); } public void SendTo(Stream stream, int length, string id) { if (!TryGetSession(id, out var session)) { throw new InvalidOperationException("The session could not be found."); } session.Context.WebSocket.Send(stream, length); } public void SendToAsync(byte[] data, string id, Action completed) { if (!TryGetSession(id, out var session)) { throw new InvalidOperationException("The session could not be found."); } session.Context.WebSocket.SendAsync(data, completed); } public void SendToAsync(string data, string id, Action completed) { if (!TryGetSession(id, out var session)) { throw new InvalidOperationException("The session could not be found."); } session.Context.WebSocket.SendAsync(data, completed); } public void SendToAsync(Stream stream, int length, string id, Action completed) { if (!TryGetSession(id, out var session)) { throw new InvalidOperationException("The session could not be found."); } session.Context.WebSocket.SendAsync(stream, length, completed); } public void Sweep() { if (_sweeping) { _log.Info("The sweeping is already in progress."); return; } lock (_forSweep) { if (_sweeping) { _log.Info("The sweeping is already in progress."); return; } _sweeping = true; } foreach (string inactiveID in InactiveIDs) { if (_state != ServerState.Start) { break; } lock (_sync) { if (_state != ServerState.Start) { break; } if (_sessions.TryGetValue(inactiveID, out var value)) { switch (value.State) { case WebSocketState.Open: value.Context.WebSocket.Close(CloseStatusCode.Abnormal); break; default: _sessions.Remove(inactiveID); break; case WebSocketState.Closing: break; } } continue; } } _sweeping = false; } public bool TryGetSession(string id, out IWebSocketSession session) { if (id == null) { throw new ArgumentNullException("id"); } if (id.Length == 0) { throw new ArgumentException("An empty string.", "id"); } return tryGetSession(id, out session); } } } namespace WebSocketSharp.Net { internal abstract class AuthenticationBase { private AuthenticationSchemes _scheme; internal NameValueCollection Parameters; public string Algorithm => Parameters["algorithm"]; public string Nonce => Parameters["nonce"]; public string Opaque => Parameters["opaque"]; public string Qop => Parameters["qop"]; public string Realm => Parameters["realm"]; public AuthenticationSchemes Scheme => _scheme; protected AuthenticationBase(AuthenticationSchemes scheme, NameValueCollection parameters) { _scheme = scheme; Parameters = parameters; } internal static string CreateNonceValue() { byte[] array = new byte[16]; new Random().NextBytes(array); StringBuilder stringBuilder = new StringBuilder(32); byte[] array2 = array; foreach (byte b in array2) { stringBuilder.Append(b.ToString("x2")); } return stringBuilder.ToString(); } internal static NameValueCollection ParseParameters(string value) { NameValueCollection nameValueCollection = new NameValueCollection(); foreach (string item in value.SplitHeaderValue(',')) { int num = item.IndexOf('='); string name = ((num > 0) ? item.Substring(0, num).Trim() : null); string value2 = ((num < 0) ? item.Trim().Trim(new char[1] { '"' }) : ((num < item.Length - 1) ? item.Substring(num + 1).Trim().Trim(new char[1] { '"' }) : string.Empty)); nameValueCollection.Add(name, value2); } return nameValueCollection; } internal abstract string ToBasicString(); internal abstract string ToDigestString(); public override string ToString() { if (_scheme != AuthenticationSchemes.Basic) { if (_scheme != AuthenticationSchemes.Digest) { return string.Empty; } return ToDigestString(); } return ToBasicString(); } } internal class AuthenticationChallenge : AuthenticationBase { public string Domain => Parameters["domain"]; public string Stale => Parameters["stale"]; private AuthenticationChallenge(AuthenticationSchemes scheme, NameValueCollection parameters) : base(scheme, parameters) { } internal AuthenticationChallenge(AuthenticationSchemes scheme, string realm) : base(scheme, new NameValueCollection()) { Parameters["realm"] = realm; if (scheme == AuthenticationSchemes.Digest) { Parameters["nonce"] = AuthenticationBase.CreateNonceValue(); Parameters["algorithm"] = "MD5"; Parameters["qop"] = "auth"; } } internal static AuthenticationChallenge CreateBasicChallenge(string realm) { return new AuthenticationChallenge(AuthenticationSchemes.Basic, realm); } internal static AuthenticationChallenge CreateDigestChallenge(string realm) { return new AuthenticationChallenge(AuthenticationSchemes.Digest, realm); } internal static AuthenticationChallenge Parse(string value) { string[] array = value.Split(new char[1] { ' ' }, 2); if (array.Length != 2) { return null; } string text = array[0].ToLower(); if (!(text == "basic")) { if (!(text == "digest")) { return null; } return new AuthenticationChallenge(AuthenticationSchemes.Digest, AuthenticationBase.ParseParameters(array[1])); } return new AuthenticationChallenge(AuthenticationSchemes.Basic, AuthenticationBase.ParseParameters(array[1])); } internal override string ToBasicString() { return string.Format("Basic realm=\"{0}\"", Parameters["realm"]); } internal override string ToDigestString() { StringBuilder stringBuilder = new StringBuilder(128); string text = Parameters["domain"]; if (text != null) { stringBuilder.AppendFormat("Digest realm=\"{0}\", domain=\"{1}\", nonce=\"{2}\"", Parameters["realm"], text, Parameters["nonce"]); } else { stringBuilder.AppendFormat("Digest realm=\"{0}\", nonce=\"{1}\"", Parameters["realm"], Parameters["nonce"]); } string text2 = Parameters["opaque"]; if (text2 != null) { stringBuilder.AppendFormat(", opaque=\"{0}\"", text2); } string text3 = Parameters["stale"]; if (text3 != null) { stringBuilder.AppendFormat(", stale={0}", text3); } string text4 = Parameters["algorithm"]; if (text4 != null) { stringBuilder.AppendFormat(", algorithm={0}", text4); } string text5 = Parameters["qop"]; if (text5 != null) { stringBuilder.AppendFormat(", qop=\"{0}\"", text5); } return stringBuilder.ToString(); } } internal class AuthenticationResponse : AuthenticationBase { private uint _nonceCount; internal uint NonceCount { get { if (_nonceCount >= uint.MaxValue) { return 0u; } return _nonceCount; } } public string Cnonce => Parameters["cnonce"]; public string Nc => Parameters["nc"]; public string Password => Parameters["password"]; public string Response => Parameters["response"]; public string Uri => Parameters["uri"]; public string UserName => Parameters["username"]; private AuthenticationResponse(AuthenticationSchemes scheme, NameValueCollection parameters) : base(scheme, parameters) { } internal AuthenticationResponse(NetworkCredential credentials) : this(AuthenticationSchemes.Basic, new NameValueCollection(), credentials, 0u) { } internal AuthenticationResponse(AuthenticationChallenge challenge, NetworkCredential credentials, uint nonceCount) : this(challenge.Scheme, challenge.Parameters, credentials, nonceCount) { } internal AuthenticationResponse(AuthenticationSchemes scheme, NameValueCollection parameters, NetworkCredential credentials, uint nonceCount) : base(scheme, parameters) { Parameters["username"] = credentials.Username; Parameters["password"] = credentials.Password; Parameters["uri"] = credentials.Domain; _nonceCount = nonceCount; if (scheme == AuthenticationSchemes.Digest) { initAsDigest(); } } private static string createA1(string username, string password, string realm) { return $"{username}:{realm}:{password}"; } private static string createA1(string username, string password, string realm, string nonce, string cnonce) { return $"{hash(createA1(username, password, realm))}:{nonce}:{cnonce}"; } private static string createA2(string method, string uri) { return $"{method}:{uri}"; } private static string createA2(string method, string uri, string entity) { return $"{method}:{uri}:{hash(entity)}"; } private static string hash(string value) { byte[] bytes = Encoding.UTF8.GetBytes(value); byte[] array = MD5.Create().ComputeHash(bytes); StringBuilder stringBuilder = new StringBuilder(64); byte[] array2 = array; foreach (byte b in array2) { stringBuilder.Append(b.ToString("x2")); } return stringBuilder.ToString(); } private void initAsDigest() { string text = Parameters["qop"]; if (text != null) { if (text.Split(new char[1] { ',' }).Contains((string qop) => qop.Trim().ToLower() == "auth")) { Parameters["qop"] = "auth"; Parameters["cnonce"] = AuthenticationBase.CreateNonceValue(); Parameters["nc"] = $"{++_nonceCount:x8}"; } else { Parameters["qop"] = null; } } Parameters["method"] = "GET"; Parameters["response"] = CreateRequestDigest(Parameters); } internal static string CreateRequestDigest(NameValueCollection parameters) { string username = parameters["username"]; string password = parameters["password"]; string realm = parameters["realm"]; string text = parameters["nonce"]; string uri = parameters["uri"]; string text2 = parameters["algorithm"]; string text3 = parameters["qop"]; string text4 = parameters["cnonce"]; string text5 = parameters["nc"]; string method = parameters["method"]; string value = ((text2 != null && text2.ToLower() == "md5-sess") ? createA1(username, password, realm, text, text4) : createA1(username, password, realm)); string value2 = ((text3 != null && text3.ToLower() == "auth-int") ? createA2(method, uri, parameters["entity"]) : createA2(method, uri)); string arg = hash(value); string arg2 = ((text3 != null) ? $"{text}:{text5}:{text4}:{text3}:{hash(value2)}" : $"{text}:{hash(value2)}"); return hash($"{arg}:{arg2}"); } internal static AuthenticationResponse Parse(string value) { try { string[] array = value.Split(new char[1] { ' ' }, 2); if (array.Length != 2) { return null; } string text = array[0].ToLower(); return (text == "basic") ? new AuthenticationResponse(AuthenticationSchemes.Basic, ParseBasicCredentials(array[1])) : ((text == "digest") ? new AuthenticationResponse(AuthenticationSchemes.Digest, AuthenticationBase.ParseParameters(array[1])) : null); } catch { } return null; } internal static NameValueCollection ParseBasicCredentials(string value) { string @string = Encoding.Default.GetString(Convert.FromBase64String(value)); int num = @string.IndexOf(':'); string text = @string.Substring(0, num); string value2 = ((num < @string.Length - 1) ? @string.Substring(num + 1) : string.Empty); num = text.IndexOf('\\'); if (num > -1) { text = text.Substring(num + 1); } return new NameValueCollection { ["username"] = text, ["password"] = value2 }; } internal override string ToBasicString() { string s = string.Format("{0}:{1}", Parameters["username"], Parameters["password"]); string text = Convert.ToBase64String(Encoding.UTF8.GetBytes(s)); return "Basic " + text; } internal override string ToDigestString() { StringBuilder stringBuilder = new StringBuilder(256); stringBuilder.AppendFormat("Digest username=\"{0}\", realm=\"{1}\", nonce=\"{2}\", uri=\"{3}\", response=\"{4}\"", Parameters["username"], Parameters["realm"], Parameters["nonce"], Parameters["uri"], Parameters["response"]); string text = Parameters["opaque"]; if (text != null) { stringBuilder.AppendFormat(", opaque=\"{0}\"", text); } string text2 = Parameters["algorithm"]; if (text2 != null) { stringBuilder.AppendFormat(", algorithm={0}", text2); } string text3 = Parameters["qop"]; if (text3 != null) { stringBuilder.AppendFormat(", qop={0}, cnonce=\"{1}\", nc={2}", text3, Parameters["cnonce"], Parameters["nc"]); } return stringBuilder.ToString(); } public IIdentity ToIdentity() { AuthenticationSchemes scheme = base.Scheme; if (scheme != AuthenticationSchemes.Basic) { return (scheme == AuthenticationSchemes.Digest) ? new HttpDigestIdentity(Parameters) : null; } return new HttpBasicIdentity(Parameters["username"], Parameters["password"]); } } public enum AuthenticationSchemes { None = 0, Digest = 1, Basic = 8, Anonymous = 0x8000 } internal class Chunk { private byte[] _data; private int _offset; public int ReadLeft => _data.Length - _offset; public Chunk(byte[] data) { _data = data; } public int Read(byte[] buffer, int offset, int count) { int num = _data.Length - _offset; if (num == 0) { return num; } if (count > num) { count = num; } Buffer.BlockCopy(_data, _offset, buffer, offset, count); _offset += count; return count; } } internal class ChunkedRequestStream : RequestStream { private const int _bufferLength = 8192; private HttpListenerContext _context; private ChunkStream _decoder; private bool _disposed; private bool _noMoreData; internal ChunkStream Decoder { get { return _decoder; } set { _decoder = value; } } internal ChunkedRequestStream(Stream stream, byte[] buffer, int offset, int count, HttpListenerContext context) : base(stream, buffer, offset, count) { _context = context; _decoder = new ChunkStream((WebHeaderCollection)context.Request.Headers); } private void onRead(IAsyncResult asyncResult) { ReadBufferState readBufferState = (ReadBufferState)asyncResult.AsyncState; HttpStreamAsyncResult asyncResult2 = readBufferState.AsyncResult; try { int count = base.EndRead(asyncResult); _decoder.Write(asyncResult2.Buffer, asyncResult2.Offset, count); count = _decoder.Read(readBufferState.Buffer, readBufferState.Offset, readBufferState.Count); readBufferState.Offset += count; readBufferState.Count -= count; if (readBufferState.Count == 0 || !_decoder.WantMore || count == 0) { _noMoreData = !_decoder.WantMore && count == 0; asyncResult2.Count = readBufferState.InitialCount - readBufferState.Count; asyncResult2.Complete(); } else { asyncResult2.Offset = 0; asyncResult2.Count = Math.Min(8192, _decoder.ChunkLeft + 6); base.BeginRead(asyncResult2.Buffer, asyncResult2.Offset, asyncResult2.Count, (AsyncCallback)onRead, (object)readBufferState); } } catch (Exception ex) { _context.Connection.SendError(ex.Message, 400); asyncResult2.Complete(ex); } } public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { if (_disposed) { throw new ObjectDisposedException(GetType().ToString()); } if (buffer == null) { throw new ArgumentNullException("buffer"); } if (offset < 0) { throw new ArgumentOutOfRangeException("offset", "A negative value."); } if (count < 0) { throw new ArgumentOutOfRangeException("count", "A negative value."); } int num = buffer.Length; if (offset + count > num) { throw new ArgumentException("The sum of 'offset' and 'count' is greater than 'buffer' length."); } HttpStreamAsyncResult httpStreamAsyncResult = new HttpStreamAsyncResult(callback, state); if (_noMoreData) { httpStreamAsyncResult.Complete(); return httpStreamAsyncResult; } int num2 = _decoder.Read(buffer, offset, count); offset += num2; count -= num2; if (count == 0) { httpStreamAsyncResult.Count = num2; httpStreamAsyncResult.Complete(); return httpStreamAsyncResult; } if (!_decoder.WantMore) { _noMoreData = num2 == 0; httpStreamAsyncResult.Count = num2; httpStreamAsyncResult.Complete(); return httpStreamAsyncResult; } httpStreamAsyncResult.Buffer = new byte[8192]; httpStreamAsyncResult.Offset = 0; httpStreamAsyncResult.Count = 8192; ReadBufferState readBufferState = new ReadBufferState(buffer, offset, count, httpStreamAsyncResult); readBufferState.InitialCount += num2; base.BeginRead(httpStreamAsyncResult.Buffer, httpStreamAsyncResult.Offset, httpStreamAsyncResult.Count, (AsyncCallback)onRead, (object)readBufferState); return httpStreamAsyncResult; } public override void Close() { if (!_disposed) { _disposed = true; base.Close(); } } public override int EndRead(IAsyncResult asyncResult) { if (_disposed) { throw new ObjectDisposedException(GetType().ToString()); } if (asyncResult == null) { throw new ArgumentNullException("asyncResult"); } if (!(asyncResult is HttpStreamAsyncResult httpStreamAsyncResult)) { throw new ArgumentException("A wrong IAsyncResult.", "asyncResult"); } if (!httpStreamAsyncResult.IsCompleted) { httpStreamAsyncResult.AsyncWaitHandle.WaitOne(); } if (httpStreamAsyncResult.HasException) { throw new HttpListenerException(400, "I/O operation aborted."); } return httpStreamAsyncResult.Count; } public override int Read(byte[] buffer, int offset, int count) { IAsyncResult asyncResult = BeginRead(buffer, offset, count, null, null); return EndRead(asyncResult); } } internal class ChunkStream { private int _chunkRead; private int _chunkSize; private List _chunks; private bool _gotIt; private WebHeaderCollection _headers; private StringBuilder _saved; private bool _sawCr; private InputChunkState _state; private int _trailerState; internal WebHeaderCollection Headers => _headers; public int ChunkLeft => _chunkSize - _chunkRead; public bool WantMore => _state != InputChunkState.End; public ChunkStream(WebHeaderCollection headers) { _headers = headers; _chunkSize = -1; _chunks = new List(); _saved = new StringBuilder(); } public ChunkStream(byte[] buffer, int offset, int count, WebHeaderCollection headers) : this(headers) { Write(buffer, offset, count); } private int read(byte[] buffer, int offset, int count) { int num = 0; int count2 = _chunks.Count; for (int i = 0; i < count2; i++) { Chunk chunk = _chunks[i]; if (chunk == null) { continue; } if (chunk.ReadLeft == 0) { _chunks[i] = null; continue; } num += chunk.Read(buffer, offset + num, count - num); if (num == count) { break; } } return num; } private static string removeChunkExtension(string value) { int num = value.IndexOf(';'); if (num <= -1) { return value; } return value.Substring(0, num); } private InputChunkState seekCrLf(byte[] buffer, ref int offset, int length) { if (!_sawCr) { if (buffer[offset++] != 13) { throwProtocolViolation("CR is expected."); } _sawCr = true; if (offset == length) { return InputChunkState.DataEnded; } } if (buffer[offset++] != 10) { throwProtocolViolation("LF is expected."); } return InputChunkState.None; } private InputChunkState setChunkSize(byte[] buffer, ref int offset, int length) { byte b = 0; while (offset < length) { b = buffer[offset++]; if (_sawCr) { if (b != 10) { throwProtocolViolation("LF is expected."); } break; } switch (b) { case 13: _sawCr = true; continue; case 10: throwProtocolViolation("LF is unexpected."); break; } if (b == 32) { _gotIt = true; } if (!_gotIt) { _saved.Append((char)b); } if (_saved.Length > 20) { throwProtocolViolation("The chunk size is too long."); } } if (!_sawCr || b != 10) { return InputChunkState.None; } _chunkRead = 0; try { _chunkSize = int.Parse(removeChunkExtension(_saved.ToString()), NumberStyles.HexNumber); } catch { throwProtocolViolation("The chunk size cannot be parsed."); } if (_chunkSize == 0) { _trailerState = 2; return InputChunkState.Trailer; } return InputChunkState.Data; } private InputChunkState setTrailer(byte[] buffer, ref int offset, int length) { if (_trailerState == 2 && buffer[offset] == 13 && _saved.Length == 0) { offset++; if (offset < length && buffer[offset] == 10) { offset++; return InputChunkState.End; } offset--; } while (offset < length && _trailerState < 4) { byte b = buffer[offset++]; _saved.Append((char)b); if (_saved.Length > 4196) { throwProtocolViolation("The trailer is too long."); } if (_trailerState == 1 || _trailerState == 3) { if (b != 10) { throwProtocolViolation("LF is expected."); } _trailerState++; continue; } switch (b) { case 13: _trailerState++; continue; case 10: throwProtocolViolation("LF is unexpected."); break; } _trailerState = 0; } if (_trailerState < 4) { return InputChunkState.Trailer; } _saved.Length -= 2; StringReader stringReader = new StringReader(_saved.ToString()); string text; while ((text = stringReader.ReadLine()) != null && text.Length > 0) { _headers.Add(text); } return InputChunkState.End; } private static void throwProtocolViolation(string message) { throw new WebException(message, null, WebExceptionStatus.ServerProtocolViolation, null); } private void write(byte[] buffer, ref int offset, int length) { if (_state == InputChunkState.End) { throwProtocolViolation("The chunks were ended."); } if (_state == InputChunkState.None) { _state = setChunkSize(buffer, ref offset, length); if (_state == InputChunkState.None) { return; } _saved.Length = 0; _sawCr = false; _gotIt = false; } if (_state == InputChunkState.Data && offset < length) { _state = writeData(buffer, ref offset, length); if (_state == InputChunkState.Data) { return; } } if (_state == InputChunkState.DataEnded && offset < length) { _state = seekCrLf(buffer, ref offset, length); if (_state == InputChunkState.DataEnded) { return; } _sawCr = false; } if (_state == InputChunkState.Trailer && offset < length) { _state = setTrailer(buffer, ref offset, length); if (_state == InputChunkState.Trailer) { return; } _saved.Length = 0; } if (offset < length) { write(buffer, ref offset, length); } } private InputChunkState writeData(byte[] buffer, ref int offset, int length) { int num = length - offset; int num2 = _chunkSize - _chunkRead; if (num > num2) { num = num2; } byte[] array = new byte[num]; Buffer.BlockCopy(buffer, offset, array, 0, num); _chunks.Add(new Chunk(array)); offset += num; _chunkRead += num; if (_chunkRead != _chunkSize) { return InputChunkState.Data; } return InputChunkState.DataEnded; } internal void ResetBuffer() { _chunkRead = 0; _chunkSize = -1; _chunks.Clear(); } internal int WriteAndReadBack(byte[] buffer, int offset, int writeCount, int readCount) { Write(buffer, offset, writeCount); return Read(buffer, offset, readCount); } public int Read(byte[] buffer, int offset, int count) { if (count <= 0) { return 0; } return read(buffer, offset, count); } public void Write(byte[] buffer, int offset, int count) { if (count > 0) { write(buffer, ref offset, offset + count); } } } public class ClientSslConfiguration { private bool _checkCertRevocation; private LocalCertificateSelectionCallback _clientCertSelectionCallback; private X509CertificateCollection _clientCerts; private SslProtocols _enabledSslProtocols; private RemoteCertificateValidationCallback _serverCertValidationCallback; private string _targetHost; public bool CheckCertificateRevocation { get { return _checkCertRevocation; } set { _checkCertRevocation = value; } } public X509CertificateCollection ClientCertificates { get { return _clientCerts; } set { _clientCerts = value; } } public LocalCertificateSelectionCallback ClientCertificateSelectionCallback { get { if (_clientCertSelectionCallback == null) { _clientCertSelectionCallback = defaultSelectClientCertificate; } return _clientCertSelectionCallback; } set { _clientCertSelectionCallback = value; } } public SslProtocols EnabledSslProtocols { get { return _enabledSslProtocols; } set { _enabledSslProtocols = value; } } public RemoteCertificateValidationCallback ServerCertificateValidationCallback { get { if (_serverCertValidationCallback == null) { _serverCertValidationCallback = defaultValidateServerCertificate; } return _serverCertValidationCallback; } set { _serverCertValidationCallback = value; } } public string TargetHost { get { return _targetHost; } set { _targetHost = value; } } public ClientSslConfiguration() { _enabledSslProtocols = SslProtocols.Default; } public ClientSslConfiguration(string targetHost) { _targetHost = targetHost; _enabledSslProtocols = SslProtocols.Default; } public ClientSslConfiguration(ClientSslConfiguration configuration) { if (configuration == null) { throw new ArgumentNullException("configuration"); } _checkCertRevocation = configuration._checkCertRevocation; _clientCertSelectionCallback = configuration._clientCertSelectionCallback; _clientCerts = configuration._clientCerts; _enabledSslProtocols = configuration._enabledSslProtocols; _serverCertValidationCallback = configuration._serverCertValidationCallback; _targetHost = configuration._targetHost; } private static X509Certificate defaultSelectClientCertificate(object sender, string targetHost, X509CertificateCollection clientCertificates, X509Certificate serverCertificate, string[] acceptableIssuers) { return null; } private static bool defaultValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return true; } } [Serializable] public sealed class Cookie { private string _comment; private Uri _commentUri; private bool _discard; private string _domain; private DateTime _expires; private bool _httpOnly; private string _name; private string _path; private string _port; private int[] _ports; private static readonly char[] _reservedCharsForName; private static readonly char[] _reservedCharsForValue; private bool _secure; private DateTime _timestamp; private string _value; private int _version; internal bool ExactDomain { get; set; } internal int MaxAge { get { if (_expires == DateTime.MinValue) { return 0; } TimeSpan timeSpan = ((_expires.Kind != DateTimeKind.Local) ? _expires.ToLocalTime() : _expires) - DateTime.Now; if (!(timeSpan > TimeSpan.Zero)) { return 0; } return (int)timeSpan.TotalSeconds; } } internal int[] Ports => _ports; public string Comment { get { return _comment; } set { _comment = value ?? string.Empty; } } public Uri CommentUri { get { return _commentUri; } set { _commentUri = value; } } public bool Discard { get { return _discard; } set { _discard = value; } } public string Domain { get { return _domain; } set { if (value.IsNullOrEmpty()) { _domain = string.Empty; ExactDomain = true; } else { _domain = value; ExactDomain = value[0] != '.'; } } } public bool Expired { get { if (_expires != DateTime.MinValue) { return _expires <= DateTime.Now; } return false; } set { _expires = (value ? DateTime.Now : DateTime.MinValue); } } public DateTime Expires { get { return _expires; } set { _expires = value; } } public bool HttpOnly { get { return _httpOnly; } set { _httpOnly = value; } } public string Name { get { return _name; } set { if (!canSetName(value, out var message)) { throw new CookieException(message); } _name = value; } } public string Path { get { return _path; } set { _path = value ?? string.Empty; } } public string Port { get { return _port; } set { if (value.IsNullOrEmpty()) { _port = string.Empty; _ports = new int[0]; return; } if (!value.IsEnclosedIn('"')) { throw new CookieException("The value specified for the Port attribute isn't enclosed in double quotes."); } if (!tryCreatePorts(value, out _ports, out var parseError)) { throw new CookieException($"The value specified for the Port attribute contains an invalid value: {parseError}"); } _port = value; } } public bool Secure { get { return _secure; } set { _secure = value; } } public DateTime TimeStamp => _timestamp; public string Value { get { return _value; } set { if (!canSetValue(value, out var message)) { throw new CookieException(message); } _value = ((value.Length > 0) ? value : "\"\""); } } public int Version { get { return _version; } set { if (value < 0 || value > 1) { throw new ArgumentOutOfRangeException("value", "Not 0 or 1."); } _version = value; } } static Cookie() { _reservedCharsForName = new char[7] { ' ', '=', ';', ',', '\n', '\r', '\t' }; _reservedCharsForValue = new char[2] { ';', ',' }; } public Cookie() { _comment = string.Empty; _domain = string.Empty; _expires = DateTime.MinValue; _name = string.Empty; _path = string.Empty; _port = string.Empty; _ports = new int[0]; _timestamp = DateTime.Now; _value = string.Empty; _version = 0; } public Cookie(string name, string value) : this() { Name = name; Value = value; } public Cookie(string name, string value, string path) : this(name, value) { Path = path; } public Cookie(string name, string value, string path, string domain) : this(name, value, path) { Domain = domain; } private static bool canSetName(string name, out string message) { if (name.IsNullOrEmpty()) { message = "The value specified for the Name is null or empty."; return false; } if (name[0] == '$' || name.Contains(_reservedCharsForName)) { message = "The value specified for the Name contains an invalid character."; return false; } message = string.Empty; return true; } private static bool canSetValue(string value, out string message) { if (value == null) { message = "The value specified for the Value is null."; return false; } if (value.Contains(_reservedCharsForValue) && !value.IsEnclosedIn('"')) { message = "The value specified for the Value contains an invalid character."; return false; } message = string.Empty; return true; } private static int hash(int i, int j, int k, int l, int m) { return i ^ ((j << 13) | (j >> 19)) ^ ((k << 26) | (k >> 6)) ^ ((l << 7) | (l >> 25)) ^ ((m << 20) | (m >> 12)); } private string toResponseStringVersion0() { StringBuilder stringBuilder = new StringBuilder(64); stringBuilder.AppendFormat("{0}={1}", _name, _value); if (_expires != DateTime.MinValue) { stringBuilder.AppendFormat("; Expires={0}", _expires.ToUniversalTime().ToString("ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'", CultureInfo.CreateSpecificCulture("en-US"))); } if (!_path.IsNullOrEmpty()) { stringBuilder.AppendFormat("; Path={0}", _path); } if (!_domain.IsNullOrEmpty()) { stringBuilder.AppendFormat("; Domain={0}", _domain); } if (_secure) { stringBuilder.Append("; Secure"); } if (_httpOnly) { stringBuilder.Append("; HttpOnly"); } return stringBuilder.ToString(); } private string toResponseStringVersion1() { StringBuilder stringBuilder = new StringBuilder(64); stringBuilder.AppendFormat("{0}={1}; Version={2}", _name, _value, _version); if (_expires != DateTime.MinValue) { stringBuilder.AppendFormat("; Max-Age={0}", MaxAge); } if (!_path.IsNullOrEmpty()) { stringBuilder.AppendFormat("; Path={0}", _path); } if (!_domain.IsNullOrEmpty()) { stringBuilder.AppendFormat("; Domain={0}", _domain); } if (!_port.IsNullOrEmpty()) { if (_port == "\"\"") { stringBuilder.Append("; Port"); } else { stringBuilder.AppendFormat("; Port={0}", _port); } } if (!_comment.IsNullOrEmpty()) { stringBuilder.AppendFormat("; Comment={0}", _comment.UrlEncode()); } if (_commentUri != null) { string originalString = _commentUri.OriginalString; stringBuilder.AppendFormat("; CommentURL={0}", originalString.IsToken() ? originalString : originalString.Quote()); } if (_discard) { stringBuilder.Append("; Discard"); } if (_secure) { stringBuilder.Append("; Secure"); } return stringBuilder.ToString(); } private static bool tryCreatePorts(string value, out int[] result, out string parseError) { string[] array = value.Trim(new char[1] { '"' }).Split(new char[1] { ',' }); int num = array.Length; int[] array2 = new int[num]; for (int i = 0; i < num; i++) { array2[i] = int.MinValue; string text = array[i].Trim(); if (text.Length != 0 && !int.TryParse(text, out array2[i])) { result = new int[0]; parseError = text; return false; } } result = array2; parseError = string.Empty; return true; } internal string ToRequestString(Uri uri) { if (_name.Length == 0) { return string.Empty; } if (_version == 0) { return $"{_name}={_value}"; } StringBuilder stringBuilder = new StringBuilder(64); stringBuilder.AppendFormat("$Version={0}; {1}={2}", _version, _name, _value); if (!_path.IsNullOrEmpty()) { stringBuilder.AppendFormat("; $Path={0}", _path); } else if (uri != null) { stringBuilder.AppendFormat("; $Path={0}", uri.GetAbsolutePath()); } else { stringBuilder.Append("; $Path=/"); } if ((uri == null || uri.Host != _domain) && !_domain.IsNullOrEmpty()) { stringBuilder.AppendFormat("; $Domain={0}", _domain); } if (!_port.IsNullOrEmpty()) { if (_port == "\"\"") { stringBuilder.Append("; $Port"); } else { stringBuilder.AppendFormat("; $Port={0}", _port); } } return stringBuilder.ToString(); } internal string ToResponseString() { if (_name.Length <= 0) { return string.Empty; } if (_version != 0) { return toResponseStringVersion1(); } return toResponseStringVersion0(); } public override bool Equals(object comparand) { if (comparand is Cookie cookie && _name.Equals(cookie.Name, StringComparison.InvariantCultureIgnoreCase) && _value.Equals(cookie.Value, StringComparison.InvariantCulture) && _path.Equals(cookie.Path, StringComparison.InvariantCulture) && _domain.Equals(cookie.Domain, StringComparison.InvariantCultureIgnoreCase)) { return _version == cookie.Version; } return false; } public override int GetHashCode() { return hash(StringComparer.InvariantCultureIgnoreCase.GetHashCode(_name), _value.GetHashCode(), _path.GetHashCode(), StringComparer.InvariantCultureIgnoreCase.GetHashCode(_domain), _version); } public override string ToString() { return ToRequestString(null); } } [Serializable] public class CookieCollection : ICollection, IEnumerable { private List _list; private object _sync; internal IList List => _list; internal IEnumerable Sorted { get { List list = new List(_list); if (list.Count > 1) { list.Sort(compareCookieWithinSorted); } return list; } } public int Count => _list.Count; public bool IsReadOnly => true; public bool IsSynchronized => false; public Cookie this[int index] { get { if (index < 0 || index >= _list.Count) { throw new ArgumentOutOfRangeException("index"); } return _list[index]; } } public Cookie this[string name] { get { if (name == null) { throw new ArgumentNullException("name"); } foreach (Cookie item in Sorted) { if (item.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) { return item; } } return null; } } public object SyncRoot => _sync ?? (_sync = ((ICollection)_list).SyncRoot); public CookieCollection() { _list = new List(); } private static int compareCookieWithinSort(Cookie x, Cookie y) { return x.Name.Length + x.Value.Length - (y.Name.Length + y.Value.Length); } private static int compareCookieWithinSorted(Cookie x, Cookie y) { int num = 0; if ((num = x.Version - y.Version) == 0) { if ((num = x.Name.CompareTo(y.Name)) == 0) { return y.Path.Length - x.Path.Length; } return num; } return num; } private static CookieCollection parseRequest(string value) { CookieCollection cookieCollection = new CookieCollection(); Cookie cookie = null; int num = 0; string[] array = splitCookieHeaderValue(value); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length == 0) { continue; } if (text.StartsWith("$version", StringComparison.InvariantCultureIgnoreCase)) { num = int.Parse(text.GetValue('=', unquote: true)); continue; } if (text.StartsWith("$path", StringComparison.InvariantCultureIgnoreCase)) { if (cookie != null) { cookie.Path = text.GetValue('='); } continue; } if (text.StartsWith("$domain", StringComparison.InvariantCultureIgnoreCase)) { if (cookie != null) { cookie.Domain = text.GetValue('='); } continue; } if (text.StartsWith("$port", StringComparison.InvariantCultureIgnoreCase)) { string port = (text.Equals("$port", StringComparison.InvariantCultureIgnoreCase) ? "\"\"" : text.GetValue('=')); if (cookie != null) { cookie.Port = port; } continue; } if (cookie != null) { cookieCollection.Add(cookie); } string value2 = string.Empty; int num2 = text.IndexOf('='); string name; if (num2 == -1) { name = text; } else if (num2 == text.Length - 1) { name = text.Substring(0, num2).TrimEnd(new char[1] { ' ' }); } else { name = text.Substring(0, num2).TrimEnd(new char[1] { ' ' }); value2 = text.Substring(num2 + 1).TrimStart(new char[1] { ' ' }); } cookie = new Cookie(name, value2); if (num != 0) { cookie.Version = num; } } if (cookie != null) { cookieCollection.Add(cookie); } return cookieCollection; } private static CookieCollection parseResponse(string value) { CookieCollection cookieCollection = new CookieCollection(); Cookie cookie = null; string[] array = splitCookieHeaderValue(value); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length == 0) { continue; } if (text.StartsWith("version", StringComparison.InvariantCultureIgnoreCase)) { if (cookie != null) { cookie.Version = int.Parse(text.GetValue('=', unquote: true)); } continue; } if (text.StartsWith("expires", StringComparison.InvariantCultureIgnoreCase)) { StringBuilder stringBuilder = new StringBuilder(text.GetValue('='), 32); if (i < array.Length - 1) { stringBuilder.AppendFormat(", {0}", array[++i].Trim()); } if (!DateTime.TryParseExact(stringBuilder.ToString(), new string[2] { "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'", "r" }, CultureInfo.CreateSpecificCulture("en-US"), DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out var result)) { result = DateTime.Now; } if (cookie != null && cookie.Expires == DateTime.MinValue) { cookie.Expires = result.ToLocalTime(); } continue; } if (text.StartsWith("max-age", StringComparison.InvariantCultureIgnoreCase)) { int num = int.Parse(text.GetValue('=', unquote: true)); DateTime expires = DateTime.Now.AddSeconds(num); if (cookie != null) { cookie.Expires = expires; } continue; } if (text.StartsWith("path", StringComparison.InvariantCultureIgnoreCase)) { if (cookie != null) { cookie.Path = text.GetValue('='); } continue; } if (text.StartsWith("domain", StringComparison.InvariantCultureIgnoreCase)) { if (cookie != null) { cookie.Domain = text.GetValue('='); } continue; } if (text.StartsWith("port", StringComparison.InvariantCultureIgnoreCase)) { string port = (text.Equals("port", StringComparison.InvariantCultureIgnoreCase) ? "\"\"" : text.GetValue('=')); if (cookie != null) { cookie.Port = port; } continue; } if (text.StartsWith("comment", StringComparison.InvariantCultureIgnoreCase)) { if (cookie != null) { cookie.Comment = text.GetValue('=').UrlDecode(); } continue; } if (text.StartsWith("commenturl", StringComparison.InvariantCultureIgnoreCase)) { if (cookie != null) { cookie.CommentUri = text.GetValue('=', unquote: true).ToUri(); } continue; } if (text.StartsWith("discard", StringComparison.InvariantCultureIgnoreCase)) { if (cookie != null) { cookie.Discard = true; } continue; } if (text.StartsWith("secure", StringComparison.InvariantCultureIgnoreCase)) { if (cookie != null) { cookie.Secure = true; } continue; } if (text.StartsWith("httponly", StringComparison.InvariantCultureIgnoreCase)) { if (cookie != null) { cookie.HttpOnly = true; } continue; } if (cookie != null) { cookieCollection.Add(cookie); } string value2 = string.Empty; int num2 = text.IndexOf('='); string name; if (num2 == -1) { name = text; } else if (num2 == text.Length - 1) { name = text.Substring(0, num2).TrimEnd(new char[1] { ' ' }); } else { name = text.Substring(0, num2).TrimEnd(new char[1] { ' ' }); value2 = text.Substring(num2 + 1).TrimStart(new char[1] { ' ' }); } cookie = new Cookie(name, value2); } if (cookie != null) { cookieCollection.Add(cookie); } return cookieCollection; } private int searchCookie(Cookie cookie) { string name = cookie.Name; string path = cookie.Path; string domain = cookie.Domain; int version = cookie.Version; for (int num = _list.Count - 1; num >= 0; num--) { Cookie cookie2 = _list[num]; if (cookie2.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase) && cookie2.Path.Equals(path, StringComparison.InvariantCulture) && cookie2.Domain.Equals(domain, StringComparison.InvariantCultureIgnoreCase) && cookie2.Version == version) { return num; } } return -1; } private static string[] splitCookieHeaderValue(string value) { return new List(value.SplitHeaderValue(',', ';')).ToArray(); } internal static CookieCollection Parse(string value, bool response) { if (!response) { return parseRequest(value); } return parseResponse(value); } internal void SetOrRemove(Cookie cookie) { int num = searchCookie(cookie); if (num == -1) { if (!cookie.Expired) { _list.Add(cookie); } } else if (!cookie.Expired) { _list[num] = cookie; } else { _list.RemoveAt(num); } } internal void SetOrRemove(CookieCollection cookies) { foreach (Cookie cookie in cookies) { SetOrRemove(cookie); } } internal void Sort() { if (_list.Count > 1) { _list.Sort(compareCookieWithinSort); } } public void Add(Cookie cookie) { if (cookie == null) { throw new ArgumentNullException("cookie"); } int num = searchCookie(cookie); if (num == -1) { _list.Add(cookie); } else { _list[num] = cookie; } } public void Add(CookieCollection cookies) { if (cookies == null) { throw new ArgumentNullException("cookies"); } foreach (Cookie cookie in cookies) { Add(cookie); } } public void CopyTo(Array array, int index) { if (array == null) { throw new ArgumentNullException("array"); } if (index < 0) { throw new ArgumentOutOfRangeException("index", "Less than zero."); } if (array.Rank > 1) { throw new ArgumentException("Multidimensional.", "array"); } if (array.Length - index < _list.Count) { throw new ArgumentException("The number of elements in this collection is greater than the available space of the destination array."); } if (!array.GetType().GetElementType().IsAssignableFrom(typeof(Cookie))) { throw new InvalidCastException("The elements in this collection cannot be cast automatically to the type of the destination array."); } ((ICollection)_list).CopyTo(array, index); } public void CopyTo(Cookie[] array, int index) { if (array == null) { throw new ArgumentNullException("array"); } if (index < 0) { throw new ArgumentOutOfRangeException("index", "Less than zero."); } if (array.Length - index < _list.Count) { throw new ArgumentException("The number of elements in this collection is greater than the available space of the destination array."); } _list.CopyTo(array, index); } public IEnumerator GetEnumerator() { return _list.GetEnumerator(); } } [Serializable] public class CookieException : FormatException, ISerializable { internal CookieException(string message) : base(message) { } internal CookieException(string message, Exception innerException) : base(message, innerException) { } protected CookieException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } public CookieException() { } [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] public override void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) { base.GetObjectData(serializationInfo, streamingContext); } [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter, SerializationFormatter = true)] void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) { base.GetObjectData(serializationInfo, streamingContext); } } internal sealed class EndPointListener { private List _all; private static readonly string _defaultCertFolderPath; private IPEndPoint _endpoint; private Dictionary _prefixes; private bool _secure; private Socket _socket; private ServerSslConfiguration _sslConfig; private List _unhandled; private Dictionary _unregistered; private object _unregisteredSync; public IPAddress Address => _endpoint.Address; public bool IsSecure => _secure; public int Port => _endpoint.Port; public ServerSslConfiguration SslConfiguration => _sslConfig; static EndPointListener() { _defaultCertFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); } internal EndPointListener(IPEndPoint endpoint, bool secure, string certificateFolderPath, ServerSslConfiguration sslConfig, bool reuseAddress) { if (secure) { X509Certificate2 certificate = getCertificate(endpoint.Port, certificateFolderPath, sslConfig.ServerCertificate); if (certificate == null) { throw new ArgumentException("No server certificate could be found."); } _secure = true; _sslConfig = new ServerSslConfiguration(sslConfig); _sslConfig.ServerCertificate = certificate; } _endpoint = endpoint; _prefixes = new Dictionary(); _unregistered = new Dictionary(); _unregisteredSync = ((ICollection)_unregistered).SyncRoot; _socket = new Socket(endpoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); if (reuseAddress) { _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, optionValue: true); } _socket.Bind(endpoint); _socket.Listen(500); _socket.BeginAccept(onAccept, this); } private static void addSpecial(List prefixes, HttpListenerPrefix prefix) { string path = prefix.Path; foreach (HttpListenerPrefix prefix2 in prefixes) { if (prefix2.Path == path) { throw new HttpListenerException(87, "The prefix is already in use."); } } prefixes.Add(prefix); } private static RSACryptoServiceProvider createRSAFromFile(string filename) { byte[] array = null; using (FileStream fileStream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) { array = new byte[fileStream.Length]; fileStream.Read(array, 0, array.Length); } RSACryptoServiceProvider rSACryptoServiceProvider = new RSACryptoServiceProvider(); rSACryptoServiceProvider.ImportCspBlob(array); return rSACryptoServiceProvider; } private static X509Certificate2 getCertificate(int port, string folderPath, X509Certificate2 defaultCertificate) { if (folderPath == null || folderPath.Length == 0) { folderPath = _defaultCertFolderPath; } try { string text = Path.Combine(folderPath, $"{port}.cer"); string text2 = Path.Combine(folderPath, $"{port}.key"); if (File.Exists(text) && File.Exists(text2)) { return new X509Certificate2(text) { PrivateKey = createRSAFromFile(text2) }; } } catch { } return defaultCertificate; } private void leaveIfNoPrefix() { if (_prefixes.Count > 0) { return; } List unhandled = _unhandled; if (unhandled == null || unhandled.Count <= 0) { unhandled = _all; if (unhandled == null || unhandled.Count <= 0) { EndPointManager.RemoveEndPoint(_endpoint); } } } private static void onAccept(IAsyncResult asyncResult) { EndPointListener endPointListener = (EndPointListener)asyncResult.AsyncState; Socket socket = null; try { socket = endPointListener._socket.EndAccept(asyncResult); } catch (SocketException) { } catch (ObjectDisposedException) { return; } try { endPointListener._socket.BeginAccept(onAccept, endPointListener); } catch { socket?.Close(); return; } if (socket != null) { processAccepted(socket, endPointListener); } } private static void processAccepted(Socket socket, EndPointListener listener) { HttpConnection httpConnection = null; try { httpConnection = new HttpConnection(socket, listener); lock (listener._unregisteredSync) { listener._unregistered[httpConnection] = httpConnection; } httpConnection.BeginReadRequest(); } catch { if (httpConnection != null) { httpConnection.Close(force: true); } else { socket.Close(); } } } private static bool removeSpecial(List prefixes, HttpListenerPrefix prefix) { string path = prefix.Path; int count = prefixes.Count; for (int i = 0; i < count; i++) { if (prefixes[i].Path == path) { prefixes.RemoveAt(i); return true; } } return false; } private static HttpListener searchHttpListenerFromSpecial(string path, List prefixes) { if (prefixes == null) { return null; } HttpListener result = null; int num = -1; foreach (HttpListenerPrefix prefix in prefixes) { string path2 = prefix.Path; int length = path2.Length; if (length >= num && path.StartsWith(path2)) { num = length; result = prefix.Listener; } } return result; } internal static bool CertificateExists(int port, string folderPath) { if (folderPath == null || folderPath.Length == 0) { folderPath = _defaultCertFolderPath; } string path = Path.Combine(folderPath, $"{port}.cer"); string path2 = Path.Combine(folderPath, $"{port}.key"); if (File.Exists(path)) { return File.Exists(path2); } return false; } internal void RemoveConnection(HttpConnection connection) { lock (_unregisteredSync) { _unregistered.Remove(connection); } } internal bool TrySearchHttpListener(Uri uri, out HttpListener listener) { listener = null; if (uri == null) { return false; } string host = uri.Host; bool flag = Uri.CheckHostName(host) == UriHostNameType.Dns; string text = uri.Port.ToString(); string text2 = HttpUtility.UrlDecode(uri.AbsolutePath); string text3 = ((text2[text2.Length - 1] != '/') ? (text2 + "/") : text2); if (host != null && host.Length > 0) { int num = -1; foreach (HttpListenerPrefix key in _prefixes.Keys) { if (flag) { string host2 = key.Host; if (Uri.CheckHostName(host2) == UriHostNameType.Dns && host2 != host) { continue; } } if (!(key.Port != text)) { string path = key.Path; int length = path.Length; if (length >= num && (text2.StartsWith(path) || text3.StartsWith(path))) { num = length; listener = _prefixes[key]; } } } if (num != -1) { return true; } } List unhandled = _unhandled; listener = searchHttpListenerFromSpecial(text2, unhandled); if (listener == null && text3 != text2) { listener = searchHttpListenerFromSpecial(text3, unhandled); } if (listener != null) { return true; } unhandled = _all; listener = searchHttpListenerFromSpecial(text2, unhandled); if (listener == null && text3 != text2) { listener = searchHttpListenerFromSpecial(text3, unhandled); } return listener != null; } public void AddPrefix(HttpListenerPrefix prefix, HttpListener listener) { if (prefix.Host == "*") { List unhandled; List list; do { unhandled = _unhandled; list = ((unhandled != null) ? new List(unhandled) : new List()); prefix.Listener = listener; addSpecial(list, prefix); } while (Interlocked.CompareExchange(ref _unhandled, list, unhandled) != unhandled); return; } if (prefix.Host == "+") { List unhandled; List list; do { unhandled = _all; list = ((unhandled != null) ? new List(unhandled) : new List()); prefix.Listener = listener; addSpecial(list, prefix); } while (Interlocked.CompareExchange(ref _all, list, unhandled) != unhandled); return; } Dictionary prefixes; Dictionary dictionary; do { prefixes = _prefixes; if (prefixes.ContainsKey(prefix)) { if (prefixes[prefix] != listener) { throw new HttpListenerException(87, $"There's another listener for {prefix}."); } break; } dictionary = new Dictionary(prefixes); dictionary[prefix] = listener; } while (Interlocked.CompareExchange(ref _prefixes, dictionary, prefixes) != prefixes); } public void Close() { _socket.Close(); HttpConnection[] array = null; lock (_unregisteredSync) { if (_unregistered.Count == 0) { return; } Dictionary.KeyCollection keys = _unregistered.Keys; array = new HttpConnection[keys.Count]; keys.CopyTo(array, 0); _unregistered.Clear(); } for (int num = array.Length - 1; num >= 0; num--) { array[num].Close(force: true); } } public void RemovePrefix(HttpListenerPrefix prefix, HttpListener listener) { if (prefix.Host == "*") { List unhandled; List list; do { unhandled = _unhandled; if (unhandled == null) { break; } list = new List(unhandled); } while (removeSpecial(list, prefix) && Interlocked.CompareExchange(ref _unhandled, list, unhandled) != unhandled); leaveIfNoPrefix(); return; } if (prefix.Host == "+") { List unhandled; List list; do { unhandled = _all; if (unhandled == null) { break; } list = new List(unhandled); } while (removeSpecial(list, prefix) && Interlocked.CompareExchange(ref _all, list, unhandled) != unhandled); leaveIfNoPrefix(); return; } Dictionary prefixes; Dictionary dictionary; do { prefixes = _prefixes; if (!prefixes.ContainsKey(prefix)) { break; } dictionary = new Dictionary(prefixes); dictionary.Remove(prefix); } while (Interlocked.CompareExchange(ref _prefixes, dictionary, prefixes) != prefixes); leaveIfNoPrefix(); } } internal sealed class EndPointManager { private static readonly Dictionary _endpoints; static EndPointManager() { _endpoints = new Dictionary(); } private EndPointManager() { } private static void addPrefix(string uriPrefix, HttpListener listener) { HttpListenerPrefix httpListenerPrefix = new HttpListenerPrefix(uriPrefix); IPAddress address = convertToIPAddress(httpListenerPrefix.Host); if (!address.IsLocal()) { throw new HttpListenerException(87, "Includes an invalid host."); } if (!int.TryParse(httpListenerPrefix.Port, out var result)) { throw new HttpListenerException(87, "Includes an invalid port."); } if (!result.IsPortNumber()) { throw new HttpListenerException(87, "Includes an invalid port."); } string path = httpListenerPrefix.Path; if (path.IndexOf('%') != -1) { throw new HttpListenerException(87, "Includes an invalid path."); } if (path.IndexOf("//", StringComparison.Ordinal) != -1) { throw new HttpListenerException(87, "Includes an invalid path."); } IPEndPoint iPEndPoint = new IPEndPoint(address, result); if (_endpoints.TryGetValue(iPEndPoint, out var value)) { if (value.IsSecure ^ httpListenerPrefix.IsSecure) { throw new HttpListenerException(87, "Includes an invalid scheme."); } } else { value = new EndPointListener(iPEndPoint, httpListenerPrefix.IsSecure, listener.CertificateFolderPath, listener.SslConfiguration, listener.ReuseAddress); _endpoints.Add(iPEndPoint, value); } value.AddPrefix(httpListenerPrefix, listener); } private static IPAddress convertToIPAddress(string hostname) { if (!(hostname == "*") && !(hostname == "+")) { return hostname.ToIPAddress(); } return IPAddress.Any; } private static void removePrefix(string uriPrefix, HttpListener listener) { HttpListenerPrefix httpListenerPrefix = new HttpListenerPrefix(uriPrefix); IPAddress address = convertToIPAddress(httpListenerPrefix.Host); if (!address.IsLocal() || !int.TryParse(httpListenerPrefix.Port, out var result) || !result.IsPortNumber()) { return; } string path = httpListenerPrefix.Path; if (path.IndexOf('%') == -1 && path.IndexOf("//", StringComparison.Ordinal) == -1) { IPEndPoint key = new IPEndPoint(address, result); if (_endpoints.TryGetValue(key, out var value) && !(value.IsSecure ^ httpListenerPrefix.IsSecure)) { value.RemovePrefix(httpListenerPrefix, listener); } } } internal static bool RemoveEndPoint(IPEndPoint endpoint) { lock (((ICollection)_endpoints).SyncRoot) { if (!_endpoints.TryGetValue(endpoint, out var value)) { return false; } _endpoints.Remove(endpoint); value.Close(); return true; } } public static void AddListener(HttpListener listener) { List list = new List(); lock (((ICollection)_endpoints).SyncRoot) { try { foreach (string prefix in listener.Prefixes) { addPrefix(prefix, listener); list.Add(prefix); } } catch { foreach (string item in list) { removePrefix(item, listener); } throw; } } } public static void AddPrefix(string uriPrefix, HttpListener listener) { lock (((ICollection)_endpoints).SyncRoot) { addPrefix(uriPrefix, listener); } } public static void RemoveListener(HttpListener listener) { lock (((ICollection)_endpoints).SyncRoot) { foreach (string prefix in listener.Prefixes) { removePrefix(prefix, listener); } } } public static void RemovePrefix(string uriPrefix, HttpListener listener) { lock (((ICollection)_endpoints).SyncRoot) { removePrefix(uriPrefix, listener); } } } public class HttpBasicIdentity : GenericIdentity { private string _password; public virtual string Password => _password; internal HttpBasicIdentity(string username, string password) : base(username, "Basic") { _password = password; } } internal sealed class HttpConnection { private byte[] _buffer; private const int _bufferLength = 8192; private HttpListenerContext _context; private bool _contextRegistered; private StringBuilder _currentLine; private InputState _inputState; private RequestStream _inputStream; private HttpListener _lastListener; private LineState _lineState; private EndPointListener _listener; private ResponseStream _outputStream; private int _position; private MemoryStream _requestBuffer; private int _reuses; private bool _secure; private Socket _socket; private Stream _stream; private object _sync; private int _timeout; private Dictionary _timeoutCanceled; private System.Threading.Timer _timer; public bool IsClosed => _socket == null; public bool IsSecure => _secure; public IPEndPoint LocalEndPoint => (IPEndPoint)_socket.LocalEndPoint; public IPEndPoint RemoteEndPoint => (IPEndPoint)_socket.RemoteEndPoint; public int Reuses => _reuses; public Stream Stream => _stream; internal HttpConnection(Socket socket, EndPointListener listener) { _socket = socket; _listener = listener; _secure = listener.IsSecure; NetworkStream networkStream = new NetworkStream(socket, ownsSocket: false); if (_secure) { ServerSslConfiguration sslConfiguration = listener.SslConfiguration; SslStream sslStream = new SslStream(networkStream, leaveInnerStreamOpen: false, sslConfiguration.ClientCertificateValidationCallback); sslStream.AuthenticateAsServer(sslConfiguration.ServerCertificate, sslConfiguration.ClientCertificateRequired, sslConfiguration.EnabledSslProtocols, sslConfiguration.CheckCertificateRevocation); _stream = sslStream; } else { _stream = networkStream; } _sync = new object(); _timeout = 90000; _timeoutCanceled = new Dictionary(); _timer = new System.Threading.Timer(onTimeout, this, -1, -1); init(); } private void close() { lock (_sync) { if (_socket == null) { return; } disposeTimer(); disposeRequestBuffer(); disposeStream(); closeSocket(); } unregisterContext(); removeConnection(); } private void closeSocket() { try { _socket.Shutdown(SocketShutdown.Both); } catch { } _socket.Close(); _socket = null; } private void disposeRequestBuffer() { if (_requestBuffer != null) { _requestBuffer.Dispose(); _requestBuffer = null; } } private void disposeStream() { if (_stream != null) { _inputStream = null; _outputStream = null; _stream.Dispose(); _stream = null; } } private void disposeTimer() { if (_timer != null) { try { _timer.Change(-1, -1); } catch { } _timer.Dispose(); _timer = null; } } private void init() { _context = new HttpListenerContext(this); _inputState = InputState.RequestLine; _inputStream = null; _lineState = LineState.None; _outputStream = null; _position = 0; _requestBuffer = new MemoryStream(); } private static void onRead(IAsyncResult asyncResult) { HttpConnection httpConnection = (HttpConnection)asyncResult.AsyncState; if (httpConnection._socket == null) { return; } lock (httpConnection._sync) { if (httpConnection._socket == null) { return; } int num = -1; int num2 = 0; try { int reuses = httpConnection._reuses; if (!httpConnection._timeoutCanceled[reuses]) { httpConnection._timer.Change(-1, -1); httpConnection._timeoutCanceled[reuses] = true; } num = httpConnection._stream.EndRead(asyncResult); httpConnection._requestBuffer.Write(httpConnection._buffer, 0, num); num2 = (int)httpConnection._requestBuffer.Length; } catch (Exception ex) { if (httpConnection._requestBuffer != null && httpConnection._requestBuffer.Length > 0) { httpConnection.SendError(ex.Message, 400); } else { httpConnection.close(); } return; } if (num <= 0) { httpConnection.close(); } else if (httpConnection.processInput(httpConnection._requestBuffer.GetBuffer(), num2)) { if (!httpConnection._context.HasError) { httpConnection._context.Request.FinishInitialization(); } if (httpConnection._context.HasError) { httpConnection.SendError(); return; } if (!httpConnection._listener.TrySearchHttpListener(httpConnection._context.Request.Url, out var listener)) { httpConnection.SendError(null, 404); return; } if (httpConnection._lastListener != listener) { httpConnection.removeConnection(); if (!listener.AddConnection(httpConnection)) { httpConnection.close(); return; } httpConnection._lastListener = listener; } httpConnection._context.Listener = listener; if (httpConnection._context.Authenticate() && httpConnection._context.Register()) { httpConnection._contextRegistered = true; } } else { httpConnection._stream.BeginRead(httpConnection._buffer, 0, 8192, onRead, httpConnection); } } } private static void onTimeout(object state) { HttpConnection httpConnection = (HttpConnection)state; int reuses = httpConnection._reuses; if (httpConnection._socket == null) { return; } lock (httpConnection._sync) { if (httpConnection._socket != null && !httpConnection._timeoutCanceled[reuses]) { httpConnection.SendError(null, 408); } } } private bool processInput(byte[] data, int length) { if (_currentLine == null) { _currentLine = new StringBuilder(64); } int read = 0; try { string text; while ((text = readLineFrom(data, _position, length, out read)) != null) { _position += read; if (text.Length == 0) { if (_inputState != 0) { if (_position > 32768) { _context.ErrorMessage = "Headers too long"; } _currentLine = null; return true; } } else { if (_inputState == InputState.RequestLine) { _context.Request.SetRequestLine(text); _inputState = InputState.Headers; } else { _context.Request.AddHeader(text); } if (_context.HasError) { return true; } } } } catch (Exception ex) { _context.ErrorMessage = ex.Message; return true; } _position += read; if (_position >= 32768) { _context.ErrorMessage = "Headers too long"; return true; } return false; } private string readLineFrom(byte[] buffer, int offset, int length, out int read) { read = 0; for (int i = offset; i < length; i++) { if (_lineState == LineState.Lf) { break; } read++; byte b = buffer[i]; switch (b) { case 13: _lineState = LineState.Cr; break; case 10: _lineState = LineState.Lf; break; default: _currentLine.Append((char)b); break; } } if (_lineState != LineState.Lf) { return null; } string result = _currentLine.ToString(); _currentLine.Length = 0; _lineState = LineState.None; return result; } private void removeConnection() { if (_lastListener != null) { _lastListener.RemoveConnection(this); } else { _listener.RemoveConnection(this); } } private void unregisterContext() { if (_contextRegistered) { _context.Unregister(); _contextRegistered = false; } } internal void Close(bool force) { if (_socket == null) { return; } lock (_sync) { if (_socket == null) { return; } if (!force) { GetResponseStream().Close(force: false); if (!_context.Response.CloseConnection && _context.Request.FlushInput()) { _reuses++; disposeRequestBuffer(); unregisterContext(); init(); BeginReadRequest(); return; } } else if (_outputStream != null) { _outputStream.Close(force: true); } close(); } } public void BeginReadRequest() { if (_buffer == null) { _buffer = new byte[8192]; } if (_reuses == 1) { _timeout = 15000; } try { _timeoutCanceled.Add(_reuses, value: false); _timer.Change(_timeout, -1); _stream.BeginRead(_buffer, 0, 8192, onRead, this); } catch { close(); } } public void Close() { Close(force: false); } public RequestStream GetRequestStream(long contentLength, bool chunked) { if (_inputStream != null || _socket == null) { return _inputStream; } lock (_sync) { if (_socket == null) { return _inputStream; } byte[] buffer = _requestBuffer.GetBuffer(); int num = (int)_requestBuffer.Length; disposeRequestBuffer(); if (chunked) { _context.Response.SendChunked = true; _inputStream = new ChunkedRequestStream(_stream, buffer, _position, num - _position, _context); } else { _inputStream = new RequestStream(_stream, buffer, _position, num - _position, contentLength); } return _inputStream; } } public ResponseStream GetResponseStream() { if (_outputStream != null || _socket == null) { return _outputStream; } lock (_sync) { if (_socket == null) { return _outputStream; } bool ignoreWriteExceptions = _context.Listener?.IgnoreWriteExceptions ?? true; _outputStream = new ResponseStream(_stream, _context.Response, ignoreWriteExceptions); return _outputStream; } } public void SendError() { SendError(_context.ErrorMessage, _context.ErrorStatus); } public void SendError(string message, int status) { if (_socket == null) { return; } lock (_sync) { if (_socket == null) { return; } try { HttpListenerResponse response = _context.Response; response.StatusCode = status; response.ContentType = "text/html"; StringBuilder stringBuilder = new StringBuilder(64); stringBuilder.AppendFormat("

{0} {1}", status, response.StatusDescription); if (message != null && message.Length > 0) { stringBuilder.AppendFormat(" ({0})

", message); } else { stringBuilder.Append(""); } Encoding uTF = Encoding.UTF8; byte[] bytes = uTF.GetBytes(stringBuilder.ToString()); response.ContentEncoding = uTF; response.ContentLength64 = bytes.LongLength; response.Close(bytes, willBlock: true); } catch { Close(force: true); } } } } public class HttpDigestIdentity : GenericIdentity { private NameValueCollection _parameters; public string Algorithm => _parameters["algorithm"]; public string Cnonce => _parameters["cnonce"]; public string Nc => _parameters["nc"]; public string Nonce => _parameters["nonce"]; public string Opaque => _parameters["opaque"]; public string Qop => _parameters["qop"]; public string Realm => _parameters["realm"]; public string Response => _parameters["response"]; public string Uri => _parameters["uri"]; internal HttpDigestIdentity(NameValueCollection parameters) : base(parameters["username"], "Digest") { _parameters = parameters; } internal bool IsValid(string password, string realm, string method, string entity) { string text = AuthenticationResponse.CreateRequestDigest(new NameValueCollection(_parameters) { ["password"] = password, ["realm"] = realm, ["method"] = method, ["entity"] = entity }); return _parameters["response"] == text; } } internal class HttpHeaderInfo { private string _name; private HttpHeaderType _type; internal bool IsMultiValueInRequest => (_type & HttpHeaderType.MultiValueInRequest) == HttpHeaderType.MultiValueInRequest; internal bool IsMultiValueInResponse => (_type & HttpHeaderType.MultiValueInResponse) == HttpHeaderType.MultiValueInResponse; public bool IsRequest => (_type & HttpHeaderType.Request) == HttpHeaderType.Request; public bool IsResponse => (_type & HttpHeaderType.Response) == HttpHeaderType.Response; public string Name => _name; public HttpHeaderType Type => _type; internal HttpHeaderInfo(string name, HttpHeaderType type) { _name = name; _type = type; } public bool IsMultiValue(bool response) { if ((_type & HttpHeaderType.MultiValue) != HttpHeaderType.MultiValue) { if (!response) { return IsMultiValueInRequest; } return IsMultiValueInResponse; } if (!response) { return IsRequest; } return IsResponse; } public bool IsRestricted(bool response) { if ((_type & HttpHeaderType.Restricted) != HttpHeaderType.Restricted) { return false; } if (!response) { return IsRequest; } return IsResponse; } } [Flags] internal enum HttpHeaderType { Unspecified = 0, Request = 1, Response = 2, Restricted = 4, MultiValue = 8, MultiValueInRequest = 0x10, MultiValueInResponse = 0x20 } public sealed class HttpListener : IDisposable { private AuthenticationSchemes _authSchemes; private Func _authSchemeSelector; private string _certFolderPath; private Dictionary _connections; private object _connectionsSync; private List _ctxQueue; private object _ctxQueueSync; private Dictionary _ctxRegistry; private object _ctxRegistrySync; private static readonly string _defaultRealm; private bool _disposed; private bool _ignoreWriteExceptions; private volatile bool _listening; private Logger _logger; private HttpListenerPrefixCollection _prefixes; private string _realm; private bool _reuseAddress; private ServerSslConfiguration _sslConfig; private Func _userCredFinder; private List _waitQueue; private object _waitQueueSync; internal bool IsDisposed => _disposed; internal bool ReuseAddress { get { return _reuseAddress; } set { _reuseAddress = value; } } public AuthenticationSchemes AuthenticationSchemes { get { CheckDisposed(); return _authSchemes; } set { CheckDisposed(); _authSchemes = value; } } public Func AuthenticationSchemeSelector { get { CheckDisposed(); return _authSchemeSelector; } set { CheckDisposed(); _authSchemeSelector = value; } } public string CertificateFolderPath { get { CheckDisposed(); return _certFolderPath; } set { CheckDisposed(); _certFolderPath = value; } } public bool IgnoreWriteExceptions { get { CheckDisposed(); return _ignoreWriteExceptions; } set { CheckDisposed(); _ignoreWriteExceptions = value; } } public bool IsListening => _listening; public static bool IsSupported => true; public Logger Log => _logger; public HttpListenerPrefixCollection Prefixes { get { CheckDisposed(); return _prefixes; } } public string Realm { get { CheckDisposed(); return _realm; } set { CheckDisposed(); _realm = value; } } public ServerSslConfiguration SslConfiguration { get { CheckDisposed(); return _sslConfig ?? (_sslConfig = new ServerSslConfiguration()); } set { CheckDisposed(); _sslConfig = value; } } public bool UnsafeConnectionNtlmAuthentication { get { throw new NotSupportedException(); } set { throw new NotSupportedException(); } } public Func UserCredentialsFinder { get { CheckDisposed(); return _userCredFinder; } set { CheckDisposed(); _userCredFinder = value; } } static HttpListener() { _defaultRealm = "SECRET AREA"; } public HttpListener() { _authSchemes = AuthenticationSchemes.Anonymous; _connections = new Dictionary(); _connectionsSync = ((ICollection)_connections).SyncRoot; _ctxQueue = new List(); _ctxQueueSync = ((ICollection)_ctxQueue).SyncRoot; _ctxRegistry = new Dictionary(); _ctxRegistrySync = ((ICollection)_ctxRegistry).SyncRoot; _logger = new Logger(); _prefixes = new HttpListenerPrefixCollection(this); _waitQueue = new List(); _waitQueueSync = ((ICollection)_waitQueue).SyncRoot; } private void cleanupConnections() { HttpConnection[] array = null; lock (_connectionsSync) { if (_connections.Count == 0) { return; } Dictionary.KeyCollection keys = _connections.Keys; array = new HttpConnection[keys.Count]; keys.CopyTo(array, 0); _connections.Clear(); } for (int num = array.Length - 1; num >= 0; num--) { array[num].Close(force: true); } } private void cleanupContextQueue(bool sendServiceUnavailable) { HttpListenerContext[] array = null; lock (_ctxQueueSync) { if (_ctxQueue.Count == 0) { return; } array = _ctxQueue.ToArray(); _ctxQueue.Clear(); } if (sendServiceUnavailable) { HttpListenerContext[] array2 = array; for (int i = 0; i < array2.Length; i++) { HttpListenerResponse response = array2[i].Response; response.StatusCode = 503; response.Close(); } } } private void cleanupContextRegistry() { HttpListenerContext[] array = null; lock (_ctxRegistrySync) { if (_ctxRegistry.Count == 0) { return; } Dictionary.KeyCollection keys = _ctxRegistry.Keys; array = new HttpListenerContext[keys.Count]; keys.CopyTo(array, 0); _ctxRegistry.Clear(); } for (int num = array.Length - 1; num >= 0; num--) { array[num].Connection.Close(force: true); } } private void cleanupWaitQueue(Exception exception) { HttpListenerAsyncResult[] array = null; lock (_waitQueueSync) { if (_waitQueue.Count == 0) { return; } array = _waitQueue.ToArray(); _waitQueue.Clear(); } HttpListenerAsyncResult[] array2 = array; for (int i = 0; i < array2.Length; i++) { array2[i].Complete(exception); } } private void close(bool force) { if (_listening) { _listening = false; EndPointManager.RemoveListener(this); } lock (_ctxRegistrySync) { cleanupContextQueue(!force); } cleanupContextRegistry(); cleanupConnections(); cleanupWaitQueue(new ObjectDisposedException(GetType().ToString())); _disposed = true; } private HttpListenerAsyncResult getAsyncResultFromQueue() { if (_waitQueue.Count == 0) { return null; } HttpListenerAsyncResult result = _waitQueue[0]; _waitQueue.RemoveAt(0); return result; } private HttpListenerContext getContextFromQueue() { if (_ctxQueue.Count == 0) { return null; } HttpListenerContext result = _ctxQueue[0]; _ctxQueue.RemoveAt(0); return result; } internal bool AddConnection(HttpConnection connection) { if (!_listening) { return false; } lock (_connectionsSync) { if (!_listening) { return false; } _connections[connection] = connection; return true; } } internal HttpListenerAsyncResult BeginGetContext(HttpListenerAsyncResult asyncResult) { lock (_ctxRegistrySync) { if (!_listening) { throw new HttpListenerException(995); } HttpListenerContext contextFromQueue = getContextFromQueue(); if (contextFromQueue == null) { _waitQueue.Add(asyncResult); } else { asyncResult.Complete(contextFromQueue, syncCompleted: true); } return asyncResult; } } internal void CheckDisposed() { if (_disposed) { throw new ObjectDisposedException(GetType().ToString()); } } internal string GetRealm() { string realm = _realm; if (realm == null || realm.Length <= 0) { return _defaultRealm; } return realm; } internal Func GetUserCredentialsFinder() { return _userCredFinder; } internal bool RegisterContext(HttpListenerContext context) { if (!_listening) { return false; } lock (_ctxRegistrySync) { if (!_listening) { return false; } _ctxRegistry[context] = context; HttpListenerAsyncResult asyncResultFromQueue = getAsyncResultFromQueue(); if (asyncResultFromQueue == null) { _ctxQueue.Add(context); } else { asyncResultFromQueue.Complete(context); } return true; } } internal void RemoveConnection(HttpConnection connection) { lock (_connectionsSync) { _connections.Remove(connection); } } internal AuthenticationSchemes SelectAuthenticationScheme(HttpListenerRequest request) { Func authSchemeSelector = _authSchemeSelector; if (authSchemeSelector == null) { return _authSchemes; } try { return authSchemeSelector(request); } catch { return AuthenticationSchemes.None; } } internal void UnregisterContext(HttpListenerContext context) { lock (_ctxRegistrySync) { _ctxRegistry.Remove(context); } } public void Abort() { if (!_disposed) { close(force: true); } } public IAsyncResult BeginGetContext(AsyncCallback callback, object state) { CheckDisposed(); if (_prefixes.Count == 0) { throw new InvalidOperationException("The listener has no URI prefix on which listens."); } if (!_listening) { throw new InvalidOperationException("The listener hasn't been started."); } return BeginGetContext(new HttpListenerAsyncResult(callback, state)); } public void Close() { if (!_disposed) { close(force: false); } } public HttpListenerContext EndGetContext(IAsyncResult asyncResult) { CheckDisposed(); if (asyncResult == null) { throw new ArgumentNullException("asyncResult"); } if (!(asyncResult is HttpListenerAsyncResult httpListenerAsyncResult)) { throw new ArgumentException("A wrong IAsyncResult.", "asyncResult"); } if (httpListenerAsyncResult.EndCalled) { throw new InvalidOperationException("This IAsyncResult cannot be reused."); } httpListenerAsyncResult.EndCalled = true; if (!httpListenerAsyncResult.IsCompleted) { httpListenerAsyncResult.AsyncWaitHandle.WaitOne(); } return httpListenerAsyncResult.GetContext(); } public HttpListenerContext GetContext() { CheckDisposed(); if (_prefixes.Count == 0) { throw new InvalidOperationException("The listener has no URI prefix on which listens."); } if (!_listening) { throw new InvalidOperationException("The listener hasn't been started."); } HttpListenerAsyncResult httpListenerAsyncResult = BeginGetContext(new HttpListenerAsyncResult(null, null)); httpListenerAsyncResult.InGet = true; return EndGetContext(httpListenerAsyncResult); } public void Start() { CheckDisposed(); if (!_listening) { EndPointManager.AddListener(this); _listening = true; } } public void Stop() { CheckDisposed(); if (_listening) { _listening = false; EndPointManager.RemoveListener(this); lock (_ctxRegistrySync) { cleanupContextQueue(sendServiceUnavailable: true); } cleanupContextRegistry(); cleanupConnections(); cleanupWaitQueue(new HttpListenerException(995, "The listener is stopped.")); } } void IDisposable.Dispose() { if (!_disposed) { close(force: true); } } } internal class HttpListenerAsyncResult : IAsyncResult { private AsyncCallback _callback; private bool _completed; private HttpListenerContext _context; private bool _endCalled; private Exception _exception; private bool _inGet; private object _state; private object _sync; private bool _syncCompleted; private ManualResetEvent _waitHandle; internal bool EndCalled { get { return _endCalled; } set { _endCalled = value; } } internal bool InGet { get { return _inGet; } set { _inGet = value; } } public object AsyncState => _state; public WaitHandle AsyncWaitHandle { get { lock (_sync) { return _waitHandle ?? (_waitHandle = new ManualResetEvent(_completed)); } } } public bool CompletedSynchronously => _syncCompleted; public bool IsCompleted { get { lock (_sync) { return _completed; } } } internal HttpListenerAsyncResult(AsyncCallback callback, object state) { _callback = callback; _state = state; _sync = new object(); } private static void complete(HttpListenerAsyncResult asyncResult) { lock (asyncResult._sync) { asyncResult._completed = true; asyncResult._waitHandle?.Set(); } AsyncCallback callback = asyncResult._callback; if (callback == null) { return; } ThreadPool.QueueUserWorkItem(delegate { try { callback(asyncResult); } catch { } }, null); } internal void Complete(Exception exception) { _exception = ((_inGet && exception is ObjectDisposedException) ? new HttpListenerException(995, "The listener is closed.") : exception); complete(this); } internal void Complete(HttpListenerContext context) { Complete(context, syncCompleted: false); } internal void Complete(HttpListenerContext context, bool syncCompleted) { _context = context; _syncCompleted = syncCompleted; complete(this); } internal HttpListenerContext GetContext() { if (_exception != null) { throw _exception; } return _context; } } public sealed class HttpListenerContext { private HttpConnection _connection; private string _error; private int _errorStatus; private HttpListener _listener; private HttpListenerRequest _request; private HttpListenerResponse _response; private IPrincipal _user; private HttpListenerWebSocketContext _websocketContext; internal HttpConnection Connection => _connection; internal string ErrorMessage { get { return _error; } set { _error = value; } } internal int ErrorStatus { get { return _errorStatus; } set { _errorStatus = value; } } internal bool HasError => _error != null; internal HttpListener Listener { get { return _listener; } set { _listener = value; } } public HttpListenerRequest Request => _request; public HttpListenerResponse Response => _response; public IPrincipal User => _user; internal HttpListenerContext(HttpConnection connection) { _connection = connection; _errorStatus = 400; _request = new HttpListenerRequest(this); _response = new HttpListenerResponse(this); } internal bool Authenticate() { AuthenticationSchemes authenticationSchemes = _listener.SelectAuthenticationScheme(_request); switch (authenticationSchemes) { case AuthenticationSchemes.Anonymous: return true; case AuthenticationSchemes.None: _response.Close(HttpStatusCode.Forbidden); return false; default: { string realm = _listener.GetRealm(); IPrincipal principal = HttpUtility.CreateUser(_request.Headers["Authorization"], authenticationSchemes, realm, _request.HttpMethod, _listener.GetUserCredentialsFinder()); if (principal == null || !principal.Identity.IsAuthenticated) { _response.CloseWithAuthChallenge(new AuthenticationChallenge(authenticationSchemes, realm).ToString()); return false; } _user = principal; return true; } } } internal bool Register() { return _listener.RegisterContext(this); } internal void Unregister() { _listener.UnregisterContext(this); } public HttpListenerWebSocketContext AcceptWebSocket(string protocol) { if (_websocketContext != null) { throw new InvalidOperationException("The accepting is already in progress."); } if (protocol != null) { if (protocol.Length == 0) { throw new ArgumentException("An empty string.", "protocol"); } if (!protocol.IsToken()) { throw new ArgumentException("Contains an invalid character.", "protocol"); } } _websocketContext = new HttpListenerWebSocketContext(this, protocol); return _websocketContext; } } [Serializable] public class HttpListenerException : Win32Exception { public override int ErrorCode => base.NativeErrorCode; protected HttpListenerException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { } public HttpListenerException() { } public HttpListenerException(int errorCode) : base(errorCode) { } public HttpListenerException(int errorCode, string message) : base(errorCode, message) { } } internal sealed class HttpListenerPrefix { private string _host; private HttpListener _listener; private string _original; private string _path; private string _port; private string _prefix; private bool _secure; public string Host => _host; public bool IsSecure => _secure; public HttpListener Listener { get { return _listener; } set { _listener = value; } } public string Original => _original; public string Path => _path; public string Port => _port; internal HttpListenerPrefix(string uriPrefix) { _original = uriPrefix; parse(uriPrefix); } private void parse(string uriPrefix) { if (uriPrefix.StartsWith("https")) { _secure = true; } int length = uriPrefix.Length; int num = uriPrefix.IndexOf(':') + 3; int num2 = uriPrefix.IndexOf('/', num + 1, length - num - 1); int num3 = uriPrefix.LastIndexOf(':', num2 - 1, num2 - num - 1); if (uriPrefix[num2 - 1] != ']' && num3 > num) { _host = uriPrefix.Substring(num, num3 - num); _port = uriPrefix.Substring(num3 + 1, num2 - num3 - 1); } else { _host = uriPrefix.Substring(num, num2 - num); _port = (_secure ? "443" : "80"); } _path = uriPrefix.Substring(num2); _prefix = string.Format("http{0}://{1}:{2}{3}", _secure ? "s" : "", _host, _port, _path); } public static void CheckPrefix(string uriPrefix) { if (uriPrefix == null) { throw new ArgumentNullException("uriPrefix"); } int length = uriPrefix.Length; if (length == 0) { throw new ArgumentException("An empty string.", "uriPrefix"); } if (!uriPrefix.StartsWith("http://") && !uriPrefix.StartsWith("https://")) { throw new ArgumentException("The scheme isn't 'http' or 'https'.", "uriPrefix"); } int num = uriPrefix.IndexOf(':') + 3; if (num >= length) { throw new ArgumentException("No host is specified.", "uriPrefix"); } if (uriPrefix[num] == ':') { throw new ArgumentException("No host is specified.", "uriPrefix"); } int num2 = uriPrefix.IndexOf('/', num, length - num); if (num2 == num) { throw new ArgumentException("No host is specified.", "uriPrefix"); } if (num2 == -1 || uriPrefix[length - 1] != '/') { throw new ArgumentException("Ends without '/'.", "uriPrefix"); } if (uriPrefix[num2 - 1] == ':') { throw new ArgumentException("No port is specified.", "uriPrefix"); } if (num2 == length - 2) { throw new ArgumentException("No path is specified.", "uriPrefix"); } } public override bool Equals(object obj) { if (obj is HttpListenerPrefix httpListenerPrefix) { return httpListenerPrefix._prefix == _prefix; } return false; } public override int GetHashCode() { return _prefix.GetHashCode(); } public override string ToString() { return _prefix; } } public class HttpListenerPrefixCollection : ICollection, IEnumerable, IEnumerable { private HttpListener _listener; private List _prefixes; public int Count => _prefixes.Count; public bool IsReadOnly => false; public bool IsSynchronized => false; internal HttpListenerPrefixCollection(HttpListener listener) { _listener = listener; _prefixes = new List(); } public void Add(string uriPrefix) { _listener.CheckDisposed(); HttpListenerPrefix.CheckPrefix(uriPrefix); if (!_prefixes.Contains(uriPrefix)) { _prefixes.Add(uriPrefix); if (_listener.IsListening) { EndPointManager.AddPrefix(uriPrefix, _listener); } } } public void Clear() { _listener.CheckDisposed(); _prefixes.Clear(); if (_listener.IsListening) { EndPointManager.RemoveListener(_listener); } } public bool Contains(string uriPrefix) { _listener.CheckDisposed(); if (uriPrefix == null) { throw new ArgumentNullException("uriPrefix"); } return _prefixes.Contains(uriPrefix); } public void CopyTo(Array array, int offset) { _listener.CheckDisposed(); ((ICollection)_prefixes).CopyTo(array, offset); } public void CopyTo(string[] array, int offset) { _listener.CheckDisposed(); _prefixes.CopyTo(array, offset); } public IEnumerator GetEnumerator() { return _prefixes.GetEnumerator(); } public bool Remove(string uriPrefix) { _listener.CheckDisposed(); if (uriPrefix == null) { throw new ArgumentNullException("uriPrefix"); } bool num = _prefixes.Remove(uriPrefix); if (num && _listener.IsListening) { EndPointManager.RemovePrefix(uriPrefix, _listener); } return num; } IEnumerator IEnumerable.GetEnumerator() { return _prefixes.GetEnumerator(); } } public sealed class HttpListenerRequest { private static readonly byte[] _100continue; private string[] _acceptTypes; private bool _chunked; private Encoding _contentEncoding; private long _contentLength; private bool _contentLengthSet; private HttpListenerContext _context; private CookieCollection _cookies; private WebHeaderCollection _headers; private Guid _identifier; private Stream _inputStream; private bool _keepAlive; private bool _keepAliveSet; private string _method; private NameValueCollection _queryString; private Uri _referer; private string _uri; private Uri _url; private string[] _userLanguages; private Version _version; private bool _websocketRequest; private bool _websocketRequestSet; public string[] AcceptTypes => _acceptTypes; public int ClientCertificateError => 0; public Encoding ContentEncoding => _contentEncoding ?? (_contentEncoding = Encoding.Default); public long ContentLength64 => _contentLength; public string ContentType => _headers["Content-Type"]; public CookieCollection Cookies => _cookies ?? (_cookies = _headers.GetCookies(response: false)); public bool HasEntityBody { get { if (_contentLength <= 0) { return _chunked; } return true; } } public NameValueCollection Headers => _headers; public string HttpMethod => _method; public Stream InputStream => _inputStream ?? (_inputStream = (HasEntityBody ? _context.Connection.GetRequestStream(_contentLength, _chunked) : Stream.Null)); public bool IsAuthenticated => _context.User != null; public bool IsLocal => RemoteEndPoint.Address.IsLocal(); public bool IsSecureConnection => _context.Connection.IsSecure; public bool IsWebSocketRequest { get { if (!_websocketRequestSet) { _websocketRequest = _method == "GET" && _version > HttpVersion.Version10 && _headers.Contains("Upgrade", "websocket") && _headers.Contains("Connection", "Upgrade"); _websocketRequestSet = true; } return _websocketRequest; } } public bool KeepAlive { get { if (!_keepAliveSet) { string text; _keepAlive = _version > HttpVersion.Version10 || _headers.Contains("Connection", "keep-alive") || ((text = _headers["Keep-Alive"]) != null && text != "closed"); _keepAliveSet = true; } return _keepAlive; } } public IPEndPoint LocalEndPoint => _context.Connection.LocalEndPoint; public Version ProtocolVersion => _version; public NameValueCollection QueryString => _queryString ?? (_queryString = HttpUtility.InternalParseQueryString(_url.Query, Encoding.UTF8)); public string RawUrl => _url.PathAndQuery; public IPEndPoint RemoteEndPoint => _context.Connection.RemoteEndPoint; public Guid RequestTraceIdentifier => _identifier; public Uri Url => _url; public Uri UrlReferrer => _referer; public string UserAgent => _headers["User-Agent"]; public string UserHostAddress => LocalEndPoint.ToString(); public string UserHostName => _headers["Host"]; public string[] UserLanguages => _userLanguages; static HttpListenerRequest() { _100continue = Encoding.ASCII.GetBytes("HTTP/1.1 100 Continue\r\n\r\n"); } internal HttpListenerRequest(HttpListenerContext context) { _context = context; _contentLength = -1L; _headers = new WebHeaderCollection(); _identifier = Guid.NewGuid(); } private static bool tryCreateVersion(string version, out Version result) { try { result = new Version(version); return true; } catch { result = null; return false; } } internal void AddHeader(string header) { int num = header.IndexOf(':'); if (num == -1) { _context.ErrorMessage = "Invalid header"; return; } string text = header.Substring(0, num).Trim(); string text2 = header.Substring(num + 1).Trim(); _headers.InternalSet(text, text2, response: false); switch (text.ToLower(CultureInfo.InvariantCulture)) { case "accept": _acceptTypes = new List(text2.SplitHeaderValue(',')).ToArray(); break; case "accept-language": _userLanguages = text2.Split(new char[1] { ',' }); break; case "content-length": { if (long.TryParse(text2, out var result) && result >= 0) { _contentLength = result; _contentLengthSet = true; } else { _context.ErrorMessage = "Invalid Content-Length header"; } break; } case "content-type": try { _contentEncoding = HttpUtility.GetEncoding(text2); break; } catch { _context.ErrorMessage = "Invalid Content-Type header"; break; } case "referer": _referer = text2.ToUri(); break; } } internal void FinishInitialization() { string text = _headers["Host"]; bool flag = text == null || text.Length == 0; if (_version > HttpVersion.Version10 && flag) { _context.ErrorMessage = "Invalid Host header"; return; } if (flag) { text = UserHostAddress; } _url = HttpUtility.CreateRequestUrl(_uri, text, IsWebSocketRequest, IsSecureConnection); if (_url == null) { _context.ErrorMessage = "Invalid request url"; return; } string text2 = Headers["Transfer-Encoding"]; if (_version > HttpVersion.Version10 && text2 != null && text2.Length > 0) { _chunked = text2.ToLower() == "chunked"; if (!_chunked) { _context.ErrorMessage = string.Empty; _context.ErrorStatus = 501; return; } } if (!_chunked && !_contentLengthSet) { string text3 = _method.ToLower(); if (text3 == "post" || text3 == "put") { _context.ErrorMessage = string.Empty; _context.ErrorStatus = 411; return; } } string text4 = Headers["Expect"]; if (text4 != null && text4.Length > 0 && text4.ToLower() == "100-continue") { _context.Connection.GetResponseStream().InternalWrite(_100continue, 0, _100continue.Length); } } internal bool FlushInput() { if (!HasEntityBody) { return true; } int num = 2048; if (_contentLength > 0) { num = (int)Math.Min(_contentLength, num); } byte[] buffer = new byte[num]; while (true) { try { IAsyncResult asyncResult = InputStream.BeginRead(buffer, 0, num, null, null); if (!asyncResult.IsCompleted && !asyncResult.AsyncWaitHandle.WaitOne(100)) { return false; } if (InputStream.EndRead(asyncResult) <= 0) { return true; } } catch { return false; } } } internal void SetRequestLine(string requestLine) { string[] array = requestLine.Split(new char[1] { ' ' }, 3); if (array.Length != 3) { _context.ErrorMessage = "Invalid request line (parts)"; return; } _method = array[0]; if (!_method.IsToken()) { _context.ErrorMessage = "Invalid request line (method)"; return; } _uri = array[1]; string text = array[2]; if (text.Length != 8 || !text.StartsWith("HTTP/") || !tryCreateVersion(text.Substring(5), out _version) || _version.Major < 1) { _context.ErrorMessage = "Invalid request line (version)"; } } public IAsyncResult BeginGetClientCertificate(AsyncCallback requestCallback, object state) { throw new NotImplementedException(); } public X509Certificate2 EndGetClientCertificate(IAsyncResult asyncResult) { throw new NotImplementedException(); } public X509Certificate2 GetClientCertificate() { throw new NotImplementedException(); } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(64); stringBuilder.AppendFormat("{0} {1} HTTP/{2}\r\n", _method, _uri, _version); stringBuilder.Append(_headers.ToString()); return stringBuilder.ToString(); } } public sealed class HttpListenerResponse : IDisposable { private bool _closeConnection; private Encoding _contentEncoding; private long _contentLength; private string _contentType; private HttpListenerContext _context; private CookieCollection _cookies; private bool _disposed; private WebHeaderCollection _headers; private bool _headersSent; private bool _keepAlive; private string _location; private ResponseStream _outputStream; private bool _sendChunked; private int _statusCode; private string _statusDescription; private Version _version; internal bool CloseConnection { get { return _closeConnection; } set { _closeConnection = value; } } internal bool HeadersSent { get { return _headersSent; } set { _headersSent = value; } } public Encoding ContentEncoding { get { return _contentEncoding; } set { checkDisposed(); _contentEncoding = value; } } public long ContentLength64 { get { return _contentLength; } set { checkDisposedOrHeadersSent(); if (value < 0) { throw new ArgumentOutOfRangeException("Less than zero.", "value"); } _contentLength = value; } } public string ContentType { get { return _contentType; } set { checkDisposed(); if (value != null && value.Length == 0) { throw new ArgumentException("An empty string.", "value"); } _contentType = value; } } public CookieCollection Cookies { get { return _cookies ?? (_cookies = new CookieCollection()); } set { _cookies = value; } } public WebHeaderCollection Headers { get { return _headers ?? (_headers = new WebHeaderCollection(HttpHeaderType.Response, internallyUsed: false)); } set { if (value != null && value.State != HttpHeaderType.Response) { throw new InvalidOperationException("The specified headers aren't valid for a response."); } _headers = value; } } public bool KeepAlive { get { return _keepAlive; } set { checkDisposedOrHeadersSent(); _keepAlive = value; } } public Stream OutputStream { get { checkDisposed(); return _outputStream ?? (_outputStream = _context.Connection.GetResponseStream()); } } public Version ProtocolVersion { get { return _version; } set { checkDisposedOrHeadersSent(); if (value == null) { throw new ArgumentNullException("value"); } if (value.Major != 1 || (value.Minor != 0 && value.Minor != 1)) { throw new ArgumentException("Not 1.0 or 1.1.", "value"); } _version = value; } } public string RedirectLocation { get { return _location; } set { checkDisposed(); if (value == null) { _location = null; return; } Uri result = null; if (!value.MaybeUri() || !Uri.TryCreate(value, UriKind.Absolute, out result)) { throw new ArgumentException("Not an absolute URL.", "value"); } _location = value; } } public bool SendChunked { get { return _sendChunked; } set { checkDisposedOrHeadersSent(); _sendChunked = value; } } public int StatusCode { get { return _statusCode; } set { checkDisposedOrHeadersSent(); if (value < 100 || value > 999) { throw new ProtocolViolationException("A value isn't between 100 and 999 inclusive."); } _statusCode = value; _statusDescription = value.GetStatusDescription(); } } public string StatusDescription { get { return _statusDescription; } set { checkDisposedOrHeadersSent(); if (value == null || value.Length == 0) { _statusDescription = _statusCode.GetStatusDescription(); return; } if (!value.IsText() || value.IndexOfAny(new char[2] { '\r', '\n' }) > -1) { throw new ArgumentException("Contains invalid characters.", "value"); } _statusDescription = value; } } internal HttpListenerResponse(HttpListenerContext context) { _context = context; _keepAlive = true; _statusCode = 200; _statusDescription = "OK"; _version = HttpVersion.Version11; } private bool canAddOrUpdate(Cookie cookie) { if (_cookies == null || _cookies.Count == 0) { return true; } List list = findCookie(cookie).ToList(); if (list.Count == 0) { return true; } int version = cookie.Version; foreach (Cookie item in list) { if (item.Version == version) { return true; } } return false; } private void checkDisposed() { if (_disposed) { throw new ObjectDisposedException(GetType().ToString()); } } private void checkDisposedOrHeadersSent() { if (_disposed) { throw new ObjectDisposedException(GetType().ToString()); } if (_headersSent) { throw new InvalidOperationException("Cannot be changed after the headers are sent."); } } private void close(bool force) { _disposed = true; _context.Connection.Close(force); } private IEnumerable findCookie(Cookie cookie) { string name = cookie.Name; string domain = cookie.Domain; string path = cookie.Path; if (_cookies == null) { yield break; } foreach (Cookie cookie2 in _cookies) { if (cookie2.Name.Equals(name, StringComparison.OrdinalIgnoreCase) && cookie2.Domain.Equals(domain, StringComparison.OrdinalIgnoreCase) && cookie2.Path.Equals(path, StringComparison.Ordinal)) { yield return cookie2; } } } internal WebHeaderCollection WriteHeadersTo(MemoryStream destination) { WebHeaderCollection webHeaderCollection = new WebHeaderCollection(HttpHeaderType.Response, internallyUsed: true); if (_headers != null) { webHeaderCollection.Add(_headers); } if (_contentType != null) { string value = ((_contentType.IndexOf("charset=", StringComparison.Ordinal) == -1 && _contentEncoding != null) ? $"{_contentType}; charset={_contentEncoding.WebName}" : _contentType); webHeaderCollection.InternalSet("Content-Type", value, response: true); } if (webHeaderCollection["Server"] == null) { webHeaderCollection.InternalSet("Server", "websocket-sharp/1.0", response: true); } CultureInfo invariantCulture = CultureInfo.InvariantCulture; if (webHeaderCollection["Date"] == null) { webHeaderCollection.InternalSet("Date", DateTime.UtcNow.ToString("r", invariantCulture), response: true); } if (!_sendChunked) { webHeaderCollection.InternalSet("Content-Length", _contentLength.ToString(invariantCulture), response: true); } else { webHeaderCollection.InternalSet("Transfer-Encoding", "chunked", response: true); } bool num = !_context.Request.KeepAlive || !_keepAlive || _statusCode == 400 || _statusCode == 408 || _statusCode == 411 || _statusCode == 413 || _statusCode == 414 || _statusCode == 500 || _statusCode == 503; int reuses = _context.Connection.Reuses; if (num || reuses >= 100) { webHeaderCollection.InternalSet("Connection", "close", response: true); } else { webHeaderCollection.InternalSet("Keep-Alive", $"timeout=15,max={100 - reuses}", response: true); if (_context.Request.ProtocolVersion < HttpVersion.Version11) { webHeaderCollection.InternalSet("Connection", "keep-alive", response: true); } } if (_location != null) { webHeaderCollection.InternalSet("Location", _location, response: true); } if (_cookies != null) { foreach (Cookie cookie in _cookies) { webHeaderCollection.InternalSet("Set-Cookie", cookie.ToResponseString(), response: true); } } Encoding encoding = _contentEncoding ?? Encoding.Default; StreamWriter streamWriter = new StreamWriter(destination, encoding, 256); streamWriter.Write("HTTP/{0} {1} {2}\r\n", _version, _statusCode, _statusDescription); streamWriter.Write(webHeaderCollection.ToStringMultiValue(response: true)); streamWriter.Flush(); destination.Position = encoding.GetPreamble().Length; return webHeaderCollection; } public void Abort() { if (!_disposed) { close(force: true); } } public void AddHeader(string name, string value) { Headers.Set(name, value); } public void AppendCookie(Cookie cookie) { Cookies.Add(cookie); } public void AppendHeader(string name, string value) { Headers.Add(name, value); } public void Close() { if (!_disposed) { close(force: false); } } public void Close(byte[] responseEntity, bool willBlock) { checkDisposed(); if (responseEntity == null) { throw new ArgumentNullException("responseEntity"); } int count = responseEntity.Length; Stream output = OutputStream; if (willBlock) { output.Write(responseEntity, 0, count); close(force: false); return; } output.BeginWrite(responseEntity, 0, count, delegate(IAsyncResult ar) { output.EndWrite(ar); close(force: false); }, null); } public void CopyFrom(HttpListenerResponse templateResponse) { if (templateResponse == null) { throw new ArgumentNullException("templateResponse"); } if (templateResponse._headers != null) { if (_headers != null) { _headers.Clear(); } Headers.Add(templateResponse._headers); } else if (_headers != null) { _headers = null; } _contentLength = templateResponse._contentLength; _statusCode = templateResponse._statusCode; _statusDescription = templateResponse._statusDescription; _keepAlive = templateResponse._keepAlive; _version = templateResponse._version; } public void Redirect(string url) { checkDisposedOrHeadersSent(); if (url == null) { throw new ArgumentNullException("url"); } Uri result = null; if (!url.MaybeUri() || !Uri.TryCreate(url, UriKind.Absolute, out result)) { throw new ArgumentException("Not an absolute URL.", "url"); } _location = url; _statusCode = 302; _statusDescription = "Found"; } public void SetCookie(Cookie cookie) { if (cookie == null) { throw new ArgumentNullException("cookie"); } if (!canAddOrUpdate(cookie)) { throw new ArgumentException("Cannot be replaced.", "cookie"); } Cookies.Add(cookie); } void IDisposable.Dispose() { if (!_disposed) { close(force: true); } } } public enum HttpRequestHeader { CacheControl, Connection, Date, KeepAlive, Pragma, Trailer, TransferEncoding, Upgrade, Via, Warning, Allow, ContentLength, ContentType, ContentEncoding, ContentLanguage, ContentLocation, ContentMd5, ContentRange, Expires, LastModified, Accept, AcceptCharset, AcceptEncoding, AcceptLanguage, Authorization, Cookie, Expect, From, Host, IfMatch, IfModifiedSince, IfNoneMatch, IfRange, IfUnmodifiedSince, MaxForwards, ProxyAuthorization, Referer, Range, Te, Translate, UserAgent, SecWebSocketKey, SecWebSocketExtensions, SecWebSocketProtocol, SecWebSocketVersion } public enum HttpResponseHeader { CacheControl, Connection, Date, KeepAlive, Pragma, Trailer, TransferEncoding, Upgrade, Via, Warning, Allow, ContentLength, ContentType, ContentEncoding, ContentLanguage, ContentLocation, ContentMd5, ContentRange, Expires, LastModified, AcceptRanges, Age, ETag, Location, ProxyAuthenticate, RetryAfter, Server, SetCookie, Vary, WwwAuthenticate, SecWebSocketExtensions, SecWebSocketAccept, SecWebSocketProtocol, SecWebSocketVersion } public enum HttpStatusCode { Continue = 100, SwitchingProtocols = 101, OK = 200, Created = 201, Accepted = 202, NonAuthoritativeInformation = 203, NoContent = 204, ResetContent = 205, PartialContent = 206, MultipleChoices = 300, Ambiguous = 300, MovedPermanently = 301, Moved = 301, Found = 302, Redirect = 302, SeeOther = 303, RedirectMethod = 303, NotModified = 304, UseProxy = 305, Unused = 306, TemporaryRedirect = 307, RedirectKeepVerb = 307, BadRequest = 400, Unauthorized = 401, PaymentRequired = 402, Forbidden = 403, NotFound = 404, MethodNotAllowed = 405, NotAcceptable = 406, ProxyAuthenticationRequired = 407, RequestTimeout = 408, Conflict = 409, Gone = 410, LengthRequired = 411, PreconditionFailed = 412, RequestEntityTooLarge = 413, RequestUriTooLong = 414, UnsupportedMediaType = 415, RequestedRangeNotSatisfiable = 416, ExpectationFailed = 417, InternalServerError = 500, NotImplemented = 501, BadGateway = 502, ServiceUnavailable = 503, GatewayTimeout = 504, HttpVersionNotSupported = 505 } internal class HttpStreamAsyncResult : IAsyncResult { private byte[] _buffer; private AsyncCallback _callback; private bool _completed; private int _count; private Exception _exception; private int _offset; private object _state; private object _sync; private int _syncRead; private ManualResetEvent _waitHandle; internal byte[] Buffer { get { return _buffer; } set { _buffer = value; } } internal int Count { get { return _count; } set { _count = value; } } internal Exception Exception => _exception; internal bool HasException => _exception != null; internal int Offset { get { return _offset; } set { _offset = value; } } internal int SyncRead { get { return _syncRead; } set { _syncRead = value; } } public object AsyncState => _state; public WaitHandle AsyncWaitHandle { get { lock (_sync) { return _waitHandle ?? (_waitHandle = new ManualResetEvent(_completed)); } } } public bool CompletedSynchronously => _syncRead == _count; public bool IsCompleted { get { lock (_sync) { return _completed; } } } internal HttpStreamAsyncResult(AsyncCallback callback, object state) { _callback = callback; _state = state; _sync = new object(); } internal void Complete() { lock (_sync) { if (_completed) { return; } _completed = true; if (_waitHandle != null) { _waitHandle.Set(); } if (_callback != null) { _callback.BeginInvoke(this, delegate(IAsyncResult ar) { _callback.EndInvoke(ar); }, null); } } } internal void Complete(Exception exception) { _exception = exception; Complete(); } } internal sealed class HttpUtility { private static Dictionary _entities; private static char[] _hexChars = "0123456789abcdef".ToCharArray(); private static object _sync = new object(); private static int getChar(byte[] bytes, int offset, int length) { int num = 0; int num2 = length + offset; for (int i = offset; i < num2; i++) { int @int = getInt(bytes[i]); if (@int == -1) { return -1; } num = (num << 4) + @int; } return num; } private static int getChar(string s, int offset, int length) { int num = 0; int num2 = length + offset; for (int i = offset; i < num2; i++) { char c = s[i]; if (c > '\u007f') { return -1; } int @int = getInt((byte)c); if (@int == -1) { return -1; } num = (num << 4) + @int; } return num; } private static char[] getChars(MemoryStream buffer, Encoding encoding) { return encoding.GetChars(buffer.GetBuffer(), 0, (int)buffer.Length); } private static Dictionary getEntities() { lock (_sync) { if (_entities == null) { initEntities(); } return _entities; } } private static int getInt(byte b) { char c = (char)b; if (c < '0' || c > '9') { if (c < 'a' || c > 'f') { if (c < 'A' || c > 'F') { return -1; } return c - 65 + 10; } return c - 97 + 10; } return c - 48; } private static void initEntities() { _entities = new Dictionary(); _entities.Add("nbsp", '\u00a0'); _entities.Add("iexcl", '¡'); _entities.Add("cent", '¢'); _entities.Add("pound", '£'); _entities.Add("curren", '¤'); _entities.Add("yen", '¥'); _entities.Add("brvbar", '¦'); _entities.Add("sect", '§'); _entities.Add("uml", '\u00a8'); _entities.Add("copy", '©'); _entities.Add("ordf", 'ª'); _entities.Add("laquo", '«'); _entities.Add("not", '¬'); _entities.Add("shy", '\u00ad'); _entities.Add("reg", '®'); _entities.Add("macr", '\u00af'); _entities.Add("deg", '°'); _entities.Add("plusmn", '±'); _entities.Add("sup2", '²'); _entities.Add("sup3", '³'); _entities.Add("acute", '\u00b4'); _entities.Add("micro", 'µ'); _entities.Add("para", '¶'); _entities.Add("middot", '·'); _entities.Add("cedil", '\u00b8'); _entities.Add("sup1", '¹'); _entities.Add("ordm", 'º'); _entities.Add("raquo", '»'); _entities.Add("frac14", '¼'); _entities.Add("frac12", '½'); _entities.Add("frac34", '¾'); _entities.Add("iquest", '¿'); _entities.Add("Agrave", 'À'); _entities.Add("Aacute", 'Á'); _entities.Add("Acirc", 'Â'); _entities.Add("Atilde", 'Ã'); _entities.Add("Auml", 'Ä'); _entities.Add("Aring", 'Å'); _entities.Add("AElig", 'Æ'); _entities.Add("Ccedil", 'Ç'); _entities.Add("Egrave", 'È'); _entities.Add("Eacute", 'É'); _entities.Add("Ecirc", 'Ê'); _entities.Add("Euml", 'Ë'); _entities.Add("Igrave", 'Ì'); _entities.Add("Iacute", 'Í'); _entities.Add("Icirc", 'Î'); _entities.Add("Iuml", 'Ï'); _entities.Add("ETH", 'Ð'); _entities.Add("Ntilde", 'Ñ'); _entities.Add("Ograve", 'Ò'); _entities.Add("Oacute", 'Ó'); _entities.Add("Ocirc", 'Ô'); _entities.Add("Otilde", 'Õ'); _entities.Add("Ouml", 'Ö'); _entities.Add("times", '×'); _entities.Add("Oslash", 'Ø'); _entities.Add("Ugrave", 'Ù'); _entities.Add("Uacute", 'Ú'); _entities.Add("Ucirc", 'Û'); _entities.Add("Uuml", 'Ü'); _entities.Add("Yacute", 'Ý'); _entities.Add("THORN", 'Þ'); _entities.Add("szlig", 'ß'); _entities.Add("agrave", 'à'); _entities.Add("aacute", 'á'); _entities.Add("acirc", 'â'); _entities.Add("atilde", 'ã'); _entities.Add("auml", 'ä'); _entities.Add("aring", 'å'); _entities.Add("aelig", 'æ'); _entities.Add("ccedil", 'ç'); _entities.Add("egrave", 'è'); _entities.Add("eacute", 'é'); _entities.Add("ecirc", 'ê'); _entities.Add("euml", 'ë'); _entities.Add("igrave", 'ì'); _entities.Add("iacute", 'í'); _entities.Add("icirc", 'î'); _entities.Add("iuml", 'ï'); _entities.Add("eth", 'ð'); _entities.Add("ntilde", 'ñ'); _entities.Add("ograve", 'ò'); _entities.Add("oacute", 'ó'); _entities.Add("ocirc", 'ô'); _entities.Add("otilde", 'õ'); _entities.Add("ouml", 'ö'); _entities.Add("divide", '÷'); _entities.Add("oslash", 'ø'); _entities.Add("ugrave", 'ù'); _entities.Add("uacute", 'ú'); _entities.Add("ucirc", 'û'); _entities.Add("uuml", 'ü'); _entities.Add("yacute", 'ý'); _entities.Add("thorn", 'þ'); _entities.Add("yuml", 'ÿ'); _entities.Add("fnof", 'ƒ'); _entities.Add("Alpha", 'Α'); _entities.Add("Beta", 'Β'); _entities.Add("Gamma", 'Γ'); _entities.Add("Delta", 'Δ'); _entities.Add("Epsilon", 'Ε'); _entities.Add("Zeta", 'Ζ'); _entities.Add("Eta", 'Η'); _entities.Add("Theta", 'Θ'); _entities.Add("Iota", 'Ι'); _entities.Add("Kappa", 'Κ'); _entities.Add("Lambda", 'Λ'); _entities.Add("Mu", 'Μ'); _entities.Add("Nu", 'Ν'); _entities.Add("Xi", 'Ξ'); _entities.Add("Omicron", 'Ο'); _entities.Add("Pi", 'Π'); _entities.Add("Rho", 'Ρ'); _entities.Add("Sigma", 'Σ'); _entities.Add("Tau", 'Τ'); _entities.Add("Upsilon", 'Υ'); _entities.Add("Phi", 'Φ'); _entities.Add("Chi", 'Χ'); _entities.Add("Psi", 'Ψ'); _entities.Add("Omega", 'Ω'); _entities.Add("alpha", 'α'); _entities.Add("beta", 'β'); _entities.Add("gamma", 'γ'); _entities.Add("delta", 'δ'); _entities.Add("epsilon", 'ε'); _entities.Add("zeta", 'ζ'); _entities.Add("eta", 'η'); _entities.Add("theta", 'θ'); _entities.Add("iota", 'ι'); _entities.Add("kappa", 'κ'); _entities.Add("lambda", 'λ'); _entities.Add("mu", 'μ'); _entities.Add("nu", 'ν'); _entities.Add("xi", 'ξ'); _entities.Add("omicron", 'ο'); _entities.Add("pi", 'π'); _entities.Add("rho", 'ρ'); _entities.Add("sigmaf", 'ς'); _entities.Add("sigma", 'σ'); _entities.Add("tau", 'τ'); _entities.Add("upsilon", 'υ'); _entities.Add("phi", 'φ'); _entities.Add("chi", 'χ'); _entities.Add("psi", 'ψ'); _entities.Add("omega", 'ω'); _entities.Add("thetasym", 'ϑ'); _entities.Add("upsih", 'ϒ'); _entities.Add("piv", 'ϖ'); _entities.Add("bull", '•'); _entities.Add("hellip", '…'); _entities.Add("prime", '′'); _entities.Add("Prime", '″'); _entities.Add("oline", '‾'); _entities.Add("frasl", '⁄'); _entities.Add("weierp", '℘'); _entities.Add("image", 'ℑ'); _entities.Add("real", 'ℜ'); _entities.Add("trade", '™'); _entities.Add("alefsym", 'ℵ'); _entities.Add("larr", '←'); _entities.Add("uarr", '↑'); _entities.Add("rarr", '→'); _entities.Add("darr", '↓'); _entities.Add("harr", '↔'); _entities.Add("crarr", '↵'); _entities.Add("lArr", '⇐'); _entities.Add("uArr", '⇑'); _entities.Add("rArr", '⇒'); _entities.Add("dArr", '⇓'); _entities.Add("hArr", '⇔'); _entities.Add("forall", '∀'); _entities.Add("part", '∂'); _entities.Add("exist", '∃'); _entities.Add("empty", '∅'); _entities.Add("nabla", '∇'); _entities.Add("isin", '∈'); _entities.Add("notin", '∉'); _entities.Add("ni", '∋'); _entities.Add("prod", '∏'); _entities.Add("sum", '∑'); _entities.Add("minus", '−'); _entities.Add("lowast", '∗'); _entities.Add("radic", '√'); _entities.Add("prop", '∝'); _entities.Add("infin", '∞'); _entities.Add("ang", '∠'); _entities.Add("and", '∧'); _entities.Add("or", '∨'); _entities.Add("cap", '∩'); _entities.Add("cup", '∪'); _entities.Add("int", '∫'); _entities.Add("there4", '∴'); _entities.Add("sim", '∼'); _entities.Add("cong", '≅'); _entities.Add("asymp", '≈'); _entities.Add("ne", '≠'); _entities.Add("equiv", '≡'); _entities.Add("le", '≤'); _entities.Add("ge", '≥'); _entities.Add("sub", '⊂'); _entities.Add("sup", '⊃'); _entities.Add("nsub", '⊄'); _entities.Add("sube", '⊆'); _entities.Add("supe", '⊇'); _entities.Add("oplus", '⊕'); _entities.Add("otimes", '⊗'); _entities.Add("perp", '⊥'); _entities.Add("sdot", '⋅'); _entities.Add("lceil", '⌈'); _entities.Add("rceil", '⌉'); _entities.Add("lfloor", '⌊'); _entities.Add("rfloor", '⌋'); _entities.Add("lang", '〈'); _entities.Add("rang", '〉'); _entities.Add("loz", '◊'); _entities.Add("spades", '♠'); _entities.Add("clubs", '♣'); _entities.Add("hearts", '♥'); _entities.Add("diams", '♦'); _entities.Add("quot", '"'); _entities.Add("amp", '&'); _entities.Add("lt", '<'); _entities.Add("gt", '>'); _entities.Add("OElig", 'Œ'); _entities.Add("oelig", 'œ'); _entities.Add("Scaron", 'Š'); _entities.Add("scaron", 'š'); _entities.Add("Yuml", 'Ÿ'); _entities.Add("circ", 'ˆ'); _entities.Add("tilde", '\u02dc'); _entities.Add("ensp", '\u2002'); _entities.Add("emsp", '\u2003'); _entities.Add("thinsp", '\u2009'); _entities.Add("zwnj", '\u200c'); _entities.Add("zwj", '\u200d'); _entities.Add("lrm", '\u200e'); _entities.Add("rlm", '\u200f'); _entities.Add("ndash", '–'); _entities.Add("mdash", '—'); _entities.Add("lsquo", '‘'); _entities.Add("rsquo", '’'); _entities.Add("sbquo", '‚'); _entities.Add("ldquo", '“'); _entities.Add("rdquo", '”'); _entities.Add("bdquo", '„'); _entities.Add("dagger", '†'); _entities.Add("Dagger", '‡'); _entities.Add("permil", '‰'); _entities.Add("lsaquo", '‹'); _entities.Add("rsaquo", '›'); _entities.Add("euro", '€'); } private static bool notEncoded(char c) { if (c != '!' && c != '\'' && c != '(' && c != ')' && c != '*' && c != '-' && c != '.') { return c == '_'; } return true; } private static void urlEncode(char c, Stream result, bool unicode) { if (c > 'ÿ') { result.WriteByte(37); result.WriteByte(117); int num = (int)c >> 12; result.WriteByte((byte)_hexChars[num]); num = ((int)c >> 8) & 0xF; result.WriteByte((byte)_hexChars[num]); num = ((int)c >> 4) & 0xF; result.WriteByte((byte)_hexChars[num]); num = c & 0xF; result.WriteByte((byte)_hexChars[num]); } else if (c > ' ' && notEncoded(c)) { result.WriteByte((byte)c); } else if (c == ' ') { result.WriteByte(43); } else if (c < '0' || (c < 'A' && c > '9') || (c > 'Z' && c < 'a') || c > 'z') { if (unicode && c > '\u007f') { result.WriteByte(37); result.WriteByte(117); result.WriteByte(48); result.WriteByte(48); } else { result.WriteByte(37); } int num2 = (int)c >> 4; result.WriteByte((byte)_hexChars[num2]); num2 = c & 0xF; result.WriteByte((byte)_hexChars[num2]); } else { result.WriteByte((byte)c); } } private static void urlPathEncode(char c, Stream result) { if (c < '!' || c > '~') { byte[] bytes = Encoding.UTF8.GetBytes(c.ToString()); foreach (byte num in bytes) { result.WriteByte(37); int num2 = num >> 4; result.WriteByte((byte)_hexChars[num2]); num2 = num & 0xF; result.WriteByte((byte)_hexChars[num2]); } } else if (c == ' ') { result.WriteByte(37); result.WriteByte(50); result.WriteByte(48); } else { result.WriteByte((byte)c); } } private static void writeCharBytes(char c, IList buffer, Encoding encoding) { if (c > 'ÿ') { byte[] bytes = encoding.GetBytes(new char[1] { c }); foreach (byte b in bytes) { buffer.Add(b); } } else { buffer.Add((byte)c); } } internal static Uri CreateRequestUrl(string requestUri, string host, bool websocketRequest, bool secure) { if (requestUri == null || requestUri.Length == 0 || host == null || host.Length == 0) { return null; } string text = null; string arg = null; if (requestUri.StartsWith("/")) { arg = requestUri; } else if (requestUri.MaybeUri()) { if (!Uri.TryCreate(requestUri, UriKind.Absolute, out Uri result) || ((!(text = result.Scheme).StartsWith("http") || websocketRequest) && !(text.StartsWith("ws") && websocketRequest))) { return null; } host = result.Authority; arg = result.PathAndQuery; } else if (!(requestUri == "*")) { host = requestUri; } if (text == null) { text = (websocketRequest ? "ws" : "http") + (secure ? "s" : string.Empty); } if (host.IndexOf(':') == -1) { host = string.Format("{0}:{1}", host, (text == "http" || text == "ws") ? 80 : 443); } if (!Uri.TryCreate($"{text}://{host}{arg}", UriKind.Absolute, out Uri result2)) { return null; } return result2; } internal static IPrincipal CreateUser(string response, AuthenticationSchemes scheme, string realm, string method, Func credentialsFinder) { if (response == null || response.Length == 0) { return null; } if (credentialsFinder == null) { return null; } if (scheme != AuthenticationSchemes.Basic && scheme != AuthenticationSchemes.Digest) { return null; } if (scheme == AuthenticationSchemes.Digest) { if (realm == null || realm.Length == 0) { return null; } if (method == null || method.Length == 0) { return null; } } if (!response.StartsWith(scheme.ToString(), StringComparison.OrdinalIgnoreCase)) { return null; } AuthenticationResponse authenticationResponse = AuthenticationResponse.Parse(response); if (authenticationResponse == null) { return null; } IIdentity identity = authenticationResponse.ToIdentity(); if (identity == null) { return null; } NetworkCredential networkCredential = null; try { networkCredential = credentialsFinder(identity); } catch { } if (networkCredential == null) { return null; } if (scheme == AuthenticationSchemes.Basic && ((HttpBasicIdentity)identity).Password != networkCredential.Password) { return null; } if (scheme == AuthenticationSchemes.Digest && !((HttpDigestIdentity)identity).IsValid(networkCredential.Password, realm, method, null)) { return null; } return new GenericPrincipal(identity, networkCredential.Roles); } internal static Encoding GetEncoding(string contentType) { string[] array = contentType.Split(new char[1] { ';' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.StartsWith("charset", StringComparison.OrdinalIgnoreCase)) { return Encoding.GetEncoding(text.GetValue('=', unquote: true)); } } return null; } internal static NameValueCollection InternalParseQueryString(string query, Encoding encoding) { int length; if (query == null || (length = query.Length) == 0 || (length == 1 && query[0] == '?')) { return new NameValueCollection(1); } if (query[0] == '?') { query = query.Substring(1); } QueryStringCollection queryStringCollection = new QueryStringCollection(); string[] array = query.Split(new char[1] { '&' }); foreach (string text in array) { int num = text.IndexOf('='); if (num > -1) { string name = UrlDecode(text.Substring(0, num), encoding); string value = ((text.Length > num + 1) ? UrlDecode(text.Substring(num + 1), encoding) : string.Empty); queryStringCollection.Add(name, value); } else { queryStringCollection.Add(null, UrlDecode(text, encoding)); } } return queryStringCollection; } internal static string InternalUrlDecode(byte[] bytes, int offset, int count, Encoding encoding) { StringBuilder stringBuilder = new StringBuilder(); using (MemoryStream memoryStream = new MemoryStream()) { int num = count + offset; for (int i = offset; i < num; i++) { if (bytes[i] == 37 && i + 2 < count && bytes[i + 1] != 37) { int @char; if (bytes[i + 1] == 117 && i + 5 < num) { if (memoryStream.Length > 0) { stringBuilder.Append(getChars(memoryStream, encoding)); memoryStream.SetLength(0L); } @char = getChar(bytes, i + 2, 4); if (@char != -1) { stringBuilder.Append((char)@char); i += 5; continue; } } else if ((@char = getChar(bytes, i + 1, 2)) != -1) { memoryStream.WriteByte((byte)@char); i += 2; continue; } } if (memoryStream.Length > 0) { stringBuilder.Append(getChars(memoryStream, encoding)); memoryStream.SetLength(0L); } if (bytes[i] == 43) { stringBuilder.Append(' '); } else { stringBuilder.Append((char)bytes[i]); } } if (memoryStream.Length > 0) { stringBuilder.Append(getChars(memoryStream, encoding)); } } return stringBuilder.ToString(); } internal static byte[] InternalUrlDecodeToBytes(byte[] bytes, int offset, int count) { using MemoryStream memoryStream = new MemoryStream(); int num = offset + count; for (int i = offset; i < num; i++) { char c = (char)bytes[i]; switch (c) { case '+': c = ' '; break; case '%': if (i < num - 2) { int @char = getChar(bytes, i + 1, 2); if (@char != -1) { c = (char)@char; i += 2; } } break; } memoryStream.WriteByte((byte)c); } memoryStream.Close(); return memoryStream.ToArray(); } internal static byte[] InternalUrlEncodeToBytes(byte[] bytes, int offset, int count) { using MemoryStream memoryStream = new MemoryStream(); int num = offset + count; for (int i = offset; i < num; i++) { urlEncode((char)bytes[i], memoryStream, unicode: false); } memoryStream.Close(); return memoryStream.ToArray(); } internal static byte[] InternalUrlEncodeUnicodeToBytes(string s) { using MemoryStream memoryStream = new MemoryStream(); for (int i = 0; i < s.Length; i++) { urlEncode(s[i], memoryStream, unicode: true); } memoryStream.Close(); return memoryStream.ToArray(); } public static string HtmlAttributeEncode(string s) { if (s == null || s.Length == 0 || !s.Contains('&', '"', '<', '>')) { return s; } StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < s.Length; i++) { char c = s[i]; stringBuilder.Append(c switch { '>' => ">", '<' => "<", '"' => """, '&' => "&", _ => c.ToString(), }); } return stringBuilder.ToString(); } public static void HtmlAttributeEncode(string s, TextWriter output) { if (output == null) { throw new ArgumentNullException("output"); } output.Write(HtmlAttributeEncode(s)); } public static string HtmlDecode(string s) { if (s == null || s.Length == 0 || !Ext.Contains(s, '&')) { return s; } StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder2 = new StringBuilder(); int num = 0; int num2 = 0; bool flag = false; foreach (char c in s) { if (num == 0) { if (c == '&') { stringBuilder.Append(c); num = 1; } else { stringBuilder2.Append(c); } continue; } if (c == '&') { num = 1; if (flag) { stringBuilder.Append(num2.ToString(CultureInfo.InvariantCulture)); flag = false; } stringBuilder2.Append(stringBuilder.ToString()); stringBuilder.Length = 0; stringBuilder.Append('&'); continue; } switch (num) { case 1: if (c == ';') { num = 0; stringBuilder2.Append(stringBuilder.ToString()); stringBuilder2.Append(c); stringBuilder.Length = 0; } else { num2 = 0; num = ((c == '#') ? 3 : 2); stringBuilder.Append(c); } break; case 2: stringBuilder.Append(c); if (c == ';') { string text = stringBuilder.ToString(); Dictionary entities = getEntities(); if (text.Length > 1 && entities.ContainsKey(text.Substring(1, text.Length - 2))) { text = entities[text.Substring(1, text.Length - 2)].ToString(); } stringBuilder2.Append(text); num = 0; stringBuilder.Length = 0; } break; case 3: if (c == ';') { if (num2 > 65535) { stringBuilder2.Append("&#"); stringBuilder2.Append(num2.ToString(CultureInfo.InvariantCulture)); stringBuilder2.Append(";"); } else { stringBuilder2.Append((char)num2); } num = 0; stringBuilder.Length = 0; flag = false; } else if (char.IsDigit(c)) { num2 = num2 * 10 + (c - 48); flag = true; } else { num = 2; if (flag) { stringBuilder.Append(num2.ToString(CultureInfo.InvariantCulture)); flag = false; } stringBuilder.Append(c); } break; } } if (stringBuilder.Length > 0) { stringBuilder2.Append(stringBuilder.ToString()); } else if (flag) { stringBuilder2.Append(num2.ToString(CultureInfo.InvariantCulture)); } return stringBuilder2.ToString(); } public static void HtmlDecode(string s, TextWriter output) { if (output == null) { throw new ArgumentNullException("output"); } output.Write(HtmlDecode(s)); } public static string HtmlEncode(string s) { if (s == null || s.Length == 0) { return s; } bool flag = false; string text = s; foreach (char c in text) { if (c == '&' || c == '"' || c == '<' || c == '>' || c > '\u009f') { flag = true; break; } } if (!flag) { return s; } StringBuilder stringBuilder = new StringBuilder(); text = s; foreach (char c2 in text) { if (c2 == '&') { stringBuilder.Append("&"); } else if (c2 == '"') { stringBuilder.Append("""); } else if (c2 == '<') { stringBuilder.Append("<"); } else if (c2 == '>') { stringBuilder.Append(">"); } else if (c2 > '\u009f') { stringBuilder.Append("&#"); int num = c2; stringBuilder.Append(num.ToString(CultureInfo.InvariantCulture)); stringBuilder.Append(";"); } else { stringBuilder.Append(c2); } } return stringBuilder.ToString(); } public static void HtmlEncode(string s, TextWriter output) { if (output == null) { throw new ArgumentNullException("output"); } output.Write(HtmlEncode(s)); } public static NameValueCollection ParseQueryString(string query) { return ParseQueryString(query, Encoding.UTF8); } public static NameValueCollection ParseQueryString(string query, Encoding encoding) { if (query == null) { throw new ArgumentNullException("query"); } return InternalParseQueryString(query, encoding ?? Encoding.UTF8); } public static string UrlDecode(string s) { return UrlDecode(s, Encoding.UTF8); } public static string UrlDecode(string s, Encoding encoding) { if (s == null || s.Length == 0 || !s.Contains('%', '+')) { return s; } if (encoding == null) { encoding = Encoding.UTF8; } List list = new List(); int length = s.Length; for (int i = 0; i < length; i++) { char c = s[i]; if (c == '%' && i + 2 < length && s[i + 1] != '%') { int @char; if (s[i + 1] == 'u' && i + 5 < length) { @char = getChar(s, i + 2, 4); if (@char != -1) { writeCharBytes((char)@char, list, encoding); i += 5; } else { writeCharBytes('%', list, encoding); } } else if ((@char = getChar(s, i + 1, 2)) != -1) { writeCharBytes((char)@char, list, encoding); i += 2; } else { writeCharBytes('%', list, encoding); } } else if (c == '+') { writeCharBytes(' ', list, encoding); } else { writeCharBytes(c, list, encoding); } } return encoding.GetString(list.ToArray()); } public static string UrlDecode(byte[] bytes, Encoding encoding) { if (bytes != null) { int count; if ((count = bytes.Length) != 0) { return InternalUrlDecode(bytes, 0, count, encoding ?? Encoding.UTF8); } return string.Empty; } return null; } public static string UrlDecode(byte[] bytes, int offset, int count, Encoding encoding) { if (bytes == null) { return null; } int num = bytes.Length; if (num == 0 || count == 0) { return string.Empty; } if (offset < 0 || offset >= num) { throw new ArgumentOutOfRangeException("offset"); } if (count < 0 || count > num - offset) { throw new ArgumentOutOfRangeException("count"); } return InternalUrlDecode(bytes, offset, count, encoding ?? Encoding.UTF8); } public static byte[] UrlDecodeToBytes(byte[] bytes) { int count; if (bytes == null || (count = bytes.Length) <= 0) { return bytes; } return InternalUrlDecodeToBytes(bytes, 0, count); } public static byte[] UrlDecodeToBytes(string s) { return UrlDecodeToBytes(s, Encoding.UTF8); } public static byte[] UrlDecodeToBytes(string s, Encoding encoding) { if (s == null) { return null; } if (s.Length == 0) { return new byte[0]; } byte[] bytes = (encoding ?? Encoding.UTF8).GetBytes(s); return InternalUrlDecodeToBytes(bytes, 0, bytes.Length); } public static byte[] UrlDecodeToBytes(byte[] bytes, int offset, int count) { int num; if (bytes == null || (num = bytes.Length) == 0) { return bytes; } if (count == 0) { return new byte[0]; } if (offset < 0 || offset >= num) { throw new ArgumentOutOfRangeException("offset"); } if (count < 0 || count > num - offset) { throw new ArgumentOutOfRangeException("count"); } return InternalUrlDecodeToBytes(bytes, offset, count); } public static string UrlEncode(byte[] bytes) { if (bytes != null) { int count; if ((count = bytes.Length) != 0) { return Encoding.ASCII.GetString(InternalUrlEncodeToBytes(bytes, 0, count)); } return string.Empty; } return null; } public static string UrlEncode(string s) { return UrlEncode(s, Encoding.UTF8); } public static string UrlEncode(string s, Encoding encoding) { int length; if (s == null || (length = s.Length) == 0) { return s; } bool flag = false; foreach (char c in s) { if ((c < '0' || (c < 'A' && c > '9') || (c > 'Z' && c < 'a') || c > 'z') && !notEncoded(c)) { flag = true; break; } } if (!flag) { return s; } if (encoding == null) { encoding = Encoding.UTF8; } byte[] bytes = new byte[encoding.GetMaxByteCount(length)]; int bytes2 = encoding.GetBytes(s, 0, length, bytes, 0); return Encoding.ASCII.GetString(InternalUrlEncodeToBytes(bytes, 0, bytes2)); } public static string UrlEncode(byte[] bytes, int offset, int count) { byte[] array = UrlEncodeToBytes(bytes, offset, count); if (array != null) { if (array.Length != 0) { return Encoding.ASCII.GetString(array); } return string.Empty; } return null; } public static byte[] UrlEncodeToBytes(byte[] bytes) { int count; if (bytes == null || (count = bytes.Length) <= 0) { return bytes; } return InternalUrlEncodeToBytes(bytes, 0, count); } public static byte[] UrlEncodeToBytes(string s) { return UrlEncodeToBytes(s, Encoding.UTF8); } public static byte[] UrlEncodeToBytes(string s, Encoding encoding) { if (s == null) { return null; } if (s.Length == 0) { return new byte[0]; } byte[] bytes = (encoding ?? Encoding.UTF8).GetBytes(s); return InternalUrlEncodeToBytes(bytes, 0, bytes.Length); } public static byte[] UrlEncodeToBytes(byte[] bytes, int offset, int count) { int num; if (bytes == null || (num = bytes.Length) == 0) { return bytes; } if (count == 0) { return new byte[0]; } if (offset < 0 || offset >= num) { throw new ArgumentOutOfRangeException("offset"); } if (count < 0 || count > num - offset) { throw new ArgumentOutOfRangeException("count"); } return InternalUrlEncodeToBytes(bytes, offset, count); } public static string UrlEncodeUnicode(string s) { if (s == null || s.Length <= 0) { return s; } return Encoding.ASCII.GetString(InternalUrlEncodeUnicodeToBytes(s)); } public static byte[] UrlEncodeUnicodeToBytes(string s) { if (s != null) { if (s.Length != 0) { return InternalUrlEncodeUnicodeToBytes(s); } return new byte[0]; } return null; } public static string UrlPathEncode(string s) { if (s == null || s.Length == 0) { return s; } using MemoryStream memoryStream = new MemoryStream(); for (int i = 0; i < s.Length; i++) { urlPathEncode(s[i], memoryStream); } memoryStream.Close(); return Encoding.ASCII.GetString(memoryStream.ToArray()); } } public class HttpVersion { public static readonly Version Version10 = new Version(1, 0); public static readonly Version Version11 = new Version(1, 1); } internal enum InputChunkState { None, Data, DataEnded, Trailer, End } internal enum InputState { RequestLine, Headers } internal enum LineState { None, Cr, Lf } public class NetworkCredential { private string _domain; private static readonly string[] _noRoles; private string _password; private string[] _roles; private string _username; public string Domain { get { return _domain ?? string.Empty; } internal set { _domain = value; } } public string Password { get { return _password ?? string.Empty; } internal set { _password = value; } } public string[] Roles { get { return _roles ?? _noRoles; } internal set { _roles = value; } } public string Username { get { return _username; } internal set { _username = value; } } static NetworkCredential() { _noRoles = new string[0]; } public NetworkCredential(string username, string password) : this(username, password, (string)null, (string[])null) { } public NetworkCredential(string username, string password, string domain, params string[] roles) { if (username == null) { throw new ArgumentNullException("username"); } if (username.Length == 0) { throw new ArgumentException("An empty string.", "username"); } _username = username; _password = password; _domain = domain; _roles = roles; } } internal sealed class QueryStringCollection : NameValueCollection { public override string ToString() { if (Count == 0) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(); string[] allKeys = AllKeys; foreach (string text in allKeys) { stringBuilder.AppendFormat("{0}={1}&", text, base[text]); } if (stringBuilder.Length > 0) { stringBuilder.Length--; } return stringBuilder.ToString(); } } internal class ReadBufferState { private HttpStreamAsyncResult _asyncResult; private byte[] _buffer; private int _count; private int _initialCount; private int _offset; public HttpStreamAsyncResult AsyncResult { get { return _asyncResult; } set { _asyncResult = value; } } public byte[] Buffer { get { return _buffer; } set { _buffer = value; } } public int Count { get { return _count; } set { _count = value; } } public int InitialCount { get { return _initialCount; } set { _initialCount = value; } } public int Offset { get { return _offset; } set { _offset = value; } } public ReadBufferState(byte[] buffer, int offset, int count, HttpStreamAsyncResult asyncResult) { _buffer = buffer; _offset = offset; _count = count; _initialCount = count; _asyncResult = asyncResult; } } internal class RequestStream : Stream { private long _bodyLeft; private byte[] _buffer; private int _count; private bool _disposed; private int _offset; private Stream _stream; public override bool CanRead => true; public override bool CanSeek => false; public override bool CanWrite => false; public override long Length { get { throw new NotSupportedException(); } } public override long Position { get { throw new NotSupportedException(); } set { throw new NotSupportedException(); } } internal RequestStream(Stream stream, byte[] buffer, int offset, int count) : this(stream, buffer, offset, count, -1L) { } internal RequestStream(Stream stream, byte[] buffer, int offset, int count, long contentLength) { _stream = stream; _buffer = buffer; _offset = offset; _count = count; _bodyLeft = contentLength; } private int fillFromBuffer(byte[] buffer, int offset, int count) { if (buffer == null) { throw new ArgumentNullException("buffer"); } if (offset < 0) { throw new ArgumentOutOfRangeException("offset", "A negative value."); } if (count < 0) { throw new ArgumentOutOfRangeException("count", "A negative value."); } int num = buffer.Length; if (offset + count > num) { throw new ArgumentException("The sum of 'offset' and 'count' is greater than 'buffer' length."); } if (_bodyLeft == 0L) { return -1; } if (_count == 0 || count == 0) { return 0; } if (count > _count) { count = _count; } if (_bodyLeft > 0 && count > _bodyLeft) { count = (int)_bodyLeft; } Buffer.BlockCopy(_buffer, _offset, buffer, offset, count); _offset += count; _count -= count; if (_bodyLeft > 0) { _bodyLeft -= count; } return count; } public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { if (_disposed) { throw new ObjectDisposedException(GetType().ToString()); } int num = fillFromBuffer(buffer, offset, count); if (num > 0 || num == -1) { HttpStreamAsyncResult httpStreamAsyncResult = new HttpStreamAsyncResult(callback, state); httpStreamAsyncResult.Buffer = buffer; httpStreamAsyncResult.Offset = offset; httpStreamAsyncResult.Count = count; httpStreamAsyncResult.SyncRead = ((num > 0) ? num : 0); httpStreamAsyncResult.Complete(); return httpStreamAsyncResult; } if (_bodyLeft >= 0 && count > _bodyLeft) { count = (int)_bodyLeft; } return _stream.BeginRead(buffer, offset, count, callback, state); } public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { throw new NotSupportedException(); } public override void Close() { _disposed = true; } public override int EndRead(IAsyncResult asyncResult) { if (_disposed) { throw new ObjectDisposedException(GetType().ToString()); } if (asyncResult == null) { throw new ArgumentNullException("asyncResult"); } if (asyncResult is HttpStreamAsyncResult) { HttpStreamAsyncResult httpStreamAsyncResult = (HttpStreamAsyncResult)asyncResult; if (!httpStreamAsyncResult.IsCompleted) { httpStreamAsyncResult.AsyncWaitHandle.WaitOne(); } return httpStreamAsyncResult.SyncRead; } int num = _stream.EndRead(asyncResult); if (num > 0 && _bodyLeft > 0) { _bodyLeft -= num; } return num; } public override void EndWrite(IAsyncResult asyncResult) { throw new NotSupportedException(); } public override void Flush() { } public override int Read(byte[] buffer, int offset, int count) { if (_disposed) { throw new ObjectDisposedException(GetType().ToString()); } int num = fillFromBuffer(buffer, offset, count); if (num == -1) { return 0; } if (num > 0) { return num; } num = _stream.Read(buffer, offset, count); if (num > 0 && _bodyLeft > 0) { _bodyLeft -= num; } return num; } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public override void SetLength(long value) { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } } internal class ResponseStream : Stream { private MemoryStream _body; private static readonly byte[] _crlf = new byte[2] { 13, 10 }; private bool _disposed; private HttpListenerResponse _response; private bool _sendChunked; private Stream _stream; private Action _write; private Action _writeBody; private Action _writeChunked; public override bool CanRead => false; public override bool CanSeek => false; public override bool CanWrite => !_disposed; public override long Length { get { throw new NotSupportedException(); } } public override long Position { get { throw new NotSupportedException(); } set { throw new NotSupportedException(); } } internal ResponseStream(Stream stream, HttpListenerResponse response, bool ignoreWriteExceptions) { _stream = stream; _response = response; if (ignoreWriteExceptions) { _write = writeWithoutThrowingException; _writeChunked = writeChunkedWithoutThrowingException; } else { _write = stream.Write; _writeChunked = writeChunked; } _body = new MemoryStream(); } private bool flush(bool closing) { if (!_response.HeadersSent) { if (!flushHeaders(closing)) { if (closing) { _response.CloseConnection = true; } return false; } _sendChunked = _response.SendChunked; _writeBody = (_sendChunked ? _writeChunked : _write); } flushBody(closing); if (closing && _sendChunked) { byte[] chunkSizeBytes = getChunkSizeBytes(0, final: true); _write(chunkSizeBytes, 0, chunkSizeBytes.Length); } return true; } private void flushBody(bool closing) { using (_body) { long length = _body.Length; if (length > int.MaxValue) { _body.Position = 0L; int num = 1024; byte[] array = new byte[num]; int num2 = 0; while ((num2 = _body.Read(array, 0, num)) > 0) { _writeBody(array, 0, num2); } } else if (length > 0) { _writeBody(_body.GetBuffer(), 0, (int)length); } } _body = ((!closing) ? new MemoryStream() : null); } private bool flushHeaders(bool closing) { using (MemoryStream memoryStream = new MemoryStream()) { WebHeaderCollection webHeaderCollection = _response.WriteHeadersTo(memoryStream); long position = memoryStream.Position; long num = memoryStream.Length - position; if (num > 32768) { return false; } if (!_response.SendChunked && _response.ContentLength64 != _body.Length) { return false; } _write(memoryStream.GetBuffer(), (int)position, (int)num); _response.CloseConnection = webHeaderCollection["Connection"] == "close"; _response.HeadersSent = true; } return true; } private static byte[] getChunkSizeBytes(int size, bool final) { return Encoding.ASCII.GetBytes(string.Format("{0:x}\r\n{1}", size, final ? "\r\n" : "")); } private void writeChunked(byte[] buffer, int offset, int count) { byte[] chunkSizeBytes = getChunkSizeBytes(count, final: false); _stream.Write(chunkSizeBytes, 0, chunkSizeBytes.Length); _stream.Write(buffer, offset, count); _stream.Write(_crlf, 0, 2); } private void writeChunkedWithoutThrowingException(byte[] buffer, int offset, int count) { try { writeChunked(buffer, offset, count); } catch { } } private void writeWithoutThrowingException(byte[] buffer, int offset, int count) { try { _stream.Write(buffer, offset, count); } catch { } } internal void Close(bool force) { if (_disposed) { return; } _disposed = true; if (!force && flush(closing: true)) { _response.Close(); } else { if (_sendChunked) { byte[] chunkSizeBytes = getChunkSizeBytes(0, final: true); _write(chunkSizeBytes, 0, chunkSizeBytes.Length); } _body.Dispose(); _body = null; _response.Abort(); } _response = null; _stream = null; } internal void InternalWrite(byte[] buffer, int offset, int count) { _write(buffer, offset, count); } public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { throw new NotSupportedException(); } public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { if (_disposed) { throw new ObjectDisposedException(GetType().ToString()); } return _body.BeginWrite(buffer, offset, count, callback, state); } public override void Close() { Close(force: false); } protected override void Dispose(bool disposing) { Close(!disposing); } public override int EndRead(IAsyncResult asyncResult) { throw new NotSupportedException(); } public override void EndWrite(IAsyncResult asyncResult) { if (_disposed) { throw new ObjectDisposedException(GetType().ToString()); } _body.EndWrite(asyncResult); } public override void Flush() { if (!_disposed && (_sendChunked || _response.SendChunked)) { flush(closing: false); } } public override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public override void SetLength(long value) { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { if (_disposed) { throw new ObjectDisposedException(GetType().ToString()); } _body.Write(buffer, offset, count); } } public class ServerSslConfiguration { private bool _checkCertRevocation; private bool _clientCertRequired; private RemoteCertificateValidationCallback _clientCertValidationCallback; private SslProtocols _enabledSslProtocols; private X509Certificate2 _serverCert; public bool CheckCertificateRevocation { get { return _checkCertRevocation; } set { _checkCertRevocation = value; } } public bool ClientCertificateRequired { get { return _clientCertRequired; } set { _clientCertRequired = value; } } public RemoteCertificateValidationCallback ClientCertificateValidationCallback { get { if (_clientCertValidationCallback == null) { _clientCertValidationCallback = defaultValidateClientCertificate; } return _clientCertValidationCallback; } set { _clientCertValidationCallback = value; } } public SslProtocols EnabledSslProtocols { get { return _enabledSslProtocols; } set { _enabledSslProtocols = value; } } public X509Certificate2 ServerCertificate { get { return _serverCert; } set { _serverCert = value; } } public ServerSslConfiguration() { _enabledSslProtocols = SslProtocols.Default; } public ServerSslConfiguration(X509Certificate2 serverCertificate) { _serverCert = serverCertificate; _enabledSslProtocols = SslProtocols.Default; } public ServerSslConfiguration(ServerSslConfiguration configuration) { if (configuration == null) { throw new ArgumentNullException("configuration"); } _checkCertRevocation = configuration._checkCertRevocation; _clientCertRequired = configuration._clientCertRequired; _clientCertValidationCallback = configuration._clientCertValidationCallback; _enabledSslProtocols = configuration._enabledSslProtocols; _serverCert = configuration._serverCert; } private static bool defaultValidateClientCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return true; } } [Serializable] [ComVisible(true)] public class WebHeaderCollection : NameValueCollection, ISerializable { private static readonly Dictionary _headers; private bool _internallyUsed; private HttpHeaderType _state; internal HttpHeaderType State => _state; public override string[] AllKeys => base.AllKeys; public override int Count => base.Count; public string this[HttpRequestHeader header] { get { return Get(Convert(header)); } set { Add(header, value); } } public string this[HttpResponseHeader header] { get { return Get(Convert(header)); } set { Add(header, value); } } public override KeysCollection Keys => base.Keys; static WebHeaderCollection() { _headers = new Dictionary(StringComparer.InvariantCultureIgnoreCase) { { "Accept", new HttpHeaderInfo("Accept", HttpHeaderType.Request | HttpHeaderType.Restricted | HttpHeaderType.MultiValue) }, { "AcceptCharset", new HttpHeaderInfo("Accept-Charset", HttpHeaderType.Request | HttpHeaderType.MultiValue) }, { "AcceptEncoding", new HttpHeaderInfo("Accept-Encoding", HttpHeaderType.Request | HttpHeaderType.MultiValue) }, { "AcceptLanguage", new HttpHeaderInfo("Accept-Language", HttpHeaderType.Request | HttpHeaderType.MultiValue) }, { "AcceptRanges", new HttpHeaderInfo("Accept-Ranges", HttpHeaderType.Response | HttpHeaderType.MultiValue) }, { "Age", new HttpHeaderInfo("Age", HttpHeaderType.Response) }, { "Allow", new HttpHeaderInfo("Allow", HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.MultiValue) }, { "Authorization", new HttpHeaderInfo("Authorization", HttpHeaderType.Request | HttpHeaderType.MultiValue) }, { "CacheControl", new HttpHeaderInfo("Cache-Control", HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.MultiValue) }, { "Connection", new HttpHeaderInfo("Connection", HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.Restricted | HttpHeaderType.MultiValue) }, { "ContentEncoding", new HttpHeaderInfo("Content-Encoding", HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.MultiValue) }, { "ContentLanguage", new HttpHeaderInfo("Content-Language", HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.MultiValue) }, { "ContentLength", new HttpHeaderInfo("Content-Length", HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.Restricted) }, { "ContentLocation", new HttpHeaderInfo("Content-Location", HttpHeaderType.Request | HttpHeaderType.Response) }, { "ContentMd5", new HttpHeaderInfo("Content-MD5", HttpHeaderType.Request | HttpHeaderType.Response) }, { "ContentRange", new HttpHeaderInfo("Content-Range", HttpHeaderType.Request | HttpHeaderType.Response) }, { "ContentType", new HttpHeaderInfo("Content-Type", HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.Restricted) }, { "Cookie", new HttpHeaderInfo("Cookie", HttpHeaderType.Request) }, { "Cookie2", new HttpHeaderInfo("Cookie2", HttpHeaderType.Request) }, { "Date", new HttpHeaderInfo("Date", HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.Restricted) }, { "Expect", new HttpHeaderInfo("Expect", HttpHeaderType.Request | HttpHeaderType.Restricted | HttpHeaderType.MultiValue) }, { "Expires", new HttpHeaderInfo("Expires", HttpHeaderType.Request | HttpHeaderType.Response) }, { "ETag", new HttpHeaderInfo("ETag", HttpHeaderType.Response) }, { "From", new HttpHeaderInfo("From", HttpHeaderType.Request) }, { "Host", new HttpHeaderInfo("Host", HttpHeaderType.Request | HttpHeaderType.Restricted) }, { "IfMatch", new HttpHeaderInfo("If-Match", HttpHeaderType.Request | HttpHeaderType.MultiValue) }, { "IfModifiedSince", new HttpHeaderInfo("If-Modified-Since", HttpHeaderType.Request | HttpHeaderType.Restricted) }, { "IfNoneMatch", new HttpHeaderInfo("If-None-Match", HttpHeaderType.Request | HttpHeaderType.MultiValue) }, { "IfRange", new HttpHeaderInfo("If-Range", HttpHeaderType.Request) }, { "IfUnmodifiedSince", new HttpHeaderInfo("If-Unmodified-Since", HttpHeaderType.Request) }, { "KeepAlive", new HttpHeaderInfo("Keep-Alive", HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.MultiValue) }, { "LastModified", new HttpHeaderInfo("Last-Modified", HttpHeaderType.Request | HttpHeaderType.Response) }, { "Location", new HttpHeaderInfo("Location", HttpHeaderType.Response) }, { "MaxForwards", new HttpHeaderInfo("Max-Forwards", HttpHeaderType.Request) }, { "Pragma", new HttpHeaderInfo("Pragma", HttpHeaderType.Request | HttpHeaderType.Response) }, { "ProxyAuthenticate", new HttpHeaderInfo("Proxy-Authenticate", HttpHeaderType.Response | HttpHeaderType.MultiValue) }, { "ProxyAuthorization", new HttpHeaderInfo("Proxy-Authorization", HttpHeaderType.Request) }, { "ProxyConnection", new HttpHeaderInfo("Proxy-Connection", HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.Restricted) }, { "Public", new HttpHeaderInfo("Public", HttpHeaderType.Response | HttpHeaderType.MultiValue) }, { "Range", new HttpHeaderInfo("Range", HttpHeaderType.Request | HttpHeaderType.Restricted | HttpHeaderType.MultiValue) }, { "Referer", new HttpHeaderInfo("Referer", HttpHeaderType.Request | HttpHeaderType.Restricted) }, { "RetryAfter", new HttpHeaderInfo("Retry-After", HttpHeaderType.Response) }, { "SecWebSocketAccept", new HttpHeaderInfo("Sec-WebSocket-Accept", HttpHeaderType.Response | HttpHeaderType.Restricted) }, { "SecWebSocketExtensions", new HttpHeaderInfo("Sec-WebSocket-Extensions", HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.Restricted | HttpHeaderType.MultiValueInRequest) }, { "SecWebSocketKey", new HttpHeaderInfo("Sec-WebSocket-Key", HttpHeaderType.Request | HttpHeaderType.Restricted) }, { "SecWebSocketProtocol", new HttpHeaderInfo("Sec-WebSocket-Protocol", HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.MultiValueInRequest) }, { "SecWebSocketVersion", new HttpHeaderInfo("Sec-WebSocket-Version", HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.Restricted | HttpHeaderType.MultiValueInResponse) }, { "Server", new HttpHeaderInfo("Server", HttpHeaderType.Response) }, { "SetCookie", new HttpHeaderInfo("Set-Cookie", HttpHeaderType.Response | HttpHeaderType.MultiValue) }, { "SetCookie2", new HttpHeaderInfo("Set-Cookie2", HttpHeaderType.Response | HttpHeaderType.MultiValue) }, { "Te", new HttpHeaderInfo("TE", HttpHeaderType.Request) }, { "Trailer", new HttpHeaderInfo("Trailer", HttpHeaderType.Request | HttpHeaderType.Response) }, { "TransferEncoding", new HttpHeaderInfo("Transfer-Encoding", HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.Restricted | HttpHeaderType.MultiValue) }, { "Translate", new HttpHeaderInfo("Translate", HttpHeaderType.Request) }, { "Upgrade", new HttpHeaderInfo("Upgrade", HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.MultiValue) }, { "UserAgent", new HttpHeaderInfo("User-Agent", HttpHeaderType.Request | HttpHeaderType.Restricted) }, { "Vary", new HttpHeaderInfo("Vary", HttpHeaderType.Response | HttpHeaderType.MultiValue) }, { "Via", new HttpHeaderInfo("Via", HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.MultiValue) }, { "Warning", new HttpHeaderInfo("Warning", HttpHeaderType.Request | HttpHeaderType.Response | HttpHeaderType.MultiValue) }, { "WwwAuthenticate", new HttpHeaderInfo("WWW-Authenticate", HttpHeaderType.Response | HttpHeaderType.Restricted | HttpHeaderType.MultiValue) } }; } internal WebHeaderCollection(HttpHeaderType state, bool internallyUsed) { _state = state; _internallyUsed = internallyUsed; } protected WebHeaderCollection(SerializationInfo serializationInfo, StreamingContext streamingContext) { if (serializationInfo == null) { throw new ArgumentNullException("serializationInfo"); } try { _internallyUsed = serializationInfo.GetBoolean("InternallyUsed"); _state = (HttpHeaderType)serializationInfo.GetInt32("State"); int @int = serializationInfo.GetInt32("Count"); for (int i = 0; i < @int; i++) { base.Add(serializationInfo.GetString(i.ToString()), serializationInfo.GetString((@int + i).ToString())); } } catch (SerializationException ex) { throw new ArgumentException(ex.Message, "serializationInfo", ex); } } public WebHeaderCollection() { } private void add(string name, string value, bool ignoreRestricted) { Action action = (ignoreRestricted ? new Action(addWithoutCheckingNameAndRestricted) : new Action(addWithoutCheckingName)); doWithCheckingState(action, checkName(name), value, setState: true); } private void addWithoutCheckingName(string name, string value) { doWithoutCheckingName(base.Add, name, value); } private void addWithoutCheckingNameAndRestricted(string name, string value) { base.Add(name, checkValue(value)); } private static int checkColonSeparated(string header) { int num = header.IndexOf(':'); if (num == -1) { throw new ArgumentException("No colon could be found.", "header"); } return num; } private static HttpHeaderType checkHeaderType(string name) { HttpHeaderInfo headerInfo = getHeaderInfo(name); if (headerInfo != null) { if (!headerInfo.IsRequest || headerInfo.IsResponse) { if (headerInfo.IsRequest || !headerInfo.IsResponse) { return HttpHeaderType.Unspecified; } return HttpHeaderType.Response; } return HttpHeaderType.Request; } return HttpHeaderType.Unspecified; } private static string checkName(string name) { if (name == null || name.Length == 0) { throw new ArgumentNullException("name"); } name = name.Trim(); if (!IsHeaderName(name)) { throw new ArgumentException("Contains invalid characters.", "name"); } return name; } private void checkRestricted(string name) { if (!_internallyUsed && isRestricted(name, response: true)) { throw new ArgumentException("This header must be modified with the appropiate property."); } } private void checkState(bool response) { if (_state != 0) { if (response && _state == HttpHeaderType.Request) { throw new InvalidOperationException("This collection has already been used to store the request headers."); } if (!response && _state == HttpHeaderType.Response) { throw new InvalidOperationException("This collection has already been used to store the response headers."); } } } private static string checkValue(string value) { if (value == null || value.Length == 0) { return string.Empty; } value = value.Trim(); if (value.Length > 65535) { throw new ArgumentOutOfRangeException("value", "Greater than 65,535 characters."); } if (!IsHeaderValue(value)) { throw new ArgumentException("Contains invalid characters.", "value"); } return value; } private static string convert(string key) { if (!_headers.TryGetValue(key, out var value)) { return string.Empty; } return value.Name; } private void doWithCheckingState(Action action, string name, string value, bool setState) { switch (checkHeaderType(name)) { case HttpHeaderType.Request: doWithCheckingState(action, name, value, response: false, setState); break; case HttpHeaderType.Response: doWithCheckingState(action, name, value, response: true, setState); break; default: action(name, value); break; } } private void doWithCheckingState(Action action, string name, string value, bool response, bool setState) { checkState(response); action(name, value); if (setState && _state == HttpHeaderType.Unspecified) { _state = ((!response) ? HttpHeaderType.Request : HttpHeaderType.Response); } } private void doWithoutCheckingName(Action action, string name, string value) { checkRestricted(name); action(name, checkValue(value)); } private static HttpHeaderInfo getHeaderInfo(string name) { foreach (HttpHeaderInfo value in _headers.Values) { if (value.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) { return value; } } return null; } private static bool isRestricted(string name, bool response) { return getHeaderInfo(name)?.IsRestricted(response) ?? false; } private void removeWithoutCheckingName(string name, string unuse) { checkRestricted(name); base.Remove(name); } private void setWithoutCheckingName(string name, string value) { doWithoutCheckingName(base.Set, name, value); } internal static string Convert(HttpRequestHeader header) { return convert(header.ToString()); } internal static string Convert(HttpResponseHeader header) { return convert(header.ToString()); } internal void InternalRemove(string name) { base.Remove(name); } internal void InternalSet(string header, bool response) { int num = checkColonSeparated(header); InternalSet(header.Substring(0, num), header.Substring(num + 1), response); } internal void InternalSet(string name, string value, bool response) { value = checkValue(value); if (IsMultiValue(name, response)) { base.Add(name, value); } else { base.Set(name, value); } } internal static bool IsHeaderName(string name) { if (name != null && name.Length > 0) { return name.IsToken(); } return false; } internal static bool IsHeaderValue(string value) { return value.IsText(); } internal static bool IsMultiValue(string headerName, bool response) { if (headerName == null || headerName.Length == 0) { return false; } return getHeaderInfo(headerName)?.IsMultiValue(response) ?? false; } internal string ToStringMultiValue(bool response) { StringBuilder buff = new StringBuilder(); Count.Times(delegate(int i) { string key = GetKey(i); if (IsMultiValue(key, response)) { string[] values = GetValues(i); foreach (string arg in values) { buff.AppendFormat("{0}: {1}\r\n", key, arg); } } else { buff.AppendFormat("{0}: {1}\r\n", key, Get(i)); } }); return buff.Append("\r\n").ToString(); } protected void AddWithoutValidate(string headerName, string headerValue) { add(headerName, headerValue, ignoreRestricted: true); } public void Add(string header) { if (header == null || header.Length == 0) { throw new ArgumentNullException("header"); } int num = checkColonSeparated(header); add(header.Substring(0, num), header.Substring(num + 1), ignoreRestricted: false); } public void Add(HttpRequestHeader header, string value) { doWithCheckingState(addWithoutCheckingName, Convert(header), value, response: false, setState: true); } public void Add(HttpResponseHeader header, string value) { doWithCheckingState(addWithoutCheckingName, Convert(header), value, response: true, setState: true); } public override void Add(string name, string value) { add(name, value, ignoreRestricted: false); } public override void Clear() { base.Clear(); _state = HttpHeaderType.Unspecified; } public override string Get(int index) { return base.Get(index); } public override string Get(string name) { return base.Get(name); } public override IEnumerator GetEnumerator() { return base.GetEnumerator(); } public override string GetKey(int index) { return base.GetKey(index); } public override string[] GetValues(int index) { string[] values = base.GetValues(index); if (values == null || values.Length == 0) { return null; } return values; } public override string[] GetValues(string header) { string[] values = base.GetValues(header); if (values == null || values.Length == 0) { return null; } return values; } [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] public override void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) { if (serializationInfo == null) { throw new ArgumentNullException("serializationInfo"); } serializationInfo.AddValue("InternallyUsed", _internallyUsed); serializationInfo.AddValue("State", (int)_state); int cnt = Count; serializationInfo.AddValue("Count", cnt); cnt.Times(delegate(int i) { serializationInfo.AddValue(i.ToString(), GetKey(i)); serializationInfo.AddValue((cnt + i).ToString(), Get(i)); }); } public static bool IsRestricted(string headerName) { return isRestricted(checkName(headerName), response: false); } public static bool IsRestricted(string headerName, bool response) { return isRestricted(checkName(headerName), response); } public override void OnDeserialization(object sender) { } public void Remove(HttpRequestHeader header) { doWithCheckingState(removeWithoutCheckingName, Convert(header), null, response: false, setState: false); } public void Remove(HttpResponseHeader header) { doWithCheckingState(removeWithoutCheckingName, Convert(header), null, response: true, setState: false); } public override void Remove(string name) { doWithCheckingState(removeWithoutCheckingName, checkName(name), null, setState: false); } public void Set(HttpRequestHeader header, string value) { doWithCheckingState(setWithoutCheckingName, Convert(header), value, response: false, setState: true); } public void Set(HttpResponseHeader header, string value) { doWithCheckingState(setWithoutCheckingName, Convert(header), value, response: true, setState: true); } public override void Set(string name, string value) { doWithCheckingState(setWithoutCheckingName, checkName(name), value, setState: true); } public byte[] ToByteArray() { return Encoding.UTF8.GetBytes(ToString()); } public override string ToString() { StringBuilder buff = new StringBuilder(); Count.Times(delegate(int i) { buff.AppendFormat("{0}: {1}\r\n", GetKey(i), Get(i)); }); return buff.Append("\r\n").ToString(); } [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter, SerializationFormatter = true)] void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) { GetObjectData(serializationInfo, streamingContext); } } } namespace WebSocketSharp.Net.WebSockets { public class HttpListenerWebSocketContext : WebSocketContext { private HttpListenerContext _context; private WebSocket _websocket; internal Logger Log => _context.Listener.Log; internal Stream Stream => _context.Connection.Stream; public override CookieCollection CookieCollection => _context.Request.Cookies; public override NameValueCollection Headers => _context.Request.Headers; public override string Host => _context.Request.Headers["Host"]; public override bool IsAuthenticated => _context.User != null; public override bool IsLocal => _context.Request.IsLocal; public override bool IsSecureConnection => _context.Connection.IsSecure; public override bool IsWebSocketRequest => _context.Request.IsWebSocketRequest; public override string Origin => _context.Request.Headers["Origin"]; public override NameValueCollection QueryString => _context.Request.QueryString; public override Uri RequestUri => _context.Request.Url; public override string SecWebSocketKey => _context.Request.Headers["Sec-WebSocket-Key"]; public override IEnumerable SecWebSocketProtocols { get { string text = _context.Request.Headers["Sec-WebSocket-Protocol"]; if (text != null) { string[] array = text.Split(new char[1] { ',' }); foreach (string text2 in array) { yield return text2.Trim(); } } } } public override string SecWebSocketVersion => _context.Request.Headers["Sec-WebSocket-Version"]; public override IPEndPoint ServerEndPoint => _context.Connection.LocalEndPoint; public override IPrincipal User => _context.User; public override IPEndPoint UserEndPoint => _context.Connection.RemoteEndPoint; public override WebSocket WebSocket => _websocket; internal HttpListenerWebSocketContext(HttpListenerContext context, string protocol) { _context = context; _websocket = new WebSocket(this, protocol); } internal void Close() { _context.Connection.Close(force: true); } internal void Close(HttpStatusCode code) { _context.Response.Close(code); } public override string ToString() { return _context.Request.ToString(); } } internal class TcpListenerWebSocketContext : WebSocketContext { private CookieCollection _cookies; private Logger _logger; private NameValueCollection _queryString; private HttpRequest _request; private bool _secure; private Stream _stream; private TcpClient _tcpClient; private Uri _uri; private IPrincipal _user; private WebSocket _websocket; internal Logger Log => _logger; internal Stream Stream => _stream; public override CookieCollection CookieCollection => _cookies ?? (_cookies = _request.Cookies); public override NameValueCollection Headers => _request.Headers; public override string Host => _request.Headers["Host"]; public override bool IsAuthenticated => _user != null; public override bool IsLocal => UserEndPoint.Address.IsLocal(); public override bool IsSecureConnection => _secure; public override bool IsWebSocketRequest => _request.IsWebSocketRequest; public override string Origin => _request.Headers["Origin"]; public override NameValueCollection QueryString => _queryString ?? (_queryString = HttpUtility.InternalParseQueryString((_uri != null) ? _uri.Query : null, Encoding.UTF8)); public override Uri RequestUri => _uri; public override string SecWebSocketKey => _request.Headers["Sec-WebSocket-Key"]; public override IEnumerable SecWebSocketProtocols { get { string text = _request.Headers["Sec-WebSocket-Protocol"]; if (text != null) { string[] array = text.Split(new char[1] { ',' }); foreach (string text2 in array) { yield return text2.Trim(); } } } } public override string SecWebSocketVersion => _request.Headers["Sec-WebSocket-Version"]; public override IPEndPoint ServerEndPoint => (IPEndPoint)_tcpClient.Client.LocalEndPoint; public override IPrincipal User => _user; public override IPEndPoint UserEndPoint => (IPEndPoint)_tcpClient.Client.RemoteEndPoint; public override WebSocket WebSocket => _websocket; internal TcpListenerWebSocketContext(TcpClient tcpClient, string protocol, bool secure, ServerSslConfiguration sslConfig, Logger logger) { _tcpClient = tcpClient; _secure = secure; _logger = logger; NetworkStream stream = tcpClient.GetStream(); if (secure) { SslStream sslStream = new SslStream(stream, leaveInnerStreamOpen: false, sslConfig.ClientCertificateValidationCallback); sslStream.AuthenticateAsServer(sslConfig.ServerCertificate, sslConfig.ClientCertificateRequired, sslConfig.EnabledSslProtocols, sslConfig.CheckCertificateRevocation); _stream = sslStream; } else { _stream = stream; } _request = HttpRequest.Read(_stream, 90000); _uri = HttpUtility.CreateRequestUrl(_request.RequestUri, _request.Headers["Host"], _request.IsWebSocketRequest, secure); _websocket = new WebSocket(this, protocol); } internal bool Authenticate(AuthenticationSchemes scheme, string realm, Func credentialsFinder) { if (scheme == AuthenticationSchemes.Anonymous) { return true; } if (scheme == AuthenticationSchemes.None) { Close(HttpStatusCode.Forbidden); return false; } string chal = new AuthenticationChallenge(scheme, realm).ToString(); int retry = -1; Func auth = null; auth = delegate { retry++; if (retry > 99) { Close(HttpStatusCode.Forbidden); return false; } IPrincipal principal = HttpUtility.CreateUser(_request.Headers["Authorization"], scheme, realm, _request.HttpMethod, credentialsFinder); if (principal == null || !principal.Identity.IsAuthenticated) { SendAuthenticationChallenge(chal); return auth(); } _user = principal; return true; }; return auth(); } internal void Close() { _stream.Close(); _tcpClient.Close(); } internal void Close(HttpStatusCode code) { _websocket.Close(HttpResponse.CreateCloseResponse(code)); } internal void SendAuthenticationChallenge(string challenge) { byte[] array = HttpResponse.CreateUnauthorizedResponse(challenge).ToByteArray(); _stream.Write(array, 0, array.Length); _request = HttpRequest.Read(_stream, 15000); } public override string ToString() { return _request.ToString(); } } public abstract class WebSocketContext { public abstract CookieCollection CookieCollection { get; } public abstract NameValueCollection Headers { get; } public abstract string Host { get; } public abstract bool IsAuthenticated { get; } public abstract bool IsLocal { get; } public abstract bool IsSecureConnection { get; } public abstract bool IsWebSocketRequest { get; } public abstract string Origin { get; } public abstract NameValueCollection QueryString { get; } public abstract Uri RequestUri { get; } public abstract string SecWebSocketKey { get; } public abstract IEnumerable SecWebSocketProtocols { get; } public abstract string SecWebSocketVersion { get; } public abstract IPEndPoint ServerEndPoint { get; } public abstract IPrincipal User { get; } public abstract IPEndPoint UserEndPoint { get; } public abstract WebSocket WebSocket { get; } } }