public override void Stop()
 {
     base.Stop();
     // Shutdown currently open streaming connections.
     lock (_currentStreamingRequests.SyncRoot)
     {
         foreach (DictionaryEntry entry in _currentStreamingRequests)
         {
             EndpointPushNotifier notifier = entry.Value as EndpointPushNotifier;
             notifier.Close();
         }
         _currentStreamingRequests = null;
     }
 }
Beispiel #2
0
        public override IMessage ServiceMessage(IMessage message)
        {
            if (FluorineContext.Current.Client != null)
            {
                FluorineContext.Current.Client.Renew();
            }

            if (message is CommandMessage)
            {
                CommandMessage commandMessage = message as CommandMessage;
                switch (commandMessage.operation)
                {
                case CommandMessage.PollOperation:
                {
                    if (log.IsDebugEnabled)
                    {
                        log.Debug(__Res.GetString(__Res.Endpoint_HandleMessage, this.Id, message.ToString()));
                    }

                    if (FluorineContext.Current.Client != null)
                    {
                        FluorineContext.Current.Client.Renew();
                    }

                    //IMessage[] messages = null;
                    IList messages = null;
                    _waitingPollRequests.Increment();
                    int waitIntervalMillis = this.ChannelDefinition.Properties.WaitIntervalMillis != -1 ? this.ChannelDefinition.Properties.WaitIntervalMillis : 60000;        // int.MaxValue;

                    if (commandMessage.HeaderExists(CommandMessage.FluorineSuppressPollWaitHeader))
                    {
                        waitIntervalMillis = 0;
                    }
                    //If async handling was not set long polling is not supported
                    if (!FluorineConfiguration.Instance.FluorineSettings.Runtime.AsyncHandler)
                    {
                        waitIntervalMillis = 0;
                    }
                    if (this.ChannelDefinition.Properties.MaxWaitingPollRequests <= 0 || _waitingPollRequests.Value >= this.ChannelDefinition.Properties.MaxWaitingPollRequests)
                    {
                        waitIntervalMillis = 0;
                    }

                    if (message.destination != null && message.destination != string.Empty)
                    {
                        string             clientId           = commandMessage.clientId as string;
                        MessageDestination messageDestination = this.GetMessageBroker().GetDestination(message.destination) as MessageDestination;
                        MessageClient      client             = messageDestination.SubscriptionManager.GetSubscriber(clientId);
                        client.Renew();
                        //messages = client.GetPendingMessages();
                    }
                    else
                    {
                        //if (FluorineContext.Current.Client != null)
                        //    messages = FluorineContext.Current.Client.GetPendingMessages(waitIntervalMillis);
                    }

                    if (FluorineContext.Current.Client != null)
                    {
                        IEndpointPushHandler handler = FluorineContext.Current.Client.GetEndpointPushHandler(this.Id);
                        if (handler != null)
                        {
                            messages = handler.GetPendingMessages();
                        }
                        if (messages == null)
                        {
                            lock (handler.SyncRoot)
                            {
                                Monitor.Wait(handler.SyncRoot, waitIntervalMillis);
                            }
                            messages = handler.GetPendingMessages();
                        }
                    }

                    _waitingPollRequests.Decrement();
                    IMessage response = null;
                    if (messages == null || messages.Count == 0)
                    {
                        response = new AcknowledgeMessage();
                    }
                    else
                    {
                        CommandMessage resultMessage = new CommandMessage();
                        resultMessage.operation = CommandMessage.ClientSyncOperation;
                        resultMessage.body      = messages;
                        response = resultMessage;
                    }
                    if (log.IsDebugEnabled)
                    {
                        log.Debug(__Res.GetString(__Res.Endpoint_Response, this.Id, response.ToString()));
                    }
                    return(response);
                }

                case CommandMessage.SubscribeOperation:
                {
                    /*
                     * if (FluorineContext.Current.Client == null)
                     *  FluorineContext.Current.SetCurrentClient(this.GetMessageBroker().ClientRegistry.GetClient(message));
                     * RemotingConnection remotingConnection = null;
                     * foreach (IConnection connection in FluorineContext.Current.Client.Connections)
                     * {
                     *  if (connection is RemotingConnection)
                     *  {
                     *      remotingConnection = connection as RemotingConnection;
                     *      break;
                     *  }
                     * }
                     * if (remotingConnection == null)
                     * {
                     *  remotingConnection = new RemotingConnection(this, null, FluorineContext.Current.Client.Id, null);
                     *  FluorineContext.Current.Client.Renew(this.ClientLeaseTime);
                     *  remotingConnection.Initialize(FluorineContext.Current.Client);
                     * }
                     * FluorineWebContext webContext = FluorineContext.Current as FluorineWebContext;
                     * webContext.SetConnection(remotingConnection);
                     */

                    if (this.ChannelDefinition.Properties.IsPollingEnabled)
                    {
                        //Create and forget, client will close the notifier
                        IEndpointPushHandler handler = FluorineContext.Current.Client.GetEndpointPushHandler(this.Id);
                        if (handler == null)
                        {
                            handler = new EndpointPushNotifier(this, FluorineContext.Current.Client);
                        }

                        /*
                         * lock (_endpointPushHandlers.SyncRoot)
                         * {
                         *  _endpointPushHandlers.Add(notifier.Id, notifier);
                         * }
                         */
                    }
                }
                break;

                case CommandMessage.DisconnectOperation:
                {
                    if (log.IsDebugEnabled)
                    {
                        log.Debug(__Res.GetString(__Res.Endpoint_HandleMessage, this.Id, message.ToString()));
                    }

                    if (FluorineContext.Current.Client != null && FluorineContext.Current.Client.IsValid)
                    {
                        IList messageClients = FluorineContext.Current.Client.MessageClients;
                        if (messageClients != null)
                        {
                            foreach (MessageClient messageClient in messageClients)
                            {
                                messageClient.Invalidate();
                            }
                        }
                        FluorineContext.Current.Client.Invalidate();
                    }
                    if (FluorineContext.Current.Session != null)
                    {
                        FluorineContext.Current.Session.Invalidate();
                    }
                    //Disconnect command is received from a client channel.
                    //The response returned by this method is not guaranteed to get to the client, which is free to terminate its physical connection at any point.
                    IMessage response = new AcknowledgeMessage();

                    if (log.IsDebugEnabled)
                    {
                        log.Debug(__Res.GetString(__Res.Endpoint_Response, this.Id, response.ToString()));
                    }
                    return(response);
                }
                }
            }
            return(base.ServiceMessage(message));
        }
        private void HandleFlexClientStreamingOpenRequest(HttpRequest request, HttpResponse response, IClient flexClient)
        {
            Session session = FluorineContext.Current.Session as Session;

            if (session == null)
            {
                string msg = string.Format("Cannot grant streaming connection when ASP.NET session state is disabled", this.Id);
                if (log.IsWarnEnabled)
                {
                    log.Warn(msg);
                }
                try
                {
                    HandleBadRequest(msg, HttpContext.Current.Response);
                }
                catch (HttpException)
                { }
                return;
            }
            if (!_canStream || !session.CanStream)
            {
                string msg = string.Format("Cannot grant streaming connection, limit has been reached", this.Id);
                try
                {
                    HandleBadRequest(msg, HttpContext.Current.Response);
                }
                catch (HttpException)
                { }
                return;
            }

            bool canStream = false;

            lock (this.SyncRoot)
            {
                _streamingClientsCount.Increment();
                if (_streamingClientsCount.Value == this.MaxStreamingClients)
                {
                    canStream  = true; // This thread got the last spot.
                    _canStream = false;
                }
                else if (_streamingClientsCount.Value > this.MaxStreamingClients)
                {
                    canStream = false;                  // This thread lost the last spot.
                    _streamingClientsCount.Decrement(); // We're not going to grant the streaming right to the client.
                }
                else
                {
                    // Allow this thread to stream.
                    canStream = true;
                }
            }
            if (!canStream)
            {
                string msg = string.Format("Cannot service streaming request, max-streaming-clients reached in endpoint {0}", this.Id);
                try
                {
                    HandleBadRequest(msg, HttpContext.Current.Response);
                }
                catch (HttpException)
                { }
                return;
            }

            UserAgent userAgent = this.ChannelDefinition.Properties.UserAgentSettings[request.Browser.Browser];

            if (userAgent != null)
            {
                lock (session.SyncRoot)
                {
                    session.MaxConnectionsPerSession = userAgent.MaxStreamingConnectionsPerSession;
                }
            }

            lock (session.SyncRoot)
            {
                session.StreamingConnectionsCount++;
                if (session.StreamingConnectionsCount == session.MaxConnectionsPerSession)
                {
                    canStream         = true; // This thread got the last spot in the session.
                    session.CanStream = false;
                }
                else if (session.StreamingConnectionsCount > session.MaxConnectionsPerSession)
                {
                    canStream = false;
                    session.StreamingConnectionsCount--;
                    _streamingClientsCount.Decrement();
                }
                else
                {
                    canStream = true;
                }
            }
            if (!canStream)
            {
                string msg = string.Format("Cannot grant streaming connection, limit has been reached", this.Id);
                try
                {
                    HandleBadRequest(msg, HttpContext.Current.Response);
                }
                catch (HttpException)
                { }
                return;
            }

            EndpointPushNotifier notifier = null;

            try
            {
                response.ContentType = ContentType.AMF;
                response.AppendHeader("Cache-Control", "no-cache");
                response.AppendHeader("Pragma", "no-cache");
                response.AppendHeader("Connection", "close");
                //response.AppendHeader("Transfer-Encoding", "chunked");
                response.Flush();

                // Setup for specific user agents.
                byte[] kickStartBytesToStream = null;
                int    kickStartBytes         = userAgent != null ? userAgent.KickstartBytes : 0;
                if (kickStartBytes > 0)
                {
                    // The minimum number of actual bytes that need to be sent to kickstart, taking into account transfer-encoding overhead.
                    try
                    {
                        int chunkLengthHeaderSize = System.Text.Encoding.ASCII.GetBytes(System.Convert.ToString(kickStartBytes, 0x10)).Length; //System.Text.ASCIIEncoding.ASCII.GetBytes(kickStartBytes.ToString("X")).Length;
                        int chunkOverhead         = chunkLengthHeaderSize + 4;                                                                 // 4 for the 2 wrapping CRLF tokens.
                        int minimumKickstartBytes = kickStartBytes - chunkOverhead;
                        kickStartBytesToStream = new byte[(minimumKickstartBytes > 0) ? minimumKickstartBytes : kickStartBytes];
                    }
                    catch
                    {
                        kickStartBytesToStream = new byte[kickStartBytes];
                    }
                }
                if (kickStartBytesToStream != null)
                {
                    StreamChunk(kickStartBytesToStream, response);
                }
                try
                {
                    notifier = new EndpointPushNotifier(this, flexClient);
                    lock (_currentStreamingRequests.SyncRoot)
                    {
                        _currentStreamingRequests.Add(notifier.Id, notifier);
                    }
                    // Push down an acknowledgement for the 'connect' request containing the unique id for this specific stream.
                    AcknowledgeMessage connectAck = new AcknowledgeMessage();
                    connectAck.body          = notifier.Id;
                    connectAck.correlationId = OpenCommand;
                    StreamMessage(connectAck, response);
                }
                catch (MessageException)
                {
                }

                if (log.IsDebugEnabled)
                {
                    string msg = string.Format("Start streaming for endpoint with id {0} and client with id {1}", this.Id, flexClient.Id);
                    log.Debug(msg);
                }

                int serverToClientHeartbeatMillis = this.ChannelDefinition.Properties.ServerToClientHeartbeatMillis >= 0 ? this.ChannelDefinition.Properties.ServerToClientHeartbeatMillis : 0;
                serverToClientHeartbeatMillis = 100;
                while (!notifier.IsClosed)
                {
                    IList messages = notifier.GetPendingMessages();
                    StreamMessages(messages, response);
                    lock (notifier.SyncRoot)
                    {
                        Monitor.Wait(notifier.SyncRoot, serverToClientHeartbeatMillis);

                        messages = notifier.GetPendingMessages();
                        // If there are no messages to send to the client, send a 0
                        // byte as a heartbeat to make sure the client is still valid.
                        if ((messages == null || messages.Count == 0) && serverToClientHeartbeatMillis > 0)
                        {
                            try
                            {
                                StreamChunk(Heartbeat, response);
                                response.Flush();
                            }
                            catch (HttpException)
                            {
                                break;
                            }
                            catch (IOException)
                            {
                                break;
                            }
                        }
                        else
                        {
                            StreamMessages(messages, response);
                        }
                    }
                }
                // Terminate the response.
                StreamChunk(null, response);
                if (log.IsDebugEnabled)
                {
                    string msg = string.Format("Releasing streaming connection for endpoint with id {0} and and client with id {1}", this.Id, flexClient.Id);
                    log.Debug(msg);
                }
            }
            catch (IOException ex)//HttpException?
            {
                if (log.IsWarnEnabled)
                {
                    log.Warn("Streaming thread for endpoint with id " + this.Id + " is closing connection due to an IO error.", ex);
                }
            }
            catch (Exception ex)
            {
                if (log.IsErrorEnabled)
                {
                    log.Error("Streaming thread for endpoint with id " + this.Id + " is closing connection due to an error.", ex);
                }
            }
            finally
            {
                if (notifier != null && _currentStreamingRequests != null)
                {
                    if (_currentStreamingRequests != null)
                    {
                        lock (_currentStreamingRequests.SyncRoot)
                        {
                            _currentStreamingRequests.Remove(notifier.Id);
                        }
                    }
                    notifier.Close();
                }
                _streamingClientsCount.Decrement();
                lock (session.SyncRoot)
                {
                    session.StreamingConnectionsCount--;
                    session.CanStream = session.StreamingConnectionsCount < session.MaxConnectionsPerSession;
                }
            }
        }
