websocket-sharp/websocket-sharp/Server/HttpServer.cs

814 lines
24 KiB
C#
Raw Normal View History

2013-05-07 01:19:31 +08:00
#region License
2013-01-11 19:32:38 +08:00
/*
2012-09-10 00:36:22 +08:00
* HttpServer.cs
*
* A simple HTTP server that allows to accept the WebSocket connection requests.
*
2012-09-10 00:36:22 +08:00
* The MIT License
*
* Copyright (c) 2012-2015 sta.blockhead
*
2012-09-10 00:36:22 +08:00
* 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.
*
2012-09-10 00:36:22 +08:00
* 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
2013-10-28 14:34:21 +08:00
#region Contributors
/*
2013-10-28 14:34:21 +08:00
* Contributors:
2014-08-31 13:44:10 +08:00
* - Juan Manuel Lallana <juan.manuel.lallana@gmail.com>
2014-10-30 18:09:12 +08:00
* - Liryna <liryna.stark@gmail.com>
*/
#endregion
2012-09-10 00:36:22 +08:00
using System;
using System.Collections.Generic;
using System.Diagnostics;
2012-09-10 00:36:22 +08:00
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Security.Principal;
2012-09-10 00:36:22 +08:00
using System.Threading;
using WebSocketSharp.Net;
using WebSocketSharp.Net.WebSockets;
2012-09-10 00:36:22 +08:00
namespace WebSocketSharp.Server
{
/// <summary>
/// Provides a simple HTTP server that allows to accept the WebSocket connection requests.
/// </summary>
/// <remarks>
/// The HttpServer class can provide multiple WebSocket services.
/// </remarks>
public class HttpServer
{
2013-05-07 01:19:31 +08:00
#region Private Fields
2012-09-10 00:36:22 +08:00
private HttpListener _listener;
private Logger _logger;
private int _port;
2014-11-25 11:30:18 +08:00
private Thread _receiveThread;
private string _rootPath;
private bool _secure;
private WebSocketServiceManager _services;
private volatile ServerState _state;
private object _sync;
private bool _windows;
2012-09-10 00:36:22 +08:00
#endregion
2013-05-07 01:19:31 +08:00
#region Public Constructors
2012-09-10 00:36:22 +08:00
/// <summary>
/// Initializes a new instance of the <see cref="HttpServer"/> class.
/// </summary>
/// <remarks>
/// An instance initialized by this constructor listens for the incoming requests on port 80.
/// </remarks>
public HttpServer ()
2012-09-10 00:36:22 +08:00
{
2015-06-29 15:20:00 +08:00
init ("*", 80, false);
2012-09-10 00:36:22 +08:00
}
/// <summary>
2015-06-29 15:20:00 +08:00
/// Initializes a new instance of the <see cref="HttpServer"/> class with
/// the specified <paramref name="port"/>.
/// </summary>
/// <remarks>
/// <para>
2015-06-29 15:20:00 +08:00
/// An instance initialized by this constructor listens for the incoming
/// requests on <paramref name="port"/>.
/// </para>
/// <para>
2014-08-31 13:44:10 +08:00
/// If <paramref name="port"/> is 443, that instance provides a secure connection.
/// </para>
/// </remarks>
/// <param name="port">
/// An <see cref="int"/> that represents the port number on which to listen.
/// </param>
/// <exception cref="ArgumentOutOfRangeException">
2015-06-29 15:20:00 +08:00
/// <paramref name="port"/> isn't between 1 and 65535 inclusive.
/// </exception>
public HttpServer (int port)
: this (port, port == 443)
2012-09-10 00:36:22 +08:00
{
}
/// <summary>
2015-06-29 15:20:00 +08:00
/// Initializes a new instance of the <see cref="HttpServer"/> class with
/// the specified <paramref name="port"/> and <paramref name="secure"/>.
/// </summary>
/// <remarks>
2015-06-29 15:20:00 +08:00
/// An instance initialized by this constructor listens for the incoming
/// requests on <paramref name="port"/>.
/// </remarks>
/// <param name="port">
/// An <see cref="int"/> that represents the port number on which to listen.
/// </param>
/// <param name="secure">
2014-08-31 13:44:10 +08:00
/// A <see cref="bool"/> that indicates providing a secure connection or not.
/// (<c>true</c> indicates providing a secure connection.)
/// </param>
2014-08-31 13:44:10 +08:00
/// <exception cref="ArgumentOutOfRangeException">
2015-06-29 15:20:00 +08:00
/// <paramref name="port"/> isn't between 1 and 65535 inclusive.
2014-08-31 13:44:10 +08:00
/// </exception>
public HttpServer (int port, bool secure)
{
if (!port.IsPortNumber ())
2015-06-29 15:20:00 +08:00
throw new ArgumentOutOfRangeException (
"port", "Not between 1 and 65535 inclusive: " + port);
2015-06-29 15:20:00 +08:00
init ("*", port, secure);
2012-09-10 00:36:22 +08:00
}
#endregion
2013-05-07 01:19:31 +08:00
#region Public Properties
2012-09-10 00:36:22 +08:00
/// <summary>
/// Gets or sets the scheme used to authenticate the clients.
/// </summary>
/// <value>
2014-08-31 13:44:10 +08:00
/// One of the <see cref="WebSocketSharp.Net.AuthenticationSchemes"/> enum values,
/// indicates the scheme used to authenticate the clients. The default value is
/// <see cref="WebSocketSharp.Net.AuthenticationSchemes.Anonymous"/>.
/// </value>
public AuthenticationSchemes AuthenticationSchemes {
get {
return _listener.AuthenticationSchemes;
}
set {
2014-08-31 13:44:10 +08:00
var msg = _state.CheckIfStartable ();
if (msg != null) {
_logger.Error (msg);
return;
2014-08-31 13:44:10 +08:00
}
_listener.AuthenticationSchemes = value;
}
}
/// <summary>
/// Gets a value indicating whether the server has started.
/// </summary>
/// <value>
/// <c>true</c> if the server has started; otherwise, <c>false</c>.
/// </value>
public bool IsListening {
get {
return _state == ServerState.Start;
}
}
/// <summary>
/// Gets a value indicating whether the server provides a secure connection.
/// </summary>
/// <value>
/// <c>true</c> if the server provides a secure connection; otherwise, <c>false</c>.
/// </value>
public bool IsSecure {
get {
return _secure;
}
}
/// <summary>
2015-06-29 15:20:00 +08:00
/// Gets or sets a value indicating whether the server cleans up
/// the inactive sessions in the WebSocket services periodically.
/// </summary>
/// <value>
2014-08-31 13:44:10 +08:00
/// <c>true</c> if the server cleans up the inactive sessions every 60 seconds;
/// otherwise, <c>false</c>. The default value is <c>true</c>.
/// </value>
public bool KeepClean {
get {
return _services.KeepClean;
}
set {
var msg = _state.CheckIfStartable ();
if (msg != null) {
_logger.Error (msg);
return;
}
_services.KeepClean = value;
}
}
2013-07-15 19:42:55 +08:00
/// <summary>
/// Gets the logging functions.
/// </summary>
/// <remarks>
/// The default logging level is <see cref="LogLevel.Error"/>. If you would like to change it,
/// you should set the <c>Log.Level</c> property to any of the <see cref="LogLevel"/> enum
/// values.
2013-07-15 19:42:55 +08:00
/// </remarks>
/// <value>
/// A <see cref="Logger"/> that provides the logging functions.
/// </value>
public Logger Log {
get {
return _logger;
}
}
/// <summary>
/// Gets the port on which to listen for incoming requests.
/// </summary>
/// <value>
/// An <see cref="int"/> that represents the port number on which to listen.
/// </value>
2012-09-10 00:36:22 +08:00
public int Port {
get {
return _port;
}
}
/// <summary>
/// Gets or sets the name of the realm associated with the server.
/// </summary>
/// <value>
2015-06-29 15:20:00 +08:00
/// A <see cref="string"/> that represents the name of the realm. The default value is
/// <c>"SECRET AREA"</c>.
/// </value>
public string Realm {
get {
return _listener.Realm;
}
set {
2014-08-31 13:44:10 +08:00
var msg = _state.CheckIfStartable ();
if (msg != null) {
_logger.Error (msg);
return;
2014-08-31 13:44:10 +08:00
}
_listener.Realm = value;
}
}
/// <summary>
2015-06-29 15:20:00 +08:00
/// Gets or sets a value indicating whether the server is allowed to be bound to
/// an address that is already in use.
/// </summary>
/// <remarks>
2015-06-29 15:20:00 +08:00
/// If you would like to resolve to wait for socket in <c>TIME_WAIT</c> state,
/// you should set this property to <c>true</c>.
/// </remarks>
/// <value>
/// <c>true</c> if the server is allowed to be bound to an address that is already in use;
/// otherwise, <c>false</c>. The default value is <c>false</c>.
/// </value>
public bool ReuseAddress {
get {
return _listener.ReuseAddress;
}
set {
var msg = _state.CheckIfStartable ();
if (msg != null) {
_logger.Error (msg);
return;
}
_listener.ReuseAddress = value;
}
}
/// <summary>
/// Gets or sets the document root path of the server.
/// </summary>
/// <value>
2014-08-31 13:44:10 +08:00
/// A <see cref="string"/> that represents the document root path of the server.
/// The default value is <c>"./Public"</c>.
/// </value>
public string RootPath {
get {
2015-06-29 15:20:00 +08:00
return _rootPath != null && _rootPath.Length > 0 ? _rootPath : (_rootPath = "./Public");
}
set {
2014-08-31 13:44:10 +08:00
var msg = _state.CheckIfStartable ();
if (msg != null) {
_logger.Error (msg);
return;
2014-08-31 13:44:10 +08:00
}
_rootPath = value;
}
2012-09-10 00:36:22 +08:00
}
2014-10-30 18:09:12 +08:00
/// <summary>
/// Gets or sets the SSL configuration used to authenticate the server and
/// optionally the client for secure connection.
2014-10-30 18:09:12 +08:00
/// </summary>
/// <value>
2015-06-29 15:20:00 +08:00
/// A <see cref="ServerSslConfiguration"/> that represents the configuration used to
/// authenticate the server and optionally the client for secure connection.
2014-10-30 18:09:12 +08:00
/// </value>
public ServerSslConfiguration SslConfiguration {
2014-10-30 18:09:12 +08:00
get {
return _listener.SslConfiguration;
2014-10-30 18:09:12 +08:00
}
set {
var msg = _state.CheckIfStartable ();
if (msg != null) {
_logger.Error (msg);
return;
}
_listener.SslConfiguration = value;
2014-10-30 18:09:12 +08:00
}
}
/// <summary>
/// Gets or sets the delegate called to find the credentials for an identity used to
/// authenticate a client.
/// </summary>
/// <value>
2015-06-29 15:20:00 +08:00
/// A <c>Func&lt;<see cref="IIdentity"/>, <see cref="NetworkCredential"/>&gt;</c> delegate that
/// references the method(s) used to find the credentials. The default value is a function that
/// only returns <see langword="null"/>.
/// </value>
public Func<IIdentity, NetworkCredential> UserCredentialsFinder {
get {
return _listener.UserCredentialsFinder;
}
set {
2014-08-31 13:44:10 +08:00
var msg = _state.CheckIfStartable ();
if (msg != null) {
_logger.Error (msg);
return;
2014-08-31 13:44:10 +08:00
}
_listener.UserCredentialsFinder = value;
}
}
/// <summary>
/// Gets or sets the wait time for the response to the WebSocket Ping or Close.
/// </summary>
/// <value>
/// A <see cref="TimeSpan"/> that represents the wait time. The default value is
/// the same as 1 second.
/// </value>
public TimeSpan WaitTime {
get {
return _services.WaitTime;
}
set {
var msg = _state.CheckIfStartable () ?? value.CheckIfValidWaitTime ();
if (msg != null) {
_logger.Error (msg);
return;
}
_services.WaitTime = value;
}
}
/// <summary>
/// Gets the access to the WebSocket services provided by the server.
/// </summary>
/// <value>
/// A <see cref="WebSocketServiceManager"/> that manages the WebSocket services.
/// </value>
public WebSocketServiceManager WebSocketServices {
get {
return _services;
}
}
2012-09-10 00:36:22 +08:00
#endregion
2013-05-07 01:19:31 +08:00
#region Public Events
2012-09-10 00:36:22 +08:00
/// <summary>
/// Occurs when the server receives an HTTP CONNECT request.
/// </summary>
public event EventHandler<HttpRequestEventArgs> OnConnect;
/// <summary>
/// Occurs when the server receives an HTTP DELETE request.
/// </summary>
public event EventHandler<HttpRequestEventArgs> OnDelete;
/// <summary>
/// Occurs when the server receives an HTTP GET request.
/// </summary>
public event EventHandler<HttpRequestEventArgs> OnGet;
/// <summary>
/// Occurs when the server receives an HTTP HEAD request.
/// </summary>
public event EventHandler<HttpRequestEventArgs> OnHead;
/// <summary>
/// Occurs when the server receives an HTTP OPTIONS request.
/// </summary>
public event EventHandler<HttpRequestEventArgs> OnOptions;
/// <summary>
/// Occurs when the server receives an HTTP PATCH request.
/// </summary>
public event EventHandler<HttpRequestEventArgs> OnPatch;
/// <summary>
/// Occurs when the server receives an HTTP POST request.
/// </summary>
public event EventHandler<HttpRequestEventArgs> OnPost;
/// <summary>
/// Occurs when the server receives an HTTP PUT request.
/// </summary>
public event EventHandler<HttpRequestEventArgs> OnPut;
/// <summary>
/// Occurs when the server receives an HTTP TRACE request.
/// </summary>
public event EventHandler<HttpRequestEventArgs> OnTrace;
2012-09-10 00:36:22 +08:00
#endregion
#region Private Methods
2013-09-25 19:35:06 +08:00
private void abort ()
{
lock (_sync) {
if (!IsListening)
2013-09-25 19:35:06 +08:00
return;
_state = ServerState.ShuttingDown;
2013-09-25 19:35:06 +08:00
}
_services.Stop (new CloseEventArgs (CloseStatusCode.ServerError), true, false);
_listener.Abort ();
2014-09-21 16:33:07 +08:00
_state = ServerState.Stop;
2013-09-25 19:35:06 +08:00
}
2014-08-31 13:44:10 +08:00
private string checkIfCertificateExists ()
{
if (!_secure)
return null;
var usr = _listener.SslConfiguration.ServerCertificate != null;
var port = EndPointListener.CertificateExists (_port, _listener.CertificateFolderPath);
if (usr && port) {
_logger.Warn ("The server certificate associated with the port number already exists.");
return null;
}
2015-06-29 15:20:00 +08:00
return !(usr || port) ? "The secure connection requires a server certificate." : null;
}
private void init (string hostname, int port, bool secure)
{
_port = port;
_secure = secure;
_listener = new HttpListener ();
_listener.Prefixes.Add (
String.Format ("http{0}://{1}:{2}/", secure ? "s" : "", hostname, port));
_logger = _listener.Log;
_services = new WebSocketServiceManager (_logger);
_sync = new object ();
var os = Environment.OSVersion;
_windows = os.Platform != PlatformID.Unix && os.Platform != PlatformID.MacOSX;
2014-08-31 13:44:10 +08:00
}
2014-11-25 11:30:18 +08:00
private void processRequest (HttpListenerContext context)
2012-09-10 00:36:22 +08:00
{
2013-07-15 19:42:55 +08:00
var method = context.Request.HttpMethod;
2014-08-31 13:44:10 +08:00
var evt = method == "GET"
? OnGet
: method == "HEAD"
? OnHead
: method == "POST"
? OnPost
: method == "PUT"
? OnPut
: method == "DELETE"
? OnDelete
: method == "OPTIONS"
? OnOptions
: method == "TRACE"
? OnTrace
: method == "CONNECT"
? OnConnect
: method == "PATCH"
? OnPatch
: null;
if (evt != null)
2014-08-31 13:44:10 +08:00
evt (this, new HttpRequestEventArgs (context));
else
context.Response.StatusCode = (int) HttpStatusCode.NotImplemented;
2012-09-11 12:21:47 +08:00
context.Response.Close ();
}
2014-11-25 11:30:18 +08:00
private void processRequest (HttpListenerWebSocketContext context)
2013-07-15 19:42:55 +08:00
{
WebSocketServiceHost host;
2014-09-18 19:52:38 +08:00
if (!_services.InternalTryGetServiceHost (context.RequestUri.AbsolutePath, out host)) {
context.Close (HttpStatusCode.NotImplemented);
return;
2013-07-15 19:42:55 +08:00
}
host.StartSession (context);
}
2013-07-15 19:42:55 +08:00
private void receiveRequest ()
{
while (true) {
try {
var ctx = _listener.GetContext ();
ThreadPool.QueueUserWorkItem (
state => {
try {
if (ctx.Request.IsUpgradeTo ("websocket")) {
processRequest (ctx.AcceptWebSocket (null));
return;
}
2014-11-25 11:30:18 +08:00
processRequest (ctx);
}
catch (Exception ex) {
_logger.Fatal (ex.ToString ());
ctx.Connection.Close (true);
}
});
}
2013-09-25 19:35:06 +08:00
catch (HttpListenerException ex) {
2015-06-29 15:20:00 +08:00
_logger.Warn ("Receiving has been stopped.\n reason: " + ex.Message);
break;
}
catch (Exception ex) {
2013-09-25 19:35:06 +08:00
_logger.Fatal (ex.ToString ());
break;
}
}
2013-09-25 19:35:06 +08:00
if (IsListening)
2013-09-25 19:35:06 +08:00
abort ();
}
private void startReceiving ()
2012-09-10 00:36:22 +08:00
{
_listener.Start ();
2014-11-25 11:30:18 +08:00
_receiveThread = new Thread (new ThreadStart (receiveRequest));
_receiveThread.IsBackground = true;
_receiveThread.Start ();
2012-09-10 00:36:22 +08:00
}
private void stopReceiving (int millisecondsTimeout)
{
_listener.Close ();
2014-11-25 11:30:18 +08:00
_receiveThread.Join (millisecondsTimeout);
}
2012-09-10 00:36:22 +08:00
#endregion
#region Public Methods
/// <summary>
/// Adds the WebSocket service with the specified behavior, <paramref name="path"/>,
/// and <paramref name="initializer"/>.
/// </summary>
/// <remarks>
/// <para>
/// This method converts <paramref name="path"/> to URL-decoded string,
/// and removes <c>'/'</c> from tail end of <paramref name="path"/>.
/// </para>
/// <para>
2014-08-31 13:44:10 +08:00
/// <paramref name="initializer"/> returns an initialized specified typed
/// <see cref="WebSocketBehavior"/> instance.
/// </para>
/// </remarks>
/// <param name="path">
/// A <see cref="string"/> that represents the absolute path to the service to add.
/// </param>
2014-08-31 13:44:10 +08:00
/// <param name="initializer">
2015-06-29 15:20:00 +08:00
/// A <c>Func&lt;T&gt;</c> delegate that references the method used to initialize
/// a new specified typed <see cref="WebSocketBehavior"/> instance (a new
/// <see cref="IWebSocketSession"/> instance).
/// </param>
/// <typeparam name="TBehavior">
/// The type of the behavior of the service to add. The TBehavior must inherit
/// the <see cref="WebSocketBehavior"/> class.
/// </typeparam>
public void AddWebSocketService<TBehavior> (string path, Func<TBehavior> initializer)
where TBehavior : WebSocketBehavior
{
var msg = path.CheckIfValidServicePath () ??
2014-08-31 13:44:10 +08:00
(initializer == null ? "'initializer' is null." : null);
if (msg != null) {
2014-09-21 16:33:07 +08:00
_logger.Error (msg);
return;
}
2014-09-18 19:52:38 +08:00
_services.Add<TBehavior> (path, initializer);
}
2015-06-29 15:20:00 +08:00
/// <summary>
/// Adds a WebSocket service with the specified behavior and <paramref name="path"/>.
/// </summary>
/// <remarks>
/// This method converts <paramref name="path"/> to URL-decoded string,
/// and removes <c>'/'</c> from tail end of <paramref name="path"/>.
/// </remarks>
/// <param name="path">
/// A <see cref="string"/> that represents the absolute path to the service to add.
/// </param>
/// <typeparam name="TBehaviorWithNew">
/// The type of the behavior of the service to add. The TBehaviorWithNew must inherit
/// the <see cref="WebSocketBehavior"/> class, and must have a public parameterless
/// constructor.
/// </typeparam>
public void AddWebSocketService<TBehaviorWithNew> (string path)
where TBehaviorWithNew : WebSocketBehavior, new ()
{
AddWebSocketService<TBehaviorWithNew> (path, () => new TBehaviorWithNew ());
}
/// <summary>
/// Gets the contents of the file with the specified <paramref name="path"/>.
/// </summary>
/// <returns>
2014-08-31 13:44:10 +08:00
/// An array of <see cref="byte"/> that receives the contents of the file,
/// or <see langword="null"/> if it doesn't exist.
/// </returns>
/// <param name="path">
/// A <see cref="string"/> that represents the virtual path to the file to find.
/// </param>
2014-08-31 13:44:10 +08:00
public byte[] GetFile (string path)
2012-09-10 00:36:22 +08:00
{
2014-11-25 11:30:18 +08:00
path = RootPath + path;
if (_windows)
2014-11-25 11:30:18 +08:00
path = path.Replace ("/", "\\");
2012-09-12 21:52:02 +08:00
2015-06-29 15:20:00 +08:00
return File.Exists (path) ? File.ReadAllBytes (path) : null;
2012-09-10 00:36:22 +08:00
}
/// <summary>
/// Removes the WebSocket service with the specified <paramref name="path"/>.
/// </summary>
/// <remarks>
/// This method converts <paramref name="path"/> to URL-decoded string,
/// and removes <c>'/'</c> from tail end of <paramref name="path"/>.
/// </remarks>
/// <returns>
/// <c>true</c> if the service is successfully found and removed; otherwise, <c>false</c>.
/// </returns>
/// <param name="path">
/// A <see cref="string"/> that represents the absolute path to the service to find.
/// </param>
public bool RemoveWebSocketService (string path)
{
var msg = path.CheckIfValidServicePath ();
if (msg != null) {
2014-09-21 16:33:07 +08:00
_logger.Error (msg);
return false;
}
return _services.Remove (path);
}
/// <summary>
/// Starts receiving the HTTP requests.
/// </summary>
public void Start ()
2012-09-10 00:36:22 +08:00
{
lock (_sync) {
2014-08-31 13:44:10 +08:00
var msg = _state.CheckIfStartable () ?? checkIfCertificateExists ();
if (msg != null) {
2014-09-21 16:33:07 +08:00
_logger.Error (msg);
2013-09-25 19:35:06 +08:00
return;
}
_services.Start ();
startReceiving ();
_state = ServerState.Start;
2013-09-25 19:35:06 +08:00
}
2012-09-10 00:36:22 +08:00
}
/// <summary>
/// Stops receiving the HTTP requests.
/// </summary>
public void Stop ()
2012-09-10 00:36:22 +08:00
{
lock (_sync) {
2014-02-02 15:28:20 +08:00
var msg = _state.CheckIfStart ();
if (msg != null) {
2014-09-21 16:33:07 +08:00
_logger.Error (msg);
2013-09-25 19:35:06 +08:00
return;
}
_state = ServerState.ShuttingDown;
2013-09-25 19:35:06 +08:00
}
_services.Stop (new CloseEventArgs (), true, true);
stopReceiving (5000);
2013-09-25 19:35:06 +08:00
_state = ServerState.Stop;
}
/// <summary>
/// Stops receiving the HTTP requests with the specified <see cref="ushort"/> and
/// <see cref="string"/> used to stop the WebSocket services.
/// </summary>
/// <param name="code">
/// A <see cref="ushort"/> that represents the status code indicating the reason for the stop.
/// </param>
/// <param name="reason">
/// A <see cref="string"/> that represents the reason for the stop.
/// </param>
public void Stop (ushort code, string reason)
{
lock (_sync) {
var msg = _state.CheckIfStart () ?? WebSocket.CheckCloseParameters (code, reason, false);
if (msg != null) {
2014-09-21 16:33:07 +08:00
_logger.Error (msg);
2013-09-25 19:35:06 +08:00
return;
}
_state = ServerState.ShuttingDown;
}
2015-05-03 13:46:32 +08:00
if (code == (ushort) CloseStatusCode.NoStatus) {
_services.Stop (new CloseEventArgs (), true, true);
}
else {
var send = !code.IsReserved ();
_services.Stop (new CloseEventArgs (code, reason), send, send);
}
stopReceiving (5000);
2013-09-25 19:35:06 +08:00
_state = ServerState.Stop;
}
/// <summary>
/// Stops receiving the HTTP requests with the specified <see cref="CloseStatusCode"/> and
/// <see cref="string"/> used to stop the WebSocket services.
/// </summary>
/// <param name="code">
2015-06-29 15:20:00 +08:00
/// One of the <see cref="CloseStatusCode"/> enum values, represents the status code indicating
/// the reason for the stop.
/// </param>
/// <param name="reason">
/// A <see cref="string"/> that represents the reason for the stop.
/// </param>
public void Stop (CloseStatusCode code, string reason)
{
lock (_sync) {
var msg = _state.CheckIfStart () ?? WebSocket.CheckCloseParameters (code, reason, false);
if (msg != null) {
2014-09-21 16:33:07 +08:00
_logger.Error (msg);
2013-09-25 19:35:06 +08:00
return;
}
_state = ServerState.ShuttingDown;
2013-09-25 19:35:06 +08:00
}
2015-05-03 13:46:32 +08:00
if (code == CloseStatusCode.NoStatus) {
_services.Stop (new CloseEventArgs (), true, true);
}
else {
var send = !code.IsReserved ();
_services.Stop (new CloseEventArgs (code, reason), send, send);
}
stopReceiving (5000);
_state = ServerState.Stop;
2012-09-10 00:36:22 +08:00
}
#endregion
}
}