/// <summary>
        /// Init constructor.
        /// </summary>
        public BayeuxConnection(IHttpDataSource connection, IHttpDataSource longPollingConnection)
        {
            if (connection == null)
            {
                throw new ArgumentNullException("connection");
            }

            _syncObject  = new object();
            _writerCache = new StringBuilder();
            _jsonWriter  = new JSonWriter(_writerCache, false);
            _jsonWriter.CompactEnumerables = true;
            _jsonReader               = new JSonReader();
            _state                    = BayeuxConnectionState.Disconnected;
            _subscribedChannels       = new List <string>();
            LongPollingRetryDelay     = DefaultRetryDelay;
            LongPollingConnectRetries = DefaultNumberOfConnectRetries;

            _httpConnection = connection;
            _httpConnection.DataReceived      += DataSource_OnDataReceived;
            _httpConnection.DataReceiveFailed += DataSource_OnDataReceiveFailed;

            if (longPollingConnection != null)
            {
                _httpLongPollingConnection = longPollingConnection;
                _httpLongPollingConnection.DataReceived      += LongPollingDataSource_OnDataReceived;
                _httpLongPollingConnection.DataReceiveFailed += LongPollingDataSource_OnDataReceiveFailed;
            }
        }
        /// <summary>
        /// Cancels the currently executed asynchronous server request.
        /// </summary>
        public void Cancel()
        {
            if (_httpConnection.IsActive)
            {
                _httpConnection.Cancel();
            }

            // check if cancelled the handshake request:
            if (_state == BayeuxConnectionState.Connecting)
            {
                _state = BayeuxConnectionState.Disconnected;
            }
        }
        /// <summary>
        /// Sends handshake request to the server.
        /// </summary>
        public void Handshake(BayeuxConnectionTypes supportedConnectionTypes, IJSonWritable data, IJSonWritable ext, bool asynchronous)
        {
            // if there is another Handshake request executed in background, try to cancel it first:
            if (_state == BayeuxConnectionState.Connecting)
            {
                Cancel(); // <-- this should reset the state to Disconnected!
            }
            if (_state != BayeuxConnectionState.Disconnected)
            {
                throw new InvalidOperationException("Connecting or already connected to bayeux server! Disconnect first");
            }

            _state = BayeuxConnectionState.Connecting;
            SendRequest(new HandshakeRequest(supportedConnectionTypes, data, ext), asynchronous);
        }
        private void ProcessResponseMessage(BayeuxRequest request, HttpStatusCode httpStatusCode, string httpStatusDescription, IJSonObject message, string rawMessage)
        {
            try
            {

                string channel = message["channel"].StringValue; // each Bayuex message must have a channel associated!
                BayeuxResponse response = null;

                if (string.IsNullOrEmpty(channel))
                    throw new BayeuxException("Unexpected message with empty channel!");
                if (request != null && (channel.StartsWith("/meta", StringComparison.Ordinal) && channel != request.Channel))
                    throw new BayeuxException(string.Format(CultureInfo.InvariantCulture, "Unexpected response with channel: '{0}'", channel), request, null, message);
                if (request != null && channel.StartsWith("/meta", StringComparison.Ordinal) && request.ID != message["id"].StringValue)
                    throw new BayeuxException(string.Format(CultureInfo.InvariantCulture, "Invalid response ID, current: '{0}', expected: '{1}'", request.ID, message["id"].StringValue), request, null,
                                              message);

                ///////////////////////////////////////////////////////////////////////////////////////////
                // identify meta messages:
                if (channel == HandshakeRequest.MetaChannel)
                {
                    var handshakeResponse = new HandshakeResponse(message);
                    response = handshakeResponse;

                    // inform, that connection succeeded:
                    if (handshakeResponse.Successful)
                    {
                        ClientID = response.ClientID;

                        if (string.IsNullOrEmpty(ClientID))
                        {
                            throw new BayeuxException("Invalid ClientID received from server", request, handshakeResponse, message);
                        }

                        OnConnected(new BayeuxConnectionEventArgs(this, httpStatusCode, httpStatusDescription, rawMessage, message, handshakeResponse));
                    }
                    else
                    {
                        // inform that Handshake failed, via dedicated event:
                        ClientID = null;
                        OnConnectionFailed(new BayeuxConnectionEventArgs(this, response.Successful ? HttpStatusCode.OK : HttpStatusCode.BadRequest, null, rawMessage, message, response));
                    }
                }

                if (channel == DisconnectRequest.MetaChannel)
                {
                    response = new BayeuxResponse(message);

                    // inform that disconnection succeeded:
                    _state = BayeuxConnectionState.Disconnected;
                    ClientID = null;

                    OnDisconnected(new BayeuxConnectionEventArgs(this, response.Successful ? HttpStatusCode.OK : HttpStatusCode.BadRequest, null, rawMessage, message, response));
                }

                if (channel == SubscribeRequest.MetaChannel)
                {
                    var subscribeResponse = new SubscribeResponse(message);
                    response = subscribeResponse;

                    if (subscribeResponse.Successful)
                        _subscribedChannels.Add(subscribeResponse.SubscriptionChannel);
                }

                if (channel == UnsubscribeRequest.MetaChannel)
                {
                    var unsubscribeResponse = new UnsubscribeResponse(message);
                    response = unsubscribeResponse;

                    if (unsubscribeResponse.Successful)
                        _subscribedChannels.Remove(unsubscribeResponse.SubscriptionChannel);
                }

                if (_subscribedChannels.Contains(channel))
                {
                    response = new BayeuxResponse(message);

                    // event from server:
                    OnEventReceived(new BayeuxConnectionEventArgs(this, httpStatusCode, httpStatusDescription, rawMessage, message, response));
                }

                // process generic response:
                if (response == null)
                    response = ProvideResponse(message);

                // allow filtering of ResponseReceived event:
                if (ProcessResponse(request, response))
                    return;

                // one place to process all responses:
                OnResponseReceived(new BayeuxConnectionEventArgs(this, httpStatusCode, httpStatusDescription, rawMessage, message, response));
            }
            catch (Exception ex)
            {
                DebugLog.WriteBayeuxException(ex);

                string statusDescription = string.Concat(httpStatusDescription, "; unexpected exception: \"", ex.Message, "\"");
                OnDataFailed(new BayeuxConnectionEventArgs(this, httpStatusCode, statusDescription, rawMessage, message));
            }
        }
        /// <summary>
        /// Cancels the currently executed asynchronous server request.
        /// </summary>
        public void Cancel()
        {
            if (_httpConnection.IsActive)
                _httpConnection.Cancel();

            // check if cancelled the handshake request:
            if (_state == BayeuxConnectionState.Connecting)
                _state = BayeuxConnectionState.Disconnected;
        }
        /// <summary>
        /// Sends handshake request to the server.
        /// </summary>
        public void Handshake(BayeuxConnectionTypes supportedConnectionTypes, IJSonWritable data, IJSonWritable ext, bool asynchronous)
        {
            // if there is another Handshake request executed in background, try to cancel it first:
            if (_state == BayeuxConnectionState.Connecting)
                Cancel(); // <-- this should reset the state to Disconnected!

            if (_state != BayeuxConnectionState.Disconnected)
                throw new InvalidOperationException("Connecting or already connected to bayeux server! Disconnect first");

            _state = BayeuxConnectionState.Connecting;
            SendRequest(new HandshakeRequest(supportedConnectionTypes, data, ext), asynchronous);
        }
        /// <summary>
        /// Init constructor.
        /// </summary>
        public BayeuxConnection(IHttpDataSource connection, IHttpDataSource longPollingConnection)
        {
            if (connection == null)
                throw new ArgumentNullException("connection");

            _syncObject = new object();
            _writerCache = new StringBuilder();
            _jsonWriter = new JSonWriter(_writerCache, false);
            _jsonWriter.CompactEnumerables = true;
            _jsonReader = new JSonReader();
            _state = BayeuxConnectionState.Disconnected;
            _subscribedChannels = new List<string>();
            LongPollingRetryDelay = DefaultRetryDelay;
            LongPollingConnectRetries = DefaultNumberOfConnectRetries;

            _httpConnection = connection;
            _httpConnection.DataReceived += DataSource_OnDataReceived;
            _httpConnection.DataReceiveFailed += DataSource_OnDataReceiveFailed;

            if (longPollingConnection != null)
            {
                _httpLongPollingConnection = longPollingConnection;
                _httpLongPollingConnection.DataReceived += LongPollingDataSource_OnDataReceived;
                _httpLongPollingConnection.DataReceiveFailed += LongPollingDataSource_OnDataReceiveFailed;
            }
        }
        private void ProcessResponseMessage(BayeuxRequest request, HttpStatusCode httpStatusCode, string httpStatusDescription, IJSonObject message, string rawMessage)
        {
            try
            {
                string         channel  = message["channel"].StringValue; // each Bayuex message must have a channel associated!
                BayeuxResponse response = null;

                if (string.IsNullOrEmpty(channel))
                {
                    throw new BayeuxException("Unexpected message with empty channel!");
                }
                if (request != null && (channel.StartsWith("/meta", StringComparison.Ordinal) && channel != request.Channel))
                {
                    throw new BayeuxException(string.Format(CultureInfo.InvariantCulture, "Unexpected response with channel: '{0}'", channel), request, null, message);
                }
                if (request != null && channel.StartsWith("/meta", StringComparison.Ordinal) && request.ID != message["id"].StringValue)
                {
                    throw new BayeuxException(string.Format(CultureInfo.InvariantCulture, "Invalid response ID, current: '{0}', expected: '{1}'", request.ID, message["id"].StringValue), request, null,
                                              message);
                }

                ///////////////////////////////////////////////////////////////////////////////////////////
                // identify meta messages:
                if (channel == HandshakeRequest.MetaChannel)
                {
                    var handshakeResponse = new HandshakeResponse(message);
                    response = handshakeResponse;

                    // inform, that connection succeeded:
                    if (handshakeResponse.Successful)
                    {
                        ClientID = response.ClientID;

                        if (string.IsNullOrEmpty(ClientID))
                        {
                            throw new BayeuxException("Invalid ClientID received from server", request, handshakeResponse, message);
                        }

                        OnConnected(new BayeuxConnectionEventArgs(this, httpStatusCode, httpStatusDescription, rawMessage, message, handshakeResponse));
                    }
                    else
                    {
                        // inform that Handshake failed, via dedicated event:
                        ClientID = null;
                        OnConnectionFailed(new BayeuxConnectionEventArgs(this, response.Successful ? HttpStatusCode.OK : HttpStatusCode.BadRequest, null, rawMessage, message, response));
                    }
                }

                if (channel == DisconnectRequest.MetaChannel)
                {
                    response = new BayeuxResponse(message);

                    // inform that disconnection succeeded:
                    _state   = BayeuxConnectionState.Disconnected;
                    ClientID = null;

                    OnDisconnected(new BayeuxConnectionEventArgs(this, response.Successful ? HttpStatusCode.OK : HttpStatusCode.BadRequest, null, rawMessage, message, response));
                }

                if (channel == SubscribeRequest.MetaChannel)
                {
                    var subscribeResponse = new SubscribeResponse(message);
                    response = subscribeResponse;

                    if (subscribeResponse.Successful)
                    {
                        _subscribedChannels.Add(subscribeResponse.SubscriptionChannel);
                    }
                }

                if (channel == UnsubscribeRequest.MetaChannel)
                {
                    var unsubscribeResponse = new UnsubscribeResponse(message);
                    response = unsubscribeResponse;

                    if (unsubscribeResponse.Successful)
                    {
                        _subscribedChannels.Remove(unsubscribeResponse.SubscriptionChannel);
                    }
                }

                if (_subscribedChannels.Contains(channel))
                {
                    response = new BayeuxResponse(message);

                    // event from server:
                    OnEventReceived(new BayeuxConnectionEventArgs(this, httpStatusCode, httpStatusDescription, rawMessage, message, response));
                }

                // process generic response:
                if (response == null)
                {
                    response = ProvideResponse(message);
                }

                // allow filtering of ResponseReceived event:
                if (ProcessResponse(request, response))
                {
                    return;
                }

                // one place to process all responses:
                OnResponseReceived(new BayeuxConnectionEventArgs(this, httpStatusCode, httpStatusDescription, rawMessage, message, response));
            }
            catch (Exception ex)
            {
                DebugLog.WriteBayeuxException(ex);

                string statusDescription = string.Concat(httpStatusDescription, "; unexpected exception: \"", ex.Message, "\"");
                OnDataFailed(new BayeuxConnectionEventArgs(this, httpStatusCode, statusDescription, rawMessage, message));
            }
        }