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