예제 #1
0
        public void ReconnectAfterHttpError()
        {
            HttpStatusCode error1 = HttpStatusCode.BadRequest, error2 = HttpStatusCode.InternalServerError;
            var            message = new MessageEvent("put", "hello", _uri);

            var handler = Handlers.Sequential(
                Handlers.Status((int)error1),
                Handlers.Status((int)error2),
                StartStream().Then(WriteEvent(message)).Then(LeaveStreamOpen())
                );

            WithServerAndEventSource(handler, c => c.InitialRetryDelay(TimeSpan.FromMilliseconds(20)), (server, es) =>
            {
                var eventSink = new EventSink(es, _testLogging);
                _             = Task.Run(es.StartAsync);

                var action1 = eventSink.ExpectAction();
                var ex1     = Assert.IsType <EventSourceServiceUnsuccessfulResponseException>(action1.Exception);
                Assert.Equal((int)error1, ex1.StatusCode);

                eventSink.ExpectActions(EventSink.ClosedAction());

                var action2 = eventSink.ExpectAction();
                var ex2     = Assert.IsType <EventSourceServiceUnsuccessfulResponseException>(action2.Exception);
                Assert.Equal((int)error2, ex2.StatusCode);

                eventSink.ExpectActions(
                    EventSink.ClosedAction(),
                    EventSink.OpenedAction(),
                    EventSink.MessageReceivedAction(message)
                    );
            });
        }
        public void ReconnectAfterHttpError()
        {
            HttpStatusCode error1 = HttpStatusCode.BadRequest, error2 = HttpStatusCode.InternalServerError;
            var            message = new MessageEvent("put", "hello", _uri);

            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.WithStatus(error1));
            handler.QueueResponse(StubResponse.WithStatus(error2));
            handler.QueueResponse(StubResponse.StartStream(StreamAction.Write(message)));

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

                var action1 = eventSink.ExpectAction();
                var ex1     = Assert.IsType <EventSourceServiceUnsuccessfulResponseException>(action1.Exception);
                Assert.Equal((int)error1, ex1.StatusCode);

                eventSink.ExpectActions(EventSink.ClosedAction());

                var action2 = eventSink.ExpectAction();
                var ex2     = Assert.IsType <EventSourceServiceUnsuccessfulResponseException>(action2.Exception);
                Assert.Equal((int)error2, ex2.StatusCode);

                eventSink.ExpectActions(
                    EventSink.ClosedAction(),
                    EventSink.OpenedAction(),
                    EventSink.MessageReceivedAction(message)
                    );
            }
        }
        public void ReadTimeoutIsDetected(bool utf8Mode)
        {
            TimeSpan readTimeout   = TimeSpan.FromMilliseconds(200);
            var      streamHandler = Handlers.StartChunks("text/event-stream")
                                     .Then(Handlers.WriteChunkString("data: event1\n\ndata: e"))
                                     .Then(Handlers.Delay(readTimeout + readTimeout))
                                     .Then(Handlers.WriteChunkString("vent2\n\n"));

            using (var server = HttpServer.Start(streamHandler))
            {
                var config = Configuration.Builder(server.Uri)
                             .LogAdapter(_testLogging)
                             .ReadTimeout(readTimeout)
                             .PreferDataAsUtf8Bytes(utf8Mode)
                             .Build();
                using (var es = new EventSource(config))
                {
                    var sink = new EventSink(es)
                    {
                        Output = _testLogger.Debug
                    };
                    _ = es.StartAsync();
                    sink.ExpectActions(
                        EventSink.OpenedAction(),
                        EventSink.MessageReceivedAction(new MessageEvent(MessageEvent.DefaultName, "event1", server.Uri)),
                        EventSink.ErrorAction(new ReadTimeoutException()),
                        EventSink.ClosedAction()
                        );
                }
            }
        }
        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);
            }
        }
예제 #5
0
        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 handler = Handlers.Sequential(
                Enumerable.Range(0, nAttempts + 1).Select(_ =>
                                                          StartStream().Then(WriteEvent(anEvent)).Then(LeaveStreamOpen())
                                                          ).ToArray());

            var backoffs = new List <TimeSpan>();

            using (var server = HttpServer.Start(handler))
            {
                using (var es = MakeEventSource(server.Uri, config => config.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);
            }
        }
예제 #6
0
        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 = StartStream().Then(async ctx =>
            {
                foreach (var s in chunks)
                {
                    await Handlers.WriteChunkString(s)(ctx);
                }
            }).Then(LeaveStreamOpen());

            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)));
            }

            WithServerAndStartedEventSource(handler, (_, eventSink) =>
            {
                eventSink.ExpectActions(expectedActions.ToArray());
            });
        }
        public void EventReceivedLogMessage()
        {
            WithServerAndEventSource(HandlerWithBasicEvent(), (server, es) =>
            {
                var eventSink = new EventSink(es, _testLogging);
                _             = Task.Run(es.StartAsync);

                eventSink.ExpectActions(
                    EventSink.OpenedAction(),
                    EventSink.MessageReceivedAction(BasicEvent)
                    );

                Assert.True(_logCapture.HasMessageWithText(LogLevel.Debug,
                                                           string.Format(@"Received event ""{0}""", BasicEvent.Name)));
            });
        }
