how to make a grpc call using http2

1,644 views
Skip to first unread message

Nikos Skalis

unread,
Apr 18, 2023, 9:48:21 AM4/18/23
to grpc.io
Hi,

I am trying to make a gRPC call based on a HTTP2 client to work like it is described here: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md

I am using an official example of a gRPC server: https://grpc.io/docs/languages/python/quickstart/

Based on grpcurl:

% grpcurl -import-path ../../protos -proto helloworld.proto -plaintext -d '{"name": "niko"}' -H 'Content-Type: application/grpc+json' localhost:50051 helloworld.Greeter/SayHello
{
  "message": "Hello, niko!"
}

Based on nghttp:

# json_data = '{"name": "niko"}'
# compressed_flag = 00
# message_length = 00000010
# message_binary = 7b226e616d65223a20226e696b6f227d
echo -n '00000000107b226e616d65223a20226e696b6f227d' | xxd -r -p - frame.bin


% nghttp  -H ":method: POST" -H "Content-Type: application/grpc+json" -H "TE: trailers" --data=frame.bin http://localhost:50051/helloworld.Greeter/SayHello --verbose

[  0.004] Connected
[  0.004] recv SETTINGS frame <length=24, flags=0x00, stream_id=0>
          (niv=4)
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):4194304]
          [SETTINGS_MAX_FRAME_SIZE(0x05):4194304]
          [SETTINGS_MAX_HEADER_LIST_SIZE(0x06):8192]
          [UNKNOWN(0xfe03):1]
[  0.004] recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
          (window_size_increment=4128769)
[  0.004] send SETTINGS frame <length=12, flags=0x00, stream_id=0>
          (niv=2)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[  0.004] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
          ; ACK
          (niv=0)
[  0.004] send PRIORITY frame <length=5, flags=0x00, stream_id=3>
          (dep_stream_id=0, weight=201, exclusive=0)
[  0.004] send PRIORITY frame <length=5, flags=0x00, stream_id=5>
          (dep_stream_id=0, weight=101, exclusive=0)
[  0.004] send PRIORITY frame <length=5, flags=0x00, stream_id=7>
          (dep_stream_id=0, weight=1, exclusive=0)
[  0.004] send PRIORITY frame <length=5, flags=0x00, stream_id=9>
          (dep_stream_id=7, weight=1, exclusive=0)
[  0.004] send PRIORITY frame <length=5, flags=0x00, stream_id=11>
          (dep_stream_id=3, weight=1, exclusive=0)
[  0.004] send HEADERS frame <length=95, flags=0x24, stream_id=13>
          ; END_HEADERS | PRIORITY
          (padlen=0, dep_stream_id=11, weight=16, exclusive=0)
          ; Open new stream
          :method: POST
          :path: /helloworld.Greeter/SayHello
          :scheme: http
          :authority: localhost:50051
          accept: */*
          accept-encoding: gzip, deflate
          user-agent: nghttp2/1.52.0
          content-length: 21
          content-type: application/grpc+json
          te: trailers
[  0.004] send DATA frame <length=21, flags=0x01, stream_id=13>
          ; END_STREAM
[  0.005] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
          ; ACK
          (niv=0)
[  0.005] recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
          (window_size_increment=21)
[  0.005] recv PING frame <length=8, flags=0x00, stream_id=0>
          (opaque_data=0000000000000000)
[  0.005] send PING frame <length=8, flags=0x01, stream_id=0>
          ; ACK
          (opaque_data=0000000000000000)
[  0.006] recv (stream_id=13) :status: 200
[  0.006] recv (stream_id=13) content-type: application/grpc
[  0.006] recv (stream_id=13) grpc-status: 13
[  0.006] recv (stream_id=13) grpc-message: Exception deserializing request!
[  0.006] recv HEADERS frame <length=95, flags=0x05, stream_id=13>
          ; END_STREAM | END_HEADERS
          (padlen=0)
          ; First response header
[  0.006] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
          (last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[])

The server complains in the same way:

% python3 greeter_server.py
Server started, listening on 50051
ERROR:grpc._common:Exception deserializing message!
Traceback (most recent call last):
  File "/Library/Python/3.9/site-packages/grpc/_common.py", line 90, in _transform
    return transformer(message)
google.protobuf.message.DecodeError: Error parsing message


Would you be so kind to help me out and advise what I am doing wrong?


Craig Tiller

unread,
Apr 18, 2023, 10:03:03 AM4/18/23
to Nikos Skalis, grpc.io
Looks like you've got the http headers in place but are missing the five byte message header from grpc in your data stream

--
You received this message because you are subscribed to the Google Groups "grpc.io" group.
To unsubscribe from this group and stop receiving emails from it, send an email to grpc-io+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/grpc-io/de08caec-4594-4173-bc74-d84aaaeb9038n%40googlegroups.com.

Nikos Skalis

unread,
Apr 18, 2023, 10:07:52 AM4/18/23
to grpc.io
Thank you for looking at it, but what do you mean exactly? How would you write the below differently?

# json_data = '{"name": "niko"}'
# compressed_flag = 00
# message_length = 00000010
# message_binary = 7b226e616d65223a20226e696b6f227d
echo -n '00000000107b226e616d65223a20226e696b6f227d' | xxd -r -p - frame.bin



Nikos Skalis

unread,
Apr 18, 2023, 2:41:42 PM4/18/23
to grpc.io
I just noticed (see above the verbose output from curl) the grpc server replies with content-type application/grpc instead of application/grpc+json like the client requested, is that expected? is this why the deserialisation fails?

Eric Anderson

unread,
May 2, 2023, 2:40:58 PM5/2/23
to Nikos Skalis, grpc.io
I took a look at this with Java server, and it actually works without issue. So, yeah, I think any issue you experienced was the server trying to decode the JSON message as a protobuf.

To test this with Java, you can use the HelloJsonServer. Add createStartScripts('io.grpc.examples.advanced.HelloJsonServer') to examples/build.gradle, ./gradlew installDist, then run ./build/install/examples/bin/hello-json-server . I see a nice { "message": "Hello niko" } reply.

If you run ./build/install/examples/bin/hello-world-server as the server instead, then you get an error 2 (UNKNOWN) because of the decoding exception you can see in the logs on server-side.

The +proto vs +json isn't entirely a thing. It was in the wire format but completely unused, until grpc-go added it thinking the other languages supported it. The only thing most languages do is strip off anything after the +.

Reply all
Reply to author
Forward
0 new messages