HttpListener.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Net;
  5. using System.Security.Cryptography.X509Certificates;
  6. using MediaBrowser.Common.Net;
  7. using MediaBrowser.Model.Cryptography;
  8. using MediaBrowser.Model.IO;
  9. using MediaBrowser.Model.Net;
  10. using MediaBrowser.Model.System;
  11. using MediaBrowser.Model.Text;
  12. using Microsoft.Extensions.Logging;
  13. namespace SocketHttpListener.Net
  14. {
  15. public sealed class HttpListener : IDisposable
  16. {
  17. internal ICryptoProvider CryptoProvider { get; private set; }
  18. internal ISocketFactory SocketFactory { get; private set; }
  19. internal IFileSystem FileSystem { get; private set; }
  20. internal ITextEncoding TextEncoding { get; private set; }
  21. internal IStreamHelper StreamHelper { get; private set; }
  22. internal INetworkManager NetworkManager { get; private set; }
  23. internal IEnvironmentInfo EnvironmentInfo { get; private set; }
  24. public bool EnableDualMode { get; set; }
  25. AuthenticationSchemes auth_schemes;
  26. HttpListenerPrefixCollection prefixes;
  27. AuthenticationSchemeSelector auth_selector;
  28. string realm;
  29. bool unsafe_ntlm_auth;
  30. bool listening;
  31. bool disposed;
  32. Dictionary<HttpListenerContext, HttpListenerContext> registry; // Dictionary<HttpListenerContext,HttpListenerContext>
  33. Dictionary<HttpConnection, HttpConnection> connections;
  34. private ILogger _logger;
  35. private X509Certificate _certificate;
  36. public Action<HttpListenerContext> OnContext { get; set; }
  37. public HttpListener(ILogger logger, ICryptoProvider cryptoProvider, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IStreamHelper streamHelper, IFileSystem fileSystem, IEnvironmentInfo environmentInfo)
  38. {
  39. _logger = logger;
  40. CryptoProvider = cryptoProvider;
  41. SocketFactory = socketFactory;
  42. NetworkManager = networkManager;
  43. TextEncoding = textEncoding;
  44. StreamHelper = streamHelper;
  45. FileSystem = fileSystem;
  46. EnvironmentInfo = environmentInfo;
  47. prefixes = new HttpListenerPrefixCollection(logger, this);
  48. registry = new Dictionary<HttpListenerContext, HttpListenerContext>();
  49. connections = new Dictionary<HttpConnection, HttpConnection>();
  50. auth_schemes = AuthenticationSchemes.Anonymous;
  51. }
  52. public HttpListener(ILogger logger, X509Certificate certificate, ICryptoProvider cryptoProvider, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IStreamHelper streamHelper, IFileSystem fileSystem, IEnvironmentInfo environmentInfo)
  53. : this(logger, cryptoProvider, socketFactory, networkManager, textEncoding, streamHelper, fileSystem, environmentInfo)
  54. {
  55. _certificate = certificate;
  56. }
  57. public void LoadCert(X509Certificate cert)
  58. {
  59. _certificate = cert;
  60. }
  61. // TODO: Digest, NTLM and Negotiate require ControlPrincipal
  62. public AuthenticationSchemes AuthenticationSchemes
  63. {
  64. get => auth_schemes;
  65. set
  66. {
  67. CheckDisposed();
  68. auth_schemes = value;
  69. }
  70. }
  71. public AuthenticationSchemeSelector AuthenticationSchemeSelectorDelegate
  72. {
  73. get => auth_selector;
  74. set
  75. {
  76. CheckDisposed();
  77. auth_selector = value;
  78. }
  79. }
  80. public bool IsListening => listening;
  81. public static bool IsSupported => true;
  82. public HttpListenerPrefixCollection Prefixes
  83. {
  84. get
  85. {
  86. CheckDisposed();
  87. return prefixes;
  88. }
  89. }
  90. // TODO: use this
  91. public string Realm
  92. {
  93. get => realm;
  94. set
  95. {
  96. CheckDisposed();
  97. realm = value;
  98. }
  99. }
  100. public bool UnsafeConnectionNtlmAuthentication
  101. {
  102. get => unsafe_ntlm_auth;
  103. set
  104. {
  105. CheckDisposed();
  106. unsafe_ntlm_auth = value;
  107. }
  108. }
  109. //internal IMonoSslStream CreateSslStream(Stream innerStream, bool ownsStream, MSI.MonoRemoteCertificateValidationCallback callback)
  110. //{
  111. // lock (registry)
  112. // {
  113. // if (tlsProvider == null)
  114. // tlsProvider = MonoTlsProviderFactory.GetProviderInternal();
  115. // if (tlsSettings == null)
  116. // tlsSettings = MSI.MonoTlsSettings.CopyDefaultSettings();
  117. // if (tlsSettings.RemoteCertificateValidationCallback == null)
  118. // tlsSettings.RemoteCertificateValidationCallback = callback;
  119. // return tlsProvider.CreateSslStream(innerStream, ownsStream, tlsSettings);
  120. // }
  121. //}
  122. internal X509Certificate Certificate => _certificate;
  123. public void Abort()
  124. {
  125. if (disposed)
  126. return;
  127. if (!listening)
  128. {
  129. return;
  130. }
  131. Close(true);
  132. }
  133. public void Close()
  134. {
  135. if (disposed)
  136. return;
  137. if (!listening)
  138. {
  139. disposed = true;
  140. return;
  141. }
  142. Close(true);
  143. disposed = true;
  144. }
  145. void Close(bool force)
  146. {
  147. CheckDisposed();
  148. HttpEndPointManager.RemoveListener(_logger, this);
  149. Cleanup(force);
  150. }
  151. void Cleanup(bool close_existing)
  152. {
  153. lock (registry)
  154. {
  155. if (close_existing)
  156. {
  157. // Need to copy this since closing will call UnregisterContext
  158. ICollection keys = registry.Keys;
  159. var all = new HttpListenerContext[keys.Count];
  160. keys.CopyTo(all, 0);
  161. registry.Clear();
  162. for (int i = all.Length - 1; i >= 0; i--)
  163. all[i].Connection.Close(true);
  164. }
  165. lock (connections)
  166. {
  167. ICollection keys = connections.Keys;
  168. var conns = new HttpConnection[keys.Count];
  169. keys.CopyTo(conns, 0);
  170. connections.Clear();
  171. for (int i = conns.Length - 1; i >= 0; i--)
  172. conns[i].Close(true);
  173. }
  174. }
  175. }
  176. internal AuthenticationSchemes SelectAuthenticationScheme(HttpListenerContext context)
  177. {
  178. if (AuthenticationSchemeSelectorDelegate != null)
  179. return AuthenticationSchemeSelectorDelegate(context.Request);
  180. else
  181. return auth_schemes;
  182. }
  183. public void Start()
  184. {
  185. CheckDisposed();
  186. if (listening)
  187. return;
  188. HttpEndPointManager.AddListener(_logger, this);
  189. listening = true;
  190. }
  191. public void Stop()
  192. {
  193. CheckDisposed();
  194. listening = false;
  195. Close(false);
  196. }
  197. void IDisposable.Dispose()
  198. {
  199. if (disposed)
  200. return;
  201. Close(true); //TODO: Should we force here or not?
  202. disposed = true;
  203. }
  204. internal void CheckDisposed()
  205. {
  206. if (disposed)
  207. throw new ObjectDisposedException(GetType().Name);
  208. }
  209. internal void RegisterContext(HttpListenerContext context)
  210. {
  211. if (OnContext != null && IsListening)
  212. {
  213. OnContext(context);
  214. }
  215. lock (registry)
  216. registry[context] = context;
  217. }
  218. internal void UnregisterContext(HttpListenerContext context)
  219. {
  220. lock (registry)
  221. registry.Remove(context);
  222. }
  223. internal void AddConnection(HttpConnection cnc)
  224. {
  225. lock (connections)
  226. {
  227. connections[cnc] = cnc;
  228. }
  229. }
  230. internal void RemoveConnection(HttpConnection cnc)
  231. {
  232. lock (connections)
  233. {
  234. connections.Remove(cnc);
  235. }
  236. }
  237. }
  238. }