From 397d41b39c14808c8a2f80eb74ca945400215aa6 Mon Sep 17 00:00:00 2001 From: Vitek Karas <10670590+vitek-karas@users.noreply.github.com> Date: Fri, 2 Oct 2026 11:52:33 +0200 Subject: [PATCH] Correct CoreFoundation number interop ABI Match CFNumberType to the 64-bit CFIndex used by supported Apple targets and CFNumberGetValue to the native one-byte Boolean return. Add network-independent PAC proxy-port regression coverage using the shared production interop. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../OSX/Interop.CoreFoundation.CFNumber.cs | 5 +- .../tests/UnitTests/CFProxyTest.cs | 52 +++++++++++++++++++ .../System.Net.Http.Unit.Tests.csproj | 23 ++++++++ 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 src/libraries/System.Net.Http/tests/UnitTests/CFProxyTest.cs diff --git a/src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFNumber.cs b/src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFNumber.cs index 22d51bdd11cc36..52d3d0621ad426 100644 --- a/src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFNumber.cs +++ b/src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFNumber.cs @@ -11,12 +11,13 @@ internal static partial class Interop { internal static partial class CoreFoundation { - internal enum CFNumberType + // CFNumberType is based on CFIndex, which is 64-bit on supported Apple platforms. + internal enum CFNumberType : long { kCFNumberIntType = 9, } [LibraryImport(Libraries.CoreFoundationLibrary)] - private static unsafe partial int CFNumberGetValue(IntPtr handle, CFNumberType type, int* value); + private static unsafe partial byte CFNumberGetValue(IntPtr handle, CFNumberType type, int* value); } } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/CFProxyTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/CFProxyTest.cs new file mode 100644 index 00000000000000..f18e80d23340c4 --- /dev/null +++ b/src/libraries/System.Net.Http/tests/UnitTests/CFProxyTest.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; +using Xunit; + +internal static partial class Interop +{ + internal static partial class Libraries + { + // The unit-test assembly also includes Windows interop for the proxy fakes. + internal const string CoreFoundationLibrary = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"; + internal const string CFNetworkLibrary = "/System/Library/Frameworks/CFNetwork.framework/CFNetwork"; + } +} + +namespace System.Net.Http.Tests +{ + public partial class CFProxyTest + { + [LibraryImport(Interop.Libraries.CFNetworkLibrary)] + private static partial SafeCFArrayHandle CFNetworkCopyProxiesForAutoConfigurationScript( + SafeCreateHandle script, SafeCreateHandle targetUrl, out SafeCFErrorHandle error); + + [Theory] + [InlineData("PROXY 127.0.0.1:1", 1)] + [InlineData("PROXY 127.0.0.1:80", 80)] + [InlineData("PROXY 127.0.0.1:443", 443)] + [InlineData("PROXY 127.0.0.1:8080", 8080)] + [InlineData("PROXY 127.0.0.1:65535", 65535)] + [InlineData("DIRECT", -1)] + public void ProxyAutoConfiguration_ReadsPortNumber(string scriptResult, int expectedPort) + { + using SafeCreateHandle script = Interop.CoreFoundation.CFStringCreateWithCString( + $"function FindProxyForURL(url, host) {{ return '{scriptResult}'; }}"); + using SafeCreateHandle url = Interop.CoreFoundation.CFURLCreateWithString("https://example.invalid/"); + using SafeCFArrayHandle proxies = CFNetworkCopyProxiesForAutoConfigurationScript(script, url, out SafeCFErrorHandle error); + using (error) + { + Assert.True(error.IsInvalid, Interop.CoreFoundation.GetErrorDescription(error)); + Assert.False(proxies.IsInvalid); + Assert.Equal(1, Interop.CoreFoundation.CFArrayGetCount(proxies)); + + using SafeCFDictionaryHandle dictionary = new SafeCFDictionaryHandle( + Interop.CoreFoundation.CFArrayGetValueAtIndex(proxies, 0), ownsHandle: false); + Interop.CoreFoundation.CFProxy proxy = new Interop.CoreFoundation.CFProxy(dictionary); + Assert.Equal(expectedPort, proxy.PortNumber); + } + } + } +} diff --git a/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj b/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj index 43167dc48c6007..c3274f64237d71 100755 --- a/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj +++ b/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj @@ -430,4 +430,27 @@ + + + + + + + + + + + + +