/// <summary>
        /// Connects to the WebSocket server.
        /// </summary>
        /// <param name="uri">The URI of the WebSocket server.</param>
        /// <param name="customHeaders">Custom headers to send with the request.</param>
        /// <returns></returns>
        public override async Task ConnectAsync(Uri uri, IReadOnlyDictionary <string, string> customHeaders = null)
        {
            SocketMessageQueue = new ConcurrentQueue <string>();
            TokenSource        = new CancellationTokenSource();

            StreamDecompressor?.Dispose();
            CompressedStream?.Dispose();
            DecompressedStream?.Dispose();

            DecompressedStream = new MemoryStream();
            CompressedStream   = new MemoryStream();
            StreamDecompressor = new DeflateStream(CompressedStream, CompressionMode.Decompress);

            Socket = new ClientWebSocket();
            Socket.Options.KeepAliveInterval = TimeSpan.FromSeconds(20);
            if (Proxy != null) // because mono doesn't implement this properly
            {
                Socket.Options.Proxy = Proxy;
            }

            if (customHeaders != null)
            {
                foreach (var kvp in customHeaders)
                {
                    Socket.Options.SetRequestHeader(kvp.Key, kvp.Value);
                }
            }

            await Socket.ConnectAsync(uri, Token).ConfigureAwait(false);

            await OnConnectedAsync().ConfigureAwait(false);

            WsListener = Task.Run(ListenAsync, Token);
        }
 /// <summary>
 /// Disposes this socket client.
 /// </summary>
 public virtual void Dispose()
 {
     StreamDecompressor?.Dispose();
     CompressedStream?.Dispose();
     DecompressedStream?.Dispose();
 }
        /// <summary>
        /// Connects to the WebSocket server.
        /// </summary>
        /// <param name="uri">The URI of the WebSocket server.</param>
        /// <param name="customHeaders">Custom headers to send with the request.</param>
        /// <returns></returns>
        public override Task ConnectAsync(Uri uri, IReadOnlyDictionary <string, string> customHeaders = null)
        {
            StreamDecompressor?.Dispose();
            CompressedStream?.Dispose();
            DecompressedStream?.Dispose();

            DecompressedStream = new MemoryStream();
            CompressedStream   = new MemoryStream();
            StreamDecompressor = new DeflateStream(CompressedStream, CompressionMode.Decompress);

            _socket = new ws4net.WebSocket(uri.ToString(), customHeaderItems: customHeaders?.ToList());
            if (Proxy != null) // f**k this, I ain't working with that shit
            {
                throw new NotImplementedException("Proxies are not supported on non-Microsoft WebSocket client implementations.");
            }

            _socket.Opened          += HandlerOpen;
            _socket.Closed          += HandlerClose;
            _socket.MessageReceived += HandlerMessage;
            _socket.DataReceived    += HandlerData;
            _socket.Error           += _socket_Error;
            _socket.Open();

            return(Task.FromResult <BaseWebSocketClient>(this));

            void HandlerOpen(object sender, s.EventArgs e)
            => _connect.InvokeAsync().ConfigureAwait(false).GetAwaiter().GetResult();

            void HandlerClose(object sender, s.EventArgs e)
            {
                if (e is ws4net.ClosedEventArgs ea)
                {
                    _disconnect.InvokeAsync(new SocketCloseEventArgs(null)
                    {
                        CloseCode = ea.Code, CloseMessage = ea.Reason
                    }).ConfigureAwait(false).GetAwaiter().GetResult();
                }
                else
                {
                    _disconnect.InvokeAsync(new SocketCloseEventArgs(null)
                    {
                        CloseCode = -1, CloseMessage = "unknown"
                    }).ConfigureAwait(false).GetAwaiter().GetResult();
                }
            }

            void HandlerMessage(object sender, ws4net.MessageReceivedEventArgs e)
            {
                _message.InvokeAsync(new SocketMessageEventArgs {
                    Message = e.Message
                }).ConfigureAwait(false).GetAwaiter().GetResult();
            }

            void HandlerData(object sender, ws4net.DataReceivedEventArgs e)
            {
                string msg;

                if (e.Data[0] == 0x78)
                {
                    CompressedStream.Write(e.Data, 2, e.Data.Length - 2);
                }
                else
                {
                    CompressedStream.Write(e.Data, 0, e.Data.Length);
                }
                CompressedStream.Flush();
                CompressedStream.Position = 0;

                // partial credit to FiniteReality
                // overall idea is his
                // I tuned the finer details
                // -Emzi
                var sfix = BitConverter.ToUInt16(e.Data, e.Data.Length - 2);

                if (sfix != ZLIB_STREAM_SUFFIX)
                {
                    using (var zlib = new DeflateStream(CompressedStream, CompressionMode.Decompress, true))
                        zlib.CopyTo(DecompressedStream);
                }
                else
                {
                    StreamDecompressor.CopyTo(DecompressedStream);
                }

                msg = UTF8.GetString(DecompressedStream.ToArray(), 0, (int)DecompressedStream.Length);

                DecompressedStream.Position = 0;
                DecompressedStream.SetLength(0);
                CompressedStream.Position = 0;
                CompressedStream.SetLength(0);

                _message.InvokeAsync(new SocketMessageEventArgs {
                    Message = msg
                }).ConfigureAwait(false).GetAwaiter().GetResult();
            }
        }