Exemple #1
0
        private async Task <MessageReceivedEventArgs> MessageReadAsync(ClientMetadata md)
        {
            using (MemoryStream stream = new MemoryStream())
            {
                byte[] buffer           = new byte[65536];
                ArraySegment <byte> seg = new ArraySegment <byte>(buffer);

                while (true)
                {
                    WebSocketReceiveResult result = await md.Ws.ReceiveAsync(seg, md.TokenSource.Token);

                    if (result.CloseStatus != null ||
                        md.Ws.State != WebSocketState.Open ||
                        result.MessageType == WebSocketMessageType.Close ||
                        md.TokenSource.Token.IsCancellationRequested)
                    {
                        throw new WebSocketException("Websocket closed.");
                    }

                    if (result.Count > 0)
                    {
                        stream.Write(buffer, 0, result.Count);
                    }

                    if (result.EndOfMessage)
                    {
                        return(new MessageReceivedEventArgs(md.IpPort, stream.ToArray(), result.MessageType));
                    }
                }
            }
        }
Exemple #2
0
        private async Task <bool> MessageWriteAsync(ClientMetadata client, byte[] data, WebSocketMessageType messageType)
        {
            try
            {
                #region Send-Message

                // Cannot have two simultaneous SendAsync calls so use a
                // semaphore to block the second until the first has completed

                await client.SendAsyncLock.WaitAsync(client.KillToken.Token);

                try
                {
                    await client.Ws.SendAsync(new ArraySegment <byte>(data, 0, data.Length),
                                              messageType, true, client.KillToken.Token);
                }
                finally
                {
                    client.SendAsyncLock.Release();
                }

                return(true);

                #endregion
            }
            catch (Exception e)
            {
                Log("*** MessageWriteAsync " + client.IpPort() + " disconnected due to exception " + e);
                return(false);
            }
        }
Exemple #3
0
        private async Task DataReceiver(ClientMetadata md)
        {
            string header = "[WatsonWsServer " + md.IpPort + "] ";

            Logger?.Invoke(header + "starting data receiver");
            byte[] buffer = new byte[65536];

            try
            {
                while (true)
                {
                    MessageReceivedEventArgs msg = await MessageReadAsync(md, buffer).ConfigureAwait(false);

                    if (msg != null)
                    {
                        if (EnableStatistics)
                        {
                            _Stats.IncrementReceivedMessages();
                            _Stats.AddReceivedBytes(msg.Data.Count);
                        }

                        if (msg.Data != null)
                        {
                            Task unawaited = Task.Run(() => MessageReceived?.Invoke(this, msg), md.TokenSource.Token);
                        }
                        else
                        {
                            await Task.Delay(10).ConfigureAwait(false);
                        }
                    }
                }
            }
            catch (TaskCanceledException)
            {
                // thrown when disposed
            }
            catch (OperationCanceledException)
            {
                // thrown when disposed
            }
            catch (WebSocketException)
            {
                // thrown by MessageReadAsync
            }
            catch (Exception e)
            {
                Logger?.Invoke(header + "exception: " + Environment.NewLine + e.ToString());
            }
            finally
            {
                string ipPort = md.IpPort;
                ClientDisconnected?.Invoke(this, new ClientDisconnectedEventArgs(md.IpPort));
                md.Ws.Dispose();
                Logger?.Invoke(header + "disconnected");
                _Clients.TryRemove(ipPort, out _);
            }
        }
