2 // System.Net.HttpListener
5 // Gonzalo Paniagua Javier (gonzalo@novell.com)
6 // Marek Safar (marek.safar@gmail.com)
8 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
9 // Copyright 2011 Xamarin Inc.
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Collections;
34 using System.Threading;
35 using System.Threading.Tasks;
38 namespace System.Net {
39 public sealed class HttpListener : IDisposable {
40 AuthenticationSchemes auth_schemes;
41 HttpListenerPrefixCollection prefixes;
42 AuthenticationSchemeSelector auth_selector;
44 bool ignore_write_exceptions;
45 bool unsafe_ntlm_auth;
49 Hashtable registry; // Dictionary<HttpListenerContext,HttpListenerContext>
50 ArrayList ctx_queue; // List<HttpListenerContext> ctx_queue;
51 ArrayList wait_queue; // List<ListenerAsyncResult> wait_queue;
52 Hashtable connections;
54 public HttpListener ()
56 prefixes = new HttpListenerPrefixCollection (this);
57 registry = new Hashtable ();
58 connections = Hashtable.Synchronized (new Hashtable ());
59 ctx_queue = new ArrayList ();
60 wait_queue = new ArrayList ();
61 auth_schemes = AuthenticationSchemes.Anonymous;
64 // TODO: Digest, NTLM and Negotiate require ControlPrincipal
65 public AuthenticationSchemes AuthenticationSchemes {
66 get { return auth_schemes; }
73 public AuthenticationSchemeSelector AuthenticationSchemeSelectorDelegate {
74 get { return auth_selector; }
77 auth_selector = value;
81 public bool IgnoreWriteExceptions {
82 get { return ignore_write_exceptions; }
85 ignore_write_exceptions = value;
89 public bool IsListening {
90 get { return listening; }
93 public static bool IsSupported {
97 public HttpListenerPrefixCollection Prefixes {
105 public string Realm {
106 get { return realm; }
113 [MonoTODO ("Support for NTLM needs some loving.")]
114 public bool UnsafeConnectionNtlmAuthentication {
115 get { return unsafe_ntlm_auth; }
118 unsafe_ntlm_auth = value;
148 void Close (bool force)
151 EndPointManager.RemoveListener (this);
155 void Cleanup (bool close_existing)
158 if (close_existing) {
159 // Need to copy this since closing will call UnregisterContext
160 ICollection keys = registry.Keys;
161 var all = new HttpListenerContext [keys.Count];
162 keys.CopyTo (all, 0);
164 for (int i = all.Length - 1; i >= 0; i--)
165 all [i].Connection.Close (true);
168 lock (connections.SyncRoot) {
169 ICollection keys = connections.Keys;
170 var conns = new HttpConnection [keys.Count];
171 keys.CopyTo (conns, 0);
172 connections.Clear ();
173 for (int i = conns.Length - 1; i >= 0; i--)
174 conns [i].Close (true);
177 var ctxs = (HttpListenerContext []) ctx_queue.ToArray (typeof (HttpListenerContext));
179 for (int i = ctxs.Length - 1; i >= 0; i--)
180 ctxs [i].Connection.Close (true);
184 Exception exc = new ObjectDisposedException ("listener");
185 foreach (ListenerAsyncResult ares in wait_queue) {
193 public IAsyncResult BeginGetContext (AsyncCallback callback, Object state)
197 throw new InvalidOperationException ("Please, call Start before using this method.");
199 ListenerAsyncResult ares = new ListenerAsyncResult (callback, state);
201 // lock wait_queue early to avoid race conditions
204 HttpListenerContext ctx = GetContextFromQueue ();
206 ares.Complete (ctx, true);
211 wait_queue.Add (ares);
217 public HttpListenerContext EndGetContext (IAsyncResult asyncResult)
220 if (asyncResult == null)
221 throw new ArgumentNullException ("asyncResult");
223 ListenerAsyncResult ares = asyncResult as ListenerAsyncResult;
225 throw new ArgumentException ("Wrong IAsyncResult.", "asyncResult");
227 throw new ArgumentException ("Cannot reuse this IAsyncResult");
228 ares.EndCalled = true;
230 if (!ares.IsCompleted)
231 ares.AsyncWaitHandle.WaitOne ();
234 int idx = wait_queue.IndexOf (ares);
236 wait_queue.RemoveAt (idx);
239 HttpListenerContext context = ares.GetContext ();
240 context.ParseAuthentication (SelectAuthenticationScheme (context));
241 return context; // This will throw on error.
244 internal AuthenticationSchemes SelectAuthenticationScheme (HttpListenerContext context)
246 if (AuthenticationSchemeSelectorDelegate != null)
247 return AuthenticationSchemeSelectorDelegate (context.Request);
252 public HttpListenerContext GetContext ()
254 // The prefixes are not checked when using the async interface!?
255 if (prefixes.Count == 0)
256 throw new InvalidOperationException ("Please, call AddPrefix before using this method.");
258 ListenerAsyncResult ares = (ListenerAsyncResult) BeginGetContext (null, null);
260 return EndGetContext (ares);
269 EndPointManager.AddListener (this);
280 void IDisposable.Dispose ()
285 Close (true); //TODO: Should we force here or not?
289 public Task<HttpListenerContext> GetContextAsync ()
291 return Task<HttpListenerContext>.Factory.FromAsync (BeginGetContext, EndGetContext, null);
294 internal void CheckDisposed ()
297 throw new ObjectDisposedException (GetType ().ToString ());
300 // Must be called with a lock on ctx_queue
301 HttpListenerContext GetContextFromQueue ()
303 if (ctx_queue.Count == 0)
306 HttpListenerContext context = (HttpListenerContext) ctx_queue [0];
307 ctx_queue.RemoveAt (0);
311 internal void RegisterContext (HttpListenerContext context)
314 registry [context] = context;
316 ListenerAsyncResult ares = null;
318 if (wait_queue.Count == 0) {
320 ctx_queue.Add (context);
322 ares = (ListenerAsyncResult) wait_queue [0];
323 wait_queue.RemoveAt (0);
327 ares.Complete (context);
330 internal void UnregisterContext (HttpListenerContext context)
333 registry.Remove (context);
335 int idx = ctx_queue.IndexOf (context);
337 ctx_queue.RemoveAt (idx);
341 internal void AddConnection (HttpConnection cnc)
343 connections [cnc] = cnc;
346 internal void RemoveConnection (HttpConnection cnc)
348 connections.Remove (cnc);