Read system proxy information on macOS (dotnet/corefx#36177)
authorFilip Navara <filip.navara@gmail.com>
Sat, 13 Apr 2019 15:19:53 +0000 (17:19 +0200)
committerDavid Shulman <david.shulman@microsoft.com>
Sat, 13 Apr 2019 15:19:53 +0000 (08:19 -0700)
* Read system proxy information on macOS

* Replace CFRunLoopRemoveSource with CFRunLoopSourceInvalidate to mimic recommended flow from Chromium / WebKit

Commit migrated from https://github.com/dotnet/corefx/commit/2aa2518a47c541148e2dc3f9d040942e44395acd

src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFArray.cs
src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFDictionary.cs [new file with mode: 0644]
src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFNumber.cs [new file with mode: 0644]
src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFProxy.cs [new file with mode: 0644]
src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFString.cs
src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFUrl.cs [new file with mode: 0644]
src/libraries/Common/src/Interop/OSX/Interop.Libraries.cs
src/libraries/Common/src/Interop/OSX/Interop.RunLoop.cs
src/libraries/System.Net.Http/src/System.Net.Http.csproj
src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/MacProxy.cs [new file with mode: 0644]
src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SystemProxyInfo.OSX.cs [new file with mode: 0644]

index d0c1351..dc60806 100644 (file)
@@ -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/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFDictionary.cs b/src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFDictionary.cs
new file mode 100644 (file)
index 0000000..79c2979
--- /dev/null
@@ -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/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFNumber.cs b/src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFNumber.cs
new file mode 100644 (file)
index 0000000..c2bffd3
--- /dev/null
@@ -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/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFProxy.cs b/src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFProxy.cs
new file mode 100644 (file)
index 0000000..1f6cc07
--- /dev/null
@@ -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);
+        }
+    }
+}
index aa7a150..17fb307 100644 (file)
@@ -91,6 +91,11 @@ namespace Microsoft.Win32.SafeHandles
         {
         }
 
+        internal SafeCFStringHandle(IntPtr handle, bool ownsHandle)
+            : base(handle, ownsHandle)
+        {
+        }
+
         protected override bool ReleaseHandle()
         {
             Interop.CoreFoundation.CFRelease(handle);
diff --git a/src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFUrl.cs b/src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFUrl.cs
new file mode 100644 (file)
index 0000000..88c2bbd
--- /dev/null
@@ -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);
+            }
+        }
+    }
+}
index 89f6a3f..ffac0b2 100644 (file)
@@ -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";
index 9257952..f7c3344 100644 (file)
@@ -35,6 +35,12 @@ internal static partial class Interop
         internal extern static void CFRunLoopRun();
 
         /// <summary>
+        /// Runs the current thread’s CFRunLoop object in a particular mode.
+        /// </summary>
+        [DllImport(Interop.Libraries.CoreFoundationLibrary)]
+        internal extern static int CFRunLoopRunInMode(CFStringRef mode, double seconds, int returnAfterSourceHandled);
+
+        /// <summary>
         /// Notifies a RunLoop to stop and return control to the execution context that called CFRunLoopRun
         /// </summary>
         /// <param name="rl">The RunLoop to notify to stop</param>
@@ -66,7 +72,14 @@ internal static partial class Interop
         /// <param name="mode">The run loop mode of rl from which to remove source.</param>
         [DllImport(Interop.Libraries.CoreFoundationLibrary)]
         internal static extern void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef mode);
-        
+
+        /// <summary>
+        /// Invalidates a CFRunLoopSource object, stopping it from ever firing again.
+        /// </summary>
+        /// <param name="source">The run loop source to invalidate.</param>
+        [DllImport(Interop.Libraries.CoreFoundationLibrary)]
+        internal static extern void CFRunLoopSourceInvalidate(CFRunLoopSourceRef source);
+
         /// <summary>
         /// Returns a bool that indicates whether the run loop is waiting for an event.
         /// </summary>
index 12c44f1..2de08e8 100644 (file)
   </ItemGroup>
   <!-- SocketsHttpHandler platform parts -->
   <ItemGroup Condition=" '$(TargetsUnix)' == 'true' And '$(TargetGroup)' == 'netcoreapp'">
-    <Compile Include="System\Net\Http\SocketsHttpHandler\SystemProxyInfo.Unix.cs" />
     <Compile Include="System\Net\Http\SocketsHttpHandler\HttpEnvironmentProxy.cs" />
     <Compile Include="$(CommonPath)\System\Net\ContextAwareResult.Unix.cs">
       <Link>Common\System\Net\ContextAwareResult.Unix.cs</Link>
       <Link>Common\System\Net\Security\Unix\SafeDeleteNegoContext.cs</Link>
     </Compile>
   </ItemGroup>
+  <ItemGroup Condition=" '$(TargetsUnix)' == 'true' And '$(TargetsOSX)' != 'true' And '$(TargetGroup)' == 'netcoreapp'">
+    <Compile Include="System\Net\Http\SocketsHttpHandler\SystemProxyInfo.Unix.cs" />
+  </ItemGroup>
+  <ItemGroup Condition=" '$(TargetsOSX)' == 'true' And '$(TargetGroup)' == 'netcoreapp'">
+    <Compile Include="System\Net\Http\SocketsHttpHandler\SystemProxyInfo.OSX.cs" />
+    <Compile Include="System\Net\Http\SocketsHttpHandler\MacProxy.cs" />
+    <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.cs">
+      <Link>Common\Interop\OSX\Interop.CoreFoundation.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFArray.cs">
+      <Link>Common\Interop\OSX\Interop.CoreFoundation.CFArray.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFData.cs">
+      <Link>Common\Interop\OSX\Interop.CoreFoundation.CFData.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFDictionary.cs">
+      <Link>Common\Interop\OSX\Interop.CoreFoundation.CFDictionary.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFProxy.cs">
+      <Link>Common\Interop\OSX\Interop.CoreFoundation.CFProxy.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFUrl.cs">
+      <Link>Common\Interop\OSX\Interop.CoreFoundation.CFUrl.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFString.cs">
+      <Link>Common\Interop\OSX\Interop.CoreFoundation.CFString.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFNumber.cs">
+      <Link>Common\Interop\OSX\Interop.CoreFoundation.CFString.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFError.cs">
+      <Link>Common\Interop\OSX\Interop.CoreFoundation.CFError.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)\Interop\OSX\Interop.RunLoop.cs">
+      <Link>Common\Interop\OSX\Interop.RunLoop.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)\Interop\OSX\Interop.Libraries.cs">
+      <Link>Common\Interop\OSX\Interop.Libraries.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeCreateHandle.OSX.cs">
+      <Link>Common\Microsoft\Win32\SafeHandles\SafeCreateHandle.OSX.cs</Link>
+    </Compile>
+  </ItemGroup>
   <ItemGroup Condition=" '$(TargetsWindows)' == 'true' And '$(TargetGroup)' == 'netcoreapp'">
     <Compile Include="System\Net\Http\SocketsHttpHandler\SystemProxyInfo.Windows.cs" />
     <Compile Include="System\Net\Http\SocketsHttpHandler\HttpSystemProxy.cs" />
diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/MacProxy.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/MacProxy.cs
new file mode 100644 (file)
index 0000000..dfd5b31
--- /dev/null
@@ -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/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SystemProxyInfo.OSX.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SystemProxyInfo.OSX.cs
new file mode 100644 (file)
index 0000000..2d143dd
--- /dev/null
@@ -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();
+        }
+    }
+}
+