Refactored EndPointListener.cs

This commit is contained in:
sta 2014-10-16 14:44:09 +09:00
parent 3dee61f5f8
commit 292a904ad7

View File

@ -51,41 +51,52 @@ namespace WebSocketSharp.Net
{ {
internal sealed class EndPointListener internal sealed class EndPointListener
{ {
#region Private Static Fields
private static readonly string _defaultCertFolderPath =
Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
#endregion
#region Private Fields #region Private Fields
private List<ListenerPrefix> _all; // host = '+' private List<ListenerPrefix> _all; // host == '+'
private X509Certificate2 _cert; private X509Certificate2 _cert;
private static readonly string _defaultCertFolderPath;
private IPEndPoint _endpoint; private IPEndPoint _endpoint;
private Dictionary<ListenerPrefix, HttpListener> _prefixes; private Dictionary<ListenerPrefix, HttpListener> _prefixes;
private bool _secure; private bool _secure;
private Socket _socket; private Socket _socket;
private List<ListenerPrefix> _unhandled; // host = '*' private List<ListenerPrefix> _unhandled; // host == '*'
private Dictionary<HttpConnection, HttpConnection> _unregistered; private Dictionary<HttpConnection, HttpConnection> _unregistered;
private object _unregisteredSync;
#endregion
#region Static Constructor
static EndPointListener ()
{
_defaultCertFolderPath =
Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
}
#endregion #endregion
#region Public Constructors #region Public Constructors
public EndPointListener ( public EndPointListener (
IPAddress address, int port, bool secure, string certFolderPath, X509Certificate2 defaultCert) IPAddress address,
int port,
bool secure,
string certificateFolderPath,
X509Certificate2 defaultCertificate)
{ {
if (secure) { if (secure) {
_secure = secure; _secure = secure;
_cert = getCertificate (port, certFolderPath, defaultCert); _cert = getCertificate (port, certificateFolderPath, defaultCertificate);
if (_cert == null) if (_cert == null)
throw new ArgumentException ("No server certificate found."); throw new ArgumentException ("No server certificate could be found.");
} }
_endpoint = new IPEndPoint (address, port); _endpoint = new IPEndPoint (address, port);
_prefixes = new Dictionary<ListenerPrefix, HttpListener> (); _prefixes = new Dictionary<ListenerPrefix, HttpListener> ();
_unregistered = new Dictionary<HttpConnection, HttpConnection> (); _unregistered = new Dictionary<HttpConnection, HttpConnection> ();
_unregisteredSync = ((ICollection) _unregistered).SyncRoot;
_socket = new Socket (address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); _socket = new Socket (address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
_socket.Bind (_endpoint); _socket.Bind (_endpoint);
@ -124,8 +135,8 @@ namespace WebSocketSharp.Net
var path = prefix.Path; var path = prefix.Path;
foreach (var pref in prefixes) foreach (var pref in prefixes)
if (pref.Path == path) // TODO: Code? if (pref.Path == path)
throw new HttpListenerException (400, "Prefix already in use."); throw new HttpListenerException (400, "The prefix is already in use."); // TODO: Code?
prefixes.Add (prefix); prefixes.Add (prefix);
} }
@ -146,9 +157,9 @@ namespace WebSocketSharp.Net
private static RSACryptoServiceProvider createRSAFromFile (string filename) private static RSACryptoServiceProvider createRSAFromFile (string filename)
{ {
byte [] pvk = null; byte[] pvk = null;
using (var 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]; pvk = new byte[fs.Length];
fs.Read (pvk, 0, pvk.Length); fs.Read (pvk, 0, pvk.Length);
} }
@ -159,14 +170,14 @@ namespace WebSocketSharp.Net
} }
private static X509Certificate2 getCertificate ( private static X509Certificate2 getCertificate (
int port, string certFolderPath, X509Certificate2 defaultCert) int port, string certificateFolderPath, X509Certificate2 defaultCertificate)
{ {
if (certFolderPath == null || certFolderPath.Length == 0) if (certificateFolderPath == null || certificateFolderPath.Length == 0)
certFolderPath = _defaultCertFolderPath; certificateFolderPath = _defaultCertFolderPath;
try { try {
var cer = Path.Combine (certFolderPath, String.Format ("{0}.cer", port)); var cer = Path.Combine (certificateFolderPath, String.Format ("{0}.cer", port));
var key = Path.Combine (certFolderPath, String.Format ("{0}.key", port)); var key = Path.Combine (certificateFolderPath, String.Format ("{0}.key", port));
if (File.Exists (cer) && File.Exists (key)) { if (File.Exists (cer) && File.Exists (key)) {
var cert = new X509Certificate2 (cer); var cert = new X509Certificate2 (cer);
cert.PrivateKey = createRSAFromFile (key); cert.PrivateKey = createRSAFromFile (key);
@ -177,7 +188,7 @@ namespace WebSocketSharp.Net
catch { catch {
} }
return defaultCert; return defaultCertificate;
} }
private static HttpListener matchFromList ( private static HttpListener matchFromList (
@ -188,14 +199,14 @@ namespace WebSocketSharp.Net
return null; return null;
HttpListener bestMatch = null; HttpListener bestMatch = null;
var bestLength = -1; var bestLen = -1;
foreach (var pref in list) { foreach (var pref in list) {
var ppath = pref.Path; var ppath = pref.Path;
if (ppath.Length < bestLength) if (ppath.Length < bestLen)
continue; continue;
if (path.StartsWith (ppath)) { if (path.StartsWith (ppath)) {
bestLength = ppath.Length; bestLen = ppath.Length;
bestMatch = pref.Listener; bestMatch = pref.Listener;
prefix = pref; prefix = pref;
} }
@ -207,7 +218,7 @@ namespace WebSocketSharp.Net
private static void onAccept (object sender, EventArgs e) private static void onAccept (object sender, EventArgs e)
{ {
var args = (SocketAsyncEventArgs) e; var args = (SocketAsyncEventArgs) e;
var listener = (EndPointListener) args.UserToken; var epl = (EndPointListener) args.UserToken;
Socket accepted = null; Socket accepted = null;
if (args.SocketError == SocketError.Success) { if (args.SocketError == SocketError.Success) {
accepted = args.AcceptSocket; accepted = args.AcceptSocket;
@ -215,7 +226,7 @@ namespace WebSocketSharp.Net
} }
try { try {
listener._socket.AcceptAsync (args); epl._socket.AcceptAsync (args);
} }
catch { catch {
if (accepted != null) if (accepted != null)
@ -229,9 +240,9 @@ namespace WebSocketSharp.Net
HttpConnection conn = null; HttpConnection conn = null;
try { try {
conn = new HttpConnection (accepted, listener); conn = new HttpConnection (accepted, epl);
lock (((ICollection) listener._unregistered).SyncRoot) lock (epl._unregisteredSync)
listener._unregistered [conn] = conn; epl._unregistered[conn] = conn;
conn.BeginReadRequest (); conn.BeginReadRequest ();
} }
@ -251,9 +262,9 @@ namespace WebSocketSharp.Net
return false; return false;
var path = prefix.Path; var path = prefix.Path;
var count = prefixes.Count; var cnt = prefixes.Count;
for (int i = 0; i < count; i++) { for (var i = 0; i < cnt; i++) {
if (prefixes [i].Path == path) { if (prefixes[i].Path == path) {
prefixes.RemoveAt (i); prefixes.RemoveAt (i);
return true; return true;
} }
@ -271,27 +282,27 @@ namespace WebSocketSharp.Net
var host = uri.Host; var host = uri.Host;
var port = uri.Port; var port = uri.Port;
var path = HttpUtility.UrlDecode (uri.AbsolutePath); var path = HttpUtility.UrlDecode (uri.AbsolutePath);
var pathSlash = path [path.Length - 1] == '/' ? path : path + "/"; var pathSlash = path[path.Length - 1] == '/' ? path : path + "/";
HttpListener bestMatch = null; HttpListener bestMatch = null;
var bestLength = -1; var bestLen = -1;
if (host != null && host.Length > 0) { if (host != null && host.Length > 0) {
foreach (var pref in _prefixes.Keys) { foreach (var pref in _prefixes.Keys) {
var ppath = pref.Path; var ppath = pref.Path;
if (ppath.Length < bestLength) if (ppath.Length < bestLen)
continue; continue;
if (pref.Host != host || pref.Port != port) if (pref.Host != host || pref.Port != port)
continue; continue;
if (path.StartsWith (ppath) || pathSlash.StartsWith (ppath)) { if (path.StartsWith (ppath) || pathSlash.StartsWith (ppath)) {
bestLength = ppath.Length; bestLen = ppath.Length;
bestMatch = _prefixes [pref]; bestMatch = _prefixes[pref];
prefix = pref; prefix = pref;
} }
} }
if (bestLength != -1) if (bestLen != -1)
return bestMatch; return bestMatch;
} }
@ -318,20 +329,20 @@ namespace WebSocketSharp.Net
#region Internal Methods #region Internal Methods
internal static bool CertificateExists (int port, string certFolderPath) internal static bool CertificateExists (int port, string certificateFolderPath)
{ {
if (certFolderPath == null || certFolderPath.Length == 0) if (certificateFolderPath == null || certificateFolderPath.Length == 0)
certFolderPath = _defaultCertFolderPath; certificateFolderPath = _defaultCertFolderPath;
var cer = Path.Combine (certFolderPath, String.Format ("{0}.cer", port)); var cer = Path.Combine (certificateFolderPath, String.Format ("{0}.cer", port));
var key = Path.Combine (certFolderPath, String.Format ("{0}.key", port)); var key = Path.Combine (certificateFolderPath, String.Format ("{0}.key", port));
return File.Exists (cer) && File.Exists (key); return File.Exists (cer) && File.Exists (key);
} }
internal void RemoveConnection (HttpConnection connection) internal void RemoveConnection (HttpConnection connection)
{ {
lock (((ICollection) _unregistered).SyncRoot) lock (_unregisteredSync)
_unregistered.Remove (connection); _unregistered.Remove (connection);
} }
@ -339,7 +350,7 @@ namespace WebSocketSharp.Net
#region Public Methods #region Public Methods
public void AddPrefix (ListenerPrefix prefix, HttpListener listener) public void AddPrefix (ListenerPrefix prefix, HttpListener httpListener)
{ {
List<ListenerPrefix> current, future; List<ListenerPrefix> current, future;
if (prefix.Host == "*") { if (prefix.Host == "*") {
@ -349,7 +360,7 @@ namespace WebSocketSharp.Net
? new List<ListenerPrefix> (current) ? new List<ListenerPrefix> (current)
: new List<ListenerPrefix> (); : new List<ListenerPrefix> ();
prefix.Listener = listener; prefix.Listener = httpListener;
addSpecial (future, prefix); addSpecial (future, prefix);
} }
while (Interlocked.CompareExchange (ref _unhandled, future, current) != current); while (Interlocked.CompareExchange (ref _unhandled, future, current) != current);
@ -364,7 +375,7 @@ namespace WebSocketSharp.Net
? new List<ListenerPrefix> (current) ? new List<ListenerPrefix> (current)
: new List<ListenerPrefix> (); : new List<ListenerPrefix> ();
prefix.Listener = listener; prefix.Listener = httpListener;
addSpecial (future, prefix); addSpecial (future, prefix);
} }
while (Interlocked.CompareExchange (ref _all, future, current) != current); while (Interlocked.CompareExchange (ref _all, future, current) != current);
@ -376,28 +387,29 @@ namespace WebSocketSharp.Net
do { do {
prefs = _prefixes; prefs = _prefixes;
if (prefs.ContainsKey (prefix)) { if (prefs.ContainsKey (prefix)) {
var other = prefs [prefix]; var other = prefs[prefix];
if (other != listener) // TODO: Code? if (other != httpListener)
throw new HttpListenerException (400, "There's another listener for " + prefix); throw new HttpListenerException (
400, String.Format ("There's another listener for {0}.", prefix)); // TODO: Code?
return; return;
} }
prefs2 = new Dictionary<ListenerPrefix, HttpListener> (prefs); prefs2 = new Dictionary<ListenerPrefix, HttpListener> (prefs);
prefs2 [prefix] = listener; prefs2[prefix] = httpListener;
} }
while (Interlocked.CompareExchange (ref _prefixes, prefs2, prefs) != prefs); while (Interlocked.CompareExchange (ref _prefixes, prefs2, prefs) != prefs);
} }
public bool BindContext (HttpListenerContext context) public bool BindContext (HttpListenerContext context)
{ {
ListenerPrefix prefix; ListenerPrefix pref;
var listener = searchListener (context.Request.Url, out prefix); var httpl = searchListener (context.Request.Url, out pref);
if (listener == null) if (httpl == null)
return false; return false;
context.Listener = listener; context.Listener = httpl;
context.Connection.Prefix = prefix; context.Connection.Prefix = pref;
return true; return true;
} }
@ -406,7 +418,7 @@ namespace WebSocketSharp.Net
{ {
_socket.Close (); _socket.Close ();
lock (((ICollection) _unregistered).SyncRoot) { lock (_unregisteredSync) {
var conns = new List<HttpConnection> (_unregistered.Keys); var conns = new List<HttpConnection> (_unregistered.Keys);
_unregistered.Clear (); _unregistered.Clear ();
foreach (var conn in conns) foreach (var conn in conns)
@ -416,7 +428,7 @@ namespace WebSocketSharp.Net
} }
} }
public void RemovePrefix (ListenerPrefix prefix, HttpListener listener) public void RemovePrefix (ListenerPrefix prefix, HttpListener httpListener)
{ {
List<ListenerPrefix> current, future; List<ListenerPrefix> current, future;
if (prefix.Host == "*") { if (prefix.Host == "*") {