Make WebRequest.GetSystemWebProxy() return a working proxy (dotnet/corefx#41692)
authorStephen Toub <stoub@microsoft.com>
Sat, 12 Oct 2019 12:15:05 +0000 (08:15 -0400)
committerGitHub <noreply@github.com>
Sat, 12 Oct 2019 12:15:05 +0000 (08:15 -0400)
* Make WebRequest.GetSystemWebProxy() return a working proxy

Today it returns a singleton on .NET Core that throws PlatformNotSupportedExceptions, and is only used for its reference identity by the WebRequest implementation.  This is problematic for existing code that's taking this proxy and passing it to, for example, ClientWebSocket's Proxy, as it will end up being an expensive nop.  In fact, there's currently no way when targeting netstandard2.0 to tell ClientWebSocket to use a default proxy.

With this fix, WebRequest.DefaultWebProxy (which just defaults to GetSystemWebProxy()) will now return HttpClient.DefaultProxy, which is actually a working proxy implementation.

* Stop throwing NotSupportedException from HttpClient.DefaultProxy.set_Credentials

The OS and how the environment is configured determines what concrete type is returned from HttpClient's DefaultProxy property.  And currently these different types behave differently from set_Credentials.  Two of them are throwing NotSupportedException, which is not expected from the IWebProxy interface.  We should just roundtrip the credentials set, even if they're not used.

Commit migrated from https://github.com/dotnet/corefx/commit/8d21b79b924d29088dbde46d42737a657d466b5e

src/libraries/System.Net.Http/src/System.Net.Http.csproj
src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpEnvironmentProxy.cs
src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/MacProxy.cs
src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SystemProxyInfo.uap.cs
src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.netcoreapp.cs
src/libraries/System.Net.Requests/src/System.Net.Requests.csproj
src/libraries/System.Net.Requests/src/System/Net/SystemWebProxy.cs [deleted file]
src/libraries/System.Net.Requests/src/System/Net/WebRequest.cs

index 00202df..da42a6c 100644 (file)
     <Compile Include="uap\System\Net\CookieHelper.cs" />
     <Compile Include="uap\System\Net\HttpHandlerToFilter.cs" />
     <Compile Include="System\Net\Http\HttpClientHandler.Core.cs" />
+    <Compile Include="System\Net\Http\SocketsHttpHandler\HttpNoProxy.cs" />
     <Compile Include="System\Net\Http\SocketsHttpHandler\SystemProxyInfo.uap.cs" />
     <Compile Include="uap\System\Net\HttpClientHandler.cs" />
   </ItemGroup>
index d5b0217..a2150ed 100644 (file)
@@ -99,7 +99,7 @@ namespace System.Net.Http
         private readonly Uri _httpProxyUri;       // String URI for HTTP requests
         private readonly Uri _httpsProxyUri;      // String URI for HTTPS requests
         private readonly string[] _bypass = null; // list of domains not to proxy
-        private readonly ICredentials _credentials;
+        private ICredentials _credentials;
 
         private HttpEnvironmentProxy(Uri httpProxy, Uri httpsProxy, string bypassList)
         {
@@ -283,7 +283,10 @@ namespace System.Net.Http
             {
                 return _credentials;
             }
-            set { throw new NotSupportedException(); }
+            set
+            {
+                _credentials = value;
+            }
         }
     }
 }
