aboutsummaryrefslogtreecommitdiff
path: root/SocketHttpListener/Net/EndPointListener.cs
diff options
context:
space:
mode:
Diffstat (limited to 'SocketHttpListener/Net/EndPointListener.cs')
-rw-r--r--SocketHttpListener/Net/EndPointListener.cs391
1 files changed, 391 insertions, 0 deletions
diff --git a/SocketHttpListener/Net/EndPointListener.cs b/SocketHttpListener/Net/EndPointListener.cs
new file mode 100644
index 000000000..2106bbec5
--- /dev/null
+++ b/SocketHttpListener/Net/EndPointListener.cs
@@ -0,0 +1,391 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Threading;
+using MediaBrowser.Model.Cryptography;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.System;
+using MediaBrowser.Model.Text;
+using SocketHttpListener.Primitives;
+
+namespace SocketHttpListener.Net
+{
+ sealed class EndPointListener
+ {
+ HttpListener listener;
+ IpEndPointInfo endpoint;
+ IAcceptSocket sock;
+ Dictionary<ListenerPrefix,HttpListener> prefixes; // Dictionary <ListenerPrefix, HttpListener>
+ List<ListenerPrefix> unhandled; // List<ListenerPrefix> unhandled; host = '*'
+ List<ListenerPrefix> all; // List<ListenerPrefix> all; host = '+'
+ ICertificate cert;
+ bool secure;
+ Dictionary<HttpConnection, HttpConnection> unregistered;
+ private readonly ILogger _logger;
+ private bool _closed;
+ private bool _enableDualMode;
+ private readonly ICryptoProvider _cryptoProvider;
+ private readonly IStreamFactory _streamFactory;
+ private readonly ISocketFactory _socketFactory;
+ private readonly ITextEncoding _textEncoding;
+ private readonly IMemoryStreamFactory _memoryStreamFactory;
+ private readonly IFileSystem _fileSystem;
+ private readonly IEnvironmentInfo _environment;
+
+ public EndPointListener(HttpListener listener, IpAddressInfo addr, int port, bool secure, ICertificate cert, ILogger logger, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IEnvironmentInfo environment)
+ {
+ this.listener = listener;
+ _logger = logger;
+ _cryptoProvider = cryptoProvider;
+ _streamFactory = streamFactory;
+ _socketFactory = socketFactory;
+ _memoryStreamFactory = memoryStreamFactory;
+ _textEncoding = textEncoding;
+ _fileSystem = fileSystem;
+ _environment = environment;
+
+ this.secure = secure;
+ this.cert = cert;
+
+ _enableDualMode = addr.Equals(IpAddressInfo.IPv6Any);
+ endpoint = new IpEndPointInfo(addr, port);
+
+ prefixes = new Dictionary<ListenerPrefix, HttpListener>();
+ unregistered = new Dictionary<HttpConnection, HttpConnection>();
+
+ CreateSocket();
+ }
+
+ internal HttpListener Listener
+ {
+ get
+ {
+ return listener;
+ }
+ }
+
+ private void CreateSocket()
+ {
+ try
+ {
+ sock = _socketFactory.CreateSocket(endpoint.IpAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp, _enableDualMode);
+ }
+ catch (SocketCreateException ex)
+ {
+ if (_enableDualMode && endpoint.IpAddress.Equals(IpAddressInfo.IPv6Any) &&
+ (string.Equals(ex.ErrorCode, "AddressFamilyNotSupported", StringComparison.OrdinalIgnoreCase) ||
+ // mono on bsd is throwing this
+ string.Equals(ex.ErrorCode, "ProtocolNotSupported", StringComparison.OrdinalIgnoreCase)))
+ {
+ endpoint = new IpEndPointInfo(IpAddressInfo.Any, endpoint.Port);
+ _enableDualMode = false;
+ sock = _socketFactory.CreateSocket(endpoint.IpAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp, _enableDualMode);
+ }
+ else
+ {
+ throw;
+ }
+ }
+
+ sock.Bind(endpoint);
+
+ // This is the number TcpListener uses.
+ sock.Listen(2147483647);
+
+ sock.StartAccept(ProcessAccept, () => _closed);
+ _closed = false;
+ }
+
+ private async void ProcessAccept(IAcceptSocket accepted)
+ {
+ try
+ {
+ var listener = this;
+
+ if (listener.secure && listener.cert == null)
+ {
+ accepted.Close();
+ return;
+ }
+
+ HttpConnection conn = await HttpConnection.Create(_logger, accepted, listener, listener.secure, listener.cert, _cryptoProvider, _streamFactory, _memoryStreamFactory, _textEncoding, _fileSystem, _environment).ConfigureAwait(false);
+
+ //_logger.Debug("Adding unregistered connection to {0}. Id: {1}", accepted.RemoteEndPoint, connectionId);
+ lock (listener.unregistered)
+ {
+ listener.unregistered[conn] = conn;
+ }
+ conn.BeginReadRequest();
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error in ProcessAccept", ex);
+ }
+ }
+
+ internal void RemoveConnection(HttpConnection conn)
+ {
+ lock (unregistered)
+ {
+ unregistered.Remove(conn);
+ }
+ }
+
+ public bool BindContext(HttpListenerContext context)
+ {
+ HttpListenerRequest req = context.Request;
+ ListenerPrefix prefix;
+ HttpListener listener = SearchListener(req.Url, out prefix);
+ if (listener == null)
+ return false;
+
+ context.Connection.Prefix = prefix;
+ return true;
+ }
+
+ public void UnbindContext(HttpListenerContext context)
+ {
+ if (context == null || context.Request == null)
+ return;
+
+ listener.UnregisterContext(context);
+ }
+
+ HttpListener SearchListener(Uri uri, out ListenerPrefix prefix)
+ {
+ prefix = null;
+ if (uri == null)
+ return null;
+
+ string host = uri.Host;
+ int port = uri.Port;
+ string path = WebUtility.UrlDecode(uri.AbsolutePath);
+ string 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;
+ if (ppath.Length < best_length)
+ continue;
+
+ if (p.Host != host || p.Port != port)
+ continue;
+
+ if (path.StartsWith(ppath) || path_slash.StartsWith(ppath))
+ {
+ best_length = ppath.Length;
+ best_match = (HttpListener)p_ro[p];
+ prefix = p;
+ }
+ }
+ if (best_length != -1)
+ return best_match;
+ }
+
+ List<ListenerPrefix> 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);
+ if (best_match != null)
+ return best_match;
+
+ 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);
+ if (best_match != null)
+ return best_match;
+
+ return null;
+ }
+
+ HttpListener MatchFromList(string host, string path, List<ListenerPrefix> list, out ListenerPrefix prefix)
+ {
+ prefix = null;
+ if (list == null)
+ return null;
+
+ HttpListener best_match = null;
+ int best_length = -1;
+
+ foreach (ListenerPrefix p in list)
+ {
+ string ppath = p.Path;
+ if (ppath.Length < best_length)
+ continue;
+
+ if (path.StartsWith(ppath))
+ {
+ best_length = ppath.Length;
+ best_match = p.Listener;
+ prefix = p;
+ }
+ }
+
+ return best_match;
+ }
+
+ void AddSpecial(List<ListenerPrefix> coll, ListenerPrefix prefix)
+ {
+ if (coll == null)
+ return;
+
+ foreach (ListenerPrefix p in coll)
+ {
+ if (p.Path == prefix.Path) //TODO: code
+ throw new HttpListenerException(400, "Prefix already in use.");
+ }
+ coll.Add(prefix);
+ }
+
+ bool RemoveSpecial(List<ListenerPrefix> coll, ListenerPrefix prefix)
+ {
+ if (coll == null)
+ return false;
+
+ int c = coll.Count;
+ for (int i = 0; i < c; i++)
+ {
+ ListenerPrefix p = (ListenerPrefix)coll[i];
+ if (p.Path == prefix.Path)
+ {
+ coll.RemoveAt(i);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void CheckIfRemove()
+ {
+ if (prefixes.Count > 0)
+ return;
+
+ List<ListenerPrefix> list = unhandled;
+ if (list != null && list.Count > 0)
+ return;
+
+ list = all;
+ if (list != null && list.Count > 0)
+ return;
+
+ EndPointManager.RemoveEndPoint(this, endpoint);
+ }
+
+ public void Close()
+ {
+ _closed = true;
+ sock.Close();
+ lock (unregistered)
+ {
+ //
+ // Clone the list because RemoveConnection can be called from Close
+ //
+ var connections = new List<HttpConnection>(unregistered.Keys);
+
+ foreach (HttpConnection c in connections)
+ c.Close(true);
+ unregistered.Clear();
+ }
+ }
+
+ public void AddPrefix(ListenerPrefix prefix, HttpListener listener)
+ {
+ List<ListenerPrefix> current;
+ List<ListenerPrefix> future;
+ if (prefix.Host == "*")
+ {
+ do
+ {
+ current = unhandled;
+ future = (current != null) ? current.ToList() : new List<ListenerPrefix>();
+ prefix.Listener = listener;
+ AddSpecial(future, prefix);
+ } while (Interlocked.CompareExchange(ref unhandled, future, current) != current);
+ return;
+ }
+
+ if (prefix.Host == "+")
+ {
+ do
+ {
+ current = all;
+ future = (current != null) ? current.ToList() : new List<ListenerPrefix>();
+ prefix.Listener = listener;
+ AddSpecial(future, prefix);
+ } while (Interlocked.CompareExchange(ref all, future, current) != current);
+ return;
+ }
+
+ Dictionary<ListenerPrefix, HttpListener> prefs;
+ Dictionary<ListenerPrefix, HttpListener> p2;
+ do
+ {
+ prefs = prefixes;
+ if (prefs.ContainsKey(prefix))
+ {
+ HttpListener other = (HttpListener)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);
+ }
+
+ public void RemovePrefix(ListenerPrefix prefix, HttpListener listener)
+ {
+ List<ListenerPrefix> current;
+ List<ListenerPrefix> future;
+ if (prefix.Host == "*")
+ {
+ do
+ {
+ current = unhandled;
+ future = (current != null) ? current.ToList() : new List<ListenerPrefix>();
+ if (!RemoveSpecial(future, prefix))
+ break; // Prefix not found
+ } while (Interlocked.CompareExchange(ref unhandled, future, current) != current);
+ CheckIfRemove();
+ return;
+ }
+
+ if (prefix.Host == "+")
+ {
+ do
+ {
+ current = all;
+ future = (current != null) ? current.ToList() : 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;
+ Dictionary<ListenerPrefix, HttpListener> p2;
+ do
+ {
+ 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();
+ }
+ }
+}