public async Task Timeout_does_not_cause_unobserved_exception()
        {
            TimeSpan readTimeout = TimeSpan.FromMilliseconds(10);
            TimeSpan timeToWait  = TimeSpan.FromMilliseconds(100);

            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.StartStream(
                                      StreamAction.Write(":\n\n").AfterDelay(timeToWait)));
            handler.QueueResponse(StubResponse.StartStream());

            var config = new ConfigurationBuilder(_uri).MessageHandler(handler).ReadTimeout(readTimeout).Build();
            var evt    = new EventSource(config);

            var caughtUnobservedException = false;
            EventHandler <UnobservedTaskExceptionEventArgs> exceptionHandler = (object sender, UnobservedTaskExceptionEventArgs e) =>
            {
                e.SetObserved();
                caughtUnobservedException = true;
            };

            TaskScheduler.UnobservedTaskException += exceptionHandler;

            try
            {
                evt.Error += (_, e) => evt.Close();

                await evt.StartAsync();

                // StartAsync has returned, meaning that the EventSource was closed by the ErrorReceiver, meaning that it
                // encountered a timeout. Wait a little bit longer to make sure that the stream reader task has got an
                // exception from the closed stream.
                Thread.Sleep(TimeSpan.FromMilliseconds(300));

                // Force finalizer to run so that if there was an unobserved exception, it will trigger that event.
                GC.Collect();
                GC.WaitForPendingFinalizers();

                Assert.False(caughtUnobservedException);
            }
            finally
            {
                TaskScheduler.UnobservedTaskException -= exceptionHandler;
            }
        }
        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 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);
            }
        }
Exemplo n.º 4
0
        public async Task Given_bad_http_responses_then_retry_delay_durations_should_be_random()
        {
            // Arrange
            var handler = new StubMessageHandler();

            var nAttempts = 2;

            for (int i = 0; i < nAttempts; i++)
            {
                var response = new HttpResponseMessageWithError();

                response.StatusCode       = HttpStatusCode.OK;
                response.ShouldThrowError = true;
                response.Content          = new StringContent("Content " + i, System.Text.Encoding.UTF8,
                                                              "text/event-stream");

                handler.QueueResponse(response);
            }

            handler.QueueResponse(new HttpResponseMessage(HttpStatusCode.NoContent));

            var evt = new EventSource(new Configuration(_uri, handler, readTimeout: _defaultReadTimeout));

            var backoffs = new List <TimeSpan>();

            evt.Error += (_, e) =>
            {
                backoffs.Add(evt.BackOffDelay);
                if (backoffs.Count >= nAttempts)
                {
                    evt.Close();
                }
            };

            //Act
            await evt.StartAsync();

            //// Assert
            Assert.NotEmpty(backoffs);
            Assert.Equal(backoffs.Distinct().Count(), backoffs.Count());
        }
        public void CanRestartStream(bool resetBackoff)
        {
            // This test is in EventSourceStreamReadingTest rather than EventSourceReconnectingTest
            // because the important thing here is that the stream reading logic can be interrupted.
            int nAttempts    = 3;
            var initialDelay = TimeSpan.FromMilliseconds(50);

            var anEvent = new MessageEvent("put", "x", _uri);
            var stream  = StubResponse.StartStream(StreamAction.Write(anEvent));
            var handler = new StubMessageHandler();

            for (var i = 0; i <= nAttempts; i++)
            {
                handler.QueueResponse(stream);
            }

            var backoffs = new List <TimeSpan>();

            using (var es = MakeEventSource(handler, builder => builder.InitialRetryDelay(initialDelay)))
            {
                var sink = new EventSink(es, _testLogging);
                es.Closed += (_, ex) =>
                {
                    backoffs.Add(es.BackOffDelay);
                };
                _ = Task.Run(es.StartAsync);

                sink.ExpectActions(
                    EventSink.OpenedAction(),
                    EventSink.MessageReceivedAction(anEvent)
                    );

                for (var i = 0; i < nAttempts; i++)
                {
                    es.Restart(resetBackoff);

                    sink.ExpectActions(
                        EventSink.ClosedAction(),
                        EventSink.OpenedAction(),
                        EventSink.MessageReceivedAction(anEvent)
                        );
                }
            }

            if (resetBackoff)
            {
                Assert.All(backoffs, delay => Assert.InRange(delay, TimeSpan.Zero, initialDelay));
            }
            else
            {
                AssertBackoffsAlwaysIncrease(backoffs, nAttempts);
            }
        }
        public async Task Default_HTTP_method_is_get()
        {
            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.StartStream());

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

            handler.RequestReceived += (s, r) => evt.Close();
            await evt.StartAsync();

            var request = handler.GetRequests().First();

            Assert.Equal(HttpMethod.Get, request.Method);
        }
        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"));
            }
        }
        public void ReceiveComment()
        {
            var commentSent = ": hello";

            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.StartStream(StreamAction.Write(commentSent + "\n\n")));

            using (var es = StartEventSource(handler, out var eventSink))
            {
                eventSink.ExpectActions(
                    EventSink.OpenedAction(),
                    EventSink.CommentReceivedAction(commentSent)
                    );;
            }
        }
