Public/Network/Get-AkaMSDownloadLink.ps1

# ($DownloadLink, $SpecificVersion, $EffectiveVersion) = Get-AkaMsLink-And-Version $NormalizedChannel $NormalizedQuality $Internal $NormalizedProduct $CLIArchitecture
function Get-AkaMSDownloadLink([string]$Channel, [string]$Quality, [bool]$Internal, [string]$Product, [string]$Architecture) {
  # Get-AkaMSDownloadLink -Channel $NormalizedChannel -Quality $NormalizedQuality -Internal $Internal -Product $ProductName -Architecture $Architecture
  Write-Invocation $MyInvocation

  #quality is not supported for LTS or current channel
  if (![string]::IsNullOrEmpty($Quality) -and (@("LTS", "current") -contains $Channel)) {
    $Quality = ""
    Out-Warning "Specifying quality for current or LTS channel is not supported, the quality will be ignored."
  }
  Out-Verbose "Retrieving primary payload URL from aka.ms link for channel: '$Channel', quality: '$Quality' product: '$Product', os: 'win', architecture: '$Architecture'."
  #construct aka.ms link
  $akaMsLink = "https://aka.ms/dotnet"
  if ($Internal) {
    $akaMsLink += "/internal"
  }
  $akaMsLink += "/$Channel"
  if (![string]::IsNullOrEmpty($Quality)) {
    $akaMsLink += "/$Quality"
  }
  $akaMsLink += "/$Product-win-$Architecture.zip"
  Out-Verbose "Constructed aka.ms link: '$akaMsLink'."
  $akaMsDownloadLink = $null

  for ($maxRedirections = 9; $maxRedirections -ge 0; $maxRedirections--) {
    #get HTTP response
    #do not pass credentials as a part of the $akaMsLink and do not apply credentials in the GetHTTPResponse function
    #otherwise the redirect link would have credentials as well
    #it would result in applying credentials twice to the resulting link and thus breaking it, and in echoing credentials to the output as a part of redirect link
    $Response = GetHTTPResponse -Uri $akaMsLink -HeaderOnly $true -DisableRedirect $true -DisableFeedCredential $true
    Out-Verbose "Received response:`n$Response"

    if ([string]::IsNullOrEmpty($Response)) {
      Out-Verbose "The link '$akaMsLink' is not valid: failed to get redirect location. The resource is not available."
      return $null
    }

    #if HTTP code is 301 (Moved Permanently), the redirect link exists
    if ($Response.StatusCode -eq 301) {
      try {
        $akaMsDownloadLink = $Response.Headers.GetValues("Location")[0]

        if ([string]::IsNullOrEmpty($akaMsDownloadLink)) {
          Out-Verbose "The link '$akaMsLink' is not valid: server returned 301 (Moved Permanently), but the headers do not contain the redirect location."
          return $null
        }

        Out-Verbose "The redirect location retrieved: '$akaMsDownloadLink'."
        # This may yet be a link to another redirection. Attempt to retrieve the page again.
        $akaMsLink = $akaMsDownloadLink
        continue
      } catch {
        Out-Verbose "The link '$akaMsLink' is not valid: failed to get redirect location."
        return $null
      }
    } elseif ((($Response.StatusCode -lt 300) -or ($Response.StatusCode -ge 400)) -and (![string]::IsNullOrEmpty($akaMsDownloadLink))) {
      # Redirections have ended.
      return $akaMsDownloadLink
    }

    Out-Verbose "The link '$akaMsLink' is not valid: failed to retrieve the redirection location."
    return $null
  }

  Out-Verbose "Aka.ms links have redirected more than the maximum allowed redirections. This may be caused by a cyclic redirection of aka.ms links."
  return $null
}

function Get-NormalizedProduct([string]$Runtime) {
  Write-Invocation $MyInvocation
  switch ($Runtime) {
    { $_ -eq "dotnet" } { return "dotnet-runtime" }
    { $_ -eq "aspnetcore" } { return "aspnetcore-runtime" }
    { $_ -eq "windowsdesktop" } { return "windowsdesktop-runtime" }
    { [string]::IsNullOrEmpty($_) } { return "dotnet-sdk" }
    default { throw "'$Runtime' is not a supported value for -Runtime option, supported values are: dotnet, aspnetcore, windowsdesktop. If you think this is a bug, report it at https://github.com/dotnet/install-scripts/issues." }
  }
}


function Get-AkaMsLink-And-Version([string] $NormalizedChannel, [string] $NormalizedQuality, [bool] $Internal, [string] $ProductName, [string] $Architecture) {
  $AkaMsDownloadLink = Get-AkaMSDownloadLink -Channel $NormalizedChannel -Quality $NormalizedQuality -Internal $Internal -Product $ProductName -Architecture $Architecture
  if ([string]::IsNullOrEmpty($AkaMsDownloadLink)) {
    if (![string]::IsNullOrEmpty($NormalizedQuality)) {
      # if quality is specified - exit with error - there is no fallback approach
      Out-Verbose $fxn "Failed to locate the latest version in the channel '$NormalizedChannel' with '$NormalizedQuality' quality for '$ProductName', os: 'win', architecture: '$Architecture'."
      Out-Verbose $fxn "Refer to: https://aka.ms/dotnet-os-lifecycle for information on .NET Core support."
      throw "aka.ms link resolution failure"
    }
    Out-Verbose "Falling back to latest.version file approach."
    return ($null, $null, $null)
  } else {
    Out-Verbose "Retrieved primary named payload URL from aka.ms link: '$AkaMsDownloadLink'."
    Out-Verbose "Downloading using legacy url will not be attempted."

    #get version from the path
    $pathParts = $AkaMsDownloadLink.Split('/')
    if ($pathParts.Length -ge 2) {
      $SpecificVersion = $pathParts[$pathParts.Length - 2]
      Out-Verbose "Version: '$SpecificVersion'."
    } else {
      Out-Verbose $fxn "Failed to extract the version from download link '$AkaMsDownloadLink'."
      return ($null, $null, $null)
    }

    #retrieve effective (product) version
    $EffectiveVersion = Get-Product-Version -SpecificVersion $SpecificVersion -PackageDownloadLink $AkaMsDownloadLink
    Out-Verbose "Product version: '$EffectiveVersion'."

    return ($AkaMsDownloadLink, $SpecificVersion, $EffectiveVersion);
  }
}