Exemple #4
0
        private async Task DataReceiver(ClientMetadata client, CancellationToken?cancelToken = null)
        {
            var clientId = client.IpPort();

            try
            {
                #region Wait-for-Data

                while (true)
                {
                    cancelToken?.ThrowIfCancellationRequested();

                    byte[] data = await MessageReadAsync(client);

                    if (data != null)
                    {
                        if (MessageReceived != null)
                        {
                            var _ = Task.Run(() => MessageReceived?.Invoke(client.IpPort(), data), CancellationToken.None);
                        }
                    }
                    else
                    {
                        // no message available
                        await Task.Delay(30, cancelToken.GetValueOrDefault());
                    }
                }

                #endregion
            }
            catch (OperationCanceledException oce)
            {
                Log("DataReceiver client " + clientId + " disconnected (canceled): " + oce.Message);
            }
            catch (WebSocketException wse)
            {
                Log("DataReceiver client " + clientId + " disconnected (websocket exception): " + wse.Message);
            }
            finally
            {
                if (RemoveClient(client))
                {
                    // must only fire disconnected event if the client was previously connected. Note that
                    // multithreading gives multiple disconnection events from the socket, the reader and the writer
                    ClientDisconnected?.Invoke(clientId);
                    client.Ws.Dispose();
                    Log("DataReceiver client " + clientId + " disconnected (now " + _Clients.Count + " clients active)");
                }
            }
        }
Exemple #5
0
        private async Task <MessageReceivedEventArgs> MessageReadAsync(ClientMetadata md)
        {
            string header = "[WatsonWsServer " + md.IpPort + "] ";

            using (MemoryStream stream = new MemoryStream())
            {
                byte[] buffer           = new byte[65536];
                ArraySegment <byte> seg = new ArraySegment <byte>(buffer);

                while (true)
                {
                    WebSocketReceiveResult result = await md.Ws.ReceiveAsync(seg, md.TokenSource.Token);

                    /*
                     * Console.WriteLine("Websocket state : " + md.Ws.State);
                     * Console.WriteLine("Close status    : " + result.CloseStatus);
                     * Console.WriteLine("Message type    : " + result.MessageType);
                     */

                    if (result.CloseStatus != null)
                    {
                        Logger?.Invoke(header + "close received");
                        await md.Ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);

                        throw new WebSocketException("Websocket closed.");
                    }

                    if (md.Ws.State != WebSocketState.Open)
                    {
                        Logger?.Invoke(header + "websocket no longer open");
                        throw new WebSocketException("Websocket closed.");
                    }

                    if (md.TokenSource.Token.IsCancellationRequested)
                    {
                        Logger?.Invoke(header + "cancel requested");
                    }

                    if (result.Count > 0)
                    {
                        stream.Write(buffer, 0, result.Count);
                    }

                    if (result.EndOfMessage)
                    {
                        return(new MessageReceivedEventArgs(md.IpPort, stream.ToArray(), result.MessageType));
                    }
                }
            }
        }
Exemple #6
0
        private async Task DataReceiver(ClientMetadata md)
        {
            string header = "[WatsonWsServer " + md.IpPort + "] ";

            Logger?.Invoke(header + "starting data receiver");

            try
            {
                while (true)
                {
                    MessageReceivedEventArgs msg = await MessageReadAsync(md);

                    if (msg != null)
                    {
                        _Stats.IncrementReceivedMessages();
                        _Stats.AddReceivedBytes(msg.Data.Length);

                        if (msg.Data != null)
                        {
                            MessageReceived?.Invoke(this, msg);
                        }
                        else
                        {
                            await Task.Delay(100);
                        }
                    }
                }
            }
            catch (OperationCanceledException)
            {
                // thrown when disposed
            }
            catch (WebSocketException)
            {
                // thrown by MessageReadAsync
            }
            catch (Exception e)
            {
                Logger?.Invoke(header + "exception: " + Environment.NewLine + e.ToString());
            }
            finally
            {
                string ipPort = md.IpPort;
                ClientDisconnected?.Invoke(this, new ClientDisconnectedEventArgs(md.IpPort));
                md.Ws.Dispose();
                Logger?.Invoke(header + "disconnected");
                _Clients.TryRemove(ipPort, out _);
            }
        }
        private async Task <bool> MessageWriteAsync(ClientMetadata md, byte[] data, WebSocketMessageType messageType)
        {
            string header = "[WatsonWsServer.MessageWriteAsync " + md.IpPort + "] ";

            try
            {
                #region Send-Message

                // Cannot have two simultaneous SendAsync calls so use a
                // semaphore to block the second until the first has completed

                await md.SendLock.WaitAsync(md.TokenSource.Token);

                try
                {
                    await md.Ws.SendAsync(new ArraySegment <byte>(data, 0, data.Length),
                                          messageType, true, md.TokenSource.Token);
                }
                finally
                {
                    md.SendLock.Release();
                }

                _Stats.SentMessages += 1;
                _Stats.SentBytes    += data.Length;

                return(true);

                #endregion
            }
            catch (OperationCanceledException oce)
            {
                Logger?.Invoke(header + "disconnected (canceled): " + oce.Message);
            }
            catch (WebSocketException wse)
            {
                Logger?.Invoke(header + "disconnected (websocket exception): " + wse.Message);
            }
            catch (Exception e)
            {
                Logger?.Invoke(header + "disconnected due to exception: " + Environment.NewLine + e.ToString());
            }

            return(false);
        }
