I want to make a web request from one of available IP addresses on server so I use this class:
public class UseIP
{
public string IP { get; private set; }
public UseIP(string IP)
{
this.IP = IP;
}
public HttpWebRequest CreateWebRequest(Uri uri)
{
ServicePoint servicePoint = ServicePointManager.FindServicePoint(uri);
servicePoint.BindIPEndPointDelegate = new BindIPEndPoint(Bind);
return WebRequest.Create(uri) as HttpWebRequest;
}
private IPEndPoint Bind(ServicePoint servicePoint, IPEndPoint remoteEndPoint, int retryCount)
{
IPAddress address = IPAddress.Parse(this.IP);
return new IPEndPoint(address, 0);
}
}
Then:
UseIP useIP = new UseIP("Valid IP address here...");
Uri uri = new Uri("http://ip.nefsc.noaa.gov");
HttpWebRequest request = useIP.CreateWebRequest(uri);
// Then make the request with the specified IP address
But the solution just works the first time!
Problem may be with the delegate getting reset on each new request. Try below:
Also as far as I know, the endpoints are cached so even clearing the delegate may not work in some cases and they may get reset regardless. You may unload/reload the app domain as the worst case scenario.
I like this new class UseIP.
There is a point at Specify the outgoing IP Address to use with WCF client about protecting yourself from IPv4/IPv6 differences.
The only thing that would need to change is the Bind method to be like this:
re: the Bind method being called multiple times.
What works for me is to remove any delegate link before I add it.
I also like the idea of caching the UseIP objects. So I added this static method to the UseIP class.
A theory:
HttpWebRequest relies on an underlying ServicePoint. The ServicePoint represents the actual connection to the URL. Much in the same way your browser keeps a connection to a URL open between requests and reuses that connection (to eliminate the overhead of opening and closing the connection with each request), ServicePoint performs the same function for HttpWebRequest.
I think that the BindIPEndPointDelegate that you are setting for the ServicePoint is not being called on each use of HttpWebRequest because the ServicePoint is reusing the connection. If you could force the connection to close, then the next call to that URL should cause the ServicePoint to need to call BindIPEndPointDelegate again.
Unfortunately, it doesn't appear that the ServicePoint interface gives you the ability to directly force a connection to close.
Two solutions (each with slightly different results)
1) For each request, set HttpWebRequest.KeepAlive = false. In my test, this caused the Bind delegate to get called one-for-one with each request.
2) Set the ServicePoint ConnectionLeaseTimeout property to zero or some small value. This will have the effect of periodically forcing the Bind delegate to be called (not one-for-one with each request).
From the documentation:
Setting this value affects all connections managed by the ServicePoint object.
The following (basic) test results in the Bind delegate getting called for each request:
I have changed your example a little and make it work on my machine:
I did that because:
FindServicePoint
actually does the request using the "default" ip, without even calling the binding delegate, to the URI you have specified. In my machine, at least, theBindIPEndPointDelegate
was not called in the way you have presented (I know the request was made because I didn't set the Proxy and got a proxy authentication error);ServicePointManager
.