Renamed ServiceHostManager.cs to WebSocketServiceHostManager.cs

This commit is contained in:
sta 2013-08-28 15:49:52 +09:00
parent cc81fd340c
commit b3fa68d8ce
8 changed files with 635 additions and 777 deletions

View File

@ -31,16 +31,11 @@ namespace Example3
onGet (e);
};
_httpsv.OnError += (sender, e) =>
{
Console.WriteLine (e.Message);
};
_httpsv.Start ();
if (_httpsv.IsListening)
{
Console.WriteLine ("HTTP Server listening on port: {0} service path:", _httpsv.Port);
foreach (var path in _httpsv.ServicePaths)
foreach (var path in _httpsv.WebSocketServices.ServicePaths)
Console.WriteLine (" {0}", path);
Console.WriteLine ();

View File

@ -46,15 +46,15 @@ namespace WebSocketSharp.Server
{
#region Private Fields
private HttpListener _listener;
private bool _listening;
private Logger _logger;
private int _port;
private Thread _receiveRequestThread;
private string _rootPath;
private bool _secure;
private ServiceHostManager _serviceHosts;
private bool _windows;
private HttpListener _listener;
private bool _listening;
private Logger _logger;
private int _port;
private Thread _receiveRequestThread;
private string _rootPath;
private bool _secure;
private WebSocketServiceHostManager _serviceHosts;
private bool _windows;
#endregion
@ -133,10 +133,7 @@ namespace WebSocketSharp.Server
set {
if (_listening)
{
var msg = "The value of Certificate property cannot be changed because the server has already been started.";
_logger.Error (msg);
error (msg);
_logger.Error ("The value of Certificate property cannot be changed because the server has already been started.");
return;
}
@ -235,10 +232,7 @@ namespace WebSocketSharp.Server
set {
if (_listening)
{
var msg = "The value of RootPath property cannot be changed because the server has already been started.";
_logger.Error (msg);
error (msg);
_logger.Error ("The value of RootPath property cannot be changed because the server has already been started.");
return;
}
@ -247,14 +241,14 @@ namespace WebSocketSharp.Server
}
/// <summary>
/// Gets the collection of paths associated with the every WebSocket services that the server provides.
/// Gets the functions for the WebSocket services that the server provides.
/// </summary>
/// <value>
/// An IEnumerable&lt;string&gt; that contains the collection of paths.
/// A <see cref="WebSocketServiceHostManager"/> that manages the WebSocket services.
/// </value>
public IEnumerable<string> ServicePaths {
public WebSocketServiceHostManager WebSocketServices {
get {
return _serviceHosts.Paths;
return _serviceHosts;
}
}
@ -272,11 +266,6 @@ namespace WebSocketSharp.Server
/// </summary>
public event EventHandler<HttpRequestEventArgs> OnDelete;
/// <summary>
/// Occurs when the server gets an error.
/// </summary>
public event EventHandler<ErrorEventArgs> OnError;
/// <summary>
/// Occurs when the server receives an HTTP GET request.
/// </summary>
@ -316,17 +305,12 @@ namespace WebSocketSharp.Server
#region Private Methods
private void error (string message)
{
OnError.Emit (this, new ErrorEventArgs (message));
}
private void init ()
{
_listener = new HttpListener ();
_listening = false;
_logger = new Logger ();
_serviceHosts = new ServiceHostManager (_logger);
_serviceHosts = new WebSocketServiceHostManager (_logger);
_windows = false;
var os = Environment.OSVersion;
@ -435,7 +419,6 @@ namespace WebSocketSharp.Server
}
catch (Exception ex) {
_logger.Fatal (ex.Message);
error ("An exception has occured.");
}
};
@ -455,8 +438,6 @@ namespace WebSocketSharp.Server
}
catch (Exception ex) {
_logger.Fatal (ex.Message);
error ("An exception has occured.");
break;
}
}
@ -476,10 +457,8 @@ namespace WebSocketSharp.Server
var data = code.Append (reason);
if (data.Length > 125)
{
var msg = "The payload length of a Close frame must be 125 bytes or less.";
_logger.Error (String.Format ("{0}\ncode: {1}\nreason: {2}", msg, code, reason));
error (msg);
_logger.Error (
String.Format ("The payload length of a Close frame must be 125 bytes or less.\ncode: {0}\nreason: {1}", code, reason));
return;
}
}
@ -514,8 +493,6 @@ namespace WebSocketSharp.Server
if (!servicePath.IsValidAbsolutePath (out msg))
{
_logger.Error (msg);
error (msg);
return;
}
@ -561,10 +538,7 @@ namespace WebSocketSharp.Server
{
if (servicePath.IsNullOrEmpty ())
{
var msg = "'servicePath' must not be null or empty.";
_logger.Error (msg);
error (msg);
_logger.Error ("'servicePath' must not be null or empty.");
return false;
}
@ -584,10 +558,7 @@ namespace WebSocketSharp.Server
Certificate == null
)
{
var msg = "Secure connection requires a server certificate.";
_logger.Error (msg);
error (msg);
_logger.Error ("Secure connection requires a server certificate.");
return;
}
@ -624,10 +595,7 @@ namespace WebSocketSharp.Server
if (!code.IsCloseStatusCode ())
{
var msg = "Invalid status code for stop.";
_logger.Error (String.Format ("{0}\ncode: {1}", msg, code));
error (msg);
_logger.Error ("Invalid status code for stop.\ncode: " + code);
return;
}

