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