Modified HttpListener.cs and others for secure connection
This commit is contained in:
parent
1b270603f4
commit
cc81fd340c
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<add key="ServerCertFile" value="/path/to/cert.pfx"/>
|
||||
<add key="CertFilePassword" value="password"/>
|
||||
<add key="ServerCertFile" value="/path/to/cert.pfx"/>
|
||||
</appSettings>
|
||||
</configuration>
|
||||
|
@ -1,6 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<add key="RootPath" value="../../Public" />
|
||||
<add key="CertFilePassword" value="password"/>
|
||||
<add key="RootPath" value="../../Public"/>
|
||||
<add key="ServerCertFile" value="/path/to/cert.pfx"/>
|
||||
</appSettings>
|
||||
</configuration>
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using WebSocketSharp;
|
||||
using WebSocketSharp.Net;
|
||||
using WebSocketSharp.Server;
|
||||
@ -13,10 +14,14 @@ namespace Example3
|
||||
public static void Main (string [] args)
|
||||
{
|
||||
_httpsv = new HttpServer (4649);
|
||||
//_httpsv = new HttpServer (4649, true);
|
||||
#if DEBUG
|
||||
_httpsv.Log.Level = LogLevel.TRACE;
|
||||
#endif
|
||||
_httpsv.RootPath = ConfigurationManager.AppSettings ["RootPath"];
|
||||
//var certFile = ConfigurationManager.AppSettings ["ServerCertFile"];
|
||||
//var password = ConfigurationManager.AppSettings ["CertFilePassword"];
|
||||
//_httpsv.Certificate = new X509Certificate2 (certFile, password);
|
||||
//_httpsv.KeepClean = false;
|
||||
_httpsv.AddWebSocketService<Echo> ("/Echo");
|
||||
_httpsv.AddWebSocketService<Chat> ("/Chat");
|
||||
@ -32,12 +37,16 @@ namespace Example3
|
||||
};
|
||||
|
||||
_httpsv.Start ();
|
||||
Console.WriteLine ("HTTP Server listening on port: {0} service path:", _httpsv.Port);
|
||||
foreach (var path in _httpsv.ServicePaths)
|
||||
Console.WriteLine (" {0}", path);
|
||||
Console.WriteLine ();
|
||||
if (_httpsv.IsListening)
|
||||
{
|
||||
Console.WriteLine ("HTTP Server listening on port: {0} service path:", _httpsv.Port);
|
||||
foreach (var path in _httpsv.ServicePaths)
|
||||
Console.WriteLine (" {0}", path);
|
||||
|
||||
Console.WriteLine ("Press enter key to stop the server...");
|
||||
Console.WriteLine ();
|
||||
}
|
||||
|
||||
Console.WriteLine ("Press Enter key to stop the server...");
|
||||
Console.ReadLine ();
|
||||
|
||||
_httpsv.Stop ();
|
||||
|
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
var wsUri = "ws://localhost:4649/Echo";
|
||||
//var wsUri = "wss://localhost:4649/Echo";
|
||||
var output;
|
||||
|
||||
function init(){
|
||||
|
@ -298,6 +298,11 @@ namespace WebSocketSharp
|
||||
return value.StartsWith ("permessage-");
|
||||
}
|
||||
|
||||
internal static bool IsPortNumber (this int value)
|
||||
{
|
||||
return value > 0 && value < 65536;
|
||||
}
|
||||
|
||||
internal static bool IsText (this string value)
|
||||
{
|
||||
int len = value.Length;
|
||||
|
@ -6,7 +6,7 @@
|
||||
// Gonzalo Paniagua Javier (gonzalo@novell.com)
|
||||
//
|
||||
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
// Copyright (c) 2012 sta.blockhead (sta.blockhead@gmail.com)
|
||||
// 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
|
||||
@ -38,110 +38,117 @@ using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
|
||||
namespace WebSocketSharp.Net {
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
internal sealed class EndPointListener
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
sealed class EndPointListener {
|
||||
|
||||
#region Fields
|
||||
|
||||
List<ListenerPrefix> all; // host = '+'
|
||||
X509Certificate2 cert;
|
||||
IPEndPoint endpoint;
|
||||
AsymmetricAlgorithm key;
|
||||
Dictionary<ListenerPrefix, HttpListener> prefixes;
|
||||
bool secure;
|
||||
Socket sock;
|
||||
List<ListenerPrefix> unhandled; // host = '*'
|
||||
Hashtable unregistered;
|
||||
List<ListenerPrefix> _all; // host = '+'
|
||||
X509Certificate2 _cert;
|
||||
IPEndPoint _endpoint;
|
||||
Dictionary<ListenerPrefix, HttpListener> _prefixes;
|
||||
bool _secure;
|
||||
Socket _socket;
|
||||
List<ListenerPrefix> _unhandled; // host = '*'
|
||||
Dictionary<HttpConnection, HttpConnection> _unregistered;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
#region Public Constructors
|
||||
|
||||
public EndPointListener (IPAddress addr, int port, bool secure)
|
||||
public EndPointListener (
|
||||
IPAddress address,
|
||||
int port,
|
||||
bool secure,
|
||||
string certFolderPath,
|
||||
X509Certificate2 defaultCert
|
||||
)
|
||||
{
|
||||
if (secure) {
|
||||
this.secure = secure;
|
||||
LoadCertificateAndKey (addr, port);
|
||||
_secure = secure;
|
||||
_cert = getCertificate (port, certFolderPath, defaultCert);
|
||||
if (_cert == null)
|
||||
throw new ArgumentException ("Server certificate not found.");
|
||||
}
|
||||
|
||||
endpoint = new IPEndPoint (addr, port);
|
||||
sock = new Socket (addr.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind (endpoint);
|
||||
sock.Listen (500);
|
||||
_endpoint = new IPEndPoint (address, port);
|
||||
_socket = new Socket (address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||
_socket.Bind (_endpoint);
|
||||
_socket.Listen (500);
|
||||
var args = new SocketAsyncEventArgs ();
|
||||
args.UserToken = this;
|
||||
args.Completed += OnAccept;
|
||||
sock.AcceptAsync (args);
|
||||
prefixes = new Dictionary<ListenerPrefix, HttpListener> ();
|
||||
unregistered = Hashtable.Synchronized (new Hashtable ());
|
||||
args.Completed += onAccept;
|
||||
_socket.AcceptAsync (args);
|
||||
_prefixes = new Dictionary<ListenerPrefix, HttpListener> ();
|
||||
_unregistered = new Dictionary<HttpConnection, HttpConnection> ();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
void AddSpecial (List<ListenerPrefix> coll, ListenerPrefix prefix)
|
||||
private static void addSpecial (List<ListenerPrefix> prefixes, ListenerPrefix prefix)
|
||||
{
|
||||
if (coll == null)
|
||||
if (prefixes == null)
|
||||
return;
|
||||
|
||||
foreach (ListenerPrefix p in coll) {
|
||||
foreach (var p in prefixes)
|
||||
if (p.Path == prefix.Path) // TODO: code
|
||||
throw new HttpListenerException (400, "Prefix already in use.");
|
||||
}
|
||||
coll.Add (prefix);
|
||||
|
||||
prefixes.Add (prefix);
|
||||
}
|
||||
|
||||
void CheckIfRemove ()
|
||||
private void checkIfRemove ()
|
||||
{
|
||||
if (prefixes.Count > 0)
|
||||
if (_prefixes.Count > 0)
|
||||
return;
|
||||
|
||||
var list = unhandled;
|
||||
if (list != null && list.Count > 0)
|
||||
if (_unhandled != null && _unhandled.Count > 0)
|
||||
return;
|
||||
|
||||
list = all;
|
||||
if (list != null && list.Count > 0)
|
||||
if (_all != null && _all.Count > 0)
|
||||
return;
|
||||
|
||||
EndPointManager.RemoveEndPoint (this, endpoint);
|
||||
EndPointManager.RemoveEndPoint (this, _endpoint);
|
||||
}
|
||||
|
||||
RSACryptoServiceProvider CreateRSAFromFile (string filename)
|
||||
private static RSACryptoServiceProvider createRSAFromFile (string filename)
|
||||
{
|
||||
if (filename == null)
|
||||
throw new ArgumentNullException ("filename");
|
||||
|
||||
var rsa = new RSACryptoServiceProvider ();
|
||||
byte[] pvk = null;
|
||||
using (FileStream fs = File.Open (filename, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
using (var fs = File.Open (filename, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
pvk = new byte [fs.Length];
|
||||
fs.Read (pvk, 0, pvk.Length);
|
||||
}
|
||||
|
||||
rsa.ImportCspBlob (pvk);
|
||||
return rsa;
|
||||
}
|
||||
|
||||
void LoadCertificateAndKey (IPAddress addr, int port)
|
||||
private static X509Certificate2 getCertificate (
|
||||
int port, string certFolderPath, X509Certificate2 defaultCert)
|
||||
{
|
||||
// Actually load the certificate
|
||||
try {
|
||||
string dirname = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
|
||||
string path = Path.Combine (dirname, ".mono");
|
||||
path = Path.Combine (path, "httplistener");
|
||||
string cert_file = Path.Combine (path, String.Format ("{0}.cer", port));
|
||||
string pvk_file = Path.Combine (path, String.Format ("{0}.pvk", port));
|
||||
cert = new X509Certificate2 (cert_file);
|
||||
key = CreateRSAFromFile (pvk_file);
|
||||
} catch {
|
||||
// ignore errors
|
||||
var cer = Path.Combine (certFolderPath, String.Format ("{0}.cer", port));
|
||||
var key = Path.Combine (certFolderPath, String.Format ("{0}.key", port));
|
||||
if (File.Exists (cer) && File.Exists (key))
|
||||
{
|
||||
var cert = new X509Certificate2 (cer);
|
||||
cert.PrivateKey = createRSAFromFile (key);
|
||||
|
||||
return cert;
|
||||
}
|
||||
}
|
||||
catch {
|
||||
}
|
||||
|
||||
return defaultCert;
|
||||
}
|
||||
|
||||
HttpListener MatchFromList (
|
||||
private static HttpListener matchFromList (
|
||||
string host, string path, List<ListenerPrefix> list, out ListenerPrefix prefix)
|
||||
{
|
||||
prefix = null;
|
||||
@ -149,14 +156,15 @@ namespace WebSocketSharp.Net {
|
||||
return null;
|
||||
|
||||
HttpListener best_match = null;
|
||||
int best_length = -1;
|
||||
|
||||
foreach (ListenerPrefix p in list) {
|
||||
string ppath = p.Path;
|
||||
var best_length = -1;
|
||||
foreach (var p in list)
|
||||
{
|
||||
var ppath = p.Path;
|
||||
if (ppath.Length < best_length)
|
||||
continue;
|
||||
|
||||
if (path.StartsWith (ppath)) {
|
||||
if (path.StartsWith (ppath))
|
||||
{
|
||||
best_length = ppath.Length;
|
||||
best_match = p.Listener;
|
||||
prefix = p;
|
||||
@ -166,101 +174,117 @@ namespace WebSocketSharp.Net {
|
||||
return best_match;
|
||||
}
|
||||
|
||||
static void OnAccept (object sender, EventArgs e)
|
||||
private static void onAccept (object sender, EventArgs e)
|
||||
{
|
||||
SocketAsyncEventArgs args = (SocketAsyncEventArgs) e;
|
||||
EndPointListener epl = (EndPointListener) args.UserToken;
|
||||
var args = (SocketAsyncEventArgs) e;
|
||||
var epListener = (EndPointListener) args.UserToken;
|
||||
Socket accepted = null;
|
||||
if (args.SocketError == SocketError.Success) {
|
||||
if (args.SocketError == SocketError.Success)
|
||||
{
|
||||
accepted = args.AcceptSocket;
|
||||
args.AcceptSocket = null;
|
||||
}
|
||||
|
||||
try {
|
||||
if (epl.sock != null)
|
||||
epl.sock.AcceptAsync (args);
|
||||
} catch {
|
||||
if (accepted != null) {
|
||||
try {
|
||||
accepted.Close ();
|
||||
} catch {}
|
||||
accepted = null;
|
||||
}
|
||||
}
|
||||
epListener._socket.AcceptAsync (args);
|
||||
}
|
||||
catch {
|
||||
if (accepted != null)
|
||||
accepted.Close ();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (accepted == null)
|
||||
return;
|
||||
|
||||
if (epl.secure && (epl.cert == null || epl.key == null)) {
|
||||
accepted.Close ();
|
||||
return;
|
||||
HttpConnection conn = null;
|
||||
try {
|
||||
conn = new HttpConnection (accepted, epListener, epListener._secure, epListener._cert);
|
||||
lock (((ICollection) epListener._unregistered).SyncRoot)
|
||||
{
|
||||
epListener._unregistered [conn] = conn;
|
||||
}
|
||||
|
||||
conn.BeginReadRequest ();
|
||||
}
|
||||
catch {
|
||||
if (conn != null)
|
||||
{
|
||||
conn.Close (true);
|
||||
return;
|
||||
}
|
||||
|
||||
accepted.Close ();
|
||||
}
|
||||
HttpConnection conn = new HttpConnection (accepted, epl, epl.secure, epl.cert, epl.key);
|
||||
epl.unregistered [conn] = conn;
|
||||
conn.BeginReadRequest ();
|
||||
}
|
||||
|
||||
bool RemoveSpecial (List<ListenerPrefix> coll, ListenerPrefix prefix)
|
||||
private static bool removeSpecial (List<ListenerPrefix> prefixes, ListenerPrefix prefix)
|
||||
{
|
||||
if (coll == null)
|
||||
if (prefixes == null)
|
||||
return false;
|
||||
|
||||
int c = coll.Count;
|
||||
for (int i = 0; i < c; i++) {
|
||||
ListenerPrefix p = coll [i];
|
||||
if (p.Path == prefix.Path) {
|
||||
coll.RemoveAt (i);
|
||||
var count = prefixes.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (prefixes [i].Path == prefix.Path)
|
||||
{
|
||||
prefixes.RemoveAt (i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
HttpListener SearchListener (Uri uri, out ListenerPrefix prefix)
|
||||
private HttpListener searchListener (Uri uri, out ListenerPrefix prefix)
|
||||
{
|
||||
prefix = null;
|
||||
if (uri == null)
|
||||
return null;
|
||||
|
||||
string host = uri.Host;
|
||||
int port = uri.Port;
|
||||
string path = HttpUtility.UrlDecode (uri.AbsolutePath);
|
||||
string path_slash = path [path.Length - 1] == '/' ? path : path + "/";
|
||||
|
||||
var host = uri.Host;
|
||||
var port = uri.Port;
|
||||
var path = HttpUtility.UrlDecode (uri.AbsolutePath);
|
||||
var path_slash = path [path.Length - 1] == '/' ? path : path + "/";
|
||||
HttpListener best_match = null;
|
||||
int best_length = -1;
|
||||
|
||||
if (host != null && host != "") {
|
||||
var p_ro = prefixes;
|
||||
foreach (ListenerPrefix p in p_ro.Keys) {
|
||||
string ppath = p.Path;
|
||||
var best_length = -1;
|
||||
if (host != null && host.Length > 0)
|
||||
{
|
||||
foreach (var p in _prefixes.Keys)
|
||||
{
|
||||
var ppath = p.Path;
|
||||
if (ppath.Length < best_length)
|
||||
continue;
|
||||
|
||||
if (p.Host != host || p.Port != port)
|
||||
continue;
|
||||
|
||||
if (path.StartsWith (ppath) || path_slash.StartsWith (ppath)) {
|
||||
if (path.StartsWith (ppath) || path_slash.StartsWith (ppath))
|
||||
{
|
||||
best_length = ppath.Length;
|
||||
best_match = p_ro [p];
|
||||
best_match = _prefixes [p];
|
||||
prefix = p;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_length != -1)
|
||||
return best_match;
|
||||
}
|
||||
|
||||
var list = unhandled;
|
||||
best_match = MatchFromList (host, path, list, out prefix);
|
||||
var list = _unhandled;
|
||||
best_match = matchFromList (host, path, list, out prefix);
|
||||
if (path != path_slash && best_match == null)
|
||||
best_match = MatchFromList (host, path_slash, list, out prefix);
|
||||
best_match = matchFromList (host, path_slash, list, out prefix);
|
||||
|
||||
if (best_match != null)
|
||||
return best_match;
|
||||
|
||||
list = all;
|
||||
best_match = MatchFromList (host, path, list, out prefix);
|
||||
list = _all;
|
||||
best_match = matchFromList (host, path, list, out prefix);
|
||||
if (path != path_slash && best_match == null)
|
||||
best_match = MatchFromList (host, path_slash, list, out prefix);
|
||||
best_match = matchFromList (host, path_slash, list, out prefix);
|
||||
|
||||
if (best_match != null)
|
||||
return best_match;
|
||||
|
||||
@ -269,11 +293,22 @@ namespace WebSocketSharp.Net {
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Method
|
||||
#region Internal Methods
|
||||
|
||||
internal void RemoveConnection (HttpConnection conn)
|
||||
internal static bool CertificateExists (int port, string certFolderPath)
|
||||
{
|
||||
unregistered.Remove (conn);
|
||||
var cer = Path.Combine (certFolderPath, String.Format ("{0}.cer", port));
|
||||
var key = Path.Combine (certFolderPath, String.Format ("{0}.key", port));
|
||||
|
||||
return File.Exists (cer) && File.Exists (key);
|
||||
}
|
||||
|
||||
internal void RemoveConnection (HttpConnection connection)
|
||||
{
|
||||
lock (((ICollection) _unregistered).SyncRoot)
|
||||
{
|
||||
_unregistered.Remove (connection);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -282,111 +317,124 @@ namespace WebSocketSharp.Net {
|
||||
|
||||
public void AddPrefix (ListenerPrefix prefix, HttpListener listener)
|
||||
{
|
||||
List<ListenerPrefix> current;
|
||||
List<ListenerPrefix> future;
|
||||
if (prefix.Host == "*") {
|
||||
List<ListenerPrefix> current, future;
|
||||
if (prefix.Host == "*")
|
||||
{
|
||||
do {
|
||||
current = unhandled;
|
||||
future = (current != null)
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
current = _unhandled;
|
||||
future = current != null
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
prefix.Listener = listener;
|
||||
AddSpecial (future, prefix);
|
||||
} while (Interlocked.CompareExchange (ref unhandled, future, current) != current);
|
||||
addSpecial (future, prefix);
|
||||
} while (Interlocked.CompareExchange (ref _unhandled, future, current) != current);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (prefix.Host == "+") {
|
||||
if (prefix.Host == "+")
|
||||
{
|
||||
do {
|
||||
current = all;
|
||||
future = (current != null)
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
current = _all;
|
||||
future = current != null
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
prefix.Listener = listener;
|
||||
AddSpecial (future, prefix);
|
||||
} while (Interlocked.CompareExchange (ref all, future, current) != current);
|
||||
addSpecial (future, prefix);
|
||||
} while (Interlocked.CompareExchange (ref _all, future, current) != current);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<ListenerPrefix, HttpListener> prefs, p2;
|
||||
do {
|
||||
prefs = prefixes;
|
||||
if (prefs.ContainsKey (prefix)) {
|
||||
prefs = _prefixes;
|
||||
if (prefs.ContainsKey (prefix))
|
||||
{
|
||||
HttpListener other = prefs [prefix];
|
||||
if (other != listener) // TODO: code.
|
||||
throw new HttpListenerException (400, "There's another listener for " + prefix);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
p2 = new Dictionary<ListenerPrefix, HttpListener> (prefs);
|
||||
p2 [prefix] = listener;
|
||||
} while (Interlocked.CompareExchange (ref prefixes, p2, prefs) != prefs);
|
||||
} while (Interlocked.CompareExchange (ref _prefixes, p2, prefs) != prefs);
|
||||
}
|
||||
|
||||
public bool BindContext (HttpListenerContext context)
|
||||
{
|
||||
HttpListenerRequest req = context.Request;
|
||||
var req = context.Request;
|
||||
ListenerPrefix prefix;
|
||||
HttpListener listener = SearchListener (req.Url, out prefix);
|
||||
var listener = searchListener (req.Url, out prefix);
|
||||
if (listener == null)
|
||||
return false;
|
||||
|
||||
context.Listener = listener;
|
||||
context.Connection.Prefix = prefix;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Close ()
|
||||
{
|
||||
sock.Close ();
|
||||
lock (unregistered.SyncRoot) {
|
||||
Hashtable copy = (Hashtable) unregistered.Clone ();
|
||||
foreach (HttpConnection c in copy.Keys)
|
||||
c.Close (true);
|
||||
_socket.Close ();
|
||||
lock (((ICollection) _unregistered).SyncRoot)
|
||||
{
|
||||
var copy = new Dictionary<HttpConnection, HttpConnection> (_unregistered);
|
||||
foreach (var conn in copy.Keys)
|
||||
conn.Close (true);
|
||||
|
||||
copy.Clear ();
|
||||
unregistered.Clear ();
|
||||
_unregistered.Clear ();
|
||||
}
|
||||
}
|
||||
|
||||
public void RemovePrefix (ListenerPrefix prefix, HttpListener listener)
|
||||
{
|
||||
List<ListenerPrefix> current;
|
||||
List<ListenerPrefix> future;
|
||||
if (prefix.Host == "*") {
|
||||
List<ListenerPrefix> current, future;
|
||||
if (prefix.Host == "*")
|
||||
{
|
||||
do {
|
||||
current = unhandled;
|
||||
future = (current != null)
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
if (!RemoveSpecial (future, prefix))
|
||||
break; // Prefix not found
|
||||
} while (Interlocked.CompareExchange (ref unhandled, future, current) != current);
|
||||
CheckIfRemove ();
|
||||
current = _unhandled;
|
||||
future = current != null
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
if (!removeSpecial (future, prefix))
|
||||
break; // Prefix not found.
|
||||
} while (Interlocked.CompareExchange (ref _unhandled, future, current) != current);
|
||||
|
||||
checkIfRemove ();
|
||||
return;
|
||||
}
|
||||
|
||||
if (prefix.Host == "+") {
|
||||
if (prefix.Host == "+")
|
||||
{
|
||||
do {
|
||||
current = all;
|
||||
future = (current != null)
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
if (!RemoveSpecial (future, prefix))
|
||||
break; // Prefix not found
|
||||
} while (Interlocked.CompareExchange (ref all, future, current) != current);
|
||||
CheckIfRemove ();
|
||||
current = _all;
|
||||
future = current != null
|
||||
? new List<ListenerPrefix> (current)
|
||||
: new List<ListenerPrefix> ();
|
||||
if (!removeSpecial (future, prefix))
|
||||
break; // Prefix not found.
|
||||
} while (Interlocked.CompareExchange (ref _all, future, current) != current);
|
||||
|
||||
checkIfRemove ();
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<ListenerPrefix, HttpListener> prefs, p2;
|
||||
do {
|
||||
prefs = prefixes;
|
||||
prefs = _prefixes;
|
||||
if (!prefs.ContainsKey (prefix))
|
||||
break;
|
||||
|
||||
p2 = new Dictionary<ListenerPrefix, HttpListener> (prefs);
|
||||
p2.Remove (prefix);
|
||||
} while (Interlocked.CompareExchange (ref prefixes, p2, prefs) != prefs);
|
||||
CheckIfRemove ();
|
||||
} while (Interlocked.CompareExchange (ref _prefixes, p2, prefs) != prefs);
|
||||
|
||||
checkIfRemove ();
|
||||
}
|
||||
|
||||
public void UnbindContext (HttpListenerContext context)
|
||||
|
@ -6,7 +6,7 @@
|
||||
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
|
||||
//
|
||||
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
// Copyright (c) 2012 sta.blockhead (sta.blockhead@gmail.com)
|
||||
// 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
|
||||
@ -33,17 +33,17 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
|
||||
namespace WebSocketSharp.Net {
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
internal sealed class EndPointManager
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
sealed class EndPointManager {
|
||||
|
||||
#region Fields
|
||||
|
||||
static Dictionary<IPAddress, Dictionary<int, EndPointListener>> ip_to_endpoints = new Dictionary<IPAddress, Dictionary<int, EndPointListener>> ();
|
||||
private static Dictionary<IPAddress, Dictionary<int, EndPointListener>> _ipToEndpoints = new Dictionary<IPAddress, Dictionary<int, EndPointListener>> ();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
#region Private Constructors
|
||||
|
||||
private EndPointManager ()
|
||||
{
|
||||
@ -53,109 +53,122 @@ namespace WebSocketSharp.Net {
|
||||
|
||||
#region Private Methods
|
||||
|
||||
static void AddPrefixInternal (string p, HttpListener listener)
|
||||
private static void addPrefix (string uriPrefix, HttpListener httpListener)
|
||||
{
|
||||
ListenerPrefix lp = new ListenerPrefix (p);
|
||||
if (lp.Path.IndexOf ('%') != -1)
|
||||
var prefix = new ListenerPrefix (uriPrefix);
|
||||
if (prefix.Path.IndexOf ('%') != -1)
|
||||
throw new HttpListenerException (400, "Invalid path.");
|
||||
|
||||
if (lp.Path.IndexOf ("//", StringComparison.Ordinal) != -1) // TODO: Code?
|
||||
if (prefix.Path.IndexOf ("//", StringComparison.Ordinal) != -1) // TODO: Code?
|
||||
throw new HttpListenerException (400, "Invalid path.");
|
||||
|
||||
// Always listens on all the interfaces, no matter the host name/ip used.
|
||||
EndPointListener epl = GetEPListener (IPAddress.Any, lp.Port, listener, lp.Secure);
|
||||
epl.AddPrefix (lp, listener);
|
||||
var epListener = getEndPointListener (IPAddress.Any, prefix.Port, httpListener, prefix.Secure);
|
||||
epListener.AddPrefix (prefix, httpListener);
|
||||
}
|
||||
|
||||
static EndPointListener GetEPListener (IPAddress addr, int port, HttpListener listener, bool secure)
|
||||
private static EndPointListener getEndPointListener (
|
||||
IPAddress address, int port, HttpListener httpListener, bool secure)
|
||||
{
|
||||
Dictionary<int, EndPointListener> p = null;
|
||||
if (ip_to_endpoints.ContainsKey (addr)) {
|
||||
p = ip_to_endpoints [addr];
|
||||
} else {
|
||||
p = new Dictionary<int, EndPointListener> ();
|
||||
ip_to_endpoints [addr] = p;
|
||||
Dictionary<int, EndPointListener> endpoints = null;
|
||||
if (_ipToEndpoints.ContainsKey (address))
|
||||
{
|
||||
endpoints = _ipToEndpoints [address];
|
||||
}
|
||||
else
|
||||
{
|
||||
endpoints = new Dictionary<int, EndPointListener> ();
|
||||
_ipToEndpoints [address] = endpoints;
|
||||
}
|
||||
|
||||
EndPointListener epl = null;
|
||||
if (p.ContainsKey (port)) {
|
||||
epl = p [port];
|
||||
} else {
|
||||
epl = new EndPointListener (addr, port, secure);
|
||||
p [port] = epl;
|
||||
EndPointListener epListener = null;
|
||||
if (endpoints.ContainsKey (port))
|
||||
{
|
||||
epListener = endpoints [port];
|
||||
}
|
||||
else
|
||||
{
|
||||
epListener = new EndPointListener (
|
||||
address, port, secure, httpListener.CertificateFolderPath, httpListener.DefaultCertificate);
|
||||
endpoints [port] = epListener;
|
||||
}
|
||||
|
||||
return epl;
|
||||
return epListener;
|
||||
}
|
||||
|
||||
static void RemovePrefixInternal (string prefix, HttpListener listener)
|
||||
private static void removePrefix (string uriPrefix, HttpListener httpListener)
|
||||
{
|
||||
ListenerPrefix lp = new ListenerPrefix (prefix);
|
||||
if (lp.Path.IndexOf ('%') != -1)
|
||||
var prefix = new ListenerPrefix (uriPrefix);
|
||||
if (prefix.Path.IndexOf ('%') != -1)
|
||||
return;
|
||||
|
||||
if (lp.Path.IndexOf ("//", StringComparison.Ordinal) != -1)
|
||||
if (prefix.Path.IndexOf ("//", StringComparison.Ordinal) != -1)
|
||||
return;
|
||||
|
||||
EndPointListener epl = GetEPListener (IPAddress.Any, lp.Port, listener, lp.Secure);
|
||||
epl.RemovePrefix (lp, listener);
|
||||
var epListener = getEndPointListener (IPAddress.Any, prefix.Port, httpListener, prefix.Secure);
|
||||
epListener.RemovePrefix (prefix, httpListener);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public static void AddListener (HttpListener listener)
|
||||
public static void AddListener (HttpListener httpListener)
|
||||
{
|
||||
List<string> added = new List<string> ();
|
||||
try {
|
||||
lock (((ICollection)ip_to_endpoints).SyncRoot) {
|
||||
foreach (string prefix in listener.Prefixes) {
|
||||
AddPrefixInternal (prefix, listener);
|
||||
var added = new List<string> ();
|
||||
lock (((ICollection) _ipToEndpoints).SyncRoot)
|
||||
{
|
||||
try {
|
||||
foreach (var prefix in httpListener.Prefixes)
|
||||
{
|
||||
addPrefix (prefix, httpListener);
|
||||
added.Add (prefix);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
foreach (string prefix in added) {
|
||||
RemovePrefix (prefix, listener);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch {
|
||||
foreach (var prefix in added)
|
||||
removePrefix (prefix, httpListener);
|
||||
|
||||
public static void AddPrefix (string prefix, HttpListener listener)
|
||||
{
|
||||
lock (((ICollection)ip_to_endpoints).SyncRoot) {
|
||||
AddPrefixInternal (prefix, listener);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemoveEndPoint (EndPointListener epl, IPEndPoint ep)
|
||||
{
|
||||
lock (((ICollection)ip_to_endpoints).SyncRoot) {
|
||||
Dictionary<int, EndPointListener> p = null;
|
||||
p = ip_to_endpoints [ep.Address];
|
||||
p.Remove (ep.Port);
|
||||
if (p.Count == 0) {
|
||||
ip_to_endpoints.Remove (ep.Address);
|
||||
}
|
||||
epl.Close ();
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemoveListener (HttpListener listener)
|
||||
{
|
||||
lock (((ICollection)ip_to_endpoints).SyncRoot) {
|
||||
foreach (string prefix in listener.Prefixes) {
|
||||
RemovePrefixInternal (prefix, listener);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemovePrefix (string prefix, HttpListener listener)
|
||||
public static void AddPrefix (string uriPrefix, HttpListener httpListener)
|
||||
{
|
||||
lock (((ICollection)ip_to_endpoints).SyncRoot) {
|
||||
RemovePrefixInternal (prefix, listener);
|
||||
lock (((ICollection) _ipToEndpoints).SyncRoot)
|
||||
{
|
||||
addPrefix (uriPrefix, httpListener);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemoveEndPoint (EndPointListener epListener, IPEndPoint endpoint)
|
||||
{
|
||||
lock (((ICollection) _ipToEndpoints).SyncRoot)
|
||||
{
|
||||
var endpoints = _ipToEndpoints [endpoint.Address];
|
||||
endpoints.Remove (endpoint.Port);
|
||||
if (endpoints.Count == 0)
|
||||
_ipToEndpoints.Remove (endpoint.Address);
|
||||
|
||||
epListener.Close ();
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemoveListener (HttpListener httpListener)
|
||||
{
|
||||
lock (((ICollection) _ipToEndpoints).SyncRoot)
|
||||
{
|
||||
foreach (var prefix in httpListener.Prefixes)
|
||||
removePrefix (prefix, httpListener);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemovePrefix (string uriPrefix, HttpListener httpListener)
|
||||
{
|
||||
lock (((ICollection) _ipToEndpoints).SyncRoot)
|
||||
{
|
||||
removePrefix (uriPrefix, httpListener);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
// Gonzalo Paniagua Javier (gonzalo@novell.com)
|
||||
//
|
||||
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
// Copyright (c) 2012-2013 sta.blockhead (sta.blockhead@gmail.com)
|
||||
// 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
|
||||
@ -41,10 +41,10 @@ using System.Text;
|
||||
using System.Threading;
|
||||
using WebSocketSharp.Net.Security;
|
||||
|
||||
namespace WebSocketSharp.Net {
|
||||
|
||||
internal sealed class HttpConnection {
|
||||
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
internal sealed class HttpConnection
|
||||
{
|
||||
#region Enums
|
||||
|
||||
enum InputState {
|
||||
@ -76,7 +76,6 @@ namespace WebSocketSharp.Net {
|
||||
private EndPointListener _epListener;
|
||||
private InputState _inputState;
|
||||
private RequestStream _inputStream;
|
||||
private AsymmetricAlgorithm _key;
|
||||
private HttpListener _lastListener;
|
||||
private LineState _lineState;
|
||||
private ResponseStream _outputStream;
|
||||
@ -95,21 +94,23 @@ namespace WebSocketSharp.Net {
|
||||
#region Public Constructors
|
||||
|
||||
public HttpConnection (
|
||||
Socket socket,
|
||||
EndPointListener listener,
|
||||
bool secure,
|
||||
X509Certificate2 cert,
|
||||
AsymmetricAlgorithm key)
|
||||
Socket socket,
|
||||
EndPointListener listener,
|
||||
bool secure,
|
||||
X509Certificate2 cert
|
||||
)
|
||||
{
|
||||
_socket = socket;
|
||||
_epListener = listener;
|
||||
_secure = secure;
|
||||
_key = key;
|
||||
|
||||
var netStream = new NetworkStream (socket, false);
|
||||
if (!secure) {
|
||||
if (!secure)
|
||||
{
|
||||
_stream = netStream;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
var sslStream = new SslStream (netStream, false);
|
||||
sslStream.AuthenticateAsServer (cert);
|
||||
_stream = sslStream;
|
||||
@ -180,8 +181,10 @@ namespace WebSocketSharp.Net {
|
||||
|
||||
try {
|
||||
_socket.Close ();
|
||||
} catch {
|
||||
} finally {
|
||||
}
|
||||
catch {
|
||||
}
|
||||
finally {
|
||||
_socket = null;
|
||||
}
|
||||
|
||||
@ -203,11 +206,6 @@ namespace WebSocketSharp.Net {
|
||||
_timeout = 90000; // 90k ms for first request, 15k ms from then on.
|
||||
}
|
||||
|
||||
private AsymmetricAlgorithm OnPVKSelection (X509Certificate certificate, string targetHost)
|
||||
{
|
||||
return _key;
|
||||
}
|
||||
|
||||
private static void OnRead (IAsyncResult asyncResult)
|
||||
{
|
||||
var conn = (HttpConnection) asyncResult.AsyncState;
|
||||
|
@ -32,6 +32,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
|
||||
// TODO: logging
|
||||
@ -45,9 +46,11 @@ namespace WebSocketSharp.Net {
|
||||
#region Private Fields
|
||||
|
||||
AuthenticationSchemes auth_schemes;
|
||||
AuthenticationSchemeSelector auth_selector;
|
||||
AuthenticationSchemeSelector auth_selector;
|
||||
string cert_folder_path;
|
||||
Dictionary<HttpConnection, HttpConnection> connections;
|
||||
List<HttpListenerContext> ctx_queue;
|
||||
X509Certificate2 default_cert;
|
||||
bool disposed;
|
||||
bool ignore_write_exceptions;
|
||||
bool listening;
|
||||
@ -121,6 +124,59 @@ namespace WebSocketSharp.Net {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path to the folder stored the certificate files used to authenticate
|
||||
/// the server on the secure connection.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This property represents the path to the folder stored the certificate files associated with
|
||||
/// the port number of each added URI prefix. A set of the certificate files is a pair of the
|
||||
/// <c>'port number'.cer</c> (DER) and <c>'port number'.key</c> (DER, RSA Private Key).
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// A <see cref="string"/> that contains the path to the certificate folder. The default value is
|
||||
/// the result of <c>Environment.GetFolderPath</c> (<see cref="Environment.SpecialFolder.ApplicationData"/>).
|
||||
/// </value>
|
||||
/// <exception cref="ObjectDisposedException">
|
||||
/// This object has been closed.
|
||||
/// </exception>
|
||||
public string CertificateFolderPath {
|
||||
get {
|
||||
CheckDisposed ();
|
||||
if (cert_folder_path.IsNullOrEmpty ())
|
||||
cert_folder_path = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
|
||||
|
||||
return cert_folder_path;
|
||||
}
|
||||
|
||||
set {
|
||||
CheckDisposed ();
|
||||
cert_folder_path = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default certificate used to authenticate the server on the secure connection.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="X509Certificate2"/> used to authenticate the server if the certificate associated with
|
||||
/// the port number of each added URI prefix is not found in the <see cref="CertificateFolderPath"/>.
|
||||
/// </value>
|
||||
/// <exception cref="ObjectDisposedException">
|
||||
/// This object has been closed.
|
||||
/// </exception>
|
||||
public X509Certificate2 DefaultCertificate {
|
||||
get {
|
||||
CheckDisposed ();
|
||||
return default_cert;
|
||||
}
|
||||
|
||||
set {
|
||||
CheckDisposed ();
|
||||
default_cert = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the <see cref="HttpListener"/> returns exceptions
|
||||
/// that occur when sending the response to the client.
|
||||
|
@ -6,7 +6,7 @@
|
||||
// Gonzalo Paniagua Javier (gonzalo@novell.com)
|
||||
//
|
||||
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
// Copyright (c) 2012-2013 sta.blockhead (sta.blockhead@gmail.com)
|
||||
// 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
|
||||
@ -32,40 +32,40 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WebSocketSharp.Net {
|
||||
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the collection used to store the URI prefixes for the <see cref="HttpListener"/>.
|
||||
/// </summary>
|
||||
public class HttpListenerPrefixCollection : ICollection<string>, IEnumerable<string>, IEnumerable
|
||||
{
|
||||
#region Fields
|
||||
#region Private Fields
|
||||
|
||||
HttpListener listener;
|
||||
List<string> prefixes;
|
||||
private HttpListener _listener;
|
||||
private List<string> _prefixes;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Constructor
|
||||
#region Private Constructors
|
||||
|
||||
private HttpListenerPrefixCollection ()
|
||||
{
|
||||
prefixes = new List<string> ();
|
||||
_prefixes = new List<string> ();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Constructor
|
||||
#region Internal Constructors
|
||||
|
||||
internal HttpListenerPrefixCollection (HttpListener listener)
|
||||
: this ()
|
||||
{
|
||||
this.listener = listener;
|
||||
_listener = listener;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of prefixes contained in the <see cref="HttpListenerPrefixCollection"/>.
|
||||
@ -74,43 +74,35 @@ namespace WebSocketSharp.Net {
|
||||
/// A <see cref="int"/> that contains the number of prefixes.
|
||||
/// </value>
|
||||
public int Count {
|
||||
get { return prefixes.Count; }
|
||||
get {
|
||||
return _prefixes.Count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether access to the <see cref="HttpListenerPrefixCollection"/> is read-only.
|
||||
/// Gets a value indicating whether access to the <see cref="HttpListenerPrefixCollection"/>
|
||||
/// is read-only.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Always returns <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsReadOnly {
|
||||
get { return false; }
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether access to the <see cref="HttpListenerPrefixCollection"/> is synchronized.
|
||||
/// Gets a value indicating whether access to the <see cref="HttpListenerPrefixCollection"/>
|
||||
/// is synchronized.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Always returns <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsSynchronized {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Explicit Interface Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Gets an object that can be used to iterate through the <see cref="HttpListenerPrefixCollection"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An object that implements the <see cref="IEnumerator"/> interface and provides access to
|
||||
/// the URI prefix strings in the <see cref="HttpListenerPrefixCollection"/>.
|
||||
/// </returns>
|
||||
IEnumerator IEnumerable.GetEnumerator ()
|
||||
{
|
||||
return prefixes.GetEnumerator ();
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -126,33 +118,38 @@ namespace WebSocketSharp.Net {
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="uriPrefix"/> is <see langword="null"/>.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="uriPrefix"/> is invalid.
|
||||
/// </exception>
|
||||
/// <exception cref="ObjectDisposedException">
|
||||
/// The <see cref="HttpListener"/> associated with this <see cref="HttpListenerPrefixCollection"/> is closed.
|
||||
/// The <see cref="HttpListener"/> associated with this <see cref="HttpListenerPrefixCollection"/>
|
||||
/// is closed.
|
||||
/// </exception>
|
||||
public void Add (string uriPrefix)
|
||||
{
|
||||
listener.CheckDisposed ();
|
||||
ListenerPrefix.CheckUri (uriPrefix);
|
||||
if (prefixes.Contains (uriPrefix))
|
||||
_listener.CheckDisposed ();
|
||||
ListenerPrefix.CheckUriPrefix (uriPrefix);
|
||||
if (_prefixes.Contains (uriPrefix))
|
||||
return;
|
||||
|
||||
prefixes.Add (uriPrefix);
|
||||
if (listener.IsListening)
|
||||
EndPointManager.AddPrefix (uriPrefix, listener);
|
||||
_prefixes.Add (uriPrefix);
|
||||
if (_listener.IsListening)
|
||||
EndPointManager.AddPrefix (uriPrefix, _listener);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all URI prefixes from the <see cref="HttpListenerPrefixCollection"/>.
|
||||
/// </summary>
|
||||
/// <exception cref="ObjectDisposedException">
|
||||
/// The <see cref="HttpListener"/> associated with this <see cref="HttpListenerPrefixCollection"/> is closed.
|
||||
/// The <see cref="HttpListener"/> associated with this <see cref="HttpListenerPrefixCollection"/>
|
||||
/// is closed.
|
||||
/// </exception>
|
||||
public void Clear ()
|
||||
{
|
||||
listener.CheckDisposed ();
|
||||
prefixes.Clear ();
|
||||
if (listener.IsListening)
|
||||
EndPointManager.RemoveListener (listener);
|
||||
_listener.CheckDisposed ();
|
||||
_prefixes.Clear ();
|
||||
if (_listener.IsListening)
|
||||
EndPointManager.RemoveListener (_listener);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -160,7 +157,7 @@ namespace WebSocketSharp.Net {
|
||||
/// the specified <paramref name="uriPrefix"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the <see cref="HttpListenerPrefixCollection"/> contains the specified <paramref name="uriPrefix"/>;
|
||||
/// <c>true</c> if the <see cref="HttpListenerPrefixCollection"/> contains <paramref name="uriPrefix"/>;
|
||||
/// otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <param name="uriPrefix">
|
||||
@ -170,51 +167,60 @@ namespace WebSocketSharp.Net {
|
||||
/// <paramref name="uriPrefix"/> is <see langword="null"/>.
|
||||
/// </exception>
|
||||
/// <exception cref="ObjectDisposedException">
|
||||
/// The <see cref="HttpListener"/> associated with this <see cref="HttpListenerPrefixCollection"/> is closed.
|
||||
/// The <see cref="HttpListener"/> associated with this <see cref="HttpListenerPrefixCollection"/>
|
||||
/// is closed.
|
||||
/// </exception>
|
||||
public bool Contains (string uriPrefix)
|
||||
{
|
||||
listener.CheckDisposed ();
|
||||
_listener.CheckDisposed ();
|
||||
if (uriPrefix == null)
|
||||
throw new ArgumentNullException ("uriPrefix");
|
||||
|
||||
return prefixes.Contains (uriPrefix);
|
||||
return _prefixes.Contains (uriPrefix);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the contents of the <see cref="HttpListenerPrefixCollection"/> to the specified <see cref="Array"/>.
|
||||
/// Copies the contents of the <see cref="HttpListenerPrefixCollection"/> to
|
||||
/// the specified <see cref="Array"/>.
|
||||
/// </summary>
|
||||
/// <param name="array">
|
||||
/// An <see cref="Array"/> that receives the URI prefix strings in the <see cref="HttpListenerPrefixCollection"/>.
|
||||
/// An <see cref="Array"/> that receives the URI prefix strings
|
||||
/// in the <see cref="HttpListenerPrefixCollection"/>.
|
||||
/// </param>
|
||||
/// <param name="offset">
|
||||
/// An <see cref="int"/> that contains the zero-based index in <paramref name="array"/> at which copying begins.
|
||||
/// An <see cref="int"/> that contains the zero-based index in <paramref name="array"/>
|
||||
/// at which copying begins.
|
||||
/// </param>
|
||||
/// <exception cref="ObjectDisposedException">
|
||||
/// The <see cref="HttpListener"/> associated with this <see cref="HttpListenerPrefixCollection"/> is closed.
|
||||
/// The <see cref="HttpListener"/> associated with this <see cref="HttpListenerPrefixCollection"/>
|
||||
/// is closed.
|
||||
/// </exception>
|
||||
public void CopyTo (Array array, int offset)
|
||||
{
|
||||
listener.CheckDisposed ();
|
||||
((ICollection) prefixes).CopyTo (array, offset);
|
||||
_listener.CheckDisposed ();
|
||||
((ICollection) _prefixes).CopyTo (array, offset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the contents of the <see cref="HttpListenerPrefixCollection"/> to the specified array of <see cref="string"/>.
|
||||
/// Copies the contents of the <see cref="HttpListenerPrefixCollection"/> to
|
||||
/// the specified array of <see cref="string"/>.
|
||||
/// </summary>
|
||||
/// <param name="array">
|
||||
/// An array of <see cref="string"/> that receives the URI prefix strings in the <see cref="HttpListenerPrefixCollection"/>.
|
||||
/// An array of <see cref="string"/> that receives the URI prefix strings
|
||||
/// in the <see cref="HttpListenerPrefixCollection"/>.
|
||||
/// </param>
|
||||
/// <param name="offset">
|
||||
/// An <see cref="int"/> that contains the zero-based index in <paramref name="array"/> at which copying begins.
|
||||
/// An <see cref="int"/> that contains the zero-based index in <paramref name="array"/>
|
||||
/// at which copying begins.
|
||||
/// </param>
|
||||
/// <exception cref="ObjectDisposedException">
|
||||
/// The <see cref="HttpListener"/> associated with this <see cref="HttpListenerPrefixCollection"/> is closed.
|
||||
/// The <see cref="HttpListener"/> associated with this <see cref="HttpListenerPrefixCollection"/>
|
||||
/// is closed.
|
||||
/// </exception>
|
||||
public void CopyTo (string [] array, int offset)
|
||||
{
|
||||
listener.CheckDisposed ();
|
||||
prefixes.CopyTo (array, offset);
|
||||
_listener.CheckDisposed ();
|
||||
_prefixes.CopyTo (array, offset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -226,15 +232,16 @@ namespace WebSocketSharp.Net {
|
||||
/// </returns>
|
||||
public IEnumerator<string> GetEnumerator ()
|
||||
{
|
||||
return prefixes.GetEnumerator ();
|
||||
return _prefixes.GetEnumerator ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified <paramref name="uriPrefix"/> from the list of prefixes in the <see cref="HttpListenerPrefixCollection"/>.
|
||||
/// Removes the specified <paramref name="uriPrefix"/> from the list of prefixes
|
||||
/// in the <see cref="HttpListenerPrefixCollection"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the <paramref name="uriPrefix"/> was found in the <see cref="HttpListenerPrefixCollection"/>
|
||||
/// and removed; otherwise, <c>false</c>.
|
||||
/// <c>true</c> if <paramref name="uriPrefix"/> is successfully found and removed;
|
||||
/// otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <param name="uriPrefix">
|
||||
/// A <see cref="string"/> that contains a URI prefix to remove.
|
||||
@ -243,21 +250,39 @@ namespace WebSocketSharp.Net {
|
||||
/// <paramref name="uriPrefix"/> is <see langword="null"/>.
|
||||
/// </exception>
|
||||
/// <exception cref="ObjectDisposedException">
|
||||
/// The <see cref="HttpListener"/> associated with this <see cref="HttpListenerPrefixCollection"/> is closed.
|
||||
/// The <see cref="HttpListener"/> associated with this <see cref="HttpListenerPrefixCollection"/>
|
||||
/// is closed.
|
||||
/// </exception>
|
||||
public bool Remove (string uriPrefix)
|
||||
{
|
||||
listener.CheckDisposed ();
|
||||
_listener.CheckDisposed ();
|
||||
if (uriPrefix == null)
|
||||
throw new ArgumentNullException ("uriPrefix");
|
||||
|
||||
bool result = prefixes.Remove (uriPrefix);
|
||||
if (result && listener.IsListening)
|
||||
EndPointManager.RemovePrefix (uriPrefix, listener);
|
||||
var result = _prefixes.Remove (uriPrefix);
|
||||
if (result && _listener.IsListening)
|
||||
EndPointManager.RemovePrefix (uriPrefix, _listener);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Explicit Interface Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Gets an object that can be used to iterate through the <see cref="HttpListenerPrefixCollection"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An object that implements the <see cref="IEnumerator"/> interface and provides access to
|
||||
/// the URI prefix strings in the <see cref="HttpListenerPrefixCollection"/>.
|
||||
/// </returns>
|
||||
IEnumerator IEnumerable.GetEnumerator ()
|
||||
{
|
||||
return _prefixes.GetEnumerator ();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
// Oleg Mihailik (mihailik gmail co_m)
|
||||
//
|
||||
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
|
||||
// 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
|
||||
@ -31,156 +32,177 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
namespace WebSocketSharp.Net {
|
||||
|
||||
sealed class ListenerPrefix {
|
||||
|
||||
namespace WebSocketSharp.Net
|
||||
{
|
||||
internal sealed class ListenerPrefix
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
IPAddress [] addresses;
|
||||
string host;
|
||||
string original;
|
||||
string path;
|
||||
ushort port;
|
||||
bool secure;
|
||||
IPAddress [] _addresses;
|
||||
string _host;
|
||||
string _original;
|
||||
string _path;
|
||||
ushort _port;
|
||||
bool _secure;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Field
|
||||
#region Public Fields
|
||||
|
||||
public HttpListener Listener;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
#region Public Constructors
|
||||
|
||||
// Must be called after calling ListenerPrefix.CheckUri.
|
||||
public ListenerPrefix (string prefix)
|
||||
// Must be called after calling ListenerPrefix.CheckUriPrefix.
|
||||
public ListenerPrefix (string uriPrefix)
|
||||
{
|
||||
original = prefix;
|
||||
Parse (prefix);
|
||||
_original = uriPrefix;
|
||||
parse (uriPrefix);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
#region Public Properties
|
||||
|
||||
public IPAddress [] Addresses {
|
||||
get { return addresses; }
|
||||
set { addresses = value; }
|
||||
get {
|
||||
return _addresses;
|
||||
}
|
||||
|
||||
set {
|
||||
_addresses = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string Host {
|
||||
get { return host; }
|
||||
}
|
||||
|
||||
public int Port {
|
||||
get { return (int) port; }
|
||||
get {
|
||||
return _host;
|
||||
}
|
||||
}
|
||||
|
||||
public string Path {
|
||||
get { return path; }
|
||||
get {
|
||||
return _path;
|
||||
}
|
||||
}
|
||||
|
||||
public int Port {
|
||||
get {
|
||||
return (int) _port;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Secure {
|
||||
get { return secure; }
|
||||
get {
|
||||
return _secure;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Method
|
||||
#region Private Methods
|
||||
|
||||
void Parse (string uri)
|
||||
private void parse (string uriPrefix)
|
||||
{
|
||||
int default_port = (uri.StartsWith ("http://")) ? 80 : 443;
|
||||
int default_port = uriPrefix.StartsWith ("http://") ? 80 : 443;
|
||||
if (default_port == 443)
|
||||
secure = true;
|
||||
_secure = true;
|
||||
|
||||
int length = uri.Length;
|
||||
int start_host = uri.IndexOf (':') + 3;
|
||||
int colon = uri.IndexOf (':', start_host, length - start_host);
|
||||
int length = uriPrefix.Length;
|
||||
int start_host = uriPrefix.IndexOf (':') + 3;
|
||||
int colon = uriPrefix.IndexOf (':', start_host, length - start_host);
|
||||
int root;
|
||||
if (colon > 0) {
|
||||
host = uri.Substring (start_host, colon - start_host);
|
||||
root = uri.IndexOf ('/', colon, length - colon);
|
||||
port = (ushort) Int32.Parse (uri.Substring (colon + 1, root - colon - 1));
|
||||
path = uri.Substring (root);
|
||||
} else {
|
||||
root = uri.IndexOf ('/', start_host, length - start_host);
|
||||
host = uri.Substring (start_host, root - start_host);
|
||||
path = uri.Substring (root);
|
||||
port = (ushort) default_port;
|
||||
if (colon > 0)
|
||||
{
|
||||
root = uriPrefix.IndexOf ('/', colon, length - colon);
|
||||
_host = uriPrefix.Substring (start_host, colon - start_host);
|
||||
_port = (ushort) Int32.Parse (uriPrefix.Substring (colon + 1, root - colon - 1));
|
||||
_path = uriPrefix.Substring (root);
|
||||
}
|
||||
else
|
||||
{
|
||||
root = uriPrefix.IndexOf ('/', start_host, length - start_host);
|
||||
_host = uriPrefix.Substring (start_host, root - start_host);
|
||||
_port = (ushort) default_port;
|
||||
_path = uriPrefix.Substring (root);
|
||||
}
|
||||
|
||||
if (path.Length != 1)
|
||||
path = path.Substring (0, path.Length - 1);
|
||||
if (_path.Length != 1)
|
||||
_path = _path.Substring (0, _path.Length - 1);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region public Methods
|
||||
|
||||
public static void CheckUri (string uri)
|
||||
public static void CheckUriPrefix (string uriPrefix)
|
||||
{
|
||||
if (uri == null)
|
||||
throw new ArgumentNullException ("uri");
|
||||
if (uriPrefix == null)
|
||||
throw new ArgumentNullException ("uriPrefix");
|
||||
|
||||
int default_port = (uri.StartsWith ("http://")) ? 80 : -1;
|
||||
int default_port = uriPrefix.StartsWith ("http://") ? 80 : -1;
|
||||
if (default_port == -1)
|
||||
default_port = (uri.StartsWith ("https://")) ? 443 : -1;
|
||||
default_port = uriPrefix.StartsWith ("https://") ? 443 : -1;
|
||||
|
||||
if (default_port == -1)
|
||||
throw new ArgumentException ("Only 'http' and 'https' schemes are supported.");
|
||||
|
||||
int length = uri.Length;
|
||||
int start_host = uri.IndexOf (':') + 3;
|
||||
int length = uriPrefix.Length;
|
||||
int start_host = uriPrefix.IndexOf (':') + 3;
|
||||
if (start_host >= length)
|
||||
throw new ArgumentException ("No host specified.");
|
||||
|
||||
int colon = uri.IndexOf (':', start_host, length - start_host);
|
||||
int colon = uriPrefix.IndexOf (':', start_host, length - start_host);
|
||||
if (start_host == colon)
|
||||
throw new ArgumentException ("No host specified.");
|
||||
|
||||
int root;
|
||||
if (colon > 0) {
|
||||
root = uri.IndexOf ('/', colon, length - colon);
|
||||
if (colon > 0)
|
||||
{
|
||||
root = uriPrefix.IndexOf ('/', colon, length - colon);
|
||||
if (root == -1)
|
||||
throw new ArgumentException ("No path specified.");
|
||||
|
||||
try {
|
||||
int p = Int32.Parse (uri.Substring (colon + 1, root - colon - 1));
|
||||
if (p <= 0 || p >= 65536)
|
||||
int port = Int32.Parse (uriPrefix.Substring (colon + 1, root - colon - 1));
|
||||
if (port <= 0 || port >= 65536)
|
||||
throw new Exception ();
|
||||
} catch {
|
||||
}
|
||||
catch {
|
||||
throw new ArgumentException ("Invalid port.");
|
||||
}
|
||||
} else {
|
||||
root = uri.IndexOf ('/', start_host, length - start_host);
|
||||
}
|
||||
else
|
||||
{
|
||||
root = uriPrefix.IndexOf ('/', start_host, length - start_host);
|
||||
if (root == -1)
|
||||
throw new ArgumentException ("No path specified.");
|
||||
}
|
||||
|
||||
if (uri [uri.Length - 1] != '/')
|
||||
throw new ArgumentException ("The prefix must end with '/'.");
|
||||
if (uriPrefix [uriPrefix.Length - 1] != '/')
|
||||
throw new ArgumentException ("The URI prefix must end with '/'.");
|
||||
}
|
||||
|
||||
// Equals and GetHashCode are required to detect duplicates in HttpListenerPrefixCollection.
|
||||
public override bool Equals (object o)
|
||||
public override bool Equals (object obj)
|
||||
{
|
||||
var other = o as ListenerPrefix;
|
||||
var other = obj as ListenerPrefix;
|
||||
if (other == null)
|
||||
return false;
|
||||
|
||||
return (original == other.original);
|
||||
return _original == other._original;
|
||||
}
|
||||
|
||||
public override int GetHashCode ()
|
||||
{
|
||||
return original.GetHashCode ();
|
||||
return _original.GetHashCode ();
|
||||
}
|
||||
|
||||
public override string ToString ()
|
||||
{
|
||||
return original;
|
||||
return _original;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -30,6 +30,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using WebSocketSharp.Net;
|
||||
|
||||
@ -51,7 +52,8 @@ namespace WebSocketSharp.Server
|
||||
private int _port;
|
||||
private Thread _receiveRequestThread;
|
||||
private string _rootPath;
|
||||
private ServiceHostManager _svcHosts;
|
||||
private bool _secure;
|
||||
private ServiceHostManager _serviceHosts;
|
||||
private bool _windows;
|
||||
|
||||
#endregion
|
||||
@ -74,9 +76,42 @@ namespace WebSocketSharp.Server
|
||||
/// <param name="port">
|
||||
/// An <see cref="int"/> that contains a port number.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// <paramref name="port"/> is 0 or less, or 65536 or greater.
|
||||
/// </exception>
|
||||
public HttpServer (int port)
|
||||
: this (port, port == 443 ? true : false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HttpServer"/> class that listens for incoming requests
|
||||
/// on the specified <paramref name="port"/> and <paramref name="secure"/>.
|
||||
/// </summary>
|
||||
/// <param name="port">
|
||||
/// An <see cref="int"/> that contains a port number.
|
||||
/// </param>
|
||||
/// <param name="secure">
|
||||
/// A <see cref="bool"/> that indicates providing a secure connection or not.
|
||||
/// (<c>true</c> indicates providing a secure connection.)
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// <paramref name="port"/> is 0 or less, or 65536 or greater.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Pair of <paramref name="port"/> and <paramref name="secure"/> is invalid.
|
||||
/// </exception>
|
||||
public HttpServer (int port, bool secure)
|
||||
{
|
||||
if (!port.IsPortNumber ())
|
||||
throw new ArgumentOutOfRangeException ("port", "Invalid port number: " + port);
|
||||
|
||||
if (port == 80 && secure || port == 443 && !secure)
|
||||
throw new ArgumentException (String.Format (
|
||||
"Invalid pair of 'port' and 'secure': {0}, {1}", port, secure));
|
||||
|
||||
_port = port;
|
||||
_secure = secure;
|
||||
init ();
|
||||
}
|
||||
|
||||
@ -84,6 +119,34 @@ namespace WebSocketSharp.Server
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the certificate used to authenticate the server on the secure connection.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="X509Certificate2"/> used to authenticate the server.
|
||||
/// </value>
|
||||
public X509Certificate2 Certificate {
|
||||
get {
|
||||
return _listener.DefaultCertificate;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (EndPointListener.CertificateExists (_port, _listener.CertificateFolderPath))
|
||||
_logger.Warn ("Server certificate associated with the port number already exists.");
|
||||
|
||||
_listener.DefaultCertificate = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the server has been started.
|
||||
/// </summary>
|
||||
@ -96,6 +159,18 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the server provides secure connection.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the server provides secure connection; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsSecure {
|
||||
get {
|
||||
return _secure;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the server cleans up the inactive WebSocket service
|
||||
/// instances periodically.
|
||||
@ -106,11 +181,11 @@ namespace WebSocketSharp.Server
|
||||
/// </value>
|
||||
public bool KeepClean {
|
||||
get {
|
||||
return _svcHosts.KeepClean;
|
||||
return _serviceHosts.KeepClean;
|
||||
}
|
||||
|
||||
set {
|
||||
_svcHosts.KeepClean = value;
|
||||
_serviceHosts.KeepClean = value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,12 +227,22 @@ namespace WebSocketSharp.Server
|
||||
/// </value>
|
||||
public string RootPath {
|
||||
get {
|
||||
return _rootPath;
|
||||
return _rootPath.IsNullOrEmpty ()
|
||||
? (_rootPath = "./Public")
|
||||
: _rootPath;
|
||||
}
|
||||
|
||||
set {
|
||||
if (!_listening)
|
||||
_rootPath = value;
|
||||
if (_listening)
|
||||
{
|
||||
var msg = "The value of RootPath property cannot be changed because the server has already been started.";
|
||||
_logger.Error (msg);
|
||||
error (msg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_rootPath = value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,7 +254,7 @@ namespace WebSocketSharp.Server
|
||||
/// </value>
|
||||
public IEnumerable<string> ServicePaths {
|
||||
get {
|
||||
return _svcHosts.Paths;
|
||||
return _serviceHosts.Paths;
|
||||
}
|
||||
}
|
||||
|
||||
@ -241,15 +326,14 @@ namespace WebSocketSharp.Server
|
||||
_listener = new HttpListener ();
|
||||
_listening = false;
|
||||
_logger = new Logger ();
|
||||
_rootPath = "./Public";
|
||||
_svcHosts = new ServiceHostManager ();
|
||||
_serviceHosts = new ServiceHostManager (_logger);
|
||||
|
||||
_windows = false;
|
||||
var os = Environment.OSVersion;
|
||||
if (os.Platform != PlatformID.Unix && os.Platform != PlatformID.MacOSX)
|
||||
_windows = true;
|
||||
|
||||
var prefix = String.Format ("http{0}://*:{1}/", _port == 443 ? "s" : "", _port);
|
||||
var prefix = String.Format ("http{0}://*:{1}/", _secure ? "s" : "", _port);
|
||||
_listener.Prefixes.Add (prefix);
|
||||
}
|
||||
|
||||
@ -319,15 +403,15 @@ namespace WebSocketSharp.Server
|
||||
var wsContext = context.AcceptWebSocket ();
|
||||
var path = wsContext.Path.UrlDecode ();
|
||||
|
||||
IServiceHost svcHost;
|
||||
if (!_svcHosts.TryGetServiceHost (path, out svcHost))
|
||||
IServiceHost host;
|
||||
if (!_serviceHosts.TryGetServiceHost (path, out host))
|
||||
{
|
||||
context.Response.StatusCode = (int) HttpStatusCode.NotImplemented;
|
||||
return false;
|
||||
}
|
||||
|
||||
wsContext.WebSocket.Log = _logger;
|
||||
svcHost.BindWebSocket (wsContext);
|
||||
host.BindWebSocket (wsContext);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -385,24 +469,49 @@ namespace WebSocketSharp.Server
|
||||
_receiveRequestThread.Start ();
|
||||
}
|
||||
|
||||
private void stop (ushort code, string reason, bool ignoreArgs)
|
||||
{
|
||||
if (!ignoreArgs)
|
||||
{
|
||||
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);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_listener.Close ();
|
||||
_receiveRequestThread.Join (5 * 1000);
|
||||
if (ignoreArgs)
|
||||
_serviceHosts.Stop ();
|
||||
else
|
||||
_serviceHosts.Stop (code, reason);
|
||||
|
||||
_listening = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified typed WebSocket service.
|
||||
/// Adds the specified typed WebSocket service with the specified <paramref name="servicePath"/>.
|
||||
/// </summary>
|
||||
/// <param name="absPath">
|
||||
/// A <see cref="string"/> that contains an absolute path associated with the WebSocket service.
|
||||
/// <param name="servicePath">
|
||||
/// A <see cref="string"/> that contains an absolute path to the WebSocket service.
|
||||
/// </param>
|
||||
/// <typeparam name="T">
|
||||
/// The type of the WebSocket service. The T must inherit the <see cref="WebSocketService"/> class.
|
||||
/// </typeparam>
|
||||
public void AddWebSocketService<T> (string absPath)
|
||||
public void AddWebSocketService<T> (string servicePath)
|
||||
where T : WebSocketService, new ()
|
||||
{
|
||||
string msg;
|
||||
if (!absPath.IsValidAbsolutePath (out msg))
|
||||
if (!servicePath.IsValidAbsolutePath (out msg))
|
||||
{
|
||||
_logger.Error (msg);
|
||||
error (msg);
|
||||
@ -410,12 +519,12 @@ namespace WebSocketSharp.Server
|
||||
return;
|
||||
}
|
||||
|
||||
var svcHost = new WebSocketServiceHost<T> (_logger);
|
||||
svcHost.Uri = absPath.ToUri ();
|
||||
var host = new WebSocketServiceHost<T> (_logger);
|
||||
host.Uri = servicePath.ToUri ();
|
||||
if (!KeepClean)
|
||||
svcHost.KeepClean = false;
|
||||
host.KeepClean = false;
|
||||
|
||||
_svcHosts.Add (absPath, svcHost);
|
||||
_serviceHosts.Add (servicePath, host);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -430,7 +539,7 @@ namespace WebSocketSharp.Server
|
||||
/// </param>
|
||||
public byte[] GetFile (string path)
|
||||
{
|
||||
var filePath = _rootPath + path;
|
||||
var filePath = RootPath + path;
|
||||
if (_windows)
|
||||
filePath = filePath.Replace ("/", "\\");
|
||||
|
||||
@ -439,6 +548,29 @@ namespace WebSocketSharp.Server
|
||||
: null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the WebSocket service with the specified <paramref name="servicePath"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the WebSocket service is successfully found and removed; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <param name="servicePath">
|
||||
/// A <see cref="string"/> that contains an absolute path to the WebSocket service to find.
|
||||
/// </param>
|
||||
public bool RemoveWebSocketService (string servicePath)
|
||||
{
|
||||
if (servicePath.IsNullOrEmpty ())
|
||||
{
|
||||
var msg = "'servicePath' must not be null or empty.";
|
||||
_logger.Error (msg);
|
||||
error (msg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return _serviceHosts.Remove (servicePath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts to receive the HTTP requests.
|
||||
/// </summary>
|
||||
@ -447,6 +579,18 @@ namespace WebSocketSharp.Server
|
||||
if (_listening)
|
||||
return;
|
||||
|
||||
if (_secure &&
|
||||
!EndPointListener.CertificateExists (_port, _listener.CertificateFolderPath) &&
|
||||
Certificate == null
|
||||
)
|
||||
{
|
||||
var msg = "Secure connection requires a server certificate.";
|
||||
_logger.Error (msg);
|
||||
error (msg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_listener.Start ();
|
||||
startReceiveRequestThread ();
|
||||
_listening = true;
|
||||
@ -460,10 +604,52 @@ namespace WebSocketSharp.Server
|
||||
if (!_listening)
|
||||
return;
|
||||
|
||||
_listener.Close ();
|
||||
_receiveRequestThread.Join (5 * 1000);
|
||||
_svcHosts.Stop ();
|
||||
_listening = false;
|
||||
stop (0, null, true);
|
||||
}
|
||||
|
||||
/// <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 contains a status code indicating the reason for stop.
|
||||
/// </param>
|
||||
/// <param name="reason">
|
||||
/// A <see cref="string"/> that contains the reason for stop.
|
||||
/// </param>
|
||||
public void Stop (ushort code, string reason)
|
||||
{
|
||||
if (!_listening)
|
||||
return;
|
||||
|
||||
if (!code.IsCloseStatusCode ())
|
||||
{
|
||||
var msg = "Invalid status code for stop.";
|
||||
_logger.Error (String.Format ("{0}\ncode: {1}", msg, code));
|
||||
error (msg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
stop (code, reason, false);
|
||||
}
|
||||
|
||||
/// <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">
|
||||
/// A <see cref="CloseStatusCode"/> that contains a status code indicating the reason for stop.
|
||||
/// </param>
|
||||
/// <param name="reason">
|
||||
/// A <see cref="string"/> that contains the reason for stop.
|
||||
/// </param>
|
||||
public void Stop (CloseStatusCode code, string reason)
|
||||
{
|
||||
if (!_listening)
|
||||
return;
|
||||
|
||||
stop ((ushort) code, reason, false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -246,7 +246,7 @@ namespace WebSocketSharp.Server
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified typed WebSocket service.
|
||||
/// Adds the specified typed WebSocket service with the specified <paramref name="servicePath"/>.
|
||||
/// </summary>
|
||||
/// <param name="servicePath">
|
||||
/// A <see cref="string"/> that contains an absolute path to the WebSocket service.
|
||||
|
@ -350,6 +350,7 @@ namespace WebSocketSharp.Server
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
client.Close ();
|
||||
_logger.Fatal (ex.Message);
|
||||
error ("An exception has occured.");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user