Skip to content

Commit 908cc85

Browse files
committed
feat: use generated RetryStrategy for retrying requests (box/box-codegen#872)
1 parent 394656a commit 908cc85

File tree

26 files changed

+483
-371
lines changed

26 files changed

+483
-371
lines changed

.codegen.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{ "engineHash": "c4dd2eb", "specHash": "cf21406", "version": "6.0.0" }
1+
{ "engineHash": "4421f42", "specHash": "cf21406", "version": "6.0.0" }

BoxSdkGen/Box.Sdk.Gen.Net/Internal/Utils.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,5 +390,16 @@ internal static Dictionary<string, string> SanitizeMap(Dictionary<string, string
390390

391391
}
392392

393+
/// <summary>
394+
/// Generates a random Double value within the specified range..
395+
/// </summary>
396+
/// <param name="min">The minimum value of the range (inclusive).</param>
397+
/// <param name="max">The maximum value of the range (inclusive).</param>
398+
/// <returns>A random Double value between min and max.</returns>
399+
internal static Double Random(Double min, Double max)
400+
{
401+
var random = new System.Random();
402+
return random.NextDouble() * (max - min) + min;
403+
}
393404
}
394405
}

BoxSdkGen/Box.Sdk.Gen.Net/Networking/BoxNetworkClient/BoxNetworkClient.cs

Lines changed: 28 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ async Task<FetchResponse> INetworkClient.FetchAsync(FetchOptions options)
5151

5252
var client = GetOrCreateHttpClient(networkSession);
5353

54-
var attempt = 1;
54+
var retryAttempt = 1;
55+
var networkExceptionRetryAttempt = 1;
5556
var cancellationToken = options.CancellationToken ?? default(System.Threading.CancellationToken);
5657

5758
bool isStreamResponse = options.ResponseFormat == ResponseFormat.Binary;
@@ -74,20 +75,6 @@ async Task<FetchResponse> INetworkClient.FetchAsync(FetchOptions options)
7475

7576
var url = response.RequestMessage?.RequestUri?.ToString() ?? request.RequestUri?.ToString() ?? options.Url;
7677
var statusCode = (int)response.StatusCode;
77-
var isRetryAfterPresent = response.Headers.Contains("retry-after");
78-
var isRetryAfterWithAcceptedPresent = isRetryAfterPresent && statusCode == 202;
79-
80-
if (response.IsSuccessStatusCode && (!isRetryAfterWithAcceptedPresent || attempt >= networkSession.RetryAttempts))
81-
{
82-
seekableStream?.Dispose();
83-
return await ReadResponse(isStreamResponse, response, statusCode, url, cancellationToken).ConfigureAwait(false);
84-
}
85-
86-
if (attempt >= networkSession.RetryAttempts)
87-
{
88-
seekableStream?.Dispose();
89-
throw new BoxSdkException($"Max retry attempts excedeed.", DateTimeOffset.UtcNow);
90-
}
9178

9279
if (statusCode >= 300 & statusCode < 400)
9380
{
@@ -112,24 +99,26 @@ async Task<FetchResponse> INetworkClient.FetchAsync(FetchOptions options)
11299
{ Auth = sameOrigin ? options.Auth : null, NetworkSession = networkSession }).ConfigureAwait(false);
113100
}
114101

115-
if (statusCode == 401)
102+
if (statusCode == 407)
116103
{
117-
if (options.Auth != null)
118-
{
119-
await options.Auth.RefreshTokenAsync(networkSession).ConfigureAwait(false);
120-
}
104+
throw new BoxSdkException($"Proxy authorization required. Check provided credentials in proxy configuration.", DateTimeOffset.UtcNow);
121105
}
122-
else if (statusCode == 429 || statusCode >= 500 || isRetryAfterWithAcceptedPresent)
123-
{
124-
var retryTimeout = isRetryAfterPresent ?
125-
int.Parse(response.Headers.GetValues("retry-after").First()) :
126-
networkSession.RetryStrategy.GetRetryTimeout(attempt);
127106

128-
await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(retryTimeout)).ConfigureAwait(false);
107+
var fetchResponse = await ReadResponse(isStreamResponse, response, statusCode, url, cancellationToken).ConfigureAwait(false);
108+
109+
if (response.IsSuccessStatusCode)
110+
{
111+
seekableStream?.Dispose();
112+
return fetchResponse;
129113
}
130-
else if (statusCode == 407)
114+
115+
var shouldRetry = await networkSession.RetryStrategy.ShouldRetryAsync(options, fetchResponse, retryAttempt).ConfigureAwait(false);
116+
117+
if (shouldRetry)
131118
{
132-
throw new BoxSdkException($"Proxy authorization required. Check provided credentials in proxy configuration.", DateTimeOffset.UtcNow);
119+
var retryAfter = networkSession.RetryStrategy.RetryAfter(options, fetchResponse, retryAttempt);
120+
121+
await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(retryAfter)).ConfigureAwait(false);
133122
}
134123
else
135124
{
@@ -138,31 +127,36 @@ async Task<FetchResponse> INetworkClient.FetchAsync(FetchOptions options)
138127
}
139128

