Skip to content

Commit c1f329d

Browse files
committed
Added 401 retry tests
1 parent 4babc8c commit c1f329d

File tree

5 files changed

+295
-45
lines changed

5 files changed

+295
-45
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Net.Http;
4+
using System.Text;
5+
6+
namespace Microsoft.Graph.Core.Helpers
7+
{
8+
internal static class ContentHelper
9+
{
10+
/// <summary>
11+
/// Check the HTTP request's content to determine if it's buffered content or not.
12+
/// </summary>
13+
/// <param name="httpRequest">The <see cref="HttpRequestMessage"/>needs to be sent.</param>
14+
/// <returns></returns>
15+
internal static bool IsBuffered(HttpRequestMessage httpRequest)
16+
{
17+
HttpContent requestContent = httpRequest.Content;
18+
19+
if ((httpRequest.Method == HttpMethod.Put || httpRequest.Method == HttpMethod.Post || httpRequest.Method.Method.Equals("PATCH"))
20+
&& requestContent != null && (requestContent.Headers.ContentLength == null || (int)requestContent.Headers.ContentLength == -1))
21+
{
22+
return false;
23+
}
24+
return true;
25+
}
26+
}
27+
}

src/Microsoft.Graph.Core/Requests/AuthenticationHandler.cs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Threading;
77
using System.Threading.Tasks;
88
using System.Net;
9+
using Microsoft.Graph.Core.Helpers;
910

