2
votes

I am developing a java based Grpc server using spring boot (2.2.2) and the grpc-server-spring-boot-starter java library. My client application is a C# application (netcoreapp3.1). Both the applications run on my laptop for this test.

This is my proto file

syntax = "proto3";

option java_multiple_files = true;
option java_package = "com.honeywell.EOM.SystemAPI.webapi.web.grpc.service";
//option java_outer_classname = "TestProto";

service Simple {
    rpc GetData (Empty) returns (Data) {
    }
}

message Empty {

}

message Data {
    string name = 1;
}

This is the service code

@GrpcService
public class TestService extends SimpleGrpc.SimpleImplBase {
    @Override
    public void getData(Empty request, StreamObserver<Data> responseObserver) {
        Data data = Data.newBuilder().setName("Somename").build();
        responseObserver.onNext(data);
        responseObserver.onCompleted();
    }
}

I first tested the server using this tool tool

The service works fine with this client tool.

However when I test using the C# client I get this error

io.grpc.netty.shaded.io.grpc.netty.NettyServerTransport notifyTerminated
INFO: Transport failed
io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2Exception: HTTP/2 client preface string missing or corrupt. Hex dump for received bytes: 16030100b6010000b203035e95b0402c6320969d3d5fba04
    at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2Exception.connectionError(Http2Exception.java:103)
    at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler$PrefaceDecoder.readClientPrefaceString(Http2ConnectionHandler.java:306)
    at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler$PrefaceDecoder.decode(Http2ConnectionHandler.java:239)
    at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler.decode(Http2ConnectionHandler.java:438)
    at io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:505)
    at io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:444)
    at io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:283)
    at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
    at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
    at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
    at io.grpc.netty.shaded.io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1422)
    at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
    at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
    at io.grpc.netty.shaded.io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:931)
    at io.grpc.netty.shaded.io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
    at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:700)
    at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:635)
    at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:552)
    at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:514)
    at io.grpc.netty.shaded.io.netty.util.concurrent.SingleThreadEventExecutor$6.run(SingleThreadEventExecutor.java:1044)
    at io.grpc.netty.shaded.io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
    at io.grpc.netty.shaded.io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.base/java.lang.Thread.run(Thread.java:834)

The C# client code is here

using var channel = GrpcChannel.ForAddress("https://localhost:9090");
var client = new Simple.SimpleClient(channel);
var reply = client.GetData(new Empty { });

What am I doing wrong? Is this an open defect?

2
what TFM is this on? this looks like it should work just fine; do you perhaps have a proxy or http-inspector (fiddler, etc) in the mix here? if so: they can break the transport by not correctly continuing as http/2Marc Gravell♦
netcoreapp3.1. I've added this to the questionAaron Dsouza
and any network inspector tools like fiddler or similar?Marc Gravell♦
Suggestion: to narrow down the problem, perhaps use the Google transport (rather than the Microsoft transport) by adding a package reference to Grpc.Core from nuget, and Channel channel = new Channel("localhost:9090", ChannelCredentials.Insecure); (the rest of your client code the same); if the problem is the same, then the problem is either network or server; if the problem goes away, then you should get in touch with JamesNK by logging it (with as much context as possible, preferably a repro) here: github.com/grpc/grpc-dotnet/issuesMarc Gravell♦
This worked! Can you add this as an answer so that I can mark the question as answered.Aaron Dsouza

2 Answers

0
votes

As stated in Eric Anderson's answer, the client is indeed using TLS.

The .NET Core client for gRPC requires additional configuration when communicating with insecure (non-TLS) servers (as of .NET Core 3.1).

// This switch must be set before creating the GrpcChannel/HttpClient.
AppContext.SetSwitch(
    "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);

using var channel = GrpcChannel.ForAddress("http://localhost:9090");
var client = new Simple.SimpleClient(channel);
var reply = client.GetData(new Empty { });
0
votes

The error message reports the received bytes (in hex):

16030100b6010000b203035e95b0402c6320969d3d5fba04

The three starting bytes 160301 look like a TLS handshake. The server is probably in plain-text mode and the client is using TLS.

To use plain-text on the client, use "http":

using var channel = GrpcChannel.ForAddress("http://localhost:9090");

Edit: this answer's code sample is broken; it does not properly enable plain-text