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 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 DetectReadTimeout() { TimeSpan readTimeout = TimeSpan.FromMilliseconds(300); TimeSpan timeToWait = readTimeout + readTimeout; var handler = new StubMessageHandler(); handler.QueueResponse(StubResponse.StartStream( StreamAction.Write(":comment1\n"), StreamAction.Write(":comment2\n"), StreamAction.Write(":comment3\n").AfterDelay(timeToWait)) ); handler.QueueResponse(StubResponse.StartStream()); using (var es = StartEventSource(handler, out var eventSink, config => config.ReadTimeout(readTimeout))) { eventSink.ExpectActions( EventSink.OpenedAction(), EventSink.CommentReceivedAction(":comment1"), EventSink.CommentReceivedAction(":comment2"), EventSink.ErrorAction(new ReadTimeoutException()), EventSink.ClosedAction() ); } }
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 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); } }
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 void CanSpecifyLoggerInstance() { WithServerAndEventSource(HandlerWithBasicEvent(), c => c.Logger(_logCapture.Logger("special")), (server, es) => { var eventSink = new EventSink(es); _ = Task.Run(es.StartAsync); eventSink.ExpectActions(EventSink.OpenedAction()); Assert.NotEmpty(_logCapture.GetMessages()); Assert.True(_logCapture.GetMessages().All(m => m.LoggerName == "special"), _logCapture.ToString()); }); }
public void ConnectingLogMessage() { WithServerAndEventSource(HandlerWithBasicEvent(), (server, es) => { var eventSink = new EventSink(es); _ = Task.Run(es.StartAsync); eventSink.ExpectActions(EventSink.OpenedAction()); Assert.True(_logCapture.HasMessageWithText(LogLevel.Debug, "Making GET request to EventSource URI " + server.Uri), _logCapture.ToString()); }); }
public void UsesDefaultLoggerNameWhenLogAdapterIsSpecified() { WithServerAndEventSource(HandlerWithBasicEvent(), (server, es) => { var eventSink = new EventSink(es); _ = Task.Run(es.StartAsync); eventSink.ExpectActions(EventSink.OpenedAction()); Assert.NotEmpty(_logCapture.GetMessages()); Assert.True(_logCapture.GetMessages().All(m => m.LoggerName == Configuration.DefaultLoggerName), _logCapture.ToString()); }); }
public void ReceiveComment() { var commentSent = ": hello"; var handler = StartStream().Then(Handlers.WriteChunkString(commentSent + "\n")) .Then(LeaveStreamOpen()); WithServerAndStartedEventSource(handler, (_, eventSink) => { eventSink.ExpectActions( EventSink.OpenedAction(), EventSink.CommentReceivedAction(commentSent) ); }); }
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))); }); }
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) );; } }
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()); } }
public void CanSpecifyLoggerInstance() { var config = new ConfigurationBuilder(_uri) .HttpMessageHandler(HandlerWithBasicEvent()) .Logger(_logCapture.Logger("special")) .Build(); using (var es = new EventSource(config)) { var eventSink = new EventSink(es); _ = Task.Run(es.StartAsync); eventSink.ExpectActions(EventSink.OpenedAction()); Assert.NotEmpty(_logCapture.GetMessages()); Assert.True(_logCapture.GetMessages().All(m => m.LoggerName == "special"), _logCapture.ToString()); } }
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 UsesDefaultLoggerNameWhenLogAdapterIsSpecified() { var config = new ConfigurationBuilder(_uri) .HttpMessageHandler(HandlerWithBasicEvent()) .LogAdapter(_logCapture) .Build(); using (var es = new EventSource(config)) { var eventSink = new EventSink(es); _ = Task.Run(es.StartAsync); eventSink.ExpectActions(EventSink.OpenedAction()); Assert.NotEmpty(_logCapture.GetMessages()); Assert.True(_logCapture.GetMessages().All(m => m.LoggerName == Configuration.DefaultLoggerName), _logCapture.ToString()); } }
public void ConnectingLogMessage() { var config = new ConfigurationBuilder(_uri) .HttpMessageHandler(HandlerWithBasicEvent()) .LogAdapter(_logCapture) .Build(); using (var es = new EventSource(config)) { var eventSink = new EventSink(es); _ = Task.Run(es.StartAsync); eventSink.ExpectActions(EventSink.OpenedAction()); Assert.True(_logCapture.HasMessageWithText(LogLevel.Debug, "Making GET request to EventSource URI " + _uri), _logCapture.ToString()); } }
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 DetectReadTimeout() { TimeSpan readTimeout = TimeSpan.FromMilliseconds(300); TimeSpan timeToWait = readTimeout + readTimeout; var handler = StartStream() .Then(Handlers.WriteChunkString(":comment1\n")) .Then(Handlers.WriteChunkString(":comment2\n")) .Then(Handlers.Delay(timeToWait)) .Then(Handlers.WriteChunkString(":comment3\n")); WithServerAndStartedEventSource(handler, config => config.ReadTimeout(readTimeout), (_, eventSink) => { eventSink.ExpectActions( EventSink.OpenedAction(), EventSink.CommentReceivedAction(":comment1"), EventSink.CommentReceivedAction(":comment2"), EventSink.ErrorAction(new ReadTimeoutException()), EventSink.ClosedAction() ); }); }
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 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); } }
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(); } }