コード例 #1
0
        public static async Task ParseAsync(string filePath, CloudFrontRecord[] items)
        {
            if (File.Exists(filePath))
            {
                using (var fileStream = File.OpenRead(filePath))
                    using (var decompressionStream = new GZipStream(fileStream, CompressionMode.Decompress))
                    {
                        var pipeReader = StreamConnection.GetReader(decompressionStream);

                        var position = 0;

                        while (true)
                        {
                            var result = await pipeReader.ReadAsync();

                            var buffer = result.Buffer;

                            ParseLines(items, ref buffer, ref position);

                            // Tell the PipeReader how much of the buffer we have consumed
                            pipeReader.AdvanceTo(buffer.Start, buffer.End);

                            // Stop reading if there's no more data coming
                            if (result.IsCompleted)
                            {
                                break;
                            }
                        }

                        // Mark the PipeReader as complete
                        pipeReader.Complete();
                    }
            }
        }
コード例 #2
0
        private async Task EnsureVirtualConnectionClientCreated()
        {
            // Asynchronous equivalent to a 'lock(...) { ... }'
            await _connectionCreationSemaphore.WaitAsync();

            try
            {
                if (_virtualConnectionClient == null)
                {
                    _physicalConnection = StreamConnection.Create();

                    var connection = await _physicalConnection.Open(_socketAddress);

                    _virtualConnectionClient          = new VirtualConnectionClient(connection);
                    _virtualConnectionClient.OnError += (ex) =>
                    {
                        // This callback is fired only if there's a protocol-level failure (e.g., child process disconnected
                        // unexpectedly). It does *not* fire when RPC calls return errors. Since there's been a protocol-level
                        // failure, this Node instance is no longer usable and should be discarded.
                        _connectionHasFailed = true;

                        OutputLogger.LogError(0, ex, ex.Message);
                    };
                }
            }
            finally
            {
                _connectionCreationSemaphore.Release();
            }
        }
コード例 #3
0
        public async Task ServerClientDoubleInverted_SslStream_PingPong()
        {
            Log.DebugLog();
            var tuple = CreateConnectedSocketPair();

            using (var client = tuple.Item1)
                using (var server = tuple.Item2)
                {
                    var clientPipe = SocketConnection.Create(client, PipeOptions, name: "socket client");
                    var serverPipe = SocketConnection.Create(server, PipeOptions, name: "socket server");

                    var serverStream = StreamConnection.GetDuplex(serverPipe, name: "stream server");
                    var serverSsl    = new SslStream(serverStream, false, IgnoreAllCertificateErrors, null, EncryptionPolicy.RequireEncryption);

                    var clientStream = StreamConnection.GetDuplex(clientPipe, name: "stream client");
                    var clientSsl    = new SslStream(clientStream, false, IgnoreAllCertificateErrors, null, EncryptionPolicy.RequireEncryption);

                    var serverAuth = serverSsl.AuthenticateAsServerAsync(SomeCertificate);
                    var clientAuth = clientSsl.AuthenticateAsClientAsync("somesite");

                    await serverAuth;
                    await clientAuth;

                    var serverRevert = StreamConnection.GetDuplex(serverSsl, PipeOptions, name: "revert server");
                    var clientRevert = StreamConnection.GetDuplex(clientSsl, PipeOptions, name: "revert client");



                    await PingPong(clientRevert, serverRevert, LoopCount);
                }
            Log.DebugLog("All good!");
        }
コード例 #4
0
        private void CreateModel()
        {
            if (StreamConnection == null || !StreamConnection.IsOpen)
            {
                Logger.LogWarning($"Connection is closed");
                return;
            }

            try
            {
                if (IsModelDisposed)
                {
                    Model           = StreamConnection.CreateModel();
                    IsModelDisposed = false;
                    Logger.LogInformation($"AMQP model sucessfully created, channelNo={Model.ChannelNumber}");
                }
                else
                {
                    Logger.LogDebug($"AMQP model already created, channelNo={Model.ChannelNumber}");
                }
            }
            catch (Exception e)
            {
                throw new Exception($"Creating AMQP model errored, errorsCout={ProcessNewConsumerErrorCounter} {e}", e);
            }
        }
