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