/// <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);
        }
        /// <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);
        }