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; } }
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; } } }
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; } } }
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); }