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;
36 using System.Net.Security;
37 using System.Security.Authentication.ExtendedProtection;
38 using System.Security.Cryptography;
39 using System.Security.Cryptography.X509Certificates;
42 namespace System.Net {
43 public sealed partial class HttpListener : IDisposable {
44 AuthenticationSchemes auth_schemes;
45 HttpListenerPrefixCollection prefixes;
46 AuthenticationSchemeSelector auth_selector;
48 bool ignore_write_exceptions;
49 bool unsafe_ntlm_auth;
53 readonly object _internalLock; // don't rename to match CoreFx
55 Hashtable registry; // Dictionary<HttpListenerContext,HttpListenerContext>
56 ArrayList ctx_queue; // List<HttpListenerContext> ctx_queue;
57 ArrayList wait_queue; // List<ListenerAsyncResult> wait_queue;
58 Hashtable connections;
60 ServiceNameStore defaultServiceNames;
61 ExtendedProtectionPolicy extendedProtectionPolicy;
62 ExtendedProtectionSelector extendedProtectionSelectorDelegate;
64 public delegate ExtendedProtectionPolicy ExtendedProtectionSelector (HttpListenerRequest request);
66 public HttpListener ()
68 _internalLock = new object ();
69 prefixes = new HttpListenerPrefixCollection (this);
70 registry = new Hashtable ();
71 connections = Hashtable.Synchronized (new Hashtable ());
72 ctx_queue = new ArrayList ();
73 wait_queue = new ArrayList ();
74 auth_schemes = AuthenticationSchemes.Anonymous;
75 defaultServiceNames = new ServiceNameStore ();
76 extendedProtectionPolicy = new ExtendedProtectionPolicy (PolicyEnforcement.Never);
80 // TODO: Digest, NTLM and Negotiate require ControlPrincipal
81 public AuthenticationSchemes AuthenticationSchemes {
82 get { return auth_schemes; }
89 public AuthenticationSchemeSelector AuthenticationSchemeSelectorDelegate {
90 get { return auth_selector; }
93 auth_selector = value;
97 public ExtendedProtectionSelector ExtendedProtectionSelectorDelegate
99 get { return extendedProtectionSelectorDelegate; }
103 throw new ArgumentNullException ();
105 if (!AuthenticationManager.OSSupportsExtendedProtection)
106 throw new PlatformNotSupportedException (SR.GetString (SR.security_ExtendedProtection_NoOSSupport));
108 extendedProtectionSelectorDelegate = value;
112 public bool IgnoreWriteExceptions {
113 get { return ignore_write_exceptions; }
116 ignore_write_exceptions = value;
120 public bool IsListening {
121 get { return listening; }
124 public static bool IsSupported {
128 public HttpListenerPrefixCollection Prefixes {
136 public HttpListenerTimeoutManager TimeoutManager {
138 throw new NotImplementedException ();
142 [MonoTODO ("not used anywhere in the implementation")]
143 public ExtendedProtectionPolicy ExtendedProtectionPolicy
146 return extendedProtectionPolicy;
152 throw new ArgumentNullException ("value");
154 if (!AuthenticationManager.OSSupportsExtendedProtection && value.PolicyEnforcement == PolicyEnforcement.Always)
155 throw new PlatformNotSupportedException (SR.GetString(SR.security_ExtendedProtection_NoOSSupport));
157 if (value.CustomChannelBinding != null)
158 throw new ArgumentException (SR.GetString (SR.net_listener_cannot_set_custom_cbt), "CustomChannelBinding");
160 extendedProtectionPolicy = value;
164 public ServiceNameCollection DefaultServiceNames
167 return defaultServiceNames.ServiceNames;
172 public string Realm {
173 get { return realm; }
180 [MonoTODO ("Support for NTLM needs some loving.")]
181 public bool UnsafeConnectionNtlmAuthentication {
182 get { return unsafe_ntlm_auth; }
185 unsafe_ntlm_auth = value;
215 void Close (bool force)
218 EndPointManager.RemoveListener (this);
222 void Cleanup (bool close_existing)
224 lock (_internalLock) {
225 if (close_existing) {
226 // Need to copy this since closing will call UnregisterContext
227 ICollection keys = registry.Keys;
228 var all = new HttpListenerContext [keys.Count];
229 keys.CopyTo (all, 0);
231 for (int i = all.Length - 1; i >= 0; i--)
232 all [i].Connection.Close (true);
235 lock (connections.SyncRoot) {
236 ICollection keys = connections.Keys;
237 var conns = new HttpConnection [keys.Count];
238 keys.CopyTo (conns, 0);
239 connections.Clear ();
240 for (int i = conns.Length - 1; i >= 0; i--)
241 conns [i].Close (true);
244 var ctxs = (HttpListenerContext []) ctx_queue.ToArray (typeof (HttpListenerContext));
246 for (int i = ctxs.Length - 1; i >= 0; i--)
247 ctxs [i].Connection.Close (true);
251 Exception exc = new ObjectDisposedException ("listener");
252 foreach (ListenerAsyncResult ares in wait_queue) {
260 public IAsyncResult BeginGetContext (AsyncCallback callback, Object state)
264 throw new InvalidOperationException ("Please, call Start before using this method.");
266 ListenerAsyncResult ares = new ListenerAsyncResult (callback, state);
268 // lock wait_queue early to avoid race conditions
271 HttpListenerContext ctx = GetContextFromQueue ();
273 ares.Complete (ctx, true);
278 wait_queue.Add (ares);
284 public HttpListenerContext EndGetContext (IAsyncResult asyncResult)
287 if (asyncResult == null)
288 throw new ArgumentNullException ("asyncResult");
290 ListenerAsyncResult ares = asyncResult as ListenerAsyncResult;
292 throw new ArgumentException ("Wrong IAsyncResult.", "asyncResult");
294 throw new ArgumentException ("Cannot reuse this IAsyncResult");
295 ares.EndCalled = true;
297 if (!ares.IsCompleted)
298 ares.AsyncWaitHandle.WaitOne ();
301 int idx = wait_queue.IndexOf (ares);
303 wait_queue.RemoveAt (idx);
306 HttpListenerContext context = ares.GetContext ();
307 context.ParseAuthentication (SelectAuthenticationScheme (context));
308 return context; // This will throw on error.
311 internal AuthenticationSchemes SelectAuthenticationScheme (HttpListenerContext context)
313 if (AuthenticationSchemeSelectorDelegate != null)
314 return AuthenticationSchemeSelectorDelegate (context.Request);
319 public HttpListenerContext GetContext ()
321 // The prefixes are not checked when using the async interface!?
322 if (prefixes.Count == 0)
323 throw new InvalidOperationException ("Please, call AddPrefix before using this method.");
325 ListenerAsyncResult ares = (ListenerAsyncResult) BeginGetContext (null, null);
327 return EndGetContext (ares);
336 EndPointManager.AddListener (this);
347 void IDisposable.Dispose ()
352 Close (true); //TODO: Should we force here or not?
356 public Task<HttpListenerContext> GetContextAsync ()
358 return Task<HttpListenerContext>.Factory.FromAsync (BeginGetContext, EndGetContext, null);
361 internal void CheckDisposed ()
364 throw new ObjectDisposedException (GetType ().ToString ());
367 // Must be called with a lock on ctx_queue
368 HttpListenerContext GetContextFromQueue ()
370 if (ctx_queue.Count == 0)
373 HttpListenerContext context = (HttpListenerContext) ctx_queue [0];
374 ctx_queue.RemoveAt (0);
378 internal void RegisterContext (HttpListenerContext context)
381 registry [context] = context;
383 ListenerAsyncResult ares = null;
385 if (wait_queue.Count == 0) {
387 ctx_queue.Add (context);
389 ares = (ListenerAsyncResult) wait_queue [0];
390 wait_queue.RemoveAt (0);
394 ares.Complete (context);
397 internal void UnregisterContext (HttpListenerContext context)
400 registry.Remove (context);
402 int idx = ctx_queue.IndexOf (context);
404 ctx_queue.RemoveAt (idx);
408 internal void AddConnection (HttpConnection cnc)
410 connections [cnc] = cnc;
413 internal void RemoveConnection (HttpConnection cnc)
415 connections.Remove (cnc);
419 #else // SECURITY_DEP
422 public sealed class HttpListener