예제 #8
0
        public void ReceiveEventWithOnlyData()
        {
            var eventData = "this is a test message";
            var sse       = "data: " + eventData + "\n\n";

            var handler = StartStream().Then(Handlers.WriteChunkString(sse))
                          .Then(LeaveStreamOpen());

            WithServerAndStartedEventSource(handler, (_, eventSink) =>
            {
                eventSink.ExpectActions(
                    EventSink.OpenedAction(),
                    EventSink.MessageReceivedAction(new MessageEvent(MessageEvent.DefaultName, eventData, _uri))
                    );
            });
        }
        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());
            }
        }
예제 #11
0
        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 = StartStream().Then(Handlers.WriteChunkString(sse))
                          .Then(LeaveStreamOpen());

            WithServerAndStartedEventSource(handler, (_, eventSink) =>
            {
                eventSink.ExpectActions(
                    EventSink.OpenedAction(),
                    EventSink.MessageReceivedAction(new MessageEvent(eventName, eventData, eventId, _uri))
                    );
            });
        }
        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 void EventReceivedLogMessage()
        {
            var config = new ConfigurationBuilder(_uri)
                         .HttpMessageHandler(HandlerWithBasicEvent())
                         .LogAdapter(_logCapture)
                         .Build();

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

                eventSink.ExpectActions(
                    EventSink.OpenedAction(),
                    EventSink.MessageReceivedAction(BasicEvent)
                    );

                Assert.True(_logCapture.HasMessageWithText(LogLevel.Debug,
                                                           string.Format(@"Received event ""{0}""", BasicEvent.Name)));
            }
        }
        public void ReadTimeoutIsDetected(bool utf8Mode)
        {
            TimeSpan readTimeout = TimeSpan.FromMilliseconds(200);

            IEnumerable <string> DoChunks()
            {
                yield return("");

                yield return("data: event1\n\ndata: e");

                Thread.Sleep(readTimeout + readTimeout);
                yield return("vent2\n\n");
            }

            using (var server = StartWebServerOnAvailablePort(out var uri, RespondWithChunks("text/event-stream", DoChunks)))
            {
                var config = Configuration.Builder(uri)
                             .LogAdapter(_testLogging)
                             .ReadTimeout(readTimeout)
                             .PreferDataAsUtf8Bytes(utf8Mode)
                             .Build();
                using (var es = new EventSource(config))
                {
                    var sink = new EventSink(es)
                    {
                        Output = _testLogger.Debug
                    };
                    _ = es.StartAsync();
                    sink.ExpectActions(
                        EventSink.OpenedAction(),
                        EventSink.MessageReceivedAction(new MessageEvent(MessageEvent.DefaultName, "event1", uri)),
                        EventSink.ErrorAction(new ReadTimeoutException()),
                        EventSink.ClosedAction()
                        );
                }
            }
        }
        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>();

            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"));
            var allEventsReceived = new EventWaitHandle(false, EventResetMode.ManualReset);

            IEnumerable <string> DoChunks()
            {
                var i = 0;

                for (var pos = 0; ;)
                {
                    int chunkSize = i % 20 + 1;
                    if (pos + chunkSize >= allBody.Length)
                    {
                        yield return(allBody.Substring(pos));

                        break;
                    }
                    yield return(allBody.Substring(pos, chunkSize));

                    pos += chunkSize;
                    i++;
                }
                allEventsReceived.WaitOne();
            }

            try
            {
                using (var server = StartWebServerOnAvailablePort(out var uri,
                                                                  RespondWithChunks("text/event-stream", DoChunks)))
                {
                    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)));
                    }

                    var config = Configuration.Builder(uri).LogAdapter(_testLogging).Build();
                    using (var es = new EventSource(config))
                    {
                        var sink = new EventSink(es);
                        _ = es.StartAsync();
                        sink.ExpectActions(expectedActions.ToArray());
                    }
                }
            }
            finally
            {
                allEventsReceived.Set();
            }
        }
        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>();

            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"));
            var allEventsReceived = new TaskCompletionSource <bool>();

            IEnumerable <string> MakeChunks()
            {
                var i = 0;

                for (var pos = 0; ;)
                {
                    int chunkSize = i % 20 + 1;
                    if (pos + chunkSize >= allBody.Length)
                    {
                        yield return(allBody.Substring(pos));

                        break;
                    }
                    yield return(allBody.Substring(pos, chunkSize));

                    pos += chunkSize;
                    i++;
                }
            }

            try
            {
                Handler streamHandler = Handlers.StartChunks("text/event-stream")
                                        .Then(async ctx =>
                {
                    foreach (var s in MakeChunks())
                    {
                        await Handlers.WriteChunkString(s)(ctx);
                    }
                    await allEventsReceived.Task;
                });
                using (var server = HttpServer.Start(streamHandler))
                {
                    var expectedActions = new List <EventSink.Action>();
                    expectedActions.Add(EventSink.OpenedAction());
                    foreach (var data in eventData)
                    {
                        expectedActions.Add(EventSink.MessageReceivedAction(new MessageEvent(MessageEvent.DefaultName, data, server.Uri)));
                    }

                    var config = Configuration.Builder(server.Uri).LogAdapter(_testLogging).Build();
                    using (var es = new EventSource(config))
                    {
                        var sink = new EventSink(es);
                        _ = es.StartAsync();
                        sink.ExpectActions(expectedActions.ToArray());
                    }
                }
            }
            finally
            {
                allEventsReceived.SetResult(true);
            }
        }