Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/client/Microsoft.Identity.Client/Http/HttpManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,15 @@ private HttpClient GetHttpClient(X509Certificate2 x509Certificate2, Func<HttpReq
private static HttpRequestMessage CreateRequestMessage(Uri endpoint, IDictionary<string, string> headers)
{
HttpRequestMessage requestMessage = new HttpRequestMessage { RequestUri = endpoint };

requestMessage.Headers.Accept.Clear();

#if NET5_0_OR_GREATER
// On .NET 5.0 and later, HTTP2 is supported through the SDK and Entra is HTTP2 compatible
// Note that HttpClient.DefaultRequestVersion does not work when using HttpRequestMessage objects
requestMessage.Version = HttpVersion.Version20; // Default to HTTP/2
requestMessage.VersionPolicy = HttpVersionPolicy.RequestVersionOrLower; // Allow fallback to HTTP/1.1
#endif
if (headers != null)
{
foreach (KeyValuePair<string, string> kvp in headers)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
using System.Runtime.InteropServices;
using Microsoft.Identity.Client;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.Identity.Test.Common
{
Expand All @@ -28,7 +31,24 @@ public HttpSnifferClientFactory()
req.Content.LoadIntoBufferAsync().GetAwaiter().GetResult();
LastHttpContentData = req.Content.ReadAsStringAsync().GetAwaiter().GetResult();
}

// check the .net runtime
var framework = RuntimeInformation.FrameworkDescription;

// This will match ".NET 5.0", ".NET 6.0", ".NET 7.0", ".NET 8.0", etc.
if (framework.StartsWith(".NET ", StringComparison.OrdinalIgnoreCase))
{
// Extract the version number
var versionString = framework.Substring(5).Trim(); // e.g., "6.0.0"
if (Version.TryParse(versionString, out var version) && version.Major >= 5)
{
Assert.AreEqual(new Version(2, 0), req.Version, $"Request version mismatch: {req.Version}. MSAL on NET 5+ expects HTTP/2.0 for all requests.");
// ESTS-R endpoint does not support HTTP/2.0, so we don't assert this
}
}

RequestsAndResponses.Add((req, res));

Trace.WriteLine($"[MSAL][HTTP Request]: {req}");
Trace.WriteLine($"[MSAL][HTTP Response]: {res}");
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Security;
Expand All @@ -11,6 +12,7 @@
using System.Threading.Tasks;
using Microsoft.Identity.Client;
using Microsoft.Identity.Client.Core;
using Microsoft.Identity.Client.Http.Retry;
using Microsoft.Identity.Test.Common;
using Microsoft.Identity.Test.Common.Core.Helpers;
using Microsoft.Identity.Test.Common.Core.Mocks;
Expand Down Expand Up @@ -533,5 +535,48 @@ public async Task TestSendPostWithRetryOnTimeoutFailureAsync()
Assert.AreEqual(Num500Errors, requestsMade);
}
}

private class CapturingHandler : HttpMessageHandler
{
public HttpRequestMessage CapturedRequest { get; private set; }
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
CapturedRequest = request;
return Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.OK));
}
}

#if NET
[TestMethod]
public async Task SendRequestAsync_SetsHttp2VersionAndPolicy()
{
// Arrange
var handler = new CapturingHandler();
var httpClient = new HttpClient(handler);
var httpClientFactory = Substitute.For<IMsalHttpClientFactory>();
httpClientFactory.GetHttpClient().Returns(httpClient);

var httpManager = new Client.Http.HttpManager(httpClientFactory, disableInternalRetries: true);

// Act
await httpManager.SendRequestAsync(
new Uri("https://login.microsoftonline.com/common/discovery/instance?api-version=1.1&authorization_endpoint=https://login.microsoftonline.com/common/oauth2/v2.0/authorize"),
null,
null,
HttpMethod.Get,
Substitute.For<ILoggerAdapter>(),
doNotThrow: true,
bindingCertificate: null,
validateServerCert: null,
cancellationToken: CancellationToken.None,
retryPolicy: Substitute.For<IRetryPolicy>()
).ConfigureAwait(false);

// Assert
Assert.IsNotNull(handler.CapturedRequest);
Assert.AreEqual(HttpVersion.Version20, handler.CapturedRequest.Version);
Assert.AreEqual(HttpVersionPolicy.RequestVersionOrLower, handler.CapturedRequest.VersionPolicy);
}
#endif
}
}
Loading