/// <inheritdoc /> public override async Task OnConnectedAsync(ConnectionContext connection) { // We check to see if HubOptions<THub> are set because those take precedence over global hub options. // Then set the keepAlive and handshakeTimeout values to the defaults in HubOptionsSetup when they were explicitly set to null. var supportedProtocols = _hubOptions.SupportedProtocols ?? _globalHubOptions.SupportedProtocols; if (supportedProtocols == null || supportedProtocols.Count == 0) { throw new InvalidOperationException("There are no supported protocols"); } var handshakeTimeout = _hubOptions.HandshakeTimeout ?? _globalHubOptions.HandshakeTimeout ?? HubOptionsSetup.DefaultHandshakeTimeout; var contextOptions = new HubConnectionContextOptions() { KeepAliveInterval = _hubOptions.KeepAliveInterval ?? _globalHubOptions.KeepAliveInterval ?? HubOptionsSetup.DefaultKeepAliveInterval, ClientTimeoutInterval = _hubOptions.ClientTimeoutInterval ?? _globalHubOptions.ClientTimeoutInterval ?? HubOptionsSetup.DefaultClientTimeoutInterval, StreamBufferCapacity = _hubOptions.StreamBufferCapacity ?? _globalHubOptions.StreamBufferCapacity ?? HubOptionsSetup.DefaultStreamBufferCapacity, MaximumReceiveMessageSize = _maximumMessageSize, SystemClock = SystemClock, MaximumParallelInvocations = _maxParallelInvokes, }; Log.ConnectedStarting(_logger); var connectionContext = new HubConnectionContext(connection, contextOptions, _loggerFactory); var resolvedSupportedProtocols = (supportedProtocols as IReadOnlyList <string>) ?? supportedProtocols.ToList(); if (!await connectionContext.HandshakeAsync(handshakeTimeout, resolvedSupportedProtocols, _protocolResolver, _userIdProvider, _enableDetailedErrors)) { return; } // -- the connectionContext has been set up -- try { await _lifetimeManager.OnConnectedAsync(connectionContext); await RunHubAsync(connectionContext); } finally { connectionContext.Cleanup(); Log.ConnectedEnding(_logger); await _lifetimeManager.OnDisconnectedAsync(connectionContext); } }
private async Task HubOnDisconnectedAsync(HubConnectionContext connection, Exception?exception) { // send close message before aborting the connection await SendCloseAsync(connection, exception, connection.AllowReconnect); // We wait on abort to complete, this is so that we can guarantee that all callbacks have fired // before OnDisconnectedAsync // Ensure the connection is aborted before firing disconnect await connection.AbortAsync(); try { await _dispatcher.OnDisconnectedAsync(connection, exception); } catch (Exception ex) { Log.ErrorDispatchingHubEvent(_logger, "OnDisconnectedAsync", ex); throw; } }
private async Task RunHubAsync(HubConnectionContext connection) { try { await _dispatcher.OnConnectedAsync(connection); } catch (Exception ex) { Log.ErrorDispatchingHubEvent(_logger, "OnConnectedAsync", ex); // The client shouldn't try to reconnect given an error in OnConnected. await SendCloseAsync(connection, ex, allowReconnect : false); // return instead of throw to let close message send successfully return; } try { await DispatchMessagesAsync(connection); } catch (OperationCanceledException) { // Don't treat OperationCanceledException as an error, it's basically a "control flow" // exception to stop things from running } catch (Exception ex) { Log.ErrorProcessingRequest(_logger, ex); await HubOnDisconnectedAsync(connection, ex); // return instead of throw to let close message send successfully return; } await HubOnDisconnectedAsync(connection, connection.CloseException); }
private async Task SendCloseAsync(HubConnectionContext connection, Exception?exception, bool allowReconnect) { var closeMessage = CloseMessage.Empty; if (exception != null) { var errorMessage = ErrorMessageHelper.BuildErrorMessage("Connection closed with an error.", exception, _enableDetailedErrors); closeMessage = new CloseMessage(errorMessage, allowReconnect); } else if (allowReconnect) { closeMessage = new CloseMessage(error: null, allowReconnect); } try { await connection.WriteAsync(closeMessage, ignoreAbort : true); } catch (Exception ex) { Log.ErrorSendingClose(_logger, ex); } }