pythongrpchttp2

How to Configure a gRPC Server in Python to Use HTTP/2 Protocol on an Insecure Channel?


I've created a gRPC server using proto3 and Python for basic client-server communication. When I start my server application, it communicates with the client as expected. However, I notice that the HTTP protocol being used is HTTP/1.1, as observed in Wireshark logs.

Sample code:

   import grpc
   from concurrent import futures
   from my_proto import my_service_pb2_grpc

   def serve():
        server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
        my_service_pb2_grpc.add_MyServiceServicer_to_server(MyService(), server)
        # Adding insecure port
        server.add_insecure_port('[::]:50051')
        server.start()
        server.wait_for_termination()

if __name__ == '__main__':
    serve()

I would like my gRPC server to communicate using the HTTP/2 protocol over an insecure channel.

Reference: I came across this GitHub issue which states that HTTP/2 support is typically associated with secure channels. However, I’d like to confirm if there is a way to ensure HTTP/2 usage on an insecure channel.

Despite configuring the server to use add_insecure_port, the communication seems to default to HTTP/1.1. I expected it to use HTTP/2, which is the default for gRPC.

server.add_secure_port(f"{ip}:{port}",credentials)


Solution

  • Your code is correct per the gRPC site's Python helloworld server and it will be using HTTP/2

    The Python gRPC implementation shares a C++ foundation with several languages (C++, Java, etc.). The Golang implementation is different (grpc-go).

    For Python (and the other languages), Troubleshooting is available through environment variables e.g. GRPC_TRACE

    There are various ways that you can test for HTTP/2:

    Python client

    The Python client uses the shared library and shares troubleshooting, so you can:

    GRPC_TRACE=http \
    python3 your_client.py
    
    chttp2_transport.cc:1227] HTTP:CLI: Transport 0x7fe4a0001170 allocating new grpc_chttp2_stream 0x7fe4a0006650 to id 1
    chttp2_transport.cc:931] W:0x7fe4a0001170 CLIENT [ipv6:%5B::1%5D:50051] state WRITING -> WRITING+MORE [START_NEW_STREAM]
    chttp2_transport.cc:1293] complete_closure_step: t=0x7fe4a0001170 0x7fe4a0006458 refs=3 flags=0x0001 desc=op->on_complete err=OK write_state=WRITING+MORE whence=(null):-1
    writing.cc:447] W:0x7fe4a0001170 CLIENT[1] im-(sent,send)=(0,1)
    chttp2_transport.cc:1293] complete_closure_step: t=0x7fe4a0001170 0x7fe4a0006458 refs=2 flags=0x0001 desc=send_initial_metadata_finished err=OK write_state=WRITING+MORE whence=(null):-1
    chttp2_transport.cc:1293] complete_closure_step: t=0x7fe4a0001170 0x7fe4a0006458 refs=1 flags=0x0001 desc=send_trailing_metadata_finished err=OK write_state=WRITING+MORE whence=(null):-1
    chttp2_transport.cc:1293] complete_closure_step: t=0x7fe4a0001170 0x7fe4a0006458 refs=0 flags=0x0001 desc=on_write_finished_cb err=OK write_state=WRITING+MORE whence=(null):-1
    chttp2_transport.cc:931] W:0x7fe4a0001170 CLIENT [ipv6:%5B::1%5D:50051] state WRITING+MORE -> WRITING [begin write in current thread]
    chttp2_transport.cc:931] W:0x7fe4a0001170 CLIENT [ipv6:%5B::1%5D:50051] state WRITING -> IDLE [finish writing]
    parsing.cc:335] INCOMING[0x7fe4a0001170]: PING len:8 id:0x00000000
    chttp2_transport.cc:931] W:0x7fe4a0001170 CLIENT [ipv6:%5B::1%5D:50051] state IDLE -> WRITING [PING_RESPONSE]
    chttp2_transport.cc:931] W:0x7fe4a0001170 CLIENT [ipv6:%5B::1%5D:50051] state WRITING -> WRITING+MORE [begin partial write in background]
    chttp2_transport.cc:931] W:0x7fe4a0001170 CLIENT [ipv6:%5B::1%5D:50051] state WRITING+MORE -> WRITING [continue writing]
    chttp2_transport.cc:931] W:0x7fe4a0001170 CLIENT [ipv6:%5B::1%5D:50051] state WRITING -> IDLE [begin writing nothing]
    parsing.cc:335] INCOMING[0x7fe4a0001170]: HEADERS:END_HEADERS len:78 id:0x00000001
    parsing.cc:758] parsing initial_metadata
    parsing.cc:335] INCOMING[0x7fe4a0001170]: DATA len:18 id:0x00000001
    chttp2_transport.cc:931] W:0x7fe4a0001170 CLIENT [ipv6:%5B::1%5D:50051] state IDLE -> WRITING [BDP_PING]
    parsing.cc:335] INCOMING[0x7fe4a0001170]: HEADERS:END_STREAM:END_HEADERS len:30 id:0x00000001
    parsing.cc:764] parsing trailing_metadata
    parsing.cc:335] INCOMING[0x7fe4a0001170]: WINDOW_UPDATE len:4 id:0x00000000
    writing.cc:139] CLIENT[0x7fe4a0001170]: Ping e209562f587e244e sent [ipv6:%5B::1%5D:50051]: max_pings_without_data: 2, pings_before_data_required: 1, last_ping_sent_time_: @1324ms
    chttp2_transport.cc:931] W:0x7fe4a0001170 CLIENT [ipv6:%5B::1%5D:50051] state WRITING -> WRITING [begin write in current thread]
    chttp2_transport.cc:931] W:0x7fe4a0001170 CLIENT [ipv6:%5B::1%5D:50051] state WRITING -> IDLE [finish writing]
    

    NOTE The use of chttp2_transport and gRPC specific message content.

    gRPCurl

    A known-good gRPC tool that uses the Golang gRPC implementation and so requires different environment variables (unfortunately):

    GODEBUG=http2debug=2 \
    grpcurl \
    -vv \
    -plaintext \
    -proto your.proto \
    localhost:50051 \
    your-package.your-server/YourMethod
    
    2024/08/13 10:48:27 http2: Framer 0xc00042a000: wrote SETTINGS len=0
    2024/08/13 10:48:27 http2: Framer 0xc00042a000: read SETTINGS len=30, settings: MAX_CONCURRENT_STREAMS=2147483647, INITIAL_WINDOW_SIZE=4194304, MAX_FRAME_SIZE=4194304, MAX_HEADER_LIST_SIZE=16384, UNKNOWN_SETTING_65027=1
    2024/08/13 10:48:27 http2: Framer 0xc00042a000: read WINDOW_UPDATE len=4 (conn) incr=4128769
    2024/08/13 10:48:27 http2: Framer 0xc00042a000: read SETTINGS flags=ACK len=0
    2024/08/13 10:48:27 http2: Framer 0xc00042a000: wrote SETTINGS flags=ACK len=0
    
    Resolved method descriptor:
    // Sends a greeting
    rpc SayHello ( .helloworld.HelloRequest ) returns ( .helloworld.HelloReply );
    
    Request metadata to send:
    (empty)
    2024/08/13 10:48:27 http2: Framer 0xc00042a000: wrote HEADERS flags=END_HEADERS stream=1 len=120
    2024/08/13 10:48:27 http2: Framer 0xc00042a000: wrote DATA flags=END_STREAM stream=1 len=5 data="\x00\x00\x00\x00\x00"
    2024/08/13 10:48:27 http2: Framer 0xc00042a000: read PING len=8 ping=" \xe1\xcd\xe3/\x9fC\x9f"
    2024/08/13 10:48:27 http2: Framer 0xc00042a000: wrote PING flags=ACK len=8 ping=" \xe1\xcd\xe3/\x9fC\x9f"
    2024/08/13 10:48:27 http2: Framer 0xc00042a000: read HEADERS flags=END_HEADERS stream=1 len=78
    2024/08/13 10:48:27 http2: decoded hpack field header field ":status" = "200"
    2024/08/13 10:48:27 http2: decoded hpack field header field "content-type" = "application/grpc"
    2024/08/13 10:48:27 http2: decoded hpack field header field "grpc-accept-encoding" = "identity, deflate, gzip"
    2024/08/13 10:48:27 http2: Framer 0xc00042a000: read DATA stream=1 len=15 data="\x00\x00\x00\x00\n\n\bHello, !"
    2024/08/13 10:48:27 http2: Framer 0xc00042a000: read HEADERS flags=END_STREAM|END_HEADERS stream=1 len=30
    2024/08/13 10:48:27 http2: decoded hpack field header field "grpc-status" = "0"
    2024/08/13 10:48:27 http2: decoded hpack field header field "grpc-message" = ""
    2024/08/13 10:48:27 http2: Framer 0xc00042a000: read WINDOW_UPDATE len=4 (conn) incr=5
    2024/08/13 10:48:27 http2: Framer 0xc00042a000: wrote WINDOW_UPDATE len=4 (conn) incr=15
    2024/08/13 10:48:27 http2: Framer 0xc00042a000: wrote PING len=8 ping="\x02\x04\x10\x10\t\x0e\a\a"
    
    Response headers received:
    content-type: application/grpc
    grpc-accept-encoding: identity, deflate, gzip
    
    Estimated response size: 10 bytes
    
    Response contents:
    {
      "message": "Hello, !"
    }
    
    Response trailers received:
    (empty)
    Sent 0 requests and received 1 response
    2024/08/13 10:48:27 http2: Framer 0xc00042a000: read PING flags=ACK len=8 ping="\x02\x04\x10\x10\t\x0e\a\a"
    Timing Data: 5.519619ms
      Dial: 1.590924ms
        BlockingDial: 1.571091ms
      InvokeRPC: 3.007197ms