public async Task ShouldLogDisconnectAndRecover()
        {
            var log = new MemoryLog();
            var clientDisconnected = 0;
            var clientConnected    = 0;
            var serverConnected    = 0;

            var config = new ConnectionConfiguration(
                onDisconnected: (e, exception) => {
                Interlocked.Increment(ref clientDisconnected);
            },
                onConnected: (e, attempt, elapsed) => {
                Interlocked.Increment(ref clientConnected);
            });

            var endpoint = TestConfig.ServerEndpoint();

            using (var server = new TcpServer(endpoint.Ip.Port, TestConfig.Log)
            {
                OnConnected = () => Interlocked.Increment(ref serverConnected)
            })
                using (new Connection(endpoint, config, log: log))
                {
                    for (var connectionAttempt = 1; connectionAttempt <= 4; connectionAttempt++)
                    {
                        var currentAttempt = connectionAttempt;
                        await AssertAsync.ThatEventually(() => serverConnected == currentAttempt, () => $"server {serverConnected}, attempt {currentAttempt}");

                        await server.SendDataAsync(new ArraySegment <byte>(CreateCorrelationMessage(connectionAttempt)));

                        TestConfig.Log.Write(LogLevel.Info, () => LogEvent.Create($"Sent CONNECTION attempt {currentAttempt}"));
                        await AssertAsync.ThatEventually(() => clientConnected == currentAttempt, TimeSpan.FromMilliseconds(200), () => $"client {clientConnected}, attempt {currentAttempt}");

                        await AssertAsync.ThatEventually(() => log.LogEvents.Count(e => e.Item1 == LogLevel.Info && e.Item2.Message.StartsWith("Received 4 bytes (id ")) == currentAttempt, () => $"attempt {currentAttempt}\n" + log.ToString(LogLevel.Info));

                        TestConfig.Log.Write(LogLevel.Info, () => LogEvent.Create($"Dropping CONNECTION attempt {currentAttempt}"));
                        server.DropConnection();
                        await AssertAsync.ThatEventually(() => clientDisconnected == currentAttempt, () => $"client {clientDisconnected}, attempt {currentAttempt}");

                        Assert.That(log.LogEvents.Count(e => e.Item1 == LogLevel.Info && e.Item2.Message.StartsWith("Disposing transport to")), Is.AtLeast(currentAttempt));
                    }
                }
        }
        public async Task ShouldReconnectAfterLosingConnectionAndBeAbleToStartNewRead()
        {
            var log      = TestConfig.Log;
            var endpoint = TestConfig.ServerEndpoint();

            using (var server = new TcpServer(endpoint.Ip.Port, TestConfig.Log)) {
                var serverDisconnects = 0;
                var serverConnects    = 0;
                var clientDisconnects = 0;
                var clientReads       = 0;
                var clientBytesRead   = 0;

                server.OnConnected    = () => Interlocked.Increment(ref serverConnects);
                server.OnDisconnected = () => Interlocked.Increment(ref serverDisconnects);
                var config = new ConnectionConfiguration(
                    onDisconnected: (e, exception) => Interlocked.Increment(ref clientDisconnects),
                    onReading: (e, available) => Interlocked.Increment(ref clientReads),
                    onRead: (e, read, elapsed) => Interlocked.Add(ref clientBytesRead, read));
                using (var conn = new Connection(endpoint, config, log)) {
                    await AssertAsync.ThatEventually(() => serverConnects > 0, () => $"connects {serverConnects}");

                    await AssertAsync.ThatEventually(() => clientReads > 0, TimeSpan.FromSeconds(1), () => $"reads {clientReads}");

                    server.DropConnection();

                    await AssertAsync.ThatEventually(() => clientDisconnects > 0, TimeSpan.FromSeconds(10), () => $"disconnects {clientDisconnects}");

                    Assert.That(clientBytesRead, Is.EqualTo(0), "client should not have received any bytes.");

                    await AssertAsync.ThatEventually(() => serverConnects == 2, TimeSpan.FromSeconds(6), () => $"connects {serverConnects}");

                    await server.SendDataAsync(new ArraySegment <byte>(8.ToBytes()));

                    await server.SendDataAsync(new ArraySegment <byte>(99.ToBytes()));

                    await AssertAsync.ThatEventually(() => clientBytesRead == 8, TimeSpan.FromSeconds(1), () => $"bytes read {clientBytesRead}");
                }
            }
        }
        public async Task ShouldNotFinishPartiallyReadMessage()
        {
            var log       = new MemoryLog();
            var bytesRead = 0;

            var config = new ConnectionConfiguration(onReadBytes: (e, attempted, actual, elapsed) => Interlocked.Add(ref bytesRead, actual));

            var endpoint = TestConfig.ServerEndpoint();

            using (var server = new TcpServer(endpoint.Ip.Port, TestConfig.Log))
                using (new Connection(endpoint, config, log))
                {
                    // send size
                    var size = 200;
                    await server.SendDataAsync(new ArraySegment <byte>(size.ToBytes()));

                    var bytes = new byte[99];
                    new Random(42).NextBytes(bytes);
                    var offset        = 0;
                    var correlationId = 200;
                    foreach (var b in correlationId.ToBytes())
                    {
                        bytes[offset++] = b;
                    }

                    // send half of payload
                    await server.SendDataAsync(new ArraySegment <byte>(bytes, 0, 99));

                    await AssertAsync.ThatEventually(() => bytesRead >= bytes.Length, () => $"read {bytesRead}, length {bytes.Length}");

                    Assert.That(log.LogEvents.Count(e => e.Item1 == LogLevel.Warn && e.Item2.Message.StartsWith($"Unexpected response (id {correlationId}, {size}? bytes) from")), Is.EqualTo(1), log.ToString());
                    Assert.That(log.LogEvents.Count(e => e.Item1 == LogLevel.Debug && e.Item2.Message.StartsWith($"Received {size} bytes (id {correlationId})")), Is.EqualTo(0));

                    server.DropConnection();

                    await AssertAsync.ThatEventually(() => log.LogEvents.Count(e => e.Item1 == LogLevel.Warn && e.Item2.Message.StartsWith("Socket has been re-established, so recovery of the remaining of the current message is uncertain at best")) == 1, log.ToString);
                }
        }