/// <summary> /// Creates a new instance of <see cref="RoRamu.WebSocket.Service.WebSocketClientProxy" /> to /// represent a new connection. /// </summary> /// <param name="socket"> /// The <see cref="RoRamu.WebSocket.WebSocketUnderlyingConnection" /> which represents the underlying /// implementation of a websocket connection. /// </param> /// <param name="connectionInfo"> /// Information about the connection that was made by the client during the handshake. For /// example, this can be used to authenticate a client based on request headers, or assign /// different behaviors to the connection based on the path used in the URL. /// </param> /// <returns> /// The new <see cref="RoRamu.WebSocket.WebSocketUnderlyingConnection" /> representing the connection. /// </returns> private async Task <WebSocketClientProxy> CreateProxy(WebSocketUnderlyingConnection socket, WebSocketConnectionInfo connectionInfo) { // Generate the connection ID string connectionId; try { connectionId = this.GenerateConnectionId(connectionInfo) ?? throw new ArgumentNullException(nameof(connectionId)); } catch { this.Logger?.Log(LogLevel.Error, $"Failed to generate connectionId.", connectionInfo); throw; } // Create the proxy WebSocketClientProxy proxy = new WebSocketClientProxy( connectionId, socket, connection => this.CreateController(connectionId, connectionInfo, connection)); // Attach the event handlers socket.OnClose = proxy.OnCloseInternal; socket.OnError = proxy.OnErrorInternal; socket.OnMessage = proxy.OnMessageInternal; // Run the "OnOpen()" method await proxy.OnOpenInternal(); return(proxy); }
internal WebSocketClientProxy( string id, WebSocketUnderlyingConnection connection, WebSocketController.FactoryDelegate controllerFactory) : base(connection, controllerFactory) { this.Id = id ?? throw new ArgumentNullException(nameof(id)); }
/// <summary> /// The logic used to handle a new incoming connection. /// </summary> /// <param name="socket"> /// Represents the underlying websocket implementation's connection object. /// </param> /// <param name="connectionInfo"> /// Information about the connection that was made by the client during the handshake. /// </param> private void OnOpen(WebSocketUnderlyingConnection socket, WebSocketConnectionInfo connectionInfo) { if (socket == null) { throw new ArgumentNullException(nameof(socket)); } if (connectionInfo == null) { throw new ArgumentNullException(nameof(connectionInfo)); } Task.Run(async() => { // Create the proxy for this connection WebSocketClientProxy proxy = await this.CreateProxy(socket, connectionInfo); // Set the proxy for this connection this._connections.AddOrUpdate( key: proxy.Id, addValue: proxy, updateValueFactory: (id, oldProxy) => { // Close the old connection and swallow any exceptions this.Logger?.Log(LogLevel.Info, $"Closing duplicate connection to client '{id}'."); oldProxy.Close().ContinueWith(closeTask => { this.Logger?.Log(LogLevel.Warning, $"Failed to close duplicate connection to client '{id}'.", closeTask.Exception); }, TaskContinuationOptions.OnlyOnFaulted); return(proxy); }); }).ContinueWith(createProxyTask => { this.Logger?.Log(LogLevel.Error, "Failed to create client connection proxy.", createProxyTask.Exception); // Get the user-safe exceptions IEnumerable <UserSafeWebSocketException> userSafeExceptions = createProxyTask?.Exception?.InnerExceptions ?.Where(e => e is UserSafeWebSocketException) ?.Select(e => e as UserSafeWebSocketException); // Send the user-safe exceptions to the client if (userSafeExceptions != null && userSafeExceptions.Any()) { var errorMessage = new ErrorResponse(new AggregateException("Failed to connect", userSafeExceptions), requestId: null); } // Close the connection socket.Close().ContinueWith(closeTask => { this.Logger?.Log(LogLevel.Warning, $"Could not close the connection for which proxy creation failed.", closeTask.Exception); }, TaskContinuationOptions.OnlyOnFaulted); }, TaskContinuationOptions.OnlyOnFaulted); }