Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of VTSIntegration v1.1.0
VTSIntegration.dll
Decompiled a year ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HG; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using On.RoR2.UI.LogBook; using RoR2; using RoR2.ExpansionManagement; using RoR2.UI; using RoR2.UI.LogBook; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; using WebSocketSharp; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("VTSIntegration")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("VTSIntegration")] [assembly: AssemblyTitle("VTSIntegration")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace VTSIntegration { internal static class Log { private static ManualLogSource _logSource; internal static void Init(ManualLogSource logSource) { _logSource = logSource; } internal static void Debug(object data) { _logSource.LogDebug(data); } internal static void Error(object data) { _logSource.LogError(data); } internal static void Fatal(object data) { _logSource.LogFatal(data); } internal static void Info(object data) { _logSource.LogInfo(data); } internal static void Message(object data) { _logSource.LogMessage(data); } internal static void Warning(object data) { _logSource.LogWarning(data); } } internal class LogBookControls { [CompilerGenerated] private static class <>O { public static hook_ViewEntry <0>__LogBookController_ViewEntry; public static hook_CanSelectItemEntry <1>__LogBookController_CanSelectItemEntry; public static hook_CanSelectEquipmentEntry <2>__LogBookController_CanSelectEquipmentEntry; public static hook_GetPickupStatus <3>__LogBookController_GetPickupStatus; } public static bool detourEntry; public static ItemIndex lastEntryItem; public static EquipmentIndex lastEntryEquipment; public static void Init() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown object obj = <>O.<0>__LogBookController_ViewEntry; if (obj == null) { hook_ViewEntry val = LogBookController_ViewEntry; <>O.<0>__LogBookController_ViewEntry = val; obj = (object)val; } LogBookController.ViewEntry += (hook_ViewEntry)obj; object obj2 = <>O.<1>__LogBookController_CanSelectItemEntry; if (obj2 == null) { hook_CanSelectItemEntry val2 = LogBookController_CanSelectItemEntry; <>O.<1>__LogBookController_CanSelectItemEntry = val2; obj2 = (object)val2; } LogBookController.CanSelectItemEntry += (hook_CanSelectItemEntry)obj2; object obj3 = <>O.<2>__LogBookController_CanSelectEquipmentEntry; if (obj3 == null) { hook_CanSelectEquipmentEntry val3 = LogBookController_CanSelectEquipmentEntry; <>O.<2>__LogBookController_CanSelectEquipmentEntry = val3; obj3 = (object)val3; } LogBookController.CanSelectEquipmentEntry += (hook_CanSelectEquipmentEntry)obj3; object obj4 = <>O.<3>__LogBookController_GetPickupStatus; if (obj4 == null) { hook_GetPickupStatus val4 = LogBookController_GetPickupStatus; <>O.<3>__LogBookController_GetPickupStatus = val4; obj4 = (object)val4; } LogBookController.GetPickupStatus += (hook_GetPickupStatus)obj4; } private static EntryStatus LogBookController_GetPickupStatus(orig_GetPickupStatus orig, ref Entry entry, UserProfile viewerProfile) { //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Invalid comparison between Unknown and I4 //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Invalid comparison between Unknown and I4 //IL_00a6: Unknown result type (might be due to invalid IL or missing references) object extraData = entry.extraData; if (extraData != null && extraData.GetType() == typeof(PickupIndex)) { PickupIndex val = (PickupIndex)extraData; PickupDef pickupDef = PickupCatalog.GetPickupDef(val); ItemIndex itemIndex = pickupDef.itemIndex; ItemDef itemDef = ItemCatalog.GetItemDef(itemIndex); EquipmentIndex equipmentIndex = pickupDef.equipmentIndex; EquipmentDef equipmentDef = EquipmentCatalog.GetEquipmentDef(equipmentIndex); string s = (((int)itemIndex != -1 && (Object)(object)itemDef != (Object)null) ? ((Object)itemDef).name : (((int)equipmentIndex != -1 && (Object)(object)equipmentDef != (Object)null) ? ((Object)equipmentDef).name : "null")); if (VTSConfig.forceEntries.Contains(s)) { return (EntryStatus)4; } } return orig.Invoke(ref entry, viewerProfile); } private static bool LogBookController_CanSelectItemEntry(orig_CanSelectItemEntry orig, ItemDef itemDef, Dictionary<ExpansionDef, bool> expansionAvailability) { if ((Object)(object)itemDef != (Object)null && VTSConfig.forceEntries.Contains(((Object)itemDef).name)) { return true; } return orig.Invoke(itemDef, expansionAvailability); } private static bool LogBookController_CanSelectEquipmentEntry(orig_CanSelectEquipmentEntry orig, EquipmentDef equipmentDef, Dictionary<ExpansionDef, bool> expansionAvailability) { if ((Object)(object)equipmentDef != (Object)null && VTSConfig.forceEntries.Contains(((Object)equipmentDef).name)) { return true; } return orig.Invoke(equipmentDef, expansionAvailability); } private static void LogBookController_ViewEntry(orig_ViewEntry orig, LogBookController self, Entry entry) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) if (detourEntry) { object extraData = entry.extraData; if (extraData != null && extraData.GetType() == typeof(PickupIndex)) { PickupIndex val = (PickupIndex)extraData; PickupDef pickupDef = PickupCatalog.GetPickupDef(val); lastEntryItem = pickupDef.itemIndex; lastEntryEquipment = pickupDef.equipmentIndex; } } else { orig.Invoke(self, entry); } } public static void GetIndiciesFromButton(HGButton button) { detourEntry = true; ((UnityEvent)((Button)button).onClick).Invoke(); detourEntry = false; } public static void HandleLogbookInteractions() { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Invalid comparison between Unknown and I4 //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_03a4: Unknown result type (might be due to invalid IL or missing references) bool keyDown = Input.GetKeyDown((KeyCode)283); bool keyDown2 = Input.GetKeyDown((KeyCode)284); bool keyDown3 = Input.GetKeyDown((KeyCode)285); bool key = Input.GetKey((KeyCode)306); bool key2 = Input.GetKey((KeyCode)304); float y = Input.mouseScrollDelta.y; if ((!(keyDown || keyDown2 || keyDown3) && y == 0f) || !EventSystem.current.IsPointerOverGameObject()) { return; } GameObject currentSelectedGameObject = EventSystem.current.currentSelectedGameObject; HGButton component = currentSelectedGameObject.GetComponent<HGButton>(); if (!((Object)(object)component != (Object)null) || !((Object)component).name.StartsWith("ItemEntryIcon")) { return; } GetIndiciesFromButton(component); ItemIndex val = lastEntryItem; EquipmentIndex val2 = lastEntryEquipment; ItemDef itemDef = ItemCatalog.GetItemDef(val); EquipmentDef equipmentDef = EquipmentCatalog.GetEquipmentDef(val2); bool flag = (Object)(object)equipmentDef != (Object)null; string text = (flag ? ((Object)equipmentDef).name : ((Object)itemDef).name); VTSConfig.ItemConfig itemConfig = VTSConfig.itemConfig[text]; if (keyDown) { VTSConfig.blockedItems.Remove(text); if (flag) { if (VTSIntegration.lastEquipment == val2) { VTSIntegration.OnEquipmentRemoved(val2); VTSIntegration.lastEquipment = (EquipmentIndex)(-1); return; } VTSIntegration.OnEquipmentPickup(val2); if ((int)VTSIntegration.lastEquipment != -1) { VTSIntegration.OnEquipmentRemoved(VTSIntegration.lastEquipment); } VTSIntegration.lastEquipment = val2; } else if (VTSIntegration.lastInventory.Contains(val)) { VTSIntegration.OnItemRemoved(val); VTSIntegration.lastInventory.Remove(val); } else { VTSIntegration.OnItemPickup(val); VTSIntegration.lastInventory.Add(val); } } else if (keyDown2) { VTSConfig.backgroundItems.Set(text, !VTSConfig.backgroundItems.Contains(text)); if (VTSApi.OrderAvailable(text)) { VTSApi.MoveItem(text, new Vector2(-1000f, -1000f), itemConfig.rotation.Value, itemConfig.size.Value, VTSApi.ClaimOrder(text)); } } else if (keyDown3) { if (key) { itemConfig.imageOverride.Value = "null"; VTSApi.cachedItems.Remove(text); if (VTSApi.loadedItems.ContainsKey(text)) { VTSApi.UnloadItem(text); VTSApi.LoadItem(text, flag ? equipmentDef.pickupIconSprite.texture : itemDef.pickupIconSprite.texture); } VTSApi.itemToReplace = string.Empty; } else if (VTSApi.itemToReplace.Equals(text)) { VTSApi.itemToReplace = string.Empty; } else { VTSApi.itemToReplace = text; } } else if (VTSApi.loadedItems.ContainsKey(text)) { int num = ((!key2) ? 1 : 5); if (key) { ConfigEntry<int> rotation = itemConfig.rotation; rotation.Value += ((!(y < 0f)) ? 1 : (-1)) * num; ConfigEntry<int> rotation2 = itemConfig.rotation; rotation2.Value %= 360; } else { float value = itemConfig.size.Value; value += ((y < 0f) ? (-0.01f) : 0.01f) * (float)num; value = Mathf.Max(0f, value); value = Mathf.Min(1f, value); itemConfig.size.Value = value; } VTSApi.MoveItem(text, new Vector2(-1000f, -1000f), itemConfig.rotation.Value, itemConfig.size.Value); } } } internal class VTSApi { public const string vtsPluginID = "RoR2-VTSIntegration"; public const string vtsPluginAuthor = "doomy64"; public const string vtsPluginIcon = "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"; public const int API_ERROR_AUTH_FAILED = 50; public const int API_ERROR_ITEM_ORDER_TAKEN = 756; public const int ITEM_ORDER_MIN = -30; private static Dictionary<string, object> baseInfo = new Dictionary<string, object> { { "apiName", "VTubeStudioPublicAPI" }, { "apiVersion", "1.0" }, { "requestID", "RoR2-VTSIntegration" } }; public static Dictionary<string, string> loadedItems = new Dictionary<string, string>(); public static Dictionary<string, string> cachedItems = new Dictionary<string, string>(); public static string lastItemLocked = string.Empty; public static string itemToReplace = string.Empty; public static Dictionary<string, Action<Dictionary<string, object>, string>> responseHandlers = new Dictionary<string, Action<Dictionary<string, object>, string>>(); public static bool connected = false; public static float lastModelSize = 0f; public static string modelID; public static List<int> availableOrdersFront = new List<int>(); public static List<int> availableOrdersBack = new List<int>(); public static List<string> itemsWaitingForSpace = new List<string>(); public static List<string> itemsWaitingToAdd = new List<string>(); public static List<string> itemsBeingMoved = new List<string>(); public static List<string> ignoredResponses; private static WebSocket socket; public static void Init() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown socket = new WebSocket(VTSConfig.address.Value, Array.Empty<string>()); Connect(); } private static void Connect() { responseHandlers.Add("AuthenticationTokenResponse", Handler_AuthTokenResponse); responseHandlers.Add("AuthenticationResponse", Handler_AuthResponse); responseHandlers.Add("APIStateResponse", Handler_APIStateResponse); responseHandlers.Add("PermissionResponse", Handler_PermissionResponse); responseHandlers.Add("CurrentModelResponse", Handler_CurrentModelResponse); responseHandlers.Add("ItemEvent", Handler_ItemEvent); responseHandlers.Add("ItemLoadResponse", Handler_ItemLoadResponse); responseHandlers.Add("ItemListResponse", Handler_ItemListResponse); responseHandlers.Add("ItemMoveResponse", Handler_ItemMoveResponse); responseHandlers.Add("ItemPinResponse", Handler_ItemPinResponse); responseHandlers.Add("ModelClickedEvent", Handler_ModelClickedEvent); responseHandlers.Add("ModelLoadedEvent", Handler_ModelLoadedEvent); responseHandlers.Add("APIError", Handler_APIError); ignoredResponses = new List<string> { "ItemUnloadResponse", "EventSubscriptionResponse" }; socket.Connect(); socket.OnMessage += delegate(object sender, MessageEventArgs e) { HandleResponse(e.Data); }; SendRequest("APIStateRequest"); } public static void OnApiConnected() { connected = true; if (VTSConfig.authToken.Value == "null") { GenerateAuthToken(); } else { Authenticate(); } } public static void OnAuthenticated() { SendRequest("PermissionRequest", new Dictionary<string, object> { { "requestedPermission", "LoadCustomImagesAsItems" } }); SendRequest("EventSubscriptionRequest", new Dictionary<string, object> { { "eventName", "ModelClickedEvent" }, { "subscribe", true }, { "config", new Dictionary<string, object> { { "onlyClicksOnModel", true } } } }); SendRequest("EventSubscriptionRequest", new Dictionary<string, object> { { "eventName", "ItemEvent" }, { "subscribe", true }, { "config", new Dictionary<string, object>() } }); SendRequest("EventSubscriptionRequest", new Dictionary<string, object> { { "eventName", "ModelLoadedEvent" }, { "subscribe", true }, { "config", new Dictionary<string, object>() } }); SendRequest("ItemListRequest", new Dictionary<string, object> { { "includeAvailableSpots", true }, { "includeAvailableItemFiles", false }, { "includeItemInstancesInScene", false } }); SendRequest("CurrentModelRequest"); } public static void SendRequest(string messageType, string requestID = null) { Dictionary<string, object> content = new Dictionary<string, object> { { "messageType", messageType } }; SendRequest(content, requestID); } public static void SendRequest(string messageType, Dictionary<string, object> data, string requestID = null) { Dictionary<string, object> content = new Dictionary<string, object> { { "messageType", messageType }, { "data", data } }; SendRequest(content, requestID); } public static void SendRequest(Dictionary<string, object> content, string requestID = null) { Dictionary<string, object>[] dictionaries = new Dictionary<string, object>[2] { baseInfo, content }; Dictionary<string, object> dictionary = Merge(dictionaries); if (requestID != null) { dictionary.Remove("requestID"); dictionary.Add("requestID", requestID); } string text = JsonConvert.SerializeObject((object)dictionary); socket.SendAsync(text, (Action<bool>)null); } public static void HandleResponse(string body) { Dictionary<string, object> dictionary = JsonConvert.DeserializeObject<Dictionary<string, object>>(body); string text = dictionary["messageType"].ToString(); Dictionary<string, object> arg = JsonConvert.DeserializeObject<Dictionary<string, object>>(dictionary["data"].ToString()); if (responseHandlers.ContainsKey(text)) { responseHandlers[text](arg, dictionary["requestID"].ToString()); } else if (!ignoredResponses.Contains(text)) { Log.Warning("Received unhandled response type: " + text); } } private static void Handler_PermissionResponse(Dictionary<string, object> data, string requestID) { } public static void Handler_APIStateResponse(Dictionary<string, object> data, string requestID) { if (data["active"].Equals(false)) { Log.Error("VTS Responded, but API is not active. Check your settings and restart your game"); } else { OnApiConnected(); } } public static void Handler_AuthTokenResponse(Dictionary<string, object> data, string requestID) { VTSConfig.authToken.Value = data["authenticationToken"].ToString(); Authenticate(); } public static void Handler_AuthResponse(Dictionary<string, object> data, string requestID) { if (data["authenticated"].Equals(false)) { Log.Warning("Authentication token is invalid, regenerating"); GenerateAuthToken(); } else { OnAuthenticated(); } } public static void Handler_CurrentModelResponse(Dictionary<string, object> data, string requestID) { if (data["modelLoaded"].Equals(true)) { modelID = data["modelID"].ToString(); VTSConfig.LoadModelConfig(modelID); } } public static void Handler_ItemLoadResponse(Dictionary<string, object> data, string requestID) { string text = requestID; text = text.Substring(text.IndexOf("|") + 1); string instanceID = data["instanceID"].ToString(); string value = data["fileName"].ToString(); if (!cachedItems.ContainsKey(text)) { cachedItems.Add(text, value); } LinkItem(text, instanceID); } public static void Handler_ItemEvent(Dictionary<string, object> data, string requestID) { //IL_00d4: Unknown result type (might be due to invalid IL or missing references) string text = data["itemEventType"].ToString(); string text2 = data["itemInstanceID"].ToString(); if (itemsBeingMoved.Contains(text2)) { return; } if (loadedItems.ContainsKey(text2)) { string text3 = loadedItems[text2]; if (text.Equals("DroppedUnpinned") || text.Equals("DroppedPinned") || text.Equals("Clicked")) { Dictionary<string, float> dictionary = JsonConvert.DeserializeObject<Dictionary<string, float>>(data["itemPosition"].ToString()); Vector2 value = default(Vector2); ((Vector2)(ref value))..ctor(dictionary["x"], dictionary["y"]); VTSConfig.itemConfig[text3].position.Value = value; if (text.Equals("DroppedUnpinned")) { VTSConfig.itemConfig[text3].SetPinData(VTSConfig.PinData.Null()); } SendRequest("ItemListRequest", new Dictionary<string, object> { { "includeAvailableSpots", true }, { "includeAvailableItemFiles", false }, { "includeItemInstancesInScene", true }, { "onlyItemsWithInstanceID", text2 } }); } else if (text.Equals("Removed")) { loadedItems.Remove(text3); loadedItems.Remove(text2); VTSConfig.blockedItems.Add(text3); } else if (text.Equals("Locked")) { lastItemLocked = text2; } else if (text.Equals("Unlocked") && text2.Equals(lastItemLocked)) { lastItemLocked = string.Empty; } } if (text.Equals("Added") && !itemToReplace.Equals(string.Empty)) { VTSConfig.itemConfig[itemToReplace].imageOverride.Value = data["itemFileName"].ToString(); if (loadedItems.ContainsKey(itemToReplace)) { UnloadItem(itemToReplace); LoadItem(itemToReplace); } itemToReplace = string.Empty; SendRequest("ItemUnloadRequest", new Dictionary<string, object> { { "unloadAllInScene", false }, { "unloadAllLoadedByThisPlugin", false }, { "allowUnloadingItemsLoadedByUserOrOtherPlugins", true }, { "instanceIDs", new List<string> { text2 } } }); } SendRequest("ItemListRequest", new Dictionary<string, object> { { "includeAvailableSpots", true }, { "includeAvailableItemFiles", false }, { "includeItemInstancesInScene", false }, { "onlyItemsWithInstanceID", text2 } }); } public static void Handler_ItemListResponse(Dictionary<string, object> data, string requestID) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) if (data["availableSpots"].GetType() == typeof(JArray)) { availableOrdersFront.Clear(); availableOrdersBack.Clear(); List<int> list = ((JToken)(JArray)data["availableSpots"]).ToObject<List<int>>(); list.ForEach(delegate(int n) { if (n > 0) { availableOrdersFront.Add(n); } else { availableOrdersBack.Add(n); } }); } List<Dictionary<string, object>> list2 = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(data["itemInstancesInScene"].ToString()); foreach (Dictionary<string, object> item in list2) { string s = loadedItems[item["instanceID"].ToString()]; VTSConfig.flippedItems.Set(s, item["flipped"].Equals(true)); } CheckForItemSpace(); } private static void Handler_ItemPinResponse(Dictionary<string, object> data, string requestID) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) if (!requestID.Equals(baseInfo["requestID"])) { List<string> list = new List<string>(); list.AddRange(requestID.Split("|")); string name = list[0]; Vector2 position = default(Vector2); ((Vector2)(ref position))..ctor(float.Parse(list[1]), float.Parse(list[2])); int rotation = int.Parse(list[3]); float size = float.Parse(list[4]); int order = int.Parse(list[5]); MoveItem(name, position, rotation, size, order, unpinIfPinned: false); } } private static void Handler_ItemMoveResponse(Dictionary<string, object> data, string requestID) { List<Dictionary<string, object>> list = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(data["movedItems"].ToString()); foreach (Dictionary<string, object> item in list) { string text = item["itemInstanceID"].ToString(); if (loadedItems.ContainsKey(text)) { string key = loadedItems[text]; VTSConfig.PinData pinData = VTSConfig.itemConfig[key].GetPinData(); SendRequest("ItemPinRequest", new Dictionary<string, object> { { "pin", true }, { "itemInstanceID", text }, { "angleRelativeTo", "RelativeToModel" }, { "sizeRelativeTo", "RelativeToCurrentItemSize" }, { "vertexPinType", "Provided" }, { "pinInfo", new Dictionary<string, object> { { "modelID", modelID }, { "artMeshID", pinData.mesh }, { "angle", VTSConfig.itemConfig[key].rotation.Value }, { "size", 0f }, { "vertexID1", pinData.vertexIDs[0] }, { "vertexID2", pinData.vertexIDs[1] }, { "vertexID3", pinData.vertexIDs[2] }, { "vertexWeight1", pinData.vertexWeights[0] }, { "vertexWeight2", pinData.vertexWeights[1] }, { "vertexWeight3", pinData.vertexWeights[2] } } } }); itemsBeingMoved.Remove(text); } } } public static void Handler_ModelClickedEvent(Dictionary<string, object> data, string requestID) { if (lastItemLocked == string.Empty) { return; } if (!loadedItems.ContainsKey(lastItemLocked)) { lastItemLocked = string.Empty; return; } string key = loadedItems[lastItemLocked]; int num = int.Parse(data["clickedArtMeshCount"].ToString()); if (num <= 0) { return; } List<Dictionary<string, object>> list = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(data["artMeshHits"].ToString()); foreach (Dictionary<string, object> item in list) { Dictionary<string, object> dictionary = JsonConvert.DeserializeObject<Dictionary<string, object>>(item["hitInfo"].ToString()); if (int.Parse(item["artMeshOrder"].ToString()) == 0) { VTSConfig.PinData pinData = default(VTSConfig.PinData); List<int> vertexIDs = new List<int> { int.Parse(dictionary["vertexID1"].ToString()), int.Parse(dictionary["vertexID2"].ToString()), int.Parse(dictionary["vertexID3"].ToString()) }; List<float> vertexWeights = new List<float> { float.Parse(dictionary["vertexWeight1"].ToString()), float.Parse(dictionary["vertexWeight2"].ToString()), float.Parse(dictionary["vertexWeight3"].ToString()) }; pinData.mesh = dictionary["artMeshID"].ToString(); pinData.vertexIDs = vertexIDs; pinData.vertexWeights = vertexWeights; VTSConfig.itemConfig[key].SetPinData(pinData); SendRequest("ItemPinRequest", new Dictionary<string, object> { { "pin", true }, { "itemInstanceID", lastItemLocked }, { "angleRelativeTo", "RelativeToModel" }, { "sizeRelativeTo", "RelativeToCurrentItemSize" }, { "vertexPinType", "Provided" }, { "pinInfo", new Dictionary<string, object> { { "modelID", modelID }, { "artMeshID", pinData.mesh }, { "angle", VTSConfig.itemConfig[key].rotation.Value }, { "size", 0f }, { "vertexID1", pinData.vertexIDs[0] }, { "vertexID2", pinData.vertexIDs[1] }, { "vertexID3", pinData.vertexIDs[2] }, { "vertexWeight1", pinData.vertexWeights[0] }, { "vertexWeight2", pinData.vertexWeights[1] }, { "vertexWeight3", pinData.vertexWeights[2] } } } }); lastItemLocked = string.Empty; break; } } } public static void Handler_ModelLoadedEvent(Dictionary<string, object> data, string requestID) { if (data["modelLoaded"].Equals(true)) { modelID = data["modelID"].ToString(); VTSConfig.LoadModelConfig(modelID); } } public static void Handler_APIError(Dictionary<string, object> data, string requestID) { switch (int.Parse(data["errorID"].ToString())) { case 50: Log.Error("Authentication was denied in VTS"); break; case 756: { string text = requestID; text = text.Substring(text.IndexOf("|") + 1); itemsWaitingForSpace.Add(text); SendRequest("ItemListRequest", new Dictionary<string, object> { { "includeAvailableSpots", true }, { "includeAvailableItemFiles", false }, { "includeItemInstancesInScene", false } }); break; } default: Log.Error("VTS returned API error: " + data["message"].ToString()); break; } } public static void LoadItem(string name, Texture2D icon = null) { //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) if (loadedItems.ContainsKey(name)) { Log.Error($"Attempted to load item {name} while it was already loaded"); return; } Log.Debug("Loading item: " + name); string value = ""; VTSConfig.ItemConfig itemConfig = VTSConfig.itemConfig[name]; string value2 = itemConfig.imageOverride.Value; int num = ClaimOrder(name); if (num < -30) { itemsWaitingForSpace.Add(name); return; } string text; if (value2 != "null") { text = value2; } else if (cachedItems.ContainsKey(name)) { text = cachedItems[name]; } else { if ((Object)(object)icon == (Object)null) { Log.Error($"Attempted to load item {name} without specifying an image"); return; } value = Convert.ToBase64String(ImageConversion.EncodeToPNG(DeCompress(icon))); text = name + ".png"; if (text.Length < 8) { text = text.PadLeft(8, '0'); } if (text.Length > 32) { text = text.Substring(text.Length - 32); } } SendRequest("ItemLoadRequest", new Dictionary<string, object> { { "fileName", text }, { "positionX", itemConfig.position.Value.x }, { "positionY", itemConfig.position.Value.y }, { "size", itemConfig.size.Value }, { "rotation", itemConfig.rotation.Value }, { "fadeTime", 0.0 }, { "order", num }, { "failIfOrderTaken", true }, { "smoothing", 0 }, { "censored", false }, { "flipped", VTSConfig.flippedItems.Contains(name) }, { "locked", false }, { "unloadWhenPluginDisconnects", true }, { "customDataBase64", value }, { "customDataAskUserFirst", false }, { "customDataSkipAskingUserIfWhitelisted", false }, { "customDataAskTimer", -1 } }, "ItemLoad|" + name); } public static void UnloadItem(string name) { if (loadedItems.ContainsKey(name)) { string text = loadedItems[name]; loadedItems.Remove(name); loadedItems.Remove(text); SendRequest("ItemUnloadRequest", new Dictionary<string, object> { { "unloadAllInScene", false }, { "unloadAllLoadedByThisPlugin", false }, { "allowUnloadingItemsLoadedByUserOrOtherPlugins", false }, { "instanceIDs", new List<string> { text } } }, "ItemUnload|" + name); } } public static void Authenticate() { SendRequest("AuthenticationRequest", new Dictionary<string, object> { { "pluginName", "RoR2-VTSIntegration" }, { "pluginDeveloper", "doomy64" }, { "authenticationToken", VTSConfig.authToken.Value } }); } public static void MoveItem(string name, Vector2 position, int rotation = -1000, float size = -1000f, int order = -1000, bool unpinIfPinned = true) { //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) if (loadedItems.ContainsKey(name)) { string text = loadedItems[name]; itemsBeingMoved.Add(text); if (unpinIfPinned) { SendRequest("ItemPinRequest", new Dictionary<string, object> { { "pin", false }, { "itemInstanceID", text } }, $"{name}|{position.x}|{position.y}|{rotation}|{size}|{order}"); } else { SendRequest("ItemMoveRequest", new Dictionary<string, object> { { "itemsToMove", new List<Dictionary<string, object>> { new Dictionary<string, object> { { "itemInstanceID", text }, { "timeInSeconds", 0 }, { "fadeMode", "zip" }, { "positionX", position.x }, { "positionY", position.y }, { "order", order }, { "size", size }, { "rotation", rotation }, { "setFlip", false }, { "flip", false }, { "userCanStop", false } } } } }); } } } public static void GenerateAuthToken() { SendRequest("AuthenticationTokenRequest", new Dictionary<string, object> { { "pluginName", "RoR2-VTSIntegration" }, { "pluginDeveloper", "doomy64" }, { "pluginIcon", "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
websocket-sharp.dll
Decompiled a year ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Net; using System.Net.Security; using System.Net.Sockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.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.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("websocket-sharp")] [assembly: AssemblyDescription("A C# implementation of the WebSocket protocol client and server")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("websocket-sharp.dll")] [assembly: AssemblyCopyright("sta.blockhead")] [assembly: AssemblyTrademark("")] [assembly: AssemblyVersion("1.0.2.37962")] namespace WebSocketSharp { public static class Ext { private static readonly byte[] _last = new byte[1]; private static readonly int _maxRetry = 5; private const string _tspecials = "()<>@,;:\\\"/[]?={} \t"; private static byte[] compress(this byte[] data) { if (data.LongLength == 0) { 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 == 0) { return memoryStream; } stream.Position = 0L; CompressionMode mode = CompressionMode.Compress; using DeflateStream deflateStream = new DeflateStream(memoryStream, mode, leaveOpen: true); CopyTo(stream, 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 == 0) { 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 == 0) { return memoryStream; } stream.Position = 0L; CompressionMode mode = CompressionMode.Decompress; using DeflateStream sourceStream = new DeflateStream(stream, mode, leaveOpen: true); CopyTo(sourceStream, 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 bool isPredefinedScheme(this string value) { switch (value[0]) { case 'h': return value == "http" || value == "https"; case 'w': return value == "ws" || value == "wss"; case 'f': return value == "file" || value == "ftp"; case 'g': return value == "gopher"; case 'm': return value == "mailto"; case 'n': { char c = value[1]; return (c != 'e') ? (value == "nntp") : (value == "news" || value == "net.pipe" || value == "net.tcp"); } default: return false; } } internal static byte[] Append(this ushort code, string reason) { byte[] array = code.ToByteArray(ByteOrder.Big); if (reason == null || reason.Length == 0) { return array; } List<byte> list = new List<byte>(array); byte[] bytes = Encoding.UTF8.GetBytes(reason); list.AddRange(bytes); return list.ToArray(); } internal static byte[] Compress(this byte[] data, CompressionMethod method) { return (method == CompressionMethod.Deflate) ? data.compress() : data; } internal static Stream Compress(this Stream stream, CompressionMethod method) { return (method == CompressionMethod.Deflate) ? stream.compress() : stream; } internal static bool Contains(this string value, params char[] anyOf) { return anyOf != null && anyOf.Length != 0 && value.IndexOfAny(anyOf) > -1; } internal static bool Contains(this NameValueCollection collection, string name) { return collection[name] != null; } internal static bool Contains(this NameValueCollection collection, string name, string value, StringComparison comparisonTypeForValue) { string text = collection[name]; if (text == null) { return false; } string[] array = text.Split(new char[1] { ',' }); foreach (string text2 in array) { if (text2.Trim().Equals(value, comparisonTypeForValue)) { return true; } } return false; } internal static bool Contains<T>(this IEnumerable<T> source, Func<T, bool> condition) { foreach (T item in source) { if (condition(item)) { return true; } } return false; } internal static bool ContainsTwice(this string[] values) { int len = values.Length; int end = len - 1; Func<int, bool> seek = null; seek = delegate(int idx) { if (idx == end) { return false; } string text = values[idx]; for (int i = idx + 1; i < len; i++) { if (values[i] == text) { return true; } } return seek(++idx); }; return seek(0); } internal static T[] Copy<T>(this T[] sourceArray, int length) { T[] array = new T[length]; Array.Copy(sourceArray, 0, array, 0, length); return array; } internal static T[] Copy<T>(this T[] sourceArray, long length) { T[] array = new T[length]; Array.Copy(sourceArray, 0L, array, 0L, length); return array; } internal static void CopyTo(this Stream sourceStream, Stream destinationStream, int bufferLength) { byte[] buffer = new byte[bufferLength]; while (true) { int num = sourceStream.Read(buffer, 0, bufferLength); if (num <= 0) { break; } destinationStream.Write(buffer, 0, num); } } internal static void CopyToAsync(this Stream sourceStream, Stream destinationStream, int bufferLength, Action completed, Action<Exception> error) { byte[] buff = new byte[bufferLength]; AsyncCallback callback = null; callback = delegate(IAsyncResult ar) { try { int num = sourceStream.EndRead(ar); if (num <= 0) { if (completed != null) { completed(); } } else { destinationStream.Write(buff, 0, num); sourceStream.BeginRead(buff, 0, bufferLength, callback, null); } } catch (Exception obj2) { if (error != null) { error(obj2); } } }; try { sourceStream.BeginRead(buff, 0, bufferLength, callback, null); } catch (Exception obj) { if (error != null) { error(obj); } } } internal static byte[] Decompress(this byte[] data, CompressionMethod method) { return (method == CompressionMethod.Deflate) ? data.decompress() : data; } internal static Stream Decompress(this Stream stream, CompressionMethod method) { return (method == CompressionMethod.Deflate) ? stream.decompress() : stream; } internal static byte[] DecompressToArray(this Stream stream, CompressionMethod method) { return (method == CompressionMethod.Deflate) ? stream.decompressToArray() : stream.ToByteArray(); } internal static void Emit(this EventHandler eventHandler, object sender, EventArgs e) { eventHandler?.Invoke(sender, e); } internal static void Emit<TEventArgs>(this EventHandler<TEventArgs> eventHandler, object sender, TEventArgs e) where TEventArgs : EventArgs { eventHandler?.Invoke(sender, e); } 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] { '?', '#' }); return (num > 0) ? originalString.Substring(0, num) : originalString; } internal static WebSocketSharp.Net.CookieCollection GetCookies(this NameValueCollection headers, bool response) { string name = (response ? "Set-Cookie" : "Cookie"); string text = headers[name]; return (text != null) ? WebSocketSharp.Net.CookieCollection.Parse(text, response) : new WebSocketSharp.Net.CookieCollection(); } internal static string GetDnsSafeHost(this Uri uri, bool bracketIPv6) { return (bracketIPv6 && uri.HostNameType == UriHostNameType.IPv6) ? uri.Host : uri.DnsSafeHost; } internal static string GetErrorMessage(this ushort code) { return code switch { 1002 => "A protocol error has occurred.", 1003 => "Unsupported data has been received.", 1006 => "An abnormal error has occurred.", 1007 => "Invalid data has been received.", 1008 => "A policy violation has occurred.", 1009 => "A too big message has been received.", 1010 => "The client did not receive expected extension(s).", 1011 => "The server got an internal error.", 1015 => "An error has occurred during a TLS handshake.", _ => string.Empty, }; } internal static string GetErrorMessage(this CloseStatusCode code) { return ((ushort)code).GetErrorMessage(); } internal static string GetName(this string nameAndValue, char separator) { int num = nameAndValue.IndexOf(separator); return (num > 0) ? nameAndValue.Substring(0, num).Trim() : null; } internal static string GetUTF8DecodedString(this byte[] bytes) { try { return Encoding.UTF8.GetString(bytes); } catch { return null; } } internal static byte[] GetUTF8EncodedBytes(this string s) { try { return Encoding.UTF8.GetBytes(s); } catch { return null; } } internal static string GetValue(this string nameAndValue, char separator) { return nameAndValue.GetValue(separator, unquote: false); } 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(); return unquote ? text.Unquote() : text; } internal static bool IsCompressionExtension(this string value, CompressionMethod method) { string value2 = method.ToExtensionString(); StringComparison comparisonType = StringComparison.Ordinal; return value.StartsWith(value2, comparisonType); } internal static bool IsEqualTo(this int value, char c, Action<int> beforeComparing) { beforeComparing(value); return value == c; } internal static bool IsHttpMethod(this string value) { int result; switch (value) { default: result = ((value == "TRACE") ? 1 : 0); break; case "GET": case "HEAD": case "POST": case "PUT": case "DELETE": case "CONNECT": case "OPTIONS": result = 1; break; } return (byte)result != 0; } internal static bool IsPortNumber(this int value) { return value > 0 && value < 65536; } internal static bool IsReserved(this CloseStatusCode code) { return ((ushort)code).IsReservedStatusCode(); } internal static bool IsReservedStatusCode(this ushort code) { return code == 1004 || code == 1005 || code == 1006 || code == 1015; } internal static bool IsSupportedOpcode(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 ("\r\n\t".IndexOf(c) == -1) { return false; } if (c == '\n') { i++; if (i == length) { break; } c = value[i]; if (" \t".IndexOf(c) == -1) { 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 > '~') { return false; } if ("()<>@,;:\\\"/[]?={} \t".IndexOf(c) > -1) { return false; } } return true; } internal static bool KeepsAlive(this NameValueCollection headers, Version version) { StringComparison comparisonTypeForValue = StringComparison.OrdinalIgnoreCase; return (version > WebSocketSharp.Net.HttpVersion.Version10) ? (!headers.Contains("Connection", "close", comparisonTypeForValue)) : headers.Contains("Connection", "keep-alive", comparisonTypeForValue); } internal static bool MaybeUri(this string value) { int num = value.IndexOf(':'); if (num < 2 || num > 9) { return false; } string value2 = value.Substring(0, num); return value2.isPredefinedScheme(); } internal static string Quote(this string value) { string format = "\"{0}\""; string arg = value.Replace("\"", "\\\""); return string.Format(format, arg); } internal static byte[] ReadBytes(this Stream stream, int length) { byte[] array = new byte[length]; int num = 0; int num2 = 0; while (length > 0) { int num3 = stream.Read(array, num, length); if (num3 <= 0) { if (num2 >= _maxRetry) { return array.SubArray(0, num); } num2++; } else { num2 = 0; num += num3; length -= num3; } } return array; } internal static byte[] ReadBytes(this Stream stream, long length, int bufferLength) { using MemoryStream memoryStream = new MemoryStream(); byte[] buffer = new byte[bufferLength]; int num = 0; while (length > 0) { if (length < bufferLength) { bufferLength = (int)length; } int num2 = stream.Read(buffer, 0, bufferLength); if (num2 <= 0) { if (num >= _maxRetry) { break; } num++; } else { num = 0; memoryStream.Write(buffer, 0, num2); length -= num2; } } memoryStream.Close(); return memoryStream.ToArray(); } internal static void ReadBytesAsync(this Stream stream, int length, Action<byte[]> completed, Action<Exception> error) { byte[] ret = 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) { if (retry < _maxRetry) { retry++; stream.BeginRead(ret, offset, length, callback, null); } else if (completed != null) { completed(ret.SubArray(0, offset)); } } else if (num == length) { if (completed != null) { completed(ret); } } else { retry = 0; offset += num; length -= num; stream.BeginRead(ret, offset, length, callback, null); } } catch (Exception obj2) { if (error != null) { error(obj2); } } }; try { stream.BeginRead(ret, offset, length, callback, null); } catch (Exception obj) { if (error != null) { error(obj); } } } internal static void ReadBytesAsync(this Stream stream, long length, int bufferLength, Action<byte[]> completed, Action<Exception> error) { MemoryStream dest = new MemoryStream(); byte[] buff = new byte[bufferLength]; int retry = 0; Action<long> read = null; read = delegate(long len) { if (len < bufferLength) { bufferLength = (int)len; } stream.BeginRead(buff, 0, bufferLength, delegate(IAsyncResult ar) { try { int num = stream.EndRead(ar); if (num <= 0) { if (retry < _maxRetry) { int num2 = retry; retry = num2 + 1; read(len); } else { if (completed != null) { dest.Close(); byte[] obj2 = dest.ToArray(); completed(obj2); } dest.Dispose(); } } else { dest.Write(buff, 0, num); if (num == len) { if (completed != null) { dest.Close(); byte[] obj3 = dest.ToArray(); completed(obj3); } dest.Dispose(); } else { retry = 0; read(len - num); } } } catch (Exception obj4) { dest.Dispose(); if (error != null) { error(obj4); } } }, null); }; try { read(length); } catch (Exception obj) { dest.Dispose(); if (error != null) { error(obj); } } } internal static T[] Reverse<T>(this T[] array) { long num = array.LongLength; T[] array2 = new T[num]; long num2 = num - 1; for (long num3 = 0L; num3 <= num2; num3++) { array2[num3] = array[num2 - num3]; } return array2; } internal static IEnumerable<string> SplitHeaderValue(this string value, params char[] separators) { int len = value.Length; int end = len - 1; StringBuilder buff = new StringBuilder(32); bool escaped = false; bool quoted = false; for (int i = 0; i <= end; i++) { char c = value[i]; buff.Append(c); switch (c) { case '"': if (escaped) { escaped = false; } else { quoted = !quoted; } continue; case '\\': if (i == end) { break; } if (value[i + 1] == '"') { escaped = true; } continue; default: if (Array.IndexOf(separators, c) > -1 && !quoted) { buff.Length--; yield return buff.ToString(); buff.Length = 0; } continue; } break; } yield return buff.ToString(); } internal static byte[] ToByteArray(this Stream stream) { stream.Position = 0L; using MemoryStream memoryStream = new MemoryStream(); CopyTo(stream, memoryStream, 1024); memoryStream.Close(); return memoryStream.ToArray(); } internal static byte[] ToByteArray(this ushort value, ByteOrder order) { byte[] bytes = BitConverter.GetBytes(value); if (!order.IsHostOrder()) { Array.Reverse((Array)bytes); } return bytes; } internal static byte[] ToByteArray(this ulong value, ByteOrder order) { byte[] bytes = BitConverter.GetBytes(value); if (!order.IsHostOrder()) { Array.Reverse((Array)bytes); } return bytes; } internal static CompressionMethod ToCompressionMethod(this string value) { Array values = Enum.GetValues(typeof(CompressionMethod)); foreach (CompressionMethod item in values) { if (item.ToExtensionString() == value) { return item; } } return CompressionMethod.None; } internal static string ToExtensionString(this CompressionMethod method, params string[] parameters) { if (method == CompressionMethod.None) { return string.Empty; } string arg = method.ToString().ToLower(); string text = $"permessage-{arg}"; if (parameters == null || parameters.Length == 0) { return text; } string arg2 = parameters.ToString("; "); return $"{text}; {arg2}"; } internal static int ToInt32(this string numericString) { return int.Parse(numericString); } 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 { IPAddress[] hostAddresses = Dns.GetHostAddresses(value); return hostAddresses[0]; } catch { return null; } } internal static List<TSource> ToList<TSource>(this IEnumerable<TSource> source) { return new List<TSource>(source); } internal static string ToString(this IPAddress address, bool bracketIPv6) { return (bracketIPv6 && address.AddressFamily == AddressFamily.InterNetworkV6) ? $"[{address}]" : address.ToString(); } internal static ushort ToUInt16(this byte[] source, ByteOrder sourceOrder) { byte[] value = source.ToHostOrder(sourceOrder); return BitConverter.ToUInt16(value, 0); } internal static ulong ToUInt64(this byte[] source, ByteOrder sourceOrder) { byte[] value = source.ToHostOrder(sourceOrder); return BitConverter.ToUInt64(value, 0); } internal static Version ToVersion(this string versionString) { return new Version(versionString); } internal static IEnumerable<string> TrimEach(this IEnumerable<string> source) { foreach (string elm in source) { yield return elm.Trim(); } } internal static string TrimSlashFromEnd(this string value) { string text = value.TrimEnd(new char[1] { '/' }); return (text.Length > 0) ? text : "/"; } internal static string TrimSlashOrBackslashFromEnd(this string value) { string text = value.TrimEnd('/', '\\'); return (text.Length > 0) ? text : value[0].ToString(); } internal static bool TryCreateVersion(this string versionString, out Version result) { result = null; try { result = new Version(versionString); } catch { return false; } return true; } 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; } if (port == -1) { port = ((scheme == "ws") ? 80 : 443); uriString = $"{scheme}://{uri.Host}:{port}{uri.PathAndQuery}"; result = new Uri(uriString); } else { result = uri; } 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 == -1) { return value; } int num2 = value.LastIndexOf('"'); if (num2 == num) { return value; } int num3 = num2 - num - 1; return (num3 > 0) ? value.Substring(num + 1, num3).Replace("\\\"", "\"") : string.Empty; } internal static bool Upgrades(this NameValueCollection headers, string protocol) { StringComparison comparisonTypeForValue = StringComparison.OrdinalIgnoreCase; return headers.Contains("Upgrade", protocol, comparisonTypeForValue) && headers.Contains("Connection", "Upgrade", comparisonTypeForValue); } internal static string UrlDecode(this string value, Encoding encoding) { return (value.IndexOfAny(new char[2] { '%', '+' }) > -1) ? HttpUtility.UrlDecode(value, encoding) : value; } internal static string UrlEncode(this string value, Encoding encoding) { return HttpUtility.UrlEncode(value, encoding); } internal static void WriteBytes(this Stream stream, byte[] bytes, int bufferLength) { using MemoryStream sourceStream = new MemoryStream(bytes); CopyTo(sourceStream, stream, bufferLength); } internal static void WriteBytesAsync(this Stream stream, byte[] bytes, int bufferLength, Action completed, Action<Exception> error) { MemoryStream src = new MemoryStream(bytes); src.CopyToAsync(stream, bufferLength, delegate { if (completed != null) { completed(); } src.Dispose(); }, delegate(Exception ex) { src.Dispose(); if (error != null) { error(ex); } }); } 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) { return value > 999 && value < 5000; } public static bool IsEnclosedIn(this string value, char c) { if (value == null) { return false; } int length = value.Length; return length > 1 && value[0] == c && value[length - 1] == c; } public static bool IsHostOrder(this ByteOrder order) { return BitConverter.IsLittleEndian == (order == ByteOrder.Little); } public static bool IsLocal(this IPAddress address) { if (address == null) { throw new ArgumentNullException("address"); } 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; } } string hostName = Dns.GetHostName(); IPAddress[] hostAddresses = Dns.GetHostAddresses(hostName); IPAddress[] array = hostAddresses; foreach (IPAddress obj in array) { if (address.Equals(obj)) { return true; } } return false; } public static bool IsNullOrEmpty(this string value) { return value == null || value.Length == 0; } public static T[] SubArray<T>(this T[] array, int startIndex, int length) { if (array == null) { throw new ArgumentNullException("array"); } int num = array.Length; if (num == 0) { if (startIndex != 0) { throw new ArgumentOutOfRangeException("startIndex"); } if (length != 0) { throw new ArgumentOutOfRangeException("length"); } return array; } if (startIndex < 0 || startIndex >= num) { throw new ArgumentOutOfRangeException("startIndex"); } if (length < 0 || length > num - startIndex) { throw new ArgumentOutOfRangeException("length"); } if (length == 0) { return new T[0]; } if (length == num) { return array; } T[] array2 = new T[length]; Array.Copy(array, startIndex, array2, 0, length); return array2; } public static T[] SubArray<T>(this T[] array, long startIndex, long length) { if (array == null) { throw new ArgumentNullException("array"); } long num = array.LongLength; if (num == 0) { if (startIndex != 0) { throw new ArgumentOutOfRangeException("startIndex"); } if (length != 0) { throw new ArgumentOutOfRangeException("length"); } return array; } if (startIndex < 0 || startIndex >= num) { throw new ArgumentOutOfRangeException("startIndex"); } if (length < 0 || length > num - startIndex) { throw new ArgumentOutOfRangeException("length"); } if (length == 0) { return new T[0]; } if (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<int> action) { if (n > 0 && action != null) { for (int i = 0; i < n; i++) { action(i); } } } public static void Times(this long n, Action<long> action) { if (n > 0 && action != null) { for (long num = 0L; num < n; num++) { action(num); } } } public static byte[] ToHostOrder(this byte[] source, ByteOrder sourceOrder) { if (source == null) { throw new ArgumentNullException("source"); } if (source.Length < 2) { return source; } if (sourceOrder.IsHostOrder()) { return source; } return source.Reverse(); } public static string ToString<T>(this T[] array, string separator) { if (array == null) { throw new ArgumentNullException("array"); } int num = array.Length; if (num == 0) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(64); int num2 = num - 1; for (int i = 0; i < num2; i++) { stringBuilder.AppendFormat("{0}{1}", array[i], separator); } stringBuilder.AppendFormat("{0}", array[num2]); return stringBuilder.ToString(); } public static Uri ToUri(this string value) { if (value == null || value.Length == 0) { return null; } UriKind uriKind = (value.MaybeUri() ? UriKind.Absolute : UriKind.Relative); Uri.TryCreate(value, uriKind, out Uri result); return result; } } 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) { return; } if (_opcode == Opcode.Binary) { _dataSet = true; return; } if (_rawData.TryGetUTF8DecodedString(out var s)) { _data = s; } _dataSet = true; } } public class CloseEventArgs : EventArgs { private bool _clean; private PayloadData _payloadData; public ushort Code => _payloadData.Code; public string Reason => _payloadData.Reason; public bool WasClean => _clean; internal CloseEventArgs(PayloadData payloadData, bool clean) { _payloadData = payloadData; _clean = clean; } } public enum ByteOrder { Little, Big } 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 class WebSocket : IDisposable { private AuthenticationChallenge _authChallenge; private string _base64Key; private bool _client; private Action _closeContext; private CompressionMethod _compression; private WebSocketContext _context; private WebSocketSharp.Net.CookieCollection _cookies; private WebSocketSharp.Net.NetworkCredential _credentials; private bool _emitOnPing; private bool _enableRedirection; private string _extensions; private bool _extensionsRequested; private object _forMessageEventQueue; private object _forPing; private object _forSend; private object _forState; private MemoryStream _fragmentsBuffer; private bool _fragmentsCompressed; private Opcode _fragmentsOpcode; private const string _guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; private Func<WebSocketContext, string> _handshakeRequestChecker; private bool _ignoreExtensions; private bool _inContinuation; private volatile bool _inMessage; private volatile Logger _log; private static readonly int _maxRetryCountForConnect; private Action<MessageEventArgs> _message; private Queue<MessageEventArgs> _messageEventQueue; private uint _nonceCount; private string _origin; private ManualResetEvent _pongReceived; private bool _preAuth; private string _protocol; private string[] _protocols; private bool _protocolsRequested; private WebSocketSharp.Net.NetworkCredential _proxyCredentials; private Uri _proxyUri; private volatile WebSocketState _readyState; private ManualResetEvent _receivingExited; private int _retryCountForConnect; private bool _secure; private ClientSslConfiguration _sslConfig; private Stream _stream; private TcpClient _tcpClient; private Uri _uri; private const string _version = "13"; private TimeSpan _waitTime; internal static readonly byte[] EmptyBytes; internal static readonly int FragmentLength; internal static readonly RandomNumberGenerator RandomNumber; internal WebSocketSharp.Net.CookieCollection CookieCollection => _cookies; internal Func<WebSocketContext, string> CustomHandshakeRequestChecker { get { return _handshakeRequestChecker; } set { _handshakeRequestChecker = value; } } internal bool IgnoreExtensions { get { return _ignoreExtensions; } set { _ignoreExtensions = value; } } public CompressionMethod Compression { get { return _compression; } set { if (!_client) { string text = "The interface is not for the client."; throw new InvalidOperationException(text); } lock (_forState) { if (canSet()) { _compression = value; } } } } public IEnumerable<WebSocketSharp.Net.Cookie> Cookies { get { lock (_cookies.SyncRoot) { foreach (WebSocketSharp.Net.Cookie cookie in _cookies) { yield return cookie; } } } } public WebSocketSharp.Net.NetworkCredential Credentials => _credentials; public bool EmitOnPing { get { return _emitOnPing; } set { _emitOnPing = value; } } public bool EnableRedirection { get { return _enableRedirection; } set { if (!_client) { string text = "The interface is not for the client."; throw new InvalidOperationException(text); } lock (_forState) { if (canSet()) { _enableRedirection = value; } } } } public string Extensions => _extensions ?? string.Empty; public bool IsAlive => ping(EmptyBytes); public bool IsSecure => _secure; public Logger Log { get { return _log; } internal set { _log = value; } } public string Origin { get { return _origin; } set { if (!_client) { string text = "The interface is not for the client."; throw new InvalidOperationException(text); } if (!value.IsNullOrEmpty()) { if (!Uri.TryCreate(value, UriKind.Absolute, out Uri result)) { string text2 = "Not an absolute URI string."; throw new ArgumentException(text2, "value"); } if (result.Segments.Length > 1) { string text3 = "It includes the path segments."; throw new ArgumentException(text3, "value"); } } lock (_forState) { if (canSet()) { _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) { string text = "The interface is not for the client."; throw new InvalidOperationException(text); } if (!_secure) { string text2 = "The interface does not use a secure connection."; throw new InvalidOperationException(text2); } return getSslConfiguration(); } } public Uri Url => _client ? _uri : _context.RequestUri; public TimeSpan WaitTime { get { return _waitTime; } set { if (value <= TimeSpan.Zero) { string text = "Zero or less."; throw new ArgumentOutOfRangeException("value", text); } lock (_forState) { if (canSet()) { _waitTime = value; } } } } public event EventHandler<CloseEventArgs> OnClose; public event EventHandler<ErrorEventArgs> OnError; public event EventHandler<MessageEventArgs> OnMessage; public event EventHandler OnOpen; static WebSocket() { _maxRetryCountForConnect = 10; EmptyBytes = new byte[0]; FragmentLength = 1016; RandomNumber = new RNGCryptoServiceProvider(); } internal WebSocket(HttpListenerWebSocketContext context, string protocol) { _context = context; _protocol = protocol; _closeContext = context.Close; _log = 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; _log = 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; _log = new Logger(); _message = messagec; _retryCountForConnect = -1; _secure = _uri.Scheme == "wss"; _waitTime = TimeSpan.FromSeconds(5.0); init(); } private void abort(string reason, Exception exception) { ushort code = (ushort)((exception is WebSocketException) ? ((WebSocketException)exception).Code : 1006); abort(code, reason); } private void abort(ushort code, string reason) { PayloadData payloadData = new PayloadData(code, reason); close(payloadData, send: false, received: false); } private bool accept() { lock (_forState) { if (_readyState == WebSocketState.Open) { _log.Trace("The connection has already been established."); return false; } if (_readyState == WebSocketState.Closing) { _log.Error("The close process is in progress."); error("An error has occurred before accepting.", null); return false; } if (_readyState == WebSocketState.Closed) { _log.Error("The connection has been closed."); error("An error has occurred before accepting.", null); return false; } _readyState = WebSocketState.Connecting; bool flag = false; try { flag = acceptHandshake(); } catch (Exception ex) { _log.Fatal(ex.Message); _log.Debug(ex.ToString()); abort(1011, "An exception has occurred while accepting."); } if (!flag) { return false; } _readyState = WebSocketState.Open; return true; } } private bool acceptHandshake() { if (!checkHandshakeRequest(_context, out var text)) { _log.Error(text); _log.Debug(_context.ToString()); refuseHandshake(1002, "A handshake error has occurred."); return false; } if (!customCheckHandshakeRequest(_context, out text)) { _log.Error(text); _log.Debug(_context.ToString()); refuseHandshake(1002, "A handshake error has occurred."); return false; } _base64Key = _context.Headers["Sec-WebSocket-Key"]; if (_protocol != null && !_context.SecWebSocketProtocols.Contains((string p) => p == _protocol)) { _protocol = null; } if (!_ignoreExtensions) { string value = _context.Headers["Sec-WebSocket-Extensions"]; processSecWebSocketExtensionsClientHeader(value); } createHandshakeResponse().WriteTo(_stream); return true; } private bool canSet() { return _readyState == WebSocketState.New || _readyState == WebSocketState.Closed; } private bool checkHandshakeRequest(WebSocketContext context, out string message) { message = null; if (!context.IsWebSocketRequest) { message = "Not a WebSocket handshake request."; return false; } NameValueCollection headers = context.Headers; string text = headers["Sec-WebSocket-Key"]; if (text == null) { message = "The Sec-WebSocket-Key header is non-existent."; return false; } if (text.Length == 0) { message = "The Sec-WebSocket-Key header is invalid."; return false; } string text2 = headers["Sec-WebSocket-Version"]; if (text2 == null) { message = "The Sec-WebSocket-Version header is non-existent."; return false; } if (text2 != "13") { message = "The Sec-WebSocket-Version header is invalid."; return false; } string text3 = headers["Sec-WebSocket-Protocol"]; if (text3 != null && text3.Length == 0) { message = "The Sec-WebSocket-Protocol header is invalid."; return false; } if (!_ignoreExtensions) { string text4 = headers["Sec-WebSocket-Extensions"]; if (text4 != null && text4.Length == 0) { message = "The Sec-WebSocket-Extensions header is invalid."; return false; } } return true; } private bool checkHandshakeResponse(HttpResponse response, out string message) { message = null; if (response.IsRedirect) { message = "The redirection is indicated."; return false; } if (response.IsUnauthorized) { message = "The authentication is required."; return false; } if (!response.IsWebSocketResponse) { message = "Not a WebSocket handshake response."; return false; } NameValueCollection headers = response.Headers; string text = headers["Sec-WebSocket-Accept"]; if (text == null) { message = "The Sec-WebSocket-Accept header is non-existent."; return false; } if (text != CreateResponseKey(_base64Key)) { message = "The Sec-WebSocket-Accept header is invalid."; return false; } string text2 = headers["Sec-WebSocket-Version"]; if (text2 != null && text2 != "13") { message = "The Sec-WebSocket-Version header is invalid."; return false; } string subp = headers["Sec-WebSocket-Protocol"]; if (subp == null) { if (_protocolsRequested) { message = "The Sec-WebSocket-Protocol header is non-existent."; return false; } } else if (!_protocolsRequested || subp.Length <= 0 || !_protocols.Contains((string p) => p == subp)) { message = "The Sec-WebSocket-Protocol header is invalid."; return false; } string text3 = headers["Sec-WebSocket-Extensions"]; if (text3 != null && !validateSecWebSocketExtensionsServerHeader(text3)) { message = "The Sec-WebSocket-Extensions header is invalid."; return false; } return true; } private static bool checkProtocols(string[] protocols, out string message) { message = null; Func<string, bool> condition = (string p) => p.IsNullOrEmpty() || !p.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 checkProxyConnectResponse(HttpResponse response, out string message) { message = null; if (response.IsProxyAuthenticationRequired) { message = "The proxy authentication is required."; return false; } if (!response.IsSuccess) { message = "The proxy has failed a connection to the requested URL."; return false; } return true; } private bool checkReceivedFrame(WebSocketFrame frame, out string message) { message = null; if (frame.IsMasked) { if (_client) { message = "A frame from the server is masked."; return false; } } else if (!_client) { message = "A frame from a client is not masked."; return false; } if (frame.IsCompressed) { if (_compression == CompressionMethod.None) { message = "A frame is compressed without any agreement for it."; return false; } if (!frame.IsData) { message = "A non data frame is compressed."; return false; } } if (frame.IsData && _inContinuation) { message = "A data frame was received while receiving continuation frames."; return false; } if (frame.IsControl) { if (frame.Fin == Fin.More) { message = "A control frame is fragmented."; return false; } if (frame.PayloadLength > 125) { message = "The payload length of a control frame is greater than 125."; 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) { _log.Trace("The close process is already in progress."); return; } if (_readyState == WebSocketState.Closed) { _log.Trace("The connection has already been closed."); return; } if (code == 1005) { close(PayloadData.Empty, send: true, received: false); return; } PayloadData payloadData = new PayloadData(code, reason); bool flag = !code.IsReservedStatusCode(); close(payloadData, flag, received: false); } private void close(PayloadData payloadData, bool send, bool received) { lock (_forState) { if (_readyState == WebSocketState.Closing) { _log.Trace("The close process is already in progress."); return; } if (_readyState == WebSocketState.Closed) { _log.Trace("The connection has already been closed."); return; } send = send && _readyState == WebSocketState.Open; _readyState = WebSocketState.Closing; } _log.Trace("Begin closing the connection."); bool clean = closeHandshake(payloadData, send, received); releaseResources(); _log.Trace("End closing the connection."); _readyState = WebSocketState.Closed; CloseEventArgs e = new CloseEventArgs(payloadData, clean); try { this.OnClose.Emit(this, e); } catch (Exception ex) { _log.Error(ex.Message); _log.Debug(ex.ToString()); } } private void closeAsync(ushort code, string reason) { if (_readyState == WebSocketState.Closing) { _log.Trace("The close process is already in progress."); return; } if (_readyState == WebSocketState.Closed) { _log.Trace("The connection has already been closed."); return; } if (code == 1005) { closeAsync(PayloadData.Empty, send: true, received: false); return; } PayloadData payloadData = new PayloadData(code, reason); bool flag = !code.IsReservedStatusCode(); closeAsync(payloadData, flag, received: false); } private void closeAsync(PayloadData payloadData, bool send, bool received) { Action<PayloadData, bool, bool> closer = close; closer.BeginInvoke(payloadData, send, received, delegate(IAsyncResult ar) { closer.EndInvoke(ar); }, null); } private bool closeHandshake(PayloadData payloadData, bool send, bool received) { bool flag = false; if (send) { WebSocketFrame webSocketFrame = WebSocketFrame.CreateCloseFrame(payloadData, _client); byte[] bytes = webSocketFrame.ToArray(); flag = sendBytes(bytes); if (_client) { webSocketFrame.Unmask(); } } if (!received && flag && _receivingExited != null) { received = _receivingExited.WaitOne(_waitTime); } bool flag2 = flag && received; string text = $"The closing was clean? {flag2} (sent: {flag} received: {received})"; _log.Debug(text); return flag2; } private bool connect() { if (_readyState == WebSocketState.Connecting) { _log.Trace("The connect process is in progress."); return false; } lock (_forState) { if (_readyState == WebSocketState.Open) { _log.Trace("The connection has already been established."); return false; } if (_readyState == WebSocketState.Closing) { _log.Error("The close process is in progress."); error("An error has occurred before connecting.", null); return false; } if (_retryCountForConnect >= _maxRetryCountForConnect) { _log.Error("An opportunity for reconnecting has been lost."); error("An error has occurred before connecting.", null); return false; } _retryCountForConnect++; _readyState = WebSocketState.Connecting; bool flag = false; try { flag = doHandshake(); } catch (Exception ex) { _log.Fatal(ex.Message); _log.Debug(ex.ToString()); abort("An exception has occurred while connecting.", ex); } if (!flag) { return false; } _retryCountForConnect = -1; _readyState = WebSocketState.Open; return true; } } private AuthenticationResponse createAuthenticationResponse() { if (_credentials == null) { return null; } if (_authChallenge != null) { AuthenticationResponse authenticationResponse = new AuthenticationResponse(_authChallenge, _credentials, _nonceCount); _nonceCount = authenticationResponse.NonceCount; return authenticationResponse; } return _preAuth ? new AuthenticationResponse(_credentials) : null; } 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) { return null; } stringBuilder.Length = length - 2; return stringBuilder.ToString(); } private HttpResponse createHandshakeFailureResponse() { HttpResponse httpResponse = HttpResponse.CreateCloseResponse(WebSocketSharp.Net.HttpStatusCode.BadRequest); httpResponse.Headers["Sec-WebSocket-Version"] = "13"; return httpResponse; } private HttpRequest createHandshakeRequest() { HttpRequest httpRequest = HttpRequest.CreateWebSocketHandshakeRequest(_uri); NameValueCollection headers = httpRequest.Headers; headers["Sec-WebSocket-Key"] = _base64Key; headers["Sec-WebSocket-Version"] = "13"; if (!_origin.IsNullOrEmpty()) { headers["Origin"] = _origin; } if (_protocols != null) { headers["Sec-WebSocket-Protocol"] = _protocols.ToString(", "); _protocolsRequested = true; } string text = createExtensions(); if (text != null) { headers["Sec-WebSocket-Extensions"] = text; _extensionsRequested = true; } AuthenticationResponse authenticationResponse = createAuthenticationResponse(); if (authenticationResponse != null) { headers["Authorization"] = authenticationResponse.ToString(); } if (_cookies.Count > 0) { httpRequest.SetCookies(_cookies); } return httpRequest; } private HttpResponse createHandshakeResponse() { HttpResponse httpResponse = HttpResponse.CreateWebSocketHandshakeResponse(); 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 true; } message = _handshakeRequestChecker(context); return message == null; } private MessageEventArgs dequeueFromMessageEventQueue() { lock (_forMessageEventQueue) { return (_messageEventQueue.Count > 0) ? _messageEventQueue.Dequeue() : null; } } private bool doHandshake() { setClientStream(); HttpResponse httpResponse = sendHandshakeRequest(); if (!checkHandshakeResponse(httpResponse, out var text)) { _log.Error(text); _log.Debug(httpResponse.ToString()); abort(1002, "A handshake error has occurred."); return false; } if (_protocolsRequested) { _protocol = httpResponse.Headers["Sec-WebSocket-Protocol"]; } if (_extensionsRequested) { string text2 = httpResponse.Headers["Sec-WebSocket-Extensions"]; if (text2 == null) { _compression = CompressionMethod.None; } else { _extensions = text2; } } WebSocketSharp.Net.CookieCollection cookies = httpResponse.Cookies; if (cookies.Count > 0) { _cookies.SetOrRemove(cookies); } return true; } private void enqueueToMessageEventQueue(MessageEventArgs e) { lock (_forMessageEventQueue) { _messageEventQueue.Enqueue(e); } } private void error(string message, Exception exception) { ErrorEventArgs e = new ErrorEventArgs(message, exception); try { this.OnError.Emit(this, e); } catch (Exception ex) { _log.Error(ex.Message); _log.Debug(ex.ToString()); } } private ClientSslConfiguration getSslConfiguration() { if (_sslConfig == null) { _sslConfig = new ClientSslConfiguration(_uri.DnsSafeHost); } return _sslConfig; } private void init() { _compression = CompressionMethod.None; _cookies = new WebSocketSharp.Net.CookieCollection(); _forPing = new object(); _forSend = new object(); _forState = new object(); _messageEventQueue = new Queue<MessageEventArgs>(); _forMessageEventQueue = ((ICollection)_messageEventQueue).SyncRoot; _readyState = WebSocketState.New; } private void message() { MessageEventArgs obj = null; lock (_forMessageEventQueue) { if (_inMessage || _messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) { return; } obj = _messageEventQueue.Dequeue(); _inMessage = true; } _message(obj); } private void messagec(MessageEventArgs e) { while (true) { try { this.OnMessage.Emit(this, e); } catch (Exception ex) { _log.Error(ex.Message); _log.Debug(ex.ToString()); error("An exception has occurred during an OnMessage event.", ex); } lock (_forMessageEventQueue) { if (_messageEventQueue.Count == 0) { _inMessage = false; break; } if (_readyState != WebSocketState.Open) { _inMessage = false; break; } e = _messageEventQueue.Dequeue(); } bool flag = true; } } private void messages(MessageEventArgs e) { try { this.OnMessage.Emit(this, e); } catch (Exception ex) { _log.Error(ex.Message); _log.Debug(ex.ToString()); error("An exception has occurred during an OnMessage event.", ex); } lock (_forMessageEventQueue) { if (_messageEventQueue.Count == 0) { _inMessage = false; return; } if (_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) { _log.Error(ex.Message); _log.Debug(ex.ToString()); error("An exception has occurred during the OnOpen event.", ex); } MessageEventArgs obj = null; lock (_forMessageEventQueue) { if (_messageEventQueue.Count == 0) { _inMessage = false; return; } if (_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; bool flag = !payloadData.HasReservedCode; close(payloadData, flag, received: true); return false; } private bool processDataFrame(WebSocketFrame frame) { MessageEventArgs e = (frame.IsCompressed ? new MessageEventArgs(frame.Opcode, frame.PayloadData.ApplicationData.Decompress(_compression)) : new MessageEventArgs(frame)); enqueueToMessageEventQueue(e); 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()); MessageEventArgs e = new MessageEventArgs(_fragmentsOpcode, rawData); enqueueToMessageEventQueue(e); } _fragmentsBuffer = null; _inContinuation = false; } return true; } private bool processPingFrame(WebSocketFrame frame) { _log.Trace("A ping was received."); WebSocketFrame webSocketFrame = WebSocketFrame.CreatePongFrame(frame.PayloadData, _client); lock (_forState) { if (_readyState != WebSocketState.Open) { _log.Trace("A pong to this ping cannot be sent."); return true; } byte[] bytes = webSocketFrame.ToArray(); if (!sendBytes(bytes)) { return false; } } _log.Trace("A pong to this ping has been sent."); if (_emitOnPing) { if (_client) { webSocketFrame.Unmask(); } MessageEventArgs e = new MessageEventArgs(frame); enqueueToMessageEventQueue(e); } return true; } private bool processPongFrame(WebSocketFrame frame) { _log.Trace("A pong was received."); try { _pongReceived.Set(); } catch (NullReferenceException) { return false; } catch (ObjectDisposedException) { return false; } _log.Trace("It has been signaled."); return true; } private bool processReceivedFrame(WebSocketFrame frame) { if (!checkReceivedFrame(frame, out var text)) { _log.Error(text); _log.Debug(frame.ToString(dump: false)); abort(1002, "An error has occurred while receiving."); return false; } frame.Unmask(); return frame.IsFragment ? processFragmentFrame(frame) : (frame.IsData ? processDataFrame(frame) : (frame.IsPing ? processPingFrame(frame) : (frame.IsPong ? processPongFrame(frame) : (frame.IsClose ? processCloseFrame(frame) : processUnsupportedFrame(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 text = item.Trim(); if (text.Length != 0 && !flag && text.IsCompressionExtension(CompressionMethod.Deflate)) { _compression = CompressionMethod.Deflate; string arg = _compression.ToExtensionString("client_no_context_takeover", "server_no_context_takeover"); stringBuilder.AppendFormat("{0}, ", arg); flag = true; } } int length = stringBuilder.Length; if (length > 2) { stringBuilder.Length = length - 2; _extensions = stringBuilder.ToString(); } } private bool processUnsupportedFrame(WebSocketFrame frame) { _log.Fatal("An unsupported frame was received."); _log.Debug(frame.ToString(dump: false)); abort(1003, "There is no way to handle it."); return false; } private void refuseHandshake(ushort code, string reason) { createHandshakeFailureResponse().WriteTo(_stream); abort(code, reason); } 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(byte[] rawFrame) { lock (_forState) { if (_readyState != WebSocketState.Open) { _log.Error("The current state of the interface is not Open."); return false; } return sendBytes(rawFrame); } } private bool send(Opcode opcode, Stream sourceStream) { lock (_forSend) { Stream stream = sourceStream; bool flag = false; bool flag2 = false; try { if (_compression != 0) { stream = sourceStream.Compress(_compression); flag = true; } flag2 = send(opcode, stream, flag); if (!flag2) { error("A send has failed.", null); } } catch (Exception ex) { _log.Error(ex.Message); _log.Debug(ex.ToString()); error("An exception has occurred during a send.", ex); } finally { if (flag) { stream.Dispose(); } sourceStream.Dispose(); } return flag2; } } private bool send(Opcode opcode, Stream dataStream, bool compressed) { long length = dataStream.Length; if (length == 0) { 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]; return dataStream.Read(array, 0, num2) == num2 && send(Fin.Final, opcode, array, compressed); case 1L: if (num2 == 0) { array = new byte[FragmentLength]; return dataStream.Read(array, 0, FragmentLength) == FragmentLength && send(Fin.Final, opcode, array, compressed); } break; } array = new byte[FragmentLength]; if (dataStream.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 (dataStream.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]; } return dataStream.Read(array, 0, num2) == num2 && send(Fin.Final, Opcode.Cont, array, compressed: false); } private bool send(Fin fin, Opcode opcode, byte[] data, bool compressed) { WebSocketFrame webSocketFrame = new WebSocketFrame(fin, opcode, data, compressed, _client); byte[] rawFrame = webSocketFrame.ToArray(); return send(rawFrame); } private void sendAsync(Opcode opcode, Stream sourceStream, Action<bool> completed) { Func<Opcode, Stream, bool> sender = send; sender.BeginInvoke(opcode, sourceStream, delegate(IAsyncResult ar) { try { bool obj = sender.EndInvoke(ar); if (completed != null) { completed(obj); } } catch (Exception ex) { _log.Error(ex.Message); _log.Debug(ex.ToString()); error("An exception 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) { _log.Error(ex.Message); _log.Debug(ex.ToString()); return false; } return true; } private HttpResponse sendHandshakeRequest() { HttpRequest httpRequest = createHandshakeRequest(); int millisecondsTimeout = 90000; HttpResponse response = httpRequest.GetResponse(_stream, millisecondsTimeout); if (response.IsUnauthorized) { string value = response.Headers["WWW-Authenticate"]; if (value.IsNullOrEmpty()) { _log.Debug("No authentication challenge is specified."); return response; } AuthenticationChallenge authenticationChallenge = AuthenticationChallenge.Parse(value); if (authenticationChallenge == null) { _log.Debug("An invalid authentication challenge is specified."); return response; } _authChallenge = authenticationChallenge; if (_credentials == null) { return response; } AuthenticationResponse authenticationResponse = new AuthenticationResponse(_authChallenge, _credentials, _nonceCount); _nonceCount = authenticationResponse.NonceCount; httpRequest.Headers["Authorization"] = authenticationResponse.ToString(); if (response.CloseConnection) { releaseClientResources(); setClientStream(); } millisecondsTimeout = 15000; response = httpRequest.GetResponse(_stream, millisecondsTimeout); } if (response.IsRedirect) { if (!_enableRedirection) { return response; } string text = response.Headers["Location"]; if (text.IsNullOrEmpty()) { _log.Debug("No URL to redirect is located."); return response; } if (!text.TryCreateWebSocketUri(out var result, out var _)) { _log.Debug("An invalid URL to redirect is located."); return response; } releaseClientResources(); _uri = result; _secure = result.Scheme == "wss"; setClientStream(); return sendHandshakeRequest(); } return response; } private HttpResponse sendProxyConnectRequest() { HttpRequest httpRequest = HttpRequest.CreateConnectRequest(_uri); int millisecondsTimeout = 90000; HttpResponse response = httpRequest.GetResponse(_stream, millisecondsTimeout); if (response.IsProxyAuthenticationRequired) { if (_proxyCredentials == null) { return response; } string value = response.Headers["Proxy-Authenticate"]; if (value.IsNullOrEmpty()) { _log.Debug("No proxy authentication challenge is specified."); return response; } AuthenticationChallenge authenticationChallenge = AuthenticationChallenge.Parse(value); if (authenticationChallenge == null) { _log.Debug("An invalid proxy authentication challenge is specified."); return response; } AuthenticationResponse authenticationResponse = new AuthenticationResponse(authenticationChallenge, _proxyCredentials, 0u); httpRequest.Headers["Proxy-Authorization"] = authenticationResponse.ToString(); if (response.CloseConnection) { releaseClientResources(); _tcpClient = new TcpClient(_proxyUri.DnsSafeHost, _proxyUri.Port); _stream = _tcpClient.GetStream(); } millisecondsTimeout = 15000; response = httpRequest.GetResponse(_stream, millisecondsTimeout); } return response; } private void setClientStream() { if (_proxyUri != null) { _tcpClient = new TcpClient(_proxyUri.DnsSafeHost, _proxyUri.Port); _stream = _tcpClient.GetStream(); HttpResponse response = sendProxyConnectRequest(); if (!checkProxyConnectResponse(response, out var text)) { throw new WebSocketException(text); } } else { _tcpClient = new TcpClient(_uri.DnsSafeHost, _uri.Port); _stream = _tcpClient.GetStream(); } if (_secure) { ClientSslConfiguration sslConfiguration = getSslConfiguration(); string targetHost = sslConfiguration.TargetHost; if (targetHost != _uri.DnsSafeHost) { string text2 = "An invalid host name is specified."; throw new WebSocketException(CloseStatusCode.TlsHandshakeFailure, text2); } 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) { message(); } } }, delegate(Exception ex) { _log.Fatal(ex.Message); _log.Debug(ex.ToString()); abort("An exception has occurred while receiving.", ex); }); }; receive(); } private bool validateSecWebSocketExtensionsServerHeader(string value) { if (!_extensionsRequested) { return false; } if (value.Length == 0) { return false; } bool flag = _compression != CompressionMethod.None; foreach (string item in value.SplitHeaderValue(',')) { string text = item.Trim(); if (flag && text.IsCompressionExtension(_compression)) { string param1 = "server_no_context_takeover"; string param2 = "client_no_context_takeover"; if (!text.Contains(param1)) { return false; } string name = _compression.ToExtensionString(); if (text.SplitHeaderValue(';').Contains(delegate(string t) { t = t.Trim(); bool flag2 = t == name || t == param1 || t == param2; return !flag2; })) { return false; } flag = false; continue; } return false; } return true; } internal void Accept() { if (accept()) { open(); } } internal void AcceptAsync() { Func<bool> acceptor = accept; acceptor.BeginInvoke(delegate(IAsyncResult ar) { if (acceptor.EndInvoke(ar)) { open(); } }, null); } internal void Close(PayloadData payloadData, byte[] rawFrame) { lock (_forState) { if (_readyState == WebSocketState.Closing) { _log.Trace("The close process is already in progress."); return; } if (_readyState == WebSocketState.Closed) { _log.Trace("The connection has already been closed."); return; } _readyState = WebSocketState.Closing; } _log.Trace("Begin closing the connection."); bool flag = rawFrame != null && sendBytes(rawFrame); bool flag2 = flag && _receivingExited != null && _receivingExited.WaitOne(_waitTime); bool flag3 = flag && flag2; string text = $"The closing was clean? {flag3} (sent: {flag} received: {flag2})"; _log.Debug(text); releaseServerResources(); releaseCommonResources(); _log.Trace("End closing the connection."); _readyState = WebSocketState.Closed; CloseEventArgs e = new CloseEventArgs(payloadData, flag3); try { this.OnClose.Emit(this, e); } catch (Exception ex) { _log.Error(ex.Message); _log.Debug(ex.ToString()); } } internal static string CreateBase64Key() { byte[] array = new byte[16]; RandomNumber.GetBytes(array); return Convert.ToBase64String(array); } internal static string CreateResponseKey(string base64Key) { SHA1 sHA = new SHA1CryptoServiceProvider(); string s = base64Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; byte[] uTF8EncodedBytes = s.GetUTF8EncodedBytes(); byte[] inArray = sHA.ComputeHash(uTF8EncodedBytes); return Convert.ToBase64String(inArray); } internal bool Ping(byte[] rawFrame) { if (_readyState != WebSocketState.Open) { return false; } ManualResetEvent pongReceived = _pongReceived; if (pongReceived == null) { return false; } lock (_forPing) { try { pongReceived.Reset(); if (!send(rawFrame)) { return false; } return pongReceived.WaitOne(_waitTime); } catch (ObjectDisposedException) { return false; } } } internal void Send(Opcode opcode, byte[] data, Dictionary<CompressionMethod, byte[]> cache) { lock (_forSend) { 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); } send(value); } } internal void Send(Opcode opcode, Stream sourceStream, Dictionary<CompressionMethod, Stream> cache) { lock (_forSend) { if (!cache.TryGetValue(_compression, out var value)) { value = sourceStream.Compress(_compression); cache.Add(_compression, value); } else { value.Position = 0L; } send(opcode, value, _compression != CompressionMethod.None); } } public void Close() { close(1005, string.Empty); } public void Close(ushort code) { Close(code, string.Empty); } public void Close(CloseStatusCode code) { Close(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) { string text2 = "1011 cannot be used."; throw new ArgumentException(text2, "code"); } if (!_client && code == 1010) { string text3 = "1010 cannot be used."; throw new ArgumentException(text3, "code"); } if (reason.IsNullOrEmpty()) { close(code, string.Empty); return; } if (code == 1005) { string text4 = "1005 cannot be used."; throw new ArgumentException(text4, "code"); } if (!reason.TryGetUTF8EncodedBytes(out var bytes)) { string text5 = "It could not be UTF-8-encoded."; throw new ArgumentException(text5, "reason"); } if (bytes.Length > 123) { string text6 = "Its size is greater than 123 bytes."; throw new ArgumentOutOfRangeException("reason", text6); } close(code, reason); } public void Close(CloseStatusCode code, string reason) { if (_client && code == CloseStatusCode.ServerError) { string text = "ServerError cannot be used."; throw new ArgumentException(text, "code"); } if (!_client && code == CloseStatusCode.MandatoryExtension) { string text2 = "MandatoryExtension cannot be used."; throw new ArgumentException(text2, "code"); } if (reason.IsNullOrEmpty()) { close((ushort)code, string.Empty); return; } if (code == CloseStatusCode.NoStatus) { string text3 = "NoStatus cannot be used."; throw new ArgumentException(text3, "code"); } if (!reason.TryGetUTF8EncodedBytes(out var bytes)) { string text4 = "It could not be UTF-8-encoded."; throw new ArgumentException(text4, "reason"); } if (bytes.Length > 123) { string text5 = "Its size is greater than 123 bytes."; throw new ArgumentOutOfRangeException("reason", text5); } close((ushort)code, reason); } public void CloseAsync() { closeAsync(1005, string.Empty); } public void CloseAsync(ushort code) { CloseAsync(code, string.Empty); } public void CloseAsync(CloseStatusCode code) { CloseAsync(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) { string text2 = "1011 cannot be used."; throw new ArgumentException(text2, "code"); } if (!_client && code == 1010) { string text3 = "1010 cannot be used."; throw new ArgumentException(text3, "code"); } if (reason.IsNullOrEmpty()) { closeAsync(code, string.Empty); return; } if (code == 1005) { string text4 = "1005 cannot be used."; throw new ArgumentException(text4, "code"); } if (!reason.TryGetUTF8EncodedBytes(out var bytes)) { string text5 = "It could not be UTF-8-encoded."; throw new ArgumentException(text5, "reason"); } if (bytes.Length > 123) { string text6 = "Its size is greater than 123 bytes."; throw new ArgumentOutOfRangeException("reason", text6); } closeAsync(code, reason); } public void CloseAsync(CloseStatusCode code, string reason) { if (_client && code == CloseStatusCode.ServerError) { string text = "ServerError cannot be used."; throw new ArgumentException(text, "code"); } if (!_client && code == CloseStatusCode.MandatoryExtension) { string text2 = "MandatoryExtension cannot be used."; throw new ArgumentException(text2, "code"); } if (reason.IsNullOrEmpty()) { closeAsync((ushort)code, string.Empty); return; } if (code == CloseStatusCode.NoStatus) { string text3 = "NoStatus cannot be used."; throw new ArgumentException(text3, "code"); } if (!reason.TryGetUTF8EncodedBytes(out var bytes)) { string text4 = "It could not be UTF-8-encoded."; throw new ArgumentException(text4, "reason"); } if (bytes.Length > 123) { string text5 = "Its size is greater than 123 bytes."; throw new ArgumentOutOfRangeException("reason", text5); } closeAsync((ushort)code, reason); } public void Connect() { if (!_client) { string text = "The interface is not for the client."; throw new InvalidOperationException(text); } if (_retryCountForConnect >= _maxRetryCountForConnect) { string text2 = "A series of reconnecting has failed."; throw new InvalidOperationException(text2); } if (connect()) { open(); } } public void ConnectAsync() { if (!_client) { string text = "The interface is not for the client."; throw new InvalidOperationException(text); } if (_retryCountForConnect >= _maxRetryCountForConnect) { string text2 = "A series of reconnecting has failed."; throw new InvalidOperationException(text2); } Func<bool> 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)) { string text = "It could not be UTF-8-encoded."; throw new ArgumentException(text, "message"); } if (bytes.Length > 125) { string text2 = "Its size is greater than 125 bytes."; throw new ArgumentOutOfRangeException("message", text2); } return ping(bytes); } public void Send(byte[] data) { if (_readyState != WebSocketState.Open) { string text = "The current state of the interface is not Open."; throw new InvalidOperationException(text); } if (data == null) { throw new ArgumentNullException("data"); } send(Opcode.Binary, new MemoryStream(data)); } public void Send(FileInfo fileInfo) { if (_readyState != WebSocketState.Open) { string text = "The current state of the interface is not Open."; throw new InvalidOperationException(text); } if (fileInfo == null) { throw new ArgumentNullException("fileInfo"); } if (!fileInfo.Exists) { string text2 = "The file does not exist."; throw new ArgumentException(text2, "fileInfo"); } if (!fileInfo.TryOpenRead(out var fileStream)) { string text3 = "The file could not be opened."; throw new ArgumentException(text3, "fileInfo"); } send(Opcode.Binary, fileStream); } public void Send(string data) { if (_readyState != WebSocketState.Open) { string text = "The current state of the interface is not Open."; throw new InvalidOperationException(text); } if (data == null) { throw new ArgumentNullException("data"); } if (!data.TryGetUTF8EncodedBytes(out var bytes)) { string text2 = "It could not be UTF-8-encoded."; throw new ArgumentException(text2, "data"); } send(Opcode.Text, new MemoryStream(bytes)); } public void Send(Stream stream, int length) { if (_readyState != WebSocketState.Open) { string text = "The current state of the interface is not Open."; throw new InvalidOperationException(text); } if (stream == null) { throw new ArgumentNullException("stream"); } if (!stream.CanRead) { string text2 = "It cannot be read."; throw new ArgumentException(text2, "stream"); } if (length < 1) { string text3 = "Less than 1."; throw new ArgumentException(text3, "length"); } byte[] array = stream.ReadBytes(length); int num = array.Length; if (num == 0) { string text4 = "No data could be read from it."; throw new ArgumentException(text4, "stream"); } if (num < length) { string format = "Only {0} byte(s) of data could be read from the stream."; string text5 = string.Format(format, num); _log.Warn(text5); } send(Opcode.Binary, new MemoryStream(array)); } public void SendAsync(byte[] data, Action<bool> completed) { if (_readyState != WebSocketState.Open) { string text = "The current state of the interface is not Open."; throw new InvalidOperationException(text); } if (data == null) { throw new ArgumentNullException("data"); } sendAsync(Opcode.Binary, new MemoryStream(data), completed); } public void SendAsync(FileInfo fileInfo, Action<bool> completed) { if (_readyState != WebSocketState.Open) { string text = "The current state of the interface is not Open."; throw new InvalidOperationException(text); } if (fileInfo == null) { throw new ArgumentNullException("fileInfo"); } if (!fileInfo.Exists) { string text2 = "The file does not exist."; throw new ArgumentException(text2, "fileInfo"); } if (!fileInfo.TryOpenRead(out var fileStream)) { string text3 = "The file could not be opened."; throw new ArgumentException(text3, "fileInfo"); } sendAsync(Opcode.Binary, fileStream, completed); } public void SendAsync(string data, Action<bool> completed) { if (_readyState != WebSocketState.Open) { string text = "The current state of the interface is not Open."; throw new InvalidOperationException(text); } if (data == null) { throw new ArgumentNullException("data"); } if (!data.TryGetUTF8EncodedBytes(out var bytes)) { string text2 = "It could not be UTF-8-encoded."; throw new ArgumentException(text2, "data"); } sendAsync(Opcode.Text, new MemoryStream(bytes), completed); } public void SendAsync(Stream stream, int length, Action<bool> completed) { if (_readyState != WebSocketState.Open) { string text = "The current state of the interface is not Open."; throw new InvalidOperationException(text); } if (stream == null) { throw new ArgumentNullException("stream"); } if (!stream.CanRead) { string text2 = "It cannot be read."; throw new ArgumentException(text2, "stream"); } if (length < 1) { string text3 = "Less than 1."; throw new ArgumentException(text3, "length"); } byte[] array = stream.ReadBytes(length); int num = array.Length; if (num == 0) { string text4 = "No data could be read from it."; throw new ArgumentException(text4, "stream"); } if (num < length) { string format = "Only {0} byte(s) of data could be read from the stream."; string text5 = string.Format(format, num); _log.Warn(text5); } sendAsync(Opcode.Binary, new MemoryStream(array), completed); } public void SetCookie(WebSocketSharp.Net.Cookie cookie) { if (!_client) { string text = "The interface is not for the client."; throw new InvalidOperationException(text); } if (cookie == null) { throw new ArgumentNullException("cookie"); } lock (_forState) { if (!canSet()) { return; } lock (_cookies.SyncRoot) { _cookies.SetOrRemove(cookie); } } } public void SetCredentials(string username, string password, bool preAuth) { if (!_client) { string text = "The interface is not for the client."; throw new InvalidOperationException(text); } if (!username.IsNullOrEmpty() && (Ext.Contains(username, ':') || !username.IsText())) { string text2 = "It contains an invalid character."; throw new ArgumentException(text2, "username"); } if (!password.IsNullOrEmpty() && !password.IsText()) { string text3 = "It contains an invalid character."; throw new ArgumentException(text3, "password"); } lock (_forState) { if (canSet()) { if (username.IsNullOrEmpty()) { _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 (!_client) { string text = "The interface is not for the client."; throw new InvalidOperationException(text); } Uri result = null; if (!url.IsNullOrEmpty()) { if (!Uri.TryCreate(url, UriKind.Absolute, out result)) { string text2 = "Not an absolute URI string."; throw new ArgumentException(text2, "url"); } if (result.Scheme != "http") { string text3 = "The scheme part is not http."; throw new ArgumentException(text3, "url"); } if (result.Segments.Length > 1) { string text4 = "It includes the path segments."; throw new ArgumentException(text4, "url"); } } if (!username.IsNullOrEmpty() && (Ext.Contains(username, ':') || !username.IsText())) { string text5 = "It contains an invalid character."; throw new ArgumentException(text5, "username"); } if (!password.IsNullOrEmpty() && !password.IsText()) { string text6 = "It contains an invalid character."; throw new ArgumentException(text6, "password"); } lock (_forState) { if (canSet()) { if (url.IsNullOrEmpty()) { _proxyUri = null; _proxyCredentials = null; } else { _proxyUri = result; _proxyCredentials = ((!username.IsNullOrEmpty()) ? new WebSocketSharp.Net.NetworkCredential(username, password, $"{_uri.DnsSafeHost}:{_uri.Port}") : null); } } } } void IDisposable.Dispose() { close(1001, string.Empty); } } 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 } internal enum Fin : byte { More, Final } internal enum Mask : byte { Off, On } internal enum Opcode : byte { Cont = 0, Text = 1, Binary = 2, Close = 8, Ping = 9, Pong = 10 } internal class PayloadData : IEnumerable<byte>, IEnumerable { private byte[] _data; private long _extDataLength; private long _length; public static readonly PayloadData Empty; public static readonly ulong MaxLength; internal ushort Code => (ushort)((_length >= 2) ? _data.SubArray(0, 2).ToUInt16(ByteOrder.Big) : 1005); internal long ExtensionDataLength { get { return _extDataLength; } set { _extDataLength = value; } } internal bool HasReservedCode => _length >= 2 && Code.IsReservedStatusCode(); internal string Reason { get { if (_length <= 2) { return string.Empty; } byte[] bytes = _data.SubArray(2L, _length - 2); string s; return bytes.TryGetUTF8DecodedString(out s) ? s : string.Empty; } } public byte[] ApplicationData => (_extDataLength > 0) ? _data.SubArray(_extDataLength, _length - _extDataLength) : _data; public byte[] ExtensionData => (_extDataLength > 0) ? _data.SubArray(0L, _extDataLength) : WebSocket.EmptyBytes; public ulong Length => (ulong)_length; static PayloadData() { Empty = new PayloadData(WebSocket.EmptyBytes, 0L); MaxLength = 9223372036854775807uL; } internal PayloadData(byte[] data) : this(data, data.LongLength) { } internal PayloadData(byte[] data, long length) { _data = data; _length = length; } internal PayloadData(ushort code, string reason) { _data = code.Append(reason); _length = _data.LongLength; } internal void Mask(byte[] key) { for (long num = 0L; num < _length; num++) { _data[num] ^= key[num % 4]; } } public IEnumerator<byte> GetEnumerator() { byte[] data = _data; for (int i = 0; i < data.Length; i++) { yield return data[i]; } } public byte[] ToArray() { return _data; } public override string ToString() { return BitConverter.ToString(_data); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } internal enum Rsv : byte { Off, On } public enum CompressionMethod : byte { None, Deflate } public class WebSocketException : Exception { private ushort _code; public ushort Code => _code; private WebSocketException(ushort code, string message, Exception innerException) : base(message ?? code.GetErrorMessage(), innerException) { _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) : this((ushort)code, message, innerException) { } } 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}]"; string text2 = $"{_level.ToString().ToUpper(),-5}"; MethodBase method = _caller.GetMethod(); Type declaringType = method.DeclaringType; int fileLineNumber = _caller.GetFileLineNumber(); string text3 = $"{declaringType.Name}.{method.Name}:{fileLineNumber}"; string[] array = _message.Replace("\r\n", "\n").TrimEnd(new char[1] { '\n' }).Split(new char[1] { '\n' }); if (array.Length <= 1) { return $"{text} {text2} {text3} {_message}"; } StringBuilder stringBuilder = new StringBuilder(64); stringBuilder.AppendFormat("{0} {1} {2}\n\n", text, text2, text3); for (int i = 0; i < array.Length; i++) { stringBuilder.AppendFormat(" {0}\n", array[i]); } return stringBuilder.ToString(); } } public enum LogLevel { Trace, Debug, Info, Warn, Error, Fatal, None } public class Logger { private volatile string _file; private volatile LogLevel _level; private Action<LogData, string> _output; private object _sync; public string File { get { return _file; } set { lock (_sync) { _file = value; } } } public LogLevel Level { get { return _level; } set { lock (_sync) { _level = value; } } } public Action<LogData, string> Output { get { return _output; } set { lock (_sync) { _output = value ?? new Action<LogData, string>(defaultOutput); } } } public Logger() : this(LogLevel.Error, null, null) { } public Logger(LogLevel level) : this(level, null, null) { } public Logger(LogLevel level, string file, Action<LogData, string> output) { _level = level; _file = file; _output = output ?? new Action<LogData, string>(defaultOutput); _sync = new object(); } private static void defaultOutput(LogData data, string path) { string value = data.ToString(); Console.WriteLine(value); if (path != null && path.Length > 0) { writeToFile(value, path); } } private void output(string message, LogLevel level) { lock (_sync) { if (_level > level) { return; } try { LogData arg = new LogData(level, new StackFrame(2, needFileInfo: true), message); _output(arg, _file); } catch (Exception ex) { LogData 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) { if (_level <= LogLevel.Fatal) { 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 WebSocketState : ushort { New, Connecting, Open, Closing, Closed } internal class WebSocketFrame : IEnumerable<byte>, IEnumerable { private static readonly int _defaultHeaderLength; private static readonly int _defaultMaskingKeyLength; 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 ulong ExactPayloadLength => (_payloadLength < 126) ? _payloadLength : ((_payloadLength == 126) ? _extPayloadLength.ToUInt16(ByteOrder.Big) : _extPayloadLength.ToUInt64(ByteOrder.Big)); internal int ExtendedPayloadLengthWidth => (_payloadLength >= 126) ? ((_payloadLength == 126) ? 2 : 8) : 0; 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 => _opcode == Opcode.Text || _opcode == Opcode.Binary; public bool IsFinal => _fin == Fin.Final; public bool IsFragment => _fin == Fin.More || _opcode == Opcode.Cont; 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)(_defaultHeaderLength + _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() { _defaultHeaderLength = 2; _defaultMaskingKeyLength = 4; } private WebSocketFrame() { } 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; _opcode = opcode; _rsv1 = (compressed ? Rsv.On : Rsv.Off); _rsv2 = Rsv.Off; _rsv3 = Rsv.Off; ulong length = payloadData.Length; if (length < 126) { _payloadLength = (byte)length; _extPayloadLength = WebSocket.EmptyBytes; } else if (length < 65536) { _payloadLength = 126; _extPayloadLength = ((ushort)length).ToByteArray(ByteOrder.Big); } else { _payloadLength = 127; _extPayloadLength = length.ToByteArray(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[