Exemple #8
0
        private async Task <MessageReceivedEventArgs> MessageReadAsync(ClientMetadata md, byte[] buffer)
        {
            string header = "[WatsonWsServer " + md.IpPort + "] ";

            using (MemoryStream ms = new MemoryStream())
            {
                ArraySegment <byte> seg = new ArraySegment <byte>(buffer);

                while (true)
                {
                    WebSocketReceiveResult result = await md.Ws.ReceiveAsync(seg, md.TokenSource.Token).ConfigureAwait(false);

                    if (result.CloseStatus != null)
                    {
                        Logger?.Invoke(header + "close received");
                        await md.Ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);

                        throw new WebSocketException("Websocket closed.");
                    }

                    if (md.Ws.State != WebSocketState.Open)
                    {
                        Logger?.Invoke(header + "websocket no longer open");
                        throw new WebSocketException("Websocket closed.");
                    }

                    if (md.TokenSource.Token.IsCancellationRequested)
                    {
                        Logger?.Invoke(header + "cancel requested");
                    }

                    if (result.Count > 0)
                    {
                        ms.Write(buffer, 0, result.Count);
                    }

                    if (result.EndOfMessage)
                    {
                        return(new MessageReceivedEventArgs(md.IpPort, new ArraySegment <byte>(ms.GetBuffer(), 0, (int)ms.Length), result.MessageType));
                    }
                }
            }
        }
