[corlib] ParseNumber.StringToInt now check for overflows.
[mono.git] / mcs / class / System / System.Net / WebConnectionGroup.cs
1 //
2 // System.Net.WebConnectionGroup
3 //
4 // Authors:
5 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //      Martin Baulig (martin.baulig@xamarin.com)
7 //
8 // (C) 2003 Ximian, Inc (http://www.ximian.com)
9 // Copyright 2011-2014 Xamarin, Inc (http://www.xamarin.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.Threading;
35 using System.Collections;
36 using System.Collections.Generic;
37 using System.Net.Configuration;
38 using System.Net.Sockets;
39 using System.Diagnostics;
40
41 namespace System.Net
42 {
43         class WebConnectionGroup
44         {
45                 ServicePoint sPoint;
46                 string name;
47                 LinkedList<ConnectionState> connections;
48                 Queue queue;
49                 bool closing;
50
51                 public WebConnectionGroup (ServicePoint sPoint, string name)
52                 {
53                         this.sPoint = sPoint;
54                         this.name = name;
55                         connections = new LinkedList<ConnectionState> ();
56                         queue = new Queue ();
57                 }
58
59                 public event EventHandler ConnectionClosed;
60
61                 void OnConnectionClosed ()
62                 {
63                         if (ConnectionClosed != null)
64                                 ConnectionClosed (this, null);
65                 }
66
67                 public void Close ()
68                 {
69                         //TODO: what do we do with the queue? Empty it out and abort the requests?
70                         //TODO: abort requests or wait for them to finish
71                         lock (sPoint) {
72                                 closing = true;
73                                 var iter = connections.First;
74                                 while (iter != null) {
75                                         var cnc = iter.Value.Connection;
76                                         var node = iter;
77                                         iter = iter.Next;
78
79                                         connections.Remove (node);
80                                         cnc.Close (false);
81                                         OnConnectionClosed ();
82                                 }
83                         }
84                 }
85
86                 public WebConnection GetConnection (HttpWebRequest request, out bool created)
87                 {
88                         lock (sPoint) {
89                                 return CreateOrReuseConnection (request, out created);
90                         }
91                 }
92
93                 static void PrepareSharingNtlm (WebConnection cnc, HttpWebRequest request)
94                 {
95                         if (!cnc.NtlmAuthenticated)
96                                 return;
97
98                         bool needs_reset = false;
99                         NetworkCredential cnc_cred = cnc.NtlmCredential;
100
101                         bool isProxy = (request.Proxy != null && !request.Proxy.IsBypassed (request.RequestUri));
102                         ICredentials req_icreds = (!isProxy) ? request.Credentials : request.Proxy.Credentials;
103                         NetworkCredential req_cred = (req_icreds != null) ? req_icreds.GetCredential (request.RequestUri, "NTLM") : null;
104
105                         if (cnc_cred == null || req_cred == null ||
106                                 cnc_cred.Domain != req_cred.Domain || cnc_cred.UserName != req_cred.UserName ||
107                                 cnc_cred.Password != req_cred.Password) {
108                                 needs_reset = true;
109                         }
110
111                         if (!needs_reset) {
112                                 bool req_sharing = request.UnsafeAuthenticatedConnectionSharing;
113                                 bool cnc_sharing = cnc.UnsafeAuthenticatedConnectionSharing;
114                                 needs_reset = (req_sharing == false || req_sharing != cnc_sharing);
115                         }
116                         if (needs_reset) {
117                                 cnc.Close (false); // closes the authenticated connection
118                                 cnc.ResetNtlm ();
119                         }
120                 }
121
122                 ConnectionState FindIdleConnection ()
123                 {
124                         foreach (var cnc in connections) {
125                                 if (cnc.Busy)
126                                         continue;
127
128                                 connections.Remove (cnc);
129                                 connections.AddFirst (cnc);
130                                 return cnc;
131                         }
132
133                         return null;
134                 }
135
136                 WebConnection CreateOrReuseConnection (HttpWebRequest request, out bool created)
137                 {
138                         var cnc = FindIdleConnection ();
139                         if (cnc != null) {
140                                 created = false;
141                                 PrepareSharingNtlm (cnc.Connection, request);
142                                 return cnc.Connection;
143                         }
144
145                         if (sPoint.ConnectionLimit > connections.Count || connections.Count == 0) {
146                                 created = true;
147                                 cnc = new ConnectionState (this);
148                                 connections.AddFirst (cnc);
149                                 return cnc.Connection;
150                         }
151
152                         created = false;
153                         cnc = connections.Last.Value;
154                         connections.Remove (cnc);
155                         connections.AddFirst (cnc);
156                         return cnc.Connection;
157                 }
158
159                 public string Name {
160                         get { return name; }
161                 }
162
163                 internal Queue Queue {
164                         get { return queue; }
165                 }
166
167                 internal bool TryRecycle (TimeSpan maxIdleTime, ref DateTime idleSince)
168                 {
169                         var now = DateTime.UtcNow;
170
171                 again:
172                         bool recycled;
173                         List<WebConnection> connectionsToClose = null;
174
175                         lock (sPoint) {
176                                 if (closing) {
177                                         idleSince = DateTime.MinValue;
178                                         return true;
179                                 }
180
181                                 int count = 0;
182                                 var iter = connections.First;
183                                 while (iter != null) {
184                                         var cnc = iter.Value;
185                                         var node = iter;
186                                         iter = iter.Next;
187
188                                         ++count;
189                                         if (cnc.Busy)
190                                                 continue;
191
192                                         if (count <= sPoint.ConnectionLimit && now - cnc.IdleSince < maxIdleTime) {
193                                                 if (cnc.IdleSince > idleSince)
194                                                         idleSince = cnc.IdleSince;
195                                                 continue;
196                                         }
197
198                                         /*
199                                          * Do not call WebConnection.Close() while holding the ServicePoint lock
200                                          * as this could deadlock when attempting to take the WebConnection lock.
201                                          * 
202                                          */
203
204                                         if (connectionsToClose == null)
205                                                 connectionsToClose = new List<WebConnection> ();
206                                         connectionsToClose.Add (cnc.Connection);
207                                         connections.Remove (node);
208                                 }
209
210                                 recycled = connections.Count == 0;
211                         }
212
213                         // Did we find anything that can be closed?
214                         if (connectionsToClose == null)
215                                 return recycled;
216
217                         // Ok, let's get rid of these!
218                         foreach (var cnc in connectionsToClose)
219                                 cnc.Close (false);
220
221                         // Re-take the lock, then remove them from the connection list.
222                         goto again;
223                 }
224
225                 class ConnectionState : IWebConnectionState {
226                         public WebConnection Connection {
227                                 get;
228                                 private set;
229                         }
230
231                         public WebConnectionGroup Group {
232                                 get;
233                                 private set;
234                         }
235
236                         public ServicePoint ServicePoint {
237                                 get { return Group.sPoint; }
238                         }
239
240                         bool busy;
241                         DateTime idleSince;
242
243                         public bool Busy {
244                                 get { return busy; }
245                         }
246
247                         public DateTime IdleSince {
248                                 get { return idleSince; }
249                         }
250
251                         public bool TrySetBusy ()
252                         {
253                                 lock (ServicePoint) {
254                                         if (busy)
255                                                 return false;
256                                         busy = true;
257                                         idleSince = DateTime.UtcNow + TimeSpan.FromDays (3650);
258                                         return true;
259                                 }
260                         }
261
262                         public void SetIdle ()
263                         {
264                                 lock (ServicePoint) {
265                                         busy = false;
266                                         idleSince = DateTime.UtcNow;
267                                 }
268                         }
269
270                         public ConnectionState (WebConnectionGroup group)
271                         {
272                                 Group = group;
273                                 idleSince = DateTime.UtcNow;
274                                 Connection = new WebConnection (this, group.sPoint);
275                         }
276                 }
277                 
278         }
279 }
280