-
Notifications
You must be signed in to change notification settings - Fork 695
/
EnhancedHttpRetryHelper.cs
176 lines (144 loc) · 8.22 KB
/
EnhancedHttpRetryHelper.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System;
using NuGet.Common;
namespace NuGet.Protocol
{
/// <summary>
/// Represents a helper class for determining if enhanced retry is enabled and what settings to use.
/// </summary>
internal class EnhancedHttpRetryHelper
{
/// <summary>
/// The default delay in milliseconds between retries.
/// </summary>
public const int DefaultDelayMilliseconds = 1000;
/// <summary>
/// The default value indicating whether or not enhanced HTTP retry is enabled.
/// </summary>
public const bool DefaultEnabled = true;
/// <summary>
/// The default number of times to retry.
/// </summary>
public const int DefaultRetryCount = 6;
/// <summary>
/// The default value indicating whether or not to retry HTTP 429 responses.
/// </summary>
public const bool DefaultRetry429 = true;
/// <summary>
/// The default value indicating whether or not to observe Retry-After headers on responses.
/// </summary>
public const bool DefaultObserveRetryAfter = true;
/// <summary>
/// The environment variable used to change the delay value.
/// </summary>
public const string DelayInMillisecondsEnvironmentVariableName = "NUGET_ENHANCED_NETWORK_RETRY_DELAY_MILLISECONDS";
/// <summary>
/// The environment variable used to enable or disable the enhanced HTTP retry.
/// </summary>
public const string IsEnabledEnvironmentVariableName = "NUGET_ENABLE_ENHANCED_HTTP_RETRY";
/// <summary>
/// The environment variable used to change the retry value.
/// </summary>
public const string RetryCountEnvironmentVariableName = "NUGET_ENHANCED_MAX_NETWORK_TRY_COUNT";
/// <summary>
/// The environment variabled to to disable retrying HTTP 429 responses.
/// </summary>
public const string Retry429EnvironmentVariableName = "NUGET_RETRY_HTTP_429";
/// <summary>
/// The envionment variable to disable observing Retry-After responses.
/// </summary>
public const string ObserveRetryAfterEnvironmentVariableName = "NUGET_OBSERVE_RETRY_AFTER";
/// <summary>
/// The environment variable used to set maximum Retry-After delay period
/// </summary>
public const string MaximumRetryAfterDurationEnvironmentVariableName = "NUGET_MAX_RETRY_AFTER_DELAY_SECONDS";
private readonly IEnvironmentVariableReader _environmentVariableReader;
private bool? _isEnabled = null;
private int? _retryCount = null;
private int? _delayInMilliseconds = null;
private bool? _retry429 = null;
private bool? _observeRetryAfter = null;
private TimeSpan? _maxRetyAfterDelay = null;
/// <summary>
/// Initializes a new instance of the <see cref="EnhancedHttpRetryHelper" /> class.
/// </summary>
/// <param name="environmentVariableReader">A <see cref="IEnvironmentVariableReader" /> to use when reading environment variables.</param>
/// <exception cref="ArgumentNullException"><paramref name="environmentVariableReader" /> is <see langword="null" />.</exception>
public EnhancedHttpRetryHelper(IEnvironmentVariableReader environmentVariableReader)
{
_environmentVariableReader = environmentVariableReader ?? throw new ArgumentNullException(nameof(environmentVariableReader));
}
/// <summary>
/// Gets a value indicating whether or not enhanced HTTP retry should be enabled. The default value is <see langword="true" />.
/// </summary>
internal bool IsEnabled => _isEnabled ??= GetBoolFromEnvironmentVariable(IsEnabledEnvironmentVariableName, defaultValue: DefaultEnabled, _environmentVariableReader);
/// <summary>
/// Gets a value indicating the maximum number of times to retry. The default value is 6.
/// </summary>
internal int RetryCount => _retryCount ??= GetIntFromEnvironmentVariable(RetryCountEnvironmentVariableName, defaultValue: DefaultRetryCount, _environmentVariableReader);
/// <summary>
/// Gets a value indicating the delay in milliseconds to wait before retrying a connection. The default value is 1000.
/// </summary>
internal int DelayInMilliseconds => _delayInMilliseconds ??= GetIntFromEnvironmentVariable(DelayInMillisecondsEnvironmentVariableName, defaultValue: DefaultDelayMilliseconds, _environmentVariableReader);
/// <summary>
/// Gets a value indicating whether or not retryable HTTP 4xx responses should be retried.
/// </summary>
internal bool Retry429 => _retry429 ??= GetBoolFromEnvironmentVariable(Retry429EnvironmentVariableName, defaultValue: true, _environmentVariableReader);
/// <summary>
/// Gets a value indicating whether or not to observe the Retry-After header on HTTP responses.
/// </summary>
internal bool ObserveRetryAfter => _observeRetryAfter ??= GetBoolFromEnvironmentVariable(ObserveRetryAfterEnvironmentVariableName, defaultValue: DefaultObserveRetryAfter, _environmentVariableReader);
internal TimeSpan MaxRetryAfterDelay
{
get
{
if (_maxRetyAfterDelay == null)
{
int maxRetryAfterDelay = GetIntFromEnvironmentVariable(MaximumRetryAfterDurationEnvironmentVariableName, defaultValue: (int)TimeSpan.FromHours(1).TotalSeconds, _environmentVariableReader);
_maxRetyAfterDelay = TimeSpan.FromSeconds(maxRetryAfterDelay);
}
return _maxRetyAfterDelay.Value;
}
}
/// <summary>
/// Gets a <see cref="bool" /> value from the specified environment variable.
/// </summary>
/// <param name="variableName">The name of the environment variable to get the value.</param>
/// <param name="defaultValue">The default value to return if the environment variable is not defined or is not a valid <see cref="bool" />.</param>
/// <param name="environmentVariableReader">An <see cref="IEnvironmentVariableReader" /> to use when reading the environment variable.</param>
/// <returns>The value of the specified as a <see cref="bool" /> if the specified environment variable is defined and is a valid value for <see cref="bool" />.</returns>
private static bool GetBoolFromEnvironmentVariable(string variableName, bool defaultValue, IEnvironmentVariableReader environmentVariableReader)
{
try
{
if (bool.TryParse(environmentVariableReader.GetEnvironmentVariable(variableName), out bool parsedValue))
{
return parsedValue;
}
}
catch (Exception) { }
return defaultValue;
}
/// <summary>
/// Gets an <see cref="int" /> value from the specified environment variable.
/// </summary>
/// <param name="variableName">The name of the environment variable to get the value.</param>
/// <param name="defaultValue">The default value to return if the environment variable is not defined or is not a valid <see cref="int" />.</param>
/// <param name="environmentVariableReader">An <see cref="IEnvironmentVariableReader" /> to use when reading the environment variable.</param>
/// <returns>The value of the specified as a <see cref="int" /> if the specified environment variable is defined and is a valid value for <see cref="int" />.</returns>
private static int GetIntFromEnvironmentVariable(string variableName, int defaultValue, IEnvironmentVariableReader environmentVariableReader)
{
try
{
if (int.TryParse(environmentVariableReader.GetEnvironmentVariable(variableName), out int parsedValue) && parsedValue >= 0)
{
return parsedValue;
}
}
catch (Exception) { }
return defaultValue;
}
}
}