private async Task OnMessageReceived(object sender, MessageReceivedEventArgs e, WebSocket webSocket, string seedString, SignInSession session) { //_logger.Log(sender.ToString()); _ = _logger.Log($"RECEIVED - {e.Message}"); try { var msg = JObject.Parse(e.Message); var route = msg["route"]?.ToString().ToLower() ?? ""; switch (route) { case "connection": // TODO: pass parameters Init(null, null, session, seedString, null, null); Instance4Net = webSocket; //this.heartbeat() _connected = true; var connectionMessage = JsonConvert.DeserializeObject <ConnectionMessage>(e.Message); Keys.Salts = connectionMessage.KeySalts; _encryptedValidationMessage = connectionMessage.EncryptedValidationMessage.Data; await SetKeys(seedString); break; case "ping": //this.heartbeat() _ = _logger.Log("SENT - PONG"); const string action = "Pong"; Instance4Net.Send(JsonConvert.SerializeObject(new { action })); break; case "applytransactions": // TODO: in progress var dbId = msg["dbId"].ToString(); var dbNameHash = msg["dbNameHash"].ToString(); // || State.dbIdToHash[dbId] if (!State.Databases.TryGetValue(dbNameHash, out var database)) { throw new Exception("Missing database"); } // queue guarantees transactions will be applied in the order they are received from the server if (database.ApplyTransactionsQueue.Count == 0) { // take a spot in the queue and proceed applying so the next caller knows queue is not empty database.ApplyTransactionsQueue.Enqueue(null); } else { // wait until prior batch in queue finishes applying successfully var promise = new TaskCompletionSource <int>(); void Resolve() { var localPromise = promise; localPromise.SetResult(0); } database.ApplyTransactionsQueue.Enqueue(Resolve); await promise.Task; } var openingDatabase = msg["dbNameHash"] != null && msg["dbKey"] != null; if (openingDatabase) { var dbKeyString = AesGcmUtils.DecryptString(Keys.EncryptionKey, msg["dbKey"].ToString()); database.DbKeyString = dbKeyString; // TODO //database.dbKey = await AesGcmUtils.GetKeyFromKeyString(dbKeyString); } //if (!database.dbKey) throw new Error('Missing db key') if (msg["bundle"] != null) { /*const bundleSeqNo = message.bundleSeqNo * const base64Bundle = message.bundle * const compressedString = await crypto.aesGcm.decryptString(database.dbKey, base64Bundle) * const plaintextString = LZString.decompress(compressedString) * const bundle = JSON.parse(plaintextString) * * database.applyBundle(bundle, bundleSeqNo)*/ } /*const newTransactions = message.transactionLog * await database.applyTransactions(newTransactions)*/ if (!database.Init) { State.DbIdToHash[dbId] = dbNameHash; database.DbId = dbId; database.Init = true; _ = _logger.Log("DB - RECEIVED MESSAGE"); database.ReceivedMessage(); } if (msg["buildBundle"] != null) { BuildBundle(database); } // start applying next batch in queue when this one is finished applying successfully database.ApplyTransactionsQueue.Dequeue(); if (database.ApplyTransactionsQueue.Count > 0) { var startApplyingNextBatchInQueue = database.ApplyTransactionsQueue.Peek(); startApplyingNextBatchInQueue(); } break; case "signout": case "updateuser": case "deleteuser": case "createdatabase": case "getdatabase": case "opendatabase": case "insert": case "update": case "delete": case "batchtransaction": case "bundle": case "validatekey": case "getpasswordsalts": var requestId = msg["requestId"]?.ToString().ToLower() ?? ""; if (string.IsNullOrEmpty(requestId)) { // TODO: log warning Console.WriteLine("Missing request id"); return; } //var request = this.requests[requestId]; if (!_pendingRequests.TryGetValue(requestId, out var request)) { // TODO: log warning Console.WriteLine($"Request {requestId} no longer exists!"); return; } else if (request.Resolve == null || request.Reject == null) { return; } var response = msg["response"]?.ToString().ToLower() ?? ""; var status = msg["response"]["status"]?.ToString().ToLower() ?? ""; var statusCode = HttpStatusCode.BadRequest; var successfulResponse = !string.IsNullOrEmpty(response) && !string.IsNullOrEmpty(status) && Enum.TryParse(status, out statusCode) && statusCode == HttpStatusCode.OK; if (successfulResponse) { request.Resolve(requestId); } else { request.Reject(requestId, route, statusCode, response); } break; default: Console.WriteLine($"Received unknown message from backend: {msg}"); break; } } catch (Exception exception) { _ = _logger.Log($"ERROR - {exception.Message}"); } }