コード例 #5
0
        public async Task ServerClient_SslStream_Inverter_PingPong()
        {
            Log.DebugLog();
            var tuple = CreateConnectedSocketPair();

            using (var client = tuple.Item1)
                using (var server = tuple.Item2)
                {
                    var serverSsl = new SslStream(new NetworkStream(server), false, IgnoreAllCertificateErrors, null, EncryptionPolicy.RequireEncryption);
                    var clientSsl = new SslStream(new NetworkStream(client), false, IgnoreAllCertificateErrors, null, EncryptionPolicy.RequireEncryption);


                    var serverAuth = serverSsl.AuthenticateAsServerAsync(SomeCertificate);
                    var clientAuth = clientSsl.AuthenticateAsClientAsync("somesite");

                    await serverAuth;
                    await clientAuth;

                    var serverPipe = StreamConnection.GetDuplex(serverSsl);
                    var clientPipe = StreamConnection.GetDuplex(clientSsl);

                    await PingPong(clientPipe, serverPipe, LoopCount);
                }
            Log.DebugLog("All good!");
        }
コード例 #6
0
 protected override void DoClose(StreamConnection connection)
 {
     Logger.Debug("closing connection");
     connection.Close();
     Logger.Debug("closing client");
     client.Close();
     Logger.Debug("closed");
     state = State.Disconnected;
 }
コード例 #7
0
        static async Task StartIpcProxyClient(string pipeName, CancellationToken cancellationToken)
        {
            // forward stdin/stdout from this process to a named pipe server
            using (var pipeClient = NamedPipes.CreatePipeClient(pipeName))
            {
                _logger?.Log("Connecting to debug server pipe...");
                await pipeClient.ConnectAsync(cancellationToken);

                _logger?.Log("Connected to debug server pipe");

                using (var stdIn = Console.OpenStandardInput())
                    using (var stdOut = Console.OpenStandardOutput())
                    {
                        // If verbose logging is enabled, setup interception streams for both the input & output of the debugger
                        // protocol stream.
                        List <Task> streamTasks       = new List <Task>();
                        Stream      proxyStreamInput  = null;
                        Stream      proxyStreamOutput = null;
                        if (_args.Trace)
                        {
                            var proxyPipeInput = new Pipe();
                            proxyStreamInput = StreamConnection.GetDuplex(proxyPipeInput.Reader, proxyPipeInput.Writer);

                            var proxyPipeOutput = new Pipe();
                            proxyStreamOutput = StreamConnection.GetDuplex(proxyPipeOutput.Reader, proxyPipeOutput.Writer);

                            var verboseProxyLogTask = Task.WhenAll(
                                CreateVerboseProxyLogger("-> ", proxyPipeInput.Reader, cancellationToken),
                                CreateVerboseProxyLogger("<- ", proxyPipeOutput.Reader, cancellationToken));
                            streamTasks.Add(verboseProxyLogTask);
                        }

                        var pipeInputTask  = NamedPipes.ConnectStream(stdIn, pipeClient, proxyStreamInput, cancellationToken);
                        var pipeOutputTask = NamedPipes.ConnectStream(pipeClient, stdOut, proxyStreamOutput, cancellationToken);

                        var checkClosedTask = Task.Run(() =>
                        {
                            while (pipeClient.IsConnected)
                            {
                                Thread.Sleep(100);
                            }
                        });

                        streamTasks.AddRange(new[] { checkClosedTask, pipeInputTask, pipeOutputTask });

                        var completedTask = await Task.WhenAny(streamTasks.ToArray());

                        if (completedTask.IsFaulted)
                        {
                            _logger?.Log("Exception while proxying between stdin/stdout and named pipes: " + completedTask.Exception.InnerException);
                        }

                        _logger?.Log("Debug server pipe connection ended");
                    }
            }
        }
コード例 #8
0
        private void HandleUnknownPacket(StreamConnection connection, uint type, PacketStream stream)
        {
            var dump = new StringBuilder();

            for (var i = stream.Pos; i < stream.Count; i++)
            {
                dump.AppendFormat("{0:x2} ", stream.Buffer[i]);
            }
            _log.Error("Unknown packet 0x{0:x2} from {1}:\n{2}", (object)type, (object)connection.Ip, (object)dump);
        }
コード例 #9
0
        public void RequestCell(StreamConnection connection, uint instanceId, int x, int y)
        {
            var doodads   = WorldManager.Instance.GetInCell <Doodad>(instanceId, x, y).ToArray(); // TODO active worldId from character
            var requestId = connection.GetNextRequestId(doodads);
            var count     = Math.Min(doodads.Length, 30);
            var res       = new Doodad[count];

            Array.Copy(doodads, 0, res, 0, count);
            connection.SendPacket(new TCDoodadStreamPacket(requestId, count, res));
        }