Exemplo n.º 9
0
        public async Task When_error_handler_closes_event_source_no_reconnect_attempt_is_made()
        {
            var handler = new StubMessageHandler();

            handler.QueueResponse(new HttpResponseMessage(HttpStatusCode.Unauthorized));

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

            var receiver = new ErrorReceiver(evt);

            receiver.CloseEventSourceOnError = true;

            await evt.StartAsync();

            Assert.Equal(1, handler.GetRequests().Count());
        }
        public async Task When_the_HttpRequest_is_sent_then_the_outgoing_request_contains_accept_header()
        {
            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.StartStream());

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

            handler.RequestReceived += (s, r) => evt.Close();

            await evt.StartAsync();

            var request = handler.GetRequests().First();

            Assert.True(request.Headers.Contains(Constants.AcceptHttpHeader));
            Assert.True(request.Headers.GetValues(Constants.AcceptHttpHeader).Contains(Constants.EventStreamContentType));
        }
        public async Task HTTP_method_can_be_specified()
        {
            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.StartStream());

            var config = new ConfigurationBuilder(_uri).MessageHandler(handler)
                         .Method(HttpMethod.Post).Build();
            var evt = new EventSource(config);

            handler.RequestReceived += (s, r) => evt.Close();
            await evt.StartAsync();

            var request = handler.GetRequests().First();

            Assert.Equal(HttpMethod.Post, request.Method);
        }
        public void ErrorForInvalidHttpStatus(HttpStatusCode statusCode)
        {
            var handler  = new StubMessageHandler();
            var response = new HttpResponseMessage(statusCode);

            handler.QueueResponse(StubResponse.WithResponse(response));

            using (var es = MakeEventSource(handler))
            {
                var eventSink = new EventSink(es, _testLogging);
                _ = Task.Run(es.StartAsync);

                var errorAction = eventSink.ExpectAction();
                var ex          = Assert.IsType <EventSourceServiceUnsuccessfulResponseException>(errorAction.Exception);
                Assert.Equal((int)statusCode, ex.StatusCode);
            }
        }
        public void ReceiveEventWithOnlyData()
        {
            var eventData = "this is a test message";
            var sse       = "data: " + eventData + "\n\n";

            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.StartStream(StreamAction.Write(sse)));

            using (var es = StartEventSource(handler, out var eventSink))
            {
                eventSink.ExpectActions(
                    EventSink.OpenedAction(),
                    EventSink.MessageReceivedAction(new MessageEvent(MessageEvent.DefaultName, eventData, _uri))
                    );
            }
        }
        public void ReceiveEventStreamInChunks()
        {
            // This simply verifies that chunked streaming works as expected and that events are being
            // parsed correctly regardless of how the chunks line up with the events.

            var eventData = new List <string>();
            var chunks    = new List <string>();

            for (var i = 0; i < 200; i++)
            {
                eventData.Add(string.Format("data{0}", i) + new string('x', i % 7));
            }
            var allBody = string.Concat(eventData.Select(data => "data:" + data + "\n\n"));

            for (var pos = 0; ;)
            {
                int i         = chunks.Count;
                int chunkSize = i % 20 + 1;
                if (pos + chunkSize >= allBody.Length)
                {
                    chunks.Add(allBody.Substring(pos));
                    break;
                }
                chunks.Add(allBody.Substring(pos, chunkSize));
                pos += chunkSize;
            }

            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.StartStream(
                                      chunks.Select(StreamAction.Write).ToArray()));

            var expectedActions = new List <EventSink.Action>();

            expectedActions.Add(EventSink.OpenedAction());
            foreach (var data in eventData)
            {
                expectedActions.Add(EventSink.MessageReceivedAction(new MessageEvent(MessageEvent.DefaultName, data, _uri)));
            }

            using (var es = StartEventSource(handler, out var eventSink))
            {
                eventSink.ExpectActions(expectedActions.ToArray());
            }
        }
        public async Task When_event_source_closes_do_not_dispose_configured_http_client()
        {
            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.WithResponse(new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent("Hello")
            }));

            var client = new HttpClient(handler);
            var evt    = new EventSource(new Configuration(_uri, httpClient: client));

            evt.Close();

            await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, _uri));

            client.Dispose();
        }
        public void ReceiveEventWithID()
        {
            var eventName = "test event";
            var eventData = "this is a test message";
            var eventId   = "123abc";
            var sse       = "event: " + eventName + "\ndata: " + eventData + "\nid: " + eventId + "\n\n";

            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.StartStream(StreamAction.Write(sse)));

            using (var es = StartEventSource(handler, out var eventSink))
            {
                eventSink.ExpectActions(
                    EventSink.OpenedAction(),
                    EventSink.MessageReceivedAction(new MessageEvent(eventName, eventData, eventId, _uri))
                    );
            }
        }
        public async Task When_an_event_message_SSE_is_received_then_a_message_event_is_raised()
        {
            var sse = "event: put\ndata: this is a test message\n\n";

            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.StartStream(StreamAction.Write(sse)));

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

            var m = new MessageReceiver();

            evt.MessageReceived += m;
            evt.MessageReceived += ((_, e) => evt.Close());

            await evt.StartAsync();

            Assert.Equal("put", m.RequireSingleEvent().EventName);
        }
        public async Task When_an_message_SSE_contains_id_is_received_then_last_event_id_is_set()
        {
            var sse = "id:200\nevent: put\ndata: this is a test message\n\n";

            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.StartStream(StreamAction.Write(sse)));

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

            var m = new MessageReceiver();

            evt.MessageReceived += m;
            evt.MessageReceived += ((_, e) => evt.Close());

            await evt.StartAsync();

            Assert.Equal("200", m.RequireSingleEvent().Message.LastEventId);
        }
        public async Task When_LastEventId_is_configured_then_the_outgoing_request_contains_Last_Event_Id_header()
        {
            var lastEventId = "10";

            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.StartStream());

            var config = new ConfigurationBuilder(_uri).MessageHandler(handler)
                         .LastEventId(lastEventId).Build();
            var evt = new EventSource(config);

            handler.RequestReceived += (s, r) => evt.Close();

            await evt.StartAsync();

            var request = handler.GetRequests().First();

            Assert.True(request.Headers.Contains(Constants.LastEventIdHttpHeader));
            Assert.True(request.Headers.GetValues(Constants.LastEventIdHttpHeader).Contains(lastEventId));
        }
