1 //------------------------------------------------------------------------------
2 // <copyright file="ServicePoint.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
8 using System.Net.Sockets;
9 using System.Collections;
11 using System.Threading;
12 using System.Security.Authentication.ExtendedProtection;
13 using System.Security.Cryptography.X509Certificates;
14 using System.Net.Security;
15 using System.Globalization;
16 using System.Collections.Generic;
17 using System.Runtime.CompilerServices;
19 public delegate IPEndPoint BindIPEndPoint(ServicePoint servicePoint, IPEndPoint remoteEndPoint, int retryCount);
22 // ServicePoints are never created directly but always handed out by the
23 // ServicePointManager. The ServicePointManager and the ServicePoints must be in
24 // the same name space so that the ServicePointManager can call the
25 // internal constructor
28 /// <para>Provides connection management for other classes.</para>
31 public class ServicePoint {
33 internal const int LoopbackConnectionLimit = Int32.MaxValue;
35 private int m_ConnectionLeaseTimeout;
36 private TimerThread.Queue m_ConnectionLeaseTimerQueue;
37 private bool m_ProxyServicePoint;
38 private bool m_UserChangedLimit;
39 private bool m_UseNagleAlgorithm;
40 private TriState m_HostLoopbackGuess;
41 private int m_ReceiveBufferSize;
42 private bool m_Expect100Continue;
43 private bool m_Understands100Continue;
44 private HttpBehaviour m_HttpBehaviour;
45 private string m_LookupString;
46 private int m_ConnectionLimit;
47 private Hashtable m_ConnectionGroupList;
48 private Uri m_Address;
49 private string m_Host;
51 private TimerThread.Queue m_IdlingQueue;
52 private TimerThread.Timer m_ExpiringTimer;
53 private DateTime m_IdleSince;
54 private string m_ConnectionName;
55 private int m_CurrentConnections;
56 private bool m_HostMode;
57 private BindIPEndPoint m_BindIPEndPointDelegate = null;
58 private object m_CachedChannelBinding;
60 private static readonly AsyncCallback m_ConnectCallbackDelegate = new AsyncCallback(ConnectSocketCallback);
62 private readonly TimerThread.Callback m_IdleConnectionGroupTimeoutDelegate;
65 private object m_ServerCertificateOrBytes;
66 private object m_ClientCertificateOrBytes;
67 #endif // !FEATURE_PAL
69 private bool m_UseTcpKeepAlive = false;
70 private int m_TcpKeepAliveTime;
71 private int m_TcpKeepAliveInterval;
73 internal string LookupString {
75 return m_LookupString;
79 internal string Hostname {
85 internal bool IsTrustedHost {
87 return m_IsTrustedHost;
91 public BindIPEndPoint BindIPEndPointDelegate {
93 return m_BindIPEndPointDelegate;
97 ExceptionHelper.InfrastructurePermission.Demand();
99 m_BindIPEndPointDelegate = value;
106 internal ServicePoint(Uri address, TimerThread.Queue defaultIdlingQueue, int defaultConnectionLimit, string lookupString, bool userChangedLimit, bool proxyServicePoint) {
107 GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::.ctor(" + lookupString+")");
108 if (Logging.On) Logging.Enter(Logging.Web, this, "ServicePoint", address.DnsSafeHost + ":" + address.Port);
110 m_ProxyServicePoint = proxyServicePoint;
112 m_ConnectionName = address.Scheme;
113 m_Host = address.DnsSafeHost;
114 m_Port = address.Port;
115 m_IdlingQueue = defaultIdlingQueue;
116 m_ConnectionLimit = defaultConnectionLimit;
117 m_HostLoopbackGuess = TriState.Unspecified;
118 m_LookupString = lookupString;
119 m_UserChangedLimit = userChangedLimit;
120 m_UseNagleAlgorithm = ServicePointManager.UseNagleAlgorithm;
121 m_Expect100Continue = ServicePointManager.Expect100Continue;
122 m_ConnectionGroupList = new Hashtable(10);
123 m_ConnectionLeaseTimeout = System.Threading.Timeout.Infinite;
124 m_ReceiveBufferSize = -1;
125 m_UseTcpKeepAlive = ServicePointManager.s_UseTcpKeepAlive;
126 m_TcpKeepAliveTime = ServicePointManager.s_TcpKeepAliveTime;
127 m_TcpKeepAliveInterval = ServicePointManager.s_TcpKeepAliveInterval;
129 // it would be safer to make sure the server is 1.1
130 // but assume it is at the beginning, and update it later
131 m_Understands100Continue = true;
132 m_HttpBehaviour = HttpBehaviour.Unknown;
134 // upon creation, the service point should be idle, by default
135 m_IdleSince = DateTime.Now;
136 m_ExpiringTimer = m_IdlingQueue.CreateTimer(ServicePointManager.IdleServicePointTimeoutDelegate, this);
137 m_IdleConnectionGroupTimeoutDelegate = new TimerThread.Callback(IdleConnectionGroupTimeoutCallback);
142 internal ServicePoint(string host, int port, TimerThread.Queue defaultIdlingQueue, int defaultConnectionLimit, string lookupString, bool userChangedLimit, bool proxyServicePoint) {
143 GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::.ctor(" + lookupString+")");
144 if (Logging.On) Logging.Enter(Logging.Web, this, "ServicePoint", host + ":" + port);
146 m_ProxyServicePoint = proxyServicePoint;
147 m_ConnectionName = "ByHost:"+host+":"+port.ToString(CultureInfo.InvariantCulture);
148 m_IdlingQueue = defaultIdlingQueue;
149 m_ConnectionLimit = defaultConnectionLimit;
150 m_HostLoopbackGuess = TriState.Unspecified;
151 m_LookupString = lookupString;
152 m_UserChangedLimit = userChangedLimit;
153 m_ConnectionGroupList = new Hashtable(10);
154 m_ConnectionLeaseTimeout = System.Threading.Timeout.Infinite;
155 m_ReceiveBufferSize = -1;
160 // upon creation, the service point should be idle, by default
161 m_IdleSince = DateTime.Now;
162 m_ExpiringTimer = m_IdlingQueue.CreateTimer(ServicePointManager.IdleServicePointTimeoutDelegate, this);
163 m_IdleConnectionGroupTimeoutDelegate = new TimerThread.Callback(IdleConnectionGroupTimeoutCallback);
170 internal object CachedChannelBinding
172 get { return m_CachedChannelBinding; }
175 internal void SetCachedChannelBinding(Uri uri, ChannelBinding binding)
177 if (uri.Scheme == Uri.UriSchemeHttps)
179 m_CachedChannelBinding = (binding != null ? (object)binding : (object)DBNull.Value);
185 FindConnectionGroup -
187 Searches for the a Group object that actually holds the connections
188 that we want to peak at.
192 request - Request that's being submitted.
193 connName - Connection Name if needed
200 private ConnectionGroup FindConnectionGroup(string connName, bool dontCreate) {
201 string lookupStr = ConnectionGroup.MakeQueryStr(connName);
203 GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::FindConnectionGroup() lookupStr:[" + ValidationHelper.ToString(connName) + "]");
205 ConnectionGroup entry = m_ConnectionGroupList[lookupStr] as ConnectionGroup;
207 if (entry==null && !dontCreate) {
208 entry = new ConnectionGroup(this, connName);
209 GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::FindConnectionGroup() adding ConnectionGroup lookupStr:[" + lookupStr + "]");
211 m_ConnectionGroupList[lookupStr] = entry;
214 GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::FindConnectionGroup() using existing ConnectionGroup");
216 GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::FindConnectionGroup() returning ConnectionGroup:" + ValidationHelper.ToString(entry) + (entry!=null ? " ConnLimit:" + entry.ConnectionLimit.ToString() : ""));
223 /// Tempory for getting a new Connection for FTP client, for the time being
226 internal Socket GetConnection(PooledStream PooledStream, object owner, bool async, out IPAddress address, ref Socket abortSocket, ref Socket abortSocket6)
228 Socket socket = null;
229 Socket socket6 = null;
230 Socket finalSocket = null;
231 Exception innerException = null;
232 WebExceptionStatus ws = WebExceptionStatus.ConnectFailure;
236 // if we will not create a tunnel through a proxy then create
237 // and connect the socket we will use for the connection
241 // IPv6 Support: If IPv6 is enabled, then we create a second socket that ServicePoint
242 // will use if it wants to connect via IPv6.
244 if ( Socket.OSSupportsIPv4 ) {
245 socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
248 if ( Socket.OSSupportsIPv6 ) {
249 socket6 = new Socket(AddressFamily.InterNetworkV6,SocketType.Stream,ProtocolType.Tcp);
252 abortSocket = socket;
253 abortSocket6 = socket6;
256 // Setup socket timeouts for [....] requests
260 ConnectSocketState state = null;
263 state = new ConnectSocketState(this, PooledStream, owner, socket, socket6);
266 ws = ConnectSocket(socket, socket6, ref finalSocket, ref address, state, out innerException);
268 if (ws == WebExceptionStatus.Pending) {
272 if (ws != WebExceptionStatus.Success) {
273 throw new WebException(
274 NetRes.GetWebStatusString(ws),
275 ws == WebExceptionStatus.ProxyNameResolutionFailure || ws == WebExceptionStatus.NameResolutionFailure ? Host : null,
278 null, /* no response */
279 WebExceptionInternalStatus.ServicePointFatal);
283 // There should be no means for socket to be null at this
284 // point, but the damage is greater if we just carry on
285 // without ensuring that it's good.
287 if ( finalSocket == null ) {
288 throw new IOException(SR.GetString(SR.net_io_transportfailure));
291 CompleteGetConnection(socket, socket6, finalSocket, address);
297 /// Complete the GetConnection(...) call, the function was divided for async completion
300 private void CompleteGetConnection(Socket socket, Socket socket6, Socket finalSocket, IPAddress address) {
302 // Decide which socket to retain
304 if ( finalSocket.AddressFamily == AddressFamily.InterNetwork ) {
305 if ( socket6 != null ) {
311 if (socket != null) {
317 // make this configurable from the user:
318 if (!UseNagleAlgorithm) {
319 finalSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1);
321 if (ReceiveBufferSize != -1) {
322 finalSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, ReceiveBufferSize);
324 if (m_UseTcpKeepAlive) {
326 // Marshal a byte array containing the params for WsaIoctl
327 // struct tcp_keepalive {
329 // u_long keepalivetime;
330 // u_long keepaliveinterval;
332 byte[] input = new byte[sizeof(int)*3];
334 input[4] = (byte) (m_TcpKeepAliveTime & 0xff);
335 input[5] = (byte) ((m_TcpKeepAliveTime >> 8) & 0xff);
336 input[6] = (byte) ((m_TcpKeepAliveTime >> 16) & 0xff);
337 input[7] = (byte) ((m_TcpKeepAliveTime >> 24) & 0xff);
338 input[8] = (byte) (m_TcpKeepAliveInterval & 0xff);
339 input[9] = (byte) ((m_TcpKeepAliveInterval >> 8) & 0xff);
340 input[10] = (byte) ((m_TcpKeepAliveInterval >> 16) & 0xff);
341 input[11] = (byte) ((m_TcpKeepAliveInterval >> 24) & 0xff);
344 finalSocket.IOControl(
345 IOControlCode.KeepAliveValues,
351 //return CreateConnection(NetworkStream stream, IPAddress address);
352 //return new NetworkStream(finalSocket, true);
358 SubmitRequest - Submit a request for sending.
360 The service point submit handler. This is called when a request needs
361 to be submitted to the network. This routine is asynchronous; the caller
362 passes in an HttpSubmitDelegate that is invoked when the caller
363 can use the underlying network. The delegate is invoked with the
364 stream that it can write to.
367 In this version, we use HttpWebRequest. In the future we use IRequest
370 Request - Request that's being submitted.
371 SubmitDelegate - Delegate to be invoked.
378 internal virtual void SubmitRequest(HttpWebRequest request) {
379 SubmitRequest(request, null);
382 // userReqeustThread says whether we can post IO from this thread or not.
383 internal void SubmitRequest(HttpWebRequest request, string connName)
386 // We attempt to locate a free connection sitting on our list
387 // avoiding multiple loops of the same the list.
388 // We do this, by enumerating the list of the connections,
389 // looking for Free items, and the least busy item
391 Connection connToUse;
392 ConnectionGroup connGroup;
393 bool forcedsubmit = false;
395 GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::SubmitRequest() Finding ConnectionGroup:[" + connName + "]");
396 connGroup = FindConnectionGroup(connName, false);
397 GlobalLog.Assert(connGroup != null, "ServicePoint#{0}::SubmitRequest()|connGroup == null", ValidationHelper.HashString(this));
401 connToUse = connGroup.FindConnection(request, connName, out forcedsubmit);
402 // The request could be already aborted
403 if (connToUse == null)
406 GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::SubmitRequest() Using Connection#" + ValidationHelper.HashString(connToUse));
407 // finally sumbit delegate
408 if (connToUse.SubmitRequest(request, forcedsubmit)) {
418 /// Gets and sets timeout for when connections should be recycled.
421 public int ConnectionLeaseTimeout {
423 return m_ConnectionLeaseTimeout;
426 if ( !ValidationHelper.ValidateRange(value, Timeout.Infinite, Int32.MaxValue)) {
427 throw new ArgumentOutOfRangeException("value");
429 if (value != m_ConnectionLeaseTimeout) {
430 m_ConnectionLeaseTimeout = value;
431 m_ConnectionLeaseTimerQueue = null;
437 /// <para>Returns a timer queue that can be used internally to create timers of
438 /// ConnectionLeaseTimeout duration.</para>
440 internal TimerThread.Queue ConnectionLeaseTimerQueue {
442 TimerThread.Queue queue = m_ConnectionLeaseTimerQueue;
444 queue = TimerThread.GetOrCreateQueue(ConnectionLeaseTimeout);
445 m_ConnectionLeaseTimerQueue = queue;
447 return m_ConnectionLeaseTimerQueue;
451 // Only the scheme and hostport, for example http://www.microsoft.com
454 /// Gets the Uniform Resource Identifier of the <see cref='System.Net.ServicePoint'/>.
460 throw new NotSupportedException(SR.GetString(SR.net_servicePointAddressNotSupportedInHostMode));
464 // Don't let low-trust apps discover the proxy information.
465 if (m_ProxyServicePoint)
467 ExceptionHelper.WebPermissionUnrestricted.Demand();
475 internal Uri InternalAddress
479 GlobalLog.Assert(!m_HostMode, "ServicePoint#{0}::InternalAddress|Can't be used in Host Mode.", ValidationHelper.HashString(this));
484 internal string Host {
489 return m_Address.Host;
501 // Gets or sets the maximum idle time allowed for connections of this ServicePoint and then for ServicePoint itself
502 // Default value coming in ctor is ServicePointManager.s_MaxServicePointIdleTime which 100 sec
504 public int MaxIdleTime {
506 return m_IdlingQueue.Duration;
509 if ( !ValidationHelper.ValidateRange(value, Timeout.Infinite, Int32.MaxValue)) {
510 throw new ArgumentOutOfRangeException("value");
514 if (value == m_IdlingQueue.Duration)
518 // Make sure we can cancel the existing one. If not, we already idled out.
519 if (m_ExpiringTimer == null || m_ExpiringTimer.Cancel())
521 m_IdlingQueue = TimerThread.GetOrCreateQueue(value);
522 if (m_ExpiringTimer != null)
524 // Need to create a one-off timer for the remaining period.
525 double elapsedDouble = (DateTime.Now - m_IdleSince).TotalMilliseconds;
526 int elapsed = elapsedDouble >= (double) Int32.MaxValue ? Int32.MaxValue : (int) elapsedDouble;
527 int timeLeft = value == Timeout.Infinite ? Timeout.Infinite : elapsed >= value ? 0 : value - elapsed;
528 m_ExpiringTimer = TimerThread.CreateQueue(timeLeft).CreateTimer(ServicePointManager.IdleServicePointTimeoutDelegate, this);
537 /// Gets or sets the Nagling algorithm on the connections that are created to this <see cref='System.Net.ServicePoint'/>.
538 /// Changing this value does not affect existing connections but only to new ones that are created from that moment on.
541 public bool UseNagleAlgorithm {
543 return m_UseNagleAlgorithm;
546 m_UseNagleAlgorithm = value;
552 /// Gets and sets the socket's receive buffer size.
555 public int ReceiveBufferSize {
557 return m_ReceiveBufferSize;
560 if ( !ValidationHelper.ValidateRange(value, -1, Int32.MaxValue)) {
561 throw new ArgumentOutOfRangeException("value");
563 m_ReceiveBufferSize = value;
571 /// Gets or sets indication whether 100-continue behaviour is desired when using this <see cref='System.Net.ServicePoint'/>.
572 /// Changing this value does not affect existing connections but only to new ones that are created from that moment on.
575 public bool Expect100Continue {
577 m_Expect100Continue = value;
580 return m_Expect100Continue;
586 /// Gets the date/time that the <see cref='System.Net.ServicePoint'/> went idle.
589 public DateTime IdleSince {
595 // HTTP Server Version
598 /// The version of the protocol being used on this <see cref='System.Net.ServicePoint'/>.
601 public virtual Version ProtocolVersion {
603 return (m_HttpBehaviour>HttpBehaviour.HTTP10 || m_HttpBehaviour == HttpBehaviour.Unknown) ? HttpVersion.Version11 : HttpVersion.Version10;
607 // Contains set accessor for Version property. Version is a read-only
608 // property at the API
609 internal HttpBehaviour HttpBehaviour {
611 return m_HttpBehaviour;
614 m_HttpBehaviour = value;
616 // if version is greater than HTTP/1.1, and server undesrtood
617 // 100 Continue so far, keep expecting it.
619 m_Understands100Continue = m_Understands100Continue && (m_HttpBehaviour>HttpBehaviour.HTTP10 || m_HttpBehaviour == HttpBehaviour.Unknown);
625 /// Gets the connection name established by the <see cref='System.Net.WebRequest'/> that created the connection.
628 public string ConnectionName {
630 return m_ConnectionName;
636 /// Gets the connection mode in use by the <see cref='System.Net.ServicePoint'/>. One of the <see cref='System.Net.ConnectionModes'/>
639 internal ConnectionModes ConnectionMode {
641 return m_HttpBehaviour>=HttpBehaviour.HTTP11 ? ConnectionModes.Pipeline : ConnectionModes.Persistent;
647 /// Removes the specified Connection group from the ServicePoint, destroys safe and unsafe groups, but not internal.
650 public bool CloseConnectionGroup(string connectionGroupName) {
651 GlobalLog.Enter("ServicePoint#" + ValidationHelper.HashString(this) + "::CloseConnectionGroup() lookupStr:[" + connectionGroupName + "]");
652 if ( ReleaseConnectionGroup(HttpWebRequest.GenerateConnectionGroup(connectionGroupName, false, false).ToString()) ||
653 ReleaseConnectionGroup(HttpWebRequest.GenerateConnectionGroup(connectionGroupName, true, false).ToString()) ||
654 ConnectionPoolManager.RemoveConnectionPool(this, connectionGroupName)) {
656 GlobalLog.Leave("ServicePoint#" + ValidationHelper.HashString(this) + "::CloseConnectionGroup()","true");
659 GlobalLog.Leave("ServicePoint#" + ValidationHelper.HashString(this) + "::CloseConnectionGroup()","false");
663 internal void CloseConnectionGroupInternal(string connectionGroupName) {
665 // Release all internal connection groups (both 'safe' and 'unsafe') with the given name. We're not
666 // interested in the result value (it's OK if it is 'false', i.e. not found).
667 // We don't need to call ConnectionPoolManager.RemoveConnectionPool() since this method is only used for
669 string connectionGroupPrefixSafe = HttpWebRequest.GenerateConnectionGroup(connectionGroupName, false, true).ToString();
670 string connectionGroupPrefixUnsafe = HttpWebRequest.GenerateConnectionGroup(connectionGroupName, true, true).ToString();
671 List<string> connectionGroupNames = null;
674 // Find all connecion groups starting with the provided prefix. We just compare prefixes, since connection
675 // groups may include suffixes for client certificates, SSL over proxy, authentication IDs.
676 foreach (var item in m_ConnectionGroupList.Keys) {
677 string current = item as string;
678 if (current.StartsWith(connectionGroupPrefixSafe, StringComparison.Ordinal) ||
679 current.StartsWith(connectionGroupPrefixUnsafe, StringComparison.Ordinal)) {
680 if (connectionGroupNames == null) {
681 connectionGroupNames = new List<string>();
683 connectionGroupNames.Add(current);
688 // If this service point contains any connection groups with the provided prefix, remove them.
689 if (connectionGroupNames != null) {
690 foreach (string item in connectionGroupNames) {
691 ReleaseConnectionGroup(item);
698 /// Gets or sets the maximum number of connections allowed on this <see cref='System.Net.ServicePoint'/>.
701 public int ConnectionLimit
705 // If there hasn't been a DNS resolution yet, make a guess based on the host name. It might change
706 // when DNS is finally done, but that's ok. It can change anyway based on other factors like redirects.
707 if (!m_UserChangedLimit && m_IPAddressInfoList == null && m_HostLoopbackGuess == TriState.Unspecified)
709 // This can only happen the first time through, and before any ConnectionGroups are made.
712 if (!m_UserChangedLimit && m_IPAddressInfoList == null && m_HostLoopbackGuess == TriState.Unspecified)
714 // First check if it's just an IP address anyway.
715 IPAddress addr = null;
716 if (IPAddress.TryParse(m_Host,out addr))
718 m_HostLoopbackGuess = IsAddressListLoopback(new IPAddress[] { addr }) ? TriState.True : TriState.False;
722 m_HostLoopbackGuess = NclUtilities.GuessWhetherHostIsLoopback(m_Host) ? TriState.True : TriState.False;
728 return m_UserChangedLimit || (m_IPAddressInfoList == null ? m_HostLoopbackGuess != TriState.True : !m_IPAddressesAreLoopback) ? m_ConnectionLimit : LoopbackConnectionLimit;
735 throw new ArgumentOutOfRangeException("value");
738 if (!m_UserChangedLimit || m_ConnectionLimit != value)
742 if (!m_UserChangedLimit || m_ConnectionLimit != value)
744 m_ConnectionLimit = value;
745 m_UserChangedLimit = true;
747 // Don't want to call ResolveConnectionLimit() or ConnectionLimit before setting m_UserChangedLimit
748 // in order to avoid the 'guess' logic in ConnectionLimit.
749 ResolveConnectionLimit();
756 // Must be called under lock.
757 private void ResolveConnectionLimit()
759 int limit = ConnectionLimit;
760 foreach (ConnectionGroup cg in m_ConnectionGroupList.Values)
762 cg.ConnectionLimit = limit;
768 /// Gets the current number of connections associated with this
769 /// <see cref='System.Net.ServicePoint'/>.
772 public int CurrentConnections {
777 foreach (ConnectionGroup group in m_ConnectionGroupList.Values)
779 connections += group.CurrentConnections;
789 /// Gets the certificate received for this <see cref='System.Net.ServicePoint'/>.
792 public X509Certificate Certificate {
794 object chkCert = m_ServerCertificateOrBytes;
795 if (chkCert != null && chkCert.GetType() == typeof(byte[]))
796 return (X509Certificate)(m_ServerCertificateOrBytes = new X509Certificate((byte[]) chkCert));
798 return chkCert as X509Certificate;
801 internal void UpdateServerCertificate(X509Certificate certificate)
803 if (certificate != null)
804 m_ServerCertificateOrBytes = certificate.GetRawCertData();
806 m_ServerCertificateOrBytes = null;
811 /// Gets the Client Certificate sent by us to the Server.
814 public X509Certificate ClientCertificate {
816 object chkCert = m_ClientCertificateOrBytes;
817 if (chkCert != null && chkCert.GetType() == typeof(byte[]))
818 return (X509Certificate)(m_ClientCertificateOrBytes = new X509Certificate((byte[]) chkCert));
820 return chkCert as X509Certificate;
823 internal void UpdateClientCertificate(X509Certificate certificate)
825 if (certificate != null)
826 m_ClientCertificateOrBytes = certificate.GetRawCertData();
828 m_ClientCertificateOrBytes = null;
831 #endif // !FEATURE_PAL
836 /// Indicates that the <see cref='System.Net.ServicePoint'/> supports pipelined connections.
839 public bool SupportsPipelining {
841 return (m_HttpBehaviour>HttpBehaviour.HTTP10 || m_HttpBehaviour==HttpBehaviour.Unknown);
848 // Enable/Disable the use of TCP keepalive option on ServicePoint
849 // connections. This method does not affect existing ServicePoints.
850 // When a ServicePoint is constructed it will inherit the current
855 // enabled - if true enables the use of the TCP keepalive option
856 // for ServicePoint connections.
858 // keepAliveTime - specifies the timeout, in milliseconds, with no
859 // activity until the first keep-alive packet is sent. Ignored if
860 // enabled parameter is false.
862 // keepAliveInterval - specifies the interval, in milliseconds, between
863 // when successive keep-alive packets are sent if no acknowledgement is
864 // received. Ignored if enabled parameter is false.
866 public void SetTcpKeepAlive(
869 int keepAliveInterval) {
872 "ServicePoint::SetTcpKeepAlive()" +
873 " enabled: " + enabled.ToString() +
874 " keepAliveTime: " + keepAliveTime.ToString() +
875 " keepAliveInterval: " + keepAliveInterval.ToString()
878 m_UseTcpKeepAlive = true;
879 if (keepAliveTime <= 0) {
880 throw new ArgumentOutOfRangeException("keepAliveTime");
882 if (keepAliveInterval <= 0) {
883 throw new ArgumentOutOfRangeException("keepAliveInterval");
885 m_TcpKeepAliveTime = keepAliveTime;
886 m_TcpKeepAliveInterval = keepAliveInterval;
888 m_UseTcpKeepAlive = false;
889 m_TcpKeepAliveTime = 0;
890 m_TcpKeepAliveInterval =0;
892 GlobalLog.Leave("ServicePoint::SetTcpKeepAlive()");
896 // Internal Properties
899 internal bool Understands100Continue {
901 m_Understands100Continue = value;
904 return m_Understands100Continue;
909 // InternalProxyServicePoint
911 // Indicates if we are using this service point to represent
912 // a proxy connection, if so we may have to use special
913 // semantics when creating connections
916 internal bool InternalProxyServicePoint {
918 return m_ProxyServicePoint;
923 // IncrementConnection
925 // call to indicate that we now are starting a new
926 // connection within this service point
929 internal void IncrementConnection() {
930 GlobalLog.Enter("ServicePoint#" + ValidationHelper.HashString(this) + "::IncrementConnection()", m_CurrentConnections.ToString());
931 // we need these to be atomic operations
933 m_CurrentConnections++;
934 if (m_CurrentConnections==1) {
935 GlobalLog.Assert(m_ExpiringTimer != null, "ServicePoint#{0}::IncrementConnection|First connection active, but ServicePoint wasn't idle.", ValidationHelper.HashString(this));
942 m_ExpiringTimer.Cancel();
943 m_ExpiringTimer = null;
946 GlobalLog.Leave("ServicePoint#" + ValidationHelper.HashString(this) + "::IncrementConnection()", m_CurrentConnections.ToString());
950 // DecrementConnection
952 // call to indicate that we now are removing
953 // a connection within this connection group
956 internal void DecrementConnection() {
957 // The timer thread is allowed to call this. (It doesn't call user code and doesn't block.)
958 GlobalLog.ThreadContract(ThreadKinds.Unknown, ThreadKinds.SafeSources | ThreadKinds.Timer, "ServicePoint#" + ValidationHelper.HashString(this) + "::DecrementConnection");
959 GlobalLog.Enter("ServicePoint#" + ValidationHelper.HashString(this) + "::DecrementConnection()", m_CurrentConnections.ToString());
961 // we need these to be atomic operations
963 m_CurrentConnections--;
964 if (m_CurrentConnections==0) {
965 GlobalLog.Assert(m_ExpiringTimer == null, "ServicePoint#{0}::DecrementConnection|Expiring timer set on non-idle ServicePoint.", ValidationHelper.HashString(this));
966 m_IdleSince = DateTime.Now;
967 m_ExpiringTimer = m_IdlingQueue.CreateTimer(ServicePointManager.IdleServicePointTimeoutDelegate, this);
969 else if ( m_CurrentConnections < 0 ) {
970 m_CurrentConnections = 0;
971 Diagnostics.Debug.Assert(false, "ServicePoint; Too many decrements.");
974 GlobalLog.Leave("ServicePoint#" + ValidationHelper.HashString(this) + "::DecrementConnection()", m_CurrentConnections.ToString());
978 internal RemoteCertValidationCallback SetupHandshakeDoneProcedure(TlsStream secureStream, Object request) {
979 // Use a private adapter to connect tlsstream and this service point
980 return HandshakeDoneProcedure.CreateAdapter(this, secureStream, request);
983 // This is an adapter class that ties a servicePoint and a TlsStream on the SSL handshake completion
984 private class HandshakeDoneProcedure {
985 TlsStream m_SecureStream;
987 ServicePoint m_ServicePoint;
989 internal static RemoteCertValidationCallback CreateAdapter(ServicePoint serviePoint, TlsStream secureStream, Object request)
991 HandshakeDoneProcedure adapter = new HandshakeDoneProcedure(serviePoint, secureStream, request);
992 return new RemoteCertValidationCallback(adapter.CertValidationCallback);
995 private HandshakeDoneProcedure (ServicePoint serviePoint, TlsStream secureStream, Object request) {
996 m_ServicePoint = serviePoint;
997 m_SecureStream = secureStream;
1001 private bool CertValidationCallback(string hostName, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) {
1002 m_ServicePoint.UpdateServerCertificate(certificate);
1003 m_ServicePoint.UpdateClientCertificate(m_SecureStream.ClientCertificate);
1004 bool useDefault = true;
1006 // Request specific validator takes priority
1007 HttpWebRequest httpWebRequest = m_Request as HttpWebRequest;
1008 if (httpWebRequest != null && httpWebRequest.ServerCertValidationCallback != null)
1010 return httpWebRequest.ServerCertValidationCallback.
1017 // If a policy is set, call the user callback inside the ExecutionContext.
1018 if (ServicePointManager.GetLegacyCertificatePolicy() != null && (m_Request is WebRequest))
1022 bool checkResult = ServicePointManager.CertPolicyValidationCallback.
1026 (WebRequest) m_Request,
1030 if (checkResult == false){
1031 if (!ServicePointManager.CertPolicyValidationCallback.UsesDefault
1032 || ServicePointManager.ServerCertificateValidationCallback == null)
1037 if (ServicePointManager.ServerCertificateValidationCallback != null)
1040 return ServicePointManager.ServerCertValidationCallback.
1048 return sslPolicyErrors == SslPolicyErrors.None;
1056 #endif // !FEATURE_PAL
1058 private void IdleConnectionGroupTimeoutCallback(TimerThread.Timer timer, int timeNoticed, object context) {
1059 ConnectionGroup connectionGroup = (ConnectionGroup)context;
1061 if (Logging.On) Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_closed_idle,
1062 "ConnectionGroup", connectionGroup.GetHashCode()));
1064 ReleaseConnectionGroup(connectionGroup.Name);
1067 internal TimerThread.Timer CreateConnectionGroupTimer(ConnectionGroup connectionGroup) {
1068 return m_IdlingQueue.CreateTimer(m_IdleConnectionGroupTimeoutDelegate, connectionGroup);
1073 /// Sets connections in this group to not be KeepAlive.
1074 /// This is called to force cleanup of the ConnectionGroup by the
1075 /// NTLM and Negotiate authentication modules.
1078 internal bool ReleaseConnectionGroup(string connName) {
1080 ConnectionGroup connectionGroup = null;
1083 // look up the ConnectionGroup based on the name
1087 connectionGroup = FindConnectionGroup(connName, true);
1089 // force all connections on the ConnectionGroup to not be KeepAlive
1091 if (connectionGroup == null) {
1095 // Cancel the timer so it doesn't fire later and clean up a different
1096 // connection group with the same name.
1097 connectionGroup.CancelIdleTimer();
1100 // remove ConnectionGroup from our Hashtable
1102 m_ConnectionGroupList.Remove(connName);
1105 // Don't call the following under the lock: ConnectionGroup will call into Connection that
1106 // may take a lock on the Connection. ServicePoint should never call members under the lock that
1107 // end up taking a lock on a Connection (risk of deadlock).
1108 connectionGroup.DisableKeepAliveOnConnections();
1115 /// - Sets all connections in all connections groups to not be KeepAlive.
1116 /// - Causes all connections to be closed, if they are not active
1117 /// - Removes all references to all connection groups and their connections
1118 /// does essentially a "ReleaseConnectionGroup" of each group in this ServicePoint
1121 internal void ReleaseAllConnectionGroups()
1123 // The timer thread is allowed to call this. (It doesn't call user code and doesn't block.)
1124 GlobalLog.ThreadContract(ThreadKinds.Unknown, ThreadKinds.SafeSources | ThreadKinds.Timer, "ServicePoint#" + ValidationHelper.HashString(this) + "::ReleaseAllConnectionGroups");
1126 // To avoid deadlock (can't lock a ServicePoint followed by a Connection), copy out all the
1127 // connection groups in a lock, then release them all outside of it.
1128 ArrayList cgs = new ArrayList(m_ConnectionGroupList.Count);
1131 foreach (ConnectionGroup cg in m_ConnectionGroupList.Values)
1135 m_ConnectionGroupList.Clear();
1137 foreach (ConnectionGroup cg in cgs)
1139 cg.DisableKeepAliveOnConnections();
1145 /// <para>Internal class, used to store state for async Connect</para>
1147 private class ConnectSocketState {
1148 internal ConnectSocketState(ServicePoint servicePoint, PooledStream pooledStream, object owner, Socket s4, Socket s6)
1150 this.servicePoint = servicePoint;
1151 this.pooledStream = pooledStream;
1156 internal ServicePoint servicePoint;
1159 internal object owner;
1160 internal IPAddress[] addresses;
1161 internal int currentIndex;
1163 internal int unsuccessfulAttempts;
1164 internal bool connectFailure;
1165 internal PooledStream pooledStream;
1170 /// <para>Proviates an async callback that is called when Connect completes [part of ConnectSocket(...)]</para>
1172 private static void ConnectSocketCallback(IAsyncResult asyncResult) {
1173 ConnectSocketState state = (ConnectSocketState)asyncResult.AsyncState;
1174 Socket socket = null;
1175 IPAddress address = null;
1176 Exception innerException = null;
1177 Exception exception = null;
1178 WebExceptionStatus ws = WebExceptionStatus.ConnectFailure;
1182 ws = state.servicePoint.ConnectSocketInternal(state.connectFailure, state.s4, state.s6, ref socket, ref address, state, asyncResult, out innerException);
1184 catch (SocketException socketException) {
1185 exception = socketException;
1187 catch (ObjectDisposedException socketException) {
1188 exception = socketException;
1191 if (ws == WebExceptionStatus.Pending) {
1195 if (ws == WebExceptionStatus.Success) {
1197 state.servicePoint.CompleteGetConnection(state.s4, state.s6, socket, address);
1199 catch (SocketException socketException) {
1200 exception = socketException;
1202 catch (ObjectDisposedException socketException) {
1203 exception = socketException;
1208 exception = new WebException(
1209 NetRes.GetWebStatusString(ws),
1210 ws == WebExceptionStatus.ProxyNameResolutionFailure || ws == WebExceptionStatus.NameResolutionFailure ? state.servicePoint.Host : null,
1213 null, /* no response */
1214 WebExceptionInternalStatus.ServicePointFatal);
1217 state.pooledStream.ConnectionCallback(state.owner, exception, socket, address);
1221 if (socket != null && socket.CleanedUp)
1222 return; // The connection was aborted and requests dispatched
1228 private void BindUsingDelegate(Socket socket, IPEndPoint remoteIPEndPoint)
1230 IPEndPoint clonedRemoteIPEndPoint = new IPEndPoint(remoteIPEndPoint.Address, remoteIPEndPoint.Port);
1233 for (retryCount=0; retryCount<int.MaxValue; retryCount++) {
1234 IPEndPoint localIPEndPoint = BindIPEndPointDelegate(this, clonedRemoteIPEndPoint, retryCount);
1235 if (localIPEndPoint == null)
1239 socket.InternalBind(localIPEndPoint);
1246 if (retryCount == int.MaxValue)
1247 throw new OverflowException("Reached maximum number of BindIPEndPointDelegate retries");
1252 /// <para>Set SocketOptionName.ReuseUnicastPort (SO_REUSE_UNICASTPORT) socket option on the outbound connection.</para>
1254 private void SetUnicastReusePortForSocket(Socket socket)
1258 if (ServicePointManager.ReusePortSupported.HasValue && !ServicePointManager.ReusePortSupported.Value) {
1259 // We tried to set the socket option before and it isn't supported on this system. So, we'll save some
1260 // time by not trying again.
1264 reusePort = ServicePointManager.ReusePort;
1268 // This socket option is defined in Windows 10.0 or later. It is also
1269 // available if an LDR servicing patch has been installed on downlevel OS.
1271 socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseUnicastPort, 0x1);
1273 Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_set_socketoption_reuseport,
1274 "Socket", socket.GetHashCode()));
1277 ServicePointManager.ReusePortSupported = true;
1279 catch (SocketException) {
1280 // The socket option is not supported. We will ignore this error and fail gracefully.
1282 Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_set_socketoption_reuseport_not_supported,
1283 "Socket", socket.GetHashCode()));
1285 ServicePointManager.ReusePortSupported = false;
1287 catch (Exception ex) {
1288 // We want to preserve app compat and trap any other unusual exceptions.
1290 Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_unexpected_exception, ex.Message));
1297 /// <para>This is the real logic for doing the Connect with IPv4 and IPv6 addresses, see ConnectSocket for details</para>
1299 private WebExceptionStatus ConnectSocketInternal(bool connectFailure, Socket s4, Socket s6, ref Socket socket,
1300 ref IPAddress address, ConnectSocketState state, IAsyncResult asyncResult, out Exception exception) {
1301 IPEndPoint remoteIPEndPoint;
1305 // so, here we are: we have the EndPoint we need to connect to, all we
1306 // need to do is call into winsock and try to connect to this HTTP server.
1308 // this is how we do it:
1309 // we'll keep trying to Connect() until either:
1310 // (1) Connect() succeeds (on which we increment the number of connections opened) or
1311 // (2) we can't get any new address for this host.
1313 // (1) is pretty easy, here's how we do (2):
1314 // If the hostinformation is every marked as failed, we will automatically refresh it
1315 // the next time it is read.
1316 // If we fail the first time using the DNS information and the DNS information is recent,
1317 // which mean's it either hasn't been tried or it failed, we will mark the
1318 // hostinformation as failed, and quit. Otherwise we'll refresh the DNS information and
1319 // try one more time. If we fail the second time, then we'll mark the DNS information
1320 // as failed and quit.
1321 IPAddress[] addresses = null;
1322 for (int unsuccessfulAttempts = 0; unsuccessfulAttempts < 2; unsuccessfulAttempts++) {
1327 // Use asyncResult to make sure it is only called at initiation time.
1328 if (asyncResult == null)
1330 // the second time, determine if the list was recent.
1332 addresses = GetIPAddressInfoList(out currentIndex, addresses);
1334 //the addresses were recent, or we couldn't resolve the addresses.
1335 if (addresses == null || addresses.Length == 0)
1340 GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::ConnectSocketInternal() resuming previous state");
1342 addresses = state.addresses;
1343 currentIndex = state.currentIndex;
1345 unsuccessfulAttempts = state.unsuccessfulAttempts;
1348 //otherwise, try all of the addresses in the list.
1349 for (; i < addresses.Length; i++)
1351 IPAddress ipAddressInfo = addresses[currentIndex];
1353 remoteIPEndPoint = new IPEndPoint(ipAddressInfo, m_Port);
1354 Socket attemptSocket;
1355 if ( remoteIPEndPoint.Address.AddressFamily==AddressFamily.InterNetwork ) {
1364 if (asyncResult != null)
1366 IAsyncResult asyncResultCopy = asyncResult;
1368 attemptSocket.EndConnect(asyncResultCopy);
1371 GlobalLog.Print("ServicePoint#" + ValidationHelper.HashString(this) + "::ConnectSocketInternal() calling BeginConnect() to:" + remoteIPEndPoint.ToString());
1373 // save off our state and do our async call
1374 state.addresses = addresses;
1375 state.currentIndex = currentIndex;
1377 state.unsuccessfulAttempts = unsuccessfulAttempts;
1378 state.connectFailure = connectFailure;
1380 if (!attemptSocket.IsBound) {
1381 if (ServicePointManager.ReusePort) {
1382 SetUnicastReusePortForSocket(attemptSocket);
1385 if (BindIPEndPointDelegate != null) {
1386 BindUsingDelegate(attemptSocket, remoteIPEndPoint);
1390 attemptSocket.UnsafeBeginConnect(remoteIPEndPoint, m_ConnectCallbackDelegate, state);
1391 return WebExceptionStatus.Pending;
1395 if (!attemptSocket.IsBound) {
1396 if (ServicePointManager.ReusePort) {
1397 SetUnicastReusePortForSocket(attemptSocket);
1400 if (BindIPEndPointDelegate != null) {
1401 BindUsingDelegate(attemptSocket, remoteIPEndPoint);
1405 attemptSocket.InternalConnect(remoteIPEndPoint);
1407 socket = attemptSocket;
1408 address = ipAddressInfo;
1410 UpdateCurrentIndex(addresses, currentIndex);
1411 return WebExceptionStatus.Success;
1413 catch (ObjectDisposedException)
1415 // This can happen if the request has been aborted and the attemptSocket got closed.
1416 return WebExceptionStatus.RequestCanceled;
1420 if (NclUtilities.IsFatal(e)) throw;
1423 connectFailure = true;
1426 if (currentIndex >= addresses.Length) {
1434 return connectFailure ? WebExceptionStatus.ConnectFailure :
1435 InternalProxyServicePoint ? WebExceptionStatus.ProxyNameResolutionFailure :
1436 WebExceptionStatus.NameResolutionFailure;
1440 /// <para>private implimentation of ConnectSocket(...)</para>
1442 private WebExceptionStatus ConnectSocket(Socket s4, Socket s6, ref Socket socket, ref IPAddress address,
1443 ConnectSocketState state, out Exception exception) {
1445 // we need this for the call to connect()
1447 return ConnectSocketInternal(false, s4, s6, ref socket, ref address, state, null, out exception);
1450 [System.Diagnostics.Conditional("DEBUG")]
1451 internal void DebugMembers(int requestHash) {
1452 foreach(ConnectionGroup connectGroup in m_ConnectionGroupList.Values) {
1453 if (connectGroup!=null) {
1455 connectGroup.DebugMembers(requestHash);
1465 // Previously: class IPHostInformation
1468 private string m_HostName = String.Empty;
1469 private bool m_IsTrustedHost = true; // CBT: False if the DNS resolve changed the host
1470 private IPAddress[] m_IPAddressInfoList;
1471 private int m_CurrentAddressInfoIndex;
1472 private bool m_ConnectedSinceDns = false;
1473 private bool m_AddressListFailed = false;
1474 private DateTime m_LastDnsResolve;
1475 private bool m_IPAddressesAreLoopback;
1477 private void Failed(IPAddress[] addresses)
1479 if (addresses == m_IPAddressInfoList){
1481 if (addresses == m_IPAddressInfoList){
1482 m_AddressListFailed = true;
1489 //if dns round robin is enabled, we don't want to update the index
1490 //because other connections may have skipped to the next address.
1491 //we need a better mechanism to handle dead connections
1492 private void UpdateCurrentIndex(IPAddress[] addresses, int currentIndex)
1494 if (addresses == m_IPAddressInfoList && (m_CurrentAddressInfoIndex != currentIndex || !m_ConnectedSinceDns)){
1496 if (addresses == m_IPAddressInfoList){
1497 if (!ServicePointManager.EnableDnsRoundRobin ) {
1498 m_CurrentAddressInfoIndex = currentIndex;
1500 m_ConnectedSinceDns = true;
1507 private bool HasTimedOut {
1509 int dnsRefreshTimeout = ServicePointManager.DnsRefreshTimeout;
1510 return dnsRefreshTimeout != Timeout.Infinite &&
1511 (m_LastDnsResolve + new TimeSpan(0, 0, 0, 0, dnsRefreshTimeout)) < DateTime.UtcNow;
1516 // If addresses is specified, we determine if the addresslist is recent
1517 // If the answer is yes, we return null. Whether its recent is determined by whether
1518 // or not the current hostinformation has ever been marked as succeeded or failed (meaning
1519 // even tried). If it isn't recent, we'll refresh the addresslist.
1521 private IPAddress[] GetIPAddressInfoList(out int currentIndex, IPAddress[] addresses)
1523 IPHostEntry ipHostEntry = null;
1525 bool needDnsResolution = false;
1526 bool dnsResolutionFailed = false;
1528 // Phase 1: Decide if we need to do a DNS resolution
1531 // return null if the current hostinformation has never been marked as succeeded or failed
1532 // (the hostinformation hasn't been used) and it hasn't changed.
1534 if (addresses != null && !m_ConnectedSinceDns && !m_AddressListFailed && addresses == m_IPAddressInfoList)
1537 // refresh the list if its already failed, or if the addresslist isn't recent
1538 if (m_IPAddressInfoList == null || m_AddressListFailed || addresses == m_IPAddressInfoList || HasTimedOut) {
1539 m_CurrentAddressInfoIndex = 0;
1540 m_ConnectedSinceDns = false;
1541 m_AddressListFailed = false;
1542 m_LastDnsResolve = DateTime.UtcNow;
1544 needDnsResolution = true;
1548 // Phase 2: If we have to do a DNS resolution now, then do it now
1549 if (needDnsResolution) {
1551 dnsResolutionFailed = !Dns.TryInternalResolve(m_Host, out ipHostEntry);
1553 catch (Exception exception)
1555 if (NclUtilities.IsFatal(exception)) throw;
1556 dnsResolutionFailed = true;
1557 GlobalLog.Print("IPHostInformation#" + ValidationHelper.HashString(this) + "::GetIPAddressInfoList() Dns.InternalResolveFast() failed with exception:\r\n" + exception.ToString());
1561 // Phase 3: If we did a DNS resolution, then deal with the results safely under a lock
1563 if (needDnsResolution) {
1565 m_IPAddressInfoList = null;
1567 if (!dnsResolutionFailed) {
1568 if (ipHostEntry!=null && ipHostEntry.AddressList!=null && ipHostEntry.AddressList.Length>0) {
1569 SetAddressList(ipHostEntry);
1572 GlobalLog.Print("IPHostInformation#" + ValidationHelper.HashString(this) + "::GetIPAddressInfoList() Dns.InternalResolveFast() failed with null");
1575 GlobalLog.Print("IPHostInformation#" + ValidationHelper.HashString(this) + "::GetIPAddressInfoList() Dns.InternalResolveFast() had thrown an exception");
1579 if (m_IPAddressInfoList!=null && m_IPAddressInfoList.Length > 0) {
1580 GlobalLog.Print("IPHostInformation#" + ValidationHelper.HashString(this) + "::GetIPAddressInfoList() m_IPAddressInfoList = "+m_IPAddressInfoList);
1581 currentIndex = m_CurrentAddressInfoIndex;
1583 //auto increment index for next connect request if round robin is enabled
1584 if (ServicePointManager.EnableDnsRoundRobin)
1586 m_CurrentAddressInfoIndex++;
1587 if (m_CurrentAddressInfoIndex >= m_IPAddressInfoList.Length) {
1588 m_CurrentAddressInfoIndex = 0;
1591 return m_IPAddressInfoList;
1594 GlobalLog.Print("IPHostInformation#" + ValidationHelper.HashString(this) + "::GetIPAddressInfoList() GetIPAddressInfoList returning null");
1599 // Called under lock(this)
1601 private void SetAddressList(IPHostEntry ipHostEntry)
1603 GlobalLog.Print("IPHostInformation#" + ValidationHelper.HashString(this) + "::SetAddressList("+ipHostEntry.HostName+")");
1605 // Create an array of IPAddress of the appropriate size, then
1606 // get a list of our local addresses. Walk through the input
1607 // address list. Copy each address in the address list into
1608 // our array, and if the address is a loopback address, mark it as
1611 // Only update the member with successfull final result.
1612 // In case of an exception m_IPAddressInfoList will stay as null
1614 bool wasLoopback = m_IPAddressesAreLoopback;
1615 bool wasNull = m_IPAddressInfoList == null;
1617 m_IPAddressesAreLoopback = IsAddressListLoopback(ipHostEntry.AddressList);
1618 m_IPAddressInfoList = ipHostEntry.AddressList;
1619 m_HostName = ipHostEntry.HostName;
1620 m_IsTrustedHost = ipHostEntry.isTrustedHost;
1622 if (wasNull || wasLoopback != m_IPAddressesAreLoopback)
1624 ResolveConnectionLimit();
1628 private static bool IsAddressListLoopback(IPAddress[] addressList)
1630 GlobalLog.Print("IPHostInformation::CheckAddressList(" + addressList.Length + ")");
1633 // Walk through each member of the input list, copying it into our temp array.
1637 IPAddress[] localAddresses = null;
1639 localAddresses = NclUtilities.LocalAddresses;
1641 catch (Exception exception)
1643 if (NclUtilities.IsFatal(exception)) throw;
1645 // ATTN: If LocalAddresses has failed terribly we will treat just resolved name as a remote server.
1650 Logging.PrintError(Logging.Web, SR.GetString(SR.net_log_retrieving_localhost_exception, exception));
1651 Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_log_resolved_servicepoint_may_not_be_remote_server));
1655 for (i = 0; i < addressList.Length; i++)
1657 // First, check to see if the current address is a loopback address.
1658 if (IPAddress.IsLoopback(addressList[i]))
1663 if (localAddresses != null)
1665 // See if the current IP address is a local address, and if
1666 // so mark it as such.
1667 for (k = 0; k < localAddresses.Length; k++)
1670 // IPv6 Changes: Use .Equals for this check !
1672 if (addressList[i].Equals(localAddresses[k]))
1677 if (k < localAddresses.Length)
1686 return i == addressList.Length;