Refactored HttpListener.cs

This commit is contained in:
sta 2014-10-12 18:23:19 +09:00
parent 468c3ab881
commit 157dcf26b4

View File

@ -58,8 +58,10 @@ namespace WebSocketSharp.Net
private Func<HttpListenerRequest, AuthenticationSchemes> _authSchemeSelector; private Func<HttpListenerRequest, AuthenticationSchemes> _authSchemeSelector;
private string _certFolderPath; private string _certFolderPath;
private Dictionary<HttpConnection, HttpConnection> _connections; private Dictionary<HttpConnection, HttpConnection> _connections;
private List<HttpListenerContext> _contextQueue; private object _connectionsSync;
private Func<IIdentity, NetworkCredential> _credentialsFinder; private List<HttpListenerContext> _ctxQueue;
private object _ctxQueueSync;
private Func<IIdentity, NetworkCredential> _credFinder;
private X509Certificate2 _defaultCert; private X509Certificate2 _defaultCert;
private bool _disposed; private bool _disposed;
private bool _ignoreWriteExceptions; private bool _ignoreWriteExceptions;
@ -67,7 +69,9 @@ namespace WebSocketSharp.Net
private HttpListenerPrefixCollection _prefixes; private HttpListenerPrefixCollection _prefixes;
private string _realm; private string _realm;
private Dictionary<HttpListenerContext, HttpListenerContext> _registry; private Dictionary<HttpListenerContext, HttpListenerContext> _registry;
private object _registrySync;
private List<ListenerAsyncResult> _waitQueue; private List<ListenerAsyncResult> _waitQueue;
private object _waitQueueSync;
#endregion #endregion
@ -79,11 +83,20 @@ namespace WebSocketSharp.Net
public HttpListener () public HttpListener ()
{ {
_authSchemes = AuthenticationSchemes.Anonymous; _authSchemes = AuthenticationSchemes.Anonymous;
_connections = new Dictionary<HttpConnection, HttpConnection> (); _connections = new Dictionary<HttpConnection, HttpConnection> ();
_contextQueue = new List<HttpListenerContext> (); _connectionsSync = ((ICollection) _connections).SyncRoot;
_ctxQueue = new List<HttpListenerContext> ();
_ctxQueueSync = ((ICollection) _ctxQueue).SyncRoot;
_prefixes = new HttpListenerPrefixCollection (this); _prefixes = new HttpListenerPrefixCollection (this);
_registry = new Dictionary<HttpListenerContext, HttpListenerContext> (); _registry = new Dictionary<HttpListenerContext, HttpListenerContext> ();
_registrySync = ((ICollection) _registry).SyncRoot;
_waitQueue = new List<ListenerAsyncResult> (); _waitQueue = new List<ListenerAsyncResult> ();
_waitQueueSync = ((ICollection) _waitQueue).SyncRoot;
} }
#endregion #endregion
@ -335,7 +348,7 @@ namespace WebSocketSharp.Net
/// </summary> /// </summary>
/// <value> /// <value>
/// A <c>Func&lt;<see cref="IIdentity"/>, <see cref="NetworkCredential"/>&gt;</c> delegate /// A <c>Func&lt;<see cref="IIdentity"/>, <see cref="NetworkCredential"/>&gt;</c> delegate
/// that invokes the method(s) used to find the credentials. The default value is a function /// that invokes the method used to find the credentials. The default value is a function
/// that only returns <see langword="null"/>. /// that only returns <see langword="null"/>.
/// </value> /// </value>
/// <exception cref="ObjectDisposedException"> /// <exception cref="ObjectDisposedException">
@ -344,12 +357,12 @@ namespace WebSocketSharp.Net
public Func<IIdentity, NetworkCredential> UserCredentialsFinder { public Func<IIdentity, NetworkCredential> UserCredentialsFinder {
get { get {
CheckDisposed (); CheckDisposed ();
return _credentialsFinder ?? (_credentialsFinder = identity => null); return _credFinder ?? (_credFinder = id => null);
} }
set { set {
CheckDisposed (); CheckDisposed ();
_credentialsFinder = value; _credFinder = value;
} }
} }
@ -359,7 +372,7 @@ namespace WebSocketSharp.Net
private void cleanup (bool force) private void cleanup (bool force)
{ {
lock (((ICollection) _registry).SyncRoot) { lock (_registrySync) {
if (!force) if (!force)
sendServiceUnavailable (); sendServiceUnavailable ();
@ -371,39 +384,39 @@ namespace WebSocketSharp.Net
private void cleanupConnections () private void cleanupConnections ()
{ {
lock (((ICollection) _connections).SyncRoot) { lock (_connectionsSync) {
if (_connections.Count == 0) if (_connections.Count == 0)
return; return;
// Need to copy this since closing will call RemoveConnection. // Need to copy this since closing will call RemoveConnection.
var keys = _connections.Keys; var keys = _connections.Keys;
var conns = new HttpConnection [keys.Count]; var conns = new HttpConnection[keys.Count];
keys.CopyTo (conns, 0); keys.CopyTo (conns, 0);
_connections.Clear (); _connections.Clear ();
for (var i = conns.Length - 1; i >= 0; i--) for (var i = conns.Length - 1; i >= 0; i--)
conns [i].Close (true); conns[i].Close (true);
} }
} }
private void cleanupContextRegistry () private void cleanupContextRegistry ()
{ {
lock (((ICollection) _registry).SyncRoot) { lock (_registrySync) {
if (_registry.Count == 0) if (_registry.Count == 0)
return; return;
// Need to copy this since closing will call UnregisterContext. // Need to copy this since closing will call UnregisterContext.
var keys = _registry.Keys; var keys = _registry.Keys;
var all = new HttpListenerContext [keys.Count]; var all = new HttpListenerContext[keys.Count];
keys.CopyTo (all, 0); keys.CopyTo (all, 0);
_registry.Clear (); _registry.Clear ();
for (var i = all.Length - 1; i >= 0; i--) for (var i = all.Length - 1; i >= 0; i--)
all [i].Connection.Close (true); all[i].Connection.Close (true);
} }
} }
private void cleanupWaitQueue () private void cleanupWaitQueue ()
{ {
lock (((ICollection) _waitQueue).SyncRoot) { lock (_waitQueueSync) {
if (_waitQueue.Count == 0) if (_waitQueue.Count == 0)
return; return;
@ -421,28 +434,28 @@ namespace WebSocketSharp.Net
cleanup (force); cleanup (force);
} }
// Must be called with a lock on _contextQueue. // Must be called with a lock on _ctxQueue.
private HttpListenerContext getContextFromQueue () private HttpListenerContext getContextFromQueue ()
{ {
if (_contextQueue.Count == 0) if (_ctxQueue.Count == 0)
return null; return null;
var context = _contextQueue [0]; var ctx = _ctxQueue[0];
_contextQueue.RemoveAt (0); _ctxQueue.RemoveAt (0);
return context; return ctx;
} }
private void sendServiceUnavailable () private void sendServiceUnavailable ()
{ {
lock (((ICollection) _contextQueue).SyncRoot) { lock (_ctxQueueSync) {
if (_contextQueue.Count == 0) if (_ctxQueue.Count == 0)
return; return;
var contexts = _contextQueue.ToArray (); var ctxs = _ctxQueue.ToArray ();
_contextQueue.Clear (); _ctxQueue.Clear ();
foreach (var context in contexts) { foreach (var ctx in ctxs) {
var res = context.Response; var res = ctx.Response;
res.StatusCode = (int) HttpStatusCode.ServiceUnavailable; res.StatusCode = (int) HttpStatusCode.ServiceUnavailable;
res.Close (); res.Close ();
} }
@ -455,8 +468,8 @@ namespace WebSocketSharp.Net
internal void AddConnection (HttpConnection connection) internal void AddConnection (HttpConnection connection)
{ {
lock (((ICollection) _connections).SyncRoot) lock (_connectionsSync)
_connections [connection] = connection; _connections[connection] = connection;
} }
internal ListenerAsyncResult BeginGetContext (ListenerAsyncResult asyncResult) internal ListenerAsyncResult BeginGetContext (ListenerAsyncResult asyncResult)
@ -469,11 +482,11 @@ namespace WebSocketSharp.Net
throw new InvalidOperationException ("Please, call Start before using this method."); throw new InvalidOperationException ("Please, call Start before using this method.");
// Lock _waitQueue early to avoid race conditions. // Lock _waitQueue early to avoid race conditions.
lock (((ICollection) _waitQueue).SyncRoot) { lock (_waitQueueSync) {
lock (((ICollection) _contextQueue).SyncRoot) { lock (_ctxQueueSync) {
var context = getContextFromQueue (); var ctx = getContextFromQueue ();
if (context != null) { if (ctx != null) {
asyncResult.Complete (context, true); asyncResult.Complete (ctx, true);
return asyncResult; return asyncResult;
} }
} }
@ -492,17 +505,17 @@ namespace WebSocketSharp.Net
internal void RegisterContext (HttpListenerContext context) internal void RegisterContext (HttpListenerContext context)
{ {
lock (((ICollection) _registry).SyncRoot) lock (_registrySync)
_registry [context] = context; _registry[context] = context;
ListenerAsyncResult ares = null; ListenerAsyncResult ares = null;
lock (((ICollection) _waitQueue).SyncRoot) { lock (_waitQueueSync) {
if (_waitQueue.Count == 0) { if (_waitQueue.Count == 0) {
lock (((ICollection) _contextQueue).SyncRoot) lock (_ctxQueueSync)
_contextQueue.Add (context); _ctxQueue.Add (context);
} }
else { else {
ares = _waitQueue [0]; ares = _waitQueue[0];
_waitQueue.RemoveAt (0); _waitQueue.RemoveAt (0);
} }
} }
@ -513,7 +526,7 @@ namespace WebSocketSharp.Net
internal void RemoveConnection (HttpConnection connection) internal void RemoveConnection (HttpConnection connection)
{ {
lock (((ICollection) _connections).SyncRoot) lock (_connectionsSync)
_connections.Remove (connection); _connections.Remove (connection);
} }
@ -526,13 +539,13 @@ namespace WebSocketSharp.Net
internal void UnregisterContext (HttpListenerContext context) internal void UnregisterContext (HttpListenerContext context)
{ {
lock (((ICollection) _registry).SyncRoot) lock (_registrySync)
_registry.Remove (context); _registry.Remove (context);
lock (((ICollection) _contextQueue).SyncRoot) { lock (_ctxQueueSync) {
var i = _contextQueue.IndexOf (context); var i = _ctxQueue.IndexOf (context);
if (i >= 0) if (i >= 0)
_contextQueue.RemoveAt (i); _ctxQueue.RemoveAt (i);
} }
} }
@ -643,18 +656,18 @@ namespace WebSocketSharp.Net
if (!ares.IsCompleted) if (!ares.IsCompleted)
ares.AsyncWaitHandle.WaitOne (); ares.AsyncWaitHandle.WaitOne ();
lock (((ICollection) _waitQueue).SyncRoot) { lock (_waitQueueSync) {
var i = _waitQueue.IndexOf (ares); var i = _waitQueue.IndexOf (ares);
if (i >= 0) if (i >= 0)
_waitQueue.RemoveAt (i); _waitQueue.RemoveAt (i);
} }
var context = ares.GetContext (); var ctx = ares.GetContext ();
var scheme = SelectAuthenticationScheme (context); var schm = SelectAuthenticationScheme (ctx);
if (scheme != AuthenticationSchemes.Anonymous) if (schm != AuthenticationSchemes.Anonymous)
context.SetUser (scheme, Realm, UserCredentialsFinder); ctx.SetUser (schm, Realm, UserCredentialsFinder);
return context; // This will throw on error. return ctx; // This will throw on error.
} }
/// <summary> /// <summary>