public override void Stop()
 {
     EventSource.OnOpen    -= OnEventSourceOpen;
     EventSource.OnMessage -= OnEventSourceMessage;
     EventSource.OnError   -= OnEventSourceError;
     EventSource.OnClosed  -= OnEventSourceClosed;
     EventSource.Close();
     EventSource = null;
 }
示例#2
0
        public async Task When_server_returns_HTTP_error_a_reconnect_attempt_is_made()
        {
            var messageData = "hello";

            var handler = new StubMessageHandler();

            handler.QueueResponse(new HttpResponseMessage(HttpStatusCode.Unauthorized));
            handler.QueueStringResponse("event: put\ndata: " + messageData + "\n\n");

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

            var receiver = new ErrorReceiver(evt);

            string messageReceived = null;

            evt.MessageReceived += (_, e) =>
            {
                messageReceived = e.Message.Data;
                evt.Close();
            };

            await evt.StartAsync();

            Assert.Equal(2, handler.GetRequests().Count());
            Assert.NotNull(receiver.ErrorReceived);
            var ex = Assert.IsType <EventSourceServiceUnsuccessfulResponseException>(receiver.ErrorReceived);

            Assert.Equal((int)HttpStatusCode.Unauthorized, ex.StatusCode);
            Assert.Equal(messageData, messageReceived);
        }
示例#3
0
        public async Task When_a_data_only_message_SSE_is_received_then_a_message_event_is_raised()
        {
            // Arrange
            var sse     = "data: this is a test message\n\n";
            var sseData = "this is a test message";

            var handler = new StubMessageHandler();

            handler.QueueStringResponse(sse);

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

            MessageEvent message = null;
            var          wasMessageReceivedEventRaised = false;

            evt.MessageReceived += (_, e) =>
            {
                message = e.Message;
                wasMessageReceivedEventRaised = true;

                evt.Close();
            };

            //// Act
            await evt.StartAsync();

            //// Assert
            Assert.Equal(sseData, message?.Data);
            Assert.True(wasMessageReceivedEventRaised);
        }
示例#4
0
        public async Task When_LastEventId_is_configured_then_the_outgoing_request_contains_Last_Event_Id_header()
        {
            // Arrange
            var sse         = ":";
            var lastEventId = "10";

            var handler = new StubMessageHandler();

            handler.QueueStringResponse(sse);

            var config = new Configuration(
                uri: _uri,
                messageHandler: handler,
                readTimeout: _defaultReadTimeout,
                lastEventId: lastEventId);

            var evt = new EventSource(config);

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

            //// Act
            await evt.StartAsync();

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

            IEnumerable <string> headerValues;
            var lastEventHeaderExists = request.Headers.TryGetValues(Constants.LastEventIdHttpHeader, out headerValues);

            //// Assert
            Assert.True(lastEventHeaderExists);
            Assert.Equal(lastEventId, headerValues.First());
        }
        public async Task When_server_returns_HTTP_error_a_reconnect_attempt_is_made()
        {
            var messageData = "hello";

            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.WithStatus(HttpStatusCode.Unauthorized));
            handler.QueueResponse(StubResponse.StartStream(
                                      StreamAction.Write("event: put\ndata: " + messageData + "\n\n")));

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

            var errorReceiver = new ErrorReceiver();

            evt.Error += errorReceiver;

            var messageReceiver = new MessageReceiver();

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

            await evt.StartAsync();

            Assert.Equal(2, handler.GetRequests().Count());
            Assert.NotNull(errorReceiver.ErrorReceived);
            var ex = Assert.IsType <EventSourceServiceUnsuccessfulResponseException>(errorReceiver.ErrorReceived);

            Assert.Equal((int)HttpStatusCode.Unauthorized, ex.StatusCode);
            Assert.Equal(messageData, messageReceiver.RequireSingleEvent().Message.Data);
        }
示例#6
0
        public async Task When_an_event_message_SSE_is_received_then_a_message_event_is_raised()
        {
            // Arrange
            var sse = "event: put\ndata: this is a test message\n\n";

            var handler = new StubMessageHandler();

            handler.QueueStringResponse(sse);

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

            var wasMessageReceivedEventRaised = false;
            var eventName = "message";

            evt.MessageReceived += (_, e) =>
            {
                eventName = e.EventName;
                wasMessageReceivedEventRaised = true;

                evt.Close();
            };

            //// Act
            await evt.StartAsync();

            //// Assert
            Assert.Equal("put", eventName);
            Assert.True(wasMessageReceivedEventRaised);
        }
        public async Task When_response_exceeds_read_timeout_then_read_timeout_exception_occurs()
        {
            TimeSpan readTimeout = TimeSpan.FromSeconds(4);
            TimeSpan timeToWait  = readTimeout.Add(TimeSpan.FromSeconds(1));

            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 receiver = new ErrorReceiver();

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

            await evt.StartAsync();

            Assert.NotNull(receiver.ErrorReceived);
            Assert.Contains(receiver.ErrorReceived.Message, Resources.EventSourceService_Read_Timeout);
            Assert.Equal(ReadyState.Closed, receiver.SourceStateReceived);
            Assert.Equal(ReadyState.Shutdown, evt.ReadyState);
        }
        public async Task Given_bad_http_responses_then_retry_delay_durations_should_increase()
        {
            var handler = new StubMessageHandler();

            var nAttempts = 2;

            for (int i = 0; i < nAttempts; i++)
            {
                handler.QueueResponse(StubResponse.WithIOError());
            }
            handler.QueueResponse(StubResponse.StartStream());

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

            var backoffs = new List <TimeSpan>();

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

            await evt.StartAsync();

            Assert.NotEmpty(backoffs);
            Assert.NotEqual(backoffs[0], backoffs[1]);
            Assert.True(backoffs[1] > backoffs[0]);
        }