コード例 #10
0
        public void RelaysOnNext(User user)
        {
            var functor = Mock.Of<IFunctor>();
            var sut = new StreamConnection<User, string>(
                user.Id, functor.Action);

            Stream<User, string>.Get(user.Id).OnNext(Observable.Return(user));

            Mock.Get(functor).Verify(f => f.Action(user), Times.Once());
        }
コード例 #11
0
        public static async Task <List <Event> > ProcessFeed(Stream stream)
        {
            var reader = StreamConnection.GetReader(stream);

            var events = new List <Event>();

            void catchEvent(Event ev) => events.Add(ev);
            await GetEvents(reader, catchEvent);

            return(events);
        }
コード例 #12
0
        async Task IDuplexMessageStream.OpenAsync(CancellationToken cancellationToken)
        {
            ReadCompleted  = false;
            WriteCompleted = false;

            readStream = await readProvider(cancellationToken).ConfigureAwait(false);

            writeStream = writeProvider == readProvider ? readStream : await writeProvider(cancellationToken).ConfigureAwait(false);

            reader = StreamConnection.GetReader(readStream);
            writer = StreamConnection.GetWriter(writeStream);
        }
コード例 #13
0
        public void Dispose()
        {
            Logger.LogDebug("Shutting down StreamController");
            ConnectionCancellation.Cancel();
            DisposeModel();
            CancelValidationMessages();
            Dispatcher.Tell(new DisposeMessage());
            Self.Tell(new DisconnectedMessage {
                IDConnection = StreamConnection?.GetHashCode()
            });

            Logger.LogInformation("StreamController correctly disposed");
        }
コード例 #14
0
        protected override void PreRestart(Exception reason, object message)
        {
            Logger.LogError(
                $"Actor restart reason exception={reason?.ToString() ?? "null"}." +
                (message != null
                    ? $" last processing messageType={message.GetType().Name}"
                    : ""));

            CancelValidationMessages();

            base.PreRestart(reason, new DisconnectedMessage {
                IDConnection = StreamConnection?.GetHashCode()
            });
        }
コード例 #15
0
        /// <summary>
        /// Send multiple messages over a stream
        /// </summary>
        public static async ValueTask SendAsync <T>(Stream destination, IAsyncEnumerable <T> source, MessagePipeOptions options = default)
        {
            var writer = StreamConnection.GetWriter(destination, options.PipeOptions);
            var tcs    = new TaskCompletionSource <object>();

#pragma warning disable CS0618                                // this *is* a Pipe; this is fine
            writer.OnReaderCompleted(s_ReaderCompleted, tcs); // attach to detect when the pipe is drained
#pragma warning restore CS0618

            await SendAsync <T>(writer, source, options.Without(MessagePipeFlags.LeaveOpen)).ConfigureAwait(false); // send the data

            await tcs.Task.ConfigureAwait(false);                                                                   // wait for the pipe to drain

            await destination.FlushAsync().ConfigureAwait(false);                                                   // flush the stream
        }
コード例 #16
0
 public override void OnConnect(Session session)
 {
     _log.Info("Connect from {0} established, session id: {1}", session.Ip.ToString(), session.Id.ToString());
     try
     {
         var con = new StreamConnection(session);
         con.OnConnect();
         StreamConnectionTable.Instance.AddConnection(con);
     }
     catch (Exception e)
     {
         session.Close();
         _log.Error(e);
     }
 }
コード例 #17
0
        public void SubscribesStreamWeak(User user)
        {
            var stream = Stream<User, string>.Get(user.Id);
            var subject = new Subject<User>();
            var subjectReference = new WeakReference(subject);
            var sut = new StreamConnection<User, string>(
                user.Id, subject.OnNext);
            var sutReference = new WeakReference(sut);

            subject = null;
            sut = null;
            GC.Collect();

            subjectReference.IsAlive.Should().BeFalse();
            sutReference.IsAlive.Should().BeFalse();
        }
コード例 #18
0
 protected override StreamConnection DoConnect(Uri source)
 {
     try {
         client = new TcpClient();
         client.Connect(source.DnsSafeHost, source.Port);
         var stream     = client.GetStream();
         var connection = new StreamConnection(stream, stream);
         connection.ReceiveTimeout = 10000;
         connection.SendTimeout    = 8000;
         return(connection);
     }
     catch (SocketException e) {
         Logger.Error(e);
         return(null);
     }
 }
