Public/Network/Get-HTTPResponse.ps1
function Get-HTTPResponse { [CmdletBinding()] param ( # Parameter help description [Parameter(Mandatory = $true)] [Uri]$Uri, # Parameter help description [Parameter(Mandatory = $false)] [bool]$HeaderOnly, # Parameter help description [Parameter(Mandatory = $false)] [bool]$DisableRedirect, # Parameter help description [Parameter(Mandatory = $false)] [bool]$DisableFeedCredential ) begin { $cts = New-Object System.Threading.CancellationTokenSource $downloadScript = { $HttpClient = $null try { # HttpClient is used vs Invoke-WebRequest in order to support Nano Server which doesn't support the Invoke-WebRequest cmdlet. try { Add-Type -Assembly System.Net.Http | Out-Null } catch { $null } if (!$ProxyAddress) { try { # Despite no proxy being explicitly specified, we may still be behind a default proxy $DefaultProxy = [System.Net.WebRequest]::DefaultWebProxy; if ($DefaultProxy -and (!$DefaultProxy.IsBypassed($Uri))) { if ($null -ne $DefaultProxy.GetProxy($Uri)) { $ProxyAddress = $DefaultProxy.GetProxy($Uri).OriginalString } else { $ProxyAddress = $null } $ProxyUseDefaultCredentials = $true } } catch { # Eat the exception and move forward as the above code is an attempt # at resolving the DefaultProxy that may not have been a problem. $ProxyAddress = $null Out-Verbose("Exception ignored: $_.Exception.Message - moving forward...") } } $HttpClientHandler = New-Object System.Net.Http.HttpClientHandler if ($ProxyAddress) { $HttpClientHandler.Proxy = New-Object System.Net.WebProxy -Property @{ Address = $ProxyAddress; UseDefaultCredentials = $ProxyUseDefaultCredentials; BypassList = $ProxyBypassList; } } if ($DisableRedirect) { $HttpClientHandler.AllowAutoRedirect = $false } $HttpClient = New-Object System.Net.Http.HttpClient -ArgumentList $HttpClientHandler # Default timeout for HttpClient is 100s. For a 50 MB download this assumes 500 KB/s average, any less will time out # Defaulting to 20 minutes allows it to work over much slower connections. $HttpClient.Timeout = New-TimeSpan -Seconds $DownloadTimeout if ($HeaderOnly) { $completionOption = [System.Net.Http.HttpCompletionOption]::ResponseHeadersRead } else { $completionOption = [System.Net.Http.HttpCompletionOption]::ResponseContentRead } if ($DisableFeedCredential) { $UriWithCredential = $Uri } else { $UriWithCredential = "${Uri}${FeedCredential}" } $Task = $HttpClient.GetAsync("$UriWithCredential", $completionOption).ConfigureAwait("false"); $Response = $Task.GetAwaiter().GetResult(); if (($null -eq $Response) -or ((!$HeaderOnly) -and (!($Response.IsSuccessStatusCode)))) { # The feed credential is potentially sensitive info. Do not log FeedCredential to console output. $DownloadException = [System.Exception] "Unable to download $Uri." if ($null -ne $Response) { $DownloadException.Data["StatusCode"] = [int] $Response.StatusCode $DownloadException.Data["ErrorMessage"] = "Unable to download $Uri. Returned HTTP status code: " + $DownloadException.Data["StatusCode"] if (404 -eq [int] $Response.StatusCode) { $cts.Cancel() } } throw $DownloadException } $response } catch [System.Net.Http.HttpRequestException] { $DownloadException = [System.Exception] "Unable to download $Uri." # Pick up the exception message and inner exceptions' messages if they exist $CurrentException = $PSItem.Exception $ErrorMsg = $CurrentException.Message + "`r`n" while ($CurrentException.InnerException) { $CurrentException = $CurrentException.InnerException $ErrorMsg += $CurrentException.Message + "`r`n" } # Check if there is an issue concerning TLS. if ($ErrorMsg -like "*SSL/TLS*") { $ErrorMsg += "Ensure that TLS 1.2 or higher is enabled to use this script.`r`n" } $DownloadException.Data["ErrorMessage"] = $ErrorMsg throw $DownloadException } finally { if ($null -ne $HttpClient) { $HttpClient.Dispose() } } } } process { try { return Invoke-RetriableCommand $downloadScript $cts.Token } finally { if ($null -ne $cts) { $cts.Dispose() } } } end { } } |