grpc-dotnet icon indicating copy to clipboard operation
grpc-dotnet copied to clipboard

troubles using grpc-web client from .net framework

Open Bonuspunkt opened this issue 3 years ago • 8 comments

im running this on Win10 21H2 (Build 19044.1415) and trying to make a call from the .net framework to my .net 6 greeter service

Server

using web.Services;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddGrpc();

var app = builder.Build();
app.UseGrpcWeb();

// Configure the HTTP request pipeline.
app.MapGrpcService<GreeterService>().EnableGrpcWeb();
app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client.");

app.Run();

client project

<Project Sdk="Microsoft.NET.Sdk">

	<PropertyGroup>
		<OutputType>Exe</OutputType>
		<TargetFrameworks>net48;net6.0</TargetFrameworks>
		<ImplicitUsings>enable</ImplicitUsings>
		<LangVersion>10.0</LangVersion>
	</PropertyGroup>

	<ItemGroup>
		<PackageReference Include="Google.Protobuf" Version="3.19.1" />
		<PackageReference Include="Grpc.Net.Client" Version="2.41.0" />
		<PackageReference Include="Grpc.Net.Client.Web" Version="2.41.0" />
		<PackageReference Include="Grpc.Tools" Version="2.42.0">
			<PrivateAssets>all</PrivateAssets>
			<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
		</PackageReference>
	</ItemGroup>

	<ItemGroup Condition="$(TargetFramework) == 'net48'" >
		<PackageReference Include="System.Net.Http" Version="4.3.4" />
	</ItemGroup>

	<ItemGroup>
		<Protobuf Include="..\Proto\greet.proto" GrpcServices="Client" Link="Proto\greet.proto" />
	</ItemGroup>

</Project>

client code

using Grpc.Net.Client;
using Greet;
using Grpc.Net.Client.Web;

var version =
#if NET48
    "net48";
#else
    "net6.0";
#endif

var handler = new GrpcWebHandler(new HttpClientHandler());
var options = new GrpcChannelOptions {HttpHandler = handler};

var channel = GrpcChannel.ForAddress("http://localhost:5001", options);
var client = new Greeter.GreeterClient(channel);

var result = await client.SayHelloAsync(new HelloRequest { Name = version });
Console.WriteLine(result.Message);

which works fine with net6

dotnet run --framework net6.0
Hello net6.0

but fails on .net framework 4.8

dotnet run --framework net48

Unhandled Exception: Grpc.Core.RpcException: Status(StatusCode="Internal", Detail="Error starting gRPC call. HttpRequestException: An error occurred while sending the request. WebException: The server committed a protocol violation. Section=ResponseStatusLine", DebugException="System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: The server committed a protocol violation. Section=ResponseStatusLine
   at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context)
   at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)
...

any hint would be highly appreciated

Bonuspunkt avatar Jan 03 '22 11:01 Bonuspunkt

Running into the same problem, was about to create an issue as well. I have a repro available here , using a net48 client with a net5.0 server using a server-side streaming call, observing the same error that @Bonuspunkt reported.

Some additional notes:

  • When running the client as netcoreapp3.1, I get a different error message:
Unhandled exception. Grpc.Core.RpcException: Status(StatusCode="Unavailable", Detail="Error starting gRPC call. HttpRequestException: An error occurred while sending the request. IOException: The response ended prematurely.", DebugException="System.Net.Http.HttpRequestException: An error occurred while sending the request.
  • On the server, I get the following debug messages (same for net48 and netcoreapp3.1 clients):
dbug: Microsoft.AspNetCore.Server.Kestrel[39]
      Connection id "0HMEESG5T174T" accepted.
dbug: Microsoft.AspNetCore.Server.Kestrel[1]
      Connection id "0HMEESG5T174T" started.
dbug: Microsoft.AspNetCore.Server.Kestrel[29]
      Connection id "0HMEESG5T174T": HTTP/2 connection error.
      Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.Http2ConnectionErrorException: HTTP/2 connection error (PROTOCOL_ERROR): Invalid HTTP/2 connection preface.
         at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.Http2Connection.ParsePreface(ReadOnlySequence`1& buffer, SequencePosition& consumed, SequencePosition& examined)
         at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.Http2Connection.TryReadPrefaceAsync()
         at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.Http2Connection.ProcessRequestsAsync[TContext](IHttpApplication`1 application)
dbug: Microsoft.AspNetCore.Server.Kestrel[36]
      Connection id "0HMEESG5T174T" is closed. The last processed stream ID was 0.
dbug: Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets[6]
      Connection id "0HMEESG5T174T" received FIN.
dbug: Microsoft.AspNetCore.Server.Kestrel[2]
      Connection id "0HMEESG5T174T" stopped.
dbug: Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets[7]
      Connection id "0HMEESG5T174T" sending FIN because: "The Socket transport's send loop completed gracefully."

ThorstenReichert avatar Jan 03 '22 18:01 ThorstenReichert

@ThorstenReichert Have you tried verifying that the port number and protocol used in the client match up with the port/protocol that the gRPC service is running on?

@Bonuspunkt Can you share a repro project?

captainsafia avatar Jan 04 '22 04:01 captainsafia

.NET Framework only supports HTTP/1.1 (unless WinHttpHandler is configured). That's fine, because gRPC-Web works over HTTP/1.1. An ASP.NET Core gRPC project is configured by default to HTTP/2 only. This is most likely your problem

You're both sending non-TLS requests (i.e. the address is http and not https). That means the .NET Framework client is sending HTTP/1.1 to a server that expects HTTP/2.

To fix this, you must either use TLS and allow HTTP/1.1 and 2 (TLS allows negotiation between the client and server of which to use) or have a separate HTTP/1.1 only http port.

JamesNK avatar Jan 04 '22 04:01 JamesNK

I think it would be worth having troubleshooting docs for this situation.

JamesNK avatar Jan 04 '22 04:01 JamesNK

@captainsafia https://github.com/Bonuspunkt/PoC_gRPC-Web

Bonuspunkt avatar Jan 04 '22 07:01 Bonuspunkt

@JamesNK thank you, that has been the issue

https://github.com/Bonuspunkt/PoC_gRPC-Web/commit/2f1013c66a484ffb7023d03bd0f681831e909587

issue can be closed

Bonuspunkt avatar Jan 04 '22 08:01 Bonuspunkt

@JamesNK indeed, changing the appsettings json from

{ "Kestrel": { "EndpointDefaults": { "Protocols": "Http2" }}}

to

{ "Kestrel": { "EndpointDefaults": { "Protocols": "Http1AndHttp2" }}}

solved the issue for me, thanks a lot!

ThorstenReichert avatar Jan 04 '22 14:01 ThorstenReichert

it's useful

cysnet avatar Jun 10 '22 02:06 cysnet

worked for me

AustinAdodo avatar Sep 18 '22 07:09 AustinAdodo

Docs that discuss this: https://learn.microsoft.com/en-us/aspnet/core/grpc/grpcweb?view=aspnetcore-6.0#http-protocol

JamesNK avatar Sep 18 '22 10:09 JamesNK