use a 2.0 network stack for MONOTOUCH
[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 #if NET_2_0
113                 static RemoteCertificateValidationCallback server_cert_cb;
114 #endif
115
116                 // Fields
117                 
118                 public const int DefaultNonPersistentConnectionLimit = 4;
119                 public const int DefaultPersistentConnectionLimit = 2;
120
121 #if !MONOTOUCH
122                 const string configKey = "system.net/connectionManagement";
123                 static ConnectionManagementData manager;
124 #endif
125                 
126                 static ServicePointManager ()
127                 {
128 #if !MONOTOUCH
129 #if NET_2_0 && CONFIGURATION_DEP
130                         object cfg = ConfigurationManager.GetSection (configKey);
131                         ConnectionManagementSection s = cfg as ConnectionManagementSection;
132                         if (s != null) {
133                                 manager = new ConnectionManagementData (null);
134                                 foreach (ConnectionManagementElement e in s.ConnectionManagement)
135                                         manager.Add (e.Address, e.MaxConnection);
136
137                                 defaultConnectionLimit = (int) manager.GetMaxConnections ("*");                         
138                                 return;
139                         }
140 #endif
141                         manager = (ConnectionManagementData) ConfigurationSettings.GetConfig (configKey);
142                         if (manager != null) {
143                                 defaultConnectionLimit = (int) manager.GetMaxConnections ("*");                         
144                         }
145 #endif
146                 }
147
148                 // Constructors
149                 private ServicePointManager ()
150                 {
151                 }               
152                 
153                 // Properties
154                 
155 #if NET_2_0
156                 [Obsolete ("Use ServerCertificateValidationCallback instead",
157                            false)]
158 #endif
159                 public static ICertificatePolicy CertificatePolicy {
160                         get { return policy; }
161                         set { policy = value; }
162                 }
163
164 #if NET_1_0
165                 // we need it for SslClientStream
166                 internal
167 #else
168                 [MonoTODO("CRL checks not implemented")]
169                 public
170 #endif
171                 static bool CheckCertificateRevocationList {
172                         get { return _checkCRL; }
173                         set { _checkCRL = false; }      // TODO - don't yet accept true
174                 }
175                 
176                 public static int DefaultConnectionLimit {
177                         get { return defaultConnectionLimit; }
178                         set { 
179                                 if (value <= 0)
180                                         throw new ArgumentOutOfRangeException ("value");
181
182                                 defaultConnectionLimit = value; 
183                         }
184                 }
185
186 #if NET_2_0
187                 static Exception GetMustImplement ()
188                 {
189                         return new NotImplementedException ();
190                 }
191                 
192                 [MonoTODO]
193                 public static int DnsRefreshTimeout
194                 {
195                         get {
196                                 throw GetMustImplement ();
197                         }
198                         set {
199                                 throw GetMustImplement ();
200                         }
201                 }
202                 
203                 [MonoTODO]
204                 public static bool EnableDnsRoundRobin
205                 {
206                         get {
207                                 throw GetMustImplement ();
208                         }
209                         set {
210                                 throw GetMustImplement ();
211                         }
212                 }
213 #endif
214                 
215                 public static int MaxServicePointIdleTime {
216                         get { 
217                                 return maxServicePointIdleTime;
218                         }
219                         set { 
220                                 if (value < -2 || value > Int32.MaxValue)
221                                         throw new ArgumentOutOfRangeException ("value");
222                                 maxServicePointIdleTime = value;
223                         }
224                 }
225                 
226                 public static int MaxServicePoints {
227                         get { 
228                                 return maxServicePoints; 
229                         }
230                         set {  
231                                 if (value < 0)
232                                         throw new ArgumentException ("value");                          
233
234                                 maxServicePoints = value;
235                                 RecycleServicePoints ();
236                         }
237                 }
238
239 #if NET_1_0
240                 // we need it for SslClientStream
241                 internal
242 #else
243                 public
244 #endif
245                 static SecurityProtocolType SecurityProtocol {
246                         get { return _securityProtocol; }
247                         set { _securityProtocol = value; }
248                 }
249
250 #if NET_2_0 && !MONOTOUCH
251                 public static RemoteCertificateValidationCallback ServerCertificateValidationCallback
252                 {
253                         get {
254                                 return server_cert_cb;
255                         }
256                         set {
257                                 server_cert_cb = value;
258                         }
259                 }
260 #endif
261
262 #if NET_1_1
263                 public static bool Expect100Continue {
264                         get { return expectContinue; }
265                         set { expectContinue = value; }
266                 }
267
268                 public static bool UseNagleAlgorithm {
269                         get { return useNagle; }
270                         set { useNagle = value; }
271                 }
272 #endif
273                 // Methods
274                 
275                 public static ServicePoint FindServicePoint (Uri address) 
276                 {
277                         return FindServicePoint (address, GlobalProxySelection.Select);
278                 }
279                 
280                 public static ServicePoint FindServicePoint (string uriString, IWebProxy proxy)
281                 {
282                         return FindServicePoint (new Uri(uriString), proxy);
283                 }
284
285                 public static ServicePoint FindServicePoint (Uri address, IWebProxy proxy)
286                 {
287                         if (address == null)
288                                 throw new ArgumentNullException ("address");
289
290                         RecycleServicePoints ();
291                         
292                         bool usesProxy = false;
293                         bool useConnect = false;
294                         if (proxy != null && !proxy.IsBypassed(address)) {
295                                 usesProxy = true;
296                                 bool isSecure = address.Scheme == "https";
297                                 address = proxy.GetProxy (address);
298                                 if (address.Scheme != "http" && !isSecure)
299                                         throw new NotSupportedException ("Proxy scheme not supported.");
300
301                                 if (isSecure && address.Scheme == "http")
302                                         useConnect = true;
303                         } 
304
305                         address = new Uri (address.Scheme + "://" + address.Authority);
306                         
307                         ServicePoint sp = null;
308                         lock (servicePoints) {
309                                 SPKey key = new SPKey (address, useConnect);
310                                 sp = servicePoints [key] as ServicePoint;
311                                 if (sp != null)
312                                         return sp;
313
314                                 if (maxServicePoints > 0 && servicePoints.Count >= maxServicePoints)
315                                         throw new InvalidOperationException ("maximum number of service points reached");
316
317                                 string addr = address.ToString ();
318 #if MONOTOUCH
319                                 int limit = defaultConnectionLimit;
320 #else
321                                 int limit = (int) manager.GetMaxConnections (addr);
322 #endif
323                                 sp = new ServicePoint (address, limit, maxServicePointIdleTime);
324 #if NET_1_1
325                                 sp.Expect100Continue = expectContinue;
326                                 sp.UseNagleAlgorithm = useNagle;
327 #endif
328                                 sp.UsesProxy = usesProxy;
329                                 sp.UseConnect = useConnect;
330                                 servicePoints.Add (key, sp);
331                         }
332                         
333                         return sp;
334                 }
335                 
336                 // Internal Methods
337
338                 internal static void RecycleServicePoints ()
339                 {
340                         ArrayList toRemove = new ArrayList ();
341                         lock (servicePoints) {
342                                 IDictionaryEnumerator e = servicePoints.GetEnumerator ();
343                                 while (e.MoveNext ()) {
344                                         ServicePoint sp = (ServicePoint) e.Value;
345                                         if (sp.AvailableForRecycling) {
346                                                 toRemove.Add (e.Key);
347                                         }
348                                 }
349                                 
350                                 for (int i = 0; i < toRemove.Count; i++) 
351                                         servicePoints.Remove (toRemove [i]);
352
353                                 if (maxServicePoints == 0 || servicePoints.Count <= maxServicePoints)
354                                         return;
355
356                                 // get rid of the ones with the longest idle time
357                                 SortedList list = new SortedList (servicePoints.Count);
358                                 e = servicePoints.GetEnumerator ();
359                                 while (e.MoveNext ()) {
360                                         ServicePoint sp = (ServicePoint) e.Value;
361                                         if (sp.CurrentConnections == 0) {
362                                                 while (list.ContainsKey (sp.IdleSince))
363                                                         sp.IdleSince = sp.IdleSince.AddMilliseconds (1);
364                                                 list.Add (sp.IdleSince, sp.Address);
365                                         }
366                                 }
367                                 
368                                 for (int i = 0; i < list.Count && servicePoints.Count > maxServicePoints; i++)
369                                         servicePoints.Remove (list.GetByIndex (i));
370                         }
371                 }
372         }
373 }