httping.psm1

$script:ModuleRoot = $PSScriptRoot

<#
.SYNOPSIS
    Tests HTTP connectivity to a specified URI by sending multiple requests and measuring response latency.
 
.DESCRIPTION
    The Test-HttpConnection cmdlet sends HTTP requests to a specified URI and measures the response time for each request.
    It provides detailed information about the HTTP connection, including status codes, response sizes, and latency measurements.
    This is useful for diagnosing network issues, testing web server responsiveness, and monitoring HTTP endpoints.
 
    By default, the cmdlet displays a summary table with key metrics. Use Format-List to view all available properties,
    including headers, protocol details, and more.
 
.PARAMETER Uri
    Specifies the URI to test the connection to. This parameter is mandatory.
 
.PARAMETER Count
    Specifies the number of times to test the connection. The default value is 4.
 
.PARAMETER SkipCertificateCheck
    Specifies whether to skip SSL certificate validation. This is useful for testing sites with self-signed or expired certificates.
    The default value is $false.
 
.PARAMETER TimeoutSeconds
    Specifies the timeout in seconds for each connection attempt. The default value is 30 seconds.
 
.OUTPUTS
    PSCustomObject. Test-HttpConnection returns custom objects with details about each HTTP request, including ping count, host,
    port, scheme, path, status code, bytes received, latency, and additional properties visible with Format-List.
 
.EXAMPLE
    PS> Test-HttpConnection -Uri 'https://http.codes/500'
 
    Httping Host Port Status Code Bytes Latency (ms)
    ------- ---- ---- ----------- ----- ------------
    1 http.codes 443 500 25 586.5403
    2 http.codes 443 500 25 573.7779
    3 http.codes 443 500 25 579.0879
    4 http.codes 443 500 25 577.2778
 
    This example tests connectivity to https://http.codes/500 four times and displays a summary table.
 
.EXAMPLE
    PS> Test-HttpConnection -Uri 'https://expired.badssl.com/' -SkipCertificateCheck | Format-List
 
    Httping : 1
    Host : expired.badssl.com
    Port : 443
    Scheme : https
    Path : /
    Status Code : 200
    Bytes : 494
    Latency (ms) : 258.7057
    Method : GET
    Version : 1.1
    Content-Type : text/html
     
    This example tests a site with an expired certificate, skipping validation, and displays all properties in list format.
 
#>

function Test-HttpConnection {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [uri]
        $Uri,

        [Parameter()]
        [ValidateRange(1, [int]::MaxValue)]
        [int]
        $Count = 4,

        [Parameter()]
        [switch]
        $SkipCertificateCheck = $false,

        [Parameter()]
        [ValidateRange(1, [int]::MaxValue)]
        [int]
        $TimeoutSeconds = 30
    )

    begin {
        $Parameters = @{
            Uri                      = $Uri
            UseBasicParsing          = $true
            SkipHttpErrorCheck       = $true
            SkipCertificateCheck     = $SkipCertificateCheck
            ConnectionTimeoutSeconds = $TimeoutSeconds
            OperationTimeoutSeconds  = $TimeoutSeconds
        }

        $OutputProperties = @{
            Property = (
                'Httping',
                @{ Name = "Host"        ; Expression = { $_.DnsSafeHost } },
                @{ Name = "Path"        ; Expression = { $_.PathAndQuery } },
                @{ Name = "Scheme"      ; Expression = { $_.Scheme } },
                @{ Name = "Port"        ; Expression = { $_.Port } },
                @{ Name = "Status Code" ; Expression = { $_.StatusCode } },
                'Bytes',
                @{ Name = "Latency (ms)"; Expression = { $_.Latency } },
                'Method',
                'Version',
                @{ Name = "Content-Type"; Expression = { $_.ContentType } }
            )
        }
    }
    process {
        for ($i = 1; $i -le $Count; $i++) {
            try {
                $TimeSpan = Measure-Command {
                    $Response = Invoke-WebRequest @Parameters
                    $Response | Out-Null
                }

                $Result = [PSCustomObject]@{
                    Httping      = $i
                    DnsSafeHost  = $Response.BaseResponse.RequestMessage.RequestUri.DnsSafeHost
                    Port         = $Response.BaseResponse.RequestMessage.RequestUri.Port
                    Scheme       = $Response.BaseResponse.RequestMessage.RequestUri.Scheme
                    PathAndQuery = $Response.BaseResponse.RequestMessage.RequestUri.PathAndQuery
                    StatusCode   = $Response.StatusCode
                    Bytes        = $Response.RawContentLength
                    Latency      = $TimeSpan.TotalMilliseconds
                    Method       = $Response.BaseResponse.RequestMessage.Method
                    Version      = $Response.BaseResponse.RequestMessage.Version
                    ContentType  = $Response.Headers.'Content-Type'
                } | Select-Object @OutputProperties

                $Result.PSObject.TypeNames.Insert(0, 'HttpingResult')
                $Result
            }
            catch {
                Write-Error "Failed to connect to $Uri on attempt $i : $_"
            }
        }
    }
    end {
        # No op
    }
}


Export-ModuleMember -Function 'Test-HttpConnection'