Hello,

Trying to wrap my head around strange gRPC behavior I am seeing. I
took a look at the trace output, but I am not familiar with the H2
processing logic so I'll try to describe my observations the best I
can.

Four scenarios of gRPC requests that I have looked at. These are all
looking at how HAProxy communicates with the backend:

First one to look at is the happy path of a single message. If H2 mux
sends HEADER and DATA, where DATA includes End Stream flag, then the
response from backend server is HEADER (initial 200 OK), DATA (gRPC
protobuf), and HEADER (grpc-status header, includes End Stream flag
too).

Second scenario is that the backend service returns a failure right
away, for a missing service. The response skips the additional DATA
and HEADER. Response from backend is simply a HEADER (200 OK, with
grpc-status header and End Stream flag).

If the write stream is not closed by the frontend client, mux does not
set the End Stream flag, obviously. Backend responds with HEADER and
DATA, the backend is sent additional DATA, then the backend responds
with HEADER (End Stream here).

Where everything breaks apart is if the request fails. The gRPC
backend server seems to return HEADER frame of 200 OK, which right
away includes `grpc-status: 12` and `grpc-message: unknown service
grpc.reflection.v1.ServerReflection` headers. There is no follow-up
DATA or a later HEADER frame. Instead, the following frame from the
backend server is RST_STREAM as the server is done with the request
and wants the next request.

HAProxy sees this situation as SH-- termination state, as it is still
in mux_h2 FRAME_P state. It seems, it really wants the DATA.. but the
server doesn't have any to offer. This results in HTTP/1.1 502 Bad
Gateway which is not helpful for H2 gRPC clients. Instead, when the
stream is reset, the HTTP/2 200 OK with gRPC error headers should be
sent to the client at frontend.

Test requests are being made via github.com/jhump/protoreflect as it
keeps the stream open on client side for gRPC server reflection.
Unredacted TCP dumps can be provided in private, if preferred.

---

HAProxy version 2.8.2-1ppa1~jammy (from Ubuntu PPA)

global
        log /dev/log local0
        log /dev/log local1 notice

defaults
        log global
        mode http
        option httplog
        timeout connect 5000
        timeout client 50000
        timeout server 50000

frontend fe
        bind 127.0.0.1:80 proto h2
        default_backend be

backend be
        server srv 65.109.88.38:12790 proto h2

---

Best regards,
Valters Jansons

Reply via email to