//
// 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 {
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;
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; }
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 ();
}
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 ();
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);
}
disposed = true;
}
+ public Task<HttpListenerContext> GetContextAsync ()
+ {
+ return Task<HttpListenerContext>.Factory.FromAsync (BeginGetContext, EndGetContext, null);
+ }
+
internal void CheckDisposed ()
{
if (disposed)
}
}
}
+#else // SECURITY_DEP
+namespace System.Net
+{
+ public sealed class HttpListener
+ {
+ }
+}
#endif
-