示例#9
0
        public async Task When_an_message_SSE_contains_id_is_received_then_last_event_id_is_set()
        {
            // Arrange
            var sse = "id:200\nevent: put\ndata: this is a test message\n\n";

            var handler = new StubMessageHandler();

            handler.QueueStringResponse(sse);

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

            MessageEvent message = null;
            var          wasMessageReceivedEventRaised = false;

            evt.MessageReceived += (_, e) =>
            {
                message = e.Message;
                wasMessageReceivedEventRaised = true;

                evt.Close();
            };

            //// Act
            await evt.StartAsync();

            //// Assert
            Assert.Equal("200", message.LastEventId);
            Assert.True(wasMessageReceivedEventRaised);
        }
        public async Task When_Configuration_Request_headers_are_set_then_the_outgoing_request_contains_those_same_headers()
        {
            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.StartStream());

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

            var config = new ConfigurationBuilder(_uri).MessageHandler(handler)
                         .RequestHeaders(headers).Build();

            var evt = new EventSource(config);

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

            await evt.StartAsync();

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

            Assert.True(headers.All(
                            item =>
                            request.Headers.Contains(item.Key) &&
                            request.Headers.GetValues(item.Key).Contains(item.Value)
                            ));
        }
        public async Task Given_content_type_not_equal_to_eventstream_when_the_http_response_is_received_then_error_event_should_occur()
        {
            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));

            var config = new Configuration(_uri, handler);

            var evt      = new EventSource(config);
            var receiver = new ErrorReceiver();

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

            await evt.StartAsync();

            Assert.NotNull(receiver.ErrorReceived);
            Assert.Equal(ReadyState.Closed, receiver.SourceStateReceived);
            Assert.Equal(ReadyState.Shutdown, evt.ReadyState);
        }
示例#12
0
        public async Task When_the_HttpRequest_is_sent_then_the_outgoing_request_contains_accept_header()
        {
            // Arrange
            var sse = ":";

            var handler = new StubMessageHandler();

            handler.QueueStringResponse(sse);

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

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

            //// Act
            await evt.StartAsync();

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

            IEnumerable <string> headerValues;
            var acceptHeaderExists = request.Headers.TryGetValues(Constants.AcceptHttpHeader, out headerValues);

            //// Assert
            Assert.True(acceptHeaderExists);
            Assert.True(headerValues.Contains(Constants.EventStreamContentType));
        }
        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));
        }
示例#14
0
        public async Task ContextChange(string newHeader)
        {
            if (_featureHost.ServerEvaluation)
            {
                if (newHeader != _xFeatureHubHeader)
                {
                    _xFeatureHubHeader = newHeader;

                    if (_eventSource == null || _eventSource.ReadyState == ReadyState.Open || _eventSource.ReadyState == ReadyState.Connecting)
                    {
                        _eventSource?.Close();

                        var promise = new TaskCompletionSource <Readyness>();

                        EventHandler <Readyness> handler = (sender, r) =>
                        {
                            promise.TrySetResult(r);
                        };

                        _repository.ReadynessHandler += handler;

                        Init();

                        await promise.Task;

                        _repository.ReadynessHandler -= handler;
                    }
                }
            }
            else if (_eventSource == null)
            {
                Init();
            }
        }
示例#15
0
        public async Task When_Configuration_Request_headers_are_set_then_the_outgoing_request_contains_those_same_headers()
        {
            // Arrange
            var sse = ":";

            var handler = new StubMessageHandler();

            handler.QueueStringResponse(sse);

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

            var config = new Configuration(_uri, handler, requestHeaders: headers, readTimeout: _defaultReadTimeout);

            var evt = new EventSource(config);

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

            //// Act
            await evt.StartAsync();

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

            //// Assert
            Assert.True(headers.All(
                            item =>
                            request.Headers.Contains(item.Key) &&
                            request.Headers.GetValues(item.Key).Contains(item.Value)
                            ));
        }