Beispiel #4
0
        public override IMessage ServiceMessage(IMessage message)
        {
            if (FluorineContext.Current.Client != null)
                FluorineContext.Current.Client.Renew();

            if (message is CommandMessage)
            {
                CommandMessage commandMessage = message as CommandMessage;
                switch (commandMessage.operation)
                {
                    case CommandMessage.PollOperation:
                        {
                            if (log.IsDebugEnabled)
                                log.Debug(__Res.GetString(__Res.Endpoint_HandleMessage, this.Id, message.ToString()));

                            if (FluorineContext.Current.Client != null)
                                FluorineContext.Current.Client.Renew();

                            //IMessage[] messages = null;
                            IList messages = null;
                            _waitingPollRequests.Increment();
                            int waitIntervalMillis = this.ChannelDefinition.Properties.WaitIntervalMillis != -1 ? this.ChannelDefinition.Properties.WaitIntervalMillis : 60000;// int.MaxValue;

                            if (commandMessage.HeaderExists(CommandMessage.FluorineSuppressPollWaitHeader))
                                waitIntervalMillis = 0;
                            //If async handling was not set long polling is not supported
                            if (!FluorineConfiguration.Instance.FluorineSettings.Runtime.AsyncHandler)
                                waitIntervalMillis = 0;
                            if (this.ChannelDefinition.Properties.MaxWaitingPollRequests <= 0 || _waitingPollRequests.Value >= this.ChannelDefinition.Properties.MaxWaitingPollRequests)
                                waitIntervalMillis = 0;

                            if (message.destination != null && message.destination != string.Empty)
                            {
                                string clientId = commandMessage.clientId as string;
                                MessageDestination messageDestination = this.GetMessageBroker().GetDestination(message.destination) as MessageDestination;
                                MessageClient client = messageDestination.SubscriptionManager.GetSubscriber(clientId);
                                client.Renew();
                                //messages = client.GetPendingMessages();
                            }
                            else
                            {
                                //if (FluorineContext.Current.Client != null)
                                //    messages = FluorineContext.Current.Client.GetPendingMessages(waitIntervalMillis);
                            }

                            if (FluorineContext.Current.Client != null)
                            {
                                IEndpointPushHandler handler = FluorineContext.Current.Client.GetEndpointPushHandler(this.Id);
                                if (handler != null)
                                    messages = handler.GetPendingMessages();
                                if (messages == null)
                                {
                                    lock (handler.SyncRoot)
                                    {
                                        Monitor.Wait(handler.SyncRoot, waitIntervalMillis);
                                    }
                                    messages = handler.GetPendingMessages();
                                }
                            }

                            _waitingPollRequests.Decrement();
                            IMessage response = null;
                            if (messages == null || messages.Count == 0)
                                response = new AcknowledgeMessage();
                            else
                            {
                                CommandMessage resultMessage = new CommandMessage();
                                resultMessage.operation = CommandMessage.ClientSyncOperation;
                                resultMessage.body = messages;
                                response = resultMessage;
                            }
                            if (log.IsDebugEnabled)
                                log.Debug(__Res.GetString(__Res.Endpoint_Response, this.Id, response.ToString()));
                            return response;

                        }
                    case CommandMessage.SubscribeOperation:
                        {
                            /*
                            if (FluorineContext.Current.Client == null)
                                FluorineContext.Current.SetCurrentClient(this.GetMessageBroker().ClientRegistry.GetClient(message));
                            RemotingConnection remotingConnection = null;
                            foreach (IConnection connection in FluorineContext.Current.Client.Connections)
                            {
                                if (connection is RemotingConnection)
                                {
                                    remotingConnection = connection as RemotingConnection;
                                    break;
                                }
                            }
                            if (remotingConnection == null)
                            {
                                remotingConnection = new RemotingConnection(this, null, FluorineContext.Current.Client.Id, null);
                                FluorineContext.Current.Client.Renew(this.ClientLeaseTime);
                                remotingConnection.Initialize(FluorineContext.Current.Client);
                            }
                            FluorineWebContext webContext = FluorineContext.Current as FluorineWebContext;
                            webContext.SetConnection(remotingConnection);
                            */

                            if (this.ChannelDefinition.Properties.IsPollingEnabled)
                            {
                                //Create and forget, client will close the notifier
                                IEndpointPushHandler handler = FluorineContext.Current.Client.GetEndpointPushHandler(this.Id);
                                if( handler == null )
                                    handler = new EndpointPushNotifier(this, FluorineContext.Current.Client);
                                /*
                                lock (_endpointPushHandlers.SyncRoot)
                                {
                                    _endpointPushHandlers.Add(notifier.Id, notifier);
                                }
                                */
                            }
                        }
                        break;
                    case CommandMessage.DisconnectOperation:
                        {
                            if (log.IsDebugEnabled)
                                log.Debug(__Res.GetString(__Res.Endpoint_HandleMessage, this.Id, message.ToString()));

                            if (FluorineContext.Current.Client != null && FluorineContext.Current.Client.IsValid)
                            {
                                IList messageClients = FluorineContext.Current.Client.MessageClients;
                                if (messageClients != null)
                                {
                                    foreach (MessageClient messageClient in messageClients)
                                    {
                                        messageClient.Invalidate();
                                    }
                                }
                                FluorineContext.Current.Client.Invalidate();
                            }
                            if (FluorineContext.Current.Session != null)
                            {
                                FluorineContext.Current.Session.Invalidate();
                            }
                            //Disconnect command is received from a client channel.
                            //The response returned by this method is not guaranteed to get to the client, which is free to terminate its physical connection at any point.
                            IMessage response = new AcknowledgeMessage();

                            if (log.IsDebugEnabled)
                                log.Debug(__Res.GetString(__Res.Endpoint_Response, this.Id, response.ToString()));
                            return response;
                        }
                }
            }
            return base.ServiceMessage(message);
        }
        private void HandleFlexClientStreamingOpenRequest(HttpRequest request, HttpResponse response, IClient flexClient)
        {
            Session session = FluorineContext.Current.Session as Session;
            if (session == null)
            {
                string msg = string.Format("Cannot grant streaming connection when ASP.NET session state is disabled", this.Id);
                if (log.IsWarnEnabled)
                    log.Warn(msg);
                try
                {
                    HandleBadRequest(msg, HttpContext.Current.Response);
                }
                catch (HttpException)
                { }
                return;
            }
            if (!_canStream || !session.CanStream)
            {
                string msg = string.Format("Cannot grant streaming connection, limit has been reached", this.Id);
                try
                {
                    HandleBadRequest(msg, HttpContext.Current.Response);
                }
                catch (HttpException)
                { }
                return;
            }

            bool canStream = false;
            lock (this.SyncRoot)
            {
                _streamingClientsCount.Increment();
                if (_streamingClientsCount.Value == this.MaxStreamingClients)
                {
                    canStream = true; // This thread got the last spot.
                    _canStream = false;
                    
                }
                else if (_streamingClientsCount.Value > this.MaxStreamingClients)
                {
                    canStream = false; // This thread lost the last spot.
                    _streamingClientsCount.Decrement();// We're not going to grant the streaming right to the client.
                }
                else
                {
                    // Allow this thread to stream.
                    canStream = true;
                }
            }
            if (!canStream)
            {
                string msg = string.Format("Cannot service streaming request, max-streaming-clients reached in endpoint {0}", this.Id);
                try
                {
                    HandleBadRequest(msg, HttpContext.Current.Response);
                }
                catch (HttpException)
                { }
                return;
            }

            UserAgent userAgent = this.ChannelDefinition.Properties.UserAgentSettings[request.Browser.Browser];
            if (userAgent != null)
            {
                lock (session.SyncRoot)
                {
                    session.MaxConnectionsPerSession = userAgent.MaxStreamingConnectionsPerSession;
                }
            }

            lock (session.SyncRoot)
            {
                session.StreamingConnectionsCount++;
                if (session.StreamingConnectionsCount == session.MaxConnectionsPerSession)
                {
                    canStream = true; // This thread got the last spot in the session.
                    session.CanStream = false;
                }
                else if (session.StreamingConnectionsCount > session.MaxConnectionsPerSession)
                {
                    canStream = false;
                    session.StreamingConnectionsCount--;
                    _streamingClientsCount.Decrement();
                }
                else
                {
                    canStream = true;
                }
            }
            if (!canStream)
            {
                string msg = string.Format("Cannot grant streaming connection, limit has been reached", this.Id);
                try
                {
                    HandleBadRequest(msg, HttpContext.Current.Response);
                }
                catch (HttpException)
                { }
                return;
            }

            EndpointPushNotifier notifier = null;
            try
            {
                response.ContentType = ContentType.AMF;
                response.AppendHeader("Cache-Control", "no-cache");
                response.AppendHeader("Pragma", "no-cache");
                response.AppendHeader("Connection", "close");
                //response.AppendHeader("Transfer-Encoding", "chunked");
                response.Flush();

                // Setup for specific user agents.
                byte[] kickStartBytesToStream = null;
                int kickStartBytes = userAgent != null ? userAgent.KickstartBytes : 0;
                if (kickStartBytes > 0)
                {
                    // The minimum number of actual bytes that need to be sent to kickstart, taking into account transfer-encoding overhead.
                    try
                    {
                        int chunkLengthHeaderSize = System.Text.Encoding.ASCII.GetBytes(System.Convert.ToString(kickStartBytes, 0x10)).Length; //System.Text.ASCIIEncoding.ASCII.GetBytes(kickStartBytes.ToString("X")).Length;
                        int chunkOverhead = chunkLengthHeaderSize + 4; // 4 for the 2 wrapping CRLF tokens.
                        int minimumKickstartBytes = kickStartBytes - chunkOverhead;
                        kickStartBytesToStream = new byte[(minimumKickstartBytes > 0) ? minimumKickstartBytes : kickStartBytes];
                    }
                    catch
                    {
                        kickStartBytesToStream = new byte[kickStartBytes];
                    }
                }
                if (kickStartBytesToStream != null)
                {
                    StreamChunk(kickStartBytesToStream, response);
                }
                try
                {
                    notifier = new EndpointPushNotifier(this, flexClient);
                    lock (_currentStreamingRequests.SyncRoot)
                    {
                        _currentStreamingRequests.Add(notifier.Id, notifier);
                    }
                    // Push down an acknowledgement for the 'connect' request containing the unique id for this specific stream.
                    AcknowledgeMessage connectAck = new AcknowledgeMessage();
                    connectAck.body = notifier.Id;
                    connectAck.correlationId = OpenCommand;
                    StreamMessage(connectAck, response);
                }
                catch (MessageException)
                {
                }

                if (log.IsDebugEnabled)
                {
                    string msg = string.Format("Start streaming for endpoint with id {0} and client with id {1}", this.Id, flexClient.Id);
                    log.Debug(msg);
                }

                int serverToClientHeartbeatMillis = this.ChannelDefinition.Properties.ServerToClientHeartbeatMillis >= 0 ? this.ChannelDefinition.Properties.ServerToClientHeartbeatMillis : 0;
                serverToClientHeartbeatMillis = 100;
                while (!notifier.IsClosed)
                {
                    IList messages = notifier.GetPendingMessages();
                    StreamMessages(messages, response);
                    lock (notifier.SyncRoot)
                    {
                        Monitor.Wait(notifier.SyncRoot, serverToClientHeartbeatMillis);

                        messages = notifier.GetPendingMessages();
                        // If there are no messages to send to the client, send a 0
                        // byte as a heartbeat to make sure the client is still valid.
                        if ((messages == null || messages.Count == 0) && serverToClientHeartbeatMillis > 0)
                        {
                            try
                            {
                                StreamChunk(Heartbeat, response);
                                response.Flush();
                            }
                            catch (HttpException)
                            {
                                break;
                            }
                            catch (IOException)
                            {
                                break;
                            }
                        }
                        else
                        {
                            StreamMessages(messages, response);
                        }
                    }
                }
                // Terminate the response.
                StreamChunk(null, response);
                if (log.IsDebugEnabled)
                {
                    string msg = string.Format("Releasing streaming connection for endpoint with id {0} and and client with id {1}", this.Id, flexClient.Id);
                    log.Debug(msg);
                }
            }
            catch (IOException ex)//HttpException?
            {
                if (log.IsWarnEnabled)
                    log.Warn("Streaming thread for endpoint with id " + this.Id + " is closing connection due to an IO error.", ex);
            }
            catch (Exception ex)
            {
                if (log.IsErrorEnabled)
                    log.Error("Streaming thread for endpoint with id " + this.Id + " is closing connection due to an error.", ex);
            }
            finally
            {
                if (notifier != null && _currentStreamingRequests != null)
                {
                    if (_currentStreamingRequests != null)
                    {
                        lock (_currentStreamingRequests.SyncRoot)
                        {
                            _currentStreamingRequests.Remove(notifier.Id);
                        }
                    }
                    notifier.Close();
                }
                _streamingClientsCount.Decrement();
                lock (session.SyncRoot)
                {
                    session.StreamingConnectionsCount--;
                    session.CanStream = session.StreamingConnectionsCount < session.MaxConnectionsPerSession;
                }
            }
        }