2 // System.Net.HttpListener
5 // Gonzalo Paniagua Javier (gonzalo@novell.com)
7 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 using System.Collections.Generic;
30 using System.Threading;
32 namespace System.Net {
33 public sealed class HttpListener : IDisposable {
34 AuthenticationSchemes auth_schemes;
35 HttpListenerPrefixCollection prefixes;
36 AuthenticationSchemeSelector auth_selector;
38 bool ignore_write_exceptions;
39 bool unsafe_ntlm_auth;
43 Dictionary<HttpListenerContext,HttpListenerContext> registry;
44 List<HttpListenerContext> ctx_queue;
45 List<ListenerAsyncResult> wait_queue;
47 public HttpListener ()
49 prefixes = new HttpListenerPrefixCollection (this);
50 registry = new Dictionary<HttpListenerContext,HttpListenerContext> ();
51 ctx_queue = new List<HttpListenerContext> ();
52 wait_queue = new List<ListenerAsyncResult> ();
53 auth_schemes = AuthenticationSchemes.Anonymous;
56 // TODO: Digest, NTLM and Negotiate require ControlPrincipal
57 public AuthenticationSchemes AuthenticationSchemes {
58 get { return auth_schemes; }
65 //TODO: when is this called?
66 public AuthenticationSchemeSelector AuthenticationSchemeSelectorDelegate {
67 get { return auth_selector; }
70 auth_selector = value;
74 public bool IgnoreWriteExceptions {
75 get { return ignore_write_exceptions; }
78 ignore_write_exceptions = value;
82 public bool IsListening {
83 get { return listening; }
86 public static bool IsSupported {
90 public HttpListenerPrefixCollection Prefixes {
106 [MonoTODO ("Support for NTLM needs some loving.")]
107 public bool UnsafeConnectionNtlmAuthentication {
108 get { return unsafe_ntlm_auth; }
111 unsafe_ntlm_auth = value;
141 void Close (bool force)
144 EndPointManager.RemoveListener (this);
149 void Cleanup (bool close_existing)
152 if (close_existing) {
153 foreach (HttpListenerContext context in registry.Keys) {
154 context.Connection.Close ();
156 registry.Clear (); // Just in case.
160 foreach (HttpListenerContext context in ctx_queue)
161 context.Connection.Close ();
167 foreach (ListenerAsyncResult ares in wait_queue) {
168 ares.Complete ("Listener was closed.");
175 public IAsyncResult BeginGetContext (AsyncCallback callback, Object state)
179 throw new InvalidOperationException ("Please, call Start before using this method.");
181 ListenerAsyncResult ares = new ListenerAsyncResult (callback, state);
183 // lock wait_queue early to avoid race conditions
186 HttpListenerContext ctx = GetContextFromQueue ();
188 ares.Complete (ctx, true);
193 wait_queue.Add (ares);
199 public HttpListenerContext EndGetContext (IAsyncResult asyncResult)
202 if (asyncResult == null)
203 throw new ArgumentNullException ("asyncResult");
205 ListenerAsyncResult ares = asyncResult as ListenerAsyncResult;
207 throw new ArgumentException ("Wrong IAsyncResult.", "asyncResult");
209 if (!ares.IsCompleted)
210 ares.AsyncWaitHandle.WaitOne ();
213 int idx = wait_queue.IndexOf (ares);
215 wait_queue.RemoveAt (idx);
218 return ares.GetContext (); // This will throw on error.
221 public HttpListenerContext GetContext ()
223 // The prefixes are not checked when using the async interface!?
224 if (prefixes.Count == 0)
225 throw new InvalidOperationException ("Please, call AddPrefix before using this method.");
227 IAsyncResult ares = BeginGetContext (null, null);
228 return EndGetContext (ares);
237 EndPointManager.AddListener (this);
248 void IDisposable.Dispose ()
254 Close (true); //TODO: Should we force here or not?
257 internal void CheckDisposed ()
260 throw new ObjectDisposedException (GetType ().ToString ());
263 // Must be called with a lock on ctx_queue
264 HttpListenerContext GetContextFromQueue ()
266 if (ctx_queue.Count == 0)
269 HttpListenerContext context = ctx_queue [0];
270 ctx_queue.RemoveAt (0);
274 internal void RegisterContext (HttpListenerContext context)
277 Monitor.Enter (registry);
278 registry [context] = context;
279 Monitor.Enter (wait_queue);
280 Monitor.Enter (ctx_queue);
281 if (wait_queue.Count == 0) {
282 ctx_queue.Add (context);
284 ListenerAsyncResult ares = wait_queue [0];
285 wait_queue.RemoveAt (0);
286 ares.Complete (context);
289 Monitor.Exit (ctx_queue);
290 Monitor.Exit (wait_queue);
291 Monitor.Exit (registry);
295 internal void UnregisterContext (HttpListenerContext context)
298 Monitor.Enter (registry);
299 Monitor.Enter (ctx_queue);
300 int idx = ctx_queue.IndexOf (context);
302 ctx_queue.RemoveAt (idx);
303 registry.Remove (context);
305 Monitor.Exit (ctx_queue);
306 Monitor.Exit (registry);