/// <summary> /// Add a new subscription to the given socket /// </summary> /// <param name="socket"></param> /// <param name="subscription"></param> public void AddSubscription(IWebsocketConnection socket, Subscription subscription) { lock (_locker) { if (_allSockets.TryGetValue(socket, out var list)) { list.Add(subscription); } else { _allSockets.Add(socket, new List <Subscription> { subscription }); } } }
/// <summary> /// Broadcasts a decoded transaction to a given socket /// </summary> /// <param name="transaction">decoded transaction to broadcast</param> /// <param name="socket">socket to which to broadcast the transaction</param> private static void BroadcastTransaction(Transaction transaction, IWebsocketConnection socket) { try { var outputs = transaction.Outputs.Aggregate("", (current, output) => current + ("{ \"type\": \"" + output.Type + "\", \"address\": \"" + output.Address + "\", \"value\": \"" + output.Value + "\", \"script\": \"" + output.ScriptHex + "\" }, ")); outputs = outputs.Substring(0, outputs.Length - 2); socket.Send("{ \"op\": \"new_tx\", \"txid\": \"" + transaction.TXIDHex + "\", " + "\"inputs\": " + transaction.Inputs.Length + ", \"outputs\": [ " + outputs + " ]"); } catch (Exception e) { Console.WriteLine("Failed to broadcast. " + e.Message); } }
internal Client(OrtcClient ortcClient) { context = ortcClient; _gotOnOpenCount = 0; _permissions = new List <KeyValuePair <string, string> >(); _subscribedChannels = new RealtimeDictionary <string, ChannelSubscription>(); _multiPartMessagesBuffer = new RealtimeDictionary <string, RealtimeDictionary <int, BufferedMessage> >(); _heartbeatTimer = new Timer(_heartbeatTimer_Elapsed, context.HeartbeatTime * 1000); _connectionTimer = new Timer(_connectionTimer_Elapsed, Constants.SERVER_HB_COUNT * 1000); _reconnectTimer = new Timer(_reconnectTimer_Elapsed, context.ConnectionTimeout * 1000); _balancer = DependencyService.Get <IBalancer> (); if (_balancer == null) { throw new OrtcGenericException( "DependencyService Failed, please include the platform plugins. This may be caused linker stripping."); } _webSocketConnection = DependencyService.Get <IWebsocketConnection>(); if (_webSocketConnection == null) { throw new OrtcGenericException( "DependencyService Failed, please include the platform plugins. This may be caused linker stripping."); } _webSocketConnection.OnOpened += _webSocketConnection_OnOpened; _webSocketConnection.OnClosed += _webSocketConnection_OnClosed; _webSocketConnection.OnError += _webSocketConnection_OnError; _webSocketConnection.OnMessageReceived += _webSocketConnection_OnMessageReceived; _msgProcessor = new MsgProcessor(this); }
/// <summary> /// Handles json messages: /// {"op": "block"} /// {"op": "transaction"} /// {"op": "address", "address": "[CASH_ADDRESS]"} /// {"op": "opreturn", "prefix": "[PREFIX(HEX)]"} /// {"op": "rm_block"} /// {"op": "rm_transaction"} /// {"op": "rm_address", "address": "[CASH_ADDRESS]"} /// {"op": "rm_opreturn", "prefix": "[PREFIX(HEX)]"} /// - Validates json /// - Validates parameters /// - Sends response (error or ok) /// - Adds valid subscriptions to subscription handler /// </summary> /// <param name="socket"></param> /// <param name="message"></param> /// <param name="subscriptionHandler"></param> public static void HandleMessage(IWebsocketConnection socket, string message, SubscriptionHandler subscriptionHandler) { message = message.Trim(); // check if the message is valid json if ((!message.StartsWith("{") || !message.EndsWith("}")) && (!message.StartsWith("[") || !message.EndsWith("]"))) { return; } JToken jToken; try { jToken = JToken.Parse(message); } catch (JsonReaderException jex) { socket.Send("{ \"op\": \"error\", \"error\": \"Unable to parse JSON request. " + JsonConvert.ToString(jex.Message) + "\" }"); return; } catch (Exception ex) { socket.Send("{ \"op\": \"error\", \"error\": \"Exception while parsing JSON request. " + JsonConvert.ToString(ex.Message) + "\" }"); return; } if (jToken.Type != JTokenType.Object) { socket.Send("{ \"op\": \"error\", \"error\": \"Error while parsing JSON request. Expected Object, but encountered " + jToken.Type + "\" }"); return; } var jObject = jToken.ToObject <JObject>(); if (!jObject.ContainsKey("op") || jObject["op"].Type != JTokenType.String) { socket.Send("{ \"op\": \"error\", \"error\": \"Error while parsing JSON request. Expected 'op' parameter as string.\" }"); return; } // block subscriptions if (jObject["op"].ToString().Equals("block", StringComparison.CurrentCultureIgnoreCase)) { subscriptionHandler.AddSubscription(socket, new BlockSubscription()); socket.Send("{ \"op\": \"block\", \"result\": \"ok\" }"); } // block un-subscribe else if (jObject["op"].ToString().Equals("rm_block", StringComparison.CurrentCultureIgnoreCase)) { socket.Send(subscriptionHandler.RemoveSubscription(socket, new BlockSubscription()) ? "{ \"op\": \"rm_block\", \"result\": \"ok\" }" : "{ \"op\": \"rm_block\", \"result\": \"failed\" }"); } // transaction subscriptions else if (jObject["op"].ToString().Equals("transaction", StringComparison.CurrentCultureIgnoreCase)) { subscriptionHandler.AddSubscription(socket, new TransactionSubscription()); socket.Send("{ \"op\": \"transaction\", \"result\": \"ok\" }"); } // transaction un-subscribe else if (jObject["op"].ToString().Equals("rm_transaction", StringComparison.CurrentCultureIgnoreCase)) { socket.Send(subscriptionHandler.RemoveSubscription(socket, new TransactionSubscription()) ? "{ \"op\": \"rm_transaction\", \"result\": \"ok\" }" : "{ \"op\": \"rm_transaction\", \"result\": \"failed\" }"); } // address subscriptions else if (jObject["op"].ToString().Equals("address", StringComparison.CurrentCultureIgnoreCase) || jObject["op"].ToString().Equals("rm_address", StringComparison.CurrentCultureIgnoreCase)) { if (!jObject.ContainsKey("address") || jObject["address"].Type != JTokenType.String) { socket.Send("{ \"op\": \"error\", \"error\": \"Error while parsing JSON request. Expected 'address' parameter as string.\" }"); return; } // attempt to decode cash address try { var decoded = CashAddress.DecodeCashAddress(jObject["address"].ToString()); if (jObject["op"].ToString().Equals("address", StringComparison.CurrentCultureIgnoreCase)) { subscriptionHandler.AddSubscription(socket, new AddressSubscription(decoded)); socket.Send("{ \"op\": \"address\", \"result\": \"ok\" }"); } else if (jObject["op"].ToString().Equals("rm_address", StringComparison.CurrentCultureIgnoreCase)) { socket.Send(!subscriptionHandler.RemoveSubscription(socket, new AddressSubscription(decoded)) ? "{ \"op\": \"rm_address\", \"result\": \"failed\" }" : "{ \"op\": \"rm_address\", \"result\": \"ok\" }"); } } catch (CashAddress.CashAddressException e) { socket.Send("{ \"op\": \"error\", \"error\": \"" + JsonConvert.ToString(e.Message) + " ... " + JsonConvert.ToString(e.InnerException.Message) + "\" }"); } } // op return subscriptions else if (jObject["op"].ToString().Equals("opreturn", StringComparison.CurrentCultureIgnoreCase) || jObject["op"].ToString().Equals("rm_opreturn", StringComparison.CurrentCultureIgnoreCase)) { if (!jObject.ContainsKey("prefix") || jObject["prefix"].Type != JTokenType.String) { socket.Send("{ \"op\": \"error\", \"error\": \"Error while parsing JSON request. Expected 'prefix' parameter as string.\" }"); return; } if (!IsValidHexString(jObject["prefix"].ToString())) { socket.Send("{ \"op\": \"error\", \"error\": \"Error while parsing JSON request. Expected 'prefix' parameter to be valid hex.\" }"); return; } if (jObject["prefix"].ToString().Length > 32) { socket.Send("{ \"op\": \"error\", \"error\": \"Error while parsing JSON request. Expected 'prefix' parameter to be less than 32 hex characters long.\" }"); return; } var prefix = ByteHexConverter.StringToByteArray(jObject["prefix"].ToString()); if (jObject["op"].ToString().Equals("opreturn", StringComparison.CurrentCultureIgnoreCase)) { subscriptionHandler.AddSubscription(socket, new OpReturnSubscription(prefix)); socket.Send("{ \"op\": \"opreturn\", \"result\": \"ok\" }"); } else if (subscriptionHandler.RemoveSubscription(socket, new OpReturnSubscription(prefix))) { socket.Send(!subscriptionHandler.RemoveSubscription(socket, new OpReturnSubscription(prefix)) ? "{ \"op\": \"rm_opreturn\", \"result\": \"failed\" }" : "{ \"op\": \"rm_opreturn\", \"result\": \"ok\" }"); } } else { // unrecognized op command socket.Send("{ \"op\": \"error\", \"error\": \"Error while parsing JSON request. Expected valid 'op' parameter.\" }"); } }
/// <summary> /// Remove a websocket connection /// </summary> /// <param name="socket"></param> public void RemoveSocket(IWebsocketConnection socket) { lock (_locker) _allSockets.Remove(socket); }
/// <summary> /// Add a new websocket connection /// </summary> /// <param name="socket"></param> public void AddSocket(IWebsocketConnection socket) { lock (_locker) _allSockets.Add(socket, new List <Subscription>()); }
/// <summary> /// Remove existing subscription from the given socket /// </summary> /// <param name="socket"></param> /// <param name="subscription"></param> /// <returns></returns> public bool RemoveSubscription(IWebsocketConnection socket, Subscription subscription) { lock (_locker) return(_allSockets.TryGetValue(socket, out var list) && list.Remove(subscription)); }