140129
response?.Dispose();
130+
131+
retryAttempt++;
141132
}
142133
else
143134
{
135+
var fetchResponse = new FetchResponse(0, new Dictionary<string, string>());
136+
var shouldRetry = await networkSession.RetryStrategy.ShouldRetryAsync(options, fetchResponse, networkExceptionRetryAttempt).ConfigureAwait(false);
144137
if (!result.IsRetryable)
145138
{
146139
seekableStream?.Dispose();
147140
throw new BoxSdkException($"Request was not retried. Inner exception: {result.Exception?.ToString()}", DateTimeOffset.UtcNow);
148141
}
149-
else if (attempt >= networkSession.RetryAttempts)
142+
else if (!shouldRetry)
150143
{
151144
seekableStream?.Dispose();
152145
throw new BoxSdkException($"Network error. Max retry attempts excedeed. {result.Exception?.ToString()}", DateTimeOffset.UtcNow);
153146
}
154147

155-
var retryTimeout = networkSession.RetryStrategy.GetRetryTimeout(attempt);
156-
await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(retryTimeout)).ConfigureAwait(false);
148+
var retryAfter = networkSession.RetryStrategy.RetryAfter(options, fetchResponse, networkExceptionRetryAttempt);
149+
150+
await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(retryAfter)).ConfigureAwait(false);
157151

158152
// reauth in case of terminated connection by the peer
159153
if (options.Auth != null)
160154
{
161155
await options.Auth.RefreshTokenAsync(networkSession).ConfigureAwait(false);
162156
}
163-
}
164157

165-
attempt++;
158+
networkExceptionRetryAttempt++;
159+
}
166160
}
167161

168162
}
@@ -261,7 +255,7 @@ private static async Task<Exception> BuildApiException(HttpRequestMessage reques
261255

262256
var dataSanitizer = options.NetworkSession?.DataSanitizer ?? new DataSanitizer();
263257

264-
return new BoxApiException(responseContent, DateTimeOffset.UtcNow, requestInfo, responseInfo) {DataSanitizer = dataSanitizer};
258+
return new BoxApiException(responseContent, DateTimeOffset.UtcNow, requestInfo, responseInfo) { DataSanitizer = dataSanitizer };
265259
}
266260

267261
private static async Task<HttpRequestMessage> BuildHttpRequest(FetchOptions options, Stream? stream)

BoxSdkGen/Box.Sdk.Gen.Net/Networking/DefaultNetworkClient/DefaultNetworkClient.cs

Lines changed: 31 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ async Task<FetchResponse> INetworkClient.FetchAsync(FetchOptions options)
6060

6161
var client = GetOrCreateHttpClient(networkSession);
6262

63-
var attempt = 1;
63+
var retryAttempt = 1;
64+
var networkExceptionRetryAttempt = 1;
6465
var cancellationToken = options.CancellationToken ?? default(System.Threading.CancellationToken);
6566

6667
bool isStreamResponse = options.ResponseFormat == ResponseFormat.Binary;
@@ -83,20 +84,6 @@ async Task<FetchResponse> INetworkClient.FetchAsync(FetchOptions options)
8384

8485
var url = response.RequestMessage?.RequestUri?.ToString() ?? request.RequestUri?.ToString() ?? options.Url;
8586
var statusCode = (int)response.StatusCode;
86-
var isRetryAfterPresent = response.Headers.Contains("retry-after");
87-
var isRetryAfterWithAcceptedPresent = isRetryAfterPresent && statusCode == 202;
88-
89-
if (response.IsSuccessStatusCode && (!isRetryAfterWithAcceptedPresent || attempt >= networkSession.RetryAttempts))
90-
{
91-
seekableStream?.Dispose();
92-
return await ReadResponse(isStreamResponse, response, statusCode, url, cancellationToken).ConfigureAwait(false);
93-
}
94-
95-
if (attempt >= networkSession.RetryAttempts)
96-
{
97-
seekableStream?.Dispose();
98-
throw new BoxSdkException($"Max retry attempts excedeed.", DateTimeOffset.UtcNow);
99-
}
10087

10188
if (statusCode >= 300 & statusCode < 400)
10289
{
@@ -114,28 +101,33 @@ async Task<FetchResponse> INetworkClient.FetchAsync(FetchOptions options)
114101
throw new BoxSdkException($"Redirect response missing Location header for: {options.Url}");
115102
}
116103

