public void CloseThrowsSouldntTakeProcessDown()
        {
            var memoryStream = MemoryStream("");
            var eventSource = new EventSourceStreamReader(memoryStream);

            eventSource.Closed = (ex) =>
            {
                throw new Exception("Throw on closed");
            };

            eventSource.Start();

            Thread.Sleep(TimeSpan.FromSeconds(5));
        }
        public void ReadTriggersOpenedOnOpen()
        {
            var memoryStream = MemoryStream("data:somedata\n\n");
            var wh = new ManualResetEvent(false);
            var tcs = new TaskCompletionSource<string>();
            var eventSource = new EventSourceStreamReader(memoryStream);

            eventSource.Opened = () => wh.Set();
            eventSource.Message = sseEvent => tcs.TrySetResult(sseEvent.Data);

            eventSource.Start();
            Assert.True(wh.WaitOne(TimeSpan.FromSeconds(5)));
            Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(5)));
            Assert.Equal("somedata", tcs.Task.Result);
        }
        public void CloseThrowsSouldntTakeProcessDown()
        {
            var memoryStream = MemoryStream("");
            var eventSource = new EventSourceStreamReader(memoryStream);

            eventSource.Closed = (ex) =>
            {
                throw new Exception("Throw on closed");
            };

            eventSource.Start();

            // Force any finalizers to run so we can see unhandled task errors
            GC.Collect();
            GC.WaitForPendingFinalizers();

            Thread.Sleep(TimeSpan.FromSeconds(5));
        }
        private void OpenConnection(IConnection connection, string data, Action initializeCallback, Action<Exception> errorCallback)
        {
            // If we're reconnecting add /connect to the url
            bool reconnecting = initializeCallback == null;
            var callbackInvoker = new ThreadSafeInvoker();

            var url = (reconnecting ? connection.Url : connection.Url + "connect") + GetReceiveQueryString(connection, data);

            Action<IRequest> prepareRequest = PrepareRequest(connection);

            #if NET35
            Debug.WriteLine(String.Format(System.Globalization.CultureInfo.InvariantCulture, "SSE: GET {0}", (object)url));
            #else
            Debug.WriteLine("SSE: GET {0}", (object)url);
            #endif

            _httpClient.GetAsync(url, request =>
            {
                prepareRequest(request);

                request.Accept = "text/event-stream";

            }).ContinueWith(task =>
            {
                if (task.IsFaulted)
                {
                    Exception exception = task.Exception.Unwrap();
                    if (!ExceptionHelper.IsRequestAborted(exception))
                    {
                        if (errorCallback != null)
                        {
                            callbackInvoker.Invoke((cb, ex) => cb(ex), errorCallback, exception);
                        }
                        else if (reconnecting)
                        {
                            // Only raise the error event if we failed to reconnect
                            connection.OnError(exception);

                            Reconnect(connection, data);
                        }
                    }
                }
                else
                {
                    IResponse response = task.Result;
                    Stream stream = response.GetResponseStream();

                    var eventSource = new EventSourceStreamReader(stream);
                    bool retry = true;

            #if MONOTOUCH
                    lock(connection.Items)
                    {
            #endif
                        connection.Items[EventSourceKey] = eventSource;
            #if MONOTOUCH
                    }
            #endif

                    eventSource.Opened = () =>
                    {
                        if (initializeCallback != null)
                        {
                            callbackInvoker.Invoke(initializeCallback);
                        }

                        if (reconnecting && connection.ChangeState(ConnectionState.Reconnecting, ConnectionState.Connected))
                        {
                            // Raise the reconnect event if the connection comes back up
                            connection.OnReconnected();
                        }
                    };

                    eventSource.Message = sseEvent =>
                    {
                        if (sseEvent.Type == EventType.Data)
                        {
                            if (sseEvent.Data.Equals("initialized", StringComparison.OrdinalIgnoreCase))
                            {
                                return;
                            }

                            bool timedOut;
                            bool disconnected;
                            ProcessResponse(connection, sseEvent.Data, out timedOut, out disconnected);

                            if (disconnected)
                            {
                                retry = false;
                            }
                        }
                    };

                    eventSource.Closed = exception =>
                    {
                        if (exception != null && !ExceptionHelper.IsRequestAborted(exception))
                        {
                            // Don't raise exceptions if the request was aborted (connection was stopped).
                            connection.OnError(exception);
                        }

                        // See http://msdn.microsoft.com/en-us/library/system.net.httpwebresponse.close.aspx
                        response.Close();

                        if (retry)
                        {
                            Reconnect(connection, data);
                        }
                        else
                        {
                            connection.Stop();
                        }
                    };

                    eventSource.Start();
                }
            });

            if (errorCallback != null)
            {
                TaskAsyncHelper.Delay(ConnectionTimeout).Then(() =>
                {
                    callbackInvoker.Invoke((conn, cb) =>
                    {
                        // Stop the connection
                        Stop(conn);

                        // Connection timeout occured
                        cb(new TimeoutException());
                    },
                    connection,
                    errorCallback);
                });
            }
        }
        private void OpenConnection(IConnection connection, string data, Action initializeCallback, Action<Exception> errorCallback)
        {
            // If we're reconnecting add /connect to the url
            bool reconnecting = initializeCallback == null;

            var url = (reconnecting ? connection.Url : connection.Url + "connect") + GetReceiveQueryString(connection, data);

            Action<IRequest> prepareRequest = PrepareRequest(connection);

            #if NET35
            Debug.WriteLine(String.Format(System.Globalization.CultureInfo.InvariantCulture, "SSE: GET {0}", (object)url));
            #else
            Debug.WriteLine("SSE: GET {0}", (object)url);
            #endif

            _httpClient.GetAsync(url, request =>
            {
                prepareRequest(request);

                request.Accept = "text/event-stream";

            }).ContinueWith(task =>
            {
                if (task.IsFaulted)
                {
                    var exception = task.Exception.Unwrap();
                    if (!ExceptionHelper.IsRequestAborted(exception))
                    {
                        if (errorCallback != null &&
                            Interlocked.Exchange(ref _initializedCalled, 1) == 0)
                        {
                            errorCallback(exception);
                        }
                        else if (reconnecting)
                        {
                            // Only raise the error event if we failed to reconnect
                            connection.OnError(exception);
                        }
                    }

                    if (reconnecting && !CancellationToken.IsCancellationRequested)
                    {
                        connection.State = ConnectionState.Reconnecting;

                        // Retry
                        Reconnect(connection, data);
                        return;
                    }
                }
                else
                {
                    IResponse response = task.Result;
                    Stream stream = response.GetResponseStream();

                    var eventSource = new EventSourceStreamReader(stream);
                    bool retry = true;

                    // When this fires close the event source
                    CancellationToken.Register(() => eventSource.Close());

                    eventSource.Opened = () =>
                    {
                        if (Interlocked.CompareExchange(ref _initializedCalled, 1, 0) == 0)
                        {
                            initializeCallback();
                        }

                        if (reconnecting)
                        {
                            // Change the status to connected
                            connection.State = ConnectionState.Connected;

                            // Raise the reconnect event if the connection comes back up
                            connection.OnReconnected();
                        }
                    };

                    eventSource.Error = connection.OnError;

                    eventSource.Message = sseEvent =>
                    {
                        if (sseEvent.Type == EventType.Data)
                        {
                            if (sseEvent.Data.Equals("initialized", StringComparison.OrdinalIgnoreCase))
                            {
                                return;
                            }

                            bool timedOut;
                            bool disconnected;
                            ProcessResponse(connection, sseEvent.Data, out timedOut, out disconnected);

                            if (disconnected)
                            {
                                retry = false;
                            }
                        }
                    };

                    eventSource.Closed = () =>
                    {
                        response.Close();

                        if (retry && !CancellationToken.IsCancellationRequested)
                        {
                            // If we're retrying then just go again
                            connection.State = ConnectionState.Reconnecting;

                            Reconnect(connection, data);
                        }
                        else
                        {
                            connection.Stop();
                        }
                    };

                    if (!CancellationToken.IsCancellationRequested)
                    {
                        eventSource.Start();
                    }
                }
            });

            if (initializeCallback != null)
            {
                TaskAsyncHelper.Delay(ConnectionTimeout).Then(() =>
                {
                    if (Interlocked.CompareExchange(ref _initializedCalled, 1, 0) == 0)
                    {
                        // Stop the connection
                        Stop(connection);

                        // Connection timeout occured
                        errorCallback(new TimeoutException());
                    }
                });
            }
        }