View File

@ -1,306 +0,0 @@
#region License
/*
* ServiceHostManager.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;
namespace WebSocketSharp.Server
{
internal class ServiceHostManager
{
#region Private Fields
private volatile bool _keepClean;
private Logger _logger;
private Dictionary<string, IServiceHost> _serviceHosts;
private object _sync;
#endregion
#region Public Constructors
public ServiceHostManager ()
: this (new Logger ())
{
}
public ServiceHostManager (Logger logger)
{
_logger = logger;
_keepClean = true;
_serviceHosts = new Dictionary<string, IServiceHost> ();
_sync = new object ();
}
#endregion
#region Public Properties
public int ConnectionCount {
get {
var count = 0;
foreach (var host in ServiceHosts)
count += host.ConnectionCount;
return count;
}
}
public int Count {
get {
lock (_sync)
{
return _serviceHosts.Count;
}
}
}
public bool KeepClean {
get {
return _keepClean;
}
set {
lock (_sync)
{
if (_keepClean ^ value)
{
_keepClean = value;
foreach (var host in _serviceHosts.Values)
host.KeepClean = value;
}
}
}
}
public IEnumerable<string> Paths {
get {
lock (_sync)
{
return _serviceHosts.Keys;
}
}
}
public IEnumerable<IServiceHost> ServiceHosts {
get {
lock (_sync)
{
return _serviceHosts.Values;
}
}
}
#endregion
#region Private Methods
private Dictionary<string, IServiceHost> copy ()
{
lock (_sync)
{
return new Dictionary<string, IServiceHost> (_serviceHosts);
}
}
#endregion
#region Public Methods
public 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 found.\npath: " + servicePath);
return;
}
_serviceHosts.Add (servicePath.UrlDecode (), serviceHost);
}
}
public void Broadcast (byte [] data)
{
foreach (var host in ServiceHosts)
host.Broadcast (data);
}
public void Broadcast (string data)
{
foreach (var host in ServiceHosts)
host.Broadcast (data);
}
public bool BroadcastTo (string servicePath, byte [] data)
{
IServiceHost host;
if (TryGetServiceHost (servicePath, out host))
{
host.Broadcast (data);
return true;
}
_logger.Error (
"The WebSocket service host with the specified path not found.\npath: " + servicePath);
return false;
}
public bool BroadcastTo (string servicePath, string data)
{
IServiceHost host;
if (TryGetServiceHost (servicePath, out host))
{
host.Broadcast (data);
return true;
}
_logger.Error (
"The WebSocket service host with the specified path not found.\npath: " + servicePath);
return false;
}
public Dictionary<string, Dictionary<string, bool>> Broadping (string message)
{
var result = new Dictionary<string, Dictionary<string, bool>> ();
foreach (var service in copy ())
result.Add (service.Key, service.Value.Broadping (message));
return result;
}
public Dictionary<string, bool> BroadpingTo (string servicePath, string message)
{
IServiceHost host;
if (TryGetServiceHost (servicePath, out host))
return host.Broadping (message);
_logger.Error (
"The WebSocket service host with the specified path not found.\npath: " + servicePath);
return null;
}
public int GetConnectionCount (string servicePath)
{
IServiceHost host;
if (TryGetServiceHost (servicePath, out host))
return host.ConnectionCount;
_logger.Error (
"The WebSocket service host with the specified path not found.\npath: " + servicePath);
return -1;
}
public bool PingTo (string servicePath, string id, string message)
{
IServiceHost host;
if (TryGetServiceHost (servicePath, out host))
return host.PingTo (id, message);
_logger.Error (
"The WebSocket service host with the specified path not found.\npath: " + servicePath);
return false;
}
public 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;
}
public bool SendTo (string servicePath, string id, byte [] data)
{
IServiceHost host;
if (TryGetServiceHost (servicePath, out host))
return host.SendTo (id, data);
_logger.Error (
"The WebSocket service host with the specified path not found.\npath: " + servicePath);
return false;
}
public bool SendTo (string servicePath, string id, string data)
{
IServiceHost host;
if (TryGetServiceHost (servicePath, out host))
return host.SendTo (id, data);
_logger.Error (
"The WebSocket service host with the specified path not found.\npath: " + servicePath);
return false;
}
public void Stop ()
{
lock (_sync)
{
foreach (var host in _serviceHosts.Values)
host.Stop ();
_serviceHosts.Clear ();
}
}
public void Stop (ushort code, string reason)
{
lock (_sync)
{
foreach (var host in _serviceHosts.Values)
host.Stop (code, reason);
_serviceHosts.Clear ();
}
}
public bool TryGetServiceHost (string servicePath, out IServiceHost serviceHost)
{
lock (_sync)
{
return _serviceHosts.TryGetValue (servicePath, out serviceHost);
}
}
#endregion
}
}

View File

@ -47,7 +47,7 @@ namespace WebSocketSharp.Server
{
#region Private Fields
private ServiceHostManager _serviceHosts;
private WebSocketServiceHostManager _serviceHosts;
#endregion
@ -86,7 +86,7 @@ namespace WebSocketSharp.Server
if (BaseUri.AbsolutePath != "/")
throw new ArgumentException ("Must not contain the path component: " + url, "url");
_serviceHosts = new ServiceHostManager (Log);
_serviceHosts = new WebSocketServiceHostManager (Log);
}
/// <summary>
@ -138,25 +138,13 @@ namespace WebSocketSharp.Server
public WebSocketServer (System.Net.IPAddress address, int port, bool secure)
: base (address, port, "/", secure)
{
_serviceHosts = new ServiceHostManager (Log);
_serviceHosts = new WebSocketServiceHostManager (Log);
}
#endregion
#region Public Properties
/// <summary>
/// Gets the connection count to the <see cref="WebSocketServer"/>.
/// </summary>
/// <value>
/// An <see cref="int"/> that contains the connection count.
/// </value>
public int ConnectionCount {
get {
return _serviceHosts.ConnectionCount;
}
}
/// <summary>
/// Gets or sets a value indicating whether the server cleans up the inactive WebSocket service
/// instances periodically.
@ -176,7 +164,7 @@ namespace WebSocketSharp.Server
}
/// <summary>
/// Gets the collection of paths associated with the every WebSocket services that the server provides.
/// Gets the collection of paths to the WebSocket services that the server provides.
/// </summary>
/// <value>
/// An IEnumerable&lt;string&gt; that contains the collection of paths.
@ -187,11 +175,23 @@ namespace WebSocketSharp.Server
? BaseUri.ToString ().TrimEnd ('/')
: String.Empty;
foreach (var path in _serviceHosts.Paths)
foreach (var path in _serviceHosts.ServicePaths)
yield return url + path;
}
}
/// <summary>
/// Gets the functions for the WebSocket services that the server provides.
/// </summary>
/// <value>
/// A <see cref="WebSocketServiceHostManager"/> that manages the WebSocket services.
/// </value>
public WebSocketServiceHostManager WebSocketServices {
get {
return _serviceHosts;
}
}
#endregion
#region Private Methods
@ -201,10 +201,8 @@ namespace WebSocketSharp.Server
var data = code.Append (reason);
if (data.Length > 125)
{
var msg = "The payload length of a Close frame must be 125 bytes or less.";
Log.Error (String.Format ("{0}\ncode: {1}\nreason: {2}", msg, code, reason));
Error (msg);
Log.Error (String.Format (
"The payload length of a Close frame must be 125 bytes or less.\ncode: {0}\nreason: {1}", code, reason));
return;
}
@ -224,19 +222,19 @@ namespace WebSocketSharp.Server
/// </param>
protected override void AcceptWebSocket (TcpListenerWebSocketContext context)
{
var ws = context.WebSocket;
var websocket = context.WebSocket;
var path = context.Path.UrlDecode ();
ws.Log = Log;
websocket.Log = Log;
IServiceHost host;
if (!_serviceHosts.TryGetServiceHost (path, out host))
{
ws.Close (HttpStatusCode.NotImplemented);
websocket.Close (HttpStatusCode.NotImplemented);
return;
}
if (BaseUri.IsAbsoluteUri)
ws.Url = new Uri (BaseUri, path);
websocket.Url = new Uri (BaseUri, path);
host.BindWebSocket (context);
}
@ -261,8 +259,6 @@ namespace WebSocketSharp.Server
if (!servicePath.IsValidAbsolutePath (out msg))
{
Log.Error (msg);
Error (msg);
return;
}
@ -277,241 +273,6 @@ namespace WebSocketSharp.Server
_serviceHosts.Add (servicePath, host);
}
/// <summary>
/// Broadcasts the specified array of <see cref="byte"/> to all clients.
/// </summary>
/// <param name="data">
/// An array of <see cref="byte"/> to broadcast.
/// </param>
public void Broadcast (byte [] data)
{
if (data == null)
{
var msg = "'data' must not be null.";
Log.Error (msg);
Error (msg);
return;
}
_serviceHosts.Broadcast (data);
}
/// <summary>
/// Broadcasts the specified <see cref="string"/> to all clients.
/// </summary>
/// <param name="data">
/// A <see cref="string"/> to broadcast.
/// </param>
public void Broadcast (string data)
{
if (data == null)
{
var msg = "'data' must not be null.";
Log.Error (msg);
Error (msg);
return;
}
_serviceHosts.Broadcast (data);
}
/// <summary>
/// Broadcasts the specified array of <see cref="byte"/> to all clients of the WebSocket service
/// with the specified <paramref name="servicePath"/>.
/// </summary>
/// <returns>
/// <c>true</c> if the WebSocket service is found; otherwise, <c>false</c>.
/// </returns>
/// <param name="servicePath">
/// A <see cref="string"/> that contains an absolute path to the WebSocket service to find.
/// </param>
/// <param name="data">
/// An array of <see cref="byte"/> to broadcast.
/// </param>
public bool BroadcastTo (string servicePath, byte [] data)
{
var msg = servicePath.IsNullOrEmpty ()
? "'servicePath' must not be null or empty."
: data == null
? "'data' must not be null."
: String.Empty;
if (msg.Length > 0)
{
Log.Error (msg);
Error (msg);
return false;
}
return _serviceHosts.BroadcastTo (servicePath, data);
}
/// <summary>
/// Broadcasts the specified <see cref="string"/> to all clients of the WebSocket service
/// with the specified <paramref name="servicePath"/>.
/// </summary>
/// <returns>
/// <c>true</c> if the WebSocket service is found; otherwise, <c>false</c>.
/// </returns>
/// <param name="servicePath">
/// A <see cref="string"/> that contains an absolute path to the WebSocket service to find.
/// </param>
/// <param name="data">
/// A <see cref="string"/> to broadcast.
/// </param>
public bool BroadcastTo (string servicePath, string data)
{
var msg = servicePath.IsNullOrEmpty ()
? "'servicePath' must not be null or empty."
: data == null
? "'data' must not be null."
: String.Empty;
if (msg.Length > 0)
{
Log.Error (msg);
Error (msg);
return false;
}
return _serviceHosts.BroadcastTo (servicePath, data);
}
/// <summary>
/// Sends Pings with the specified <see cref="string"/> to all clients.
/// </summary>
/// <returns>
/// A Dictionary&lt;string, Dictionary&lt;string, bool&gt;&gt; that contains the collection of
/// service paths and pairs of ID and value indicating whether the <see cref="WebSocketServer"/>
/// received the Pongs from each clients in a time.
/// </returns>
/// <param name="message">
/// A <see cref="string"/> that contains a message to send.
/// </param>
public Dictionary<string, Dictionary<string, bool>> Broadping (string message)
{
if (message.IsNullOrEmpty ())
return _serviceHosts.Broadping (String.Empty);
var len = Encoding.UTF8.GetBytes (message).Length;
if (len > 125)
{
var msg = "The payload length of a Ping frame must be 125 bytes or less.";
Log.Error (msg);
Error (msg);
return null;
}
return _serviceHosts.Broadping (message);
}
/// <summary>
/// Sends Pings with the specified <see cref="string"/> to all clients of the WebSocket service
/// with the specified <paramref name="servicePath"/>.
/// </summary>
/// <returns>
/// A Dictionary&lt;string, bool&gt; that contains the collection of session IDs and values
/// indicating whether the <see cref="WebSocketServer"/> received the Pongs from each clients
/// in a time. If the WebSocket service is not found, returns <see langword="null"/>.
/// </returns>
/// <param name="servicePath">
/// A <see cref="string"/> that contains an absolute path to the WebSocket service to find.
/// </param>
/// <param name="message">
/// A <see cref="string"/> that contains a message to send.
/// </param>
public Dictionary<string, bool> BroadpingTo (string servicePath, string message)
{
if (message == null)
message = String.Empty;
var msg = servicePath.IsNullOrEmpty ()
? "'servicePath' must not be null or empty."
: Encoding.UTF8.GetBytes (message).Length > 125
? "The payload length of a Ping frame must be 125 bytes or less."
: String.Empty;
if (msg.Length > 0)
{
Log.Error (msg);
Error (msg);
return null;
}
return _serviceHosts.BroadpingTo (servicePath, message);
}
/// <summary>
/// Gets the connection count to the WebSocket service with the specified <paramref name="servicePath"/>.
/// </summary>
/// <returns>
/// An <see cref="int"/> that contains the connection count if the WebSocket service is successfully found;
/// otherwise, <c>-1</c>.
/// </returns>
/// <param name="servicePath">
/// A <see cref="string"/> that contains an absolute path to the WebSocket service to find.
/// </param>
public int GetConnectionCount (string servicePath)
{
if (servicePath.IsNullOrEmpty ())
{
var msg = "'servicePath' must not be null or empty.";
Log.Error (msg);
Error (msg);
return -1;
}
return _serviceHosts.GetConnectionCount (servicePath);
}
/// <summary>
/// Sends a Ping with the specified <see cref="string"/> to the client associated with
/// the specified <paramref name="servicePath"/> and <paramref name="id"/>.
/// </summary>
/// <returns>
/// <c>true</c> if the <see cref="WebSocketServer"/> receives a Pong from the client in a time;
/// otherwise, <c>false</c>.
/// </returns>
/// <param name="servicePath">
/// A <see cref="string"/> that contains an absolute path to the WebSocket service to find.
/// </param>
/// <param name="id">
/// A <see cref="string"/> that contains an ID that represents the destination for the Ping.
/// </param>
/// <param name="message">
/// A <see cref="string"/> that contains a message to send.
/// </param>
public bool PingTo (string servicePath, string id, string message)
{
if (message == null)
message = String.Empty;
var msg = servicePath.IsNullOrEmpty ()
? "'servicePath' must not be null or empty."
: id.IsNullOrEmpty ()
? "'id' must not be null or empty."
: Encoding.UTF8.GetBytes (message).Length > 125
? "The payload length of a Ping frame must be 125 bytes or less."
: String.Empty;
if (msg.Length > 0)
{
Log.Error (msg);
Error (msg);
return false;
}
return _serviceHosts.PingTo (servicePath, id, message);
}
/// <summary>
/// Removes the WebSocket service with the specified <paramref name="servicePath"/>.
/// </summary>
@ -525,90 +286,13 @@ namespace WebSocketSharp.Server
{
if (servicePath.IsNullOrEmpty ())
{
var msg = "'servicePath' must not be null or empty.";
Log.Error (msg);
Error (msg);
Log.Error ("'servicePath' must not be null or empty.");
return false;
}
return _serviceHosts.Remove (servicePath);
}
/// <summary>
/// Sends a binary data to the client associated with the specified <paramref name="servicePath"/> and
/// <paramref name="id"/>.
/// </summary>
/// <returns>
/// <c>true</c> if the client is successfully found; otherwise, <c>false</c>.
/// </returns>
/// <param name="servicePath">
/// A <see cref="string"/> that contains an absolute path to the WebSocket service to find.
/// </param>
/// <param name="id">
/// A <see cref="string"/> that contains an ID that represents the destination for the data.
/// </param>
/// <param name="data">
/// An array of <see cref="byte"/> that contains a binary data to send.
/// </param>
public bool SendTo (string servicePath, string id, byte [] data)
{
var msg = servicePath.IsNullOrEmpty ()
? "'servicePath' must not be null or empty."
: id.IsNullOrEmpty ()
? "'id' must not be null or empty."
: data == null
? "'data' must not be null."
: String.Empty;
if (msg.Length > 0)
{
Log.Error (msg);
Error (msg);
return false;
}
return _serviceHosts.SendTo (servicePath, id, data);
}
/// <summary>
/// Sends a text data to the client associated with the specified <paramref name="servicePath"/> and
/// <paramref name="id"/>.
/// </summary>
/// <returns>
/// <c>true</c> if the client is successfully found; otherwise, <c>false</c>.
/// </returns>
/// <param name="servicePath">
/// A <see cref="string"/> that contains an absolute path to the WebSocket service to find.
/// </param>
/// <param name="id">
/// A <see cref="string"/> that contains an ID that represents the destination for the data.
/// </param>
/// <param name="data">
/// A <see cref="string"/> that contains a text data to send.
/// </param>
public bool SendTo (string servicePath, string id, string data)
{
var msg = servicePath.IsNullOrEmpty ()
? "'servicePath' must not be null or empty."
: id.IsNullOrEmpty ()
? "'id' must not be null or empty."
: data == null
? "'data' must not be null."
: String.Empty;
if (msg.Length > 0)
{
Log.Error (msg);
Error (msg);
return false;
}
return _serviceHosts.SendTo (servicePath, id, data);
}
/// <summary>
/// Stops receiving the WebSocket connection requests.
/// </summary>
@ -632,10 +316,7 @@ namespace WebSocketSharp.Server
{
if (!code.IsCloseStatusCode ())
{
var msg = "Invalid status code for stop.";
Log.Error (String.Format ("{0}\ncode: {1}", msg, code));
Error (msg);
Log.Error ("Invalid status code for stop.\ncode: " + code);
return;
}

View File

@ -303,22 +303,8 @@ namespace WebSocketSharp.Server
#endregion
#region Public Events
/// <summary>
/// Occurs when the server gets an error.
/// </summary>
public event EventHandler<ErrorEventArgs> OnError;
#endregion
#region Private Methods
private void error (string message)
{
OnError.Emit (this, new ErrorEventArgs (message));
}
private void init ()
{
_listening = false;
@ -352,7 +338,6 @@ namespace WebSocketSharp.Server
{
client.Close ();
_logger.Fatal (ex.Message);
error ("An exception has occured.");
}
};
@ -372,8 +357,6 @@ namespace WebSocketSharp.Server
}
catch (Exception ex) {
_logger.Fatal (ex.Message);
error ("An exception has occured.");
break;
}
}
@ -414,20 +397,6 @@ namespace WebSocketSharp.Server
/// </param>
protected abstract void AcceptWebSocket (TcpListenerWebSocketContext context);
/// <summary>
/// Occurs the <see cref="WebSocketServerBase.OnError"/> event with the specified <see cref="string"/>.
/// </summary>
/// <param name="message">
/// A <see cref="string"/> that contains an error message.
/// </param>
protected virtual void Error (string message)
{
if (message.IsNullOrEmpty ())
return;
error (message);
}
#endregion
#region Public Methods
@ -442,10 +411,7 @@ namespace WebSocketSharp.Server
if (_secure && _cert == null)
{
var msg = "Secure connection requires a server certificate.";
_logger.Error (msg);
error (msg);
_logger.Error ("Secure connection requires a server certificate.");
return;
}

View File

@ -246,10 +246,8 @@ namespace WebSocketSharp.Server
var data = code.Append (reason);
if (data.Length > 125)
{
var msg = "The payload length of a Close frame must be 125 bytes or less.";
Log.Error (String.Format ("{0}\ncode: {1}\nreason: {2}", msg, code, reason));
Error (msg);
Log.Error (String.Format (
"The payload length of a Close frame must be 125 bytes or less.\ncode: {0}\nreason: {1}", code, reason));
return;
}
@ -299,10 +297,7 @@ namespace WebSocketSharp.Server
{
if (data == null)
{
var msg = "'data' must not be null.";
Log.Error (msg);
Error (msg);
Log.Error ("'data' must not be null.");
return;
}
@ -319,10 +314,7 @@ namespace WebSocketSharp.Server
{
if (data == null)
{
var msg = "'data' must not be null.";
Log.Error (msg);
Error (msg);
Log.Error ("'data' must not be null.");
return;
}
@ -347,10 +339,7 @@ namespace WebSocketSharp.Server
var len = Encoding.UTF8.GetBytes (message).Length;
if (len > 125)
{
var msg = "The payload length of a Ping frame must be 125 bytes or less.";
Log.Error (msg);
Error (msg);
Log.Error ("The payload length of a Ping frame must be 125 bytes or less.");
return null;
}
@ -379,13 +368,11 @@ namespace WebSocketSharp.Server
? "'id' must not be null or empty."
: Encoding.UTF8.GetBytes (message).Length > 125
? "The payload length of a Ping frame must be 125 bytes or less."
: String.Empty;
: null;
if (msg.Length > 0)
if (msg != null)
{
Log.Error (msg);
Error (msg);
return false;
}
@ -411,13 +398,11 @@ namespace WebSocketSharp.Server
? "'id' must not be null or empty."
: data == null
? "'data' must not be null."
: String.Empty;
: null;
if (msg.Length > 0)
if (msg != null)
{
Log.Error (msg);
Error (msg);
return false;
}
@ -443,13 +428,11 @@ namespace WebSocketSharp.Server
? "'id' must not be null or empty."
: data == null
? "'data' must not be null."
: String.Empty;
: null;
if (msg.Length > 0)
if (msg != null)
{
Log.Error (msg);
Error (msg);
return false;
}
@ -479,10 +462,7 @@ namespace WebSocketSharp.Server
{
if (!code.IsCloseStatusCode ())
{
var msg = "Invalid status code for stop.";
Log.Error (String.Format ("{0}\ncode: {1}", msg, code));
Error (msg);
Log.Error ("Invalid status code for stop.\ncode: " + code);
return;
}

View File

@ -0,0 +1,574 @@
#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
{
/// <summary>
/// Manages the collection of the WebSocket service hosts.
/// </summary>
public class WebSocketServiceHostManager
{
#region Private Fields
private volatile bool _keepClean;
private Logger _logger;
private Dictionary<string, IServiceHost> _serviceHosts;
private object _sync;
#endregion
#region Internal Constructors
internal WebSocketServiceHostManager ()
: this (new Logger ())
{
}
internal WebSocketServiceHostManager (Logger logger)
{
_logger = logger;
_keepClean = true;
_serviceHosts = new Dictionary<string, IServiceHost> ();
_sync = new object ();
}
#endregion
#region Public Properties
/// <summary>
/// Gets the connection count to the WebSocket services managed by the <see cref="WebSocketServiceHostManager"/>.
/// </summary>
/// <value>
/// An <see cref="int"/> that contains the connection count.
/// </value>
public int ConnectionCount {
get {
var count = 0;
foreach (var host in ServiceHosts)
count += host.ConnectionCount;
return count;
}
}
/// <summary>
/// Gets the number of the WebSocket services managed by the <see cref="WebSocketServiceHostManager"/>.
/// </summary>
/// <value>
/// An <see cref="int"/> that contains the number of the WebSocket services.
/// </value>
public int ServiceCount {
get {
lock (_sync)
{
return _serviceHosts.Count;
}
}
}
/// <summary>
/// Gets the collection of paths to the WebSocket services managed by the <see cref="WebSocketServiceHostManager"/>.
/// </summary>
/// <value>
/// An IEnumerable&lt;string&gt; that contains the collection of paths.
/// </value>
public IEnumerable<string> 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<IServiceHost> ServiceHosts {
get {
lock (_sync)
{
return _serviceHosts.Values;
}
}
}
#endregion
#region Private Methods
private Dictionary<string, IServiceHost> copy ()
{
lock (_sync)
{
return new Dictionary<string, IServiceHost> (_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
/// <summary>
/// Broadcasts the specified array of <see cref="byte"/> to all clients of the WebSocket services.
/// </summary>
/// <param name="data">
/// An array of <see cref="byte"/> to broadcast.
/// </param>
public void Broadcast (byte [] data)
{
if (data == null)
{
_logger.Error ("'data' must not be null.");
return;
}
foreach (var host in ServiceHosts)
host.Broadcast (data);
}
/// <summary>
/// Broadcasts the specified <see cref="string"/> to all clients of the WebSocket services.
/// </summary>
/// <param name="data">
/// A <see cref="string"/> to broadcast.
/// </param>
public void Broadcast (string data)
{
if (data == null)
{
_logger.Error ("'data' must not be null.");
return;
}
foreach (var host in ServiceHosts)
host.Broadcast (data);
}
/// <summary>
/// Broadcasts the specified array of <see cref="byte"/> to all clients of the WebSocket service
/// with the specified <paramref name="servicePath"/>.
/// </summary>
/// <returns>
/// <c>true</c> if <paramref name="data"/> is broadcasted; otherwise, <c>false</c>.
/// </returns>
/// <param name="data">
/// An array of <see cref="byte"/> to broadcast.
/// </param>
/// <param name="servicePath">
/// A <see cref="string"/> that contains an absolute path to the WebSocket service to find.
/// </param>
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;
}
/// <summary>
/// Broadcasts the specified <see cref="string"/> to all clients of the WebSocket service
/// with the specified <paramref name="servicePath"/>.
/// </summary>
/// <returns>
/// <c>true</c> if <paramref name="data"/> is broadcasted; otherwise, <c>false</c>.
/// </returns>
/// <param name="data">
/// A <see cref="string"/> to broadcast.
/// </param>
/// <param name="servicePath">
/// A <see cref="string"/> that contains an absolute path to the WebSocket service to find.
/// </param>
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;
}
/// <summary>
/// Sends Pings with the specified <paramref name="message"/> to all clients of the WebSocket services.
/// </summary>
/// <returns>
/// A Dictionary&lt;string, Dictionary&lt;string, bool&gt;&gt; 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.
/// </returns>
/// <param name="message">
/// A <see cref="string"/> that contains a message to send.
/// </param>
public Dictionary<string, Dictionary<string, bool>> 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<string, Dictionary<string, bool>> ();
foreach (var service in copy ())
result.Add (service.Key, service.Value.Broadping (message));
return result;
}
/// <summary>
/// Sends Pings with the specified <paramref name="message"/> to all clients of the WebSocket service
/// with the specified <paramref name="servicePath"/>.
/// </summary>
/// <returns>
/// A Dictionary&lt;string, bool&gt; 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 <see langword="null"/>.
/// </returns>
/// <param name="message">
/// A <see cref="string"/> that contains a message to send.
/// </param>
/// <param name="servicePath">
/// A <see cref="string"/> that contains an absolute path to the WebSocket service to find.
/// </param>
public Dictionary<string, bool> 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);
}
/// <summary>
/// Gets the connection count to the WebSocket service with the specified <paramref name="servicePath"/>.
/// </summary>
/// <returns>
/// An <see cref="int"/> that contains the connection count if the WebSocket service is successfully found;
/// otherwise, <c>-1</c>.
/// </returns>
/// <param name="servicePath">
/// A <see cref="string"/> that contains an absolute path to the WebSocket service to find.
/// </param>
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;
}
/// <summary>
/// Sends a Ping with the specified <paramref name="message"/> to the client associated with
/// the specified <paramref name="id"/> and <paramref name="servicePath"/>.
/// </summary>
/// <returns>
/// <c>true</c> if the WebSocket service with <paramref name="servicePath"/> receives a Pong
/// from the client in a time; otherwise, <c>false</c>.
/// </returns>
/// <param name="message">
/// A <see cref="string"/> that contains a message to send.
/// </param>
/// <param name="id">
/// A <see cref="string"/> that contains an ID that represents the destination for the Ping.
/// </param>
/// <param name="servicePath">
/// A <see cref="string"/> that contains an absolute path to the WebSocket service to find.
/// </param>
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);
}
/// <summary>
/// Sends a binary data to the client associated with the specified <paramref name="id"/> and
/// <paramref name="servicePath"/>.
/// </summary>
/// <returns>
/// <c>true</c> if <paramref name="data"/> is successfully sent; otherwise, <c>false</c>.
/// </returns>
/// <param name="data">
/// An array of <see cref="byte"/> that contains a binary data to send.
/// </param>
/// <param name="id">
/// A <see cref="string"/> that contains an ID that represents the destination for the data.
/// </param>
/// <param name="servicePath">
/// A <see cref="string"/> that contains an absolute path to the WebSocket service to find.
/// </param>
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);
}
/// <summary>
/// Sends a text data to the client associated with the specified <paramref name="id"/> and
/// <paramref name="servicePath"/>.
/// </summary>
/// <returns>
/// <c>true</c> if <paramref name="data"/> is successfully sent; otherwise, <c>false</c>.
/// </returns>
/// <param name="data">
/// A <see cref="string"/> that contains a text data to send.
/// </param>
/// <param name="id">
/// A <see cref="string"/> that contains an ID that represents the destination for the data.
/// </param>
/// <param name="servicePath">
/// A <see cref="string"/> that contains an absolute path to the WebSocket service to find.
/// </param>
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
}
}

View File

@ -112,7 +112,6 @@
<Compile Include="Net\WebSockets\HttpListenerWebSocketContext.cs" />
<Compile Include="Net\WebSockets\TcpListenerWebSocketContext.cs" />
<Compile Include="Net\WebSockets\WebSocketContext.cs" />
<Compile Include="Server\ServiceHostManager.cs" />
<Compile Include="Server\WebSocketServiceManager.cs" />
<Compile Include="Server\HttpRequestEventArgs.cs" />
<Compile Include="Net\HttpHeaderType.cs" />
@ -128,6 +127,7 @@
<Compile Include="HandshakeBase.cs" />
<Compile Include="HandshakeRequest.cs" />
<Compile Include="HandshakeResponse.cs" />
<Compile Include="Server\WebSocketServiceHostManager.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>