New test.
[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 //
37 // notes:
38 // A service point manager manages service points (duh!).
39 // A service point maintains a list of connections (per scheme + authority).
40 // According to HttpWebRequest.ConnectionGroupName each connection group
41 // creates additional connections. therefor, a service point has a hashtable
42 // of connection groups where each value is a list of connections.
43 // 
44 // when we need to make an HttpWebRequest, we need to do the following:
45 // 1. find service point, given Uri and Proxy 
46 // 2. find connection group, given service point and group name
47 // 3. find free connection in connection group, or create one (if ok due to limits)
48 // 4. lease connection
49 // 5. execute request
50 // 6. when finished, return connection
51 //
52
53
54 namespace System.Net 
55 {
56         public class ServicePointManager
57         {
58                 private static HybridDictionary servicePoints = new HybridDictionary ();
59                 
60                 // Static properties
61                 
62                 private static ICertificatePolicy policy = new DefaultCertificatePolicy ();
63                 private static int defaultConnectionLimit = DefaultPersistentConnectionLimit;
64                 private static int maxServicePointIdleTime = 900000; // 15 minutes
65                 private static int maxServicePoints = 0;
66                 private static bool _checkCRL = false;
67 #if (NET_1_0 || NET_1_1)
68                 private static SecurityProtocolType _securityProtocol = SecurityProtocolType.Ssl3;
69 #else
70                 private static SecurityProtocolType _securityProtocol = SecurityProtocolType.Default;
71 #endif
72 #if NET_1_1
73                 static bool expectContinue = true;
74                 static bool useNagle;
75 #endif
76
77                 // Fields
78                 
79                 public const int DefaultNonPersistentConnectionLimit = 4;
80                 public const int DefaultPersistentConnectionLimit = 2;
81
82                 const string configKey = "system.net/connectionManagement";
83                 static ConnectionManagementData manager;
84                 
85                 static ServicePointManager ()
86                 {
87 #if NET_2_0 && CONFIGURATION_DEP
88                         object cfg = ConfigurationManager.GetSection (configKey);
89                         ConnectionManagementSection s = cfg as ConnectionManagementSection;
90                         if (s != null) {
91                                 manager = new ConnectionManagementData (null);
92                                 foreach (ConnectionManagementElement e in s.ConnectionManagement)
93                                         manager.Add (e.Address, e.MaxConnection);
94
95                                 return;
96                         }
97 #endif
98                         manager = (ConnectionManagementData) ConfigurationSettings.GetConfig (configKey);
99                 }
100
101                 // Constructors
102                 private ServicePointManager ()
103                 {
104                 }               
105                 
106                 // Properties
107                 
108                 public static ICertificatePolicy CertificatePolicy {
109                         get { return policy; }
110                         set { policy = value; }
111                 }
112
113 #if NET_1_0
114                 // we need it for SslClientStream
115                 internal
116 #else
117                 [MonoTODO("CRL checks not implemented")]
118                 public
119 #endif
120                 static bool CheckCertificateRevocationList {
121                         get { return _checkCRL; }
122                         set { _checkCRL = false; }      // TODO - don't yet accept true
123                 }
124                 
125                 public static int DefaultConnectionLimit {
126                         get { return defaultConnectionLimit; }
127                         set { 
128                                 if (value <= 0)
129                                         throw new ArgumentOutOfRangeException ("value");
130
131                                 defaultConnectionLimit = value; 
132                         }
133                 }
134                 
135                 public static int MaxServicePointIdleTime {
136                         get { 
137                                 return maxServicePointIdleTime;
138                         }
139                         set { 
140                                 if (value < -2 || value > Int32.MaxValue)
141                                         throw new ArgumentOutOfRangeException ("value");
142                                 maxServicePointIdleTime = value;
143                         }
144                 }
145                 
146                 public static int MaxServicePoints {
147                         get { 
148                                 return maxServicePoints; 
149                         }
150                         set {  
151                                 if (value < 0)
152                                         throw new ArgumentException ("value");                          
153
154                                 maxServicePoints = value;
155                                 RecycleServicePoints ();
156                         }
157                 }
158
159 #if NET_1_0
160                 // we need it for SslClientStream
161                 internal
162 #else
163                 public
164 #endif
165                 static SecurityProtocolType SecurityProtocol {
166                         get { return _securityProtocol; }
167                         set { _securityProtocol = value; }
168                 }
169
170 #if NET_1_1
171                 public static bool Expect100Continue {
172                         get { return expectContinue; }
173                         set { expectContinue = value; }
174                 }
175
176                 public static bool UseNagleAlgorithm {
177                         get { return useNagle; }
178                         set { useNagle = value; }
179                 }
180 #endif
181                 // Methods
182                 
183                 public static ServicePoint FindServicePoint (Uri address) 
184                 {
185                         return FindServicePoint (address, GlobalProxySelection.Select);
186                 }
187                 
188                 public static ServicePoint FindServicePoint (string uriString, IWebProxy proxy)
189                 {
190                         return FindServicePoint (new Uri(uriString), proxy);
191                 }
192                 
193                 public static ServicePoint FindServicePoint (Uri address, IWebProxy proxy)
194                 {
195                         if (address == null)
196                                 throw new ArgumentNullException ("address");
197
198                         RecycleServicePoints ();
199                         
200                         bool usesProxy = false;
201                         bool useConnect = false;
202                         if (proxy != null && !proxy.IsBypassed(address)) {
203                                 usesProxy = true;
204                                 bool isSecure = address.Scheme == "https";
205                                 address = proxy.GetProxy (address);
206                                 if (address.Scheme != "http" && !isSecure)
207                                         throw new NotSupportedException ("Proxy scheme not supported.");
208
209                                 if (isSecure && address.Scheme == "http")
210                                         useConnect = true;
211                         } 
212
213                         address = new Uri (address.Scheme + "://" + address.Authority);
214                         
215                         ServicePoint sp = null;
216                         lock (servicePoints) {
217                                 int key = address.GetHashCode () + (int) ((useConnect) ? 1 : 0);
218                                 sp = servicePoints [key] as ServicePoint;
219                                 if (sp != null)
220                                         return sp;
221
222                                 if (maxServicePoints > 0 && servicePoints.Count >= maxServicePoints)
223                                         throw new InvalidOperationException ("maximum number of service points reached");
224
225                                 string addr = address.ToString ();
226                                 int limit = (int) manager.GetMaxConnections (addr);
227                                 sp = new ServicePoint (address, limit, maxServicePointIdleTime);
228 #if NET_1_1
229                                 sp.Expect100Continue = expectContinue;
230                                 sp.UseNagleAlgorithm = useNagle;
231 #endif
232                                 sp.UsesProxy = usesProxy;
233                                 sp.UseConnect = useConnect;
234                                 servicePoints.Add (key, sp);
235                         }
236                         
237                         return sp;
238                 }
239                 
240                 // Internal Methods
241
242                 internal static void RecycleServicePoints ()
243                 {
244                         ArrayList toRemove = new ArrayList ();
245                         lock (servicePoints) {
246                                 IDictionaryEnumerator e = servicePoints.GetEnumerator ();
247                                 while (e.MoveNext ()) {
248                                         ServicePoint sp = (ServicePoint) e.Value;
249                                         if (sp.AvailableForRecycling) {
250                                                 toRemove.Add (e.Key);
251                                         }
252                                 }
253                                 
254                                 for (int i = 0; i < toRemove.Count; i++) 
255                                         servicePoints.Remove (toRemove [i]);
256
257                                 if (maxServicePoints == 0 || servicePoints.Count <= maxServicePoints)
258                                         return;
259
260                                 // get rid of the ones with the longest idle time
261                                 SortedList list = new SortedList (servicePoints.Count);
262                                 e = servicePoints.GetEnumerator ();
263                                 while (e.MoveNext ()) {
264                                         ServicePoint sp = (ServicePoint) e.Value;
265                                         if (sp.CurrentConnections == 0) {
266                                                 while (list.ContainsKey (sp.IdleSince))
267                                                         sp.IdleSince.AddMilliseconds (1);
268                                                 list.Add (sp.IdleSince, sp.Address);
269                                         }
270                                 }
271                                 
272                                 for (int i = 0; i < list.Count && servicePoints.Count > maxServicePoints; i++)
273                                         servicePoints.Remove (list.GetByIndex (i));
274                         }
275                 }
276         }
277 }