Private/Test-EFHttpEndpoint.ps1
|
function Test-EFHttpEndpoint { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [uri]$Uri, [ValidateRange(100, 30000)] [int]$TimeoutMilliseconds = 5000, [ValidateSet('Head', 'Get')] [string]$Method = 'Head', [switch]$AllowRedirects ) $checkScript = { param($InputData) $handler = $null $client = $null $cancellation = $null try { $null = Add-Type -AssemblyName System.Net.Http -ErrorAction Stop $handler = [Net.Http.HttpClientHandler]::new() $handler.AllowAutoRedirect = $false $handler.UseCookies = $false $handler.UseDefaultCredentials = $false $handler.Credentials = $null $handler.UseProxy = $true $handler.Proxy = $null $handler.DefaultProxyCredentials = $null $client = [Net.Http.HttpClient]::new($handler) $client.Timeout = [Threading.Timeout]::InfiniteTimeSpan $httpMethod = if ([string]$InputData.Method -eq 'Get') { [Net.Http.HttpMethod]::Get } else { [Net.Http.HttpMethod]::Head } $currentUri = [uri][string]$InputData.Uri $startingUri = $currentUri $cancellation = [Threading.CancellationTokenSource]::new([int]$InputData.TimeoutMilliseconds) for ($redirectCount = 0; $redirectCount -le 5; $redirectCount++) { $request = $null $response = $null try { $request = [Net.Http.HttpRequestMessage]::new($httpMethod, $currentUri) $response = $client.SendAsync( $request, [Net.Http.HttpCompletionOption]::ResponseHeadersRead, $cancellation.Token ).GetAwaiter().GetResult() $statusCode = [int]$response.StatusCode $isRedirect = $statusCode -in @(301, 302, 303, 307, 308) $location = $response.Headers.Location if (-not [bool]$InputData.AllowRedirects -or -not $isRedirect -or $null -eq $location) { return [pscustomobject]@{ Responded = $true StatusCode = $statusCode FailureReason = 'None' IsEvaluationError = $false } } if ($redirectCount -ge 5) { return [pscustomobject]@{ Responded = $false StatusCode = $statusCode FailureReason = 'TooManyRedirects' IsEvaluationError = $true } } $nextUri = if ($location.IsAbsoluteUri) { $location } else { [uri]::new($currentUri, $location) } if ($nextUri.AbsoluteUri.Length -gt 2048 -or -not [string]::IsNullOrWhiteSpace($nextUri.UserInfo) -or -not [string]::IsNullOrWhiteSpace($nextUri.Query) -or -not [string]::IsNullOrWhiteSpace($nextUri.Fragment) -or [System.Management.Automation.WildcardPattern]::ContainsWildcardCharacters($nextUri.AbsoluteUri)) { return [pscustomobject]@{ Responded = $false StatusCode = $statusCode FailureReason = 'UnsafeRedirectBlocked' IsEvaluationError = $true } } if ($nextUri.Scheme -ine $startingUri.Scheme -or $nextUri.Host -ine $startingUri.Host -or $nextUri.Port -ne $startingUri.Port) { return [pscustomobject]@{ Responded = $false StatusCode = $statusCode FailureReason = 'CrossOriginRedirectBlocked' IsEvaluationError = $true } } $currentUri = $nextUri } finally { if ($null -ne $response) { $response.Dispose() } if ($null -ne $request) { $request.Dispose() } } } } catch [OperationCanceledException] { return [pscustomobject]@{ Responded = $false StatusCode = $null FailureReason = 'Timeout' IsEvaluationError = $false } } catch { $socketError = $null $webStatus = $null $currentError = $_.Exception while ($null -ne $currentError) { if ($currentError -is [Net.Sockets.SocketException]) { $socketError = $currentError.SocketErrorCode break } if ($currentError -is [Net.WebException]) { $webStatus = $currentError.Status } $currentError = $currentError.InnerException } $isDefiniteConnectionFailure = $socketError -in @( [Net.Sockets.SocketError]::ConnectionRefused, [Net.Sockets.SocketError]::TimedOut ) -or $webStatus -eq [Net.WebExceptionStatus]::Timeout return [pscustomobject]@{ Responded = $false StatusCode = $null FailureReason = if ($isDefiniteConnectionFailure) { 'ConnectionFailed' } else { 'RequestFailed' } IsEvaluationError = -not $isDefiniteConnectionFailure } } finally { if ($null -ne $client) { $client.Dispose() } if ($null -ne $handler) { $handler.Dispose() } if ($null -ne $cancellation) { $cancellation.Dispose() } } } Invoke-EFIsolatedCheck -ScriptBlock $checkScript -InputData @{ Uri = $Uri.AbsoluteUri TimeoutMilliseconds = $TimeoutMilliseconds Method = $Method AllowRedirects = [bool]$AllowRedirects } -TimeoutMilliseconds $TimeoutMilliseconds -StartupAllowanceMilliseconds 3000 ` -Activity 'The HTTP endpoint check' } |