Merge pull request #901 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / System / System.Net / ServicePointManager.cs
index d8acf615ab65aee36ddf8c7771270e26b575e71f..6e23af5ce04be141ac6a10d72171062e106a284c 100644 (file)
 
 #if SECURITY_DEP
 
+#if MONOTOUCH
+using Mono.Security.Protocol.Tls;
+using MSX = Mono.Security.X509;
+using Mono.Security.X509.Extensions;
+#else
 extern alias MonoSecurity;
-
-using System.Text.RegularExpressions;
 using MonoSecurity::Mono.Security.X509.Extensions;
 using MonoSecurity::Mono.Security.Protocol.Tls;
 using MSX = MonoSecurity::Mono.Security.X509;
 #endif
 
+using System.Text.RegularExpressions;
+#endif
+
 using System;
+using System.Threading;
 using System.Collections;
+using System.Collections.Generic;
 using System.Collections.Specialized;
 using System.Configuration;
 using System.Net.Configuration;
@@ -48,6 +56,7 @@ using System.Security.Cryptography.X509Certificates;
 
 using System.Globalization;
 using System.Net.Security;
+using System.Diagnostics;
 
 //
 // notes:
@@ -72,10 +81,12 @@ namespace System.Net
        public class ServicePointManager {
                class SPKey {
                        Uri uri; // schema/host/port
+                       Uri proxy;
                        bool use_connect;
 
-                       public SPKey (Uri uri, bool use_connect) {
+                       public SPKey (Uri uri, Uri proxy, bool use_connect) {
                                this.uri = uri;
+                               this.proxy = proxy;
                                this.use_connect = use_connect;
                        }
 
@@ -87,8 +98,16 @@ namespace System.Net
                                get { return use_connect; }
                        }
 
+                       public bool UsesProxy {
+                               get { return proxy != null; }
+                       }
+
                        public override int GetHashCode () {
-                               return uri.GetHashCode () + ((use_connect) ? 1 : 0);
+                               int hash = 23;
+                               hash = hash * 31 + ((use_connect) ? 1 : 0);
+                               hash = hash * 31 + uri.GetHashCode ();
+                               hash = hash * 31 + (proxy != null ? proxy.GetHashCode () : 0);
+                               return hash;
                        }
 
                        public override bool Equals (object obj) {
@@ -97,7 +116,13 @@ namespace System.Net
                                        return false;
                                }
 
-                               return (uri.Equals (other.uri) && other.use_connect == use_connect);
+                               if (!uri.Equals (other.uri))
+                                       return false;
+                               if (use_connect != other.use_connect || UsesProxy != other.UsesProxy)
+                                       return false;
+                               if (UsesProxy && !proxy.Equals (other.proxy))
+                                       return false;
+                               return true;
                        }
                }
 
@@ -107,7 +132,7 @@ namespace System.Net
                
                private static ICertificatePolicy policy = new DefaultCertificatePolicy ();
                private static int defaultConnectionLimit = DefaultPersistentConnectionLimit;
-               private static int maxServicePointIdleTime = 900000; // 15 minutes
+               private static int maxServicePointIdleTime = 100000; // 100 seconds
                private static int maxServicePoints = 0;
                private static bool _checkCRL = false;
                private static SecurityProtocolType _securityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
@@ -304,7 +329,8 @@ namespace System.Net
                        if (address == null)
                                throw new ArgumentNullException ("address");
 
-                       RecycleServicePoints ();
+                       if ((servicePoints.Count % 4) == 0)
+                               RecycleServicePoints ();
 
                        var origAddress = new Uri (address.Scheme + "://" + address.Authority);
                        
@@ -324,8 +350,8 @@ namespace System.Net
                        address = new Uri (address.Scheme + "://" + address.Authority);
                        
                        ServicePoint sp = null;