Exemple #9
0
        private async Task AcceptConnections()
        {
            string header = "[WatsonWsServer.AcceptConnections] ";

            try
            {
                _Listener.Start();

                while (!_Token.IsCancellationRequested)
                {
                    HttpListenerContext ctx = await _Listener.GetContextAsync();

                    string ip     = ctx.Request.RemoteEndPoint.Address.ToString();
                    int    port   = ctx.Request.RemoteEndPoint.Port;
                    string ipPort = ip + ":" + port;

                    lock (_PermittedIpsLock)
                    {
                        if (PermittedIpAddresses != null &&
                            PermittedIpAddresses.Count > 0 &&
                            !PermittedIpAddresses.Contains(ip))
                        {
                            Logger?.Invoke(header + "rejecting connection from " + ipPort + " (not permitted)");
                            ctx.Response.StatusCode = 401;
                            ctx.Response.Close();
                            continue;
                        }
                    }

                    if (!ctx.Request.IsWebSocketRequest)
                    {
                        if (HttpHandler != null)
                        {
                            Logger?.Invoke(header + "non-websocket request forwarded to HTTP handler from " + ipPort + ": " + ctx.Request.HttpMethod.ToString() + " " + ctx.Request.RawUrl);
                            HttpHandler.Invoke(ctx);
                        }
                        else
                        {
                            Logger?.Invoke(header + "non-websocket request rejected from " + ipPort);
                            ctx.Response.StatusCode = 400;
                            ctx.Response.Close();
                        }

                        continue;
                    }

                    CancellationTokenSource tokenSource = new CancellationTokenSource();
                    CancellationToken       token       = tokenSource.Token;

                    await Task.Run(() =>
                    {
                        Logger?.Invoke(header + "starting data receiver for " + ipPort);

                        Task.Run(async() =>
                        {
                            WebSocketContext wsContext;
                            try
                            {
                                wsContext = await ctx.AcceptWebSocketAsync(subProtocol: null);
                            }
                            catch (Exception)
                            {
                                Logger?.Invoke(header + "unable to retrieve websocket content for client " + ipPort);
                                ctx.Response.StatusCode = 500;
                                ctx.Response.Close();
                                return;
                            }

                            WebSocket ws      = wsContext.WebSocket;
                            ClientMetadata md = new ClientMetadata(ctx, ws, wsContext, tokenSource);

                            _Clients.TryAdd(md.IpPort, md);
                            ClientConnected?.Invoke(this, new ClientConnectedEventArgs(md.IpPort, ctx.Request));
                            await Task.Run(() => DataReceiver(md), token);
                        }, token);
                    }, _Token);
                }
            }
            catch (HttpListenerException)
            {
                // can be thrown when disposed
            }
            catch (OperationCanceledException)
            {
                // thrown when disposed
            }
            catch (Exception e)
            {
                Logger?.Invoke(header + "exception:" + Environment.NewLine + e.ToString());
            }
            finally
            {
                ServerStopped?.Invoke(this, EventArgs.Empty);
            }
        }
Exemple #10
0
        private async Task <byte[]> MessageReadAsync(ClientMetadata client)
        {
            /*
             *
             * Do not catch exceptions, let them get caught by the data reader
             * to destroy the connection
             *
             */

            #region Check-for-Null-Values

            if (client.HttpContext == null)
            {
                return(null);
            }
            if (client.WsContext == null)
            {
                return(null);
            }

            #endregion

            #region Variables

            byte[] contentBytes;

            #endregion

            #region Read-Data

            using (var dataStream = new MemoryStream())
            {
                const long bufferSize = 16 * 1024;

                var buffer        = new byte[bufferSize];
                var bufferSegment = new ArraySegment <byte>(buffer);
                while (client.Ws.State == WebSocketState.Open)
                {
                    var receiveResult = await client.Ws.ReceiveAsync(bufferSegment, client.KillToken.Token);

                    if (receiveResult.MessageType == WebSocketMessageType.Close)
                    {
                        throw new WebSocketException("Socket closed");
                    }

                    // write this chunk to the data stream
                    dataStream.Write(buffer, 0, receiveResult.Count);
                    if (receiveResult.EndOfMessage)
                    {
                        // end of message so return the buffer
                        break;
                    }
                }

                contentBytes = dataStream.ToArray();
            }

            #endregion

            return(contentBytes.Length == 0 ? null : contentBytes);
        }
Exemple #11
0
 private bool RemoveClient(ClientMetadata client)
 {
     return(_Clients.TryRemove(client.IpPort(), out var removedClient));
 }
Exemple #12
0
 private bool AddClient(ClientMetadata client)
 {
     _Clients.TryRemove(client.IpPort(), out var removedClient);
     return(_Clients.TryAdd(client.IpPort(), client));
 }