104+
var originUri = new Uri(url);
105+
var redirectUri = new Uri(locationHeader.Value.First());
106+
var sameOrigin = originUri.Host == redirectUri.Host && originUri.Port == redirectUri.Port && originUri.Scheme == redirectUri.Scheme;
117107
return await ((INetworkClient)this).FetchAsync(new FetchOptions(locationHeader.Value.First(), "GET", options.ContentType, options.ResponseFormat)
118-
{ Auth = options.Auth, NetworkSession = networkSession }).ConfigureAwait(false);
108+
{ Auth = sameOrigin ? options.Auth : null, NetworkSession = networkSession }).ConfigureAwait(false);
119109
}
120110

121-
if (statusCode == 401)
111+
if (statusCode == 407)
122112
{
123-
if (options.Auth != null)
124-
{
125-
await options.Auth.RefreshTokenAsync(networkSession).ConfigureAwait(false);
126-
}
113+
throw new BoxSdkException($"Proxy authorization required. Check provided credentials in proxy configuration.", DateTimeOffset.UtcNow);
127114
}
128-
else if (statusCode == 429 || statusCode >= 500 || isRetryAfterWithAcceptedPresent)
129-
{
130-
var retryTimeout = isRetryAfterPresent ?
131-
int.Parse(response.Headers.GetValues("retry-after").First()) :
132-
networkSession.RetryStrategy.GetRetryTimeout(attempt);
133115

134-
await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(retryTimeout)).ConfigureAwait(false);
116+
var fetchResponse = await ReadResponse(isStreamResponse, response, statusCode, url, cancellationToken).ConfigureAwait(false);
117+
118+
if (response.IsSuccessStatusCode)
119+
{
120+
seekableStream?.Dispose();
121+
return fetchResponse;
135122
}
136-
else if (statusCode == 407)
123+
124+
var shouldRetry = await networkSession.RetryStrategy.ShouldRetryAsync(options, fetchResponse, retryAttempt).ConfigureAwait(false);
125+
126+
if (shouldRetry)
137127
{
138-
throw new BoxSdkException($"Proxy authorization required. Check provided credentials in proxy configuration.", DateTimeOffset.UtcNow);
128+
var retryAfter = networkSession.RetryStrategy.RetryAfter(options, fetchResponse, retryAttempt);
129+
130+
await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(retryAfter)).ConfigureAwait(false);
139131
}
140132
else
141133
{
@@ -144,31 +136,36 @@ async Task<FetchResponse> INetworkClient.FetchAsync(FetchOptions options)
144136
}
145137

146138
response?.Dispose();
139+
140+
retryAttempt++;
147141
}
148142
else
149143
{
144+
var fetchResponse = new FetchResponse(0, new Dictionary<string, string>());
145+
var shouldRetry = await networkSession.RetryStrategy.ShouldRetryAsync(options, fetchResponse, networkExceptionRetryAttempt).ConfigureAwait(false);
150146
if (!result.IsRetryable)
151147
{
152148
seekableStream?.Dispose();
153149
throw new BoxSdkException($"Request was not retried. Inner exception: {result.Exception?.ToString()}", DateTimeOffset.UtcNow);
154150
}
155-
else if (attempt >= networkSession.RetryAttempts)
151+
else if (!shouldRetry)
156152
{
157153
seekableStream?.Dispose();
158154
throw new BoxSdkException($"Network error. Max retry attempts excedeed. {result.Exception?.ToString()}", DateTimeOffset.UtcNow);
159155
}
160156

161-
var retryTimeout = networkSession.RetryStrategy.GetRetryTimeout(attempt);
162-
await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(retryTimeout)).ConfigureAwait(false);
157+
var retryAfter = networkSession.RetryStrategy.RetryAfter(options, fetchResponse, networkExceptionRetryAttempt);
158+
159+
await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(retryAfter)).ConfigureAwait(false);
163160

164161
// reauth in case of terminated connection by the peer
165162
if (options.Auth != null)
166163
{
167164
await options.Auth.RefreshTokenAsync(networkSession).ConfigureAwait(false);
168165
}
169-
}
170166

171-
attempt++;
167+
networkExceptionRetryAttempt++;
168+
}
172169
}
173170

174171
}

BoxSdkGen/Box.Sdk.Gen.Net/Networking/ExponentialBackoffRetryStrategy.cs

Lines changed: 0 additions & 28 deletions
This file was deleted.

BoxSdkGen/Box.Sdk.Gen.Net/Networking/IRetryStrategy.cs

Lines changed: 0 additions & 15 deletions
This file was deleted.

BoxSdkGen/Box.Sdk.Gen.Net/Networking/NetworkSession.cs

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,10 @@ public class NetworkSession
1818
/// </summary>
1919
public BaseUrls BaseUrls { get; } = new BaseUrls();
2020