+                       SPKey key = new SPKey (origAddress, usesProxy ? address : null, useConnect);
                        lock (servicePoints) {
-                               SPKey key = new SPKey (origAddress, useConnect);
                                sp = servicePoints [key] as ServicePoint;
                                if (sp != null)
                                        return sp;
@@ -354,16 +380,23 @@ namespace System.Net
                
                // Internal Methods
 
-               internal static void RecycleServicePoints ()
+               static void RecycleServicePoints ()
                {
-                       ArrayList toRemove = new ArrayList ();
                        lock (servicePoints) {
+                               var toRemove = new ArrayList ();
+                               var idleList = new SortedDictionary<DateTime, ServicePoint> ();
                                IDictionaryEnumerator e = servicePoints.GetEnumerator ();
                                while (e.MoveNext ()) {
                                        ServicePoint sp = (ServicePoint) e.Value;
-                                       if (sp.AvailableForRecycling) {
+                                       DateTime idleSince;
+                                       if (sp.CheckAvailableForRecycling (out idleSince)) {
                                                toRemove.Add (e.Key);
+                                               continue;
                                        }
+
+                                       while (idleList.ContainsKey (idleSince))
+                                               idleSince = idleSince.AddMilliseconds (1);
+                                       idleList.Add (idleSince, sp);
                                }
                                
                                for (int i = 0; i < toRemove.Count; i++) 
@@ -373,25 +406,18 @@ namespace System.Net
                                        return;
 
                                // get rid of the ones with the longest idle time
-                               SortedList list = new SortedList (servicePoints.Count);
-                               e = servicePoints.GetEnumerator ();
-                               while (e.MoveNext ()) {
-                                       ServicePoint sp = (ServicePoint) e.Value;
-                                       if (sp.CurrentConnections == 0) {
-                                               while (list.ContainsKey (sp.IdleSince))
-                                                       sp.IdleSince = sp.IdleSince.AddMilliseconds (1);
-                                               list.Add (sp.IdleSince, sp.Address);
-                                       }
+                               foreach (var sp in idleList.Values) {
+                                       if (servicePoints.Count <= maxServicePoints)
+                                               break;
+                                       servicePoints.Remove (sp);
                                }
-                               
-                               for (int i = 0; i < list.Count && servicePoints.Count > maxServicePoints; i++)
-                                       servicePoints.Remove (list.GetByIndex (i));
                        }
                }
 #if SECURITY_DEP
                internal class ChainValidationHelper {
                        object sender;
                        string host;
+                       RemoteCertificateValidationCallback cb;
 
 #if !MONOTOUCH
                        static bool is_macosx = System.IO.File.Exists (OSX509Certificates.SecurityLibrary);
@@ -410,19 +436,19 @@ namespace System.Net
                        }
 #endif
 
-                       public ChainValidationHelper (object sender)
+                       public ChainValidationHelper (object sender, string hostName)
                        {
                                this.sender = sender;
+                               host = hostName;
                        }
 
-                       public string Host {
+                       public RemoteCertificateValidationCallback ServerCertificateValidationCallback {
                                get {
-                                       if (host == null && sender is HttpWebRequest)
-                                               host = ((HttpWebRequest) sender).Address.Host;
-                                       return host;
+                                       if (cb == null)
+                                               cb = ServicePointManager.ServerCertificateValidationCallback;
+                                       return cb;
                                }
-
-                               set { host = value; }
+                               set { cb = value; }
                        }
 
                        // Used when the obsolete ICertificatePolicy is set to DefaultCertificatePolicy
@@ -435,7 +461,6 @@ namespace System.Net
                                        return null;
 
                                ICertificatePolicy policy = ServicePointManager.CertificatePolicy;
-                               RemoteCertificateValidationCallback cb = ServicePointManager.ServerCertificateValidationCallback;
 
                                X509Certificate2 leaf = new X509Certificate2 (certs [0].RawData);
                                int status11 = 0; // Error code passed to the obsolete ICertificatePolicy callback
@@ -448,7 +473,7 @@ namespace System.Net
                                // the certificates that the server provided (which generally does not include the root) so, only  
                                // if there's a user callback, we'll create the X509Chain but won't build it
                                // ref: https://bugzilla.xamarin.com/show_bug.cgi?id=7245
-                               if (cb != null) {
+                               if (ServerCertificateValidationCallback != null) {
 #endif
                                chain = new X509Chain ();
                                chain.ChainPolicy = new X509ChainPolicy ();
@@ -478,7 +503,7 @@ namespace System.Net
                                                status11 = -2146762490; //CERT_E_PURPOSE 0x800B0106
                                        }
 
-                                       if (!CheckServerIdentity (certs [0], Host)) {
+                                       if (!CheckServerIdentity (certs [0], host)) {
                                                errors |= SslPolicyErrors.RemoteCertificateNameMismatch;
                                                status11 = -2146762481; // CERT_E_CN_NO_MATCH 0x800B010F
                                        }
@@ -488,7 +513,7 @@ namespace System.Net
                                        // Ideally we should return the SecTrustResult
                                        OSX509Certificates.SecTrustResult trustResult = OSX509Certificates.SecTrustResult.Deny;
                                        try {
-                                               trustResult = OSX509Certificates.TrustEvaluateSsl (certs, Host);
+                                               trustResult = OSX509Certificates.TrustEvaluateSsl (certs, host);
                                                // We could use the other values of trustResult to pass this extra information
                                                // to the .NET 2 callback for values like SecTrustResult.Confirm
                                                result = (trustResult == OSX509Certificates.SecTrustResult.Proceed ||
@@ -510,7 +535,7 @@ namespace System.Net
                                }
 #endif
 
-#if MONODROID
+#if MONODROID && SECURITY_DEP
                                result = AndroidPlatform.TrustEvaluateSsl (certs, sender, leaf, chain, errors);
                                if (result) {
                                        // chain.Build() + GetErrorsFromChain() (above) will ALWAYS fail on
@@ -534,8 +559,8 @@ namespace System.Net
                                        user_denied = !result && !(policy is DefaultCertificatePolicy);
                                }
                                // If there's a 2.0 callback, it takes precedence
-                               if (cb != null) {
-                                       result = cb (sender, leaf, chain, errors);
+                               if (ServerCertificateValidationCallback != null) {
+                                       result = ServerCertificateValidationCallback (sender, leaf, chain, errors);
                                        user_denied = !result;
                                }
                                return new ValidationResult (result, user_denied, status11);