Skip to content

Commit 7a20ffb

Browse files
authored
Merge pull request #2028 from microsoftgraph/po/FixFinalHttpHandler
Fixes Request Header Logging When Using WinHttpHandler
2 parents 0fe2175 + 67a8090 commit 7a20ffb

File tree

4 files changed

+78
-30
lines changed

4 files changed

+78
-30
lines changed

src/Authentication/Authentication/Handlers/RequestHeaderHandler.cs

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@
22
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
33
// ------------------------------------------------------------------------------
44

5-
using Microsoft.Graph.PowerShell.Authentication.Cmdlets;
65
using System;
76
using System.Collections.Generic;
87
using System.Linq;
98
using System.Net.Http;
10-
using System.Net.Http.Headers;
119
using System.Reflection;
1210
using System.Threading;
1311
using System.Threading.Tasks;
@@ -21,37 +19,30 @@ namespace Microsoft.Graph.PowerShell.Authentication.Handlers
2119
internal class RequestHeaderHandler : DelegatingHandler
2220
{
2321
/// The version for current assembly.
24-
private static readonly AssemblyName _assemblyInfo = typeof(ConnectMgGraph).GetTypeInfo().Assembly.GetName();
22+
private static readonly AssemblyName _assemblyInfo = typeof(RequestHeaderHandler).GetTypeInfo().Assembly.GetName();
2523

2624
public RequestHeaderHandler() { }
2725

28-
public RequestHeaderHandler(HttpRequestHeaders requestHeaders, HttpMessageHandler innerHandler) : base(innerHandler) { }
29-
3026
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
3127
{
32-
SetRequestHeaders(request);
33-
return base.SendAsync(request, cancellationToken);
34-
}
35-
36-
private static void SetRequestHeaders(HttpRequestMessage request)
37-
{
38-
string sdkVersionHeaderValue = string.Format(request.RequestUri.AbsolutePath.StartsWith("/beta") ? Constants.PSSDKHeaderValueBeta : Constants.PSSDKHeaderValueV1, _assemblyInfo.Version.Major, _assemblyInfo.Version.Minor, _assemblyInfo.Version.Build);
39-
PrependHeader(request, CoreConstants.Headers.SdkVersionHeaderName, sdkVersionHeaderValue);
40-
}
41-
42-
private static void PrependHeader(HttpRequestMessage request, string headerName, string headerValue)
43-
{
44-
if (request.Headers.TryGetValues(headerName, out IEnumerable<string> previousSDKHeaders))
28+
string psSdkVersionHeader = string.Format(request.RequestUri.AbsolutePath.StartsWith("/beta") ? Constants.PSSDKHeaderValueBeta
29+
: Constants.PSSDKHeaderValueV1, _assemblyInfo.Version.Major, _assemblyInfo.Version.Minor, _assemblyInfo.Version.Build);
30+
if (request.Headers.TryGetValues(CoreConstants.Headers.SdkVersionHeaderName, out IEnumerable<string> previousSDKHeaders))
4531
{
46-
request.Headers.Remove(headerName);
47-
request.Headers.Add(headerName, new[] {
48-
headerValue, previousSDKHeaders.Where(h => h.StartsWith(Constants.DotNetSDKHeaderValue, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault()
49-
});
32+
var dotNetSdkHeader = previousSDKHeaders.Where(h => h.StartsWith(Constants.DotNetSDKHeaderValue, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
33+
request.Headers.Remove(CoreConstants.Headers.SdkVersionHeaderName);
34+
request.Headers.Add(CoreConstants.Headers.SdkVersionHeaderName, new[] { psSdkVersionHeader, dotNetSdkHeader });
5035
}
5136
else
5237
{
53-
request.Headers.Add(headerName, headerValue);
38+
request.Headers.Add(CoreConstants.Headers.SdkVersionHeaderName, psSdkVersionHeader);
5439
}
40+
41+
if (request.Headers.Contains(CoreConstants.Headers.ClientRequestId))
42+
request.Headers.Remove(CoreConstants.Headers.ClientRequestId);
43+
request.Headers.Add(CoreConstants.Headers.ClientRequestId, Guid.NewGuid().ToString());
44+
45+
return base.SendAsync(request, cancellationToken);
5546
}
5647
}
5748
}

src/Authentication/Authentication/Helpers/HttpHelpers.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ private static HttpClient GetGraphHttpClient(IAuthenticationProvider authProvide
4646
throw new AuthenticationException(string.Format(CultureInfo.InvariantCulture, Core.ErrorConstants.Message.MissingSessionProperty, nameof(requestContext)));
4747

4848
IList<DelegatingHandler> delegatingHandlers = new List<DelegatingHandler> {
49-
new RequestHeaderHandler(),
5049
new AuthenticationHandler(authProvider),
5150
new NationalCloudHandler(),
5251
new ODataQueryOptionsHandler(),
@@ -57,7 +56,8 @@ private static HttpClient GetGraphHttpClient(IAuthenticationProvider authProvide
5756
MaxRetry = requestContext.MaxRetry,
5857
RetriesTimeLimit= requestContext.RetriesTimeLimit
5958
}),
60-
new RedirectHandler()
59+
new RedirectHandler(),
60+
new RequestHeaderHandler() // Should always be last.
6161
};
6262

6363
HttpClient httpClient = GraphClientFactory.Create(delegatingHandlers);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// ------------------------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
3+
// ------------------------------------------------------------------------------
4+
5+
using System;
6+
7+
namespace Microsoft.Graph.PowerShell.Authentication.Helpers
8+
{
9+
/// <summary>
10+
/// Utility class containing runtime utility methods.
11+
/// </summary>
12+
internal static class RuntimeUtils
13+
{
14+
/// <summary>
15+
/// Determines if the PSEdition of the current process is Core.
16+
/// </summary>
17+
/// <returns><see cref="true"/> when PSEdition is core, else <see cref="false"/>.</returns>
18+
internal static bool IsPsCore()
19+
{
20+
var psCoreVersion = new Version(6, 0, 0);
21+
return GraphSession.Instance.AuthContext.PSHostVersion >= psCoreVersion;
22+
}
23+
}
24+
}

tools/Custom/HttpMessageLogFormatter.cs

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ namespace NamespacePrefixPlaceholder.PowerShell
77
using Newtonsoft.Json;
88
using System;
99
using System.Collections.Generic;
10+
using System.IO;
1011
using System.Linq;
1112
using System.Net.Http;
1213
using System.Net.Http.Headers;
@@ -17,22 +18,54 @@ namespace NamespacePrefixPlaceholder.PowerShell
1718

1819
public static class HttpMessageLogFormatter
1920
{
21+
internal static async Task<HttpRequestMessage> CloneAsync(this HttpRequestMessage originalRequest)
22+
{
23+
var newRequest = new HttpRequestMessage(originalRequest.Method, originalRequest.RequestUri);
24+
25+
// Copy requestClone headers.
26+
foreach (var header in originalRequest.Headers)
27+
newRequest.Headers.TryAddWithoutValidation(header.Key, header.Value);
28+
29+
// Copy requestClone properties.
30+
foreach (var property in originalRequest.Properties)
31+
newRequest.Properties.Add(property);
32+
33+
// Set Content if previous requestClone had one.
34+
if (originalRequest.Content != null)
35+
{
36+
// HttpClient doesn't rewind streams and we have to explicitly do so.
37+
await originalRequest.Content.ReadAsStreamAsync().ContinueWith(t =>
38+
{
39+
if (t.Result.CanSeek)
40+
t.Result.Seek(0, SeekOrigin.Begin);
41+
42+
newRequest.Content = new StreamContent(t.Result);
43+
}).ConfigureAwait(false);
44+
45+
// Copy content headers.
46+
if (originalRequest.Content.Headers != null)
47+
foreach (var contentHeader in originalRequest.Content.Headers)
48+
newRequest.Content.Headers.TryAddWithoutValidation(contentHeader.Key, contentHeader.Value);
49+
}
50+
return newRequest;
51+
}
52+
2053
public static async Task<string> GetHttpRequestLogAsync(HttpRequestMessage request)
2154
{
2255
if (request == null) return string.Empty;
23-
56+
var requestClone = await request.CloneAsync().ConfigureAwait(false);
2457
string body = string.Empty;
2558
try
2659
{
27-
body = (request.Content == null) ? string.Empty : FormatString(await request.Content.ReadAsStringAsync());
60+
body = (requestClone.Content == null) ? string.Empty : FormatString(await requestClone.Content.ReadAsStringAsync());
2861
}
2962
catch { }
3063

3164
StringBuilder stringBuilder = new StringBuilder();
3265
stringBuilder.AppendLine($"============================ HTTP REQUEST ============================{Environment.NewLine}");
33-
stringBuilder.AppendLine($"HTTP Method:{Environment.NewLine}{request.Method.ToString()}{Environment.NewLine}");
34-
stringBuilder.AppendLine($"Absolute Uri:{Environment.NewLine}{request.RequestUri.ToString()}{Environment.NewLine}");
35-
stringBuilder.AppendLine($"Headers:{Environment.NewLine}{HeadersToString(ConvertHttpHeadersToCollection(request.Headers))}{Environment.NewLine}");
66+
stringBuilder.AppendLine($"HTTP Method:{Environment.NewLine}{requestClone.Method.ToString()}{Environment.NewLine}");
67+
stringBuilder.AppendLine($"Absolute Uri:{Environment.NewLine}{requestClone.RequestUri.ToString()}{Environment.NewLine}");
68+
stringBuilder.AppendLine($"Headers:{Environment.NewLine}{HeadersToString(ConvertHttpHeadersToCollection(requestClone.Headers))}{Environment.NewLine}");
3669
stringBuilder.AppendLine($"Body:{Environment.NewLine}{SanitizeBody(body)}{Environment.NewLine}");
3770
return stringBuilder.ToString();
3871
}

0 commit comments

Comments
 (0)