2 // System.Net.ServicePointManager
5 // Lawrence Pit (loz@cable.a2000.nl)
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
30 using System
.Collections
;
31 using System
.Collections
.Specialized
;
32 using System
.Configuration
;
33 using System
.Net
.Configuration
;
34 using System
.Security
.Cryptography
.X509Certificates
;
37 using System
.Net
.Security
;
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.
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
54 // 6. when finished, return connection
60 public class ServicePointManager
63 Uri uri
; // schema/host/port
66 public SPKey (Uri uri
, bool use_connect
) {
68 this.use_connect
= use_connect
;
75 public bool UseConnect
{
76 get { return use_connect; }
79 public override int GetHashCode () {
80 return uri
.GetHashCode () + ((use_connect
) ? 1 : 0);
83 public override bool Equals (object obj
) {
84 SPKey other
= obj
as SPKey
;
89 return (uri
.Equals (other
.uri
) && other
.use_connect
== use_connect
);
93 private static HybridDictionary servicePoints
= new HybridDictionary ();
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
;
106 static bool expectContinue
= false;
108 static bool expectContinue
= true;
110 static bool useNagle
;
113 static RemoteCertificateValidationCallback server_cert_cb
;
118 public const int DefaultNonPersistentConnectionLimit
= 4;
119 public const int DefaultPersistentConnectionLimit
= 2;
122 const string configKey
= "system.net/connectionManagement";
123 static ConnectionManagementData manager
;
126 static ServicePointManager ()
129 #if NET_2_0 && CONFIGURATION_DEP
130 object cfg
= ConfigurationManager
.GetSection (configKey
);
131 ConnectionManagementSection s
= cfg
as ConnectionManagementSection
;
133 manager
= new ConnectionManagementData (null);
134 foreach (ConnectionManagementElement e
in s
.ConnectionManagement
)
135 manager
.Add (e
.Address
, e
.MaxConnection
);
137 defaultConnectionLimit
= (int) manager
.GetMaxConnections ("*");
141 manager
= (ConnectionManagementData
) ConfigurationSettings
.GetConfig (configKey
);
142 if (manager
!= null) {
143 defaultConnectionLimit
= (int) manager
.GetMaxConnections ("*");
149 private ServicePointManager ()
156 [Obsolete ("Use ServerCertificateValidationCallback instead",
159 public static ICertificatePolicy CertificatePolicy
{
160 get { return policy; }
161 set { policy = value; }
165 // we need it for SslClientStream
168 [MonoTODO("CRL checks not implemented")]
171 static bool CheckCertificateRevocationList
{
172 get { return _checkCRL; }
173 set { _checkCRL = false; }
// TODO - don't yet accept true
176 public static int DefaultConnectionLimit
{
177 get { return defaultConnectionLimit; }
180 throw new ArgumentOutOfRangeException ("value");
182 defaultConnectionLimit
= value;
187 static Exception
GetMustImplement ()
189 return new NotImplementedException ();
193 public static int DnsRefreshTimeout
196 throw GetMustImplement ();
199 throw GetMustImplement ();
204 public static bool EnableDnsRoundRobin
207 throw GetMustImplement ();
210 throw GetMustImplement ();
215 public static int MaxServicePointIdleTime
{
217 return maxServicePointIdleTime
;
220 if (value < -2 || value > Int32
.MaxValue
)
221 throw new ArgumentOutOfRangeException ("value");
222 maxServicePointIdleTime
= value;
226 public static int MaxServicePoints
{
228 return maxServicePoints
;
232 throw new ArgumentException ("value");
234 maxServicePoints
= value;
235 RecycleServicePoints ();
240 // we need it for SslClientStream
245 static SecurityProtocolType SecurityProtocol
{
246 get { return _securityProtocol; }
247 set { _securityProtocol = value; }
251 public static RemoteCertificateValidationCallback ServerCertificateValidationCallback
254 return server_cert_cb
;
257 server_cert_cb
= value;
263 public static bool Expect100Continue
{
264 get { return expectContinue; }
265 set { expectContinue = value; }
268 public static bool UseNagleAlgorithm
{
269 get { return useNagle; }
270 set { useNagle = value; }
275 public static ServicePoint
FindServicePoint (Uri address
)
277 return FindServicePoint (address
, GlobalProxySelection
.Select
);
280 public static ServicePoint
FindServicePoint (string uriString
, IWebProxy proxy
)
282 return FindServicePoint (new Uri(uriString
), proxy
);
285 public static ServicePoint
FindServicePoint (Uri address
, IWebProxy proxy
)
288 throw new ArgumentNullException ("address");
290 RecycleServicePoints ();
292 bool usesProxy
= false;
293 bool useConnect
= false;
294 if (proxy
!= null && !proxy
.IsBypassed(address
)) {
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.");
301 if (isSecure
&& address
.Scheme
== "http")
305 address
= new Uri (address
.Scheme
+ "://" + address
.Authority
);
307 ServicePoint sp
= null;
308 lock (servicePoints
) {
309 SPKey key
= new SPKey (address
, useConnect
);
310 sp
= servicePoints
[key
] as ServicePoint
;
314 if (maxServicePoints
> 0 && servicePoints
.Count
>= maxServicePoints
)
315 throw new InvalidOperationException ("maximum number of service points reached");
317 string addr
= address
.ToString ();
319 int limit
= defaultConnectionLimit
;
321 int limit
= (int) manager
.GetMaxConnections (addr
);
323 sp
= new ServicePoint (address
, limit
, maxServicePointIdleTime
);
325 sp
.Expect100Continue
= expectContinue
;
326 sp
.UseNagleAlgorithm
= useNagle
;
328 sp
.UsesProxy
= usesProxy
;
329 sp
.UseConnect
= useConnect
;
330 servicePoints
.Add (key
, sp
);
338 internal static void RecycleServicePoints ()
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
);
350 for (int i
= 0; i
< toRemove
.Count
; i
++)
351 servicePoints
.Remove (toRemove
[i
]);
353 if (maxServicePoints
== 0 || servicePoints
.Count
<= maxServicePoints
)
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
);
368 for (int i
= 0; i
< list
.Count
&& servicePoints
.Count
> maxServicePoints
; i
++)
369 servicePoints
.Remove (list
.GetByIndex (i
));