1

My service makes external calls to other services. It helps to know exact IP address for established connection (in case there is a need to follow up with owners of those services).

I'm using HttpClient. Is there a way to get underlying IP address (reflection is fine)?

ZakiMa
  • 5,637
  • 1
  • 24
  • 48
  • 1
    But you've already connected to the remote address, why can't you use that? – DavidG Sep 07 '19 at 01:22
  • HttpClient contains a connection pool, it doesn't establish a single connection, but creates connections as needed. Multiple connections to the same service could end up with different IP addresses, depending on how the service is provided, if there is a load balancer for example. – Jonathon K Sep 07 '19 at 01:28
  • of interest?: https://stackoverflow.com/questions/37582553/how-to-get-client-ip-address-in-azure-functions-c – Mitch Wheat Sep 07 '19 at 01:28
  • Did you actually checked with "owners of those services" if providing IP would be *of any use* to them? – Alexei Levenkov Sep 07 '19 at 03:31
  • It is possible to do it for HttpWebRequest: https://stackoverflow.com/questions/55446711/find-ip-address-used-in-failed-httpwebrequest. I wonder whether there is a way to get similar information for HttpClient. – ZakiMa Sep 07 '19 at 09:30
  • @JonathonK, I understand that HttpClient uses a connection pool. – ZakiMa Sep 07 '19 at 09:30
  • @Mitch Wheat, attached thread solves opposite problem (how to learn client ip). – ZakiMa Sep 07 '19 at 09:30
  • @Alexei Levenkov, yes, need to know IP address. – ZakiMa Sep 07 '19 at 09:31
  • You probably have to implement your own HttpMessagHandler... – Jonathon K Sep 07 '19 at 15:25
  • Sorry if it's a dumb question, but why can't you do a DNS lookup and get the list of ip-addresses your desired host resolves to, and just call ip addresses? E.g. `await Dns.GetHostEntryAsync("stackoverflow.com")` is dns entry containing `[151.101.1.69], [151.101.193.69], [151.101.65.69], [151.101.129.69]`, can you use that in any way? – pneuma Sep 09 '19 at 18:22
  • My service establishes thousands and thousands of connections every minute per virtual machine and reports health of those services. Need to provide exact IP which failed. DNS lookup will not work as great - it provides a set of IPs and on top if it this will result in many extra calls I'd like to avoid. I agree that for many other scenarios DNS lookup would suffice (or even IP information not being needed in the first place) – ZakiMa Sep 09 '19 at 18:27

1 Answers1

1

Inspired by this and this posts.

It is possible to get access to underlying ServicePoint through reflection and override BindIPEndPoint:

private static void TryHttpClientWithServicePoint()
{
    var webRequestHandler = SetServicePointOptions(new WebRequestHandler());
    var client = new HttpClient(webRequestHandler);
    var content = client.GetStringAsync("https://stackoverflow.com").Result;
    Console.WriteLine(content.Substring(0, 256));
}

private static WebRequestHandler SetServicePointOptions(WebRequestHandler handler)
{
    var field = typeof(HttpClientHandler).GetField("_startRequest",
        BindingFlags.NonPublic | BindingFlags.Instance); // Fieldname has a _ due to being private
    var startRequest = (Action<object>) field.GetValue(handler);

    Action<object> newStartRequest = obj =>
    {
        var webReqField = obj.GetType().GetField("webRequest", BindingFlags.NonPublic | BindingFlags.Instance);
        var webRequest = webReqField.GetValue(obj) as HttpWebRequest;
        webRequest.ServicePoint.BindIPEndPointDelegate = BindIPEndPoint;

        startRequest(obj); //call original action
    };

    field.SetValue(handler, newStartRequest); //replace original 'startRequest' with the one above

    return handler;
}

public static IPEndPoint BindIPEndPoint(ServicePoint servicePoint, IPEndPoint remoteEndPoint, int retryCount)
{
    Console.WriteLine($"IP address = {remoteEndPoint}");
    return null;
}

Gives access to remote IP:

enter image description here

ZakiMa
  • 5,637
  • 1
  • 24
  • 48