diff --git a/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFArray.cs b/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFArray.cs
index d0c13510c015..dc6080646700 100644
--- a/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFArray.cs
+++ b/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFArray.cs
@@ -38,11 +38,16 @@ namespace Microsoft.Win32.SafeHandles
{
internal sealed class SafeCFArrayHandle : SafeHandle
{
- internal SafeCFArrayHandle()
+ private SafeCFArrayHandle()
: base(IntPtr.Zero, ownsHandle: true)
{
}
+ internal SafeCFArrayHandle(IntPtr handle, bool ownsHandle)
+ : base(handle, ownsHandle)
+ {
+ }
+
protected override bool ReleaseHandle()
{
Interop.CoreFoundation.CFRelease(handle);
diff --git a/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFDictionary.cs b/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFDictionary.cs
new file mode 100644
index 000000000000..79c2979732de
--- /dev/null
+++ b/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFDictionary.cs
@@ -0,0 +1,43 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+using Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static partial class CoreFoundation
+ {
+ [DllImport(Libraries.CoreFoundationLibrary)]
+ internal static extern IntPtr CFDictionaryGetValue(SafeCFDictionaryHandle handle, IntPtr key);
+ }
+}
+
+namespace Microsoft.Win32.SafeHandles
+{
+ internal sealed class SafeCFDictionaryHandle : SafeHandle
+ {
+ private SafeCFDictionaryHandle()
+ : base(IntPtr.Zero, ownsHandle: true)
+ {
+ }
+
+ internal SafeCFDictionaryHandle(IntPtr handle, bool ownsHandle)
+ : base(handle, ownsHandle)
+ {
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.CoreFoundation.CFRelease(handle);
+ SetHandle(IntPtr.Zero);
+ return true;
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero;
+ }
+}
diff --git a/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFNumber.cs b/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFNumber.cs
new file mode 100644
index 000000000000..c2bffd316dc9
--- /dev/null
+++ b/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFNumber.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+using Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static partial class CoreFoundation
+ {
+ internal enum CFNumberType
+ {
+ kCFNumberIntType = 9,
+ }
+
+ [DllImport(Libraries.CoreFoundationLibrary)]
+ private static extern int CFNumberGetValue(IntPtr handle, CFNumberType type, out int value);
+ }
+}
diff --git a/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFProxy.cs b/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFProxy.cs
new file mode 100644
index 000000000000..1f6cc07064e5
--- /dev/null
+++ b/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFProxy.cs
@@ -0,0 +1,145 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+using Microsoft.Win32.SafeHandles;
+
+using CFRunLoopSourceRef = System.IntPtr;
+
+internal static partial class Interop
+{
+ internal static partial class CoreFoundation
+ {
+ [DllImport(Libraries.CFNetworkLibrary)]
+ internal static extern SafeCFDictionaryHandle CFNetworkCopySystemProxySettings();
+
+ [DllImport(Libraries.CFNetworkLibrary)]
+ internal static extern SafeCFArrayHandle CFNetworkCopyProxiesForURL(SafeCreateHandle url, SafeCFDictionaryHandle proxySettings);
+
+ internal delegate void CFProxyAutoConfigurationResultCallback(IntPtr client, IntPtr proxyList, IntPtr error);
+
+ [DllImport(Libraries.CFNetworkLibrary)]
+ internal static extern CFRunLoopSourceRef CFNetworkExecuteProxyAutoConfigurationURL(
+ IntPtr proxyAutoConfigURL,
+ SafeCreateHandle targetURL,
+ CFProxyAutoConfigurationResultCallback cb,
+ ref CFStreamClientContext clientContext);
+
+ [DllImport(Libraries.CFNetworkLibrary)]
+ internal static extern CFRunLoopSourceRef CFNetworkExecuteProxyAutoConfigurationScript(
+ IntPtr proxyAutoConfigurationScript,
+ SafeCreateHandle targetURL,
+ CFProxyAutoConfigurationResultCallback cb,
+ ref CFStreamClientContext clientContext);
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct CFStreamClientContext
+ {
+ public IntPtr Version;
+ public IntPtr Info;
+ public IntPtr Retain;
+ public IntPtr Release;
+ public IntPtr CopyDescription;
+ }
+
+ internal class CFProxy
+ {
+ private SafeCFDictionaryHandle _dictionary;
+
+ internal static readonly string kCFProxyTypeAutoConfigurationURL;
+ internal static readonly string kCFProxyTypeAutoConfigurationJavaScript;
+ internal static readonly string kCFProxyTypeFTP;
+ internal static readonly string kCFProxyTypeHTTP;
+ internal static readonly string kCFProxyTypeHTTPS;
+ internal static readonly string kCFProxyTypeSOCKS;
+
+ private static readonly IntPtr kCFProxyAutoConfigurationJavaScriptKey;
+ private static readonly IntPtr kCFProxyAutoConfigurationURLKey;
+ private static readonly IntPtr kCFProxyHostNameKey;
+ private static readonly IntPtr kCFProxyPasswordKey;
+ private static readonly IntPtr kCFProxyPortNumberKey;
+ private static readonly IntPtr kCFProxyTypeKey;
+ private static readonly IntPtr kCFProxyUsernameKey;
+
+ static CFProxy()
+ {
+ IntPtr lib = NativeLibrary.Load(Interop.Libraries.CFNetworkLibrary);
+ if (lib != IntPtr.Zero)
+ {
+ kCFProxyTypeAutoConfigurationURL = LoadCFStringSymbol(lib, "kCFProxyTypeAutoConfigurationURL");
+ kCFProxyTypeAutoConfigurationJavaScript = LoadCFStringSymbol(lib, "kCFProxyTypeAutoConfigurationJavaScript");
+ kCFProxyTypeFTP = LoadCFStringSymbol(lib, "kCFProxyTypeFTP");
+ kCFProxyTypeHTTP = LoadCFStringSymbol(lib, "kCFProxyTypeHTTP");
+ kCFProxyTypeHTTPS = LoadCFStringSymbol(lib, "kCFProxyTypeHTTPS");
+ kCFProxyTypeSOCKS = LoadCFStringSymbol(lib, "kCFProxyTypeSOCKS");
+
+ kCFProxyAutoConfigurationJavaScriptKey = LoadSymbol(lib, "kCFProxyAutoConfigurationJavaScriptKey");
+ kCFProxyAutoConfigurationURLKey = LoadSymbol(lib, "kCFProxyAutoConfigurationURLKey");
+ kCFProxyHostNameKey = LoadSymbol(lib, "kCFProxyHostNameKey");
+ kCFProxyPasswordKey = LoadSymbol(lib, "kCFProxyPasswordKey");
+ kCFProxyPortNumberKey = LoadSymbol(lib, "kCFProxyPortNumberKey");
+ kCFProxyTypeKey = LoadSymbol(lib, "kCFProxyTypeKey");
+ kCFProxyUsernameKey = LoadSymbol(lib, "kCFProxyUsernameKey");
+ }
+ }
+
+ public CFProxy(SafeCFDictionaryHandle dictionary)
+ {
+ _dictionary = dictionary;
+ }
+
+ private static IntPtr LoadSymbol(IntPtr lib, string name)
+ {
+ IntPtr indirect = NativeLibrary.GetExport(lib, name);
+ return indirect == IntPtr.Zero ? IntPtr.Zero : Marshal.ReadIntPtr(indirect);
+ }
+
+ private static string LoadCFStringSymbol(IntPtr lib, string name)
+ {
+ using (SafeCFStringHandle cfString = new SafeCFStringHandle(LoadSymbol(lib, name), false))
+ {
+ Debug.Assert(!cfString.IsInvalid);
+ return Interop.CoreFoundation.CFStringToString(cfString);
+ }
+ }
+
+ private string GetString(IntPtr key)
+ {
+ IntPtr dictValue = CFDictionaryGetValue(_dictionary, key);
+ if (dictValue != IntPtr.Zero)
+ {
+ using (SafeCFStringHandle handle = new SafeCFStringHandle(dictValue, false))
+ {
+ return CFStringToString(handle);
+ }
+ }
+ return null;
+ }
+
+ public string ProxyType => GetString(kCFProxyTypeKey);
+ public string HostName => GetString(kCFProxyHostNameKey);
+ public string Username => GetString(kCFProxyUsernameKey);
+ public string Password => GetString(kCFProxyPasswordKey);
+
+ public int PortNumber
+ {
+ get
+ {
+ IntPtr dictValue = CFDictionaryGetValue(_dictionary, kCFProxyPortNumberKey);
+ if (dictValue != IntPtr.Zero && CFNumberGetValue(dictValue, CFNumberType.kCFNumberIntType, out int value) > 0)
+ {
+ return value;
+ }
+ return -1;
+ }
+ }
+
+ public IntPtr AutoConfigurationURL => CFDictionaryGetValue(_dictionary, kCFProxyAutoConfigurationURLKey);
+ public IntPtr AutoConfigurationJavaScript => CFDictionaryGetValue(_dictionary, kCFProxyAutoConfigurationJavaScriptKey);
+ }
+ }
+}
diff --git a/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFString.cs b/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFString.cs
index aa7a15054cc5..17fb307d32d1 100644
--- a/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFString.cs
+++ b/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFString.cs
@@ -91,6 +91,11 @@ internal SafeCFStringHandle()
{
}
+ internal SafeCFStringHandle(IntPtr handle, bool ownsHandle)
+ : base(handle, ownsHandle)
+ {
+ }
+
protected override bool ReleaseHandle()
{
Interop.CoreFoundation.CFRelease(handle);
diff --git a/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFUrl.cs b/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFUrl.cs
new file mode 100644
index 000000000000..88c2bbd0ac5b
--- /dev/null
+++ b/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFUrl.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+using Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static partial class CoreFoundation
+ {
+ [DllImport(Libraries.CoreFoundationLibrary)]
+ private static extern SafeCreateHandle CFURLCreateWithString(
+ IntPtr allocator,
+ SafeCreateHandle str,
+ IntPtr baseUrl);
+
+ internal static SafeCreateHandle CFURLCreateWithString(string url)
+ {
+ Debug.Assert(url != null);
+ using (SafeCreateHandle stringHandle = CFStringCreateWithCString(url))
+ {
+ return CFURLCreateWithString(IntPtr.Zero, stringHandle, IntPtr.Zero);
+ }
+ }
+ }
+}
diff --git a/src/Common/src/Interop/OSX/Interop.Libraries.cs b/src/Common/src/Interop/OSX/Interop.Libraries.cs
index 89f6a3f53755..ffac0b25dcde 100644
--- a/src/Common/src/Interop/OSX/Interop.Libraries.cs
+++ b/src/Common/src/Interop/OSX/Interop.Libraries.cs
@@ -7,7 +7,8 @@ internal static partial class Interop
internal static partial class Libraries
{
internal const string CoreFoundationLibrary = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation";
- internal const string CoreServicesLibrary = "/System/Library/Frameworks/CoreServices.framework/CoreServices";
+ internal const string CoreServicesLibrary = "/System/Library/Frameworks/CoreServices.framework/CoreServices";
+ internal const string CFNetworkLibrary = "/System/Library/Frameworks/CFNetwork.framework/CFNetwork";
internal const string libproc = "libproc";
internal const string LibSystemCommonCrypto = "/usr/lib/system/libcommonCrypto";
internal const string LibSystemKernel = "/usr/lib/system/libsystem_kernel";
diff --git a/src/Common/src/Interop/OSX/Interop.RunLoop.cs b/src/Common/src/Interop/OSX/Interop.RunLoop.cs
index 9257952b4499..f7c33442662c 100644
--- a/src/Common/src/Interop/OSX/Interop.RunLoop.cs
+++ b/src/Common/src/Interop/OSX/Interop.RunLoop.cs
@@ -34,6 +34,12 @@ internal static partial class RunLoop
#endif
internal extern static void CFRunLoopRun();
+ ///
+ /// Runs the current thread’s CFRunLoop object in a particular mode.
+ ///
+ [DllImport(Interop.Libraries.CoreFoundationLibrary)]
+ internal extern static int CFRunLoopRunInMode(CFStringRef mode, double seconds, int returnAfterSourceHandled);
+
///
/// Notifies a RunLoop to stop and return control to the execution context that called CFRunLoopRun
///
@@ -66,7 +72,14 @@ internal static partial class RunLoop
/// The run loop mode of rl from which to remove source.
[DllImport(Interop.Libraries.CoreFoundationLibrary)]
internal static extern void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef mode);
-
+
+ ///
+ /// Invalidates a CFRunLoopSource object, stopping it from ever firing again.
+ ///
+ /// The run loop source to invalidate.
+ [DllImport(Interop.Libraries.CoreFoundationLibrary)]
+ internal static extern void CFRunLoopSourceInvalidate(CFRunLoopSourceRef source);
+
///
/// Returns a bool that indicates whether the run loop is waiting for an event.
///
diff --git a/src/System.Net.Http/src/System.Net.Http.csproj b/src/System.Net.Http/src/System.Net.Http.csproj
index 9b800cfd5f26..cf5336a230fd 100644
--- a/src/System.Net.Http/src/System.Net.Http.csproj
+++ b/src/System.Net.Http/src/System.Net.Http.csproj
@@ -214,7 +214,6 @@
-
Common\System\Net\ContextAwareResult.Unix.cs
@@ -256,6 +255,49 @@
Common\System\Net\Security\Unix\SafeDeleteNegoContext.cs
+
+
+
+
+
+
+
+ Common\Interop\OSX\Interop.CoreFoundation.cs
+
+
+ Common\Interop\OSX\Interop.CoreFoundation.CFArray.cs
+
+
+ Common\Interop\OSX\Interop.CoreFoundation.CFData.cs
+
+
+ Common\Interop\OSX\Interop.CoreFoundation.CFDictionary.cs
+
+
+ Common\Interop\OSX\Interop.CoreFoundation.CFProxy.cs
+
+
+ Common\Interop\OSX\Interop.CoreFoundation.CFUrl.cs
+
+
+ Common\Interop\OSX\Interop.CoreFoundation.CFString.cs
+
+
+ Common\Interop\OSX\Interop.CoreFoundation.CFString.cs
+
+
+ Common\Interop\OSX\Interop.CoreFoundation.CFError.cs
+
+
+ Common\Interop\OSX\Interop.RunLoop.cs
+
+
+ Common\Interop\OSX\Interop.Libraries.cs
+
+
+ Common\Microsoft\Win32\SafeHandles\SafeCreateHandle.OSX.cs
+
+
diff --git a/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/MacProxy.cs b/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/MacProxy.cs
new file mode 100644
index 000000000000..dfd5b31bbd87
--- /dev/null
+++ b/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/MacProxy.cs
@@ -0,0 +1,128 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Net.Http;
+using System.Net;
+using System.Collections.Generic;
+using Microsoft.Win32.SafeHandles;
+using static Interop.CoreFoundation;
+using static Interop.RunLoop;
+
+using CFRunLoopRef = System.IntPtr;
+using CFRunLoopSourceRef = System.IntPtr;
+
+namespace System.Net.Http
+{
+ internal sealed class MacProxy : IWebProxy
+ {
+ public ICredentials Credentials
+ {
+ get => null;
+ set => throw new NotSupportedException();
+ }
+
+ private static Uri GetProxyUri(string scheme, CFProxy proxy)
+ {
+ var uriBuilder = new UriBuilder(
+ scheme,
+ proxy.HostName,
+ proxy.PortNumber);
+
+ // TODO: Issue #26593 - Credentials are not propagated
+
+ return uriBuilder.Uri;
+ }
+
+ public Uri ExecuteProxyAutoConfiguration(SafeCreateHandle cfurl, CFProxy proxy)
+ {
+ Uri result = null;
+ CFRunLoopRef runLoop = CFRunLoopGetCurrent();
+
+ // Callback that will be called after executing the configuration script
+ CFProxyAutoConfigurationResultCallback cb = (IntPtr client, IntPtr proxyListHandle, IntPtr error) =>
+ {
+ if (proxyListHandle != IntPtr.Zero)
+ {
+ using (var proxyList = new SafeCFArrayHandle(proxyListHandle, false))
+ {
+ long proxyCount = CFArrayGetCount(proxyList);
+ for (int i = 0; i < proxyCount; i++)
+ {
+ IntPtr proxyValue = CFArrayGetValueAtIndex(proxyList, i);
+ using (SafeCFDictionaryHandle proxyDict = new SafeCFDictionaryHandle(proxyValue, false))
+ {
+ CFProxy proxy = new CFProxy(proxyDict);
+ if (proxy.ProxyType == CFProxy.kCFProxyTypeHTTP || proxy.ProxyType == CFProxy.kCFProxyTypeHTTPS)
+ {
+ result = GetProxyUri("http", proxy);
+ break;
+ }
+ }
+ }
+ }
+ }
+ CFRunLoopStop(runLoop);
+ };
+
+ var clientContext = new CFStreamClientContext();
+ CFRunLoopSourceRef loopSource =
+ proxy.ProxyType == CFProxy.kCFProxyTypeAutoConfigurationURL ?
+ CFNetworkExecuteProxyAutoConfigurationURL(proxy.AutoConfigurationURL, cfurl, cb, ref clientContext) :
+ CFNetworkExecuteProxyAutoConfigurationScript(proxy.AutoConfigurationJavaScript, cfurl, cb, ref clientContext);
+
+ using (var mode = CFStringCreateWithCString(typeof(MacProxy).FullName))
+ {
+ IntPtr modeHandle = mode.DangerousGetHandle();
+ CFRunLoopAddSource(runLoop, loopSource, modeHandle);
+ CFRunLoopRunInMode(modeHandle, double.MaxValue, 0);
+ CFRunLoopSourceInvalidate(loopSource);
+ }
+
+ GC.KeepAlive(cb);
+
+ return result;
+ }
+
+ public Uri GetProxy(Uri targetUri)
+ {
+ using (SafeCFDictionaryHandle systemProxySettings = CFNetworkCopySystemProxySettings())
+ using (SafeCreateHandle cfurl = CFURLCreateWithString(targetUri.AbsoluteUri))
+ using (SafeCFArrayHandle proxies = CFNetworkCopyProxiesForURL(cfurl, systemProxySettings))
+ {
+ long proxyCount = CFArrayGetCount(proxies);
+ for (int i = 0; i < proxyCount; i++)
+ {
+ IntPtr proxyValue = CFArrayGetValueAtIndex(proxies, i);
+ using (SafeCFDictionaryHandle proxyDict = new SafeCFDictionaryHandle(proxyValue, false))
+ {
+ CFProxy proxy = new CFProxy(proxyDict);
+
+ if (proxy.ProxyType == CFProxy.kCFProxyTypeAutoConfigurationURL || proxy.ProxyType == CFProxy.kCFProxyTypeAutoConfigurationJavaScript)
+ {
+ Uri result = ExecuteProxyAutoConfiguration(cfurl, proxy);
+ if (result != null)
+ return result;
+ }
+ else if (proxy.ProxyType == CFProxy.kCFProxyTypeHTTP || proxy.ProxyType == CFProxy.kCFProxyTypeHTTPS)
+ {
+ return GetProxyUri("http", proxy);
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public bool IsBypassed(Uri targetUri)
+ {
+ if (targetUri == null)
+ throw new ArgumentNullException(nameof(targetUri));
+
+ Uri proxyUri = GetProxy(targetUri);
+ return Equals(proxyUri, targetUri) || proxyUri == null;
+ }
+ }
+}
diff --git a/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SystemProxyInfo.OSX.cs b/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SystemProxyInfo.OSX.cs
new file mode 100644
index 000000000000..2d143dd0183d
--- /dev/null
+++ b/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SystemProxyInfo.OSX.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Net.Http
+{
+ internal static class SystemProxyInfo
+ {
+ // On Unix we get default proxy configuration from environment variables
+ public static IWebProxy ConstructSystemProxy()
+ {
+ return HttpEnvironmentProxy.TryCreate(out IWebProxy proxy) ? proxy : new MacProxy();
+ }
+ }
+}
+