How to make calls to HTTP2 on Xamarin.Forms
Asked Answered
M

1

10

gRpc for Xamarin.Forms with .Net Standart 2.0 works on http2, so it should be some way to make HttpClient calls or reuse existing gRpc functionality. It could be that i missing something.

Sample app to reproduce issue. You need to host gRpc service somewhere. WebClient call is in AboutPage.xaml.cs aslo test project with asp core 3.1 in web folder. XamarinHttp2WithBackend GitHub

Fallowing instructions Microsoft.com - HttpClient Stack and SSL/TLS Implementation Selector for Android and Stackoverflow.com - Use HTTP 2 with HttpClient in .Net didnt helped either.

For Asp Core 3.1 console app you could do (bellow) and works. It wont works on 2.2 and lower

AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);

var client = new HttpClient();
var req = new HttpRequestMessage(HttpMethod.Get, $"http://123.123.123.123:1234/ping/")
{
    Version = new Version(2, 0),
};

var response = await client.SendAsync(req);

Using same on Xamarin throws exception

 Java.IO.IOException: unexpected end of stream on com.android.okhttp.Address@ce6f1800 ---> Java.IO.EOFException: 
 not found: size=17 content=0000080700000000000000000000000001...
01-23 15:10:13.472 I/MonoDroid(28829):    --- End of inner exception stack trace ---
01-23 15:10:13.472 I/MonoDroid(28829):   at Java.Interop.JniEnvironment+InstanceMethods.CallIntMethod (Java.Interop.JniObjectReference instance, Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue* args) [0x0006e] in <e7e2d009b69d4e5f9a00e6ee600b8a8e>:0 
01-23 15:10:13.472 I/MonoDroid(28829):   at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeVirtualInt32Method (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x0002a] in <e7e2d009b69d4e5f9a00e6ee600b8a8e>:0 
01-23 15:10:13.472 I/MonoDroid(28829):   at Java.Net.HttpURLConnection.get_ResponseCode () [0x0000a] in <d706cf8faf5542949900cf6d57864528>:0 
01-23 15:10:13.472 I/MonoDroid(28829):   at Xamarin.Android.Net.AndroidClientHandler+<>c__DisplayClass46_0.<DoProcessRequest>b__2 () [0x00000] in <d706cf8faf5542949900cf6d57864528>:0 
01-23 15:10:13.472 I/MonoDroid(28829):   at System.Threading.Tasks.Task`1[TResult].InnerInvoke () [0x0000f] in /Users/builder/jenkins/workspace/archive-mono/2019-08/android/release/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs:534 
01-23 15:10:13.472 I/MonoDroid(28829):   at System.Threading.Tasks.Task.Execute () [0x00000] in /Users/builder/jenkins/workspace/archive-mono/2019-08/android/release/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs:2319 

Solution setting for DEBUG

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidLinkMode>None</AndroidLinkMode>
<AotAssemblies>false</AotAssemblies>
<EnableLLVM>false</EnableLLVM>
<AndroidEnableProfiledAot>false</AndroidEnableProfiledAot>
<BundleAssemblies>false</BundleAssemblies>
<AndroidSupportedAbis>
</AndroidSupportedAbis>
<EmbedAssembliesIntoApk>false</EmbedAssembliesIntoApk>
<Debugger>Xamarin</Debugger>
<AndroidUseSharedRuntime>true</AndroidUseSharedRuntime>
<AndroidUseAapt2>false</AndroidUseAapt2>
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
<AndroidTlsProvider>btls</AndroidTlsProvider>
</PropertyGroup>

My asp startup. I use it with grp service along. Publish as console single executable

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddGrpc((options => { options.EnableDetailedErrors = true; }));
        services.AddMvc(options => options.EnableEndpointRouting = false);

        //services.AddDbContext<PuvDbContext>();
        services.AddScoped<IAccountService, AccountService>();
        services.AddSingleton<IFirebirdService, FirebirdService>();
        services.AddSingleton<IClassificatorService, ClassificatorService>();
        services.AddSingleton<IClassificatorRepository, ClassificatorRepository>();

        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseRouting();
        app.UseMvcWithDefaultRoute();

        app.UseStaticFiles();
        app.UseMvc();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapGrpcService<GreeterService>();
            endpoints.MapGrpcService<AccountController>();
            endpoints.MapGrpcService<ReviewController>();
            endpoints.MapGrpcService<StaticDataController>();
            endpoints.MapGrpcService<TaskController>();
            endpoints.MapControllers();
        });


    }
}

My controller method which i call

[Route("files")]
public class FileController : Controller
{
    public FileController()
    {       
    }

    [HttpGet("hi")]
    public async Task<HttpResponseMessage> GetTest()
    {
        return new HttpResponseMessage(HttpStatusCode.OK);
    }
}
Mayman answered 23/1, 2020 at 21:9 Comment(16)
When you go into you Android project option and make sure the "HttpClient implementation" says AndroidClientHandler and what's the "SSL/TLS implementation" ?Soll
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType><AndroidTlsProvider>btls</AndroidTlsProvider>Mayman
When you changed the configuration to Release, do you still see the issue? Also does it work on iOS and can you share the code where you are setting the client handler to okhttp?Soll
I have same issue on Release. I coundnt test on iOs. I tested with exactly same code on console app and it works. I dont set any handler. Thats all code what i use. Also i dont use any library.Mayman
Were you able to make the same code work in a console application? Or a tool like PostmanSoll
Looks like you havent read question carefully. I stated that it works on console. Also mentioned again in comments. Also PostMan doesnt work with http/2. I just got idea to check with .net standart 2 lib or asp core 2, since i tested with asp core 3.1 which worked.Mayman
Ah my bad. Can you provide a sample application? So I can replicate it on my computerSoll
I updated question with solution (github) and some instructions which i tried to fallow. Important to note that it works on 3.1 but dont on 2.2. What makes me nervous that grpc works so it must be way how to make it work without many nasty workarounds.Mayman
@Mayman Did you manage to solve this?Alp
Sadly no. If you could target .net core 3 then there is no problem. Originally i wanted file transfer over htpp2, so i switched to http1 and my server for http1 one port and htpp2 another port.Mayman
I have no issues calling grpc services in Xamarin.Forms. Like i said grpc works for me, but calling http2 dont work.Mayman
@Mayman Since xamarin works for you, Can you please have a look at below question that I posted? #60360930Alp
@Mayman Did you ever have a chance to try out grpc with Xamarin.Forms on iOS?Domenicadomenico
No i havent. I will in second part of this year. I read that a lot about http2 and apple push notications or smth like that so assume it should work. What issue do you have?Mayman
@Mayman Did you find a solution to your problem? I am currently running into the same problemSuggs
No. I just gave up. Even more i started to use grpc-web since it support blazor, iis.Mayman
S
0

There's a few possible solutions:

  1. If you simply update your Target framework to .NET Standard 2.1 or newer, then that should fix your problem. If you can't do that because your solution in older, then try the rest. enter image description here
  2. Open your Android project options and then in "Android build", update the HttpClient implementation value to "AndroidClientHandler" or any other value and try again, and this got added in the CSPROJ for that config
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
<AndroidTlsProvider>btls</AndroidTlsProvider>

And then I just had to pass this HttpClientHandler in my HttpClient:

httpClient = new HttpClient(new HttpClientHandler()
{
    UseProxy = true
});
  1. If that doesn't work either, then you have to use one of these two nuget packages http2dotnet (https://github.com/Matthias247/http2dotnet) or httptwo (https://github.com/Redth/HttpTwo) instead as shown in their GitHub:
// Uri to request
var uri = new Uri ("http://somesite.com:80/index.html");

// Create a Http2Client
var http2 = new Http2Client (uri);

// Specify any custom headers
var headers = new NameValueCollection ();
headers.Add ("some-header", "value");

// For some requests you may have a request body
byte[] data = null; 

// Await our response
var response = await http2.Send (uri, HttpMethod.Get, headers, data); 
Soll answered 15/6, 2020 at 18:24 Comment(6)
1. .NET Core 3.0 and Xamarin. I think you misunderstand smth here 2. I said that i tried. Dont help. 3. I think i already tried both of them. Of course i have to try with fresh head now.Mayman
1,2 suggestion didnt helped. It could be server related too. I think i tried http2dotnet without success. Since grpc-web is release almost sure it gonna work on that.Mayman
i dont think thats even working. it is an answer based on assumption not based on experience and testingBathroom
@Bathroom what error are you facing? Different firewalls & servers have different requirements, did you try all of them?Soll
when i observe with mitmproxy on mac and it is always 1.1 whatever you do. Did you the Ios app or your answer is for Android?Bathroom
The logs in the question & the tag were for Android, and so was my answer @BathroomSoll

© 2022 - 2024 — McMap. All rights reserved.