Private/Get-GuacResponseDetails.ps1
|
function Get-GuacErrorCategory { <# .SYNOPSIS Maps an HTTP status code to a PowerShell ErrorCategory. .DESCRIPTION Transport-level failures (status 0) map to ConnectionError. Authentication failures (401, 403) map to SecurityError. Client errors (404, 429, etc.) map to InvalidArgument. Server errors (500, 503, etc.) map to ResourceUnavailable. This function is private to the N2C.GuacAdmin module. #> [CmdletBinding()] [OutputType([System.Management.Automation.ErrorCategory])] param ( [Parameter(Mandatory = $true)] [int] $StatusCode ) switch ($StatusCode) { 0 { return [System.Management.Automation.ErrorCategory]::ConnectionError } 401 { return [System.Management.Automation.ErrorCategory]::SecurityError } 403 { return [System.Management.Automation.ErrorCategory]::SecurityError } 404 { return [System.Management.Automation.ErrorCategory]::InvalidArgument } 429 { return [System.Management.Automation.ErrorCategory]::InvalidArgument } 500 { return [System.Management.Automation.ErrorCategory]::ResourceUnavailable } 503 { return [System.Management.Automation.ErrorCategory]::ResourceUnavailable } default { if ($StatusCode -ge 500) { return [System.Management.Automation.ErrorCategory]::ResourceUnavailable } return [System.Management.Automation.ErrorCategory]::InvalidArgument } } } function Get-GuacResponseStatus { <# .SYNOPSIS Extracts the HTTP status code from a caught transport error. .DESCRIPTION Inspects the exception chain to find the underlying web response. In PowerShell 7.x, Invoke-WebRequest throws HttpRequestException with a Response property of type HttpResponseMessage. In Windows PowerShell 5.1, it throws WebException with Response of type HttpWebResponse. Returns 0 when the exception is transport-level (no response received). This function is private to the N2C.GuacAdmin module. #> [CmdletBinding()] [OutputType([int])] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.ErrorRecord] $ErrorRecord ) $exception = $ErrorRecord.Exception while ($null -ne $exception) { # Windows PowerShell 5.1: WebException with HttpWebResponse. try { if ($exception -is [System.Net.WebException] -and $null -ne $exception.Response) { $response = $exception.Response if ($response -is [System.Net.HttpWebResponse]) { return [int]$response.StatusCode } } } catch [System.Exception] { # Best effort; keep walking. } # PowerShell 7.x: HttpRequestException with HttpResponseMessage. # The type check must be wrapped because the type may not exist # in Windows PowerShell 5.1 (.NET Framework). try { $httpReqExType = [System.Net.Http.HttpRequestException] if ($exception -is $httpReqExType -and $null -ne $exception.Response) { return [int]$exception.Response.StatusCode } } catch [System.Exception] { # Type may not exist in PS 5.1; keep walking. } $exception = $exception.InnerException } return 0 } function Get-GuacResponseBody { <# .SYNOPSIS Extracts the raw response body text from a caught transport error. .DESCRIPTION Tries, in order: PowerShell's ErrorDetails.Message (populated by Invoke-WebRequest on HTTP error responses on both 5.1 and 7.x), the HttpResponseMessage content (PowerShell 7.x), and finally the underlying response stream (Windows PowerShell 5.1). Returns $null when no body is available (transport-level failures have no body). This function is private to the N2C.GuacAdmin module. #> [CmdletBinding()] [OutputType([string])] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.ErrorRecord] $ErrorRecord ) # First check ErrorDetails.Message (set by Invoke-WebRequest on HTTP errors) if ($null -ne $ErrorRecord.ErrorDetails) { try { $message = $ErrorRecord.ErrorDetails.Message if (-not [string]::IsNullOrEmpty($message)) { return $message } } catch [System.Exception] { # Best effort; keep going. } } $exception = $ErrorRecord.Exception while ($null -ne $exception) { $response = $null try { # Windows PowerShell 5.1: WebException with HttpWebResponse if ($exception -is [System.Net.WebException]) { $webEx = $exception if ($null -ne $webEx.Response) { $response = $webEx.Response } } # PowerShell 7.x: HttpRequestException with HttpResponseMessage elseif ($exception -is [System.Net.Http.HttpRequestException]) { $httpEx = $exception if ($null -ne $httpEx.Response) { $response = $httpEx.Response } } } catch [System.Exception] { # Best effort; keep walking. } if ($null -ne $response) { try { # PowerShell 7.x: HttpResponseMessage # The type check must be wrapped because the type may not exist # in Windows PowerShell 5.1 (.NET Framework). try { $httpRespMsgType = [System.Net.Http.HttpResponseMessage] if ($response -is $httpRespMsgType) { $task = $response.Content.ReadAsStringAsync() $task.Wait(2000) return $task.Result } } catch [System.Exception] { # Type may not exist in PS 5.1; fall through to WebResponse handling. } # Windows PowerShell 5.1: WebResponse if ($response -is [System.Net.WebResponse]) { $stream = $response.GetResponseStream() if ($null -ne $stream) { try { # Use UTF-8 encoding explicitly for PS 5.1 compatibility $reader = [System.IO.StreamReader]::new($stream, [System.Text.Encoding]::UTF8) try { $body = $reader.ReadToEnd() if (-not [string]::IsNullOrEmpty($body)) { return $body } } finally { $reader.Dispose() } } finally { $stream.Dispose() } } } } catch [System.Exception] { # Best effort; keep walking. } } $exception = $exception.InnerException } return $null } function Get-GuacResponseHeaderValue { <# .SYNOPSIS Looks up a response header value case-insensitively. .DESCRIPTION Response header collections differ between Windows PowerShell 5.1 (case-insensitive NameValueCollection) and PowerShell 7.x (case-sensitive OrderedDictionary), so the lookup scans the keys with an ordinal case-insensitive comparison. This function is private to the N2C.GuacAdmin module. #> [CmdletBinding()] [OutputType([string])] param ( [Parameter(Mandatory = $true)] [AllowNull()] [object] $Headers, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Name ) if ($null -eq $Headers) { return $null } try { foreach ($key in $Headers.Keys) { if ([string]$key -ieq $Name) { return [string]$Headers[$key] } } } catch [System.Exception] { # No Keys property or enumeration failed; return null. } # Fall back to direct indexer access (case-insensitive on 5.1). try { $value = $Headers[$Name] if ($null -ne $value) { return [string]$value } } catch [System.Exception] { # Indexer not available; return null. } return $null } |