/// <summary>
 /// Init constructor.
 /// </summary>
 public BayeuxException(string message, BayeuxRequest request, BayeuxResponse response, IJSonObject responseObject)
     : base(message)
 {
     Request = request;
     Response = response;
     JSonResponse = responseObject;
 }
        /// <summary>
        /// Process given response. Request is also provided.
        /// The default behavior is that it only passes response to request, however there might be
        /// a situation, when no request was given (when a 'text' message has been sent).
        /// Returning 'true' as a result will filter out the ResponseReceived event.
        /// </summary>
        protected virtual bool ProcessResponse(BayeuxRequest request, BayeuxResponse response)
        {
            if (request != null)
                request.ProcessResponse(response);
            else
            {
                DebugLog.WriteBayeuxLine("BayeuxConnection: Processing response ignored for null request!");
            }

            return false;
        }
        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>
 /// Sends bayeux message.
 /// </summary>
 protected void SendRequest(BayeuxRequest message, bool asynchronous)
 {
     Send(message, null, asynchronous);
 }
 /// <summary>
 /// Converts <see cref="BayeuxRequest"/> to string containing its JSON representation.
 /// </summary>
 protected string SerializeRequest(BayeuxRequest request)
 {
     _writerCache.Remove(0, _writerCache.Length);
     _jsonWriter.WriteArrayBegin();
     request.Write(_jsonWriter);
     _jsonWriter.WriteArrayEnd();
     return _jsonWriter.ToString();
 }
        /// <summary>
        /// Sends a bayeux message to the server with the ability to override the JSON returned by request object itself
        /// with the one specified as raw JSON data.
        /// </summary>
        public void Send(BayeuxRequest message, string overrideWithJSonData, bool asynchronous)
        {
            if (message == null)
                throw new ArgumentNullException("message");

            string dataToSend = overrideWithJSonData;

            lock (_syncObject)
            {
                if (_request != null)
                {
                    if (_httpConnection.IsActive)
                        _httpConnection.Cancel(); // this is a sync operation, which should cause the _request to be nulled ...
                    _request = null;
                }

                if (_request != null)
                    throw new InvalidOperationException("Can't start new request, when current has not been finished/cancelled");

                if (string.IsNullOrEmpty(message.ClientID))
                    message.ClientID = ClientID;

                _request = message;

                // serialize JSON data as text message if not provided as parameter:
                if (string.IsNullOrEmpty(overrideWithJSonData))
                    dataToSend = SerializeRequest(message);
            }

            if (asynchronous)
                _httpConnection.SendRequestAsync(null, dataToSend, message.RequestMethod, HttpDataSourceResponseType.AsString);
            else
                _httpConnection.SendRequest(null, dataToSend, message.RequestMethod, HttpDataSourceResponseType.AsString);
        }
        /// <summary>
        /// Sends a bayeux message to the server.
        /// Connection must be established first otherwise the exception will be thrown.
        /// </summary>
        public void Send(BayeuxRequest message, bool asynchronous)
        {
            if (_state != BayeuxConnectionState.Connected)
                throw new InvalidOperationException("Not connected");

            if (message == null)
                throw new ArgumentNullException("message");

            Send(message, null, asynchronous);
        }
 /// <summary>
 /// Stops listening for asynchronous data.
 /// </summary>
 public void StopLongPolling()
 {
     if (IsLongPolling)
     {
         _longPollingRequest = null;
         _longPolling = false;
         _httpLongPollingConnection.Cancel();
     }
 }
        /// <summary>
        /// Starts another long-polling HTTP connection to asynchronously receive events from Bayeux server.
        /// </summary>
        public void StartLongPolling(BayeuxRequest request)
        {
            if (request == null)
                throw new ArgumentNullException("request");
            if (_httpLongPollingConnection == null)
                throw new InvalidOperationException("Long-polling connection not specified during BayeuxConnection object creation");
            if (State != BayeuxConnectionState.Connected)
                throw new InvalidOperationException("Not connected");
            if (IsLongPolling)
                throw new InvalidOperationException("Connection is already polling");

            _longPollingRequest = request;
            _longPolling = true;
            ReconnectLongPollingConnection(true);
        }
        private void DataSource_OnDataReceiveFailed(object sender, HttpDataSourceEventArgs e)
        {
            // say, there the current request is no more needed:
            BayeuxRequest r = _request;
            _request = null;

            if (r != null)
                r.ProcessFailed(e);

            OnDataFailed(new BayeuxConnectionEventArgs(this, e.StatusCode, e.StatusDescription));
        }
        private void ProcessReceivedData(BayeuxRequest request, HttpDataSourceEventArgs e)
        {
            IJSonObject jsonData = null;

            try
            {
                // read JSON data:
                jsonData = _jsonReader.ReadAsJSonObject(e.StringData);
                OnDataReceived(new BayeuxConnectionEventArgs(this, e.StatusCode, e.StatusDescription, e.StringData, jsonData, null));
            }
            catch (JSonReaderException ex)
            {
                OnDataFailed(new BayeuxConnectionEventArgs(this, e.StatusCode, e.StatusDescription, e.StringData, ex));
            }

            if (jsonData != null)
            {
                // process JSON response, in case it is an array of responses:
                if (jsonData.IsArray)
                {
                    foreach (IJSonObject message in jsonData.ArrayItems)
                    {
                        ProcessResponseMessage(request, e.StatusCode, e.StatusDescription, message, e.StringData);
                    }
                }
                else
                {
                    ProcessResponseMessage(request, e.StatusCode, e.StatusDescription, jsonData, e.StringData);
                }
            }
            else
            {
                DebugLog.WriteBayeuxLine("--- No JSON data received from server ---");
                OnDataFailed(new BayeuxConnectionEventArgs(this, HttpStatusCode.NoContent, "No JSON data received"));
            }
        }
 private void DataSource_OnDataReceived(object sender, HttpDataSourceEventArgs e)
 {
     // say, there the current request is no more needed:
     BayeuxRequest r = _request;
     _request = null;
     ProcessReceivedData(r, e);
 }