Skip to content

Commit 9ce6e49

Browse files
authored
Merge pull request #2501 from CedricKassen/master
Fix premature leaving of context due to improper Http2ServerCallStream handling
2 parents fe42d64 + 8ed0a50 commit 9ce6e49

File tree

2 files changed

+86
-94
lines changed

2 files changed

+86
-94
lines changed

packages/grpc-js/src/server-call.ts

Lines changed: 72 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -553,105 +553,99 @@ export class Http2ServerCallStream<
553553
return metadata;
554554
}
555555

556-
receiveUnaryMessage(
557-
encoding: string,
558-
next: (
559-
err: Partial<ServerStatusResponse> | null,
560-
request?: RequestType
561-
) => void
562-
): void {
563-
const { stream } = this;
556+
receiveUnaryMessage(encoding: string): Promise<RequestType | void> {
557+
return new Promise((resolve, reject) => {
558+
const { stream } = this;
559+
560+
let receivedLength = 0;
561+
562+
// eslint-disable-next-line @typescript-eslint/no-this-alias
563+
const call = this;
564+
const body: Buffer[] = [];
565+
const limit = this.maxReceiveMessageSize;
564566

565-
let receivedLength = 0;
567+
this.stream.on('data', onData);
568+
this.stream.on('end', onEnd);
569+
this.stream.on('error', onEnd);
566570

567-
// eslint-disable-next-line @typescript-eslint/no-this-alias
568-
const call = this;
569-
const body: Buffer[] = [];
570-
const limit = this.maxReceiveMessageSize;
571+
function onData(chunk: Buffer) {
572+
receivedLength += chunk.byteLength;
571573

572-
stream.on('data', onData);
573-
stream.on('end', onEnd);
574-
stream.on('error', onEnd);
574+
if (limit !== -1 && receivedLength > limit) {
575+
stream.removeListener('data', onData);
576+
stream.removeListener('end', onEnd);
577+
stream.removeListener('error', onEnd);
578+
579+
reject({
580+
code: Status.RESOURCE_EXHAUSTED,
581+
details: `Received message larger than max (${receivedLength} vs. ${limit})`,
582+
});
583+
return;
584+
}
575585

576-
function onData(chunk: Buffer) {
577-
receivedLength += chunk.byteLength;
586+
body.push(chunk);
587+
}
578588

579-
if (limit !== -1 && receivedLength > limit) {
589+
function onEnd(err?: Error) {
580590
stream.removeListener('data', onData);
581591
stream.removeListener('end', onEnd);
582592
stream.removeListener('error', onEnd);
583-
next({
584-
code: Status.RESOURCE_EXHAUSTED,
585-
details: `Received message larger than max (${receivedLength} vs. ${limit})`,
586-
});
587-
return;
588-
}
589593

590-
body.push(chunk);
591-
}
592-
593-
function onEnd(err?: Error) {
594-
stream.removeListener('data', onData);
595-
stream.removeListener('end', onEnd);
596-
stream.removeListener('error', onEnd);
594+
if (err !== undefined) {
595+
reject({ code: Status.INTERNAL, details: err.message });
596+
return;
597+
}
597598

598-
if (err !== undefined) {
599-
next({ code: Status.INTERNAL, details: err.message });
600-
return;
601-
}
599+
if (receivedLength === 0) {
600+
reject({
601+
code: Status.INTERNAL,
602+
details: 'received empty unary message',
603+
});
604+
return;
605+
}
602606

603-
if (receivedLength === 0) {
604-
next({
605-
code: Status.INTERNAL,
606-
details: 'received empty unary message',
607-
});
608-
return;
609-
}
607+
call.emit('receiveMessage');
610608

611-
call.emit('receiveMessage');
609+
const requestBytes = Buffer.concat(body, receivedLength);
610+
const compressed = requestBytes.readUInt8(0) === 1;
611+
const compressedMessageEncoding = compressed ? encoding : 'identity';
612+
const decompressedMessage = call.getDecompressedMessage(
613+
requestBytes,
614+
compressedMessageEncoding
615+
);
612616

613-
const requestBytes = Buffer.concat(body, receivedLength);
614-
const compressed = requestBytes.readUInt8(0) === 1;
615-
const compressedMessageEncoding = compressed ? encoding : 'identity';
616-
const decompressedMessage = call.getDecompressedMessage(
617-
requestBytes,
618-
compressedMessageEncoding
619-
);
617+
if (Buffer.isBuffer(decompressedMessage)) {
618+
resolve(
619+
call.deserializeMessageWithInternalError(decompressedMessage)
620+
);
621+
return;
622+
}
620623

621-
if (Buffer.isBuffer(decompressedMessage)) {
622-
call.safeDeserializeMessage(decompressedMessage, next);
623-
return;
624+
decompressedMessage.then(
625+
decompressed =>
626+
resolve(call.deserializeMessageWithInternalError(decompressed)),
627+
(err: any) =>
628+
reject(
629+
err.code
630+
? err
631+
: {
632+
code: Status.INTERNAL,
633+
details: `Received "grpc-encoding" header "${encoding}" but ${encoding} decompression failed`,
634+
}
635+
)
636+
);
624637
}
625-
626-
decompressedMessage.then(
627-
decompressed => call.safeDeserializeMessage(decompressed, next),
628-
(err: any) =>
629-
next(
630-
err.code
631-
? err
632-
: {
633-
code: Status.INTERNAL,
634-
details: `Received "grpc-encoding" header "${encoding}" but ${encoding} decompression failed`,
635-
}
636-
)
637-
);
638-
}
638+
});
639639
}
640640

641-
private safeDeserializeMessage(
642-
buffer: Buffer,
643-
next: (
644-
err: Partial<ServerStatusResponse> | null,
645-
request?: RequestType
646-
) => void
647-
) {
641+
private async deserializeMessageWithInternalError(buffer: Buffer) {
648642
try {
649-
next(null, this.deserializeMessage(buffer));
643+
return this.deserializeMessage(buffer);
650644
} catch (err) {
651-
next({
645+
throw {
652646
details: getErrorMessage(err),
653647
code: Status.INTERNAL,
654-
});
648+
};
655649
}
656650
}
657651

packages/grpc-js/src/server.ts

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,17 +1176,14 @@ export class Server {
11761176
}
11771177
}
11781178

1179-
function handleUnary<RequestType, ResponseType>(
1179+
async function handleUnary<RequestType, ResponseType>(
11801180
call: Http2ServerCallStream<RequestType, ResponseType>,
11811181
handler: UnaryHandler<RequestType, ResponseType>,
11821182
metadata: Metadata,
11831183
encoding: string
1184-
): void {
1185-
call.receiveUnaryMessage(encoding, (err, request) => {
1186-
if (err) {
1187-
call.sendError(err);
1188-
return;
1189-
}
1184+
): Promise<void> {
1185+
try {
1186+
const request = await call.receiveUnaryMessage(encoding);
11901187

11911188
if (request === undefined || call.cancelled) {
11921189
return;
@@ -1209,7 +1206,9 @@ function handleUnary<RequestType, ResponseType>(
12091206
call.sendUnaryMessage(err, value, trailer, flags);
12101207
}
12111208
);
1212-
});
1209+
} catch (err) {
1210+
call.sendError(err as ServerErrorResponse)
1211+
}
12131212
}
12141213

12151214
function handleClientStreaming<RequestType, ResponseType>(
@@ -1243,17 +1242,14 @@ function handleClientStreaming<RequestType, ResponseType>(
12431242
handler.func(stream, respond);
12441243
}
12451244

1246-
function handleServerStreaming<RequestType, ResponseType>(
1245+
async function handleServerStreaming<RequestType, ResponseType>(
12471246
call: Http2ServerCallStream<RequestType, ResponseType>,
12481247
handler: ServerStreamingHandler<RequestType, ResponseType>,
12491248
metadata: Metadata,
12501249
encoding: string
1251-
): void {
1252-
call.receiveUnaryMessage(encoding, (err, request) => {
1253-
if (err) {
1254-
call.sendError(err);
1255-
return;
1256-
}
1250+
): Promise<void> {
1251+
try {
1252+
const request = await call.receiveUnaryMessage(encoding);
12571253

12581254
if (request === undefined || call.cancelled) {
12591255
return;
@@ -1267,7 +1263,9 @@ function handleServerStreaming<RequestType, ResponseType>(
12671263
);
12681264

12691265
handler.func(stream);
1270-
});
1266+
} catch (err) {
1267+
call.sendError(err as ServerErrorResponse)
1268+
}
12711269
}
12721270

12731271
function handleBidiStreaming<RequestType, ResponseType>(

0 commit comments

Comments
 (0)