示例#16
0
        public async Task When_a_comment_SSE_is_received_then_a_comment_event_is_raised()
        {
            // Arrange
            var commentSent = ":";

            var handler = new StubMessageHandler();

            handler.QueueStringResponse(commentSent);

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

            string commentReceived       = string.Empty;
            var    wasCommentEventRaised = false;

            evt.CommentReceived += (_, e) =>
            {
                commentReceived       = e.Comment;
                wasCommentEventRaised = true;

                evt.Close();
            };

            //// Act
            await evt.StartAsync();

            //// Assert
            Assert.Equal(commentSent, commentReceived);
            Assert.True(wasCommentEventRaised);
        }
示例#17
0
 public void Dispose()
 {
     _parents.Remove(this);
     if (_eventSource != null && _eventSource.ReadyState == ReadyState.Open)
     {
         _eventSource.Close();
     }
 }
示例#18
0
 public void HandleError(object sender, ExceptionEventArgs e)
 {
     ErrorReceived       = e.Exception;
     SourceStateReceived = _source.ReadyState;
     if (CloseEventSourceOnError)
     {
         _source.Close();
     }
 }
        public void Init()
        {
            var config = new Configuration(uri: new UriBuilder(_featureHost.Url).Uri,
                                           requestHeaders: _featureHost.ServerEvaluation ? BuildContextHeader() : null);

            _eventSource = new EventSource(config);

            // _eventSource.Closed += (sender, args) =>
            // {
            //   Console.WriteLine("source closed\n");
            // };

            _eventSource.MessageReceived += (sender, args) =>
            {
                SSEResultState?state;
                switch (args.EventName)
                {
                case "features":
                    state = SSEResultState.Features;
                    break;

                case "feature":
                    state = SSEResultState.Feature;
                    break;

                case "failure":
                    state = SSEResultState.Failure;
                    break;

                case "delete_feature":
                    state = SSEResultState.Deletefeature;
                    break;

                default:
                    state = null;
                    break;
                }

                // Console.WriteLine($"The state was {state} with value {args.Message.Data}\n");

                if (state == null)
                {
                    return;
                }

                _repository.Notify(state.Value, args.Message.Data);

                if (state == SSEResultState.Failure)
                {
                    _eventSource.Close();
                }
            };

            _eventSource.StartAsync();
        }
        public async Task CustomHttpClientIsNotClosedWhenEventSourceCloses()
        {
            var handler = new StubMessageHandler(StubResponse.WithStatus(HttpStatusCode.OK));

            using (var client = new HttpClient(handler))
            {
                var es = new EventSource(Configuration.Builder(_uri).HttpClient(client).Build());
                es.Close();

                await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, _uri));
            }
        }
        public async Task CustomHttpClientIsNotClosedWhenEventSourceCloses()
        {
            using (var server = HttpServer.Start(Handlers.Status(200)))
            {
                using (var client = new HttpClient())
                {
                    var es = new EventSource(Configuration.Builder(server.Uri).HttpClient(client).Build());
                    es.Close();

                    await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, server.Uri));
                }
            }
        }
示例#22
0
        public async Task Default_HTTP_method_is_get()
        {
            var handler = new StubMessageHandler();

            handler.QueueStringResponse(":");

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

            evt.CommentReceived += (_, e) => { evt.Close(); };
            await evt.StartAsync();

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

            Assert.Equal(HttpMethod.Get, request.Method);
        }
        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 async Task When_error_handler_closes_event_source_no_reconnect_attempt_is_made()
        {
            var handler = new StubMessageHandler();

            handler.QueueResponse(StubResponse.WithStatus(HttpStatusCode.Unauthorized));
            handler.QueueResponse(StubResponse.StartStream());

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

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

            await evt.StartAsync();

            Assert.Equal(1, handler.GetRequests().Count());
        }
示例#25
0
        public void Init(string url, FeatureHubRepository repository)
        {
            var config = new Configuration(uri: new UriBuilder(url).Uri);

            _eventSource = new EventSource(config);
            _eventSource.MessageReceived += (sender, args) =>
            {
                // Console.WriteLine($"{args.EventName}:\n\t {args.Message.Data}");

                SSEResultState?state;
                switch (args.EventName)
                {
                case "features":
                    state = SSEResultState.Features;
                    break;

                case "feature":
                    state = SSEResultState.Feature;
                    break;

                case "failure":
                    state = SSEResultState.Failure;
                    break;

                case "delete_feature":
                    state = SSEResultState.Deletefeature;
                    break;

                default:
                    state = null;
                    break;
                }

                if (state == null)
                {
                    return;
                }

                repository.Notify(state.Value, args.Message.Data);

                if (state == SSEResultState.Failure)
                {
                    _eventSource.Close();
                }
            };

            _eventSource.StartAsync();
        }
        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 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 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_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 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);
        }