2009-06-12 Bill Holmes <billholmes54@gmail.com>
[mono.git] / mcs / class / System / System.Net / ServicePointManager.cs
1 //
2 // System.Net.ServicePointManager
3 //
4 // Author:
5 //   Lawrence Pit (loz@cable.a2000.nl)
6 //
7
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System;
30 using System.Collections;
31 using System.Collections.Specialized;
32 using System.Configuration;
33 using System.Net.Configuration;
34 using System.Security.Cryptography.X509Certificates;
35
36 #if NET_2_0
37 using System.Net.Security;
38 #endif
39
40 //
41 // notes:
42 // A service point manager manages service points (duh!).
43 // A service point maintains a list of connections (per scheme + authority).
44 // According to HttpWebRequest.ConnectionGroupName each connection group
45 // creates additional connections. therefor, a service point has a hashtable
46 // of connection groups where each value is a list of connections.
47 // 
48 // when we need to make an HttpWebRequest, we need to do the following:
49 // 1. find service point, given Uri and Proxy 
50 // 2. find connection group, given service point and group name
51 // 3. find free connection in connection group, or create one (if ok due to limits)
52 // 4. lease connection
53 // 5. execute request
54 // 6. when finished, return connection
55 //
56
57
58 namespace System.Net 
59 {
60         public class ServicePointManager
61         {
62                 class SPKey {
63                         Uri uri; // schema/host/port
64                         bool use_connect;
65
66                         public SPKey (Uri uri, bool use_connect) {
67                                 this.uri = uri;
68                                 this.use_connect = use_connect;
69                         }
70
71                         public Uri Uri {
72                                 get { return uri; }
73                         }
74
75                         public bool UseConnect {
76                                 get { return use_connect; }
77                         }
78
79                         public override int GetHashCode () {
80                                 return uri.GetHashCode () + ((use_connect) ? 1 : 0);
81                         }
82
83                         public override bool Equals (object obj) {
84                                 SPKey other = obj as SPKey;
85                                 if (obj == null) {
86                                         return false;
87                                 }
88
89                                 return (uri.Equals (other.uri) && other.use_connect == use_connect);
90                         }
91                 }
92
93                 private static HybridDictionary servicePoints = new HybridDictionary ();
94                 
95                 // Static properties
96                 
97                 private static ICertificatePolicy policy = new DefaultCertificatePolicy ();
98                 private static int defaultConnectionLimit = DefaultPersistentConnectionLimit;
99                 private static int maxServicePointIdleTime = 900000; // 15 minutes
100                 private static int maxServicePoints = 0;
101                 private static bool _checkCRL = false;
102                 private static SecurityProtocolType _securityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
103
104 #if NET_1_1
105 #if TARGET_JVM
106                 static bool expectContinue = false;
107 #else
108                 static bool expectContinue = true;
109 #endif
110                 static bool useNagle;
111 #endif
112
113                 // Fields
114                 
115                 public const int DefaultNonPersistentConnectionLimit = 4;
116                 public const int DefaultPersistentConnectionLimit = 2;
117
118                 const string configKey = "system.net/connectionManagement";
119                 static ConnectionManagementData manager;
120                 
121                 static ServicePointManager ()
122                 {
123 #if NET_2_0 && CONFIGURATION_DEP
124                         object cfg = ConfigurationManager.GetSection (configKey);
125                         ConnectionManagementSection s = cfg as ConnectionManagementSection;
126                         if (s != null) {
127                                 manager = new ConnectionManagementData (null);
128                                 foreach (ConnectionManagementElement e in s.ConnectionManagement)
129                                         manager.Add (e.Address, e.MaxConnection);
130
131                                 defaultConnectionLimit = (int) manager.GetMaxConnections ("*");                         
132                                 return;
133                         }
134 #endif
135                         manager = (ConnectionManagementData) ConfigurationSettings.GetConfig (configKey);
136                         if (manager != null) {
137                                 defaultConnectionLimit = (int) manager.GetMaxConnections ("*");                         
138                         }
139                 }
140
141                 // Constructors
142                 private ServicePointManager ()
143                 {
144                 }               
145                 
146                 // Properties
147                 
148 #if NET_2_0
149                 [Obsolete ("Use ServerCertificateValidationCallback instead",
150                            false)]
151 #endif
152                 public static ICertificatePolicy CertificatePolicy {
153                         get { return policy; }
154                         set { policy = value; }
155                 }
156
157 #if NET_1_0
158                 // we need it for SslClientStream
159                 internal
160 #else
161                 [MonoTODO("CRL checks not implemented")]
162                 public
163 #endif
164                 static bool CheckCertificateRevocationList {
165                         get { return _checkCRL; }
166                         set { _checkCRL = false; }      // TODO - don't yet accept true
167                 }
168                 
169                 public static int DefaultConnectionLimit {
170                         get { return defaultConnectionLimit; }
171                         set { 
172                                 if (value <= 0)
173                                         throw new ArgumentOutOfRangeException ("value");
174
175                                 defaultConnectionLimit = value; 
176                         }
177                 }
178
179 #if NET_2_0
180                 static Exception GetMustImplement ()
181                 {
182                         return new NotImplementedException ();
183                 }
184                 
185                 [MonoTODO]
186                 public static int DnsRefreshTimeout
187                 {
188                         get {
189                                 throw GetMustImplement ();
190                         }
191                         set {
192                                 throw GetMustImplement ();
193                         }
194                 }
195                 
196                 [MonoTODO]
197                 public static bool EnableDnsRoundRobin
198                 {
199                         get {
200                                 throw GetMustImplement ();
201                         }
202                         set {
203                                 throw GetMustImplement ();
204                         }
205                 }
206 #endif
207                 
208                 public static int MaxServicePointIdleTime {
209                         get { 
210                                 return maxServicePointIdleTime;
211                         }
212                         set { 
213                                 if (value < -2 || value > Int32.MaxValue)
214                                         throw new ArgumentOutOfRangeException ("value");
215                                 maxServicePointIdleTime = value;
216                         }
217                 }
218                 
219                 public static int MaxServicePoints {
220                         get { 
221                                 return maxServicePoints; 
222                         }
223                         set {  
224                                 if (value < 0)
225                                         throw new ArgumentException ("value");                          
226
227                                 maxServicePoints = value;
228                                 RecycleServicePoints ();
229                         }
230                 }
231
232 #if NET_1_0
233                 // we need it for SslClientStream
234                 internal
235 #else
236                 public
237 #endif
238                 static SecurityProtocolType SecurityProtocol {
239                         get { return _securityProtocol; }
240                         set { _securityProtocol = value; }
241                 }
242
243 #if NET_2_0 && SECURITY_DEP
244                 [MonoTODO]
245                 public static RemoteCertificateValidationCallback ServerCertificateValidationCallback
246                 {
247                         get {
248                                 throw GetMustImplement ();
249                         }
250                         set {
251                                 throw GetMustImplement ();
252                         }
253                 }
254 #endif
255
256 #if NET_1_1
257                 public static bool Expect100Continue {
258                         get { return expectContinue; }
259                         set { expectContinue = value; }
260                 }
261
262                 public static bool UseNagleAlgorithm {
263                         get { return useNagle; }
264                         set { useNagle = value; }
265                 }
266 #endif
267                 // Methods
268                 
269                 public static ServicePoint FindServicePoint (Uri address) 
270                 {
271                         return FindServicePoint (address, GlobalProxySelection.Select);
272                 }
273                 
274                 public static ServicePoint FindServicePoint (string uriString, IWebProxy proxy)
275                 {
276                         return FindServicePoint (new Uri(uriString), proxy);
277                 }
278
279                 public static ServicePoint FindServicePoint (Uri address, IWebProxy proxy)
280                 {
281                         if (address == null)
282                                 throw new ArgumentNullException ("address");
283
284                         RecycleServicePoints ();
285                         
286                         bool usesProxy = false;
287                         bool useConnect = false;
288                         if (proxy != null && !proxy.IsBypassed(address)) {
289                                 usesProxy = true;
290                                 bool isSecure = address.Scheme == "https";
291                                 address = proxy.GetProxy (address);
292                                 if (address.Scheme != "http" && !isSecure)
293                                         throw new NotSupportedException ("Proxy scheme not supported.");
294
295                                 if (isSecure && address.Scheme == "http")
296                                         useConnect = true;
297                         } 
298
299                         address = new Uri (address.Scheme + "://" + address.Authority);
300                         
301                         ServicePoint sp = null;
302                         lock (servicePoints) {
303                                 SPKey key = new SPKey (address, useConnect);
304                                 sp = servicePoints [key] as ServicePoint;
305                                 if (sp != null)
306                                         return sp;
307
308                                 if (maxServicePoints > 0 && servicePoints.Count >= maxServicePoints)
309                                         throw new InvalidOperationException ("maximum number of service points reached");
310
311                                 string addr = address.ToString ();
312                                 int limit = (int) manager.GetMaxConnections (addr);
313                                 sp = new ServicePoint (address, limit, maxServicePointIdleTime);
314 #if NET_1_1
315                                 sp.Expect100Continue = expectContinue;
316                                 sp.UseNagleAlgorithm = useNagle;
317 #endif
318                                 sp.UsesProxy = usesProxy;
319                                 sp.UseConnect = useConnect;
320                                 servicePoints.Add (key, sp);
321                         }
322                         
323                         return sp;
324                 }
325                 
326                 // Internal Methods
327
328                 internal static void RecycleServicePoints ()
329                 {
330                         ArrayList toRemove = new ArrayList ();
331                         lock (servicePoints) {
332                                 IDictionaryEnumerator e = servicePoints.GetEnumerator ();
333                                 while (e.MoveNext ()) {
334                                         ServicePoint sp = (ServicePoint) e.Value;
335                                         if (sp.AvailableForRecycling) {
336                                                 toRemove.Add (e.Key);
337                                         }
338                                 }
339                                 
340                                 for (int i = 0; i < toRemove.Count; i++) 
341                                         servicePoints.Remove (toRemove [i]);
342
343                                 if (maxServicePoints == 0 || servicePoints.Count <= maxServicePoints)
344                                         return;
345
346                                 // get rid of the ones with the longest idle time
347                                 SortedList list = new SortedList (servicePoints.Count);
348                                 e = servicePoints.GetEnumerator ();
349                                 while (e.MoveNext ()) {
350                                         ServicePoint sp = (ServicePoint) e.Value;
351                                         if (sp.CurrentConnections == 0) {
352                                                 while (list.ContainsKey (sp.IdleSince))
353                                                         sp.IdleSince = sp.IdleSince.AddMilliseconds (1);
354                                                 list.Add (sp.IdleSince, sp.Address);
355                                         }
356                                 }
357                                 
358                                 for (int i = 0; i < list.Count && servicePoints.Count > maxServicePoints; i++)
359                                         servicePoints.Remove (list.GetByIndex (i));
360                         }
361                 }
362         }
363 }