21-
/// <summary>
22-
/// Number of request retries.
23-
/// </summary>
24-
public int RetryAttempts { get; init; } = 5;
25-
2621
/// <summary>
2722
/// IRetryStrategy used when retrying http/s request.
2823
/// </summary>
29-
public IRetryStrategy RetryStrategy { get; init; } = new ExponentialBackoffRetryStrategy();
24+
public IRetryStrategy RetryStrategy { get; init; } = new BoxRetryStrategy();
3025

3126
/// <summary>
3227
/// Proxy configuration
@@ -55,7 +50,7 @@ public NetworkSession(Dictionary<string, string>? additionalHeaders = default, B
5550
/// </summary>
5651
public NetworkSession WithNetworkClient(INetworkClient networkClient)
5752
{
58-
return new NetworkSession(this.AdditionalHeaders, this.BaseUrls) { RetryAttempts = this.RetryAttempts, RetryStrategy = this.RetryStrategy, proxyConfig = this.proxyConfig, NetworkClient = networkClient, DataSanitizer = this.DataSanitizer };
53+
return new NetworkSession(this.AdditionalHeaders, this.BaseUrls) { RetryStrategy = this.RetryStrategy, proxyConfig = this.proxyConfig, NetworkClient = networkClient, DataSanitizer = this.DataSanitizer };
5954
}
6055

6156
/// <summary>
@@ -67,7 +62,7 @@ public NetworkSession WithNetworkClient(INetworkClient networkClient)
6762
/// </param>
6863
public NetworkSession WithAdditionalHeaders(Dictionary<string, string> additionalHeaders)
6964
{
70-
return new NetworkSession(DictionaryUtils.MergeDictionaries(this.AdditionalHeaders, additionalHeaders), this.BaseUrls) { RetryAttempts = this.RetryAttempts, RetryStrategy = this.RetryStrategy, proxyConfig = this.proxyConfig, NetworkClient = this.NetworkClient, DataSanitizer = this.DataSanitizer };
65+
return new NetworkSession(DictionaryUtils.MergeDictionaries(this.AdditionalHeaders, additionalHeaders), this.BaseUrls) { RetryStrategy = this.RetryStrategy, proxyConfig = this.proxyConfig, NetworkClient = this.NetworkClient, DataSanitizer = this.DataSanitizer };
7166
}
7267

7368
/// <summary>
@@ -79,7 +74,7 @@ public NetworkSession WithAdditionalHeaders(Dictionary<string, string> additiona
7974
/// </param>
8075
public NetworkSession WithCustomBaseUrls(BaseUrls baseUrls)
8176
{
82-
return new NetworkSession(this.AdditionalHeaders, baseUrls) { RetryAttempts = this.RetryAttempts, RetryStrategy = this.RetryStrategy, proxyConfig = this.proxyConfig, NetworkClient = this.NetworkClient, DataSanitizer = this.DataSanitizer };
77+
return new NetworkSession(this.AdditionalHeaders, baseUrls) { RetryStrategy = this.RetryStrategy, proxyConfig = this.proxyConfig, NetworkClient = this.NetworkClient, DataSanitizer = this.DataSanitizer };
8378
}
8479

8580
/// <summary>
@@ -91,15 +86,15 @@ public NetworkSession WithCustomBaseUrls(BaseUrls baseUrls)
9186
/// </param>
9287
public NetworkSession WithProxy(ProxyConfig config)
9388
{
94-
return new NetworkSession(this.AdditionalHeaders, this.BaseUrls) { RetryAttempts = this.RetryAttempts, RetryStrategy = this.RetryStrategy, proxyConfig = config, NetworkClient = this.NetworkClient, DataSanitizer = this.DataSanitizer };
89+
return new NetworkSession(this.AdditionalHeaders, this.BaseUrls) { RetryStrategy = this.RetryStrategy, proxyConfig = config, NetworkClient = this.NetworkClient, DataSanitizer = this.DataSanitizer };
9590
}
9691

9792
/// <summary>
9893
/// Generate a fresh network session by duplicating the existing configuration and network parameters,
9994
/// while also including a data sanitizer to be used to sanitize sensitive data for logging.
10095
public NetworkSession WithDataSanitizer(DataSanitizer dataSanitizer)
10196
{
102-
return new NetworkSession(this.AdditionalHeaders, this.BaseUrls) { RetryAttempts = this.RetryAttempts, RetryStrategy = this.RetryStrategy, proxyConfig = this.proxyConfig, NetworkClient = this.NetworkClient, DataSanitizer = dataSanitizer };
97+
return new NetworkSession(this.AdditionalHeaders, this.BaseUrls) { RetryStrategy = this.RetryStrategy, proxyConfig = this.proxyConfig, NetworkClient = this.NetworkClient, DataSanitizer = dataSanitizer };
10398
}
10499
}
105100
}

0 commit comments

Comments
 (0)