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 #if NET_2_0 && SECURITY_DEP
31 using System.Collections;
32 using System.Threading;
34 namespace System.Net {
35 public sealed class HttpListener : IDisposable {
36 AuthenticationSchemes auth_schemes;
37 HttpListenerPrefixCollection prefixes;
38 AuthenticationSchemeSelector auth_selector;
40 bool ignore_write_exceptions;
41 bool unsafe_ntlm_auth;
45 Hashtable registry; // Dictionary<HttpListenerContext,HttpListenerContext>
46 ArrayList ctx_queue; // List<HttpListenerContext> ctx_queue;
47 ArrayList wait_queue; // List<ListenerAsyncResult> wait_queue;
48 Hashtable connections;
50 public HttpListener ()
52 prefixes = new HttpListenerPrefixCollection (this);
53 registry = new Hashtable ();
54 connections = Hashtable.Synchronized (new Hashtable ());
55 ctx_queue = new ArrayList ();
56 wait_queue = new ArrayList ();
57 auth_schemes = AuthenticationSchemes.Anonymous;
60 // TODO: Digest, NTLM and Negotiate require ControlPrincipal
61 public AuthenticationSchemes AuthenticationSchemes {
62 get { return auth_schemes; }
69 public AuthenticationSchemeSelector AuthenticationSchemeSelectorDelegate {
70 get { return auth_selector; }
73 auth_selector = value;
77 public bool IgnoreWriteExceptions {
78 get { return ignore_write_exceptions; }
81 ignore_write_exceptions = value;
85 public bool IsListening {
86 get { return listening; }
89 public static bool IsSupported {
93 public HttpListenerPrefixCollection Prefixes {
101 public string Realm {
102 get { return realm; }
109 [MonoTODO ("Support for NTLM needs some loving.")]
110 public bool UnsafeConnectionNtlmAuthentication {
111 get { return unsafe_ntlm_auth; }
114 unsafe_ntlm_auth = value;
144 void Close (bool force)
147 EndPointManager.RemoveListener (this);
151 void Cleanup (bool close_existing)
154 if (close_existing) {
155 // Need to copy this since closing will call UnregisterContext
156 ICollection keys = registry.Keys;
157 var all = new HttpListenerContext [keys.Count];
158 keys.CopyTo (all, 0);
160 for (int i = all.Length - 1; i >= 0; i--)
161 all [i].Connection.Close (true);
165 ICollection keys = connections.Keys;
166 var conns = new HttpConnection [keys.Count];
167 keys.CopyTo (conns, 0);
168 connections.Clear ();
169 for (int i = conns.Length - 1; i >= 0; i--)
170 conns [i].Close (true);
173 ICollection keys = connections.Keys;
174 var ctxs = new HttpListenerContext [keys.Count];
175 keys.CopyTo (ctxs, 0);
177 for (int i = ctxs.Length - 1; i >= 0; i--)
178 ctxs [i].Connection.Close (true);
182 foreach (ListenerAsyncResult ares in wait_queue) {
183 ares.Complete ("Listener was closed.");
190 public IAsyncResult BeginGetContext (AsyncCallback callback, Object state)
194 throw new InvalidOperationException ("Please, call Start before using this method.");
196 ListenerAsyncResult ares = new ListenerAsyncResult (callback, state);
198 // lock wait_queue early to avoid race conditions
201 HttpListenerContext ctx = GetContextFromQueue ();
203 ares.Complete (ctx, true);
208 wait_queue.Add (ares);
214 public HttpListenerContext EndGetContext (IAsyncResult asyncResult)
217 if (asyncResult == null)
218 throw new ArgumentNullException ("asyncResult");
220 ListenerAsyncResult ares = asyncResult as ListenerAsyncResult;
222 throw new ArgumentException ("Wrong IAsyncResult.", "asyncResult");
224 if (!ares.IsCompleted)
225 ares.AsyncWaitHandle.WaitOne ();
228 int idx = wait_queue.IndexOf (ares);
230 wait_queue.RemoveAt (idx);
233 HttpListenerContext context = ares.GetContext ();
234 context.ParseAuthentication (SelectAuthenticationScheme (context));
235 return context; // This will throw on error.
238 internal AuthenticationSchemes SelectAuthenticationScheme (HttpListenerContext context)
240 if (AuthenticationSchemeSelectorDelegate != null)
241 return AuthenticationSchemeSelectorDelegate (context.Request);
246 public HttpListenerContext GetContext ()
248 // The prefixes are not checked when using the async interface!?
249 if (prefixes.Count == 0)
250 throw new InvalidOperationException ("Please, call AddPrefix before using this method.");
252 IAsyncResult ares = BeginGetContext (null, null);
253 return EndGetContext (ares);
262 EndPointManager.AddListener (this);
273 void IDisposable.Dispose ()
278 Close (true); //TODO: Should we force here or not?
282 internal void CheckDisposed ()
285 throw new ObjectDisposedException (GetType ().ToString ());
288 // Must be called with a lock on ctx_queue
289 HttpListenerContext GetContextFromQueue ()
291 if (ctx_queue.Count == 0)
294 HttpListenerContext context = (HttpListenerContext) ctx_queue [0];
295 ctx_queue.RemoveAt (0);
299 internal void RegisterContext (HttpListenerContext context)
302 registry [context] = context;
304 ListenerAsyncResult ares = null;
306 if (wait_queue.Count == 0) {
308 ctx_queue.Add (context);
310 ares = (ListenerAsyncResult) wait_queue [0];
311 wait_queue.RemoveAt (0);
315 ares.Complete (context);
318 internal void UnregisterContext (HttpListenerContext context)
321 registry.Remove (context);
323 int idx = ctx_queue.IndexOf (context);
325 ctx_queue.RemoveAt (idx);
329 internal void AddConnection (HttpConnection cnc)
331 connections [cnc] = cnc;
334 internal void RemoveConnection (HttpConnection cnc)
336 connections.Remove (cnc);