EndPointListener.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Net;
  6. using System.Net.Sockets;
  7. using System.Security.Cryptography.X509Certificates;
  8. using System.Threading;
  9. using MediaBrowser.Model.Cryptography;
  10. using MediaBrowser.Model.IO;
  11. using MediaBrowser.Model.Logging;
  12. using MediaBrowser.Model.Net;
  13. using MediaBrowser.Model.System;
  14. using MediaBrowser.Model.Text;
  15. using SocketHttpListener.Primitives;
  16. using ProtocolType = MediaBrowser.Model.Net.ProtocolType;
  17. using SocketType = MediaBrowser.Model.Net.SocketType;
  18. namespace SocketHttpListener.Net
  19. {
  20. sealed class EndPointListener
  21. {
  22. HttpListener listener;
  23. IPEndPoint endpoint;
  24. Socket sock;
  25. Dictionary<ListenerPrefix, HttpListener> prefixes; // Dictionary <ListenerPrefix, HttpListener>
  26. List<ListenerPrefix> unhandled; // List<ListenerPrefix> unhandled; host = '*'
  27. List<ListenerPrefix> all; // List<ListenerPrefix> all; host = '+'
  28. X509Certificate cert;
  29. bool secure;
  30. Dictionary<HttpConnection, HttpConnection> unregistered;
  31. private readonly ILogger _logger;
  32. private bool _closed;
  33. private bool _enableDualMode;
  34. private readonly ICryptoProvider _cryptoProvider;
  35. private readonly ISocketFactory _socketFactory;
  36. private readonly ITextEncoding _textEncoding;
  37. private readonly IMemoryStreamFactory _memoryStreamFactory;
  38. private readonly IFileSystem _fileSystem;
  39. private readonly IEnvironmentInfo _environment;
  40. public EndPointListener(HttpListener listener, IPAddress addr, int port, bool secure, X509Certificate cert, ILogger logger, ICryptoProvider cryptoProvider, ISocketFactory socketFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IEnvironmentInfo environment)
  41. {
  42. this.listener = listener;
  43. _logger = logger;
  44. _cryptoProvider = cryptoProvider;
  45. _socketFactory = socketFactory;
  46. _memoryStreamFactory = memoryStreamFactory;
  47. _textEncoding = textEncoding;
  48. _fileSystem = fileSystem;
  49. _environment = environment;
  50. this.secure = secure;
  51. this.cert = cert;
  52. _enableDualMode = addr.Equals(IPAddress.IPv6Any);
  53. endpoint = new IPEndPoint(addr, port);
  54. prefixes = new Dictionary<ListenerPrefix, HttpListener>();
  55. unregistered = new Dictionary<HttpConnection, HttpConnection>();
  56. CreateSocket();
  57. }
  58. internal HttpListener Listener
  59. {
  60. get
  61. {
  62. return listener;
  63. }
  64. }
  65. private void CreateSocket()
  66. {
  67. try
  68. {
  69. sock = CreateSocket(endpoint.Address.AddressFamily, _enableDualMode);
  70. }
  71. catch (SocketCreateException ex)
  72. {
  73. if (_enableDualMode && endpoint.Address.Equals(IPAddress.IPv6Any) &&
  74. (string.Equals(ex.ErrorCode, "AddressFamilyNotSupported", StringComparison.OrdinalIgnoreCase) ||
  75. // mono on bsd is throwing this
  76. string.Equals(ex.ErrorCode, "ProtocolNotSupported", StringComparison.OrdinalIgnoreCase)))
  77. {
  78. endpoint = new IPEndPoint(IPAddress.Any, endpoint.Port);
  79. _enableDualMode = false;
  80. sock = CreateSocket(endpoint.Address.AddressFamily, _enableDualMode);
  81. }
  82. else
  83. {
  84. throw;
  85. }
  86. }
  87. sock.Bind(endpoint);
  88. // This is the number TcpListener uses.
  89. sock.Listen(2147483647);
  90. new SocketAcceptor(_logger, sock, ProcessAccept, () => _closed).StartAccept();
  91. _closed = false;
  92. }
  93. private Socket CreateSocket(AddressFamily addressFamily, bool dualMode)
  94. {
  95. try
  96. {
  97. var socket = new Socket(addressFamily, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
  98. if (dualMode)
  99. {
  100. socket.DualMode = true;
  101. }
  102. return socket;
  103. }
  104. catch (SocketException ex)
  105. {
  106. throw new SocketCreateException(ex.SocketErrorCode.ToString(), ex);
  107. }
  108. catch (ArgumentException ex)
  109. {
  110. if (dualMode)
  111. {
  112. // Mono for BSD incorrectly throws ArgumentException instead of SocketException
  113. throw new SocketCreateException("AddressFamilyNotSupported", ex);
  114. }
  115. else
  116. {
  117. throw;
  118. }
  119. }
  120. }
  121. private async void ProcessAccept(Socket accepted)
  122. {
  123. try
  124. {
  125. var listener = this;
  126. if (listener.secure && listener.cert == null)
  127. {
  128. accepted.Close();
  129. return;
  130. }
  131. HttpConnection conn = await HttpConnection.Create(_logger, accepted, listener, listener.secure, listener.cert, _cryptoProvider, _memoryStreamFactory, _textEncoding, _fileSystem, _environment).ConfigureAwait(false);
  132. //_logger.Debug("Adding unregistered connection to {0}. Id: {1}", accepted.RemoteEndPoint, connectionId);
  133. lock (listener.unregistered)
  134. {
  135. listener.unregistered[conn] = conn;
  136. }
  137. conn.BeginReadRequest();
  138. }
  139. catch (Exception ex)
  140. {
  141. _logger.ErrorException("Error in ProcessAccept", ex);
  142. }
  143. }
  144. internal void RemoveConnection(HttpConnection conn)
  145. {
  146. lock (unregistered)
  147. {
  148. unregistered.Remove(conn);
  149. }
  150. }
  151. public bool BindContext(HttpListenerContext context)
  152. {
  153. HttpListenerRequest req = context.Request;
  154. ListenerPrefix prefix;
  155. HttpListener listener = SearchListener(req.Url, out prefix);
  156. if (listener == null)
  157. return false;
  158. context.Connection.Prefix = prefix;
  159. return true;
  160. }
  161. public void UnbindContext(HttpListenerContext context)
  162. {
  163. if (context == null || context.Request == null)
  164. return;
  165. listener.UnregisterContext(context);
  166. }
  167. HttpListener SearchListener(Uri uri, out ListenerPrefix prefix)
  168. {
  169. prefix = null;
  170. if (uri == null)
  171. return null;
  172. string host = uri.Host;
  173. int port = uri.Port;
  174. string path = WebUtility.UrlDecode(uri.AbsolutePath);
  175. string path_slash = path[path.Length - 1] == '/' ? path : path + "/";
  176. HttpListener best_match = null;
  177. int best_length = -1;
  178. if (host != null && host != "")
  179. {
  180. var p_ro = prefixes;
  181. foreach (ListenerPrefix p in p_ro.Keys)
  182. {
  183. string ppath = p.Path;
  184. if (ppath.Length < best_length)
  185. continue;
  186. if (p.Host != host || p.Port != port)
  187. continue;
  188. if (path.StartsWith(ppath) || path_slash.StartsWith(ppath))
  189. {
  190. best_length = ppath.Length;
  191. best_match = (HttpListener)p_ro[p];
  192. prefix = p;
  193. }
  194. }
  195. if (best_length != -1)
  196. return best_match;
  197. }
  198. List<ListenerPrefix> list = unhandled;
  199. best_match = MatchFromList(host, path, list, out prefix);
  200. if (path != path_slash && best_match == null)
  201. best_match = MatchFromList(host, path_slash, list, out prefix);
  202. if (best_match != null)
  203. return best_match;
  204. list = all;
  205. best_match = MatchFromList(host, path, list, out prefix);
  206. if (path != path_slash && best_match == null)
  207. best_match = MatchFromList(host, path_slash, list, out prefix);
  208. if (best_match != null)
  209. return best_match;
  210. return null;
  211. }
  212. HttpListener MatchFromList(string host, string path, List<ListenerPrefix> list, out ListenerPrefix prefix)
  213. {
  214. prefix = null;
  215. if (list == null)
  216. return null;
  217. HttpListener best_match = null;
  218. int best_length = -1;
  219. foreach (ListenerPrefix p in list)
  220. {
  221. string ppath = p.Path;
  222. if (ppath.Length < best_length)
  223. continue;
  224. if (path.StartsWith(ppath))
  225. {
  226. best_length = ppath.Length;
  227. best_match = p.Listener;
  228. prefix = p;
  229. }
  230. }
  231. return best_match;
  232. }
  233. void AddSpecial(List<ListenerPrefix> coll, ListenerPrefix prefix)
  234. {
  235. if (coll == null)
  236. return;
  237. foreach (ListenerPrefix p in coll)
  238. {
  239. if (p.Path == prefix.Path) //TODO: code
  240. throw new HttpListenerException(400, "Prefix already in use.");
  241. }
  242. coll.Add(prefix);
  243. }
  244. bool RemoveSpecial(List<ListenerPrefix> coll, ListenerPrefix prefix)
  245. {
  246. if (coll == null)
  247. return false;
  248. int c = coll.Count;
  249. for (int i = 0; i < c; i++)
  250. {
  251. ListenerPrefix p = (ListenerPrefix)coll[i];
  252. if (p.Path == prefix.Path)
  253. {
  254. coll.RemoveAt(i);
  255. return true;
  256. }
  257. }
  258. return false;
  259. }
  260. void CheckIfRemove()
  261. {
  262. if (prefixes.Count > 0)
  263. return;
  264. List<ListenerPrefix> list = unhandled;
  265. if (list != null && list.Count > 0)
  266. return;
  267. list = all;
  268. if (list != null && list.Count > 0)
  269. return;
  270. EndPointManager.RemoveEndPoint(this, endpoint);
  271. }
  272. public void Close()
  273. {
  274. _closed = true;
  275. sock.Close();
  276. lock (unregistered)
  277. {
  278. //
  279. // Clone the list because RemoveConnection can be called from Close
  280. //
  281. var connections = new List<HttpConnection>(unregistered.Keys);
  282. foreach (HttpConnection c in connections)
  283. c.Close(true);
  284. unregistered.Clear();
  285. }
  286. }
  287. public void AddPrefix(ListenerPrefix prefix, HttpListener listener)
  288. {
  289. List<ListenerPrefix> current;
  290. List<ListenerPrefix> future;
  291. if (prefix.Host == "*")
  292. {
  293. do
  294. {
  295. current = unhandled;
  296. future = (current != null) ? current.ToList() : new List<ListenerPrefix>();
  297. prefix.Listener = listener;
  298. AddSpecial(future, prefix);
  299. } while (Interlocked.CompareExchange(ref unhandled, future, current) != current);
  300. return;
  301. }
  302. if (prefix.Host == "+")
  303. {
  304. do
  305. {
  306. current = all;
  307. future = (current != null) ? current.ToList() : new List<ListenerPrefix>();
  308. prefix.Listener = listener;
  309. AddSpecial(future, prefix);
  310. } while (Interlocked.CompareExchange(ref all, future, current) != current);
  311. return;
  312. }
  313. Dictionary<ListenerPrefix, HttpListener> prefs;
  314. Dictionary<ListenerPrefix, HttpListener> p2;
  315. do
  316. {
  317. prefs = prefixes;
  318. if (prefs.ContainsKey(prefix))
  319. {
  320. HttpListener other = (HttpListener)prefs[prefix];
  321. if (other != listener) // TODO: code.
  322. throw new HttpListenerException(400, "There's another listener for " + prefix);
  323. return;
  324. }
  325. p2 = new Dictionary<ListenerPrefix, HttpListener>(prefs);
  326. p2[prefix] = listener;
  327. } while (Interlocked.CompareExchange(ref prefixes, p2, prefs) != prefs);
  328. }
  329. public void RemovePrefix(ListenerPrefix prefix, HttpListener listener)
  330. {
  331. List<ListenerPrefix> current;
  332. List<ListenerPrefix> future;
  333. if (prefix.Host == "*")
  334. {
  335. do
  336. {
  337. current = unhandled;
  338. future = (current != null) ? current.ToList() : new List<ListenerPrefix>();
  339. if (!RemoveSpecial(future, prefix))
  340. break; // Prefix not found
  341. } while (Interlocked.CompareExchange(ref unhandled, future, current) != current);
  342. CheckIfRemove();
  343. return;
  344. }
  345. if (prefix.Host == "+")
  346. {
  347. do
  348. {
  349. current = all;
  350. future = (current != null) ? current.ToList() : new List<ListenerPrefix>();
  351. if (!RemoveSpecial(future, prefix))
  352. break; // Prefix not found
  353. } while (Interlocked.CompareExchange(ref all, future, current) != current);
  354. CheckIfRemove();
  355. return;
  356. }
  357. Dictionary<ListenerPrefix, HttpListener> prefs;
  358. Dictionary<ListenerPrefix, HttpListener> p2;
  359. do
  360. {
  361. prefs = prefixes;
  362. if (!prefs.ContainsKey(prefix))
  363. break;
  364. p2 = new Dictionary<ListenerPrefix, HttpListener>(prefs);
  365. p2.Remove(prefix);
  366. } while (Interlocked.CompareExchange(ref prefixes, p2, prefs) != prefs);
  367. CheckIfRemove();
  368. }
  369. }
  370. }