Skip to content

Commit 928cc99

Browse files
authored
Compression Handler Header Handling (#123)
* Support native platform Http handlers for Xamarin.Mac * Update macOS to use NSUrlSessionHandler in foundation namespace - support for Xamarin.Mac20 * Compression header handling (#118)
1 parent 590dc4d commit 928cc99

File tree

8 files changed

+164
-32
lines changed

8 files changed

+164
-32
lines changed

scripts/IncrementPreviewVersion.ps1

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@
1515
1616
#>
1717

18+
[CmdletBinding()]
1819
Param(
19-
[parameter(Mandatory = $true)]
20-
[string]$packageName,
20+
[parameter(Mandatory = $false)]
21+
[string]$packageName = 'microsoft.graph.core',
2122

22-
[parameter(Mandatory = $true)]
23-
[string]$projectPath
23+
[parameter(Mandatory = $false)]
24+
[string]$projectPath = '.\src\Microsoft.Graph.Core\Microsoft.Graph.Core.csproj'
2425
)
2526

2627
$xmlDoc = New-Object System.Xml.XmlDocument
@@ -31,9 +32,9 @@ $versionSuffixString = $xmlDoc.Project.PropertyGroup[0].VersionSuffix
3132

3233
# Don't do anything if a VersionSuffix has been set in the .csproj.
3334
if ($versionSuffixString -ne '' -and $versionSuffixString -ne $null) {
34-
Write-Host "The VersionSuffix has been set as $versionSuffixString in the csproj file. `
35-
Skip the automatic setting or incrementing of the version suffix. Delete the value in `
36-
VersionSuffix to enable auto-incrementing the preview version." -ForegroundColor Yellow
35+
Write-Host "`tThe VersionSuffix has been set as $versionSuffixString in the csproj file. `
36+
`tSkip the automatic setting or incrementing of the version suffix. Delete the value in `
37+
`tVersionSuffix to enable auto-incrementing the preview version." -ForegroundColor Yellow
3738

3839
Exit 0
3940
}
@@ -51,19 +52,31 @@ $url = "https://api.nuget.org/v3-flatcontainer/$packageName/index.json"
5152
# Call the NuGet API for the package and get the highest SemVer 2.0.0 version for the package.
5253
# Per rules https://semver.org/spec/v2.0.0.html#spec-item-11
5354
$nugetIndex = Invoke-RestMethod -Uri $url -Method Get
54-
$highestPublishedVersion = $nugetIndex.versions[$nugetIndex.versions.Length - 1]
5555

56-
# We do need to make sure it is listed. For example, we didn't properly suffix M.G.A so the
57-
# highest reported version is incorrect.
56+
# We need the versionPrefix from the csproj so that we target the
57+
# the highest version. This enables us to support v1.x.x and vNext.x.x
58+
$versionPrefix = $xmlDoc.Project.PropertyGroup[0].VersionPrefix
5859

59-
# We assume that the Version takes the form of 'x.y.z' with an optional '-preview.n' appended for preview releases.
60-
Write-Host "The highest published version of $packageName is $highestPublishedVersion"
61-
62-
# Set or increment the VersionSuffix.
63-
if ($highestPublishedVersion.Indexof('-preview') -eq -1) {
60+
# Only consider versions that match the version prefix in the csproj.
61+
$versionmatches = $nugetIndex.versions -match $versionPrefix
62+
if ($versionmatches.Count -eq 0) {
63+
# if the version hasn't been published, we need to add a preview versionSuffix.
64+
# Add an initial preview versionSuffix
6465
$versionSuffixString = 'preview.1' # Build applies the hyphen
6566
}
66-
else {
67+
else {
68+
# We have published preview versions for this version prefix.
69+
70+
# Only look at preview versions, also to referred to as versionSuffix
71+
$versionmatches = $versionmatches -match 'preview'
72+
73+
# Assumption: the API returns the versions in order.
74+
$highestPublishedVersion = $versionmatches[$versionmatches.Count - 1]
75+
76+
# We assume that the Version takes the form of 'x.y.z' with an optional '-preview.n' appended for preview releases.
77+
Write-Host "The highest published version of $packageName is $highestPublishedVersion"
78+
79+
# if the version has been published with a versionSuffix, then increment the suffix.
6780
# A preview has been previously released. Let's increment the VersionSuffix.
6881
$currentPreviewVersion = [int]$highestPublishedVersion.Split('-')[1].Split('.')[1]
6982

@@ -72,7 +85,7 @@ else {
7285
$versionSuffixString = "preview.{0}" -f $incrementedPreviewVersion
7386
}
7487

75-
Write-Host "The preview version is now $versionSuffixString" -ForegroundColor Green
88+
Write-Host "The preview version is now $versionPrefix-$versionSuffixString" -ForegroundColor Green
7689

7790
$xmlDoc.Project.PropertyGroup[0].VersionSuffix = $versionSuffixString
7891
$xmlDoc.Save($projectPath)

src/Microsoft.Graph.Core/Microsoft.Graph.Core.csproj

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
55
<AssemblyTitle>Microsoft Graph Core Client Library</AssemblyTitle>
66
<Authors>Microsoft</Authors>
7-
<TargetFrameworks>netstandard1.1;net45;Xamarin.iOS10;MonoAndroid70</TargetFrameworks>
7+
<TargetFrameworks>netstandard1.1;net45;Xamarin.iOS10;Xamarin.Mac20;MonoAndroid70</TargetFrameworks>
88
<PreserveCompilationContext>false</PreserveCompilationContext>
99
<AssemblyName>Microsoft.Graph.Core</AssemblyName>
1010
<PackageId>Microsoft.Graph.Core</PackageId>
@@ -19,10 +19,11 @@
1919
<DelaySign>false</DelaySign>
2020
<AssemblyOriginatorKeyFile>35MSSharedLib1024.snk</AssemblyOriginatorKeyFile>
2121
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
22-
<VersionPrefix>1.20.1</VersionPrefix>
22+
<VersionPrefix>1.21.0</VersionPrefix>
2323
<VersionSuffix></VersionSuffix>
2424
<PackageReleaseNotes>
25-
- Fix for DeltaResponseHandler not handling array of primitives. #93
25+
- Support Xamarin.Mac20.
26+
- Fix CompressionHandler feature.
2627
</PackageReleaseNotes>
2728
</PropertyGroup>
2829
<!--We manually configure LanguageTargets for Xamarin due to .Net SDK TFMs limitation https://github.com/dotnet/sdk/issues/491 -->
@@ -33,6 +34,13 @@
3334
<DebugType>full</DebugType>
3435
<LanguageTargets>$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets</LanguageTargets>
3536
</PropertyGroup>
37+
<PropertyGroup Condition="'$(TargetFramework)' == 'Xamarin.Mac20'">
38+
<TargetFrameworkIdentifier>Xamarin.Mac</TargetFrameworkIdentifier>
39+
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
40+
<DefineConstants>$(DefineConstants);macOS</DefineConstants>
41+
<DebugType>full</DebugType>
42+
<LanguageTargets>$(MSBuildExtensionsPath)\Xamarin\Mac\Xamarin.Mac.CSharp.targets</LanguageTargets>
43+
</PropertyGroup>
3644
<PropertyGroup Condition="'$(TargetFramework)' == 'MonoAndroid70'">
3745
<TargetFrameworkIdentifier>MonoAndroid</TargetFrameworkIdentifier>
3846
<TargetFrameworkVersion>v7.0</TargetFrameworkVersion>
@@ -77,6 +85,15 @@
7785
<PackageReference Include="Newtonsoft.Json" Version="11.0.1" />
7886
<PackageReference Include="System.Net.Http" Version="4.3.3" />
7987
</ItemGroup>
88+
<ItemGroup Condition=" '$(TargetFramework)' == 'Xamarin.Mac20' ">
89+
<Reference Include="Xamarin.Mac" />
90+
<Reference Include="System" />
91+
<Reference Include="System.Core" />
92+
<Reference Include="System.Xml" />
93+
<Reference Include="Microsoft.CSharp" />
94+
<PackageReference Include="Newtonsoft.Json" Version="11.0.1" />
95+
<PackageReference Include="System.Net.Http" Version="4.3.3" />
96+
</ItemGroup>
8097
<ItemGroup Condition=" '$(TargetFramework)' == 'MonoAndroid70' ">
8198
<Reference Include="Mono.Android" />
8299
<Reference Include="System" />

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

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,15 +179,20 @@ internal static (HttpMessageHandler Pipeline, FeatureFlag FeatureFlags) CreatePi
179179
throw new ArgumentNullException(nameof(handlers), "DelegatingHandler array contains null item.");
180180
}
181181

182+
#if iOS || macOS
182183
#if iOS
183-
// Skip CompressionHandler since NSUrlSessionHandler automatically handles decompression on iOS and it can't be turned off.
184+
// Skip CompressionHandler since NSUrlSessionHandler automatically handles decompression on iOS and macOS and it can't be turned off.
184185
// See issue https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/481 for more details.
185186
if (finalHandler.GetType().Equals(typeof(NSUrlSessionHandler)) && handler.GetType().Equals(typeof(CompressionHandler)))
187+
#elif macOS
188+
if (finalHandler.GetType().Equals(typeof(Foundation.NSUrlSessionHandler)) && handler.GetType().Equals(typeof(CompressionHandler)))
189+
#endif
186190
{
187191
// Skip chaining of CompressionHandler.
188192
continue;
189193
}
190194
#endif
195+
191196
// Check for duplicate handler by type.
192197
if (!existingHandlerTypes.Add(handler.GetType()))
193198
{
@@ -206,18 +211,20 @@ internal static (HttpMessageHandler Pipeline, FeatureFlag FeatureFlags) CreatePi
206211
}
207212

208213
/// <summary>
209-
/// Gets a platform's native http handler i.e. NSUrlSessionHandler for Xamarin.iOS, AndroidClientHandler for Xamarin.Android and HttpClientHandler for others.
214+
/// Gets a platform's native http handler i.e. NSUrlSessionHandler for Xamarin.iOS and Xamarin.Mac, AndroidClientHandler for Xamarin.Android and HttpClientHandler for others.
210215
/// </summary>
211216
/// <param name="proxy">The proxy to be used with created client.</param>
212217
/// <returns>
213-
/// 1. NSUrlSessionHandler for Xamarin.iOS
218+
/// 1. NSUrlSessionHandler for Xamarin.iOS and Xamarin.Mac
214219
/// 2. AndroidClientHandler for Xamarin.Android.
215220
/// 3. HttpClientHandler for other platforms.
216221
/// </returns>
217222
internal static HttpMessageHandler GetNativePlatformHttpHandler(IWebProxy proxy = null)
218223
{
219224
#if iOS
220225
return new NSUrlSessionHandler { AllowAutoRedirect = false };
226+
#elif macOS
227+
return new Foundation.NSUrlSessionHandler { AllowAutoRedirect = false };
221228
#elif ANDROID
222229
return new Xamarin.Android.Net.AndroidClientHandler { Proxy = proxy, AllowAutoRedirect = false, AutomaticDecompression = DecompressionMethods.None };
223230
#else

src/Microsoft.Graph.Core/Requests/Middleware/CompressionHandler.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace Microsoft.Graph
99
using System.Threading.Tasks;
1010
using System.Net.Http.Headers;
1111
using System.IO.Compression;
12+
using System.Collections.Generic;
1213

1314
/// <summary>
1415
/// A <see cref="DelegatingHandler"/> implementation that handles compression.
@@ -53,7 +54,13 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
5354
// Decompress response content when Content-Encoding: gzip header is present.
5455
if (ShouldDecompressContent(response))
5556
{
56-
response.Content = new StreamContent(new GZipStream(await response.Content.ReadAsStreamAsync(), CompressionMode.Decompress));
57+
StreamContent streamContent = new StreamContent(new GZipStream(await response.Content.ReadAsStreamAsync(), CompressionMode.Decompress));
58+
// Copy Content Headers to the destination stream content
59+
foreach (var httpContentHeader in response.Content.Headers)
60+
{
61+
streamContent.Headers.TryAddWithoutValidation(httpContentHeader.Key, httpContentHeader.Value);
62+
}
63+
response.Content = streamContent;
5764
}
5865

5966
return response;

tests/Microsoft.Graph.DotnetCore.Core.Test/Microsoft.Graph.DotnetCore.Core.Test.csproj

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
3-
<TargetFrameworks>netcoreapp3.1;Xamarin.iOS10;MonoAndroid70</TargetFrameworks>
3+
<TargetFrameworks>netcoreapp3.1;Xamarin.iOS10;Xamarin.Mac20;MonoAndroid70;</TargetFrameworks>
44
<AssemblyName>Microsoft.Graph.DotnetCore.Core.Test</AssemblyName>
55
<PackageId>Microsoft.Graph.DotnetCore.Core.Test</PackageId>
66
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
@@ -21,6 +21,14 @@
2121
<DebugType>full</DebugType>
2222
<LanguageTargets>$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets</LanguageTargets>
2323
</PropertyGroup>
24+
25+
<PropertyGroup Condition="'$(TargetFramework)' == 'Xamarin.Mac20'">
26+
<TargetFrameworkIdentifier>Xamarin.Mac</TargetFrameworkIdentifier>
27+
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
28+
<DefineConstants>$(DefineConstants);macOS</DefineConstants>
29+
<DebugType>full</DebugType>
30+
<LanguageTargets>$(MSBuildExtensionsPath)\Xamarin\Mac\Xamarin.Mac.CSharp.targets</LanguageTargets>
31+
</PropertyGroup>
2432

2533
<PropertyGroup Condition="'$(TargetFramework)' == 'MonoAndroid70'">
2634
<TargetFrameworkIdentifier>MonoAndroid</TargetFrameworkIdentifier>
@@ -58,6 +66,16 @@
5866
<Reference Include="System.Runtime.Serialization" />
5967
<PackageReference Include="System.Net.Http" Version="4.3.3" />
6068
</ItemGroup>
69+
70+
<ItemGroup Condition=" '$(TargetFramework)' == 'Xamarin.Mac20' ">
71+
<Reference Include="Microsoft.CSharp" />
72+
<Reference Include="Xamarin.Mac" />
73+
<Reference Include="System" />
74+
<Reference Include="System.Core" />
75+
<Reference Include="System.Xml" />
76+
<Reference Include="System.Runtime.Serialization" />
77+
<PackageReference Include="System.Net.Http" Version="4.3.3" />
78+
</ItemGroup>
6179

6280
<ItemGroup Condition=" '$(TargetFramework)' == 'MonoAndroid70' ">
6381
<Reference Include="Microsoft.CSharp" />
@@ -69,7 +87,7 @@
6987
<PackageReference Include="System.Net.Http" Version="4.3.3" />
7088
</ItemGroup>
7189

72-
<ItemGroup>
90+
<ItemGroup>
7391
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
7492
</ItemGroup>
7593
</Project>

tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/GraphClientFactoryTests.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,18 @@ public void Dispose()
4141
// and 'GraphClientFactory.DefaultHttpHandler' can easily be modified
4242
// by other tests since it's a static delegate.
4343

44-
#if iOS
44+
#if iOS || macOS
4545
[Fact]
4646
public void Should_CreatePipeline_Without_CompressionHandler()
4747
{
4848
using (AuthenticationHandler authenticationHandler = (AuthenticationHandler)GraphClientFactory.CreatePipeline(handlers))
4949
using (RetryHandler retryHandler = (RetryHandler)authenticationHandler.InnerHandler)
5050
using (RedirectHandler redirectHandler = (RedirectHandler)retryHandler.InnerHandler)
51+
#if iOS
5152
using (NSUrlSessionHandler innerMost = (NSUrlSessionHandler)redirectHandler.InnerHandler)
53+
#elif macOS
54+
using (Foundation.NSUrlSessionHandler innerMost = (Foundation.NSUrlSessionHandler)redirectHandler.InnerHandler)
55+
#endif
5256
{
5357
Assert.NotNull(authenticationHandler);
5458
Assert.NotNull(retryHandler);
@@ -57,7 +61,11 @@ public void Should_CreatePipeline_Without_CompressionHandler()
5761
Assert.IsType<AuthenticationHandler>(authenticationHandler);
5862
Assert.IsType<RetryHandler>(retryHandler);
5963
Assert.IsType<RedirectHandler>(redirectHandler);
64+
#if iOS
6065
Assert.IsType<NSUrlSessionHandler>(innerMost);
66+
#elif macOS
67+
Assert.IsType<Foundation.NSUrlSessionHandler>(innerMost);
68+
#endif
6169
}
6270
}
6371
#else

tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/HttpProviderTests.cs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ public void HttpProvider_DefaultConstructor()
115115
#elif iOS
116116
Assert.IsType<NSUrlSessionHandler>(defaultHttpProvider.httpMessageHandler);
117117
Assert.False((defaultHttpProvider.httpMessageHandler as NSUrlSessionHandler).AllowAutoRedirect);
118+
#elif macOS
119+
Assert.IsType<Foundation.NSUrlSessionHandler>(defaultHttpProvider.httpMessageHandler);
120+
Assert.False((defaultHttpProvider.httpMessageHandler as Foundation.NSUrlSessionHandler).AllowAutoRedirect);
118121
#else
119122
Assert.IsType<HttpClientHandler>(defaultHttpProvider.httpMessageHandler);
120123
Assert.False((defaultHttpProvider.httpMessageHandler as HttpClientHandler).AllowAutoRedirect);
@@ -554,12 +557,16 @@ public void HttpProvider_CustomAndroidClientHandler()
554557
#endif
555558
#endregion
556559

557-
#region iOS
558-
#if iOS
560+
#region iOS_macOS
561+
#if iOS || macOS
559562
[Fact]
560563
public void HttpProvider_CustomNSUrlSessionHandler()
561564
{
565+
#if iOS
562566
using (var httpClientHandler = new NSUrlSessionHandler())
567+
#elif macOS
568+
using (var httpClientHandler = new Foundation.NSUrlSessionHandler())
569+
#endif
563570
using (var httpProvider = new HttpProvider(httpClientHandler, false, null))
564571
{
565572
Assert.Equal(httpClientHandler, httpProvider.httpMessageHandler);
@@ -568,6 +575,6 @@ public void HttpProvider_CustomNSUrlSessionHandler()
568575
}
569576
}
570577
#endif
571-
#endregion
572-
}
573-
}
578+
#endregion
579+
}
580+
}

tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Middleware/CompressionHandlerTests.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ namespace Microsoft.Graph.DotnetCore.Core.Test.Requests.Middleware
1212
using System.Threading.Tasks;
1313
using System.Threading;
1414
using System.Net.Http.Headers;
15+
using System.Linq;
16+
using System.Collections.Generic;
17+
using Xunit.Abstractions;
1518

1619
public class CompressionHandlerTests : IDisposable
1720
{
@@ -94,5 +97,57 @@ public async Task CompressionHandler_should_not_decompress_response_without_cont
9497
Assert.Same(httpRequestMessage, compressedResponse.RequestMessage);
9598
Assert.NotEqual(stringToCompress, responseContentString);
9699
}
100+
101+
/// <summary>
102+
/// Compression Handler should keep content headers after decompression
103+
/// </summary>
104+
/// <returns></returns>
105+
[Fact]
106+
public async Task CompressionHandler_should_keep_headers_after_decompression()
107+
{
108+
HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://example.org/foo");
109+
110+
string stringToCompress = "Microsoft Graph";
111+
StringContent stringContent = new StringContent(stringToCompress);
112+
stringContent.Headers.ContentType = new MediaTypeHeaderValue(CoreConstants.MimeTypeNames.Application.Json);
113+
114+
HttpResponseMessage httpResponse = new HttpResponseMessage(HttpStatusCode.OK)
115+
{
116+
Content = new MockCompressedContent(stringContent)
117+
};
118+
httpResponse.Content.Headers.ContentEncoding.Add(CoreConstants.Encoding.GZip);
119+
httpResponse.Headers.CacheControl = new CacheControlHeaderValue { Private = true };
120+
// Examples of Custom Headers returned by Microsoft Graph
121+
httpResponse.Headers.Add(CoreConstants.Headers.ClientRequestId, Guid.NewGuid().ToString());
122+
httpResponse.Headers.Add("request-id", Guid.NewGuid().ToString());
123+
httpResponse.Headers.Add("OData-Version", "4.0");
124+
125+
this.testHttpMessageHandler.SetHttpResponse(httpResponse);
126+
127+
HttpResponseMessage compressedResponse = await this.invoker.SendAsync(httpRequestMessage, new CancellationToken());
128+
string decompressedResponseString = await compressedResponse.Content.ReadAsStringAsync();
129+
130+
Assert.Equal(decompressedResponseString, stringToCompress);
131+
132+
// Ensure that headers in the compressedResponse are the same as in the original, expected response.
133+
Assert.NotEmpty(compressedResponse.Headers);
134+
Assert.NotEmpty(compressedResponse.Content.Headers);
135+
Assert.Equal(httpResponse.Headers, compressedResponse.Headers, new HttpHeaderComparer());
136+
Assert.Equal(httpResponse.Content.Headers, compressedResponse.Content.Headers, new HttpHeaderComparer());
137+
}
138+
139+
internal class HttpHeaderComparer : IEqualityComparer<KeyValuePair<string, IEnumerable<string>>>
140+
{
141+
public bool Equals(KeyValuePair<string, IEnumerable<string>> x, KeyValuePair<string, IEnumerable<string>> y)
142+
{
143+
// For each key, the collection of header values should be equal.
144+
return x.Key == y.Key && x.Value.SequenceEqual(y.Value);
145+
}
146+
147+
public int GetHashCode(KeyValuePair<string, IEnumerable<string>> obj)
148+
{
149+
return obj.Key.GetHashCode();
150+
}
151+
}
97152
}
98153
}

0 commit comments

Comments
 (0)