コード例 #19
0
        private async Task ConnectionHandler(CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                try
                {
                    _whenConnectionStatusChanged.OnNext(new ConnectionStatus()
                    {
                        State = State.Connecting
                    });
                    await _internalSocket.ConnectAsync(_endpoint);

                    _whenConnectionStatusChanged.OnNext(new ConnectionStatus()
                    {
                        State = State.Connected
                    });

                    IDuplexPipe pipe = StreamConnection.GetDuplex(new NetworkStream(_internalSocket), (PipeOptions)null, (string)null);
                    _clientPipe = pipe;
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
                    Task.Factory.StartNew(async() => await ReadPipeAsync(pipe.Input, cancellationToken));
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
                    await CheckConnectionAsync(_internalSocket, cancellationToken);

                    _internalSocket.Shutdown(SocketShutdown.Both);
                    _internalSocket.Disconnect(true);
                    _whenConnectionStatusChanged.OnNext(new ConnectionStatus()
                    {
                        State = State.Disconnected
                    });
                }
                catch (SocketException sex)
                {
                }
                catch (Exception ex)
                {
                }
                if (_settings.Reconnect)
                {
                    await Task.Delay(_settings.ReconnectionDelay);
                }
                else
                {
                    return;
                }
            }
        }
コード例 #20
0
        public async Task Basic_NetworkStream_Pipelines_PingPong()
        {
            Log.DebugLog();
            var tuple = CreateConnectedSocketPair();

            using (var client = tuple.Item1)
                using (var server = tuple.Item2)
                {
                    var clientStream = new NetworkStream(client);
                    var serverStream = new NetworkStream(server);

                    var clientPipe = StreamConnection.GetDuplex(clientStream, name: "client");
                    var serverPipe = StreamConnection.GetDuplex(serverStream, name: "server");
                    await PingPong(clientPipe, serverPipe, LoopCount);
                }
            Log.DebugLog("All good!");
        }
コード例 #21
0
        public async Task ServerInverted_PingPong()
        {
            Log.DebugLog();
            var tuple = CreateConnectedSocketPair();

            using (var client = tuple.Item1)
                using (var server = tuple.Item2)
                {
                    var clientPipe = SocketConnection.Create(client, PipeOptions, name: "socket client");
                    var serverPipe = SocketConnection.Create(server, PipeOptions, name: "socket server");

                    var serverStream = StreamConnection.GetDuplex(serverPipe, name: "stream server");

                    await PingPong(clientPipe, serverStream, LoopCount);
                }
            Log.DebugLog("All good!");
        }
コード例 #22
0
        private async Task <VirtualConnectionClient> GetOrCreateVirtualConnectionClientAsync()
        {
            var client = _currentVirtualConnectionClient;

            if (client == null)
            {
                await _clientModificationSemaphore.WaitAsync();

                try
                {
                    if (_currentVirtualConnectionClient == null)
                    {
                        var address = _addressForNextConnection;
                        if (string.IsNullOrEmpty(address))
                        {
                            // This shouldn't happen, because we always await 'EnsureReady' before getting here.
                            throw new InvalidOperationException("Cannot open connection to Node process until it has signalled that it is ready");
                        }

                        _currentPhysicalConnection = StreamConnection.Create();

                        var connection = await _currentPhysicalConnection.Open(address);

                        _currentVirtualConnectionClient          = new VirtualConnectionClient(connection);
                        _currentVirtualConnectionClient.OnError += (ex) =>
                        {
                            // TODO: Log the exception properly. Need to change the chain of calls up to this point to supply
                            // an ILogger or IServiceProvider etc.
                            Console.WriteLine(ex.Message);
                            ExitNodeProcess(); // We'll restart it next time there's a request to it
                        };
                    }

                    return(_currentVirtualConnectionClient);
                }
                finally
                {
                    _clientModificationSemaphore.Release();
                }
            }
            else
            {
                return(client);
            }
        }
コード例 #23
0
 protected StreamConnection DoConnect(IPEndPoint endpoint)
 {
     try {
         client = new TcpClient();
         client.Connect(endpoint);
         remoteEndPoint = (IPEndPoint)client.Client.RemoteEndPoint;
         var stream     = client.GetStream();
         var connection = new StreamConnection(stream, stream);
         connection.ReceiveTimeout = 10000;
         connection.SendTimeout    = 8000;
         Logger.Debug("Connected: {0}", endpoint);
         return(connection);
     }
     catch (SocketException e) {
         Logger.Debug("Connection Failed: {0}", endpoint);
         Logger.Debug(e);
         return(null);
     }
 }
