Merge pull request #2274 from esdrubal/udpclientreceive
[mono.git] / mcs / class / System / System.Net / HttpListener.cs
index 2b8f2708800f08c33e2f2bc16096d43c39cb4db8..29afc0fd84b78ceca4e33b0dfe9efa2b4f9767fd 100644 (file)
@@ -1,10 +1,12 @@
 //
 // System.Net.HttpListener
 //
-// Author:
+// Authors:
 //     Gonzalo Paniagua Javier (gonzalo@novell.com)
+//     Marek Safar (marek.safar@gmail.com)
 //
 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+// Copyright 2011 Xamarin Inc.
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-#if NET_2_0 && SECURITY_DEP
+#if SECURITY_DEP
+#if MONO_SECURITY_ALIAS
+extern alias MonoSecurity;
+using MonoSecurity::Mono.Security.Authenticode;
+using MSI = MonoSecurity::Mono.Security.Interface;
+#else
+using Mono.Security.Authenticode;
+using MSI = Mono.Security.Interface;
+#endif
 
+using System.IO;
 using System.Collections;
 using System.Threading;
+using System.Threading.Tasks;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+
+using Mono.Net.Security;
+
+
 //TODO: logging
 namespace System.Net {
        public sealed class HttpListener : IDisposable {
@@ -42,6 +60,10 @@ namespace System.Net {
                bool listening;
                bool disposed;
 
+               IMonoTlsProvider tlsProvider;
+               MSI.MonoTlsSettings tlsSettings;
+               X509Certificate certificate;
+
                Hashtable registry;   // Dictionary<HttpListenerContext,HttpListenerContext> 
                ArrayList ctx_queue;  // List<HttpListenerContext> ctx_queue;
                ArrayList wait_queue; // List<ListenerAsyncResult> wait_queue;
@@ -57,6 +79,56 @@ namespace System.Net {
                        auth_schemes = AuthenticationSchemes.Anonymous;
                }
 
+               internal HttpListener (X509Certificate certificate, IMonoTlsProvider tlsProvider, MSI.MonoTlsSettings tlsSettings)
+                       : this ()
+               {
+                       this.certificate = certificate;
+                       this.tlsProvider = tlsProvider;
+                       this.tlsSettings = tlsSettings;
+               }
+
+               internal X509Certificate LoadCertificateAndKey (IPAddress addr, int port)
+               {
+                       lock (registry) {
+                               if (certificate != null)
+                                       return certificate;
+
+                               // Actually load the certificate
+                               try {
+                                       string dirname = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
+                                       string path = Path.Combine (dirname, ".mono");
+                                       path = Path.Combine (path, "httplistener");
+                                       string cert_file = Path.Combine (path, String.Format ("{0}.cer", port));
+                                       if (!File.Exists (cert_file))
+                                               return null;
+                                       string pvk_file = Path.Combine (path, String.Format ("{0}.pvk", port));
+                                       if (!File.Exists (pvk_file))
+                                               return null;
+                                       var cert = new X509Certificate2 (cert_file);
+                                       cert.PrivateKey = PrivateKey.CreateFromFile (pvk_file).RSA;
+                                       certificate = cert;
+                                       return certificate;
+                               } catch {
+                                       // ignore errors
+                                       certificate = null;
+                                       return null;
+                               }
+                       }
+               }
+
+               internal IMonoSslStream CreateSslStream (Stream innerStream, bool ownsStream, MSI.MonoRemoteCertificateValidationCallback callback)
+               {
+                       lock (registry) {
+                               if (tlsProvider == null)
+                                       tlsProvider = MonoTlsProviderFactory.GetProviderInternal ();
+                               if (tlsSettings == null)
+                                       tlsSettings = MSI.MonoTlsSettings.CopyDefaultSettings ();
+                               if (tlsSettings.RemoteCertificateValidationCallback == null)
+                                       tlsSettings.RemoteCertificateValidationCallback = callback;
+                               return tlsProvider.CreateSslStream (innerStream, ownsStream, tlsSettings);
+                       }
+               }
+
                // TODO: Digest, NTLM and Negotiate require ControlPrincipal
                public AuthenticationSchemes AuthenticationSchemes {
                        get { return auth_schemes; }
@@ -158,25 +230,28 @@ namespace System.Net {
                                        keys.CopyTo (all, 0);
                                        registry.Clear ();
                                        for (int i = all.Length - 1; i >= 0; i--)
-                                               all[i].Connection.Close (true);
+                                               all [i].Connection.Close (true);
                                }
 
-                               lock (connections) {
-                                       foreach (HttpConnection cnc in connections.Keys) {
-                                               cnc.Close (true);
-                                       }
+                               lock (connections.SyncRoot) {
+                                       ICollection keys = connections.Keys;
+                                       var conns = new HttpConnection [keys.Count];
+                                       keys.CopyTo (conns, 0);
                                        connections.Clear ();
+                                       for (int i = conns.Length - 1; i >= 0; i--)
+                                               conns [i].Close (true);
                                }
                                lock (ctx_queue) {
-                                       foreach (HttpListenerContext context in ctx_queue)
-                                               context.Connection.Close (true);
-
+                                       var ctxs = (HttpListenerContext []) ctx_queue.ToArray (typeof (HttpListenerContext));
                                        ctx_queue.Clear ();
+                                       for (int i = ctxs.Length - 1; i >= 0; i--)
+                                               ctxs [i].Connection.Close (true);
                                }
 
                                lock (wait_queue) {
+                                       Exception exc = new ObjectDisposedException ("listener");
                                        foreach (ListenerAsyncResult ares in wait_queue) {
-                                               ares.Complete ("Listener was closed.");
+                                               ares.Complete (exc);
                                        }
                                        wait_queue.Clear ();
                                }
@@ -216,6 +291,9 @@ namespace System.Net {
                        ListenerAsyncResult ares = asyncResult as ListenerAsyncResult;
                        if (ares == null)
                                throw new ArgumentException ("Wrong IAsyncResult.", "asyncResult");
+                       if (ares.EndCalled)
+                               throw new ArgumentException ("Cannot reuse this IAsyncResult");
+                       ares.EndCalled = true;
 
                        if (!ares.IsCompleted)
                                ares.AsyncWaitHandle.WaitOne ();
@@ -245,7 +323,8 @@ namespace System.Net {
                        if (prefixes.Count == 0)
                                throw new InvalidOperationException ("Please, call AddPrefix before using this method.");
 
-                       IAsyncResult ares = BeginGetContext (null, null);
+                       ListenerAsyncResult ares = (ListenerAsyncResult) BeginGetContext (null, null);
+                       ares.InGet = true;
                        return EndGetContext (ares);
                }
 
@@ -275,6 +354,11 @@ namespace System.Net {
                        disposed = true;
                }
 
+               public Task<HttpListenerContext> GetContextAsync ()
+               {
+                       return Task<HttpListenerContext>.Factory.FromAsync (BeginGetContext, EndGetContext, null);
+               }
+
                internal void CheckDisposed ()
                {
                        if (disposed)
@@ -333,5 +417,11 @@ namespace System.Net {
                }
        }
 }
+#else // SECURITY_DEP
+namespace System.Net
+{
+       public sealed class HttpListener
+       {
+       }
+}
 #endif
-