Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
using System.Text;
using Microsoft.Win32.SafeHandles;

// Declared as signed long, which has sizeof(void*) on OSX.
using CFIndex = System.IntPtr;

internal static partial class Interop
{
internal static partial class CoreFoundation
Expand All @@ -16,7 +19,14 @@ internal enum CFNumberType
kCFNumberIntType = 9,
}

[LibraryImport(Libraries.CoreFoundationLibrary)]
private static unsafe partial int CFNumberGetValue(IntPtr handle, CFNumberType type, int* value);
// CFNumberType is declared as CF_ENUM(CFIndex, CFNumberType), so it has to be passed as a CFIndex.
// Passing the int-sized managed enum leaves the upper bits of the argument register unspecified.
[LibraryImport(Libraries.CoreFoundationLibrary, EntryPoint = "CFNumberGetValue")]
private static unsafe partial int _CFNumberGetValue(IntPtr handle, CFIndex type, int* value);

private static unsafe int CFNumberGetValue(IntPtr handle, CFNumberType type, int* value)
{
return _CFNumberGetValue(handle, (CFIndex)type, value);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,16 @@ private static Uri GetProxyUri(string scheme, CFProxy proxy)
public Uri? GetProxy(Uri targetUri)
{
using (SafeCFDictionaryHandle systemProxySettings = CFNetworkCopySystemProxySettings())
{
return GetProxy(targetUri, systemProxySettings);
}
}

// Resolves the proxy for targetUri from proxy settings in the format returned by CFNetworkCopySystemProxySettings.
internal static Uri? GetProxy(Uri targetUri, SafeCFDictionaryHandle proxySettings)
{
using (SafeCreateHandle cfurl = CFURLCreateWithString(targetUri.AbsoluteUri))
using (SafeCFArrayHandle proxies = CFNetworkCopyProxiesForURL(cfurl, systemProxySettings))
using (SafeCFArrayHandle proxies = CFNetworkCopyProxiesForURL(cfurl, proxySettings))
{
long proxyCount = CFArrayGetCount(proxies);
for (int i = 0; i < proxyCount; i++)
Expand Down
24 changes: 0 additions & 24 deletions src/libraries/System.Net.Http/tests/UnitTests/Fakes/MacProxy.cs

This file was deleted.

117 changes: 117 additions & 0 deletions src/libraries/System.Net.Http/tests/UnitTests/MacProxyTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// 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;

namespace System.Net.Http.Tests
{
// Runs MacProxy against proxy settings created in-process, in the format returned by
// CFNetworkCopySystemProxySettings, so the tests don't depend on the machine's proxy configuration.
// The tests that return a proxy read its port with CFNumberGetValue, whose CFNumberType parameter is
// CFIndex-sized. Passing it as a 32-bit value crashed on the CoreCLR interpreter (iOS, tvOS, Mac Catalyst).
public partial class MacProxyTest
{
private static readonly Uri s_targetUri = new Uri("http://www.example.com/");
private static readonly Uri s_proxyUri = new Uri("http://127.0.0.1:8888/");

[Fact]
public void GetProxy_HttpProxy_ReturnsProxyUri()
{
using SafeCFDictionaryHandle settings = CreateProxySettings(
("kCFNetworkProxiesHTTPEnable", CreateNumber(1)),
("kCFNetworkProxiesHTTPProxy", Interop.CoreFoundation.CFStringCreateWithCString("127.0.0.1")),
("kCFNetworkProxiesHTTPPort", CreateNumber(8888)));

Assert.Equal(s_proxyUri, MacProxy.GetProxy(s_targetUri, settings));
}

[Fact]
public void GetProxy_ProxyAutoConfigurationScript_ReturnsProxyUri()
{
using SafeCFDictionaryHandle settings = CreateProxySettings(
("kCFNetworkProxiesProxyAutoConfigEnable", CreateNumber(1)),
("kCFNetworkProxiesProxyAutoConfigJavaScript", Interop.CoreFoundation.CFStringCreateWithCString(
"function FindProxyForURL(url, host) { return \"PROXY 127.0.0.1:8888\"; }")));

Assert.Equal(s_proxyUri, MacProxy.GetProxy(s_targetUri, settings));
}

[Fact]
public void GetProxy_ProxyAutoConfigurationScriptReturnsDirect_ReturnsNull()
{
using SafeCFDictionaryHandle settings = CreateProxySettings(
("kCFNetworkProxiesProxyAutoConfigEnable", CreateNumber(1)),
("kCFNetworkProxiesProxyAutoConfigJavaScript", Interop.CoreFoundation.CFStringCreateWithCString(
"function FindProxyForURL(url, host) { return \"DIRECT\"; }")));

Assert.Null(MacProxy.GetProxy(s_targetUri, settings));
}

[Fact]
public void GetProxy_NoProxy_ReturnsNull()
{
using SafeCFDictionaryHandle settings = CreateProxySettings();

Assert.Null(MacProxy.GetProxy(s_targetUri, settings));
}

private static unsafe SafeCreateHandle CreateNumber(int value) =>
CFNumberCreate(IntPtr.Zero, kCFNumberIntType, &value);

// Keys are the names of CFStringRef constants exported by CFNetwork. The values are released once the dictionary has retained them.
private static unsafe SafeCFDictionaryHandle CreateProxySettings(params (string Key, SafeCreateHandle Value)[] settings)
{
try
{
IntPtr cfNetwork = NativeLibrary.Load(Interop.Libraries.CFNetworkLibrary);
IntPtr coreFoundation = NativeLibrary.Load(Interop.Libraries.CoreFoundationLibrary);

IntPtr[] keys = new IntPtr[settings.Length];
IntPtr[] values = new IntPtr[settings.Length];
for (int i = 0; i < settings.Length; i++)
{
keys[i] = Marshal.ReadIntPtr(NativeLibrary.GetExport(cfNetwork, settings[i].Key));
values[i] = settings[i].Value.DangerousGetHandle();
}

fixed (IntPtr* pKeys = keys)
fixed (IntPtr* pValues = values)
{
return CFDictionaryCreate(IntPtr.Zero, pKeys, pValues, settings.Length,
NativeLibrary.GetExport(coreFoundation, "kCFTypeDictionaryKeyCallBacks"),
NativeLibrary.GetExport(coreFoundation, "kCFTypeDictionaryValueCallBacks"));
}
}
finally
{
foreach ((_, SafeCreateHandle value) in settings)
{
value.Dispose();
}
}
}

// CFNumberType is declared as CF_ENUM(CFIndex, CFNumberType).
private const nint kCFNumberIntType = 9;

[LibraryImport(Interop.Libraries.CoreFoundationLibrary)]
private static unsafe partial SafeCreateHandle CFNumberCreate(IntPtr allocator, nint theType, int* valuePtr);

[LibraryImport(Interop.Libraries.CoreFoundationLibrary)]
private static unsafe partial SafeCFDictionaryHandle CFDictionaryCreate(
IntPtr allocator, IntPtr* keys, IntPtr* values, nint numValues, IntPtr keyCallBacks, IntPtr valueCallBacks);
}
}

internal static partial class Interop
{
// Interop\OSX\Interop.Libraries.cs can't be compiled next to Interop\Windows\Interop.Libraries.cs
// (both define MsQuic), so define the libraries that the MacProxy interop uses.
internal static partial class Libraries
{
internal const string CoreFoundationLibrary = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation";
internal const string CFNetworkLibrary = "/System/Library/Frameworks/CFNetwork.framework/CFNetwork";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,6 @@
<Compile Include="DigestAuthenticationTests.cs" />
<Compile Include="Fakes\HttpClientHandler.cs" />
<Compile Include="Fakes\HttpTelemetry.cs" />
<Compile Include="Fakes\MacProxy.cs" Condition="'$(TargetPlatformIdentifier)' == 'osx' or '$(TargetPlatformIdentifier)' == 'ios' or '$(TargetPlatformIdentifier)' == 'tvos'" />
<Compile Include="Headers\AltSvcHeaderParserTest.cs" />
<Compile Include="Headers\AuthenticationHeaderParserTest.cs" />
<Compile Include="Headers\AuthenticationHeaderValueTest.cs" />
Expand Down Expand Up @@ -430,4 +429,29 @@
<Compile Include="..\..\..\System.Net.Http.WinHttpHandler\tests\UnitTests\TestServer.cs"
Link="WinHttpHandler\UnitTests\TestServer.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'osx' or '$(TargetPlatformIdentifier)' == 'ios' or '$(TargetPlatformIdentifier)' == 'tvos' or '$(TargetPlatformIdentifier)' == 'maccatalyst'">
<Compile Include="MacProxyTest.cs" />
<Compile Include="..\..\src\System\Net\Http\SocketsHttpHandler\MacProxy.cs"
Link="ProductionCode\System\Net\Http\SocketsHttpHandler\MacProxy.cs" />
<Compile Include="$(CommonPath)Interop\OSX\Interop.CoreFoundation.cs"
Link="ProductionCode\Common\Interop\OSX\Interop.CoreFoundation.cs" />
<Compile Include="$(CommonPath)Interop\OSX\Interop.CoreFoundation.CFArray.cs"
Link="ProductionCode\Common\Interop\OSX\Interop.CoreFoundation.CFArray.cs" />
<Compile Include="$(CommonPath)Interop\OSX\Interop.CoreFoundation.CFData.cs"
Link="ProductionCode\Common\Interop\OSX\Interop.CoreFoundation.CFData.cs" />
<Compile Include="$(CommonPath)Interop\OSX\Interop.CoreFoundation.CFDictionary.cs"
Link="ProductionCode\Common\Interop\OSX\Interop.CoreFoundation.CFDictionary.cs" />
<Compile Include="$(CommonPath)Interop\OSX\Interop.CoreFoundation.CFNumber.cs"
Link="ProductionCode\Common\Interop\OSX\Interop.CoreFoundation.CFNumber.cs" />
<Compile Include="$(CommonPath)Interop\OSX\Interop.CoreFoundation.CFProxy.cs"
Link="ProductionCode\Common\Interop\OSX\Interop.CoreFoundation.CFProxy.cs" />
<Compile Include="$(CommonPath)Interop\OSX\Interop.CoreFoundation.CFString.cs"
Link="ProductionCode\Common\Interop\OSX\Interop.CoreFoundation.CFString.cs" />
<Compile Include="$(CommonPath)Interop\OSX\Interop.CoreFoundation.CFUrl.cs"
Link="ProductionCode\Common\Interop\OSX\Interop.CoreFoundation.CFUrl.cs" />
<Compile Include="$(CommonPath)Interop\OSX\Interop.RunLoop.cs"
Link="ProductionCode\Common\Interop\OSX\Interop.RunLoop.cs" />
<Compile Include="$(CommonPath)Microsoft\Win32\SafeHandles\SafeCreateHandle.OSX.cs"
Link="ProductionCode\Common\Microsoft\Win32\SafeHandles\SafeCreateHandle.OSX.cs" />
</ItemGroup>
</Project>
Loading