Add [Category ("NotWorking")] to failing test.
[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;
35 using System.Net.Sockets;
36 using System.Security.Cryptography.X509Certificates;
37 using System.Threading;
38
39 namespace System.Net 
40 {
41         public class ServicePoint
42         {
43                 Uri uri;
44                 int connectionLimit;
45                 int maxIdleTime;
46                 int currentConnections;
47                 DateTime idleSince;
48                 Version protocolVersion;
49                 X509Certificate certificate;
50                 X509Certificate clientCertificate;
51                 IPHostEntry host;
52                 bool usesProxy;
53                 Hashtable groups;
54                 bool sendContinue = true;
55                 bool useConnect;
56                 object locker = new object ();
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                 
64                 // Constructors
65
66                 internal ServicePoint (Uri uri, int connectionLimit, int maxIdleTime)
67                 {
68                         this.uri = uri;  
69                         this.connectionLimit = connectionLimit;
70                         this.maxIdleTime = maxIdleTime;                 
71                         this.currentConnections = 0;
72                         this.idleSince = DateTime.UtcNow;
73                 }
74                 
75                 // Properties
76                 
77                 public Uri Address {
78                         get { return uri; }
79                 }
80
81                 static Exception GetMustImplement ()
82                 {
83                         return new NotImplementedException ();
84                 }
85
86                 public BindIPEndPoint BindIPEndPointDelegate
87                 {
88                         get { return endPointCallback; }
89                         set { endPointCallback = value; }
90                 }
91                 
92                 public X509Certificate Certificate {
93                         get { return certificate; }
94                 }
95                 
96                 public X509Certificate ClientCertificate {
97                         get { return clientCertificate; }
98                 }
99
100                 [MonoTODO]
101                 public int ConnectionLeaseTimeout
102                 {
103                         get {
104                                 throw GetMustImplement ();
105                         }
106                         set {
107                                 throw GetMustImplement ();
108                         }
109                 }
110                 
111                 public int ConnectionLimit {
112                         get { return connectionLimit; }
113                         set {
114                                 if (value <= 0)
115                                         throw new ArgumentOutOfRangeException ();
116
117                                 connectionLimit = value;
118                         }
119                 }
120                 
121                 public string ConnectionName {
122                         get { return uri.Scheme; }
123                 }
124
125                 public int CurrentConnections {
126                         get {
127                                 return currentConnections;
128                         }
129                 }
130
131                 public DateTime IdleSince {
132                         get {
133                                 return idleSince.ToLocalTime ();
134                         }
135                         internal set {
136                                 lock (locker)
137                                         idleSince = value.ToUniversalTime ();
138                         }
139                 }
140                 
141                 public int MaxIdleTime {
142                         get { return maxIdleTime; }
143                         set { 
144                                 if (value < Timeout.Infinite || value > Int32.MaxValue)
145                                         throw new ArgumentOutOfRangeException ();
146                                 this.maxIdleTime = value; 
147                         }
148                 }
149                 
150                 public virtual Version ProtocolVersion {
151                         get { return protocolVersion; }
152                 }
153
154                 [MonoTODO]
155                 public int ReceiveBufferSize
156                 {
157                         get {
158                                 throw GetMustImplement ();
159                         }
160                         set {
161                                 throw GetMustImplement ();
162                         }
163                 }
164                 
165                 public bool SupportsPipelining {
166                         get { return HttpVersion.Version11.Equals (protocolVersion); }
167                 }
168
169
170                 public bool Expect100Continue {
171                         get { return SendContinue; }
172                         set { SendContinue = value; }
173                 }
174
175                 public bool UseNagleAlgorithm {
176                         get { return useNagle; }
177                         set { useNagle = value; }
178                 }
179
180                 internal bool SendContinue {
181                         get { return sendContinue &&
182                                      (protocolVersion == null || protocolVersion == HttpVersion.Version11); }
183                         set { sendContinue = value; }
184                 }
185                 // Methods
186
187                 public void SetTcpKeepAlive (bool enabled, int keepAliveTime, int keepAliveInterval)
188                 {
189                         if (enabled) {
190                                 if (keepAliveTime <= 0)
191                                         throw new ArgumentOutOfRangeException ("keepAliveTime", "Must be greater than 0");
192                                 if (keepAliveInterval <= 0)
193                                         throw new ArgumentOutOfRangeException ("keepAliveInterval", "Must be greater than 0");
194                         }
195
196                         tcp_keepalive = enabled;
197                         tcp_keepalive_time = keepAliveTime;
198                         tcp_keepalive_interval = keepAliveInterval;
199                 }
200
201                 internal void KeepAliveSetup (Socket socket)
202                 {
203                         if (!tcp_keepalive)
204                                 return;
205
206                         byte [] bytes = new byte [12];
207                         PutBytes (bytes, (uint) (tcp_keepalive ? 1 : 0), 0);
208                         PutBytes (bytes, (uint) tcp_keepalive_time, 4);
209                         PutBytes (bytes, (uint) tcp_keepalive_interval, 8);
210                         socket.IOControl (IOControlCode.KeepAliveValues, bytes, null);
211                 }
212
213                 static void PutBytes (byte [] bytes, uint v, int offset)
214                 {
215                         if (BitConverter.IsLittleEndian) {
216                                 bytes [offset] = (byte) (v & 0x000000ff);
217                                 bytes [offset + 1] = (byte) ((v & 0x0000ff00) >> 8);
218                                 bytes [offset + 2] = (byte) ((v & 0x00ff0000) >> 16);
219                                 bytes [offset + 3] = (byte) ((v & 0xff000000) >> 24);
220                         } else {
221                                 bytes [offset + 3] = (byte) (v & 0x000000ff);
222                                 bytes [offset + 2] = (byte) ((v & 0x0000ff00) >> 8);
223                                 bytes [offset + 1] = (byte) ((v & 0x00ff0000) >> 16);
224                                 bytes [offset] = (byte) ((v & 0xff000000) >> 24);
225                         }
226                 }
227
228                 // Internal Methods
229
230                 internal bool UsesProxy {
231                         get { return usesProxy; }
232                         set { usesProxy = value; }
233                 }
234
235                 internal bool UseConnect {
236                         get { return useConnect; }
237                         set { useConnect = value; }
238                 }
239
240                 internal bool AvailableForRecycling {
241                         get { 
242                                 return CurrentConnections == 0
243                                     && maxIdleTime != Timeout.Infinite
244                                     && DateTime.UtcNow >= IdleSince.AddMilliseconds (maxIdleTime);
245                         }
246                 }
247
248                 internal Hashtable Groups {
249                         get {
250                                 if (groups == null)
251                                         groups = new Hashtable ();
252
253                                 return groups;
254                         }
255                 }
256
257                 internal IPHostEntry HostEntry
258                 {
259                         get {
260                                 lock (hostE) {
261                                         if (host != null)
262                                                 return host;
263
264                                         string uriHost = uri.Host;
265
266                                         // There is no need to do DNS resolution on literal IP addresses
267                                         if (uri.HostNameType == UriHostNameType.IPv6 ||
268                                                 uri.HostNameType == UriHostNameType.IPv4) {
269
270                                                 if (uri.HostNameType == UriHostNameType.IPv6) {
271                                                         // Remove square brackets
272                                                         uriHost = uriHost.Substring(1,uriHost.Length-2);
273                                                 }
274
275                                                 // Creates IPHostEntry
276                                                 host = new IPHostEntry();
277                                                 host.AddressList = new IPAddress[] { IPAddress.Parse(uriHost) };
278
279                                                 return host;
280                                         }
281
282                                         // Try DNS resolution on host names
283                                         try  {
284                                                 host = Dns.GetHostByName (uriHost);
285                                         } 
286                                         catch {
287                                                 return null;
288                                         }
289                                 }
290
291                                 return host;
292                         }
293                 }
294
295                 internal void SetVersion (Version version)
296                 {
297                         protocolVersion = version;
298                 }
299
300 #if !TARGET_JVM
301                 WebConnectionGroup GetConnectionGroup (string name)
302                 {
303                         if (name == null)
304                                 name = "";
305
306                         WebConnectionGroup group = Groups [name] as WebConnectionGroup;
307                         if (group != null)
308                                 return group;
309
310                         group = new WebConnectionGroup (this, name);
311                         Groups [name] = group;
312                         return group;
313                 }
314
315                 internal EventHandler SendRequest (HttpWebRequest request, string groupName)
316                 {
317                         WebConnection cnc;
318                         
319                         lock (locker) {
320                                 WebConnectionGroup cncGroup = GetConnectionGroup (groupName);
321                                 cnc = cncGroup.GetConnection (request);
322                         }
323                         
324                         return cnc.SendRequest (request);
325                 }
326 #endif
327                 public bool CloseConnectionGroup (string connectionGroupName)
328                 {
329                         lock (locker) {
330                                 WebConnectionGroup cncGroup = GetConnectionGroup (connectionGroupName);
331                                 if (cncGroup != null) {
332                                         cncGroup.Close ();
333                                         return true;
334                                 }
335                         }
336
337                         return false;
338                 }
339
340                 internal void IncrementConnection ()
341                 {
342                         lock (locker) {
343                                 currentConnections++;
344                                 idleSince = DateTime.UtcNow.AddMilliseconds (1000000);
345                         }
346                 }
347
348                 internal void DecrementConnection ()
349                 {
350                         lock (locker) {
351                                 currentConnections--;
352                                 if (currentConnections == 0)
353                                         idleSince = DateTime.UtcNow;
354                         }
355                 }
356
357                 internal void SetCertificates (X509Certificate client, X509Certificate server) 
358                 {
359                         certificate = server;
360                         clientCertificate = client;
361                 }
362
363                 internal bool CallEndPointDelegate (Socket sock, IPEndPoint remote)
364                 {
365                         if (endPointCallback == null)
366                                 return true;
367
368                         int count = 0;
369                         for (;;) {
370                                 IPEndPoint local = null;
371                                 try {
372                                         local = endPointCallback (this,
373                                                 remote, count);
374                                 } catch {
375                                         // This is to differentiate from an
376                                         // OverflowException, which should propagate.
377                                         return false;
378                                 }
379
380                                 if (local == null)
381                                         return true;
382
383                                 try {
384                                         sock.Bind (local);
385                                 } catch (SocketException) {
386                                         // This is intentional; the docs say
387                                         // that if the Bind fails, we keep
388                                         // going until there is an
389                                         // OverflowException on the retry
390                                         // count.
391                                         checked { ++count; }
392                                         continue;
393                                 }
394
395                                 return true;
396                         }
397                 }
398         }
399 }
400
401