internal ConnectionManager(SemaphoreSlim stateLock, Logger logger, int connectionTimeout, Func <Task> onConnecting, Func <Exception, Task> onDisconnecting, Action <Func <Exception, Task> > clientDisconnectHandler) { _stateLock = stateLock; _logger = logger; _connectionTimeout = connectionTimeout; _onConnecting = onConnecting; _onDisconnecting = onDisconnecting; clientDisconnectHandler(ex => { if (ex != null) { WebSocketClosedException ex2 = ex as WebSocketClosedException; if (ex2?.CloseCode == 4006) { CriticalError(new Exception("WebSocket session expired", ex)); } else if (ex2?.CloseCode == 4014) { CriticalError(new Exception("WebSocket connection was closed, some intents defined are not allowed by the bot app.", ex)); } else { Error(new Exception("WebSocket connection was closed", ex)); } } else { Error(new Exception("WebSocket connection was closed")); } return(Task.Delay(0)); }); }
private void OnClosed(object sender, CloseEventArgs e) { if (e.WasClean) { this.Closed?.Invoke(null).GetAwaiter().GetResult(); return; } var ex = new WebSocketClosedException(e.Code, e.Reason); this.Closed?.Invoke(ex).GetAwaiter().GetResult(); }
private async Task Receive(Func <Stream, Task> handleMessage, CancellationToken ct) { Exception closingException = null; while (!ct.IsCancellationRequested && _clientWebSocket.State == WebSocketState.Open) { try { ArraySegment <byte> buffer = new ArraySegment <byte>(new byte[1024 * 4]); using (var stream = _clientWebSocket.GetReadStream()) { // Wait for data await stream.WaitForDataAsync(ct); var receiveIterator = _middleware.GetEnumerator(); RecieveDelegate receiveDelegator = null; receiveDelegator = async transformedMessage => { if (receiveIterator.MoveNext()) { using (_logger?.Tracer($"Middleware[{receiveIterator.Current.GetType()}].RecieveAsync(...)")) { await receiveIterator.Current.RecieveAsync(new ConnectionContext(_clientWebSocket, transformedMessage.Stream), receiveDelegator).ConfigureAwait(false); } } else { using (_logger?.Tracer("Connection.InternalReceive(...)")) { await handleMessage(transformedMessage.Stream).ConfigureAwait(false); } } }; await receiveDelegator(new ConnectionContext(_clientWebSocket, stream)) .ContinueWith(task => { // Dispose of our middleware iterator before we handle possible exceptions receiveIterator.Dispose(); // Unwrap and allow the outer catch to handle if we have an exception task.WaitAndUnwrapException(); }) .ConfigureAwait(false); } } catch (WebSocketException e) when(e.Message == "The remote party closed the WebSocket connection without completing the close handshake.") { _logger.LogWarning("Observed WebSocketException - likely server closing our connection"); // Eat the exception because we're closing closingException = new WebSocketClosedException("Observed WebSocketException - likely server closing our connection", e); try { if (_clientWebSocket.State == WebSocketState.CloseReceived) { // Attempt to be "good" and close await _clientWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, null, ct); } else { _clientWebSocket.Abort(); } } catch (Exception) { } // Fire the closing event FireClosed(e); // But we cancel out because the socket is closed break; } catch (WebSocketClosedException e) { _logger.LogWarning("Observed WebSocketClosedException - likely OK"); // Eat the exception because we're closing closingException = e; try { if (_clientWebSocket.State == WebSocketState.CloseReceived) { // Attempt to be "good" and close await _clientWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, null, ct); } } catch (Exception) { } // Fire the closing event FireClosed(e); // But we cancel out because the socket is closed break; } catch (Exception e) { _logger.LogError(new EventId(), e, $"Observed {e.GetType()}!"); Error?.Invoke(e); } } // We're closing so we need to unblock any pending TaskCompletionSources InvocationRequest[] calls; lock (_pendingCallsLock) { calls = _pendingCalls.Values.ToArray(); _pendingCalls.Clear(); } foreach (var request in calls) { request.Registration.Dispose(); request.Completion.TrySetException(closingException ?? new WebSocketClosedException("The WebSocket has been closed!")); } }