Private/HeadersAndRateLimit.ps1
|
function ConvertFrom-DattoApiHeaders { [CmdletBinding()] param( [Parameter(Mandatory)] [System.Net.Http.HttpResponseMessage]$Response ) $headers = [ordered]@{} foreach ($header in $Response.Headers) { $headers[[string]$header.Key] = (@($header.Value) -join ', ') } if ($null -ne $Response.Content) { foreach ($header in $Response.Content.Headers) { $headers[[string]$header.Key] = (@($header.Value) -join ', ') } } return $headers } function Get-DattoApiHeaderValue { [CmdletBinding()] param( [Parameter(Mandatory)] [System.Collections.IDictionary]$Headers, [Parameter(Mandatory)] [string]$Name ) foreach ($key in $Headers.Keys) { if ([string]$key -ieq $Name) { return [string]$Headers[$key] } } return $null } function ConvertFrom-DattoApiEpochSecondsInternal { [CmdletBinding()] param( [Parameter(Mandatory)] [long]$Seconds ) return ([datetimeoffset]'1970-01-01T00:00:00+00:00').AddSeconds($Seconds) } function Update-DattoApiRateLimit { [CmdletBinding()] param( [Parameter(Mandatory)] [System.Collections.IDictionary]$ConnectionState, [Parameter(Mandatory)] [System.Collections.IDictionary]$Headers ) $remainingText = Get-DattoApiHeaderValue -Headers $Headers -Name 'X-API-Limit-Remaining' $resetText = Get-DattoApiHeaderValue -Headers $Headers -Name 'X-API-Limit-Resets' $costText = Get-DattoApiHeaderValue -Headers $Headers -Name 'X-API-Limit-Cost' $remaining = 0L $resetEpoch = 0L $cost = 0L $hasAny = $false if (-not [string]::IsNullOrWhiteSpace($remainingText) -and [long]::TryParse($remainingText, [ref]$remaining)) { $ConnectionState.RateLimit.Remaining = $remaining $hasAny = $true } if (-not [string]::IsNullOrWhiteSpace($resetText) -and [long]::TryParse($resetText, [ref]$resetEpoch)) { $ConnectionState.RateLimit.ResetEpoch = $resetEpoch try { $ConnectionState.RateLimit.ResetAtUtc = ConvertFrom-DattoApiEpochSecondsInternal -Seconds $resetEpoch } catch { $ConnectionState.RateLimit.ResetAtUtc = $null } $hasAny = $true } if (-not [string]::IsNullOrWhiteSpace($costText) -and [long]::TryParse($costText, [ref]$cost)) { $ConnectionState.RateLimit.Cost = $cost $hasAny = $true } if ($hasAny) { $ConnectionState.RateLimit.ObservedAtUtc = [datetimeoffset]::UtcNow } } function Get-DattoApiRetryDelayMilliseconds { [CmdletBinding()] param( [Parameter(Mandatory)] [int]$Attempt, [Parameter(Mandatory)] [System.Collections.IDictionary]$Headers, [Parameter(Mandatory)] [int]$MaximumDelaySeconds, [Parameter()] [switch]$UseRateLimitReset ) $delaySeconds = $null $retryAfter = Get-DattoApiHeaderValue -Headers $Headers -Name 'Retry-After' $seconds = 0 if (-not [string]::IsNullOrWhiteSpace($retryAfter)) { if ([int]::TryParse($retryAfter, [ref]$seconds)) { $delaySeconds = [double]$seconds } else { $retryDate = [datetimeoffset]::MinValue $dateStyles = [Globalization.DateTimeStyles]::AssumeUniversal -bor [Globalization.DateTimeStyles]::AdjustToUniversal if ([datetimeoffset]::TryParse($retryAfter, [Globalization.CultureInfo]::InvariantCulture, $dateStyles, [ref]$retryDate)) { $delaySeconds = [Math]::Max(0, ($retryDate - [datetimeoffset]::UtcNow).TotalSeconds) } } } if ($null -eq $delaySeconds -and $UseRateLimitReset) { $resetText = Get-DattoApiHeaderValue -Headers $Headers -Name 'X-API-Limit-Resets' $resetEpoch = 0L if (-not [string]::IsNullOrWhiteSpace($resetText) -and [long]::TryParse($resetText, [ref]$resetEpoch)) { try { $resetDate = ConvertFrom-DattoApiEpochSecondsInternal -Seconds $resetEpoch $delaySeconds = [Math]::Max(0, ($resetDate - [datetimeoffset]::UtcNow).TotalSeconds) } catch { $delaySeconds = $null } } } if ($null -eq $delaySeconds) { $jitterMilliseconds = Get-Random -Minimum 0 -Maximum 500 $delaySeconds = [Math]::Pow(2, [Math]::Max(0, $Attempt - 1)) + ($jitterMilliseconds / 1000.0) } $delaySeconds = [Math]::Min([double]$MaximumDelaySeconds, [Math]::Max(0, [double]$delaySeconds)) return [int][Math]::Round($delaySeconds * 1000) } |