コード例 #24
0
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_virtualConnectionClient != null)
                {
                    _virtualConnectionClient.Dispose();
                    _virtualConnectionClient = null;
                }

                if (_physicalConnection != null)
                {
                    _physicalConnection.Dispose();
                    _physicalConnection = null;
                }
            }

            base.Dispose(disposing);
        }
コード例 #25
0
        public void ContinueCell(StreamConnection connection, int requestId, int next)
        {
            var doodads = connection.GetRequest(requestId);

            if (doodads == null)
            {
                return;
            }
            if (next >= doodads.Length)
            {
                connection.RemoveRequest(requestId);
            }
            var count = Math.Min(doodads.Length - next, 30);
            var res   = new Doodad[count];

            Array.Copy(doodads, next > 0 ? next - 1 : 0, res, 0, count);
            next += count;
            connection.SendPacket(new TCDoodadStreamPacket(requestId, next, res));
        }
コード例 #26
0
        public static IEnumerable <Atom> RecvAtoms(this StreamConnection connection)
        {
            var res = new Queue <Atom>();

            connection.Recv(s => {
                while (s.Position < s.Length)
                {
                    var pos = s.Position;
                    try {
                        var atom = AtomReader.Read(s);
                        res.Enqueue(atom);
                    }
                    catch (System.IO.EndOfStreamException) {
                        s.Position = pos;
                        break;
                    }
                }
            });
            return(res);
        }
コード例 #27
0
 public void Login(StreamConnection connection, uint accountId, uint token)
 {
     if (_accounts.ContainsKey(token))
     {
         if (accountId == _accounts[token])
         {
             var gCon = GameConnectionTable.Instance.GetConnection(token);
             connection.GameConnection = gCon;
             connection.SendPacket(new TCJoinResponsePacket(0));
         }
         else
         {
             _accounts.Remove(token);
             connection.SendPacket(new TCJoinResponsePacket(1));
         }
     }
     else
     {
         connection.SendPacket(new TCJoinResponsePacket(1));
     }
 }
コード例 #28
0
        private async Task ConnectionHandler(CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                _whenConnectionStatusChanged.OnNext(new ConnectionStatus()
                {
                    State = State.Listening
                });
                Socket socket = await this._internalSocket.AcceptAsync();

                _whenConnectionStatusChanged.OnNext(new ConnectionStatus()
                {
                    State = State.Connected
                });
                IDuplexPipe pipe = StreamConnection.GetDuplex(new NetworkStream(socket), (PipeOptions)null, (string)null);
                _connectedClients.Add(pipe);
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
                Task.Factory.StartNew(async() => await ReadPipeAsync(pipe.Input, cancellationToken));
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
            }
        }
コード例 #29
0
        protected virtual void CloseConnection()
        {
            if (StreamConnection != null)
            {
                Logger.LogDebug($"CloseConnection triggered {StreamConnection}");
                try
                {
                    {
                        StreamConnection.ConnectionShutdown -= OnConnectionShutdown;
                        if (StreamConnection.IsOpen)
                        {
                            StreamConnection.Close();
                            Logger.LogDebug("Connection Closed");
                        }
                    }
                }
                catch (Exception e)
                {
                    Logger.LogWarning($"Failed to close connection {e}");
                }

                try
                {
                    {
                        DisposeModel();
                        StreamConnection.Dispose();
                        Logger.LogDebug("Connection Disposed");
                    }
                }
                catch (Exception e)
                {
                    Logger.LogWarning($"Failed to dispose connection {e}");
                }
                StreamConnection = null;
            }
            else
            {
                Logger.LogDebug("No need to CloseConnection");
            }
        }
コード例 #30
0
        private void EnsurePipeRpcClientDisposed()
        {
            _clientModificationSemaphore.Wait();

            try
            {
                if (_currentVirtualConnectionClient != null)
                {
                    _currentVirtualConnectionClient.Dispose();
                    _currentVirtualConnectionClient = null;
                }

                if (_currentPhysicalConnection != null)
                {
                    _currentPhysicalConnection.Dispose();
                    _currentPhysicalConnection = null;
                }
            }
            finally
            {
                _clientModificationSemaphore.Release();
            }
        }
