[System] HttpListenerRequest: ignore bad cookies and keep request alive (#5657)
[mono.git] / mcs / class / System / System.Net / ServicePoint.cs
1 //
2 // System.Net.ServicePoint
3 //
4 // Authors:
5 //      Lawrence Pit (loz@cable.a2000.nl)
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //
8 // (c) 2002 Lawrence Pit
9 // (c) 2003 Ximian, Inc. (http://www.ximian.com)
10 //
11
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System;
34 using System.Collections.Generic;
35 using System.Diagnostics;
36 using System.Collections;
37 using System.Net.Sockets;
38 using System.Security.Cryptography.X509Certificates;
39 using System.Threading;
40
41 namespace System.Net 
42 {
43         public class ServicePoint
44         {
45                 readonly Uri uri;
46                 int connectionLimit;
47                 int maxIdleTime;
48                 int currentConnections;
49                 DateTime idleSince;
50                 DateTime lastDnsResolve;
51                 Version protocolVersion;
52                 IPHostEntry host;
53                 bool usesProxy;
54                 Dictionary<string,WebConnectionGroup> groups;
55                 bool sendContinue = true;
56                 bool useConnect;
57                 object hostE = new object ();
58                 bool useNagle;
59                 BindIPEndPoint endPointCallback = null;
60                 bool tcp_keepalive;
61                 int tcp_keepalive_time;
62                 int tcp_keepalive_interval;
63                 Timer idleTimer;
64
65                 // Constructors
66
67                 internal ServicePoint (Uri uri, int connectionLimit, int maxIdleTime)
68                 {
69                         this.uri = uri;  
70                         this.connectionLimit = connectionLimit;
71                         this.maxIdleTime = maxIdleTime; 
72                         this.currentConnections = 0;
73                         this.idleSince = DateTime.UtcNow;
74                 }
75                 
76                 // Properties
77                 
78                 public Uri Address {
79                         get { return uri; }
80                 }
81
82                 static Exception GetMustImplement ()
83                 {
84                         return new NotImplementedException ();
85                 }
86
87                 public BindIPEndPoint BindIPEndPointDelegate
88                 {
89                         get { return endPointCallback; }
90                         set { endPointCallback = value; }
91                 }
92                 
93                 [MonoTODO]
94                 public int ConnectionLeaseTimeout
95                 {
96                         get {
97                                 throw GetMustImplement ();
98                         }
99                         set {
100                                 throw GetMustImplement ();
101                         }
102                 }
103                 
104                 public int ConnectionLimit {
105                         get { return connectionLimit; }
106                         set {
107                                 if (value <= 0)
108                                         throw new ArgumentOutOfRangeException ();
109
110                                 connectionLimit = value;
111                         }
112                 }
113                 
114                 public string ConnectionName {
115                         get { return uri.Scheme; }
116                 }
117
118                 public int CurrentConnections {
119                         get {
120                                 return currentConnections;
121                         }
122                 }
123
124                 public DateTime IdleSince {
125                         get {
126                                 return idleSince.ToLocalTime ();
127                         }
128                 }
129
130                 public int MaxIdleTime {
131                         get { return maxIdleTime; }
132                         set { 
133                                 if (value < Timeout.Infinite || value > Int32.MaxValue)
134                                         throw new ArgumentOutOfRangeException ();
135
136                                 lock (this) {
137                                         maxIdleTime = value;
138                                         if (idleTimer != null)
139                                                 idleTimer.Change (maxIdleTime, maxIdleTime);
140                                 }
141                         }
142                 }
143                 
144                 public virtual Version ProtocolVersion {
145                         get { return protocolVersion; }
146                 }
147
148                 [MonoTODO]
149                 public int ReceiveBufferSize
150                 {
151                         get {
152                                 throw GetMustImplement ();
153                         }
154                         set {
155                                 throw GetMustImplement ();
156                         }
157                 }
158                 
159                 public bool SupportsPipelining {
160                         get { return HttpVersion.Version11.Equals (protocolVersion); }
161                 }
162
163
164                 public bool Expect100Continue {
165                         get { return SendContinue; }
166                         set { SendContinue = value; }
167                 }
168
169                 public bool UseNagleAlgorithm {
170                         get { return useNagle; }
171                         set { useNagle = value; }
172                 }
173
174                 internal bool SendContinue {
175                         get { return sendContinue &&
176                                      (protocolVersion == null || protocolVersion == HttpVersion.Version11); }
177                         set { sendContinue = value; }
178                 }
179                 // Methods
180
181                 public void SetTcpKeepAlive (bool enabled, int keepAliveTime, int keepAliveInterval)
182                 {
183                         if (enabled) {
184                                 if (keepAliveTime <= 0)
185                                         throw new ArgumentOutOfRangeException ("keepAliveTime", "Must be greater than 0");
186                                 if (keepAliveInterval <= 0)
187                                         throw new ArgumentOutOfRangeException ("keepAliveInterval", "Must be greater than 0");
188                         }
189
190                         tcp_keepalive = enabled;
191                         tcp_keepalive_time = keepAliveTime;
192                         tcp_keepalive_interval = keepAliveInterval;
193                 }
194
195                 internal void KeepAliveSetup (Socket socket)
196                 {
197                         if (!tcp_keepalive)
198                                 return;
199
200                         byte [] bytes = new byte [12];
201                         PutBytes (bytes, (uint) (tcp_keepalive ? 1 : 0), 0);
202                         PutBytes (bytes, (uint) tcp_keepalive_time, 4);
203                         PutBytes (bytes, (uint) tcp_keepalive_interval, 8);
204                         socket.IOControl (IOControlCode.KeepAliveValues, bytes, null);
205                 }
206
207                 static void PutBytes (byte [] bytes, uint v, int offset)
208                 {
209                         if (BitConverter.IsLittleEndian) {
210                                 bytes [offset] = (byte) (v & 0x000000ff);
211                                 bytes [offset + 1] = (byte) ((v & 0x0000ff00) >> 8);
212                                 bytes [offset + 2] = (byte) ((v & 0x00ff0000) >> 16);
213                                 bytes [offset + 3] = (byte) ((v & 0xff000000) >> 24);
214                         } else {
215                                 bytes [offset + 3] = (byte) (v & 0x000000ff);
216                                 bytes [offset + 2] = (byte) ((v & 0x0000ff00) >> 8);
217                                 bytes [offset + 1] = (byte) ((v & 0x00ff0000) >> 16);
218                                 bytes [offset] = (byte) ((v & 0xff000000) >> 24);
219                         }
220                 }
221
222                 // Internal Methods
223
224                 internal bool UsesProxy {
225                         get { return usesProxy; }
226                         set { usesProxy = value; }
227                 }
228
229                 internal bool UseConnect {
230                         get { return useConnect; }
231                         set { useConnect = value; }
232                 }
233
234                 WebConnectionGroup GetConnectionGroup (string name)
235                 {
236                         if (name == null)
237                                 name = "";
238
239                         /*
240                          * Optimization:
241                          * 
242                          * In the vast majority of cases, we only have one single WebConnectionGroup per ServicePoint, so we
243                          * don't need to allocate a dictionary.
244                          * 
245                          */
246
247                         WebConnectionGroup group;
248                         if (groups != null && groups.TryGetValue (name, out group))
249                                 return group;
250
251                         group = new WebConnectionGroup (this, name);
252                         group.ConnectionClosed += (s, e) => currentConnections--;
253
254                         if (groups == null)
255                                 groups = new Dictionary<string, WebConnectionGroup> ();
256                         groups.Add (name, group);
257
258                         return group;
259                 }
260
261                 void RemoveConnectionGroup (WebConnectionGroup group)
262                 {
263                         if (groups == null || groups.Count == 0)
264                                 throw new InvalidOperationException ();
265
266                         groups.Remove (group.Name);
267                 }
268
269                 bool CheckAvailableForRecycling (out DateTime outIdleSince)
270                 {
271                         outIdleSince = DateTime.MinValue;
272
273                         TimeSpan idleTimeSpan;
274                         List<WebConnectionGroup> groupList = null, removeList = null;
275                         lock (this) {
276                                 if (groups == null || groups.Count == 0) {
277                                         idleSince = DateTime.MinValue;
278                                         return true;
279                                 }
280
281                                 idleTimeSpan = TimeSpan.FromMilliseconds (maxIdleTime);
282
283                                 /*
284                                  * WebConnectionGroup.TryRecycle() must run outside the lock, so we need to
285                                  * copy the group dictionary if it exists.
286                                  * 
287                                  * In most cases, we only have a single connection group, so we can simply store
288                                  * that in a local variable instead of copying a collection.
289                                  * 
290                                  */
291
292                                 groupList = new List<WebConnectionGroup> (groups.Values);
293                         }
294
295                         foreach (var group in groupList) {
296                                 if (!group.TryRecycle (idleTimeSpan, ref outIdleSince))
297                                         continue;
298                                 if (removeList == null)
299                                         removeList = new List<WebConnectionGroup> ();
300                                 removeList.Add (group);
301                         }
302
303                         lock (this) {
304                                 idleSince = outIdleSince;
305
306                                 if (removeList != null && groups != null) {
307                                         foreach (var group in removeList)
308                                                 if (groups.ContainsKey (group.Name))
309                                                         RemoveConnectionGroup (group);
310                                 }
311
312                                 if (groups != null && groups.Count == 0)
313                                         groups = null;
314
315                                 if (groups == null) {
316                                         if (idleTimer != null) {
317                                                 idleTimer.Dispose ();
318                                                 idleTimer = null;
319                                         }
320                                         return true;
321                                 }
322
323                                 return false;
324                         }
325                 }
326
327                 void IdleTimerCallback (object obj)
328                 {
329                         DateTime dummy;
330                         CheckAvailableForRecycling (out dummy);
331                 }
332
333                 private bool HasTimedOut
334                 {
335                         get {
336                                 int timeout = ServicePointManager.DnsRefreshTimeout;
337                                 return timeout != Timeout.Infinite &&
338                                         (lastDnsResolve + TimeSpan.FromMilliseconds (timeout)) < DateTime.UtcNow;
339                         }
340                 }
341
342                 internal IPHostEntry HostEntry
343                 {
344                         get {
345                                 lock (hostE) {
346                                         string uriHost = uri.Host;
347
348                                         // Cannot do DNS resolution on literal IP addresses
349                                         if (uri.HostNameType == UriHostNameType.IPv6 || uri.HostNameType == UriHostNameType.IPv4) {
350                                                 if (host != null)
351                                                         return host;
352
353                                                 if (uri.HostNameType == UriHostNameType.IPv6) {
354                                                         // Remove square brackets
355                                                         uriHost = uriHost.Substring (1, uriHost.Length - 2);
356                                                 }
357
358                                                 // Creates IPHostEntry
359                                                 host = new IPHostEntry();
360                                                 host.AddressList = new IPAddress[] { IPAddress.Parse (uriHost) };
361                                                 return host;
362                                         }
363
364                                         if (!HasTimedOut && host != null)
365                                                 return host;
366
367                                         lastDnsResolve = DateTime.UtcNow;
368
369                                         try {
370                                                 host = Dns.GetHostEntry (uriHost);
371                                         } catch {
372                                                 return null;
373                                         }
374                                 }
375
376                                 return host;
377                         }
378                 }
379
380                 internal void SetVersion (Version version)
381                 {
382                         protocolVersion = version;
383                 }
384
385                 internal EventHandler SendRequest (HttpWebRequest request, string groupName)
386                 {
387                         WebConnection cnc;
388                         
389                         lock (this) {
390                                 bool created;
391                                 WebConnectionGroup cncGroup = GetConnectionGroup (groupName);
392                                 cnc = cncGroup.GetConnection (request, out created);
393                                 if (created) {
394                                         ++currentConnections;
395                                         if (idleTimer == null)
396                                                 idleTimer = new Timer (IdleTimerCallback, null, maxIdleTime, maxIdleTime);
397                                 }
398                         }
399                         
400                         return cnc.SendRequest (request);
401                 }
402                 public bool CloseConnectionGroup (string connectionGroupName)
403                 {
404                         WebConnectionGroup cncGroup = null;
405
406                         lock (this) {
407                                 cncGroup = GetConnectionGroup (connectionGroupName);
408                                 if (cncGroup != null) {
409                                         RemoveConnectionGroup (cncGroup);
410                                 }
411                         }
412
413                         // WebConnectionGroup.Close() must *not* be called inside the lock
414                         if (cncGroup != null) {
415                                 cncGroup.Close ();
416                                 return true;
417                         }
418
419                         return false;
420                 }
421
422                 //
423                 // Copied from the referencesource
424                 //
425
426                 object m_ServerCertificateOrBytes;
427                 object m_ClientCertificateOrBytes;
428
429                 /// <devdoc>
430                 ///    <para>
431                 ///       Gets the certificate received for this <see cref='System.Net.ServicePoint'/>.
432                 ///    </para>
433                 /// </devdoc>
434                 public  X509Certificate Certificate {
435                         get {
436                                 object chkCert = m_ServerCertificateOrBytes;
437                                 if (chkCert != null && chkCert.GetType() == typeof(byte[]))
438                                         return (X509Certificate)(m_ServerCertificateOrBytes = new X509Certificate((byte[]) chkCert));
439                                 else
440                                         return chkCert as X509Certificate;
441                         }
442                 }
443                 internal void UpdateServerCertificate(X509Certificate certificate)
444                 {
445                         if (certificate != null)
446                                 m_ServerCertificateOrBytes = certificate.GetRawCertData();
447                         else
448                                 m_ServerCertificateOrBytes = null;
449                 }
450
451                 /// <devdoc>
452                 /// <para>
453                 /// Gets the Client Certificate sent by us to the Server.
454                 /// </para>
455                 /// </devdoc>
456                 public  X509Certificate ClientCertificate {
457                         get {
458                                 object chkCert = m_ClientCertificateOrBytes;
459                                 if (chkCert != null && chkCert.GetType() == typeof(byte[]))
460                                         return (X509Certificate)(m_ClientCertificateOrBytes = new X509Certificate((byte[]) chkCert));
461                                 else
462                                         return chkCert as X509Certificate;
463                         }
464                 }
465                 internal void UpdateClientCertificate(X509Certificate certificate)
466                 {
467                         if (certificate != null)
468                                 m_ClientCertificateOrBytes = certificate.GetRawCertData();
469                         else
470                                 m_ClientCertificateOrBytes = null;
471                 }
472
473                 internal bool CallEndPointDelegate (Socket sock, IPEndPoint remote)
474                 {
475                         if (endPointCallback == null)
476                                 return true;
477
478                         int count = 0;
479                         for (;;) {
480                                 IPEndPoint local = null;
481                                 try {
482                                         local = endPointCallback (this,
483                                                 remote, count);
484                                 } catch {
485                                         // This is to differentiate from an
486                                         // OverflowException, which should propagate.
487                                         return false;
488                                 }
489
490                                 if (local == null)
491                                         return true;
492
493                                 try {
494                                         sock.Bind (local);
495                                 } catch (SocketException) {
496                                         // This is intentional; the docs say
497                                         // that if the Bind fails, we keep
498                                         // going until there is an
499                                         // OverflowException on the retry
500                                         // count.
501                                         checked { ++count; }
502                                         continue;
503                                 }
504
505                                 return true;
506                         }
507                 }
508
509                 internal Socket GetConnection(PooledStream PooledStream, object owner, bool async, out IPAddress address, ref Socket abortSocket, ref Socket abortSocket6)
510                 {
511                         throw new NotImplementedException ();
512                 }
513         }
514 }
515
516