@@ -164,18 +164,38 @@ public async Task<Response> Send(HttpMethod method, string path, Dictionary<stri
164
164
request = await authStrategy . AuthenticateRequest ( request ) . ConfigureAwait ( false ) ;
165
165
}
166
166
167
- var response = await Send ( request ) . ConfigureAwait ( false ) ;
167
+ Response response ;
168
168
169
- // If we get unauthorized response, try to authenticate a second time.
170
- // This is common in OAuth when an access token is expired.
171
- if ( ( response . StatusCode == HttpStatusCode . Unauthorized || response . StatusCode == HttpStatusCode . Forbidden ) && authStrategy is IRefreshableAuthStrategy refreshableAuthStrategy )
169
+ try
172
170
{
173
- // we need to build a new request here; requests can only be sent once
174
- request . Dispose ( ) ;
171
+ response = await Send ( request ) . ConfigureAwait ( false ) ;
172
+
173
+ // If we get unauthorized response, try to authenticate a second time.
174
+ // This is common in OAuth when an access token is expired.
175
+ if ( ( response . StatusCode == HttpStatusCode . Unauthorized || response . StatusCode == HttpStatusCode . Forbidden ) && authStrategy is IRefreshableAuthStrategy refreshableAuthStrategy )
176
+ {
177
+ // we need to build a new request here; requests can only be sent once
178
+ request . Dispose ( ) ;
175
179
#pragma warning disable CA2000 // Dispose objects before losing scope
176
- request = PrepareRequestMessage ( method , url , headers , body ) ;
180
+ request = PrepareRequestMessage ( method , url , headers , body ) ;
177
181
#pragma warning restore CA2000 // Dispose objects before losing scope
178
- request = await refreshableAuthStrategy . RefreshCredentialsAndAuthenticateRequest ( request ) . ConfigureAwait ( false ) ;
182
+ request = await refreshableAuthStrategy . RefreshCredentialsAndAuthenticateRequest ( request ) . ConfigureAwait ( false ) ;
183
+ response = await Send ( request ) . ConfigureAwait ( false ) ;
184
+ }
185
+ }
186
+ catch ( RequestRedirectedException ex )
187
+ {
188
+ request . Dispose ( ) ;
189
+
190
+ // recreate a new request with the redirected url.
191
+ request = PrepareRequestMessage ( method , ex . RedirectUri , headers , body ) ;
192
+
193
+ if ( authStrategy != null )
194
+ {
195
+ // Send the first authenticated request
196
+ request = await authStrategy . AuthenticateRequest ( request ) . ConfigureAwait ( false ) ;
197
+ }
198
+
179
199
response = await Send ( request ) . ConfigureAwait ( false ) ;
180
200
}
181
201
@@ -280,9 +300,24 @@ async Task<Response> Send(HttpRequestMessage request)
280
300
281
301
try
282
302
{
303
+ var originalUri = request . RequestUri ;
304
+
283
305
var httpResponse = await webClient . SendAsync ( request ) . ConfigureAwait ( false ) ;
284
306
var responseContent = await httpResponse . Content . ReadAsStringAsync ( ) . ConfigureAwait ( false ) ;
285
307
308
+ // Verify if a redirect happened.
309
+ if ( httpResponse . StatusCode == HttpStatusCode . Moved
310
+ || httpResponse . StatusCode == HttpStatusCode . MovedPermanently )
311
+ {
312
+ var redirectUrl = httpResponse . Headers . Location ;
313
+
314
+ if ( redirectUrl . Host == request . RequestUri . Host
315
+ && redirectUrl . AbsolutePath != request . RequestUri ? . AbsolutePath )
316
+ {
317
+ throw new RequestRedirectedException ( redirectUrl ) ;
318
+ }
319
+ }
320
+
286
321
return new Response ( httpResponse , responseContent ) ;
287
322
}
288
323
catch ( Exception e )
@@ -295,5 +330,18 @@ async Task<Response> Send(HttpRequestMessage request)
295
330
throw ;
296
331
}
297
332
}
333
+
334
+ /// <summary>
335
+ /// Request redirected exception helper to handle a content redirect.
336
+ /// </summary>
337
+ internal class RequestRedirectedException : Exception
338
+ {
339
+ public RequestRedirectedException ( Uri redirectUri )
340
+ {
341
+ RedirectUri = redirectUri ;
342
+ }
343
+
344
+ public Uri RedirectUri { get ; }
345
+ }
298
346
}
299
347
}
0 commit comments