1011
namespace Microsoft.Graph
1112
{
@@ -14,7 +15,11 @@ namespace Microsoft.Graph
1415
/// </summary>
1516
public class AuthenticationHandler: DelegatingHandler
1617
{
18+
/// <summary>
19+
/// MaxRetry property for 401's
20+
/// </summary>
1721
public int MaxRetry { get; set; } = 1;
22+
1823
/// <summary>
1924
/// AuthenticationProvider property
2025
/// </summary>
@@ -42,18 +47,18 @@ public AuthenticationHandler(IAuthenticationProvider authenticationProvider, Htt
4247
/// <summary>
4348
/// Check HTTP response message status code if it's unauthorized (401) or not
4449
/// </summary>
45-
/// <param name="httpResponseMessage"></param>
50+
/// <param name="httpResponseMessage">The <see cref="HttpResponseMessage"/>to send.</param>
4651
/// <returns></returns>
47-
public bool IsUnauthorized(HttpResponseMessage httpResponseMessage)
52+
private bool IsUnauthorized(HttpResponseMessage httpResponseMessage)
4853
{
4954
return httpResponseMessage.StatusCode == HttpStatusCode.Unauthorized;
5055
}
5156

5257
/// <summary>
5358
/// Retry sending HTTP request
5459
/// </summary>
55-
/// <param name="httpResponseMessage"></param>
56-
/// <param name="cancellationToken"></param>
60+
/// <param name="httpResponseMessage">The <see cref="HttpResponseMessage"/>to send.</param>
61+
/// <param name="cancellationToken">The <see cref="CancellationToken"/>to send.</param>
5762
/// <returns></returns>
5863
private async Task<HttpResponseMessage> SendRetryAsync(HttpResponseMessage httpResponseMessage, CancellationToken cancellationToken)
5964
{
@@ -68,7 +73,7 @@ private async Task<HttpResponseMessage> SendRetryAsync(HttpResponseMessage httpR
6873

6974
retryAttempt++;
7075

71-
if (!IsUnauthorized(httpResponseMessage))
76+
if (!IsUnauthorized(httpResponseMessage) || !ContentHelper.IsBuffered(originalRequest))
7277
{
7378
// Re-issue the request to get a new access token
7479
return httpResponseMessage;
@@ -90,15 +95,14 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
9095
await AuthenticationProvider.AuthenticateRequestAsync(httpRequest);
9196
HttpResponseMessage response = await base.SendAsync(httpRequest, cancellationToken);
9297

93-
// Chcek if response is a 401
94-
if (IsUnauthorized(response))
98+
// Chcek if response is a 401 & is not a streamed body (is buffered)
99+
if (IsUnauthorized(response) && ContentHelper.IsBuffered(httpRequest))
95100
{
96101
// re-issue the request to get a new access token
97102
response = await SendRetryAsync(response, cancellationToken);
98103
}
99104

100105
return response;
101106
}
102-
103107
}
104108
}

src/Microsoft.Graph.Core/Requests/RetryHandler.cs

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ namespace Microsoft.Graph
1111
using System.Net.Http;
1212
using System.Net;
1313
using System.Net.Http.Headers;
14+
using Microsoft.Graph.Core.Helpers;
15+
1416
/// <summary>
1517
/// An <see cref="DelegatingHandler"/> implementation using standard .NET libraries.
1618
/// </summary>
@@ -54,7 +56,7 @@ protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage
5456

5557
var response = await base.SendAsync(httpRequest, cancellationToken);
5658

57-
if (IsRetry(response) && IsBuffered(httpRequest))
59+
if (IsRetry(response) && ContentHelper.IsBuffered(httpRequest))
5860
{
5961
response = await SendRetryAsync(response, cancellationToken);
6062
}
@@ -94,7 +96,7 @@ public async Task<HttpResponseMessage> SendRetryAsync(HttpResponseMessage respon
9496
// Call base.SendAsync to send the request
9597
response = await base.SendAsync(request, cancellationToken);
9698

97-
if (!IsRetry(response) || !IsBuffered(request))
99+
if (!IsRetry(response) || !ContentHelper.IsBuffered(request))
98100
{
99101
return response;
100102
}
@@ -126,23 +128,6 @@ public bool IsRetry(HttpResponseMessage response)
126128
return false;
127129
}
128130

129-
/// <summary>
130-
/// Check the HTTP request's content to determine whether it can be retried or not.
131-
/// </summary>
132-
/// <param name="request">The <see cref="HttpRequestMessage"/>needs to be sent.</param>
133-
/// <returns></returns>
134-
private bool IsBuffered(HttpRequestMessage request)
135-
{
136-
HttpContent content = request.Content;
137-
138-
if ((request.Method == HttpMethod.Put || request.Method == HttpMethod.Post || request.Method.Method.Equals("PATCH"))
139-
&& content != null && (content.Headers.ContentLength == null || (int)content.Headers.ContentLength == -1))
140-
{
141-
return false;
142-
}
143-
return true;
144-
145-
}
146131

147132
/// <summary>
148133
/// Update Retry-Attempt header in the HTTP request

tests/Microsoft.Graph.Core.Test/Requests/AuthenticationHandlerTests.cs

Lines changed: 125 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public void AuthHandler_DefaultConstructor()
4040
using (AuthenticationHandler auth = new AuthenticationHandler())
4141
{
4242
Assert.IsNull(auth.InnerHandler, "Http message handler initialized");
43+
Assert.IsNull(auth.AuthenticationProvider, "Authentication provider initialized");
4344
Assert.IsInstanceOfType(auth, typeof(AuthenticationHandler), "Unexpected authentication handler set");
4445
}
4546
}
@@ -54,11 +55,17 @@ public void AuthHandler_AuthProviderHttpMessageHandlerConstructor()
5455
Assert.IsInstanceOfType(authenticationHandler, typeof(AuthenticationHandler), "Unexpected authentication handler set");
5556
}
5657

57-
[TestMethod]
58-
public async Task AuthHandler_OkStatusShouldPassThrough()
58+
[DataTestMethod]
59+
[DataRow(HttpStatusCode.OK)]
60+
[DataRow(HttpStatusCode.MovedPermanently)]
61+
[DataRow(HttpStatusCode.NotFound)]
62+
[DataRow(HttpStatusCode.BadRequest)]
63+
[DataRow(HttpStatusCode.Forbidden)]
64+
[DataRow(HttpStatusCode.GatewayTimeout)]
65+
public async Task AuthHandler_NonUnauthorizedStatusShouldPassThrough(HttpStatusCode statusCode)
5966
{
6067
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://example.org/foo");
61-
var expectedResponse = new HttpResponseMessage(HttpStatusCode.OK);
68+
var expectedResponse = new HttpResponseMessage(statusCode);
6269

6370
testHttpMessageHandler.SetHttpResponse(expectedResponse);
6471

@@ -68,20 +75,130 @@ public async Task AuthHandler_OkStatusShouldPassThrough()
6875
Assert.AreSame(response.RequestMessage, httpRequestMessage, "Http response message sets wrong request message");
6976
}
7077

71-
[DataTestMethod]
72-
[DataRow(HttpStatusCode.Unauthorized)]
73-
public async Task AuthHandler_ShouldRetryUnAuthorizedResponse(HttpStatusCode statusCode)
78+
[TestMethod]
79+
public async Task AuthHandler_ShouldRetryUnauthorizedGetRequest()
7480
{
7581
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://example.com/bar");
76-
var unAuthorizedResponse = new HttpResponseMessage(statusCode);
82+
var unauthorizedResponse = new HttpResponseMessage(HttpStatusCode.Unauthorized);
83+
var expectedResponse = new HttpResponseMessage(HttpStatusCode.OK);
84+
85+
testHttpMessageHandler.SetHttpResponse(unauthorizedResponse, expectedResponse);
86+
87+
var response = await invoker.SendAsync(httpRequestMessage, new CancellationToken());
88+
89+
Assert.AreSame(response.RequestMessage, httpRequestMessage, "Http response message sets wrong request message");
90+
Assert.AreSame(response, expectedResponse, "Retry didn't happen");
91+
Assert.IsNull(response.RequestMessage.Content, "Content is not null");
92+
}
93+
94+
[TestMethod]
95+
public async Task AuthHandler_ShouldRetryUnauthorizedPostRequestWithNoContent()
96+
{
97+
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "http://example.com/bar");
98+
var unauthorizedResponse = new HttpResponseMessage(HttpStatusCode.Unauthorized);
7799
var expectedResponse = new HttpResponseMessage(HttpStatusCode.OK);
78100

79-
testHttpMessageHandler.SetHttpResponse(unAuthorizedResponse, expectedResponse);
101+
testHttpMessageHandler.SetHttpResponse(unauthorizedResponse, expectedResponse);
80102

81103
var response = await invoker.SendAsync(httpRequestMessage, new CancellationToken());
82104

83105
Assert.AreSame(response.RequestMessage, httpRequestMessage, "Http response message sets wrong request message");
84106
Assert.AreSame(response, expectedResponse, "Retry didn't happen");
107+
Assert.IsNull(response.RequestMessage.Content, "Content is not null");
108+
}
109+
110+
[TestMethod]
111+
public async Task AuthHandler_ShouldRetryUnauthorizedPostRequestWithBufferContent()
112+
{
113+
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "http://example.com/bar");
114+
httpRequestMessage.Content = new StringContent("Hello World!");
115+
116+
var unauthorizedResponse = new HttpResponseMessage(HttpStatusCode.Unauthorized);
117+
var okResponse = new HttpResponseMessage(HttpStatusCode.OK);
118+
119+
testHttpMessageHandler.SetHttpResponse(unauthorizedResponse, okResponse);
120+
121+
var response = await invoker.SendAsync(httpRequestMessage, new CancellationToken());
122+
123+
Assert.AreSame(response, okResponse, "Retry didn't happen");
124+
Assert.AreNotSame(response, unauthorizedResponse, "Retry didn't happen");
125+
Assert.IsNotNull(response.RequestMessage.Content, "The request content is null");
126+
Assert.AreEqual(response.RequestMessage.Content.ReadAsStringAsync().Result, "Hello World!", "Content changed");
127+
}
128+
129+
[TestMethod]
130+
public async Task AuthHandler_ShouldRetryUnauthorizedPatchRequestWithBufferContent()
131+
{
132+
var httpRequestMessage = new HttpRequestMessage(new HttpMethod("PATCH"), "http://example.com/bar");
133+
httpRequestMessage.Content = new StringContent("Hello World!");
134+
135+
var unauthorizedResponse = new HttpResponseMessage(HttpStatusCode.Unauthorized);
136+
var okResponse = new HttpResponseMessage(HttpStatusCode.OK);
137+
138+
testHttpMessageHandler.SetHttpResponse(unauthorizedResponse, okResponse);
139+
140+
var response = await invoker.SendAsync(httpRequestMessage, new CancellationToken());
141+
142+
Assert.AreSame(response, okResponse, "Retry didn't happen");
143+
Assert.AreNotSame(response, unauthorizedResponse, "Retry didn't happen");
144+
Assert.IsNotNull(response.RequestMessage.Content, "The request content is null");
145+
Assert.AreEqual(response.RequestMessage.Content.ReadAsStringAsync().Result, "Hello World!", "Content changed");
146+
}
147+
148+
[TestMethod]
149+
public async Task AuthHandler_ShouldNotRetryUnauthorizedPutRequestWithStreamContent()
150+
{
151+
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Put, "http://example.com/bar");
152+
httpRequestMessage.Content = new StringContent("Jambo");
153+
httpRequestMessage.Content.Headers.ContentLength = -1;
154+
155+
var unauthorizedResponse = new HttpResponseMessage(HttpStatusCode.Unauthorized);
156+
var okResponse = new HttpResponseMessage(HttpStatusCode.OK);
157+
158+
testHttpMessageHandler.SetHttpResponse(unauthorizedResponse, okResponse);
159+
160+
var response = await invoker.SendAsync(httpRequestMessage, new CancellationToken());
161+
162+
Assert.AreNotSame(response, okResponse, "Unexpected retry");
163+
Assert.AreSame(response, unauthorizedResponse, "Unexpected retry");
164+
Assert.IsNotNull(response.RequestMessage.Content, "Request content is null");
165+
Assert.AreEqual(response.RequestMessage.Content.Headers.ContentLength, -1, "Request content length changed");
166+
}
167+
168+
[TestMethod]
169+
public async Task AuthHandler_ShouldNotRetryUnauthorizedPatchRequestWithStreamContent()
170+
{
171+
var httpRequestMessage = new HttpRequestMessage(new HttpMethod("PATCH"), "http://example.com/bar");
172+
httpRequestMessage.Content = new StringContent("Jambo");
173+
httpRequestMessage.Content.Headers.ContentLength = -1;
174+
175+
var unauthorizedResponse = new HttpResponseMessage(HttpStatusCode.Unauthorized);
176+
var okResponse = new HttpResponseMessage(HttpStatusCode.OK);
177+
178+
testHttpMessageHandler.SetHttpResponse(unauthorizedResponse, okResponse);
179+
180+
var response = await invoker.SendAsync(httpRequestMessage, new CancellationToken());
181+
182+
Assert.AreNotSame(response, okResponse, "Unexpected retry");
183+
Assert.AreSame(response, unauthorizedResponse, "Unexpected retry");
184+
Assert.IsNotNull(response.RequestMessage.Content, "Request content is null");
185+
Assert.AreEqual(response.RequestMessage.Content.Headers.ContentLength, -1, "Request content length changed");
186+
}
187+
188+
[TestMethod]
189+
public async Task AuthHandler_ShouldReturnUnauthorizedRequestWithDefaultMaxRetryExceeded()
190+
{
191+
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Put, "http://example.com/bar");
192+
httpRequestMessage.Content = new StringContent("Hello Mars!");
193+
var unauthorizedResponse = new HttpResponseMessage(HttpStatusCode.Unauthorized);
194+
var expectedResponse = new HttpResponseMessage(HttpStatusCode.Unauthorized);
195+
196+
testHttpMessageHandler.SetHttpResponse(unauthorizedResponse, expectedResponse);
197+
198+
var response = await invoker.SendAsync(httpRequestMessage, new CancellationToken());
199+
200+
Assert.AreSame(response, expectedResponse, "Unexpected code returned");
201+
Assert.AreEqual(response.RequestMessage.Content.ReadAsStringAsync().Result, "Hello Mars!");
85202
}
86203
}
87204
}

0 commit comments

Comments
 (0)