Exemple #13
0
        private async Task AcceptConnections()
        {
            try
            {
                #region Accept-WS-Connections

                _Listener.Start();

                while (!_Token.IsCancellationRequested)
                {
                    #region Accept-and-Verify-Connection

                    HttpListenerContext ctx = await _Listener.GetContextAsync();

                    string clientIp   = ctx.Request.RemoteEndPoint.Address.ToString();
                    int    clientPort = ctx.Request.RemoteEndPoint.Port;
                    string ipPort     = clientIp + ":" + clientPort;

                    lock (_PermittedIpsLock)
                    {
                        if (PermittedIpAddresses != null &&
                            PermittedIpAddresses.Count > 0 &&
                            !PermittedIpAddresses.Contains(clientIp))
                        {
                            Log("*** AcceptConnections rejecting connection from " + clientIp + " (not permitted)");
                            ctx.Response.StatusCode = 401;
                            ctx.Response.Close();
                            continue;
                        }
                    }

                    Log("AcceptConnections accepted connection from " + clientIp + ":" + clientPort);

                    if (!ctx.Request.IsWebSocketRequest)
                    {
                        Log("*** AcceptConnections rejecting connection from " + clientIp + " (not a websocket request)");
                        ctx.Response.StatusCode = 400;
                        ctx.Response.Close();
                        continue;
                    }

                    #endregion

                    #region Start-Client-Task

                    CancellationTokenSource killTs    = new CancellationTokenSource();
                    CancellationToken       killToken = killTs.Token;

                    Task _ = Task.Run(() =>
                    {
                        Log("AcceptConnections starting data receiver for " + ipPort + " (now " + _Clients.Count + " clients)");

                        Task.Run(async() =>
                        {
                            #region Connection-Callback

                            if (ClientConnected != null)
                            {
                                bool connected = await ClientConnected(ipPort, ctx.Request);

                                if (!connected)
                                {
                                    Log("*** AcceptConnections unable to validate client " + ipPort);
                                    ctx.Response.StatusCode = 401;
                                    ctx.Response.Close();
                                    return;
                                }
                            }

                            #endregion

                            #region Get-Websocket-Context

                            WebSocketContext wsContext;
                            try
                            {
                                wsContext = await ctx.AcceptWebSocketAsync(subProtocol: null);
                            }
                            catch (Exception)
                            {
                                Log("*** AcceptConnections unable to retrieve websocket content for client " + ipPort);
                                ctx.Response.StatusCode = 500;
                                ctx.Response.Close();
                                return;
                            }

                            WebSocket ws          = wsContext.WebSocket;
                            ClientMetadata client = new ClientMetadata(ctx, ws, wsContext, killTs);

                            #endregion

                            #region Add-Client

                            if (!AddClient(client))
                            {
                                Log("*** AcceptConnections unable to add client " + ipPort);
                                ctx.Response.StatusCode = 500;
                                ctx.Response.Close();
                                return;
                            }

                            await DataReceiver(client, killToken);

                            #endregion
                        }, killToken);
                    }, _Token);

                    #endregion
                }

                #endregion
            }
            catch (OperationCanceledException)
            {
            }
            catch (Exception e)
            {
                LogException("AcceptConnections", e);
            }
            finally
            {
                ServerStopped?.Invoke();
            }
        }
