fix(protobuf): exclude trailing whitespace from gRPC-web trailer values (#7069)

RFC 9110 section 5.5 excludes leading and trailing whitespace from a field value. The gRPC-web trailer parser stripped only leading SP/HTAB, so 'grpc-status: 0 ' was reported as '0 '. Trailing SP/HTAB is now excluded too; internal whitespace and the examined byte count are unchanged.
This commit is contained in:
Adolanium
2026-10-25 07:45:22 +02:00
committed by GitHub
parent 60ab148afa
commit 06a4c338fc
2 changed files with 24 additions and 1 deletions
@@ -73,6 +73,25 @@ it("retains allowed whitespace, visible text, and valid UTF-8 obs-text", async (
expect(result.examined_bytes).toBe(5 + Buffer.byteLength(text));
});
it("excludes trailing whitespace from trailer field values", async () => {
const text =
"grpc-status: 0 \r\ngrpc-message: alpha\tbeta \t\r\nx-empty: \t\r\n";
const result = await decodeProtobufBody(
trailerFrame(text),
"grpc-web",
source,
same,
new ProtobufBudget(),
);
expect(result.status).toBe("complete");
expect(result.frames[0]?.trailers).toEqual([
{ name: "grpc-status", value: "0" },
{ name: "grpc-message", value: "alpha\tbeta" },
{ name: "x-empty", value: "" },
]);
expect(result.examined_bytes).toBe(5 + Buffer.byteLength(text));
});
it.each(["grpc-web", "grpc-web-text"] as const)(
"keeps valid trailer fields around an opaque non-UTF-8 value in %s framing",
async (framing) => {
@@ -84,9 +84,13 @@ export const parseGrpcWebTrailers = (
"gRPC-web trailer field value contains a forbidden control character",
});
}
// RFC 9110 section 5.5: a field value excludes surrounding whitespace.
let valueEnd = lineEnd;
while (valueEnd > valueStart && [9, 32].includes(bytes[valueEnd - 1]!))
valueEnd -= 1;
let value: string;
try {
value = decoder.decode(bytes.subarray(valueStart, lineEnd));
value = decoder.decode(bytes.subarray(valueStart, valueEnd));
} catch (cause) {
if (!(cause instanceof TypeError)) throw cause;
decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });