HttpListener.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998
  1. #region License
  2. /*
  3. * HttpListener.cs
  4. *
  5. * This code is derived from HttpListener.cs (System.Net) of Mono
  6. * (http://www.mono-project.com).
  7. *
  8. * The MIT License
  9. *
  10. * Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
  11. * Copyright (c) 2012-2022 sta.blockhead
  12. *
  13. * Permission is hereby granted, free of charge, to any person obtaining a copy
  14. * of this software and associated documentation files (the "Software"), to deal
  15. * in the Software without restriction, including without limitation the rights
  16. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  17. * copies of the Software, and to permit persons to whom the Software is
  18. * furnished to do so, subject to the following conditions:
  19. *
  20. * The above copyright notice and this permission notice shall be included in
  21. * all copies or substantial portions of the Software.
  22. *
  23. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  24. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  25. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  26. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  27. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  28. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  29. * THE SOFTWARE.
  30. */
  31. #endregion
  32. #region Authors
  33. /*
  34. * Authors:
  35. * - Gonzalo Paniagua Javier <gonzalo@novell.com>
  36. */
  37. #endregion
  38. #region Contributors
  39. /*
  40. * Contributors:
  41. * - Liryna <liryna.stark@gmail.com>
  42. */
  43. #endregion
  44. using System;
  45. using System.Collections;
  46. using System.Collections.Generic;
  47. using System.Security.Cryptography.X509Certificates;
  48. using System.Security.Principal;
  49. using System.Threading;
  50. // TODO: Logging.
  51. namespace WebSocketSharp.Net
  52. {
  53. /// <summary>
  54. /// Provides a simple, programmatically controlled HTTP listener.
  55. /// </summary>
  56. /// <remarks>
  57. /// <para>
  58. /// The listener supports HTTP/1.1 version request and response.
  59. /// </para>
  60. /// <para>
  61. /// And the listener allows to accept WebSocket handshake requests.
  62. /// </para>
  63. /// <para>
  64. /// This class cannot be inherited.
  65. /// </para>
  66. /// </remarks>
  67. public sealed class HttpListener : IDisposable
  68. {
  69. #region Private Fields
  70. private AuthenticationSchemes _authSchemes;
  71. private Func<HttpListenerRequest, AuthenticationSchemes> _authSchemeSelector;
  72. private string _certFolderPath;
  73. private Queue<HttpListenerContext> _contextQueue;
  74. private LinkedList<HttpListenerContext> _contextRegistry;
  75. private object _contextRegistrySync;
  76. private static readonly string _defaultRealm;
  77. private bool _disposed;
  78. private bool _ignoreWriteExceptions;
  79. private volatile bool _listening;
  80. private Logger _log;
  81. private string _objectName;
  82. private HttpListenerPrefixCollection _prefixes;
  83. private string _realm;
  84. private bool _reuseAddress;
  85. private ServerSslConfiguration _sslConfig;
  86. private object _sync;
  87. private Func<IIdentity, NetworkCredential> _userCredFinder;
  88. private Queue<HttpListenerAsyncResult> _waitQueue;
  89. #endregion
  90. #region Static Constructor
  91. static HttpListener ()
  92. {
  93. _defaultRealm = "SECRET AREA";
  94. }
  95. #endregion
  96. #region Public Constructors
  97. /// <summary>
  98. /// Initializes a new instance of the <see cref="HttpListener"/> class.
  99. /// </summary>
  100. public HttpListener ()
  101. {
  102. _authSchemes = AuthenticationSchemes.Anonymous;
  103. _contextQueue = new Queue<HttpListenerContext> ();
  104. _contextRegistry = new LinkedList<HttpListenerContext> ();
  105. _contextRegistrySync = ((ICollection) _contextRegistry).SyncRoot;
  106. _log = new Logger ();
  107. _objectName = GetType ().ToString ();
  108. _prefixes = new HttpListenerPrefixCollection (this);
  109. _sync = new object ();
  110. _waitQueue = new Queue<HttpListenerAsyncResult> ();
  111. }
  112. #endregion
  113. #region Internal Properties
  114. internal bool ReuseAddress {
  115. get {
  116. return _reuseAddress;
  117. }
  118. set {
  119. _reuseAddress = value;
  120. }
  121. }
  122. #endregion
  123. #region Public Properties
  124. /// <summary>
  125. /// Gets or sets the scheme used to authenticate the clients.
  126. /// </summary>
  127. /// <value>
  128. /// <para>
  129. /// One of the <see cref="WebSocketSharp.Net.AuthenticationSchemes"/>
  130. /// enum values.
  131. /// </para>
  132. /// <para>
  133. /// It represents the scheme used to authenticate the clients.
  134. /// </para>
  135. /// <para>
  136. /// The default value is
  137. /// <see cref="WebSocketSharp.Net.AuthenticationSchemes.Anonymous"/>.
  138. /// </para>
  139. /// </value>
  140. /// <exception cref="ObjectDisposedException">
  141. /// This listener has been closed.
  142. /// </exception>
  143. public AuthenticationSchemes AuthenticationSchemes {
  144. get {
  145. if (_disposed)
  146. throw new ObjectDisposedException (_objectName);
  147. return _authSchemes;
  148. }
  149. set {
  150. if (_disposed)
  151. throw new ObjectDisposedException (_objectName);
  152. _authSchemes = value;
  153. }
  154. }
  155. /// <summary>
  156. /// Gets or sets the delegate called to select the scheme used to
  157. /// authenticate the clients.
  158. /// </summary>
  159. /// <remarks>
  160. /// <para>
  161. /// If this property is set, the listener uses the authentication
  162. /// scheme selected by the delegate for each request.
  163. /// </para>
  164. /// <para>
  165. /// Or if this property is not set, the listener uses the value of
  166. /// the <see cref="HttpListener.AuthenticationSchemes"/> property
  167. /// as the authentication scheme for all requests.
  168. /// </para>
  169. /// </remarks>
  170. /// <value>
  171. /// <para>
  172. /// A <c>Func&lt;<see cref="HttpListenerRequest"/>,
  173. /// <see cref="AuthenticationSchemes"/>&gt;</c> delegate or
  174. /// <see langword="null"/> if not needed.
  175. /// </para>
  176. /// <para>
  177. /// The delegate references the method used to select
  178. /// an authentication scheme.
  179. /// </para>
  180. /// <para>
  181. /// The default value is <see langword="null"/>.
  182. /// </para>
  183. /// </value>
  184. /// <exception cref="ObjectDisposedException">
  185. /// This listener has been closed.
  186. /// </exception>
  187. public Func<HttpListenerRequest, AuthenticationSchemes> AuthenticationSchemeSelector {
  188. get {
  189. if (_disposed)
  190. throw new ObjectDisposedException (_objectName);
  191. return _authSchemeSelector;
  192. }
  193. set {
  194. if (_disposed)
  195. throw new ObjectDisposedException (_objectName);
  196. _authSchemeSelector = value;
  197. }
  198. }
  199. /// <summary>
  200. /// Gets or sets the path to the folder in which stores the certificate
  201. /// files used to authenticate the server on the secure connection.
  202. /// </summary>
  203. /// <remarks>
  204. /// <para>
  205. /// This property represents the path to the folder in which stores
  206. /// the certificate files associated with each port number of added
  207. /// URI prefixes.
  208. /// </para>
  209. /// <para>
  210. /// A set of the certificate files is a pair of &lt;port number&gt;.cer
  211. /// (DER) and &lt;port number&gt;.key (DER, RSA Private Key).
  212. /// </para>
  213. /// <para>
  214. /// If this property is <see langword="null"/> or an empty string,
  215. /// the result of <c>System.Environment.GetFolderPath (<see
  216. /// cref="Environment.SpecialFolder.ApplicationData"/>)</c>
  217. /// is used as the default path.
  218. /// </para>
  219. /// </remarks>
  220. /// <value>
  221. /// <para>
  222. /// A <see cref="string"/> that represents the path to the folder
  223. /// in which stores the certificate files.
  224. /// </para>
  225. /// <para>
  226. /// The default value is <see langword="null"/>.
  227. /// </para>
  228. /// </value>
  229. /// <exception cref="ObjectDisposedException">
  230. /// This listener has been closed.
  231. /// </exception>
  232. public string CertificateFolderPath {
  233. get {
  234. if (_disposed)
  235. throw new ObjectDisposedException (_objectName);
  236. return _certFolderPath;
  237. }
  238. set {
  239. if (_disposed)
  240. throw new ObjectDisposedException (_objectName);
  241. _certFolderPath = value;
  242. }
  243. }
  244. /// <summary>
  245. /// Gets or sets a value indicating whether the listener returns
  246. /// exceptions that occur when sending the response to the client.
  247. /// </summary>
  248. /// <value>
  249. /// <para>
  250. /// <c>true</c> if the listener should not return those exceptions;
  251. /// otherwise, <c>false</c>.
  252. /// </para>
  253. /// <para>
  254. /// The default value is <c>false</c>.
  255. /// </para>
  256. /// </value>
  257. /// <exception cref="ObjectDisposedException">
  258. /// This listener has been closed.
  259. /// </exception>
  260. public bool IgnoreWriteExceptions {
  261. get {
  262. if (_disposed)
  263. throw new ObjectDisposedException (_objectName);
  264. return _ignoreWriteExceptions;
  265. }
  266. set {
  267. if (_disposed)
  268. throw new ObjectDisposedException (_objectName);
  269. _ignoreWriteExceptions = value;
  270. }
  271. }
  272. /// <summary>
  273. /// Gets a value indicating whether the listener has been started.
  274. /// </summary>
  275. /// <value>
  276. /// <c>true</c> if the listener has been started; otherwise, <c>false</c>.
  277. /// </value>
  278. public bool IsListening {
  279. get {
  280. return _listening;
  281. }
  282. }
  283. /// <summary>
  284. /// Gets a value indicating whether the listener can be used with
  285. /// the current operating system.
  286. /// </summary>
  287. /// <value>
  288. /// <c>true</c>.
  289. /// </value>
  290. public static bool IsSupported {
  291. get {
  292. return true;
  293. }
  294. }
  295. /// <summary>
  296. /// Gets the logging functions.
  297. /// </summary>
  298. /// <remarks>
  299. /// <para>
  300. /// The default logging level is <see cref="LogLevel.Error"/>.
  301. /// </para>
  302. /// <para>
  303. /// If you would like to change it, you should set the <c>Log.Level</c>
  304. /// property to any of the <see cref="LogLevel"/> enum values.
  305. /// </para>
  306. /// </remarks>
  307. /// <value>
  308. /// A <see cref="Logger"/> that provides the logging functions.
  309. /// </value>
  310. public Logger Log {
  311. get {
  312. return _log;
  313. }
  314. }
  315. /// <summary>
  316. /// Gets the URI prefixes handled by the listener.
  317. /// </summary>
  318. /// <value>
  319. /// A <see cref="HttpListenerPrefixCollection"/> that contains the URI
  320. /// prefixes.
  321. /// </value>
  322. /// <exception cref="ObjectDisposedException">
  323. /// This listener has been closed.
  324. /// </exception>
  325. public HttpListenerPrefixCollection Prefixes {
  326. get {
  327. if (_disposed)
  328. throw new ObjectDisposedException (_objectName);
  329. return _prefixes;
  330. }
  331. }
  332. /// <summary>
  333. /// Gets or sets the name of the realm associated with the listener.
  334. /// </summary>
  335. /// <remarks>
  336. /// If this property is <see langword="null"/> or an empty string,
  337. /// "SECRET AREA" will be used as the name of the realm.
  338. /// </remarks>
  339. /// <value>
  340. /// <para>
  341. /// A <see cref="string"/> that represents the name of the realm.
  342. /// </para>
  343. /// <para>
  344. /// The default value is <see langword="null"/>.
  345. /// </para>
  346. /// </value>
  347. /// <exception cref="ObjectDisposedException">
  348. /// This listener has been closed.
  349. /// </exception>
  350. public string Realm {
  351. get {
  352. if (_disposed)
  353. throw new ObjectDisposedException (_objectName);
  354. return _realm;
  355. }
  356. set {
  357. if (_disposed)
  358. throw new ObjectDisposedException (_objectName);
  359. _realm = value;
  360. }
  361. }
  362. /// <summary>
  363. /// Gets the configuration for secure connection.
  364. /// </summary>
  365. /// <value>
  366. /// A <see cref="ServerSslConfiguration"/> that represents the
  367. /// configuration used to provide secure connections.
  368. /// </value>
  369. /// <exception cref="ObjectDisposedException">
  370. /// This listener has been closed.
  371. /// </exception>
  372. public ServerSslConfiguration SslConfiguration {
  373. get {
  374. if (_disposed)
  375. throw new ObjectDisposedException (_objectName);
  376. if (_sslConfig == null)
  377. _sslConfig = new ServerSslConfiguration ();
  378. return _sslConfig;
  379. }
  380. }
  381. /// <summary>
  382. /// Gets or sets a value indicating whether, when NTLM authentication is used,
  383. /// the authentication information of first request is used to authenticate
  384. /// additional requests on the same connection.
  385. /// </summary>
  386. /// <remarks>
  387. /// This property is not currently supported and always throws
  388. /// a <see cref="NotSupportedException"/>.
  389. /// </remarks>
  390. /// <value>
  391. /// <c>true</c> if the authentication information of first request is used;
  392. /// otherwise, <c>false</c>.
  393. /// </value>
  394. /// <exception cref="NotSupportedException">
  395. /// Any use of this property.
  396. /// </exception>
  397. public bool UnsafeConnectionNtlmAuthentication {
  398. get {
  399. throw new NotSupportedException ();
  400. }
  401. set {
  402. throw new NotSupportedException ();
  403. }
  404. }
  405. /// <summary>
  406. /// Gets or sets the delegate called to find the credentials for
  407. /// an identity used to authenticate a client.
  408. /// </summary>
  409. /// <value>
  410. /// <para>
  411. /// A <c>Func&lt;<see cref="IIdentity"/>,
  412. /// <see cref="NetworkCredential"/>&gt;</c> delegate or
  413. /// <see langword="null"/> if not needed.
  414. /// </para>
  415. /// <para>
  416. /// It references the method used to find the credentials.
  417. /// </para>
  418. /// <para>
  419. /// The default value is <see langword="null"/>.
  420. /// </para>
  421. /// </value>
  422. /// <exception cref="ObjectDisposedException">
  423. /// This listener has been closed.
  424. /// </exception>
  425. public Func<IIdentity, NetworkCredential> UserCredentialsFinder {
  426. get {
  427. if (_disposed)
  428. throw new ObjectDisposedException (_objectName);
  429. return _userCredFinder;
  430. }
  431. set {
  432. if (_disposed)
  433. throw new ObjectDisposedException (_objectName);
  434. _userCredFinder = value;
  435. }
  436. }
  437. #endregion
  438. #region Private Methods
  439. private bool authenticateClient (HttpListenerContext context)
  440. {
  441. var schm = selectAuthenticationScheme (context.Request);
  442. if (schm == AuthenticationSchemes.Anonymous)
  443. return true;
  444. if (schm == AuthenticationSchemes.None) {
  445. var msg = "Authentication not allowed";
  446. context.SendError (403, msg);
  447. return false;
  448. }
  449. var realm = getRealm ();
  450. if (!context.SetUser (schm, realm, _userCredFinder)) {
  451. context.SendAuthenticationChallenge (schm, realm);
  452. return false;
  453. }
  454. return true;
  455. }
  456. private HttpListenerAsyncResult beginGetContext (
  457. AsyncCallback callback, object state
  458. )
  459. {
  460. lock (_contextRegistrySync) {
  461. if (!_listening) {
  462. var msg = "The method is canceled.";
  463. throw new HttpListenerException (995, msg);
  464. }
  465. var ares = new HttpListenerAsyncResult (callback, state);
  466. if (_contextQueue.Count == 0) {
  467. _waitQueue.Enqueue (ares);
  468. return ares;
  469. }
  470. var ctx = _contextQueue.Dequeue ();
  471. ares.Complete (ctx, true);
  472. return ares;
  473. }
  474. }
  475. private void cleanupContextQueue (bool force)
  476. {
  477. if (_contextQueue.Count == 0)
  478. return;
  479. if (force) {
  480. _contextQueue.Clear ();
  481. return;
  482. }
  483. var ctxs = _contextQueue.ToArray ();
  484. _contextQueue.Clear ();
  485. foreach (var ctx in ctxs)
  486. ctx.SendError (503);
  487. }
  488. private void cleanupContextRegistry ()
  489. {
  490. var cnt = _contextRegistry.Count;
  491. if (cnt == 0)
  492. return;
  493. var ctxs = new HttpListenerContext[cnt];
  494. lock (_contextRegistrySync) {
  495. _contextRegistry.CopyTo (ctxs, 0);
  496. _contextRegistry.Clear ();
  497. }
  498. foreach (var ctx in ctxs)
  499. ctx.Connection.Close (true);
  500. }
  501. private void cleanupWaitQueue (string message)
  502. {
  503. if (_waitQueue.Count == 0)
  504. return;
  505. var aress = _waitQueue.ToArray ();
  506. _waitQueue.Clear ();
  507. foreach (var ares in aress) {
  508. var ex = new HttpListenerException (995, message);
  509. ares.Complete (ex);
  510. }
  511. }
  512. private void close (bool force)
  513. {
  514. lock (_sync) {
  515. if (_disposed)
  516. return;
  517. lock (_contextRegistrySync) {
  518. if (!_listening) {
  519. _disposed = true;
  520. return;
  521. }
  522. _listening = false;
  523. }
  524. cleanupContextQueue (force);
  525. cleanupContextRegistry ();
  526. var msg = "The listener is closed.";
  527. cleanupWaitQueue (msg);
  528. EndPointManager.RemoveListener (this);
  529. _disposed = true;
  530. }
  531. }
  532. private string getRealm ()
  533. {
  534. var realm = _realm;
  535. return realm != null && realm.Length > 0 ? realm : _defaultRealm;
  536. }
  537. private bool registerContext (HttpListenerContext context)
  538. {
  539. if (!_listening)
  540. return false;
  541. lock (_contextRegistrySync) {
  542. if (!_listening)
  543. return false;
  544. context.Listener = this;
  545. _contextRegistry.AddLast (context);
  546. if (_waitQueue.Count == 0) {
  547. _contextQueue.Enqueue (context);
  548. return true;
  549. }
  550. var ares = _waitQueue.Dequeue ();
  551. ares.Complete (context, false);
  552. return true;
  553. }
  554. }
  555. private AuthenticationSchemes selectAuthenticationScheme (
  556. HttpListenerRequest request
  557. )
  558. {
  559. var selector = _authSchemeSelector;
  560. if (selector == null)
  561. return _authSchemes;
  562. try {
  563. return selector (request);
  564. }
  565. catch {
  566. return AuthenticationSchemes.None;
  567. }
  568. }
  569. #endregion
  570. #region Internal Methods
  571. internal void CheckDisposed ()
  572. {
  573. if (_disposed)
  574. throw new ObjectDisposedException (_objectName);
  575. }
  576. internal bool RegisterContext (HttpListenerContext context)
  577. {
  578. if (!authenticateClient (context))
  579. return false;
  580. if (!registerContext (context)) {
  581. context.SendError (503);
  582. return false;
  583. }
  584. return true;
  585. }
  586. internal void UnregisterContext (HttpListenerContext context)
  587. {
  588. lock (_contextRegistrySync)
  589. _contextRegistry.Remove (context);
  590. }
  591. #endregion
  592. #region Public Methods
  593. /// <summary>
  594. /// Shuts down the listener immediately.
  595. /// </summary>
  596. public void Abort ()
  597. {
  598. if (_disposed)
  599. return;
  600. close (true);
  601. }
  602. /// <summary>
  603. /// Begins getting an incoming request asynchronously.
  604. /// </summary>
  605. /// <remarks>
  606. /// <para>
  607. /// This asynchronous operation must be completed by calling
  608. /// the EndGetContext method.
  609. /// </para>
  610. /// <para>
  611. /// Typically, the EndGetContext method is called by
  612. /// <paramref name="callback"/>.
  613. /// </para>
  614. /// </remarks>
  615. /// <returns>
  616. /// An <see cref="IAsyncResult"/> that represents the status of
  617. /// the asynchronous operation.
  618. /// </returns>
  619. /// <param name="callback">
  620. /// An <see cref="AsyncCallback"/> delegate that references the method
  621. /// to invoke when the asynchronous operation completes.
  622. /// </param>
  623. /// <param name="state">
  624. /// An <see cref="object"/> that specifies a user defined object to
  625. /// pass to <paramref name="callback"/>.
  626. /// </param>
  627. /// <exception cref="InvalidOperationException">
  628. /// <para>
  629. /// This listener has not been started or is currently stopped.
  630. /// </para>
  631. /// <para>
  632. /// -or-
  633. /// </para>
  634. /// <para>
  635. /// This listener has no URI prefix on which listens.
  636. /// </para>
  637. /// </exception>
  638. /// <exception cref="HttpListenerException">
  639. /// This method is canceled.
  640. /// </exception>
  641. /// <exception cref="ObjectDisposedException">
  642. /// This listener has been closed.
  643. /// </exception>
  644. public IAsyncResult BeginGetContext (AsyncCallback callback, object state)
  645. {
  646. if (_disposed)
  647. throw new ObjectDisposedException (_objectName);
  648. if (!_listening) {
  649. var msg = "The listener has not been started.";
  650. throw new InvalidOperationException (msg);
  651. }
  652. if (_prefixes.Count == 0) {
  653. var msg = "The listener has no URI prefix on which listens.";
  654. throw new InvalidOperationException (msg);
  655. }
  656. return beginGetContext (callback, state);
  657. }
  658. /// <summary>
  659. /// Shuts down the listener.
  660. /// </summary>
  661. public void Close ()
  662. {
  663. if (_disposed)
  664. return;
  665. close (false);
  666. }
  667. /// <summary>
  668. /// Ends an asynchronous operation to get an incoming request.
  669. /// </summary>
  670. /// <remarks>
  671. /// This method completes an asynchronous operation started by
  672. /// calling the BeginGetContext method.
  673. /// </remarks>
  674. /// <returns>
  675. /// A <see cref="HttpListenerContext"/> that represents a request.
  676. /// </returns>
  677. /// <param name="asyncResult">
  678. /// An <see cref="IAsyncResult"/> instance obtained by calling
  679. /// the BeginGetContext method.
  680. /// </param>
  681. /// <exception cref="ArgumentNullException">
  682. /// <paramref name="asyncResult"/> is <see langword="null"/>.
  683. /// </exception>
  684. /// <exception cref="ArgumentException">
  685. /// <paramref name="asyncResult"/> was not obtained by calling
  686. /// the BeginGetContext method.
  687. /// </exception>
  688. /// <exception cref="InvalidOperationException">
  689. /// <para>
  690. /// This listener has not been started or is currently stopped.
  691. /// </para>
  692. /// <para>
  693. /// -or-
  694. /// </para>
  695. /// <para>
  696. /// This method was already called for <paramref name="asyncResult"/>.
  697. /// </para>
  698. /// </exception>
  699. /// <exception cref="HttpListenerException">
  700. /// This method is canceled.
  701. /// </exception>
  702. /// <exception cref="ObjectDisposedException">
  703. /// This listener has been closed.
  704. /// </exception>
  705. public HttpListenerContext EndGetContext (IAsyncResult asyncResult)
  706. {
  707. if (_disposed)
  708. throw new ObjectDisposedException (_objectName);
  709. if (!_listening) {
  710. var msg = "The listener has not been started.";
  711. throw new InvalidOperationException (msg);
  712. }
  713. if (asyncResult == null)
  714. throw new ArgumentNullException ("asyncResult");
  715. var ares = asyncResult as HttpListenerAsyncResult;
  716. if (ares == null) {
  717. var msg = "A wrong IAsyncResult instance.";
  718. throw new ArgumentException (msg, "asyncResult");
  719. }
  720. lock (ares.SyncRoot) {
  721. if (ares.EndCalled) {
  722. var msg = "This IAsyncResult instance cannot be reused.";
  723. throw new InvalidOperationException (msg);
  724. }
  725. ares.EndCalled = true;
  726. }
  727. if (!ares.IsCompleted)
  728. ares.AsyncWaitHandle.WaitOne ();
  729. return ares.Context;
  730. }
  731. /// <summary>
  732. /// Gets an incoming request.
  733. /// </summary>
  734. /// <remarks>
  735. /// This method waits for an incoming request and returns when
  736. /// a request is received.
  737. /// </remarks>
  738. /// <returns>
  739. /// A <see cref="HttpListenerContext"/> that represents a request.
  740. /// </returns>
  741. /// <exception cref="InvalidOperationException">
  742. /// <para>
  743. /// This listener has not been started or is currently stopped.
  744. /// </para>
  745. /// <para>
  746. /// -or-
  747. /// </para>
  748. /// <para>
  749. /// This listener has no URI prefix on which listens.
  750. /// </para>
  751. /// </exception>
  752. /// <exception cref="HttpListenerException">
  753. /// This method is canceled.
  754. /// </exception>
  755. /// <exception cref="ObjectDisposedException">
  756. /// This listener has been closed.
  757. /// </exception>
  758. public HttpListenerContext GetContext ()
  759. {
  760. if (_disposed)
  761. throw new ObjectDisposedException (_objectName);
  762. if (!_listening) {
  763. var msg = "The listener has not been started.";
  764. throw new InvalidOperationException (msg);
  765. }
  766. if (_prefixes.Count == 0) {
  767. var msg = "The listener has no URI prefix on which listens.";
  768. throw new InvalidOperationException (msg);
  769. }
  770. var ares = beginGetContext (null, null);
  771. ares.EndCalled = true;
  772. if (!ares.IsCompleted)
  773. ares.AsyncWaitHandle.WaitOne ();
  774. return ares.Context;
  775. }
  776. /// <summary>
  777. /// Starts receiving incoming requests.
  778. /// </summary>
  779. /// <exception cref="ObjectDisposedException">
  780. /// This listener has been closed.
  781. /// </exception>
  782. public void Start ()
  783. {
  784. if (_disposed)
  785. throw new ObjectDisposedException (_objectName);
  786. lock (_sync) {
  787. if (_disposed)
  788. throw new ObjectDisposedException (_objectName);
  789. lock (_contextRegistrySync) {
  790. if (_listening)
  791. return;
  792. EndPointManager.AddListener (this);
  793. _listening = true;
  794. }
  795. }
  796. }
  797. /// <summary>
  798. /// Stops receiving incoming requests.
  799. /// </summary>
  800. /// <exception cref="ObjectDisposedException">
  801. /// This listener has been closed.
  802. /// </exception>
  803. public void Stop ()
  804. {
  805. if (_disposed)
  806. throw new ObjectDisposedException (_objectName);
  807. lock (_sync) {
  808. if (_disposed)
  809. throw new ObjectDisposedException (_objectName);
  810. lock (_contextRegistrySync) {
  811. if (!_listening)
  812. return;
  813. _listening = false;
  814. }
  815. cleanupContextQueue (false);
  816. cleanupContextRegistry ();
  817. var msg = "The listener is stopped.";
  818. cleanupWaitQueue (msg);
  819. EndPointManager.RemoveListener (this);
  820. }
  821. }
  822. #endregion
  823. #region Explicit Interface Implementations
  824. /// <summary>
  825. /// Releases all resources used by the listener.
  826. /// </summary>
  827. void IDisposable.Dispose ()
  828. {
  829. if (_disposed)
  830. return;
  831. close (true);
  832. }
  833. #endregion
  834. }
  835. }