Skip to content

Commit 613ed41

Browse files
committed
Revert back to new HttpClient instance per request
Move back to creating a new HttpClient instance per request for few reasons: - The current code/pattern was broken - we were double disposing of the singleton client instance. - It makes setting headers slightly easier as the caller no longer must create request message with the headers set manually - just set the default headers on their exclusive client. In practice we're not going to be running for a long time, and we're not going to be making many requests, which would otherwise drain all the available network ports. If the individual providers wish to obtain a new HttpClient instance and reuse it for all their web calls, they are free to do so.
1 parent bf20c20 commit 613ed41

File tree

6 files changed

+28
-45
lines changed

6 files changed

+28
-45
lines changed

src/Microsoft.AzureRepos/AzureDevOpsRestApi.cs

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,12 @@ public async Task<string> GetAuthorityAsync(Uri organizationUri)
4444
const string commonAuthority = authorityBase + "common";
4545
const string msaAuthority = authorityBase + "live.com";
4646

47-
var headers = new[] {Constants.Http.AcceptHeader(Constants.Http.MimeTypeJson)};
47+
HttpClient client = _httpFactory.GetClient();
48+
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(Constants.Http.MimeTypeJson));
4849

4950
_context.Trace.WriteLine($"HTTP: HEAD {organizationUri}");
50-
using (var client = _httpFactory.GetClient())
51-
using (var response = await client.SendAsync(HttpMethod.Head, organizationUri, headers))
51+
using (client)
52+
using (var response = await client.HeadAsync(organizationUri))
5253
{
5354
_context.Trace.WriteLine("HTTP: Response code ignored.");
5455
_context.Trace.WriteLine("Inspecting headers...");
@@ -112,16 +113,14 @@ public async Task<string> CreatePersonalAccessTokenAsync(Uri organizationUri, st
112113

113114
Uri requestUri = new Uri(identityServiceUri, sessionTokenUrl);
114115

115-
var headers = new[]
116-
{
117-
Constants.Http.AcceptHeader(Constants.Http.MimeTypeJson),
118-
Constants.Http.AuthorizationBearerHeader(accessToken)
119-
};
116+
HttpClient client = _httpFactory.GetClient();
117+
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(Constants.Http.MimeTypeJson));
118+
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(Constants.Http.WwwAuthenticateBearerScheme, accessToken);
120119

121120
_context.Trace.WriteLine($"HTTP: POST {requestUri}");
121+
using (client)
122122
using (StringContent content = CreateAccessTokenRequestJson(organizationUri, scopes))
123-
using (var client = _httpFactory.GetClient())
124-
using (var response = await client.SendAsync(HttpMethod.Post, requestUri, headers, content))
123+
using (var response = await client.PostAsync(requestUri, content))
125124
{
126125
_context.Trace.WriteLine($"HTTP: Response {(int)response.StatusCode} [{response.StatusCode}]");
127126

@@ -162,15 +161,13 @@ private async Task<Uri> GetIdentityServiceUriAsync(Uri organizationUri, string a
162161
Query = locationServiceQuery,
163162
}.Uri;
164163

165-
var headers = new[]
166-
{
167-
Constants.Http.AcceptHeader(Constants.Http.MimeTypeJson),
168-
Constants.Http.AuthorizationBearerHeader(accessToken)
169-
};
164+
HttpClient client = _httpFactory.GetClient();
165+
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(Constants.Http.MimeTypeJson));
166+
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(Constants.Http.WwwAuthenticateBearerScheme, accessToken);
170167

171168
_context.Trace.WriteLine($"HTTP: GET {requestUri}");
172-
using (var client = _httpFactory.GetClient())
173-
using (var response = await client.SendAsync(HttpMethod.Get, requestUri, headers))
169+
using (client)
170+
using (var response = await client.GetAsync(requestUri))
174171
{
175172
_context.Trace.WriteLine($"HTTP: Response {(int)response.StatusCode} [{response.StatusCode}]");
176173
if (response.IsSuccessStatusCode)

src/Microsoft.Git.CredentialManager/Authentication/WindowsIntegratedAuthentication.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public async Task<bool> GetIsSupportedAsync(Uri uri)
3434

3535
_context.Trace.WriteLine($"HTTP: HEAD {uri}");
3636
using (HttpClient client = _httpFactory.GetClient())
37-
using (HttpResponseMessage response = await client.SendAsync(HttpMethod.Head, uri))
37+
using (HttpResponseMessage response = await client.HeadAsync(uri))
3838
{
3939
_context.Trace.WriteLine("HTTP: Response code ignored.");
4040

src/Microsoft.Git.CredentialManager/Constants.cs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,12 @@ public static class EnvironmentVariables
2020

2121
public static class Http
2222
{
23+
public const string WwwAuthenticateBasicScheme = "Basic";
2324
public const string WwwAuthenticateBearerScheme = "Bearer";
2425
public const string WwwAuthenticateNegotiateScheme = "Negotiate";
2526
public const string WwwAuthenticateNtlmScheme = "NTLM";
2627

2728
public const string MimeTypeJson = "application/json";
28-
29-
public static Header AcceptHeader(string value)
30-
{
31-
return new Header("Accept", new[] {value});
32-
}
33-
34-
public static Header AuthorizationBearerHeader(string bearerToken)
35-
{
36-
return new Header("Authorization", new[] {$"{WwwAuthenticateBearerScheme} {bearerToken}"});
37-
}
3829
}
3930

4031
/// <summary>

src/Microsoft.Git.CredentialManager/HttpClientFactory.cs

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,19 @@ public interface IHttpClientFactory
1919

2020
public class HttpClientFactory : IHttpClientFactory
2121
{
22-
private HttpClient _client;
23-
2422
public HttpClient GetClient()
2523
{
26-
if (_client is null)
27-
{
28-
// Initialize a new HttpClient
29-
_client = new HttpClient();
24+
// Initialize a new HttpClient
25+
var client = new HttpClient();
3026

31-
// Add default headers
32-
_client.DefaultRequestHeaders.UserAgent.ParseAdd(Constants.GetHttpUserAgent());
33-
_client.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue
34-
{
35-
NoCache = true
36-
};
37-
}
27+
// Add default headers
28+
client.DefaultRequestHeaders.UserAgent.ParseAdd(Constants.GetHttpUserAgent());
29+
client.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue
30+
{
31+
NoCache = true
32+
};
3833

39-
return _client;
34+
return client;
4035
}
4136
}
4237
}

tests/Microsoft.AzureRepos.Tests/AzureDevOpsApiTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ public async Task AzureDevOpsRestApi_CreatePersonalAccessTokenAsync_IdentSvcRetu
295295
var httpHandler = new TestHttpMessageHandler {ThrowOnUnexpectedRequest = true};
296296
httpHandler.Setup(HttpMethod.Get, locSvcRequestUri, x =>
297297
{
298-
AssertHeader(x, Constants.Http.AcceptHeader(Constants.Http.MimeTypeJson));
298+
AssertAcceptJson(x);
299299
AssertBearerToken(x, accessToken);
300300
return locSvcResponse;
301301
});

tests/Microsoft.Git.CredentialManager.Tests/HttpClientFactoryTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ public void HttpClientFactory_GetClient_SetsDefaultHeaders()
2020
}
2121

2222
[Fact]
23-
public void HttpClientFactory_GetClient_MultipleCalls_ReturnsSameInstance()
23+
public void HttpClientFactory_GetClient_MultipleCalls_ReturnsNewInstance()
2424
{
2525
var factory = new HttpClientFactory();
2626

2727
HttpClient client1 = factory.GetClient();
2828
HttpClient client2 = factory.GetClient();
2929

30-
Assert.Same(client1, client2);
30+
Assert.NotSame(client1, client2);
3131
}
3232
}
3333
}

0 commit comments

Comments
 (0)