private async Task DataReceiver() { DisconnectReason reason = DisconnectReason.Normal; while (true) { bool readLocked = false; try { #region Check-for-Connection if (_Client == null || !_Client.Connected) { _Settings.Logger?.Invoke(_Header + "disconnect detected"); break; } #endregion #region Read-Message while (true) { readLocked = await _ReadLock.WaitAsync(10, _Token).ConfigureAwait(false); if (readLocked) { break; } await Task.Delay(10, _Token).ConfigureAwait(false); } WatsonMessage msg = new WatsonMessage(_DataStream, (_Settings.DebugMessages ? _Settings.Logger : null)); bool buildSuccess = await msg.BuildFromStream(_Token).ConfigureAwait(false); if (!buildSuccess) { _Settings.Logger?.Invoke(_Header + "disconnect detected"); break; } if (msg == null) { await Task.Delay(30, _Token).ConfigureAwait(false); continue; } #endregion #region Process-by-Status if (msg.Status == MessageStatus.Removed) { _Settings.Logger?.Invoke(_Header + "disconnect due to server-side removal"); reason = DisconnectReason.Removed; break; } else if (msg.Status == MessageStatus.Shutdown) { _Settings.Logger?.Invoke(_Header + "disconnect due to server shutdown"); reason = DisconnectReason.Shutdown; break; } else if (msg.Status == MessageStatus.Timeout) { _Settings.Logger?.Invoke(_Header + "disconnect due to timeout"); reason = DisconnectReason.Timeout; break; } else if (msg.Status == MessageStatus.AuthSuccess) { _Settings.Logger?.Invoke(_Header + "authentication successful"); Task unawaited = Task.Run(() => _Events.HandleAuthenticationSucceeded(this, EventArgs.Empty), _Token); continue; } else if (msg.Status == MessageStatus.AuthFailure) { _Settings.Logger?.Invoke(_Header + "authentication failed"); Task unawaited = Task.Run(() => _Events.HandleAuthenticationFailure(this, EventArgs.Empty), _Token); continue; } else if (msg.Status == MessageStatus.AuthRequired) { _Settings.Logger?.Invoke(_Header + "authentication required by server; please authenticate using pre-shared key"); string psk = _Callbacks.HandleAuthenticationRequested(); if (!String.IsNullOrEmpty(psk)) { Authenticate(psk); } continue; } #endregion #region Process-Message if (msg.SyncRequest != null && msg.SyncRequest.Value) { DateTime expiration = WatsonCommon.GetExpirationTimestamp(msg); byte[] msgData = await WatsonCommon.ReadMessageDataAsync(msg, _Settings.StreamBufferSize).ConfigureAwait(false); if (DateTime.Now < expiration) { SyncRequest syncReq = new SyncRequest( _ServerIp + ":" + _ServerPort, msg.ConversationGuid, msg.Expiration.Value, msg.Metadata, msgData); SyncResponse syncResp = _Callbacks.HandleSyncRequestReceived(syncReq); if (syncResp != null) { WatsonCommon.BytesToStream(syncResp.Data, out long contentLength, out Stream stream); WatsonMessage respMsg = new WatsonMessage( syncResp.Metadata, contentLength, stream, false, true, msg.Expiration.Value, msg.ConversationGuid, (_Settings.DebugMessages ? _Settings.Logger : null)); SendInternal(respMsg, contentLength, stream); } } else { _Settings.Logger?.Invoke(_Header + "expired synchronous request received and discarded"); } } else if (msg.SyncResponse != null && msg.SyncResponse.Value) { // No need to amend message expiration; it is copied from the request, which was set by this node // DateTime expiration = WatsonCommon.GetExpirationTimestamp(msg); byte[] msgData = await WatsonCommon.ReadMessageDataAsync(msg, _Settings.StreamBufferSize).ConfigureAwait(false); if (DateTime.Now < msg.Expiration.Value) { lock (_SyncResponseLock) { _SyncResponses.Add(msg.ConversationGuid, new SyncResponse(msg.Expiration.Value, msg.Metadata, msgData)); } } else { _Settings.Logger?.Invoke(_Header + "expired synchronous response received and discarded"); } } else { byte[] msgData = null; if (_Events.IsUsingMessages) { msgData = await WatsonCommon.ReadMessageDataAsync(msg, _Settings.StreamBufferSize).ConfigureAwait(false); MessageReceivedEventArgs args = new MessageReceivedEventArgs((_ServerIp + ":" + _ServerPort), msg.Metadata, msgData); await Task.Run(() => _Events.HandleMessageReceived(this, args)); } else if (_Events.IsUsingStreams) { StreamReceivedEventArgs sr = null; WatsonStream ws = null; if (msg.ContentLength >= _Settings.MaxProxiedStreamSize) { ws = new WatsonStream(msg.ContentLength, msg.DataStream); sr = new StreamReceivedEventArgs((_ServerIp + ":" + _ServerPort), msg.Metadata, msg.ContentLength, ws); // sr = new StreamReceivedFromServerEventArgs(msg.Metadata, msg.ContentLength, msg.DataStream); // must run synchronously, data exists in the underlying stream _Events.HandleStreamReceived(this, sr); } else { MemoryStream ms = WatsonCommon.DataStreamToMemoryStream(msg.ContentLength, msg.DataStream, _Settings.StreamBufferSize); ws = new WatsonStream(msg.ContentLength, ms); sr = new StreamReceivedEventArgs((_ServerIp + ":" + _ServerPort), msg.Metadata, msg.ContentLength, ws); // sr = new StreamReceivedFromServerEventArgs(msg.Metadata, msg.ContentLength, ms); // data has been read, can continue to next message Task unawaited = Task.Run(() => _Events.HandleStreamReceived(this, sr), _Token); } } else { _Settings.Logger?.Invoke(_Header + "event handler not set for either MessageReceived or StreamReceived"); break; } } #endregion _Statistics.IncrementReceivedMessages(); _Statistics.AddReceivedBytes(msg.ContentLength); } catch (ObjectDisposedException) { break; } catch (TaskCanceledException) { break; } catch (OperationCanceledException) { break; } catch (Exception e) { _Settings.Logger?.Invoke( _Header + "data receiver exception for " + _ServerIp + ":" + _ServerPort + ":" + Environment.NewLine + SerializationHelper.SerializeJson(e, true) + Environment.NewLine); _Events.HandleExceptionEncountered(this, new ExceptionEventArgs(e)); break; } finally { if (readLocked && _ReadLock != null) { _ReadLock.Release(); } } } Connected = false; _Settings.Logger?.Invoke(_Header + "data receiver terminated for " + _ServerIp + ":" + _ServerPort); _Events.HandleServerDisconnected(this, new DisconnectionEventArgs((_ServerIp + ":" + _ServerPort), reason)); }
internal void HandleStreamReceived(object sender, StreamReceivedEventArgs args) { WrappedEventHandler(() => StreamReceived?.Invoke(sender, args), "StreamReceived", sender); }