Exemplo n.º 20
0
        public async Task Given_status_code_when_the_http_response_is_recieved_then_error_event_should_occur(HttpStatusCode statusCode)
        {
            // Arrange
            var handler = new StubMessageHandler();

            handler.QueueResponse(new HttpResponseMessage(statusCode));

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

            //Act
            var raisedEvent = await Assert.RaisesAsync <ExceptionEventArgs>(
                h => evt.Error += h,
                h => evt.Error -= h,
                () => evt.StartAsync());

            //// Assert
            Assert.NotNull(raisedEvent);
            Assert.Equal(evt, raisedEvent.Sender);
            Assert.IsType <EventSourceServiceCancelledException>(raisedEvent.Arguments.Exception);
            Assert.True(evt.ReadyState == ReadyState.Closed);
        }
        public void CustomRequestHeaders()
        {
            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.StartStream());

            var headers = new Dictionary <string, string> {
                { "User-Agent", "mozilla" }, { "Authorization", "testing" }
            };

            using (var es = MakeEventSource(handler, builder => builder.RequestHeaders(headers)))
            {
                _ = Task.Run(es.StartAsync);

                var req = handler.AwaitRequest();
                Assert.True(headers.All(
                                item =>
                                req.Headers.Contains(item.Key) &&
                                req.Headers.GetValues(item.Key).Contains(item.Value)
                                ));
            }
        }
        public async Task When_a_comment_SSE_is_received_then_a_comment_event_is_raised()
        {
            var commentSent = ": hello";

            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.StartStream(StreamAction.Write(commentSent + "\n\n")));

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

            string commentReceived = null;

            evt.CommentReceived += (_, e) =>
            {
                commentReceived = e.Comment;
                evt.Close();
            };

            await evt.StartAsync();

            Assert.Equal(commentSent, commentReceived);
        }
        public async Task Given_status_code_when_the_http_response_is_received_then_error_event_should_occur(HttpStatusCode statusCode)
        {
            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.WithStatus(statusCode));

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

            var receiver = new ErrorReceiver();

            evt.Error += receiver;
            evt.Error += (_, e) => evt.Close();

            await evt.StartAsync();

            Assert.NotNull(receiver.ErrorReceived);
            var ex = Assert.IsType <EventSourceServiceUnsuccessfulResponseException>(receiver.ErrorReceived);

            Assert.Equal((int)statusCode, ex.StatusCode);
            Assert.Equal(ReadyState.Closed, receiver.SourceStateReceived);
            Assert.Equal(ReadyState.Shutdown, evt.ReadyState);
        }
        public async Task When_response_does_not_exceed_read_timeout_then_expected_message_event_occurs()
        {
            var      sse         = "event: put\ndata: this is a test message\n\n";
            TimeSpan readTimeout = TimeSpan.FromSeconds(4);
            TimeSpan timeToWait  = readTimeout.Subtract(TimeSpan.FromSeconds(1));

            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.StartStream(
                                      StreamAction.Write(sse).AfterDelay(timeToWait)));

            var config = new ConfigurationBuilder(_uri).MessageHandler(handler).ReadTimeout(readTimeout).Build();
            var evt    = new EventSource(config);

            var receiver = new MessageReceiver();

            evt.MessageReceived += receiver;
            evt.MessageReceived += (_, e) => evt.Close();

            await evt.StartAsync();

            Assert.Equal("put", receiver.RequireSingleEvent().EventName);
        }
