public void RetryDelayDurationsShouldIncrease()
        {
            var handler = new StubMessageHandler();

            var nAttempts = 3;

            for (var i = 0; i < nAttempts; i++)
            {
                handler.QueueResponse(StubResponse.StartStream(
                                          StreamAction.Write(":hi\n"),
                                          StreamAction.CloseStream()));
            }
            handler.QueueResponse(StubResponse.StartStream());

            var backoffs = new List <TimeSpan>();

            using (var es = MakeEventSource(handler, builder => builder.InitialRetryDelay(TimeSpan.FromMilliseconds(100))))
            {
                _          = new EventSink(es, _testLogging);
                es.Closed += (_, state) =>
                {
                    backoffs.Add(es.BackOffDelay);
                };

                _ = Task.Run(es.StartAsync);

                for (int i = 0; i <= nAttempts; i++)
                {
                    _ = handler.AwaitRequest();
                }
            }

            AssertBackoffsAlwaysIncrease(backoffs, nAttempts);
        }
        public async Task Connection_closed_before_threshold_gets_increasing_backoff_delay()
        {
            TimeSpan  threshold  = TimeSpan.FromSeconds(1);
            TimeSpan  closeDelay = TimeSpan.FromMilliseconds(100);
            const int nAttempts  = 3;

            var handler = new StubMessageHandler();

            for (var i = 0; i < nAttempts; i++)
            {
                handler.QueueResponse(StubResponse.StartStream(
                                          StreamAction.CloseStream().AfterDelay(closeDelay)));
            }
            handler.QueueResponse(StubResponse.StartStream());

            var config = new ConfigurationBuilder(_uri).MessageHandler(handler)
                         .BackoffResetThreshold(threshold).Build();
            var evt = new EventSource(config);

            var requestTimes = new List <DateTime>();

            handler.RequestReceived += (_, r) =>
            {
                requestTimes.Add(DateTime.Now);
                if (requestTimes.Count > nAttempts)
                {
                    evt.Close();
                }
            };

            await evt.StartAsync();

            Assert.Equal(nAttempts + 1, handler.GetRequests().Count());

            for (var i = 0; i < nAttempts; i++)
            {
                var interval = requestTimes[i + 1] - requestTimes[i] - closeDelay;
                var min      = (i == 0) ? TimeSpan.Zero : GetMaxBackoffDelayForAttempt(config, i);
                var max      = GetMaxBackoffDelayForAttempt(config, i + 1);
                AssertTimeSpanInRange(interval, min, max);
            }
        }
        public async Task When_reconnecting_the_outgoing_request_overrides_Last_Event_Id_from_configuration()
        {
            var lastEventId    = "10";
            var firstResponse  = $"id:{lastEventId}\nevent: put\ndata: this is a test message\n\n";
            var secondResponse = $"id:20\nevent: put\ndata: this is a test message\n\n";

            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.StartStream(StreamAction.Write(firstResponse), StreamAction.CloseStream()));
            handler.QueueResponse(StubResponse.StartStream(StreamAction.Write(secondResponse), StreamAction.CloseStream()));

            var configuration = Configuration
                                .Builder(_uri)
                                .MessageHandler(handler)
                                .LastEventId("0")
                                .RequestHeader(Constants.LastEventIdHttpHeader, "0")
                                .Build();
            var evt   = new EventSource(configuration);
            var first = true;

            handler.RequestReceived += (s, r) =>
            {
                if (first)
                {
                    first = false;
                }
                else
                {
                    evt.Close();
                }
            };

            await evt.StartAsync();

            var requests = handler.GetRequests().ToList();

            Assert.True(requests[0].Headers.Contains(Constants.LastEventIdHttpHeader));
            Assert.True(requests[1].Headers.Contains(Constants.LastEventIdHttpHeader));
            var lastEventIdHeader = requests[1].Headers.GetValues(Constants.LastEventIdHttpHeader).ToArray();

            Assert.Equal(1, lastEventIdHeader.Length);
            Assert.Equal(lastEventId, lastEventIdHeader[0]);
        }
        public async Task When_reconnecting_the_outgoing_request_contains_Last_Event_Id_header()
        {
            var lastEventId    = "10";
            var firstResponse  = $"id:{lastEventId}\nevent: put\ndata: this is a test message\n\n";
            var secondResponse = $"id:20\nevent: put\ndata: this is a test message\n\n";

            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.StartStream(StreamAction.Write(firstResponse), StreamAction.CloseStream()));
            handler.QueueResponse(StubResponse.StartStream(StreamAction.Write(secondResponse), StreamAction.CloseStream()));

            var evt   = new EventSource(new Configuration(_uri, handler));
            var first = true;

            handler.RequestReceived += (s, r) =>
            {
                if (first)
                {
                    first = false;
                }
                else
                {
                    evt.Close();
                }
            };

            await evt.StartAsync();

            var requests = handler.GetRequests().ToList();

            Assert.False(requests[0].Headers.Contains(Constants.LastEventIdHttpHeader));
            Assert.True(requests[1].Headers.Contains(Constants.LastEventIdHttpHeader));
            Assert.True(requests[1].Headers.GetValues(Constants.LastEventIdHttpHeader).Contains(lastEventId));
        }
        public void SendMostRecentEventIdOnReconnect()
        {
            var initialEventId = "abc123";
            var eventId1       = "xyz456";
            var message1       = new MessageEvent("put", "this is a test message", eventId1, _uri);

            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.StartStream(StreamAction.Write(message1), StreamAction.CloseStream()));
            handler.QueueResponse(StubResponse.StartStream());

            using (var es = MakeEventSource(handler, builder => builder.InitialRetryDelay(TimeSpan.FromMilliseconds(20))
                                            .LastEventId(initialEventId)))
            {
                var eventSink = new EventSink(es, _testLogging);
                _ = Task.Run(es.StartAsync);

                var req1 = handler.AwaitRequest();
                Assert.Contains(initialEventId, req1.Headers.GetValues("Last-Event-Id"));

                var req2 = handler.AwaitRequest();
                Assert.Contains(eventId1, req2.Headers.GetValues("Last-Event-Id"));
            }
        }