2 // System.Net.ServicePoint
5 // Lawrence Pit (loz@cable.a2000.nl)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 // (c) 2002 Lawrence Pit
9 // (c) 2003 Ximian, Inc. (http://www.ximian.com)
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:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
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.
34 using System.Collections;
35 using System.Net.Sockets;
36 using System.Security.Cryptography.X509Certificates;
37 using System.Threading;
41 public class ServicePoint
46 int currentConnections;
48 Version protocolVersion;
49 X509Certificate certificate;
50 X509Certificate clientCertificate;
54 bool sendContinue = true;
56 object locker = new object ();
57 object hostE = new object ();
59 BindIPEndPoint endPointCallback = null;
61 int tcp_keepalive_time;
62 int tcp_keepalive_interval;
66 internal ServicePoint (Uri uri, int connectionLimit, int maxIdleTime)
69 this.connectionLimit = connectionLimit;
70 this.maxIdleTime = maxIdleTime;
71 this.currentConnections = 0;
72 this.idleSince = DateTime.UtcNow;
81 static Exception GetMustImplement ()
83 return new NotImplementedException ();
86 public BindIPEndPoint BindIPEndPointDelegate
88 get { return endPointCallback; }
89 set { endPointCallback = value; }
92 public X509Certificate Certificate {
93 get { return certificate; }
96 public X509Certificate ClientCertificate {
97 get { return clientCertificate; }
101 public int ConnectionLeaseTimeout
104 throw GetMustImplement ();
107 throw GetMustImplement ();
111 public int ConnectionLimit {
112 get { return connectionLimit; }
115 throw new ArgumentOutOfRangeException ();
117 connectionLimit = value;
121 public string ConnectionName {
122 get { return uri.Scheme; }
125 public int CurrentConnections {
127 return currentConnections;
131 public DateTime IdleSince {
133 return idleSince.ToLocalTime ();
137 idleSince = value.ToUniversalTime ();
141 public int MaxIdleTime {
142 get { return maxIdleTime; }
144 if (value < Timeout.Infinite || value > Int32.MaxValue)
145 throw new ArgumentOutOfRangeException ();
146 this.maxIdleTime = value;
150 public virtual Version ProtocolVersion {
151 get { return protocolVersion; }
155 public int ReceiveBufferSize
158 throw GetMustImplement ();
161 throw GetMustImplement ();
165 public bool SupportsPipelining {
166 get { return HttpVersion.Version11.Equals (protocolVersion); }
170 public bool Expect100Continue {
171 get { return SendContinue; }
172 set { SendContinue = value; }
175 public bool UseNagleAlgorithm {
176 get { return useNagle; }
177 set { useNagle = value; }
180 internal bool SendContinue {
181 get { return sendContinue &&
182 (protocolVersion == null || protocolVersion == HttpVersion.Version11); }
183 set { sendContinue = value; }
187 public void SetTcpKeepAlive (bool enabled, int keepAliveTime, int keepAliveInterval)
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");
196 tcp_keepalive = enabled;
197 tcp_keepalive_time = keepAliveTime;
198 tcp_keepalive_interval = keepAliveInterval;
201 internal void KeepAliveSetup (Socket socket)
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);
213 static void PutBytes (byte [] bytes, uint v, int offset)
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);
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);
230 internal bool UsesProxy {
231 get { return usesProxy; }
232 set { usesProxy = value; }
235 internal bool UseConnect {
236 get { return useConnect; }
237 set { useConnect = value; }
240 internal bool AvailableForRecycling {
242 return CurrentConnections == 0
243 && maxIdleTime != Timeout.Infinite
244 && DateTime.UtcNow >= IdleSince.AddMilliseconds (maxIdleTime);
248 internal Hashtable Groups {
251 groups = new Hashtable ();
257 internal IPHostEntry HostEntry
264 string uriHost = uri.Host;
266 // There is no need to do DNS resolution on literal IP addresses
267 if (uri.HostNameType == UriHostNameType.IPv6 ||
268 uri.HostNameType == UriHostNameType.IPv4) {
270 if (uri.HostNameType == UriHostNameType.IPv6) {
271 // Remove square brackets
272 uriHost = uriHost.Substring(1,uriHost.Length-2);
275 // Creates IPHostEntry
276 host = new IPHostEntry();
277 host.AddressList = new IPAddress[] { IPAddress.Parse(uriHost) };
282 // Try DNS resolution on host names
284 host = Dns.GetHostByName (uriHost);
295 internal void SetVersion (Version version)
297 protocolVersion = version;
301 WebConnectionGroup GetConnectionGroup (string name)
306 WebConnectionGroup group = Groups [name] as WebConnectionGroup;
310 group = new WebConnectionGroup (this, name);
311 Groups [name] = group;
315 internal EventHandler SendRequest (HttpWebRequest request, string groupName)
320 WebConnectionGroup cncGroup = GetConnectionGroup (groupName);
321 cnc = cncGroup.GetConnection (request);
324 return cnc.SendRequest (request);
327 public bool CloseConnectionGroup (string connectionGroupName)
330 WebConnectionGroup cncGroup = GetConnectionGroup (connectionGroupName);
331 if (cncGroup != null) {
340 internal void IncrementConnection ()
343 currentConnections++;
344 idleSince = DateTime.UtcNow.AddMilliseconds (1000000);
348 internal void DecrementConnection ()
351 currentConnections--;
352 if (currentConnections == 0)
353 idleSince = DateTime.UtcNow;
357 internal void SetCertificates (X509Certificate client, X509Certificate server)
359 certificate = server;
360 clientCertificate = client;
363 internal bool CallEndPointDelegate (Socket sock, IPEndPoint remote)
365 if (endPointCallback == null)
370 IPEndPoint local = null;
372 local = endPointCallback (this,
375 // This is to differentiate from an
376 // OverflowException, which should propagate.
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