コード例 #31
0
        private void ValidateConnection(ValidateConnectionMessage validateConnectionMessage)
        {
            //validate whether the reconnection was successful
            Logger.LogInformation("Starting validation for reconnection connHash={0}",
                                  StreamConnection?.GetHashCode().ToString() ?? "null");

            //in case the connection is swapped by RMQ library while the check is running
            var testConnection = StreamConnection;

            if (testConnection == null)
            {
                Logger.LogWarning("Reconnection failed, connection has been disposed, the disconnection event needs to be raised");
                Self.Tell(new DisconnectedMessage {
                    IDConnection = StreamConnection?.GetHashCode()
                });

                return;
            }

            Logger.LogInformation("Veryfing that connection is open ? {0}", testConnection.IsOpen);

            if (testConnection.IsOpen)
            {
                Context.System.ActorSelection(SdkActorSystem.EchoControllerActorPath).Tell(new ResetAllEchoesMessage());
                Logger.LogInformation("Reconnection successful, disconnection event will not be raised");

                Self.Tell(new ValidationSucceededMessage());
            }
            else
            {
                Logger.LogWarning("Connection validation failed, connection is not open - calling CloseConnection() to dispose it");
                Self.Tell(new DisconnectedMessage {
                    IDConnection = StreamConnection?.GetHashCode()
                });
            }
        }
コード例 #32
0
        private static async Task Main(string[] args)
        {
            // run for 90 seconds
            var timeout = TimeSpan.FromSeconds(90);

            // in reality this will likely never be reached, but it is useful to guard against
            // conditions where the queue isn't drained, or not drained fast enough.
            const int maxNonKeyEventRetention = 128;

            var source = new CancellationTokenSource(timeout);
            var token  = source.Token;
            var handle = Kernel32.GetStdHandle(Kernel32.StdHandleType.STD_INPUT_HANDLE);

            if (!Kernel32.GetConsoleMode(handle, out Kernel32.CONSOLE_INPUT_MODE mode))
            {
                throw NativeMethods.GetExceptionForWin32Error(Marshal.GetLastWin32Error());
            }

            mode |= Kernel32.CONSOLE_INPUT_MODE.ENABLE_WINDOW_INPUT;
            mode |= Kernel32.CONSOLE_INPUT_MODE.ENABLE_VIRTUAL_TERMINAL_INPUT;
            mode &= ~Kernel32.CONSOLE_INPUT_MODE.ENABLE_ECHO_INPUT;
            mode &= ~Kernel32.CONSOLE_INPUT_MODE.ENABLE_LINE_INPUT;

            if (!Kernel32.SetConsoleMode(handle, mode))
            {
                throw NativeMethods.GetExceptionForLastWin32Error();
            }

            // base our provider/consumer on a bounded queue to keep memory usage under control
            var events = new BlockingCollection <Kernel32.INPUT_RECORD>(
                new ConcurrentBoundedQueue <Kernel32.INPUT_RECORD>(maxNonKeyEventRetention));

            // Task that will consume non-key events asynchronously
            var consumeEvents = Task.Run(() =>
            {
                Console.WriteLine("consumeEvents started");

                try
                {
                    while (!events.IsCompleted)
                    {
                        // blocking call
                        var record = events.Take(token);

                        Console.WriteLine("record: {0}",
                                          Enum.GetName(typeof(Kernel32.EVENT_TYPE), record.EventType));
                    }
                }
                catch (OperationCanceledException)
                {
                    // timeout
                }

                Console.WriteLine("consumeEvents ended");
            }, token);

            // Task that will watch for key events while feeding non-key events into our provider/consumer collection
            var readInputAndProduceEvents = Task.Run(async() =>
            {
                //So, this is the key point - we cannot use the following or we lose all non-key events:
                // Stream stdin = Console.OpenStandardInput();

                // get a unicode character stream over console input
                Stream stdin = new ReadConsoleInputStream(handle, events);

                // wrap in a System.IO.Pipelines.PipeReader to get clean async and span/memory usage
                var reader = StreamConnection.GetReader(stdin);

                while (!token.IsCancellationRequested)
                {
                    // blocking call
                    var result = await reader.ReadAsync(token);

                    if (result.IsCanceled)
                    {
                        break;
                    }

                    var sequence = result.Buffer;
                    var segment  = sequence.Start;

                    while (sequence.TryGet(ref segment, out var mem))
                    {
                        // decode back from unicode
                        var datum = Encoding.Unicode.GetString(mem.Span);
                        Console.Write(datum);
                    }

                    reader.AdvanceTo(sequence.End);
                }
            }, token);

            Console.WriteLine("Running");

            try
            {
                await Task.WhenAll(consumeEvents, readInputAndProduceEvents);
            }
            catch (OperationCanceledException)
            {
                // timeout
            }

            Console.WriteLine("press any key...");
            Console.ReadKey(true);
        }