In a gRPC-Web response body, how does a client tell the trailer frame from a message frame?
answer
- one bit distinguishes the two
- look at the frame's first byte
- the most significant bit is the flag
- 0x80 uncompressed, 0x81 compressed
- payload is lowercase key-value lines
basics
~20 sEvery frame in a gRPC-Web response body opens with a flag byte and a four-byte length. gRPC-Web sets the most significant bit of that flag byte to mark the trailer frame: 0x80 uncompressed, 0x81 compressed. It must be the last frame.
solid answer
~40 sThe body of a gRPC-Web response is a sequence of frames, each opening with a one-byte flag followed by a four-byte big-endian length. The variant claims the **most significant bit of that first byte**: with it set, the frame is not a message but the trailing status. `10000000b` (`0x80`) is an uncompressed trailer frame and `10000001b` (`0x81`) a compressed one. Its payload is not an encoded message - it is a block of key-and-value lines in the HTTP/1 form of RFC 7230 section 3.2, with lowercase field names, carrying `grpc-status` and, when present, `grpc-message`. That frame must be the last frame of the response, and nothing follows it: the body simply ends.
code
http · 5 linesHTTP/1.1 200 OK
content-type: application/grpc-web+proto
[ 00 ][ 00 00 00 2A ] <42 bytes: encoded response message>
[ 80 ][ 00 00 00 0F ] grpc-status:0<CRLF>go deeper
The takeaway is that the status rides in the body as a special last frame, and one bit in that frame's first byte is how a reader recognises it. You are not expected to recall the byte values yet.
Be able to draw the five-byte prefix, say which bit is the flag and which byte carries it, give 0x80 and 0x81, and describe the payload as a lowercase key-and-value block rather than an encoded message.
Connect the mechanism to the failure: because the trailer frame is the last thing in the body and the body's end is the close, a truncated response and a completed one differ only by that frame's presence. Say what a client must do when it is absent.
The design judgement here is that reframing trailing metadata as a textual block keeps translation schema-independent, so an intermediary can bridge browser traffic without compiling any service's definitions - a cost worth naming when deciding where translation lives.
## One bit does the whole job A gRPC-Web response body is a sequence of frames laid end to end. Each frame opens with a fixed five-byte prefix: **one flag byte**, then a **four-byte big-endian length** for the payload that follows. In the native protocol that flag byte is used to say whether the message payload is compressed. gRPC-Web keeps that meaning and adds one of its own by claiming the byte's **most significant bit**: | First byte | Binary | Meaning | |---|---|---| | `0x00` | `00000000b` | A message frame, payload not compressed | | `0x01` | `00000001b` | A message frame, payload compressed | | `0x80` | `10000000b` | The **trailer frame**, not compressed | | `0x81` | `10000001b` | The **trailer frame**, compressed | So a receiver reads five bytes, looks at the top bit of the first one, and knows immediately whether the next *length* bytes are a message to hand to the application or the status that ends the call. Note precisely which byte carries the flag: it is the **first byte of the frame**, not the first byte of the four-byte length. ## What is inside the trailer frame The payload is deliberately *not* an encoded message. It is a block of **key-and-value lines in the HTTP/1 form**, the shape described by RFC 7230 section 3.2, with **lowercase field names** - the same textual shape the fields would have had in a trailer section, simply relocated into the body. A minimal one is a single line: - `grpc-status:0` followed by a carriage return and line feed - fifteen bytes, so the frame's length prefix reads `00 00 00 0F`. A failing call adds the status message field, and any trailing metadata the service attached rides in the same block. The point of reusing the textual header-block form is that the translating step in either direction is mechanical: an intermediary lifting a native trailer section into a gRPC-Web body is moving a block of lowercase name-and-value pairs from one place to another, not re-encoding it. ## Placement rules that follow from it 1. **The trailer frame is the last frame of the response.** No message frame may follow it. A server-streaming response is therefore *n* message frames and then exactly one trailer frame. 2. **The body ends there.** There is no protocol event that closes the response - end of body is the close. 3. **A response without that frame did not deliver a status**, which is what makes a cut response detectable at all. ## Reading a response as a receiver The loop a conforming client runs is small: - Read five bytes. If fewer arrive and the body has ended, the response was truncated. - If the top bit of byte one is clear, read *length* bytes as a message and deliver it to the caller. - If the top bit is set, read *length* bytes, parse them as a lowercase key-and-value block, and complete the call with the status it carries. - If the body ends with no such frame, fail the call - do not report success. ## Why an interviewer likes this question It separates candidates who have only read that "gRPC-Web puts trailers in the body" from those who know *how a receiver tells them apart*. The headline is a slogan; the bit is the mechanism. It also sets up the failure this leaf really cares about - a body that ends without that final frame is a call with no outcome, and nothing at the HTTP level will tell you so, because the status line said success long before the body was cut. One more discipline this question rewards: naming which trailer you mean. The native protocol's trailer section, the request field by which a client says it will accept one, and this re-encoded block in the body are three different things that all get called "trailers" in conversation. In a gRPC-Web response only the third exists.
- What does the byte `0x81` at the start of a gRPC-Web frame mean?The top bit marks it as the trailer frame and the low bit says its payload is compressed, so a receiver decompresses the payload before parsing the key-and-value block inside it. It is still the last frame of the response; compression changes how the block is carried, not where it sits.
- Why reuse a textual key-and-value block instead of encoding the status as a message?Because the block is the same shape the fields already have in a native trailer section. Translation in either direction becomes a relocation of lowercase name-and-value pairs rather than a re-encoding, and an intermediary needs no knowledge of the service's message definitions to perform it.
The delivery receipt is packed inside the last box rather than taped to the outside of the shipment: you learn the outcome only by opening every box in order, and a shipment that stops early looks complete until you notice the receipt never came.
saying these in an interview costs you the question
- Says the trailer frame is an encoded message like any other
- Looks for the flag in the four-byte length rather than the flag byte
- Thinks the trailer frame may appear anywhere in the body
- Confuses this block with a native HTTP/2 trailer section
- Assumes a body with no trailer frame is a completed call