Skip to content

Commit 1fc82b6

Browse files
rolfbjarneCopilot
andauthored
[Foundation] Add KeepHeadersAfterDecompression app context switch to NSUrlSessionHandler (#24957)
Add an opt-out switch 'Foundation.NSUrlSessionHandler.KeepHeadersAfterDecompression' that, when enabled, preserves the original Content-Encoding and Content-Length headers on auto-decompressed responses instead of removing them. This allows apps that depend on the original headers to restore the previous behavior. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 0eb08a7 commit 1fc82b6

2 files changed

Lines changed: 46 additions & 2 deletions

File tree

src/Foundation/NSUrlSessionHandler.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,9 @@ void DidReceiveResponseImpl (NSUrlSession session, NSUrlSessionDataTask dataTask
987987
// - SocketsHttpHandler: https://github.com/dotnet/runtime/blob/b2974279efd059efaa17f359ed4b266b1c705721/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/DecompressionHandler.cs#L122-L123
988988
// - AndroidMessageHandler: https://github.com/dotnet/android/pull/7785
989989
// Ref: https://github.com/dotnet/macios/issues/23958
990+
// This behavior can be opted out of by setting the
991+
// Foundation.NSUrlSessionHandler.KeepHeadersAfterDecompression switch.
992+
var keepHeaders = AppContext.TryGetSwitch ("Foundation.NSUrlSessionHandler.KeepHeadersAfterDecompression", out var keepHeadersEnabled) && keepHeadersEnabled;
990993
string? contentEncodingValue = null;
991994
string? contentLengthValue = null;
992995

@@ -999,11 +1002,11 @@ void DidReceiveResponseImpl (NSUrlSession session, NSUrlSessionDataTask dataTask
9991002
// NSUrlSession tries to be smart with cookies, we will not use the raw value but the ones provided by the cookie storage
10001003
if (key == SetCookie) continue;
10011004

1002-
if (string.Equals (key, ContentEncodingHeaderName, StringComparison.OrdinalIgnoreCase)) {
1005+
if (!keepHeaders && string.Equals (key, ContentEncodingHeaderName, StringComparison.OrdinalIgnoreCase)) {
10031006
contentEncodingValue = value;
10041007
continue;
10051008
}
1006-
if (string.Equals (key, ContentLengthHeaderName, StringComparison.OrdinalIgnoreCase)) {
1009+
if (!keepHeaders && string.Equals (key, ContentLengthHeaderName, StringComparison.OrdinalIgnoreCase)) {
10071010
contentLengthValue = value;
10081011
continue;
10091012
}

tests/monotouch-test/System.Net.Http/NSUrlSessionHandlerTest.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,47 @@ public void NonCompressedResponseHasContentLength ()
9393
Assert.IsTrue (body.Length > 0, "Response body should not be empty");
9494
}
9595

96+
// https://github.com/dotnet/macios/issues/23958
97+
[Test]
98+
public void KeepHeadersAfterDecompressionSwitch ()
99+
{
100+
bool hasContentEncoding = false;
101+
bool hasContentLength = false;
102+
string body = "";
103+
104+
AppContext.SetSwitch ("Foundation.NSUrlSessionHandler.KeepHeadersAfterDecompression", true);
105+
try {
106+
var done = TestRuntime.TryRunAsync (TimeSpan.FromSeconds (30), async () => {
107+
using var handler = new NSUrlSessionHandler ();
108+
using var client = new HttpClient (handler);
109+
using var request = new HttpRequestMessage (HttpMethod.Get, $"{NetworkResources.Httpbin.Url}/gzip");
110+
request.Headers.TryAddWithoutValidation ("Accept-Encoding", "gzip");
111+
var response = await client.SendAsync (request, HttpCompletionOption.ResponseHeadersRead);
112+
113+
if (!response.IsSuccessStatusCode) {
114+
Assert.Inconclusive ($"Request failed with status {response.StatusCode}");
115+
return;
116+
}
117+
118+
hasContentEncoding = response.Content.Headers.ContentEncoding.Count > 0;
119+
hasContentLength = response.Content.Headers.ContentLength is not null;
120+
body = await response.Content.ReadAsStringAsync ();
121+
}, out var ex);
122+
123+
if (!done) {
124+
TestRuntime.IgnoreInCI ("Transient network failure - ignore in CI");
125+
Assert.Inconclusive ("Request timed out.");
126+
}
127+
TestRuntime.IgnoreInCIIfBadNetwork (ex);
128+
Assert.IsNull (ex, $"Exception: {ex}");
129+
Assert.IsTrue (hasContentEncoding, "Content-Encoding header should be preserved when KeepHeadersAfterDecompression is enabled");
130+
Assert.IsTrue (hasContentLength, "Content-Length header should be preserved when KeepHeadersAfterDecompression is enabled");
131+
Assert.IsTrue (body.Contains ("\"gzipped\"", StringComparison.OrdinalIgnoreCase), "Response body should contain decompressed gzip data");
132+
} finally {
133+
AppContext.SetSwitch ("Foundation.NSUrlSessionHandler.KeepHeadersAfterDecompression", false);
134+
}
135+
}
136+
96137
// https://github.com/dotnet/macios/issues/24376
97138
[Test]
98139
public void DisposeAndRecreateBackgroundSessionHandler ()

0 commit comments

Comments
 (0)