1

I have several IPs on a host and want to choose which is used when I make a connection from a HttpClient with a HttpRequestMessage. Can this be done without me going down to sockets and writing a simple http client myself?

I'm looking for an equivalent of bind before connect at socket level but for HttpClient... or an alternative.

CodeAngry
  • 12,760
  • 3
  • 50
  • 57

1 Answers1

2

So, if you are using the default HttpClient message handler on a Windows machine, HttpClient will be using the .Net HttpWebRequest to do its work. This in turn, relies on the ServicePointManager to provide a ServicePoint to manage connections to the URI. For any domain, you can hook into its ServicePoint to change the way it binds to an IPEndPoint.

If you take the URI that you are trying to connect to and find its ServicePoint:

var sp = ServicePointManager.FindServicePoint(uri);

then you can:

var sp = ServicePointManager.FindServicePoint(new Uri("http://www.google.com"));
sp.BindIPEndPointDelegate = (servicePoint, remoteEndPoint, retryCount) => {
    IPAddress adapterIpAddress = //your chosen adapter's IP address
    return new IPEndPoint(adapterIpAddress, 0);
};

If you want to get really jazzy, you could fold this into a DelegatingHandler so that the HttpClient takes care of everything for you:

public class RebindingHandler : DelegatingHandler
{
    private BindIPEndPoint bindHandler;
    public RebindingHandler(IEnumerable<IPAddress> adapterAddresses, 
                            HttpMessageHandler innerHandler = null)
        : base(innerHandler ?? new WebRequestHandler())
    {
        var addresses = adapterAddresses.ToList();
        if(!addresses.Any())
        {
            throw new ArgumentException();
        }
        var idx = 0;
        bindHandler = (servicePoint, remoteEndPoint, retryCount) => {
            int i = Interlocked.Increment(ref idx);
            uint i2 = unchecked((uint)i);
            int index = (int)(((long)i2) % addresses.Count);

            IPAddress adapterIpAddress = addresses[index];
            return new IPEndPoint(adapterIpAddress, 0);
        };
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        var sp = ServicePointManager.FindServicePoint(request.RequestUri);
        sp.BindIPEndPointDelegate = bindHandler;
        var httpResponseMessage = await base.SendAsync(request, cancellationToken);
        return httpResponseMessage;
    }
}

Then you can:

var addresses = new List<IPAddress>(); //this contains your adapter addresses
var client = new HttpClient(new RebindingHandler(addresses));

and when you use client, the inner handler will automatically enroll the relevant ServicePoint to use a BindIPEndPoint that rotates around a bunch of IPEndPoints.

spender
  • 117,338
  • 33
  • 229
  • 351
  • I'll be hitting a single domain. I just need it to see different IP addresses. Is there a way to avoid mentioning a URI there? – CodeAngry Jan 22 '16 at 15:40
  • @CodeAngry I wrote a delegating handler that should do everything you need. – spender Jan 22 '16 at 15:54
  • @CodeAngry I made a change to the code to avoid a concurrency issue. – spender Jan 22 '16 at 16:08
  • I got it. I'll deal with anything else from here. I know where to start now. Thanks! – CodeAngry Jan 22 '16 at 16:09
  • @CodeAngry Good, because I tested nothing here :) – spender Jan 22 '16 at 16:10
  • @CodeAngry working on same issue for days now and the solution provided is still getting me same servicepoint for each binding to different ip.http://stackoverflow.com/questions/36217696/webrequests-with-binding-different-ips-using-bindipendpointdelegate-does-not-res – devprashant Mar 26 '16 at 06:11
  • @devprashant Ended up writing a minimalist socket HTTP client that fit my needs. After some testing, none of these methods were reliable or fast enough for what I wanted to do. – CodeAngry Mar 26 '16 at 15:19
  • @CodeAngry Implemented socket and using socket.bind() to bind to specific NIC. Still only 2 NICs are working at a time. When data from 1 out of 2 NIC gets completely downloaded then only data download starts from 3rd NIC. – devprashant Mar 30 '16 at 13:42