@@ -112,6 +112,8 @@ public static string GetHeaderValue (this NSHttpCookie cookie)
112112 public partial class NSUrlSessionHandler : HttpMessageHandler {
113113 private const string SetCookie = "Set-Cookie" ;
114114 private const string Cookie = "Cookie" ;
115+ private const string ContentEncodingHeaderName = "Content-Encoding" ;
116+ private const string ContentLengthHeaderName = "Content-Length" ;
115117 private CookieContainer ? cookieContainer ;
116118 readonly Dictionary < string , string > headerSeparators = new Dictionary < string , string > {
117119 [ "User-Agent" ] = " " ,
@@ -869,6 +871,24 @@ public bool UseProxy {
869871 }
870872 }
871873
874+ static bool HasCompressedEncoding ( string headerValue )
875+ {
876+ foreach ( var encoding in headerValue . Split ( ',' ) ) {
877+ if ( IsCompressedEncoding ( encoding . Trim ( ) ) )
878+ return true ;
879+ }
880+ return false ;
881+ }
882+
883+ static bool IsCompressedEncoding ( string encoding )
884+ {
885+ return string . Equals ( encoding , "gzip" , StringComparison . OrdinalIgnoreCase )
886+ || string . Equals ( encoding , "deflate" , StringComparison . OrdinalIgnoreCase )
887+ || string . Equals ( encoding , "br" , StringComparison . OrdinalIgnoreCase )
888+ || string . Equals ( encoding , "compress" , StringComparison . OrdinalIgnoreCase )
889+ || string . Equals ( encoding , "zstd" , StringComparison . OrdinalIgnoreCase ) ;
890+ }
891+
872892 partial class NSUrlSessionHandlerDelegate : NSUrlSessionDataDelegate {
873893 readonly NSUrlSessionHandler sessionHandler ;
874894
@@ -959,6 +979,17 @@ void DidReceiveResponseImpl (NSUrlSession session, NSUrlSessionDataTask dataTask
959979 if ( wasRedirected )
960980 httpResponse . RequestMessage . RequestUri = absoluteUri ;
961981
982+ // NSURLSession automatically decompresses content for all supported
983+ // encodings (gzip, deflate, br, zstd, etc.), and there's no way to
984+ // turn it off. After decompression, Content-Encoding and Content-Length
985+ // are stale (Content-Length refers to compressed size), so we need to
986+ // remove them to match the behavior of other HTTP handlers:
987+ // - SocketsHttpHandler: https://github.com/dotnet/runtime/blob/b2974279efd059efaa17f359ed4b266b1c705721/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/DecompressionHandler.cs#L122-L123
988+ // - AndroidMessageHandler: https://github.com/dotnet/android/pull/7785
989+ // Ref: https://github.com/dotnet/macios/issues/23958
990+ string ? contentEncodingValue = null ;
991+ string ? contentLengthValue = null ;
992+
962993 foreach ( var v in urlResponse . AllHeaderFields ) {
963994 var key = v . Key ? . ToString ( ) ;
964995 var value = v . Value ? . ToString ( ) ;
@@ -968,10 +999,31 @@ void DidReceiveResponseImpl (NSUrlSession session, NSUrlSessionDataTask dataTask
968999 // NSUrlSession tries to be smart with cookies, we will not use the raw value but the ones provided by the cookie storage
9691000 if ( key == SetCookie ) continue ;
9701001
1002+ if ( string . Equals ( key , ContentEncodingHeaderName , StringComparison . OrdinalIgnoreCase ) ) {
1003+ contentEncodingValue = value ;
1004+ continue ;
1005+ }
1006+ if ( string . Equals ( key , ContentLengthHeaderName , StringComparison . OrdinalIgnoreCase ) ) {
1007+ contentLengthValue = value ;
1008+ continue ;
1009+ }
1010+
9711011 httpResponse . Headers . TryAddWithoutValidation ( key , value ) ;
9721012 httpResponse . Content . Headers . TryAddWithoutValidation ( key , value ) ;
9731013 }
9741014
1015+ var contentWasDecompressed = contentEncodingValue is not null && HasCompressedEncoding ( contentEncodingValue ) ;
1016+ if ( ! contentWasDecompressed ) {
1017+ if ( contentEncodingValue is not null ) {
1018+ httpResponse . Headers . TryAddWithoutValidation ( ContentEncodingHeaderName , contentEncodingValue ) ;
1019+ httpResponse . Content . Headers . TryAddWithoutValidation ( ContentEncodingHeaderName , contentEncodingValue ) ;
1020+ }
1021+ if ( contentLengthValue is not null ) {
1022+ httpResponse . Headers . TryAddWithoutValidation ( ContentLengthHeaderName , contentLengthValue ) ;
1023+ httpResponse . Content . Headers . TryAddWithoutValidation ( ContentLengthHeaderName , contentLengthValue ) ;
1024+ }
1025+ }
1026+
9751027 // it might be confusing that we are not using the managed CookieStore here, this is ONLY for those cookies that have been retrieved from
9761028 // the server via a Set-Cookie header, the managed container does not know a thing about this and apple is storing them in the native
9771029 // cookie container. Once we have the cookies from the response, we need to update the managed cookie container
0 commit comments