#region License
/*
* WebSocketServiceHostManager.cs
*
* The MIT License
*
* Copyright (c) 2012-2013 sta.blockhead
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Text;
namespace WebSocketSharp.Server
{
///
/// Manages the collection of the WebSocket service hosts.
///
public class WebSocketServiceHostManager
{
#region Private Fields
private volatile bool _keepClean;
private Logger _logger;
private Dictionary _serviceHosts;
private object _sync;
#endregion
#region Internal Constructors
internal WebSocketServiceHostManager ()
: this (new Logger ())
{
}
internal WebSocketServiceHostManager (Logger logger)
{
_logger = logger;
_keepClean = true;
_serviceHosts = new Dictionary ();
_sync = new object ();
}
#endregion
#region Public Properties
///
/// Gets the connection count to the WebSocket services managed by the .
///
///
/// An that contains the connection count.
///
public int ConnectionCount {
get {
var count = 0;
foreach (var host in ServiceHosts)
count += host.ConnectionCount;
return count;
}
}
///
/// Gets the number of the WebSocket services managed by the .
///
///
/// An that contains the number of the WebSocket services.
///
public int ServiceCount {
get {
lock (_sync)
{
return _serviceHosts.Count;
}
}
}
///
/// Gets the collection of paths to the WebSocket services managed by the .
///
///
/// An IEnumerable<string> that contains the collection of paths.
///
public IEnumerable ServicePaths {
get {
lock (_sync)
{
return _serviceHosts.Keys;
}
}
}
#endregion
#region Internal Properties
internal bool KeepClean {
get {
return _keepClean;
}
set {
lock (_sync)
{
if (_keepClean ^ value)
{
_keepClean = value;
foreach (var host in _serviceHosts.Values)
host.KeepClean = value;
}
}
}
}
internal IEnumerable ServiceHosts {
get {
lock (_sync)
{
return _serviceHosts.Values;
}
}
}
#endregion
#region Private Methods
private Dictionary copy ()
{
lock (_sync)
{
return new Dictionary (_serviceHosts);
}
}
#endregion
#region Internal Methods
internal void Add (string servicePath, IServiceHost serviceHost)
{
lock (_sync)
{
IServiceHost host;
if (_serviceHosts.TryGetValue (servicePath, out host))
{
_logger.Error (
"The WebSocket service host with the specified path already exists.\npath: " + servicePath);
return;
}
_serviceHosts.Add (servicePath.UrlDecode (), serviceHost);
}
}
internal bool Remove (string servicePath)
{
IServiceHost host;
lock (_sync)
{
if (!_serviceHosts.TryGetValue (servicePath, out host))
{
_logger.Error (
"The WebSocket service host with the specified path not found.\npath: " + servicePath);
return false;
}
_serviceHosts.Remove (servicePath);
}
host.Stop ((ushort) CloseStatusCode.AWAY, String.Empty);
return true;
}
internal void Stop ()
{
lock (_sync)
{
foreach (var host in _serviceHosts.Values)
host.Stop ();
_serviceHosts.Clear ();
}
}
internal void Stop (ushort code, string reason)
{
lock (_sync)
{
foreach (var host in _serviceHosts.Values)
host.Stop (code, reason);
_serviceHosts.Clear ();
}
}
internal bool TryGetServiceHost (string servicePath, out IServiceHost serviceHost)
{
lock (_sync)
{
return _serviceHosts.TryGetValue (servicePath, out serviceHost);
}
}
#endregion
#region Public Methods
///
/// Broadcasts the specified array of to all clients of the WebSocket services.
///
///
/// An array of to broadcast.
///
public void Broadcast (byte [] data)
{
if (data == null)
{
_logger.Error ("'data' must not be null.");
return;
}
foreach (var host in ServiceHosts)
host.Broadcast (data);
}
///
/// Broadcasts the specified to all clients of the WebSocket services.
///
///
/// A to broadcast.
///
public void Broadcast (string data)
{
if (data == null)
{
_logger.Error ("'data' must not be null.");
return;
}
foreach (var host in ServiceHosts)
host.Broadcast (data);
}
///
/// Broadcasts the specified array of to all clients of the WebSocket service
/// with the specified .
///
///
/// true if is broadcasted; otherwise, false.
///
///
/// An array of to broadcast.
///
///
/// A that contains an absolute path to the WebSocket service to find.
///
public bool BroadcastTo (byte [] data, string servicePath)
{
var msg = data == null
? "'data' must not be null."
: servicePath.IsNullOrEmpty ()
? "'servicePath' must not be null or empty."
: null;
if (msg != null)
{
_logger.Error (msg);
return false;
}
IServiceHost host;
if (!TryGetServiceHost (servicePath, out host))
{
_logger.Error ("The WebSocket service with the specified path not found.\npath: " + servicePath);
return false;
}
host.Broadcast (data);
return true;
}
///
/// Broadcasts the specified to all clients of the WebSocket service
/// with the specified .
///
///
/// true if is broadcasted; otherwise, false.
///
///
/// A to broadcast.
///
///
/// A that contains an absolute path to the WebSocket service to find.
///
public bool BroadcastTo (string data, string servicePath)
{
var msg = data == null
? "'data' must not be null."
: servicePath.IsNullOrEmpty ()
? "'servicePath' must not be null or empty."
: null;
if (msg != null)
{
_logger.Error (msg);
return false;
}
IServiceHost host;
if (!TryGetServiceHost (servicePath, out host))
{
_logger.Error ("The WebSocket service with the specified path not found.\npath: " + servicePath);
return false;
}
host.Broadcast (data);
return true;
}
///
/// Sends Pings with the specified to all clients of the WebSocket services.
///
///
/// A Dictionary<string, Dictionary<string, bool>> that contains the collection of
/// service paths and pairs of session ID and value indicating whether each WebSocket service
/// received the Pong from each client in a time.
///
///
/// A that contains a message to send.
///
public Dictionary> Broadping (string message)
{
if (!message.IsNullOrEmpty ())
{
var len = Encoding.UTF8.GetBytes (message).Length;
if (len > 125)
{
_logger.Error ("The payload length of a Ping frame must be 125 bytes or less.");
return null;
}
}
var result = new Dictionary> ();
foreach (var service in copy ())
result.Add (service.Key, service.Value.Broadping (message));
return result;
}
///
/// Sends Pings with the specified to all clients of the WebSocket service
/// with the specified .
///
///
/// A Dictionary<string, bool> that contains the collection of pairs of session ID and value
/// indicating whether the WebSocket service received the Pong from each client in a time.
/// If the WebSocket service is not found, returns .
///
///
/// A that contains a message to send.
///
///
/// A that contains an absolute path to the WebSocket service to find.
///
public Dictionary BroadpingTo (string message, string servicePath)
{
if (message == null)
message = String.Empty;
var msg = Encoding.UTF8.GetBytes (message).Length > 125
? "The payload length of a Ping frame must be 125 bytes or less."
: servicePath.IsNullOrEmpty ()
? "'servicePath' must not be null or empty."
: null;
if (msg != null)
{
_logger.Error (msg);
return null;
}
IServiceHost host;
if (!TryGetServiceHost (servicePath, out host))
{
_logger.Error ("The WebSocket service with the specified path not found.\npath: " + servicePath);
return null;
}
return host.Broadping (message);
}
///
/// Gets the connection count to the WebSocket service with the specified .
///
///
/// An that contains the connection count if the WebSocket service is successfully found;
/// otherwise, -1.
///
///
/// A that contains an absolute path to the WebSocket service to find.
///
public int GetConnectionCount (string servicePath)
{
if (servicePath.IsNullOrEmpty ())
{
_logger.Error ("'servicePath' must not be null or empty.");
return -1;
}
IServiceHost host;
if (!TryGetServiceHost (servicePath, out host))
{
_logger.Error ("The WebSocket service with the specified path not found.\npath: " + servicePath);
return -1;
}
return host.ConnectionCount;
}
///
/// Sends a Ping with the specified to the client associated with
/// the specified and .
///
///
/// true if the WebSocket service with receives a Pong
/// from the client in a time; otherwise, false.
///
///
/// A that contains a message to send.
///
///
/// A that contains an ID that represents the destination for the Ping.
///
///
/// A that contains an absolute path to the WebSocket service to find.
///
public bool PingTo (string message, string id, string servicePath)
{
if (message == null)
message = String.Empty;
var msg = Encoding.UTF8.GetBytes (message).Length > 125
? "The payload length of a Ping frame must be 125 bytes or less."
: id.IsNullOrEmpty ()
? "'id' must not be null or empty."
: servicePath.IsNullOrEmpty ()
? "'servicePath' must not be null or empty."
: null;
if (msg != null)
{
_logger.Error (msg);
return false;
}
IServiceHost host;
if (!TryGetServiceHost (servicePath, out host))
{
_logger.Error ("The WebSocket service with the specified path not found.\npath: " + servicePath);
return false;
}
return host.PingTo (id, message);
}
///
/// Sends a binary data to the client associated with the specified and
/// .
///
///
/// true if is successfully sent; otherwise, false.
///
///
/// An array of that contains a binary data to send.
///
///
/// A that contains an ID that represents the destination for the data.
///
///
/// A that contains an absolute path to the WebSocket service to find.
///
public bool SendTo (byte [] data, string id, string servicePath)
{
var msg = data == null
? "'data' must not be null."
: id.IsNullOrEmpty ()
? "'id' must not be null or empty."
: servicePath.IsNullOrEmpty ()
? "'servicePath' must not be null or empty."
: null;
if (msg != null)
{
_logger.Error (msg);
return false;
}
IServiceHost host;
if (!TryGetServiceHost (servicePath, out host))
{
_logger.Error ("The WebSocket service with the specified path not found.\npath: " + servicePath);
return false;
}
return host.SendTo (id, data);
}
///
/// Sends a text data to the client associated with the specified and
/// .
///
///
/// true if is successfully sent; otherwise, false.
///
///
/// A that contains a text data to send.
///
///
/// A that contains an ID that represents the destination for the data.
///
///
/// A that contains an absolute path to the WebSocket service to find.
///
public bool SendTo (string data, string id, string servicePath)
{
var msg = data == null
? "'data' must not be null."
: id.IsNullOrEmpty ()
? "'id' must not be null or empty."
: servicePath.IsNullOrEmpty ()
? "'servicePath' must not be null or empty."
: null;
if (msg != null)
{
_logger.Error (msg);
return false;
}
IServiceHost host;
if (!TryGetServiceHost (servicePath, out host))
{
_logger.Error ("The WebSocket service with the specified path not found.\npath: " + servicePath);
return false;
}
return host.SendTo (id, data);
}
#endregion
}
}