< Summary - Kestrun — Combined Coverage

Information
Class: Private.Server.Resolve-KrEndpointBinding
Assembly: Kestrun.PowerShell.Private
File(s): /home/runner/work/Kestrun/Kestrun/src/PowerShell/Kestrun/Private/Server/Resolve-KrEndpointBinding.ps1
Tag: Kestrun/Kestrun@25c95cfbd84df534a61ed03b9abab034ac673a75
Line coverage
80%
Covered lines: 74
Uncovered lines: 18
Coverable lines: 92
Total lines: 366
Line coverage: 80.4%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 04/04/2026 - 18:57:31 Line coverage: 80.4% (74/92) Total lines: 366 Tag: Kestrun/Kestrun@25c95cfbd84df534a61ed03b9abab034ac673a75

Metrics

File(s)

/home/runner/work/Kestrun/Kestrun/src/PowerShell/Kestrun/Private/Server/Resolve-KrEndpointBinding.ps1

#LineLine coverage
 1<#
 2.SYNOPSIS
 3    Resolves the effective Kestrun endpoint binding from explicit parameters,
 4    environment variables, or built-in defaults.
 5.DESCRIPTION
 6    Binding precedence:
 7
 8    1. Explicit -Uri
 9    2. Explicit -HostName
 10    3. Explicit -Port / -IPAddress
 11    4. ASPNETCORE_URLS environment variable
 12    5. PORT environment variable
 13    6. Built-in defaults
 14
 15    Environment binding is only considered when no explicit listener target
 16    was supplied by the caller.
 17.PARAMETER BoundParameters
 18    The caller's $BoundParameters dictionary.
 19.PARAMETER Port
 20    The current Port value from the caller.
 21.PARAMETER IPAddress
 22    The current IPAddress value from the caller.
 23.PARAMETER HostName
 24    The current HostName value from the caller.
 25.PARAMETER Uri
 26    The current Uri value from the caller.
 27.PARAMETER DefaultPort
 28    The default port to use when no explicit or environment binding exists.
 29.PARAMETER DefaultIPAddress
 30    The default IP address to use when no explicit or environment binding exists.
 31.PARAMETER IgnoreEnvironment
 32    If specified, environment lookup is disabled.
 33.OUTPUTS
 34    PSCustomObject with:
 35        - Mode   : Uri | HostName | PortIPAddress
 36        - Source : Explicit | Environment:ASPNETCORE_URLS | Environment:PORT | Default
 37        - Uri
 38        - HostName
 39        - Port
 40        - IPAddress
 41        - EndpointNames
 42        - RawUrl
 43.EXAMPLE
 44    $binding = Resolve-KrEndpointBinding `
 45        -BoundParameters $BoundParameters `
 46        -Port $Port `
 47        -IPAddress $IPAddress `
 48        -HostName $HostName `
 49        -Uri $Uri
 50#>
 51function Resolve-KrEndpointBinding {
 52
 53    [CmdletBinding()]
 54    param(
 55        [Parameter(Mandatory)]
 56        [System.Collections.IDictionary]$BoundParameters,
 57
 58        [int]$Port = 0,
 59
 60        [System.Net.IPAddress]$IPAddress = [System.Net.IPAddress]::Any,
 61
 62        [string]$HostName,
 63
 64        [uri]$Uri,
 65
 66        [int]$DefaultPort = 5000,
 67
 68        [System.Net.IPAddress]$DefaultIPAddress = [System.Net.IPAddress]::Loopback,
 69
 70        [switch]$IgnoreEnvironment
 71    )
 72    function Format-KrEndpointName {
 73        <#
 74        .SYNOPSIS
 75            Formats an endpoint name from a server and port.
 76        .PARAMETER Server
 77            The server name or IP address.
 78        .PARAMETER Port
 79            The port number.
 80        .OUTPUTS
 81            A formatted endpoint name string.
 82        #>
 83        param(
 84            [Parameter(Mandatory)]
 85            [string]$Server,
 86            [Parameter(Mandatory)]
 87            [int]$Port
 88        )
 89
 190        $parsedIp = $null
 191        $formattedHost = if ([System.Net.IPAddress]::TryParse($Server, [ref]$parsedIp) -and $parsedIp.AddressFamily -eq 
 192            "[$Server]"
 93        } else {
 194            $Server
 95        }
 96
 197        return "${formattedHost}:$Port"
 98    }
 99
 100    function Get-KrEndpointName {
 101        <#
 102        .SYNOPSIS
 103            Generates a list of endpoint names based on the binding mode and parameters.
 104        .DESCRIPTION
 105            This function constructs a list of endpoint names that correspond to the resolved binding information.
 106            It takes into account special cases such as wildcard hosts and localhost to ensure that all relevant endpoin
 107        .PARAMETER Mode
 108            The mode of binding resolution: 'Uri', 'HostName', or 'PortIPAddress'.
 109        .PARAMETER Uri
 110            The resolved URI if Mode is 'Uri'.
 111        .PARAMETER HostName
 112            The resolved HostName if Mode is 'HostName'.
 113        .PARAMETER Port
 114            The resolved Port if Mode is 'HostName' or 'PortIPAddress'.
 115        .PARAMETER IPAddress
 116            The resolved IPAddress if Mode is 'PortIPAddress'.
 117        .OUTPUTS
 118            An array of endpoint name strings corresponding to the resolved binding.
 119        #>
 120        param(
 121            [Parameter(Mandatory)]
 122            [ValidateSet('Uri', 'HostName', 'PortIPAddress')]
 123            [string]$Mode,
 124            [uri]$Uri,
 125            [string]$HostName,
 126            [int]$Port,
 127            [System.Net.IPAddress]$IPAddress
 128        )
 129
 1130        $names = [System.Collections.Generic.List[string]]::new()
 131        function Add-EndpointName {
 132            <#
 133            .SYNOPSIS
 134                Adds an endpoint name to the list if it is not null, empty, or already present.
 135            .PARAMETER Name
 136                The endpoint name to add.
 137            #>
 138            param([string]$Name)
 1139            if (-not [string]::IsNullOrWhiteSpace($Name) -and -not $names.Contains($Name)) {
 1140                $names.Add($Name)
 141            }
 142        }
 143
 1144        switch ($Mode) {
 145            'Uri' {
 1146                if ($null -ne $Uri) {
 2147                    Add-EndpointName (Format-KrEndpointName -Server $Uri.Host -Port $Uri.Port)
 1148                    if ($Uri.Host -eq 'localhost') {
 0149                        Add-EndpointName (Format-KrEndpointName -Server ([System.Net.IPAddress]::Loopback.ToString()) -P
 0150                        Add-EndpointName (Format-KrEndpointName -Server ([System.Net.IPAddress]::IPv6Loopback.ToString()
 151                    }
 152                }
 153            }
 154
 155            'HostName' {
 2156                Add-EndpointName (Format-KrEndpointName -Server $HostName -Port $Port)
 1157                if ($HostName -eq 'localhost') {
 3158                    Add-EndpointName (Format-KrEndpointName -Server ([System.Net.IPAddress]::Loopback.ToString()) -Port 
 3159                    Add-EndpointName (Format-KrEndpointName -Server ([System.Net.IPAddress]::IPv6Loopback.ToString()) -P
 160                }
 161            }
 162
 163            'PortIPAddress' {
 1164                if ($null -eq $IPAddress) {
 165                    break
 166                }
 167
 1168                if ($IPAddress.Equals([System.Net.IPAddress]::Any) -or $IPAddress.Equals([System.Net.IPAddress]::IPv6Any
 2169                    Add-EndpointName (Format-KrEndpointName -Server 'localhost' -Port $Port)
 3170                    Add-EndpointName (Format-KrEndpointName -Server ([System.Net.IPAddress]::Loopback.ToString()) -Port 
 3171                    Add-EndpointName (Format-KrEndpointName -Server ([System.Net.IPAddress]::IPv6Loopback.ToString()) -P
 172                } else {
 2173                    Add-EndpointName (Format-KrEndpointName -Server $IPAddress.ToString() -Port $Port)
 1174                    if ($IPAddress.Equals([System.Net.IPAddress]::Loopback) -or $IPAddress.Equals([System.Net.IPAddress]
 2175                        Add-EndpointName (Format-KrEndpointName -Server 'localhost' -Port $Port)
 176                    }
 177                }
 178            }
 179        }
 180
 1181        return [string[]]$names
 182    }
 183
 184    function New-KrBindingResult {
 185        <#
 186        .SYNOPSIS
 187            Helper function to create a standardized binding result object.
 188        .DESCRIPTION
 189            This function constructs a PSCustomObject representing the resolved binding information,
 190            including the mode of resolution, source of the binding information, and the relevant properties.
 191        .PARAMETER Mode
 192            The mode of binding resolution: 'Uri', 'HostName', or 'PortIPAddress'.
 193        .PARAMETER Source
 194            The source of the binding information, e.g., 'Explicit', 'Environment:ASPNETCORE_URLS', 'Environment:PORT', 
 195        .PARAMETER Scheme
 196            The URI scheme (e.g., 'http' or 'https') if applicable.
 197        .PARAMETER Uri
 198            The resolved URI if Mode is 'Uri'.
 199        .PARAMETER HostName
 200            The resolved HostName if Mode is 'HostName'.
 201        .PARAMETER Port
 202            The resolved Port if Mode is 'PortIPAddress'.
 203        .PARAMETER IPAddress
 204            The resolved IPAddress if Mode is 'PortIPAddress'.
 205        .PARAMETER RawUrl
 206            The original URL string used for logging and diagnostics, especially when the binding was derived from envir
 207        .OUTPUTS
 208            A PSCustomObject containing the binding resolution details.
 209        #>
 210        [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
 211        param(
 212            [Parameter(Mandatory)][ValidateSet('Uri', 'HostName', 'PortIPAddress')] [string]$Mode,
 213            [Parameter(Mandatory)][string]$Source,
 214            [string]$Scheme,
 215            [uri]$Uri,
 216            [string]$HostName,
 217            [int]$Port,
 218            [System.Net.IPAddress]$IPAddress,
 219            [string]$RawUrl
 220        )
 221
 1222        [pscustomobject]@{
 1223            Mode = $Mode
 1224            Source = $Source
 1225            Scheme = $Scheme
 1226            Uri = $Uri
 1227            HostName = $HostName
 1228            Port = $Port
 1229            IPAddress = $IPAddress
 2230            EndpointNames = @(Get-KrEndpointName -Mode $Mode -Uri $Uri -HostName $HostName -Port $Port -IPAddress $IPAdd
 1231            RawUrl = $RawUrl
 232        }
 233    }
 234
 235    function Convert-KrUrlToBinding {
 236        <#
 237        .SYNOPSIS
 238            Converts a URL string into a structured binding result object.
 239        .DESCRIPTION
 240            This function takes a URL string, validates it, and extracts the relevant components to create a standardize
 241            It handles special cases for wildcard hosts and ensures that the URL is well-formed and contains the necessa
 242        .PARAMETER Url
 243            The URL string to convert into a binding result.
 244        .PARAMETER Source
 245            A string indicating the source of the URL, used for logging and diagnostics.
 246        .OUTPUTS
 247            A PSCustomObject containing the binding resolution details derived from the URL.
 248        #>
 249        param(
 250            [Parameter(Mandatory)][string]$Url,
 251            [string]$Source = 'Environment'
 252        )
 253
 1254        $trimmed = $Url.Trim()
 1255        if ([string]::IsNullOrWhiteSpace($trimmed)) {
 0256            throw 'Binding URL is empty.'
 257        }
 258
 259        # Handle ASP.NET-style wildcard hosts that [uri] doesn't like directly.
 1260        if ($trimmed -match '^(?<scheme>https?)://(?<host>\+|\*|\[[^\]]+\]|[^:/]+)(:(?<port>\d+))?/?$') {
 1261            $scheme = $Matches['scheme']
 1262            $hostname = $Matches['host']
 1263            $portText = $Matches['port']
 264
 1265            if (-not $portText) {
 0266                throw "Binding URL '$trimmed' does not specify a port."
 267            }
 268
 1269            $resolvedPort = [int]$portText
 270
 1271            switch ($hostname) {
 272                '+' {
 2273                    return New-KrBindingResult -Mode 'PortIPAddress' -Source $Source -Port $resolvedPort -IPAddress ([Sy
 274                }
 275                '*' {
 0276                    return New-KrBindingResult -Mode 'PortIPAddress' -Source $Source -Port $resolvedPort -IPAddress ([Sy
 277                }
 278                '0.0.0.0' {
 0279                    return New-KrBindingResult -Mode 'PortIPAddress' -Source $Source -Port $resolvedPort -IPAddress ([Sy
 280                }
 281                '::' {
 0282                    return New-KrBindingResult -Mode 'PortIPAddress' -Source $Source -Port $resolvedPort -IPAddress ([Sy
 283                }
 284                'localhost' {
 1285                    return New-KrBindingResult -Mode 'HostName' -Source $Source -HostName 'localhost' -Port $resolvedPor
 286                }
 287                default {
 0288                    $parsedIp = $null
 0289                    if ([System.Net.IPAddress]::TryParse($hostname.Trim('[', ']'), [ref]$parsedIp)) {
 0290                        return New-KrBindingResult -Mode 'PortIPAddress' -Source $Source -Port $resolvedPort -IPAddress 
 291                    }
 292
 0293                    return New-KrBindingResult -Mode 'HostName' -Source $Source -HostName $hostname -Port $resolvedPort 
 294                }
 295            }
 296        }
 297
 298        try {
 0299            $parsedUri = [uri]$trimmed
 300        } catch {
 0301            throw "Invalid binding URL '$trimmed'. $($_.Exception.Message)"
 302        }
 303
 0304        if (-not $parsedUri.IsAbsoluteUri) {
 0305            throw "Binding URL '$trimmed' must be an absolute URI."
 306        }
 307
 0308        if ($parsedUri.Port -lt 0) {
 0309            throw "Binding URL '$trimmed' must specify a port."
 310        }
 311
 0312        return New-KrBindingResult -Mode 'Uri' -Source $Source -Scheme $parsedUri.Scheme -Uri $parsedUri -RawUrl $trimme
 313    }
 314
 1315    $hasUri = $BoundParameters.ContainsKey('Uri')
 1316    $hasHostName = $BoundParameters.ContainsKey('HostName')
 1317    $hasPort = $BoundParameters.ContainsKey('Port')
 1318    $hasIPAddress = $BoundParameters.ContainsKey('IPAddress')
 319
 320    # 1. Explicit URI
 1321    if ($hasUri) {
 1322        return New-KrBindingResult -Mode 'Uri' -Source 'Explicit' -Uri $Uri -RawUrl $Uri.AbsoluteUri
 323    }
 324
 325    # 2. Explicit HostName
 1326    if ($hasHostName) {
 3327        $effectivePort = if ($hasPort) { $Port } elseif ($Port -gt 0) { $Port } else { $DefaultPort }
 1328        return New-KrBindingResult -Mode 'HostName' -Source 'Explicit' -HostName $HostName -Port $effectivePort
 329    }
 330
 331    # 3. Explicit Port/IPAddress
 1332    if ($hasPort -or $hasIPAddress) {
 2333        $effectivePort = if ($hasPort) { $Port } elseif ($Port -gt 0) { $Port } else { $DefaultPort }
 4334        $effectiveIP = if ($hasIPAddress) { $IPAddress } elseif ($null -ne $IPAddress) { $IPAddress } else { $DefaultIPA
 335
 1336        return New-KrBindingResult -Mode 'PortIPAddress' -Source 'Explicit' -Port $effectivePort -IPAddress $effectiveIP
 337    }
 338
 339    # 4. Environment: ASPNETCORE_URLS
 1340    if (-not $IgnoreEnvironment) {
 1341        $aspnetcoreUrls = [Environment]::GetEnvironmentVariable('ASPNETCORE_URLS')
 1342        if (-not [string]::IsNullOrWhiteSpace($aspnetcoreUrls)) {
 5343            $firstUrl = ($aspnetcoreUrls -split '\s*;\s*' | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Sel
 1344            if ($firstUrl) {
 1345                return Convert-KrUrlToBinding -Url $firstUrl -Source 'Environment:ASPNETCORE_URLS'
 346            }
 347        }
 348
 349        # 5. Environment: PORT
 1350        $portValue = [Environment]::GetEnvironmentVariable('PORT')
 1351        if (-not [string]::IsNullOrWhiteSpace($portValue)) {
 1352            $parsedPort = 0
 1353            if (-not [int]::TryParse($portValue, [ref]$parsedPort) -or $parsedPort -le 0 -or $parsedPort -gt 65535) {
 1354                throw "Environment variable PORT has invalid value '$portValue'. Expected an integer between 1 and 65535
 355            }
 356
 2357            return New-KrBindingResult -Mode 'PortIPAddress' -Source 'Environment:PORT' -Port $parsedPort -IPAddress ([S
 358        }
 359    }
 360
 361    # 6. Defaults
 2362    $fallbackPort = if ($Port -gt 0) { $Port } else { $DefaultPort }
 2363    $fallbackIp = if ($null -ne $IPAddress) { $IPAddress } else { $DefaultIPAddress }
 364
 1365    return New-KrBindingResult -Mode 'PortIPAddress' -Source 'Default' -Port $fallbackPort -IPAddress $fallbackIp
 366}