From 879f6699bb5e8bd684bcd0f0acc55401ad64991c Mon Sep 17 00:00:00 2001 From: monojenkins Date: Wed, 12 Feb 2020 08:24:23 -0500 Subject: [2020-02] Allow users to switch to MonoWebRequestHandler on Android via UI (#18785) * Bring back old HttpHandler for Android Co-authored-by: Egor Bogatov --- .../HttpClientHandler.SocketsHandler.Android.cs | 20 ++++++ mcs/class/System.Net.Http/System.Net.Http.csproj | 2 +- .../System.Net.Http/HttpClient.android.cs | 41 +++++++----- .../UnitTests/HttpClientHandlerTests.Android.cs | 74 ++++++++++++++++++++++ ...oid_System.Net.Http.UnitTests_xtest.dll.sources | 1 + .../monodroid_System.Net.Http.dll.exclude.sources | 1 + .../monodroid_System.Net.Http.dll.sources | 1 + 7 files changed, 123 insertions(+), 17 deletions(-) create mode 100644 mcs/class/System.Net.Http/HttpClientHandler.SocketsHandler.Android.cs create mode 100644 mcs/class/System.Net.Http/UnitTests/HttpClientHandlerTests.Android.cs (limited to 'mcs/class') diff --git a/mcs/class/System.Net.Http/HttpClientHandler.SocketsHandler.Android.cs b/mcs/class/System.Net.Http/HttpClientHandler.SocketsHandler.Android.cs new file mode 100644 index 00000000000..c483c28f03b --- /dev/null +++ b/mcs/class/System.Net.Http/HttpClientHandler.SocketsHandler.Android.cs @@ -0,0 +1,20 @@ +namespace System.Net.Http +{ + partial class HttpClientHandler : HttpMessageHandler + { + static IMonoHttpClientHandler CreateDefaultHandler () + { + string envvar = Environment.GetEnvironmentVariable ("XA_HTTP_CLIENT_HANDLER_TYPE")?.Trim (); + if (envvar?.StartsWith("System.Net.Http.MonoWebRequestHandler", StringComparison.InvariantCulture) == true) + return new MonoWebRequestHandler (); + // Ignore other types of handlers here (e.g. AndroidHttpHandler) to keep the old behavior + // and always create SocketsHttpHandler for code like this if MonoWebRequestHandler was not specified: + // + // var handler = new HttpClientHandler { Credentials = ... }; + // var httpClient = new HttpClient (handler); + // + // AndroidHttpHandler is used only when we use the parameterless ctor of HttpClient + return new SocketsHttpHandler (); + } + } +} diff --git a/mcs/class/System.Net.Http/System.Net.Http.csproj b/mcs/class/System.Net.Http/System.Net.Http.csproj index ccd76ef08cf..554e2d19150 100644 --- a/mcs/class/System.Net.Http/System.Net.Http.csproj +++ b/mcs/class/System.Net.Http/System.Net.Http.csproj @@ -1328,7 +1328,7 @@ - + diff --git a/mcs/class/System.Net.Http/System.Net.Http/HttpClient.android.cs b/mcs/class/System.Net.Http/System.Net.Http/HttpClient.android.cs index 1ef1af81b77..9b899bcffd8 100644 --- a/mcs/class/System.Net.Http/System.Net.Http/HttpClient.android.cs +++ b/mcs/class/System.Net.Http/System.Net.Http/HttpClient.android.cs @@ -8,22 +8,31 @@ namespace System.Net.Http { static HttpMessageHandler CreateDefaultHandler () { - Type type = Type.GetType("Android.Runtime.AndroidEnvironment, Mono.Android"); - if (type == null) - return GetFallback ("Invalid Mono.Android assembly? Cannot find Android.Runtime.AndroidEnvironment"); - - MethodInfo method = type.GetMethod ("GetHttpMessageHandler", BindingFlags.Static | BindingFlags.NonPublic); - if (method == null) - return GetFallback ("Your Xamarin.Android version does not support obtaining of the custom HttpClientHandler"); - - object ret = method.Invoke (null, null); - if (ret == null) - return GetFallback ("Xamarin.Android returned no custom HttpClientHandler"); - - var handler = ret as HttpMessageHandler; - if (handler == null) - return GetFallback ($"{ret?.GetType()} is not a valid HttpMessageHandler"); - return handler; + string envvar = Environment.GetEnvironmentVariable ("XA_HTTP_CLIENT_HANDLER_TYPE")?.Trim (); + + if (string.IsNullOrEmpty (envvar)) + return GetFallback ($"XA_HTTP_CLIENT_HANDLER_TYPE is empty"); + + if (envvar?.StartsWith("System.Net.Http.MonoWebRequestHandler", StringComparison.InvariantCulture) == true) + return new HttpClientHandler (new MonoWebRequestHandler ()); + + Type handlerType = Type.GetType (envvar, false); + if (handlerType == null && !envvar.Contains (", ")) + { + // if assembly was not specified - look for it in Mono.Android too + // (e.g. AndroidHttpHandler is there) + handlerType = Type.GetType (envvar + ", Mono.Android", false); + } + + if (handlerType == null) + return GetFallback ($"'{envvar}' type was not found"); + + object handlerObj = Activator.CreateInstance (handlerType); + + if (handlerObj is HttpMessageHandler msgHandler) + return msgHandler; + + return GetFallback ($"{handlerObj?.GetType ()} is not a valid HttpMessageHandler or MonoWebRequestHandler"); } static HttpMessageHandler GetFallback (string message) diff --git a/mcs/class/System.Net.Http/UnitTests/HttpClientHandlerTests.Android.cs b/mcs/class/System.Net.Http/UnitTests/HttpClientHandlerTests.Android.cs new file mode 100644 index 00000000000..00c1cda6405 --- /dev/null +++ b/mcs/class/System.Net.Http/UnitTests/HttpClientHandlerTests.Android.cs @@ -0,0 +1,74 @@ +// 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.Text; +using System.Net.Http; +using System.Reflection; + +using Xunit; +using Xunit.Abstractions; + +namespace System.Net.Http.Tests +{ + public class HttpClientHandlerTestsAndroid + { + static Type GetInnerHandlerType (HttpClient httpClient) + { + BindingFlags bflasgs = BindingFlags.Instance | BindingFlags.NonPublic; + FieldInfo handlerField = typeof (HttpMessageInvoker).GetField("_handler", bflasgs); + Assert.NotNull (handlerField); + object handler = handlerField.GetValue (httpClient); + FieldInfo innerHandlerField = handler.GetType ().GetField ("_delegatingHandler", bflasgs); + Assert.NotNull (handlerField); + object innerHandler = innerHandlerField.GetValue (handler); + return innerHandler.GetType (); + } + + [Fact] + public void TestEnvVarSwitchForInnerHttpHandler () + { + const string xaHandlerKey = "XA_HTTP_CLIENT_HANDLER_TYPE"; + var prevHandler = Environment.GetEnvironmentVariable (xaHandlerKey); + + // "" + Environment.SetEnvironmentVariable (xaHandlerKey, ""); + var httpClient1 = new HttpClient (); + Assert.Equal ("SocketsHttpHandler", GetInnerHandlerType (httpClient1).Name); + + var handler2 = new HttpClientHandler (); + var httpClient2 = new HttpClient (handler2); + Assert.Equal ("SocketsHttpHandler", GetInnerHandlerType (httpClient2).Name); + + // "System.Net.Http.MonoWebRequestHandler" + Environment.SetEnvironmentVariable (xaHandlerKey, "System.Net.Http.MonoWebRequestHandler"); + var httpClient3 = new HttpClient (); + Assert.Equal ("MonoWebRequestHandler", GetInnerHandlerType (httpClient3).Name); + + var handler4 = new HttpClientHandler (); + var httpClient4 = new HttpClient (handler4); + Assert.Equal ("MonoWebRequestHandler", GetInnerHandlerType (httpClient4).Name); + + // "System.Net.Http.MonoWebRequestHandler, System.Net.Http" + Environment.SetEnvironmentVariable (xaHandlerKey, "System.Net.Http.MonoWebRequestHandler, System.Net.Http"); + var httpClient5 = new HttpClient (); + Assert.Equal ("MonoWebRequestHandler", GetInnerHandlerType (httpClient5).Name); + + var handler6 = new HttpClientHandler (); + var httpClient6 = new HttpClient (handler6); + Assert.Equal ("MonoWebRequestHandler", GetInnerHandlerType (httpClient6).Name); + + // "System.Net.Http.HttpClientHandler" + Environment.SetEnvironmentVariable (xaHandlerKey, "System.Net.Http.HttpClientHandler"); + var httpClient7 = new HttpClient (); + Assert.Equal ("SocketsHttpHandler", GetInnerHandlerType (httpClient7).Name); + + var handler8 = new HttpClientHandler (); + var httpClient8 = new HttpClient (handler8); + Assert.Equal ("SocketsHttpHandler", GetInnerHandlerType (httpClient8).Name); + + Environment.SetEnvironmentVariable (xaHandlerKey, prevHandler); + } + } +} diff --git a/mcs/class/System.Net.Http/UnitTests/monodroid_System.Net.Http.UnitTests_xtest.dll.sources b/mcs/class/System.Net.Http/UnitTests/monodroid_System.Net.Http.UnitTests_xtest.dll.sources index ea8b52eca73..9334d2fcca4 100644 --- a/mcs/class/System.Net.Http/UnitTests/monodroid_System.Net.Http.UnitTests_xtest.dll.sources +++ b/mcs/class/System.Net.Http/UnitTests/monodroid_System.Net.Http.UnitTests_xtest.dll.sources @@ -1 +1,2 @@ #include unit-tests.sources +HttpClientHandlerTests.Android.cs diff --git a/mcs/class/System.Net.Http/monodroid_System.Net.Http.dll.exclude.sources b/mcs/class/System.Net.Http/monodroid_System.Net.Http.dll.exclude.sources index c1788b13bf2..9164cfe38ef 100644 --- a/mcs/class/System.Net.Http/monodroid_System.Net.Http.dll.exclude.sources +++ b/mcs/class/System.Net.Http/monodroid_System.Net.Http.dll.exclude.sources @@ -1 +1,2 @@ HttpClient.DefaultHandler.cs +HttpClientHandler.SocketsHandler.cs diff --git a/mcs/class/System.Net.Http/monodroid_System.Net.Http.dll.sources b/mcs/class/System.Net.Http/monodroid_System.Net.Http.dll.sources index eca4fd33a42..2397da00e5e 100644 --- a/mcs/class/System.Net.Http/monodroid_System.Net.Http.dll.sources +++ b/mcs/class/System.Net.Http/monodroid_System.Net.Http.dll.sources @@ -1,2 +1,3 @@ #include socketshandler.sources System.Net.Http/HttpClient.android.cs +HttpClientHandler.SocketsHandler.Android.cs -- cgit v1.2.3