Exemple #14
0
        private async Task AcceptConnections()
        {
            try
            {
                _Listener.Start();

                while (true)
                {
                    if (_Token.IsCancellationRequested)
                    {
                        break;
                    }
                    if (!_Listener.IsListening)
                    {
                        Task.Delay(100).Wait();
                        continue;
                    }

                    HttpListenerContext ctx = await _Listener.GetContextAsync();

                    string ip     = ctx.Request.RemoteEndPoint.Address.ToString();
                    int    port   = ctx.Request.RemoteEndPoint.Port;
                    string ipPort = ip + ":" + port;

                    lock (_PermittedIpsLock)
                    {
                        if (PermittedIpAddresses != null &&
                            PermittedIpAddresses.Count > 0 &&
                            !PermittedIpAddresses.Contains(ip))
                        {
                            Logger?.Invoke(_Header + "rejecting " + ipPort + " (not permitted)");
                            ctx.Response.StatusCode = 401;
                            ctx.Response.Close();
                            continue;
                        }
                    }

                    if (!ctx.Request.IsWebSocketRequest)
                    {
                        if (HttpHandler == null)
                        {
                            Logger?.Invoke(_Header + "non-websocket request rejected from " + ipPort);
                            ctx.Response.StatusCode = 400;
                            ctx.Response.Close();
                        }
                        else
                        {
                            Logger?.Invoke(_Header + "non-websocket request from " + ipPort + " HTTP-forwarded: " + ctx.Request.HttpMethod.ToString() + " " + ctx.Request.RawUrl);
                            HttpHandler.Invoke(ctx);
                        }

                        continue;
                    }
                    else
                    {
                        /*
                         * HttpListenerRequest req = ctx.Request;
                         * Console.WriteLine(Environment.NewLine + req.HttpMethod.ToString() + " " + req.RawUrl);
                         * if (req.Headers != null && req.Headers.Count > 0)
                         * {
                         *  Console.WriteLine("Headers:");
                         *  var items = req.Headers.AllKeys.SelectMany(req.Headers.GetValues, (k, v) => new { key = k, value = v });
                         *  foreach (var item in items)
                         *  {
                         *      Console.WriteLine("  {0}: {1}", item.key, item.value);
                         *  }
                         * }
                         */
                    }

                    await Task.Run(() =>
                    {
                        Logger?.Invoke(_Header + "starting data receiver for " + ipPort);

                        CancellationTokenSource tokenSource = new CancellationTokenSource();
                        CancellationToken token             = tokenSource.Token;

                        Task.Run(async() =>
                        {
                            WebSocketContext wsContext = await ctx.AcceptWebSocketAsync(subProtocol: null);
                            WebSocket ws      = wsContext.WebSocket;
                            ClientMetadata md = new ClientMetadata(ctx, ws, wsContext, tokenSource);

                            _Clients.TryAdd(md.IpPort, md);

                            ClientConnected?.Invoke(this, new ClientConnectedEventArgs(md.IpPort, ctx.Request));
                            await Task.Run(() => DataReceiver(md), token);
                        }, token);
                    }, _Token);
                }
            }
            catch (HttpListenerException)
            {
                // thrown when disposed
            }
            catch (OperationCanceledException)
            {
                // thrown when disposed
            }
            catch (ObjectDisposedException)
            {
                // thrown when disposed
            }
            catch (Exception e)
            {
                Logger?.Invoke(_Header + "listener exception:" + Environment.NewLine + e.ToString());
            }
            finally
            {
                ServerStopped?.Invoke(this, EventArgs.Empty);
            }
        }
        private async Task <bool> MessageWriteAsync(ClientMetadata md, byte[] data, WebSocketMessageType msgType, CancellationToken token)
        {
            string header = "[WatsonWsServer " + md.IpPort + "] ";

            CancellationToken[] tokens = new CancellationToken[3];
            tokens[0] = _Token;
            tokens[1] = token;
            tokens[2] = md.TokenSource.Token;

            using (CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(tokens))
            {
                try
                {
                    #region Send-Message

                    await md.SendLock.WaitAsync(md.TokenSource.Token).ConfigureAwait(false);

                    try
                    {
                        await md.Ws.SendAsync(new ArraySegment <byte>(data, 0, data.Length), msgType, true, linkedCts.Token).ConfigureAwait(false);
                    }
                    finally
                    {
                        md.SendLock.Release();
                    }

                    _Stats.IncrementSentMessages();
                    _Stats.AddSentBytes(data.Length);

                    return(true);

                    #endregion
                }
                catch (TaskCanceledException)
                {
                    if (_Token.IsCancellationRequested)
                    {
                        Logger?.Invoke(header + "server canceled");
                    }
                    else if (token.IsCancellationRequested)
                    {
                        Logger?.Invoke(header + "message send canceled");
                    }
                    else if (md.TokenSource.Token.IsCancellationRequested)
                    {
                        Logger?.Invoke(header + "client canceled");
                    }
                }
                catch (OperationCanceledException)
                {
                    if (_Token.IsCancellationRequested)
                    {
                        Logger?.Invoke(header + "canceled");
                    }
                    else if (token.IsCancellationRequested)
                    {
                        Logger?.Invoke(header + "message send canceled");
                    }
                    else if (md.TokenSource.Token.IsCancellationRequested)
                    {
                        Logger?.Invoke(header + "client canceled");
                    }
                }
                catch (ObjectDisposedException)
                {
                    Logger?.Invoke(header + "disposed");
                }
                catch (WebSocketException)
                {
                    Logger?.Invoke(header + "websocket disconnected");
                }
                catch (SocketException)
                {
                    Logger?.Invoke(header + "socket disconnected");
                }
                catch (InvalidOperationException)
                {
                    Logger?.Invoke(header + "disconnected due to invalid operation");
                }
                catch (IOException)
                {
                    Logger?.Invoke(header + "IO disconnected");
                }
                catch (Exception e)
                {
                    Logger?.Invoke(header + "exception: " + Environment.NewLine + e.ToString());
                }
            }

            return(false);
        }
        private async Task AcceptConnections()
        {
            try
            {
                #region Accept-WS-Connections

                Listener.Start();

                while (!Token.IsCancellationRequested)
                {
                    #region Accept-Connection

                    HttpListenerContext httpContext = await Listener.GetContextAsync();

                    #endregion

                    if (httpContext.Request.RemoteEndPoint != null)
                    {
                        #region Check-IP-Address

                        string clientIp   = httpContext.Request.RemoteEndPoint.Address.ToString();
                        int    clientPort = httpContext.Request.RemoteEndPoint.Port;

                        Dictionary <string, string> query        = new Dictionary <string, string>();
                        NameValueCollection         requestQuery = httpContext.Request.QueryString;
                        foreach (string key in requestQuery.AllKeys)
                        {
                            query[key] = requestQuery[key];
                        }

                        if (PermittedIps != null && PermittedIps.Count > 0)
                        {
                            if (!PermittedIps.ContainsKey(clientIp))
                            {
                                Log("*** AcceptConnections rejecting connection from " + clientIp + " (not permitted)");
                                httpContext.Response.StatusCode = 401;
                                httpContext.Response.Close();
                                return;
                            }
                        }

                        Log("AcceptConnections accepted connection from " + clientIp + ":" + clientPort);

                        #endregion

                        #region Get-Websocket-Context

                        WebSocketContext wsContext = null;
                        try
                        {
                            wsContext = httpContext.AcceptWebSocketAsync(subProtocol: null).Result;
                        }
                        catch (Exception)
                        {
                            Log("*** AcceptConnections unable to retrieve websocket content for client " + clientIp + ":" + clientPort);
                            httpContext.Response.StatusCode = 500;
                            httpContext.Response.Close();
                            return;
                        }

                        WebSocket ws = wsContext.WebSocket;

                        #endregion

                        Task unawaited = Task.Run(() =>
                        {
                            #region Add-to-Client-List

                            // Do not decrement in this block, decrement is done by the connection reader

                            ClientMetadata currClient = new ClientMetadata(httpContext, ws, wsContext);
                            if (!AddClient(currClient))
                            {
                                Log("*** AcceptConnections unable to add client " + clientIp + ":" + clientPort);
                                httpContext.Response.StatusCode = 500;
                                httpContext.Response.Close();
                                return;
                            }

                            #endregion

                            #region Start-Data-Receiver
                            CancellationToken killToken = currClient.KillToken.Token;

                            Log("AcceptConnections starting data receiver for " + clientIp + ":" + clientPort + " (now " + Clients.Count + " clients)");
                            if (ClientConnected != null)
                            {
                                Task.Run(() => ClientConnected(clientIp + ":" + clientPort, query), killToken);
                            }

                            Task.Run(async() => await DataReceiver(currClient, killToken), killToken);

                            #endregion
                        }, Token);
                    }
                }

                #endregion
            }
            catch (Exception e)
            {
                LogException("AcceptConnections", e);
            }
        }