index dfd5b31..1e19e91 100644 (file)
@@ -17,11 +17,7 @@ namespace System.Net.Http
 {
     internal sealed class MacProxy : IWebProxy
     {
-        public ICredentials Credentials
-        {
-            get => null;
-            set => throw new NotSupportedException();
-        }
+        public ICredentials Credentials { get; set; }
 
         private static Uri GetProxyUri(string scheme, CFProxy proxy)
         {
index dd2dc66..d508a78 100644 (file)
@@ -6,10 +6,10 @@ namespace System.Net.Http
 {
     internal static partial class SystemProxyInfo
     {
-        // For UAP this is currently not implemented.
         public static IWebProxy ConstructSystemProxy()
         {
-            throw new PlatformNotSupportedException();
+            // For UAP this is currently not implemented.
+            return new HttpNoProxy();
         }
     }
 }
index cfaeb7e..b794bb4 100644 (file)
@@ -38,6 +38,24 @@ namespace System.Net.Http.Functional.Tests
         }
 
         [Fact]
+        public void DefaultProxy_Credentials_SetGet_Roundtrips()
+        {
+            RemoteExecutor.Invoke(() =>
+            {
+                IWebProxy proxy = HttpClient.DefaultProxy;
+                ICredentials nc = proxy.Credentials;
+
+                proxy.Credentials = null;
+                Assert.Null(proxy.Credentials);
+
+                proxy.Credentials = nc;
+                Assert.Same(nc, proxy.Credentials);
+
+                return RemoteExecutor.SuccessExitCode;
+            }).Dispose();
+        }
+
+        [Fact]
         public async Task PatchAsync_Canceled_Throws()
         {
             using (var client = new HttpClient(new CustomResponseHandler((r, c) => WhenCanceled<HttpResponseMessage>(c))))
index 532974b..5823fae 100644 (file)
@@ -22,7 +22,6 @@
     <Compile Include="System\Net\IWebRequestCreate.cs" />
     <Compile Include="System\Net\ProtocolViolationException.cs" />
     <Compile Include="System\Net\RequestStream.cs" />
-    <Compile Include="System\Net\SystemWebProxy.cs" />
     <Compile Include="System\Net\TaskExtensions.cs" />
     <Compile Include="System\Net\WebException.cs" />
     <Compile Include="System\Net\WebExceptionStatus.cs" />
diff --git a/src/libraries/System.Net.Requests/src/System/Net/SystemWebProxy.cs b/src/libraries/System.Net.Requests/src/System/Net/SystemWebProxy.cs
deleted file mode 100644 (file)
index 181a3fa..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-// 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.Threading;
-
-namespace System.Net
-{
-    // Singleton proxy object representing the System proxy
-    // that is determined by per-user registry settings.  These
-    // are the proxy settings that Internet Explorer and Wininet
-    // use to look up a suitable proxy server. This object is the
-    // initial default value of WebRequest.DefaultWebProxy.
-    //
-    // This object is only used as a sentinel to provide backward
-    // compatibility for developers using the WebRequest.DefaultWebProxy
-    // object. Developers use the DefaultWebProxy object either explicitly
-    // by referencing it or implicitly when expecting .NET Desktop behaviors
-    // when used with HttpWebRequest or HttpClientHandler.
-    internal class SystemWebProxy : IWebProxy
-    {
-        private static IWebProxy s_systemWebProxy = null;
-        private static bool s_systemWebProxyInitialized = false;
-        private static object s_lockObject = new object();
-
-        private ICredentials _credentials = null;
-
-        private SystemWebProxy()
-        {
-        }
-
-        public static IWebProxy Get() => LazyInitializer.EnsureInitialized(ref s_systemWebProxy, ref s_systemWebProxyInitialized, ref s_lockObject, () => new SystemWebProxy());
-
-        public ICredentials Credentials
-        {
-            get
-            {
-                return _credentials;
-            }
-            set
-            {
-                _credentials = value;
-            }
-        }
-
-        // This is a sentinel object and can't support the GetProxy or IsBypassed
-        // methods directly. Our .NET Core and .NET Native code will handle this exception
-        // and call into WinInet/WinHttp as appropriate to use the system proxy.
-        public Uri GetProxy(Uri destination)
-        {
-            throw new PlatformNotSupportedException();
-        }
-
-        public bool IsBypassed(Uri host)
-        {
-            throw new PlatformNotSupportedException();
-        }
-    }
-}
index a320990..fee9c54 100644 (file)
@@ -6,6 +6,7 @@ using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
 using System.Net.Cache;
+using System.Net.Http;
 using System.Net.Security;
 using System.Runtime.Serialization;
 using System.Security.Principal;
@@ -556,17 +557,16 @@ namespace System.Net
             throw NotImplemented.ByDesignWithMessage(SR.net_MethodNotImplementedException);
         }
 
-        // Default Web Proxy implementation.
         private static IWebProxy s_DefaultWebProxy;
         private static bool s_DefaultWebProxyInitialized;
 
-        public static IWebProxy GetSystemWebProxy() => SystemWebProxy.Get();
+        public static IWebProxy GetSystemWebProxy() => HttpClient.DefaultProxy;
 
         public static IWebProxy DefaultWebProxy
         {
             get
             {
-                return LazyInitializer.EnsureInitialized(ref s_DefaultWebProxy, ref s_DefaultWebProxyInitialized, ref s_internalSyncObject, () => SystemWebProxy.Get());
+                return LazyInitializer.EnsureInitialized(ref s_DefaultWebProxy, ref s_DefaultWebProxyInitialized, ref s_internalSyncObject, () => GetSystemWebProxy());
             }
             set
             {