Perform http request with a connection to specified IP address

Viewed 99

I am attempting to write a function to verify web servers behind a load balancer. As a result, I will need to connect to an IP address that is not registered in DNS because DNS will be registered to the front-end IP address of the load balancer. Other suggestions have been to use something like "https://192.168.1.17/ and ignore certificate errors, but I need to validate the certificate as part of the process. I believe the best option would be to use a connection call back method, but I have not been able to get something that is close to working.

Here a code snippet from one of my attempts.

$SourceUri = [System.Uri]::New("https://site.domain.com/healthCheck")
[System.Net.IPAddress]$IPAddress = '192.168.1.17'
[int]$Port = 443

$SocketsHttpHandler = [System.Net.Http.SocketsHttpHandler]::New()
$SocketsHttpHandler.ConnectCallback = function:{
    param([System.Net.IPAddress]$IPAddress, [int]$Port)

    $Socket = [System.Net.Sockets.Socket]::New([System.Net.Sockets.SocketType]::Stream, [System.Net.Sockets.ProtocolType]::Tcp)
    $Socket.NoDelay = $true
    $Socket.ConnectAsync($IPAddress, $Port)

    $NetworkStream = [System.Net.Sockets.NetworkStream]::New($Socket, $true)
    return $NetworkStream
}

$HttpClient = [System.Net.Http.HttpClient]::New($SocketsHttpHandler($IPAddress, $Port))
$task = $HttpClient.GetStringAsync($SourceUri)
$task.wait()
if ($task.IsCompleted) {
    Write-Output $task.Result
} else {
    Write-Output "Something went wrong: " + $task.Exception.Message
}

Please let me know what I should do to get the call back method working. Thank you.

Update I think I made some forward progress, but receiving an error setting the callback.

$SourceUri = [System.Uri]::New("https://site.domain.com/healthCheck")
[System.Net.IPAddress]$IPAddress = '192.168.1.17'
[int]$Port = 443

$Code = @'
using System;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

public sealed class SocketCallbackClosure
{
    public IPAddress IPAddress { get; }
    public int Port { get; }

    public Func<SocketsHttpConnectionContext, CancellationToken, Task<NetworkStream>> Callback { get; }

    public SocketCallbackClosure(IPAddress IPAddress, int Port)
    {
        this.IPAddress = IPAddress;
        this.Port = Port;

        this.Callback = CallbackImpl;
    }

    private async Task<NetworkStream> CallbackImpl(
        SocketsHttpConnectionContext ConnectionContext,
        CancellationToken CToken)
    {
        var s = new Socket(SocketType.Stream, ProtocolType.Tcp) { NoDelay = true };

        try
        {
            s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
            s.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, 5);
            s.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, 5);
            s.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount, 5);

            await s.ConnectAsync(this.IPAddress, this.Port, CToken);

            return new NetworkStream(s, ownsSocket: true);
        }
        catch
        {
            s.Dispose();
            throw;
        }
    }
}
'@

Add-Type -TypeDefinition $Code

$SocketCallbackClosure = [SocketCallbackClosure]::New($IPAddress, $Port)

$SocketsHttpHandler = [System.Net.Http.SocketsHttpHandler]::New()
$SocketsHttpHandler.ConnectCallback = $SocketCallbackClosure.Callback

$HttpClient = [System.Net.Http.HttpClient]::New($SocketsHttpHandler)
$task = $HttpClient.GetStringAsync($SourceUri)
$task.wait()
if ($task.IsCompleted) {
    Write-Output $task.Result
} else {
    Write-Output "Something went wrong: " + $task.Exception.Message
}
0 Answers
Related