Exemplo n.º 25
0
        public void CanReceiveUtf8EventDataAsStrings(bool setExplicitEncoding)
        {
            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.StartStream(
                                      setExplicitEncoding ? Encoding.UTF8 : null,
                                      BasicStreamTestParams.StreamActions));

            var config = Configuration.Builder(_uri).HttpMessageHandler(handler)
                         .LogAdapter(_testLogging)
                         .Build();

            using (var es = new EventSource(config))
            {
                var eventSink = new EventSink(es, _testLogging)
                {
                    ExpectUtf8Data = false
                };

                _ = Task.Run(es.StartAsync);

                eventSink.ExpectActions(BasicStreamTestParams.ExpectedEventActions);
            }
        }
        public async Task HTTP_request_body_can_be_specified()
        {
            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.StartStream());

            HttpContent content = new StringContent("{}");

            Configuration.HttpContentFactory contentFn = () =>
            {
                return(content);
            };

            var config = new ConfigurationBuilder(_uri).MessageHandler(handler)
                         .Method(HttpMethod.Post).RequestBodyFactory(contentFn).Build();
            var evt = new EventSource(config);

            handler.RequestReceived += (s, r) => evt.Close();
            await evt.StartAsync();

            var request = handler.GetRequests().First();

            Assert.Equal(content, request.Content);
        }
Exemplo n.º 27
0
        public async Task Given_204_when_the_http_response_is_received_then_error_event_should_occur()
        {
            // Arrange
            var handler = new StubMessageHandler();

            handler.QueueResponse(new HttpResponseMessage(HttpStatusCode.NoContent));

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

            var receiver = new ErrorReceiver(evt);

            receiver.CloseEventSourceOnError = true;

            //Act
            await evt.StartAsync();

            //// Assert
            Assert.NotNull(receiver.ErrorReceived);
            var ex = Assert.IsType <EventSourceServiceUnsuccessfulResponseException>(receiver.ErrorReceived);

            Assert.Equal(204, ex.StatusCode);
            Assert.Equal(ReadyState.Closed, receiver.SourceStateReceived);
            Assert.Equal(ReadyState.Shutdown, evt.ReadyState);
        }
        public void ErrorForIncorrectContentType()
        {
            var handler = new StubMessageHandler();

            var response =
                new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent("testing", System.Text.Encoding.UTF8)
            };

            response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html");

            handler.QueueResponse(StubResponse.WithResponse(response));

            using (var es = MakeEventSource(handler))
            {
                var eventSink = new EventSink(es, _testLogging);
                _ = Task.Run(es.StartAsync);

                var errorAction = eventSink.ExpectAction();
                var ex          = Assert.IsType <EventSourceServiceCancelledException>(errorAction.Exception);
                Assert.Matches(".*content type.*text/html", ex.Message);
            }
        }