diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.RegNotifyChangeKeyValue.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.RegNotifyChangeKeyValue.cs new file mode 100644 index 0000000000000..547ad136ccb61 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.RegNotifyChangeKeyValue.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Advapi32 + { + internal const int REG_NOTIFY_CHANGE_NAME = 0x1; + internal const int REG_NOTIFY_CHANGE_ATTRIBUTES = 0x2; + internal const int REG_NOTIFY_CHANGE_LAST_SET = 0x4; + internal const int REG_NOTIFY_CHANGE_SECURITY = 0x8; + internal const int REG_NOTIFY_THREAD_AGNOSTIC = 0x10000000; + + [LibraryImport(Libraries.Advapi32, EntryPoint = "RegNotifyChangeKeyValue", StringMarshalling = StringMarshalling.Utf16)] + internal static partial int RegNotifyChangeKeyValue( + SafeHandle hKey, + [MarshalAs(UnmanagedType.Bool)] bool watchSubtree, + uint notifyFilter, + SafeHandle hEvent, + [MarshalAs(UnmanagedType.Bool)] bool asynchronous); + } +} diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs index a7bab6a78b6f0..d3e00ce0327b0 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs @@ -505,7 +505,7 @@ public async Task MultiProxy_PAC_Failover_Succeeds() winInetProxyHelperType.GetField("_proxyBypass", Reflection.BindingFlags.Instance | Reflection.BindingFlags.NonPublic).SetValue(winInetProxyHelper, null); // Create a HttpWindowsProxy with our custom WinInetProxyHelper. - IWebProxy httpWindowsProxy = (IWebProxy)Activator.CreateInstance(Type.GetType("System.Net.Http.HttpWindowsProxy, System.Net.Http", true), Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance, null, new[] { winInetProxyHelper, null }, null); + IWebProxy httpWindowsProxy = (IWebProxy)Activator.CreateInstance(Type.GetType("System.Net.Http.HttpWindowsProxy, System.Net.Http", true), Reflection.BindingFlags.Public | Reflection.BindingFlags.NonPublic| Reflection.BindingFlags.Instance, null, new[] { winInetProxyHelper }, null); Task nextFailedConnection = null; diff --git a/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/HttpWindowsProxyTest.cs b/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/HttpWindowsProxyTest.cs index aa5cccbb28886..f0f28e9fc205d 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/HttpWindowsProxyTest.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/HttpWindowsProxyTest.cs @@ -28,11 +28,6 @@ public static IEnumerable ManualSettingsMemberData() yield return new object[] { new Uri("http://localhost"), true }; } - [Fact] - public void TryCreate_WinInetProxySettingsAllOff_ReturnsFalse() - { - Assert.False(HttpWindowsProxy.TryCreate(out IWebProxy webProxy)); - } [Theory] [MemberData(nameof(ManualSettingsMemberData))] @@ -44,7 +39,7 @@ public void GetProxy_BothAutoDetectAndManualSettingsButFailedAutoDetect_ManualSe FakeRegistry.WinInetProxySettings.ProxyBypass = ManualSettingsProxyBypassList; TestControl.PACFileNotDetectedOnNetwork = true; - Assert.True(HttpWindowsProxy.TryCreate(out IWebProxy webProxy)); + IWebProxy webProxy = new HttpWindowsProxy(); // The first GetProxy() call will try using WinInetProxyHelper (and thus WinHTTP) since AutoDetect is on. Uri proxyUri1 = webProxy.GetProxy(destination); @@ -74,7 +69,7 @@ public void GetProxy_ManualSettingsOnly_ManualSettingsUsed( FakeRegistry.WinInetProxySettings.Proxy = ManualSettingsProxyHost; FakeRegistry.WinInetProxySettings.ProxyBypass = ManualSettingsProxyBypassList; - Assert.True(HttpWindowsProxy.TryCreate(out IWebProxy webProxy)); + IWebProxy webProxy = new HttpWindowsProxy(); Uri proxyUri = webProxy.GetProxy(destination); if (bypassProxy) { @@ -90,7 +85,7 @@ public void GetProxy_ManualSettingsOnly_ManualSettingsUsed( public void IsBypassed_ReturnsFalse() { FakeRegistry.WinInetProxySettings.AutoDetect = true; - Assert.True(HttpWindowsProxy.TryCreate(out IWebProxy webProxy)); + IWebProxy webProxy = new HttpWindowsProxy(); Assert.False(webProxy.IsBypassed(new Uri("http://www.microsoft.com/"))); } } diff --git a/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/System.Net.Http.WinHttpHandler.Unit.Tests.csproj b/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/System.Net.Http.WinHttpHandler.Unit.Tests.csproj index fdb2638bdc028..f8b72896871da 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/System.Net.Http.WinHttpHandler.Unit.Tests.csproj +++ b/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/System.Net.Http.WinHttpHandler.Unit.Tests.csproj @@ -96,6 +96,8 @@ Link="ProductionCode\IMultiWebProxy.cs" /> + diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index dea20f206cdc9..fbaf1d950a196 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -384,6 +384,8 @@ Link="Common\Interop\Windows\WinHttp\Interop.winhttp_types.cs" /> + + diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpWindowsProxy.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpWindowsProxy.cs index 9104ace31623d..caf9104abc8d1 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpWindowsProxy.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpWindowsProxy.cs @@ -6,45 +6,81 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.IO.Compression; using System.Net.NetworkInformation; using System.Runtime.InteropServices; using System.Text; using System.Threading; +using Microsoft.Win32; using SafeWinHttpHandle = Interop.WinHttp.SafeWinHttpHandle; namespace System.Net.Http { internal sealed class HttpWindowsProxy : IMultiWebProxy, IDisposable { - private readonly MultiProxy _insecureProxy; // URI of the http system proxy if set - private readonly MultiProxy _secureProxy; // URI of the https system proxy if set - private readonly FailedProxyCache _failedProxies = new FailedProxyCache(); - private readonly List? _bypass; // list of domains not to proxy - private readonly bool _bypassLocal; // we should bypass domain considered local - private readonly List? _localIp; + private readonly RegistryKey? _internetSettingsRegistry = Registry.CurrentUser?.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"); + private MultiProxy _insecureProxy; // URI of the http system proxy if set + private MultiProxy _secureProxy; // URI of the https system proxy if set + private FailedProxyCache _failedProxies = new FailedProxyCache(); + private List? _bypass; // list of domains not to proxy + private List? _localIp; private ICredentials? _credentials; - private readonly WinInetProxyHelper _proxyHelper; + private WinInetProxyHelper _proxyHelper; private SafeWinHttpHandle? _sessionHandle; private bool _disposed; + private EventWaitHandle _waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset); + private const int RegistrationFlags = Interop.Advapi32.REG_NOTIFY_CHANGE_NAME | Interop.Advapi32.REG_NOTIFY_CHANGE_LAST_SET | Interop.Advapi32.REG_NOTIFY_CHANGE_ATTRIBUTES | Interop.Advapi32.REG_NOTIFY_THREAD_AGNOSTIC; + private RegisteredWaitHandle? _registeredWaitHandle; - public static bool TryCreate([NotNullWhen(true)] out IWebProxy? proxy) + // 'proxy' used from tests via Reflection + public HttpWindowsProxy(WinInetProxyHelper? proxy = null) { - // This will get basic proxy setting from system using existing - // WinInetProxyHelper functions. If no proxy is enabled, it will return null. - SafeWinHttpHandle? sessionHandle = null; - proxy = null; - WinInetProxyHelper proxyHelper = new WinInetProxyHelper(); - if (!proxyHelper.ManualSettingsOnly && !proxyHelper.AutoSettingsUsed) + if (_internetSettingsRegistry != null && proxy == null) { - return false; + // we register for change notifications so we can react to changes during lifetime. + if (Interop.Advapi32.RegNotifyChangeKeyValue(_internetSettingsRegistry.Handle, true, RegistrationFlags, _waitHandle.SafeWaitHandle, true) == 0) + { + _registeredWaitHandle = ThreadPool.RegisterWaitForSingleObject(_waitHandle, RegistryChangeNotificationCallback, this, -1, false); + } + } + + UpdateConfiguration(proxy); + } + + private static void RegistryChangeNotificationCallback(object? state, bool timedOut) + { + HttpWindowsProxy proxy = (HttpWindowsProxy)state!; + if (!proxy._disposed) + { + + // This is executed from threadpool. we should not ever throw here. + try + { + // We need to register for notification every time. We regisrerand lock before we process configuration + // so if there is update it would be serialized to ensure consistency. + Interop.Advapi32.RegNotifyChangeKeyValue(proxy._internetSettingsRegistry!.Handle, true, RegistrationFlags, proxy._waitHandle.SafeWaitHandle, true); + lock (proxy) + { + proxy.UpdateConfiguration(); + } + } + catch (Exception ex) + { + if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(proxy, $"Failed to refresh proxy configuration: {ex.Message}"); + } } + } + + [MemberNotNull(nameof(_proxyHelper))] + private void UpdateConfiguration(WinInetProxyHelper? proxyHelper = null) + { + + proxyHelper ??= new WinInetProxyHelper(); if (proxyHelper.AutoSettingsUsed) { if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(proxyHelper, $"AutoSettingsUsed, calling {nameof(Interop.WinHttp.WinHttpOpen)}"); - sessionHandle = Interop.WinHttp.WinHttpOpen( + SafeWinHttpHandle? sessionHandle = Interop.WinHttp.WinHttpOpen( IntPtr.Zero, Interop.WinHttp.WINHTTP_ACCESS_TYPE_NO_PROXY, Interop.WinHttp.WINHTTP_NO_PROXY_NAME, @@ -56,18 +92,10 @@ public static bool TryCreate([NotNullWhen(true)] out IWebProxy? proxy) // Proxy failures are currently ignored by managed handler. if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(proxyHelper, $"{nameof(Interop.WinHttp.WinHttpOpen)} returned invalid handle"); sessionHandle.Dispose(); - return false; } - } - - proxy = new HttpWindowsProxy(proxyHelper, sessionHandle); - return true; - } - private HttpWindowsProxy(WinInetProxyHelper proxyHelper, SafeWinHttpHandle? sessionHandle) - { - _proxyHelper = proxyHelper; - _sessionHandle = sessionHandle; + _sessionHandle = sessionHandle; + } if (proxyHelper.ManualSettingsUsed) { @@ -80,10 +108,12 @@ private HttpWindowsProxy(WinInetProxyHelper proxyHelper, SafeWinHttpHandle? sess { int idx = 0; string? tmp; + bool bypassLocal = false; + List? localIp = null; // Process bypass list for manual setting. // Initial list size is best guess based on string length assuming each entry is at least 5 characters on average. - _bypass = new List(proxyHelper.ProxyBypass.Length / 5); + List? bypass = new List(proxyHelper.ProxyBypass.Length / 5); while (idx < proxyHelper.ProxyBypass.Length) { @@ -114,7 +144,7 @@ private HttpWindowsProxy(WinInetProxyHelper proxyHelper, SafeWinHttpHandle? sess } else if (string.Compare(proxyHelper.ProxyBypass, start, "", 0, 7, StringComparison.OrdinalIgnoreCase) == 0) { - _bypassLocal = true; + bypassLocal = true; tmp = null; } else @@ -137,28 +167,29 @@ private HttpWindowsProxy(WinInetProxyHelper proxyHelper, SafeWinHttpHandle? sess continue; } - _bypass.Add(tmp); - } - if (_bypass.Count == 0) - { - // Bypass string only had garbage we did not parse. - _bypass = null; + bypass.Add(tmp); } - } - if (_bypassLocal) - { - _localIp = new List(); - foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces()) + _bypass = bypass.Count > 0 ? bypass : null; + + if (bypassLocal) { - IPInterfaceProperties ipProps = netInterface.GetIPProperties(); - foreach (UnicastIPAddressInformation addr in ipProps.UnicastAddresses) + localIp = new List(); + foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces()) { - _localIp.Add(addr.Address); + IPInterfaceProperties ipProps = netInterface.GetIPProperties(); + foreach (UnicastIPAddressInformation addr in ipProps.UnicastAddresses) + { + localIp.Add(addr.Address); + } } } + + _localIp = localIp?.Count > 0 ? localIp : null; } } + + _proxyHelper = proxyHelper; } public void Dispose() @@ -171,6 +202,10 @@ public void Dispose() { SafeWinHttpHandle.DisposeAndClearHandle(ref _sessionHandle); } + + _waitHandle?.Dispose(); + _internetSettingsRegistry?.Dispose(); + _registeredWaitHandle?.Unregister(null); } } @@ -179,6 +214,11 @@ public void Dispose() /// public Uri? GetProxy(Uri uri) { + if (!_proxyHelper.AutoSettingsUsed && !_proxyHelper.ManualSettingsOnly) + { + return null; + } + GetMultiProxy(uri).ReadNext(out Uri? proxyUri, out _); return proxyUri; } @@ -240,7 +280,7 @@ public MultiProxy GetMultiProxy(Uri uri) // Fallback to manual settings if present. if (_proxyHelper.ManualSettingsUsed) { - if (_bypassLocal) + if (_localIp != null) { IPAddress? address; @@ -261,7 +301,7 @@ public MultiProxy GetMultiProxy(Uri uri) { // Host is valid IP address. // Check if it belongs to local system. - foreach (IPAddress a in _localIp!) + foreach (IPAddress a in _localIp) { if (a.Equals(address)) { diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SystemProxyInfo.Windows.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SystemProxyInfo.Windows.cs index c49163a90602b..4f51c72ac666a 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SystemProxyInfo.Windows.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SystemProxyInfo.Windows.cs @@ -10,10 +10,11 @@ public static IWebProxy ConstructSystemProxy() { if (!HttpEnvironmentProxy.TryCreate(out IWebProxy? proxy)) { - HttpWindowsProxy.TryCreate(out proxy); + // We create instance even if there is currently no proxy as that can change during application run. + proxy = new HttpWindowsProxy(); } - return proxy ?? new HttpNoProxy(); + return proxy; } } } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/HttpWindowsProxyTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/HttpWindowsProxyTest.cs index b09ad4a29d967..9e966e301a3a6 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/HttpWindowsProxyTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/HttpWindowsProxyTest.cs @@ -39,8 +39,6 @@ await RemoteExecutor.Invoke((proxyString, insecureProxy, secureProxy) => { FakeRegistry.Reset(); - Assert.False(HttpWindowsProxy.TryCreate(out IWebProxy p)); - FakeRegistry.WinInetProxySettings.Proxy = proxyString; WinInetProxyHelper proxyHelper = new WinInetProxyHelper(); Assert.Null(proxyHelper.AutoConfigUrl); @@ -48,8 +46,7 @@ await RemoteExecutor.Invoke((proxyString, insecureProxy, secureProxy) => Assert.False(proxyHelper.AutoSettingsUsed); Assert.True(proxyHelper.ManualSettingsUsed); - Assert.True(HttpWindowsProxy.TryCreate(out p)); - Assert.NotNull(p); + IWebProxy p = new HttpWindowsProxy(proxyHelper); Assert.Equal(!string.IsNullOrEmpty(insecureProxy) ? new Uri(insecureProxy) : null, p.GetProxy(new Uri(fooHttp))); Assert.Equal(!string.IsNullOrEmpty(secureProxy) ? new Uri(secureProxy) : null, p.GetProxy(new Uri(fooHttps))); @@ -81,8 +78,6 @@ await RemoteExecutor.Invoke((proxyString, insecureProxy, secureProxy) => { TestControl.ResetAll(); - Assert.False(HttpWindowsProxy.TryCreate(out IWebProxy p)); - FakeRegistry.WinInetProxySettings.AutoConfigUrl = "http://127.0.0.1/proxy.pac"; WinInetProxyHelper proxyHelper = new WinInetProxyHelper(); Assert.Null(proxyHelper.Proxy); @@ -90,8 +85,7 @@ await RemoteExecutor.Invoke((proxyString, insecureProxy, secureProxy) => Assert.False(proxyHelper.ManualSettingsUsed); Assert.True(proxyHelper.AutoSettingsUsed); - Assert.True(HttpWindowsProxy.TryCreate(out p)); - Assert.NotNull(p); + IWebProxy p = new HttpWindowsProxy(proxyHelper); // With a HttpWindowsProxy created configured to use auto-config, now set Proxy so when it // attempts to resolve a proxy, it resolves our string. @@ -137,15 +131,12 @@ public async Task HttpProxy_WindowsProxy_Loaded(string rawProxyString, string ex { await RemoteExecutor.Invoke((proxyString, expectedString) => { - IWebProxy p; - FakeRegistry.Reset(); FakeRegistry.WinInetProxySettings.Proxy = proxyString; WinInetProxyHelper proxyHelper = new WinInetProxyHelper(); - Assert.True(HttpWindowsProxy.TryCreate(out p)); - Assert.NotNull(p); + IWebProxy p = new HttpWindowsProxy(proxyHelper); Assert.Equal(expectedString, p.GetProxy(new Uri(fooHttp)).ToString()); Assert.Equal(expectedString, p.GetProxy(new Uri(fooHttps)).ToString()); }, rawProxyString, expectedUri).DisposeAsync(); @@ -174,14 +165,12 @@ public async Task HttpProxy_Local_Bypassed(string name, bool shouldBypass) await RemoteExecutor.Invoke((url, expected) => { bool expectedResult = Boolean.Parse(expected); - IWebProxy p; FakeRegistry.Reset(); FakeRegistry.WinInetProxySettings.Proxy = insecureProxyUri; FakeRegistry.WinInetProxySettings.ProxyBypass = "23.23.86.44;*.foo.com;;BAR.COM; ; 162*;[2002::11];[*:f8b0:4005:80a::200e]; http://www.xn--mnchhausen-9db.at;http://*.xn--bb-bjab.eu;http://xn--bb-bjab.eu;"; - Assert.True(HttpWindowsProxy.TryCreate(out p)); - Assert.NotNull(p); + IWebProxy p = new HttpWindowsProxy(); Uri u = new Uri(url); Assert.Equal(expectedResult, p.GetProxy(u) == null); @@ -199,14 +188,12 @@ public async Task HttpProxy_Local_Parsing(string bypass, int count) await RemoteExecutor.Invoke((bypassValue, expected) => { int expectedCount = Convert.ToInt32(expected); - IWebProxy p; FakeRegistry.Reset(); FakeRegistry.WinInetProxySettings.Proxy = insecureProxyUri; FakeRegistry.WinInetProxySettings.ProxyBypass = bypassValue; - Assert.True(HttpWindowsProxy.TryCreate(out p)); - Assert.NotNull(p); + IWebProxy p = new HttpWindowsProxy(); HttpWindowsProxy sp = p as HttpWindowsProxy; Assert.NotNull(sp); @@ -222,34 +209,6 @@ await RemoteExecutor.Invoke((bypassValue, expected) => }, bypass, count.ToString()).DisposeAsync(); } - [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - [InlineData("http://")] - [InlineData("http=")] - [InlineData("http://;")] - [InlineData("http=;")] - [InlineData(" ; ")] - public async Task HttpProxy_InvalidWindowsProxy_Null(string rawProxyString) - { - await RemoteExecutor.Invoke((proxyString) => - { - IWebProxy p; - - FakeRegistry.Reset(); - Assert.False(HttpWindowsProxy.TryCreate(out p)); - - FakeRegistry.WinInetProxySettings.Proxy = proxyString; - WinInetProxyHelper proxyHelper = new WinInetProxyHelper(); - - Assert.True(HttpWindowsProxy.TryCreate(out p)); - Assert.NotNull(p); - - Assert.Null(p.GetProxy(new Uri(fooHttp))); - Assert.Null(p.GetProxy(new Uri(fooHttps))); - Assert.Null(p.GetProxy(new Uri(fooWs))); - Assert.Null(p.GetProxy(new Uri(fooWss))); - }, rawProxyString).DisposeAsync(); - } - [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [MemberData(nameof(HttpProxy_Multi_Data))] public async Task HttpProxy_Multi_Success(string proxyConfig, string url, string expected) @@ -262,7 +221,7 @@ await RemoteExecutor.Invoke((proxyConfigValue, urlValue, expectedValue) => TestControl.ResetAll(); FakeRegistry.WinInetProxySettings.AutoConfigUrl = "http://dummy.com"; - Assert.True(HttpWindowsProxy.TryCreate(out IWebProxy p)); + IWebProxy p = new HttpWindowsProxy(); HttpWindowsProxy wp = Assert.IsType(p); // Now that HttpWindowsProxy has been constructed to use autoconfig, @@ -319,7 +278,7 @@ await RemoteExecutor.Invoke(manualValue => FakeRegistry.WinInetProxySettings.AutoConfigUrl = "http://dummy.com"; } - Assert.True(HttpWindowsProxy.TryCreate(out IWebProxy p)); + IWebProxy p = new HttpWindowsProxy(); HttpWindowsProxy wp = Assert.IsType(p); if (!manual) 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 05f9963958ced..f0f23f16ccb3c 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 @@ -353,6 +353,8 @@ Link="ProductionCode\Common\Interop\Windows\Interop.Libraries.cs" /> +