123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998 |
- #region License
- /*
- * HttpListener.cs
- *
- * This code is derived from HttpListener.cs (System.Net) of Mono
- * (http://www.mono-project.com).
- *
- * The MIT License
- *
- * Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
- * Copyright (c) 2012-2022 sta.blockhead
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
- #endregion
- #region Authors
- /*
- * Authors:
- * - Gonzalo Paniagua Javier <gonzalo@novell.com>
- */
- #endregion
- #region Contributors
- /*
- * Contributors:
- * - Liryna <liryna.stark@gmail.com>
- */
- #endregion
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Security.Cryptography.X509Certificates;
- using System.Security.Principal;
- using System.Threading;
- // TODO: Logging.
- namespace WebSocketSharp.Net
- {
- /// <summary>
- /// Provides a simple, programmatically controlled HTTP listener.
- /// </summary>
- /// <remarks>
- /// <para>
- /// The listener supports HTTP/1.1 version request and response.
- /// </para>
- /// <para>
- /// And the listener allows to accept WebSocket handshake requests.
- /// </para>
- /// <para>
- /// This class cannot be inherited.
- /// </para>
- /// </remarks>
- public sealed class HttpListener : IDisposable
- {
- #region Private Fields
- private AuthenticationSchemes _authSchemes;
- private Func<HttpListenerRequest, AuthenticationSchemes> _authSchemeSelector;
- private string _certFolderPath;
- private Queue<HttpListenerContext> _contextQueue;
- private LinkedList<HttpListenerContext> _contextRegistry;
- private object _contextRegistrySync;
- private static readonly string _defaultRealm;
- private bool _disposed;
- private bool _ignoreWriteExceptions;
- private volatile bool _listening;
- private Logger _log;
- private string _objectName;
- private HttpListenerPrefixCollection _prefixes;
- private string _realm;
- private bool _reuseAddress;
- private ServerSslConfiguration _sslConfig;
- private object _sync;
- private Func<IIdentity, NetworkCredential> _userCredFinder;
- private Queue<HttpListenerAsyncResult> _waitQueue;
- #endregion
- #region Static Constructor
- static HttpListener ()
- {
- _defaultRealm = "SECRET AREA";
- }
- #endregion
- #region Public Constructors
- /// <summary>
- /// Initializes a new instance of the <see cref="HttpListener"/> class.
- /// </summary>
- public HttpListener ()
- {
- _authSchemes = AuthenticationSchemes.Anonymous;
- _contextQueue = new Queue<HttpListenerContext> ();
- _contextRegistry = new LinkedList<HttpListenerContext> ();
- _contextRegistrySync = ((ICollection) _contextRegistry).SyncRoot;
- _log = new Logger ();
- _objectName = GetType ().ToString ();
- _prefixes = new HttpListenerPrefixCollection (this);
- _sync = new object ();
- _waitQueue = new Queue<HttpListenerAsyncResult> ();
- }
- #endregion
- #region Internal Properties
- internal bool ReuseAddress {
- get {
- return _reuseAddress;
- }
- set {
- _reuseAddress = value;
- }
- }
- #endregion
- #region Public Properties
- /// <summary>
- /// Gets or sets the scheme used to authenticate the clients.
- /// </summary>
- /// <value>
- /// <para>
- /// One of the <see cref="WebSocketSharp.Net.AuthenticationSchemes"/>
- /// enum values.
- /// </para>
- /// <para>
- /// It represents the scheme used to authenticate the clients.
- /// </para>
- /// <para>
- /// The default value is
- /// <see cref="WebSocketSharp.Net.AuthenticationSchemes.Anonymous"/>.
- /// </para>
- /// </value>
- /// <exception cref="ObjectDisposedException">
- /// This listener has been closed.
- /// </exception>
- public AuthenticationSchemes AuthenticationSchemes {
- get {
- if (_disposed)
- throw new ObjectDisposedException (_objectName);
- return _authSchemes;
- }
- set {
- if (_disposed)
- throw new ObjectDisposedException (_objectName);
- _authSchemes = value;
- }
- }
- /// <summary>
- /// Gets or sets the delegate called to select the scheme used to
- /// authenticate the clients.
- /// </summary>
- /// <remarks>
- /// <para>
- /// If this property is set, the listener uses the authentication
- /// scheme selected by the delegate for each request.
- /// </para>
- /// <para>
- /// Or if this property is not set, the listener uses the value of
- /// the <see cref="HttpListener.AuthenticationSchemes"/> property
- /// as the authentication scheme for all requests.
- /// </para>
- /// </remarks>
- /// <value>
- /// <para>
- /// A <c>Func<<see cref="HttpListenerRequest"/>,
- /// <see cref="AuthenticationSchemes"/>></c> delegate or
- /// <see langword="null"/> if not needed.
- /// </para>
- /// <para>
- /// The delegate references the method used to select
- /// an authentication scheme.
- /// </para>
- /// <para>
- /// The default value is <see langword="null"/>.
- /// </para>
- /// </value>
- /// <exception cref="ObjectDisposedException">
- /// This listener has been closed.
- /// </exception>
- public Func<HttpListenerRequest, AuthenticationSchemes> AuthenticationSchemeSelector {
- get {
- if (_disposed)
- throw new ObjectDisposedException (_objectName);
- return _authSchemeSelector;
- }
- set {
- if (_disposed)
- throw new ObjectDisposedException (_objectName);
- _authSchemeSelector = value;
- }
- }
- /// <summary>
- /// Gets or sets the path to the folder in which stores the certificate
- /// files used to authenticate the server on the secure connection.
- /// </summary>
- /// <remarks>
- /// <para>
- /// This property represents the path to the folder in which stores
- /// the certificate files associated with each port number of added
- /// URI prefixes.
- /// </para>
- /// <para>
- /// A set of the certificate files is a pair of <port number>.cer
- /// (DER) and <port number>.key (DER, RSA Private Key).
- /// </para>
- /// <para>
- /// If this property is <see langword="null"/> or an empty string,
- /// the result of <c>System.Environment.GetFolderPath (<see
- /// cref="Environment.SpecialFolder.ApplicationData"/>)</c>
- /// is used as the default path.
- /// </para>
- /// </remarks>
- /// <value>
- /// <para>
- /// A <see cref="string"/> that represents the path to the folder
- /// in which stores the certificate files.
- /// </para>
- /// <para>
- /// The default value is <see langword="null"/>.
- /// </para>
- /// </value>
- /// <exception cref="ObjectDisposedException">
- /// This listener has been closed.
- /// </exception>
- public string CertificateFolderPath {
- get {
- if (_disposed)
- throw new ObjectDisposedException (_objectName);
- return _certFolderPath;
- }
- set {
- if (_disposed)
- throw new ObjectDisposedException (_objectName);
- _certFolderPath = value;
- }
- }
- /// <summary>
- /// Gets or sets a value indicating whether the listener returns
- /// exceptions that occur when sending the response to the client.
- /// </summary>
- /// <value>
- /// <para>
- /// <c>true</c> if the listener should not return those exceptions;
- /// otherwise, <c>false</c>.
- /// </para>
- /// <para>
- /// The default value is <c>false</c>.
- /// </para>
- /// </value>
- /// <exception cref="ObjectDisposedException">
- /// This listener has been closed.
- /// </exception>
- public bool IgnoreWriteExceptions {
- get {
- if (_disposed)
- throw new ObjectDisposedException (_objectName);
- return _ignoreWriteExceptions;
- }
- set {
- if (_disposed)
- throw new ObjectDisposedException (_objectName);
- _ignoreWriteExceptions = value;
- }
- }
- /// <summary>
- /// Gets a value indicating whether the listener has been started.
- /// </summary>
- /// <value>
- /// <c>true</c> if the listener has been started; otherwise, <c>false</c>.
- /// </value>
- public bool IsListening {
- get {
- return _listening;
- }
- }
- /// <summary>
- /// Gets a value indicating whether the listener can be used with
- /// the current operating system.
- /// </summary>
- /// <value>
- /// <c>true</c>.
- /// </value>
- public static bool IsSupported {
- get {
- return true;
- }
- }
- /// <summary>
- /// Gets the logging functions.
- /// </summary>
- /// <remarks>
- /// <para>
- /// The default logging level is <see cref="LogLevel.Error"/>.
- /// </para>
- /// <para>
- /// If you would like to change it, you should set the <c>Log.Level</c>
- /// property to any of the <see cref="LogLevel"/> enum values.
- /// </para>
- /// </remarks>
- /// <value>
- /// A <see cref="Logger"/> that provides the logging functions.
- /// </value>
- public Logger Log {
- get {
- return _log;
- }
- }
- /// <summary>
- /// Gets the URI prefixes handled by the listener.
- /// </summary>
- /// <value>
- /// A <see cref="HttpListenerPrefixCollection"/> that contains the URI
- /// prefixes.
- /// </value>
- /// <exception cref="ObjectDisposedException">
- /// This listener has been closed.
- /// </exception>
- public HttpListenerPrefixCollection Prefixes {
- get {
- if (_disposed)
- throw new ObjectDisposedException (_objectName);
- return _prefixes;
- }
- }
- /// <summary>
- /// Gets or sets the name of the realm associated with the listener.
- /// </summary>
- /// <remarks>
- /// If this property is <see langword="null"/> or an empty string,
- /// "SECRET AREA" will be used as the name of the realm.
- /// </remarks>
- /// <value>
- /// <para>
- /// A <see cref="string"/> that represents the name of the realm.
- /// </para>
- /// <para>
- /// The default value is <see langword="null"/>.
- /// </para>
- /// </value>
- /// <exception cref="ObjectDisposedException">
- /// This listener has been closed.
- /// </exception>
- public string Realm {
- get {
- if (_disposed)
- throw new ObjectDisposedException (_objectName);
- return _realm;
- }
- set {
- if (_disposed)
- throw new ObjectDisposedException (_objectName);
- _realm = value;
- }
- }
- /// <summary>
- /// Gets the configuration for secure connection.
- /// </summary>
- /// <value>
- /// A <see cref="ServerSslConfiguration"/> that represents the
- /// configuration used to provide secure connections.
- /// </value>
- /// <exception cref="ObjectDisposedException">
- /// This listener has been closed.
- /// </exception>
- public ServerSslConfiguration SslConfiguration {
- get {
- if (_disposed)
- throw new ObjectDisposedException (_objectName);
- if (_sslConfig == null)
- _sslConfig = new ServerSslConfiguration ();
- return _sslConfig;
- }
- }
- /// <summary>
- /// Gets or sets a value indicating whether, when NTLM authentication is used,
- /// the authentication information of first request is used to authenticate
- /// additional requests on the same connection.
- /// </summary>
- /// <remarks>
- /// This property is not currently supported and always throws
- /// a <see cref="NotSupportedException"/>.
- /// </remarks>
- /// <value>
- /// <c>true</c> if the authentication information of first request is used;
- /// otherwise, <c>false</c>.
- /// </value>
- /// <exception cref="NotSupportedException">
- /// Any use of this property.
- /// </exception>
- public bool UnsafeConnectionNtlmAuthentication {
- get {
- throw new NotSupportedException ();
- }
- set {
- throw new NotSupportedException ();
- }
- }
- /// <summary>
- /// Gets or sets the delegate called to find the credentials for
- /// an identity used to authenticate a client.
- /// </summary>
- /// <value>
- /// <para>
- /// A <c>Func<<see cref="IIdentity"/>,
- /// <see cref="NetworkCredential"/>></c> delegate or
- /// <see langword="null"/> if not needed.
- /// </para>
- /// <para>
- /// It references the method used to find the credentials.
- /// </para>
- /// <para>
- /// The default value is <see langword="null"/>.
- /// </para>
- /// </value>
- /// <exception cref="ObjectDisposedException">
- /// This listener has been closed.
- /// </exception>
- public Func<IIdentity, NetworkCredential> UserCredentialsFinder {
- get {
- if (_disposed)
- throw new ObjectDisposedException (_objectName);
- return _userCredFinder;
- }
- set {
- if (_disposed)
- throw new ObjectDisposedException (_objectName);
- _userCredFinder = value;
- }
- }
- #endregion
- #region Private Methods
- private bool authenticateClient (HttpListenerContext context)
- {
- var schm = selectAuthenticationScheme (context.Request);
- if (schm == AuthenticationSchemes.Anonymous)
- return true;
- if (schm == AuthenticationSchemes.None) {
- var msg = "Authentication not allowed";
- context.SendError (403, msg);
- return false;
- }
- var realm = getRealm ();
- if (!context.SetUser (schm, realm, _userCredFinder)) {
- context.SendAuthenticationChallenge (schm, realm);
- return false;
- }
- return true;
- }
- private HttpListenerAsyncResult beginGetContext (
- AsyncCallback callback, object state
- )
- {
- lock (_contextRegistrySync) {
- if (!_listening) {
- var msg = "The method is canceled.";
- throw new HttpListenerException (995, msg);
- }
- var ares = new HttpListenerAsyncResult (callback, state);
- if (_contextQueue.Count == 0) {
- _waitQueue.Enqueue (ares);
- return ares;
- }
- var ctx = _contextQueue.Dequeue ();
- ares.Complete (ctx, true);
- return ares;
- }
- }
- private void cleanupContextQueue (bool force)
- {
- if (_contextQueue.Count == 0)
- return;
- if (force) {
- _contextQueue.Clear ();
- return;
- }
- var ctxs = _contextQueue.ToArray ();
- _contextQueue.Clear ();
- foreach (var ctx in ctxs)
- ctx.SendError (503);
- }
- private void cleanupContextRegistry ()
- {
- var cnt = _contextRegistry.Count;
- if (cnt == 0)
- return;
- var ctxs = new HttpListenerContext[cnt];
- lock (_contextRegistrySync) {
- _contextRegistry.CopyTo (ctxs, 0);
- _contextRegistry.Clear ();
- }
- foreach (var ctx in ctxs)
- ctx.Connection.Close (true);
- }
- private void cleanupWaitQueue (string message)
- {
- if (_waitQueue.Count == 0)
- return;
- var aress = _waitQueue.ToArray ();
- _waitQueue.Clear ();
- foreach (var ares in aress) {
- var ex = new HttpListenerException (995, message);
- ares.Complete (ex);
- }
- }
- private void close (bool force)
- {
- lock (_sync) {
- if (_disposed)
- return;
- lock (_contextRegistrySync) {
- if (!_listening) {
- _disposed = true;
- return;
- }
- _listening = false;
- }
- cleanupContextQueue (force);
- cleanupContextRegistry ();
- var msg = "The listener is closed.";
- cleanupWaitQueue (msg);
- EndPointManager.RemoveListener (this);
- _disposed = true;
- }
- }
- private string getRealm ()
- {
- var realm = _realm;
- return realm != null && realm.Length > 0 ? realm : _defaultRealm;
- }
- private bool registerContext (HttpListenerContext context)
- {
- if (!_listening)
- return false;
- lock (_contextRegistrySync) {
- if (!_listening)
- return false;
- context.Listener = this;
- _contextRegistry.AddLast (context);
- if (_waitQueue.Count == 0) {
- _contextQueue.Enqueue (context);
- return true;
- }
- var ares = _waitQueue.Dequeue ();
- ares.Complete (context, false);
- return true;
- }
- }
- private AuthenticationSchemes selectAuthenticationScheme (
- HttpListenerRequest request
- )
- {
- var selector = _authSchemeSelector;
- if (selector == null)
- return _authSchemes;
- try {
- return selector (request);
- }
- catch {
- return AuthenticationSchemes.None;
- }
- }
- #endregion
- #region Internal Methods
- internal void CheckDisposed ()
- {
- if (_disposed)
- throw new ObjectDisposedException (_objectName);
- }
- internal bool RegisterContext (HttpListenerContext context)
- {
- if (!authenticateClient (context))
- return false;
- if (!registerContext (context)) {
- context.SendError (503);
- return false;
- }
- return true;
- }
- internal void UnregisterContext (HttpListenerContext context)
- {
- lock (_contextRegistrySync)
- _contextRegistry.Remove (context);
- }
- #endregion
- #region Public Methods
- /// <summary>
- /// Shuts down the listener immediately.
- /// </summary>
- public void Abort ()
- {
- if (_disposed)
- return;
- close (true);
- }
- /// <summary>
- /// Begins getting an incoming request asynchronously.
- /// </summary>
- /// <remarks>
- /// <para>
- /// This asynchronous operation must be completed by calling
- /// the EndGetContext method.
- /// </para>
- /// <para>
- /// Typically, the EndGetContext method is called by
- /// <paramref name="callback"/>.
- /// </para>
- /// </remarks>
- /// <returns>
- /// An <see cref="IAsyncResult"/> that represents the status of
- /// the asynchronous operation.
- /// </returns>
- /// <param name="callback">
- /// An <see cref="AsyncCallback"/> delegate that references the method
- /// to invoke when the asynchronous operation completes.
- /// </param>
- /// <param name="state">
- /// An <see cref="object"/> that specifies a user defined object to
- /// pass to <paramref name="callback"/>.
- /// </param>
- /// <exception cref="InvalidOperationException">
- /// <para>
- /// This listener has not been started or is currently stopped.
- /// </para>
- /// <para>
- /// -or-
- /// </para>
- /// <para>
- /// This listener has no URI prefix on which listens.
- /// </para>
- /// </exception>
- /// <exception cref="HttpListenerException">
- /// This method is canceled.
- /// </exception>
- /// <exception cref="ObjectDisposedException">
- /// This listener has been closed.
- /// </exception>
- public IAsyncResult BeginGetContext (AsyncCallback callback, object state)
- {
- if (_disposed)
- throw new ObjectDisposedException (_objectName);
- if (!_listening) {
- var msg = "The listener has not been started.";
- throw new InvalidOperationException (msg);
- }
- if (_prefixes.Count == 0) {
- var msg = "The listener has no URI prefix on which listens.";
- throw new InvalidOperationException (msg);
- }
- return beginGetContext (callback, state);
- }
- /// <summary>
- /// Shuts down the listener.
- /// </summary>
- public void Close ()
- {
- if (_disposed)
- return;
- close (false);
- }
- /// <summary>
- /// Ends an asynchronous operation to get an incoming request.
- /// </summary>
- /// <remarks>
- /// This method completes an asynchronous operation started by
- /// calling the BeginGetContext method.
- /// </remarks>
- /// <returns>
- /// A <see cref="HttpListenerContext"/> that represents a request.
- /// </returns>
- /// <param name="asyncResult">
- /// An <see cref="IAsyncResult"/> instance obtained by calling
- /// the BeginGetContext method.
- /// </param>
- /// <exception cref="ArgumentNullException">
- /// <paramref name="asyncResult"/> is <see langword="null"/>.
- /// </exception>
- /// <exception cref="ArgumentException">
- /// <paramref name="asyncResult"/> was not obtained by calling
- /// the BeginGetContext method.
- /// </exception>
- /// <exception cref="InvalidOperationException">
- /// <para>
- /// This listener has not been started or is currently stopped.
- /// </para>
- /// <para>
- /// -or-
- /// </para>
- /// <para>
- /// This method was already called for <paramref name="asyncResult"/>.
- /// </para>
- /// </exception>
- /// <exception cref="HttpListenerException">
- /// This method is canceled.
- /// </exception>
- /// <exception cref="ObjectDisposedException">
- /// This listener has been closed.
- /// </exception>
- public HttpListenerContext EndGetContext (IAsyncResult asyncResult)
- {
- if (_disposed)
- throw new ObjectDisposedException (_objectName);
- if (!_listening) {
- var msg = "The listener has not been started.";
- throw new InvalidOperationException (msg);
- }
- if (asyncResult == null)
- throw new ArgumentNullException ("asyncResult");
- var ares = asyncResult as HttpListenerAsyncResult;
- if (ares == null) {
- var msg = "A wrong IAsyncResult instance.";
- throw new ArgumentException (msg, "asyncResult");
- }
- lock (ares.SyncRoot) {
- if (ares.EndCalled) {
- var msg = "This IAsyncResult instance cannot be reused.";
- throw new InvalidOperationException (msg);
- }
- ares.EndCalled = true;
- }
- if (!ares.IsCompleted)
- ares.AsyncWaitHandle.WaitOne ();
- return ares.Context;
- }
- /// <summary>
- /// Gets an incoming request.
- /// </summary>
- /// <remarks>
- /// This method waits for an incoming request and returns when
- /// a request is received.
- /// </remarks>
- /// <returns>
- /// A <see cref="HttpListenerContext"/> that represents a request.
- /// </returns>
- /// <exception cref="InvalidOperationException">
- /// <para>
- /// This listener has not been started or is currently stopped.
- /// </para>
- /// <para>
- /// -or-
- /// </para>
- /// <para>
- /// This listener has no URI prefix on which listens.
- /// </para>
- /// </exception>
- /// <exception cref="HttpListenerException">
- /// This method is canceled.
- /// </exception>
- /// <exception cref="ObjectDisposedException">
- /// This listener has been closed.
- /// </exception>
- public HttpListenerContext GetContext ()
- {
- if (_disposed)
- throw new ObjectDisposedException (_objectName);
- if (!_listening) {
- var msg = "The listener has not been started.";
- throw new InvalidOperationException (msg);
- }
- if (_prefixes.Count == 0) {
- var msg = "The listener has no URI prefix on which listens.";
- throw new InvalidOperationException (msg);
- }
- var ares = beginGetContext (null, null);
- ares.EndCalled = true;
- if (!ares.IsCompleted)
- ares.AsyncWaitHandle.WaitOne ();
- return ares.Context;
- }
- /// <summary>
- /// Starts receiving incoming requests.
- /// </summary>
- /// <exception cref="ObjectDisposedException">
- /// This listener has been closed.
- /// </exception>
- public void Start ()
- {
- if (_disposed)
- throw new ObjectDisposedException (_objectName);
- lock (_sync) {
- if (_disposed)
- throw new ObjectDisposedException (_objectName);
- lock (_contextRegistrySync) {
- if (_listening)
- return;
- EndPointManager.AddListener (this);
- _listening = true;
- }
- }
- }
- /// <summary>
- /// Stops receiving incoming requests.
- /// </summary>
- /// <exception cref="ObjectDisposedException">
- /// This listener has been closed.
- /// </exception>
- public void Stop ()
- {
- if (_disposed)
- throw new ObjectDisposedException (_objectName);
- lock (_sync) {
- if (_disposed)
- throw new ObjectDisposedException (_objectName);
- lock (_contextRegistrySync) {
- if (!_listening)
- return;
- _listening = false;
- }
- cleanupContextQueue (false);
- cleanupContextRegistry ();
- var msg = "The listener is stopped.";
- cleanupWaitQueue (msg);
- EndPointManager.RemoveListener (this);
- }
- }
- #endregion
- #region Explicit Interface Implementations
- /// <summary>
- /// Releases all resources used by the listener.
- /// </summary>
- void IDisposable.Dispose ()
- {
- if (_disposed)
- return;
- close (true);
- }
- #endregion
- }
- }
|