Skip to content

Commit 7719e37

Browse files
committed
grpc-js: Fix client hang when receiving extra messages for a unary response
1 parent 45e5fe5 commit 7719e37

File tree

2 files changed

+97
-4
lines changed

2 files changed

+97
-4
lines changed

packages/grpc-js/src/client.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ export class Client {
330330
// eslint-disable-next-line @typescript-eslint/no-explicit-any
331331
onReceiveMessage(message: any) {
332332
if (responseMessage !== null) {
333-
call.cancelWithStatus(Status.INTERNAL, 'Too many responses received');
333+
call.cancelWithStatus(Status.UNIMPLEMENTED, 'Too many responses received');
334334
}
335335
responseMessage = message;
336336
},
@@ -345,7 +345,7 @@ export class Client {
345345
callProperties.callback!(
346346
callErrorFromStatus(
347347
{
348-
code: Status.INTERNAL,
348+
code: Status.UNIMPLEMENTED,
349349
details: 'No message received',
350350
metadata: status.metadata,
351351
},
@@ -463,9 +463,10 @@ export class Client {
463463
// eslint-disable-next-line @typescript-eslint/no-explicit-any
464464
onReceiveMessage(message: any) {
465465
if (responseMessage !== null) {
466-
call.cancelWithStatus(Status.INTERNAL, 'Too many responses received');
466+
call.cancelWithStatus(Status.UNIMPLEMENTED, 'Too many responses received');
467467
}
468468
responseMessage = message;
469+
call.startRead();
469470
},
470471
onReceiveStatus(status: StatusObject) {
471472
if (receivedStatus) {
@@ -478,7 +479,7 @@ export class Client {
478479
callProperties.callback!(
479480
callErrorFromStatus(
480481
{
481-
code: Status.INTERNAL,
482+
code: Status.UNIMPLEMENTED,
482483
details: 'No message received',
483484
metadata: status.metadata,
484485
},

packages/grpc-js/test/test-server-errors.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,98 @@ describe('Server serialization failure handling', () => {
286286
});
287287
});
288288

289+
describe('Cardinality violations', () => {
290+
let client: ServiceClient;
291+
let server: Server;
292+
let responseCount: number = 1;
293+
const testMessage = Buffer.from([]);
294+
before(done => {
295+
const serverServiceDefinition = {
296+
testMethod: {
297+
path: '/TestService/TestMethod/',
298+
requestStream: false,
299+
responseStream: true,
300+
requestSerialize: identity,
301+
requestDeserialize: identity,
302+
responseDeserialize: identity,
303+
responseSerialize: identity
304+
}
305+
};
306+
const clientServiceDefinition = {
307+
testMethod: {
308+
path: '/TestService/TestMethod/',
309+
requestStream: true,
310+
responseStream: false,
311+
requestSerialize: identity,
312+
requestDeserialize: identity,
313+
responseDeserialize: identity,
314+
responseSerialize: identity
315+
}
316+
};
317+
const TestClient = grpc.makeClientConstructor(clientServiceDefinition, 'TestService');
318+
server = new grpc.Server();
319+
server.addService(serverServiceDefinition, {
320+
testMethod(stream: ServerWritableStream<any, any>) {
321+
for (let i = 0; i < responseCount; i++) {
322+
stream.write(testMessage);
323+
}
324+
stream.end();
325+
}
326+
});
327+
server.bindAsync('localhost:0', serverInsecureCreds, (error, port) => {
328+
assert.ifError(error);
329+
client = new TestClient(`localhost:${port}`, clientInsecureCreds);
330+
done();
331+
});
332+
});
333+
beforeEach(() => {
334+
responseCount = 1;
335+
});
336+
after(done => {
337+
client.close();
338+
server.tryShutdown(done);
339+
});
340+
it('Should fail if the client sends too few messages', done => {
341+
const call = client.testMethod((err: ServiceError, data: any) => {
342+
assert(err);
343+
assert.strictEqual(err.code, grpc.status.UNIMPLEMENTED);
344+
done();
345+
});
346+
call.end();
347+
});
348+
it('Should fail if the client sends too many messages', done => {
349+
const call = client.testMethod((err: ServiceError, data: any) => {
350+
assert(err);
351+
assert.strictEqual(err.code, grpc.status.UNIMPLEMENTED);
352+
done();
353+
});
354+
call.write(testMessage);
355+
call.write(testMessage);
356+
call.end();
357+
});
358+
it('Should fail if the server sends too few messages', done => {
359+
responseCount = 0;
360+
const call = client.testMethod((err: ServiceError, data: any) => {
361+
assert(err);
362+
assert.strictEqual(err.code, grpc.status.UNIMPLEMENTED);
363+
done();
364+
});
365+
call.write(testMessage);
366+
call.end();
367+
});
368+
it('Should fail if the server sends too many messages', done => {
369+
responseCount = 2;
370+
const call = client.testMethod((err: ServiceError, data: any) => {
371+
assert(err);
372+
assert.strictEqual(err.code, grpc.status.UNIMPLEMENTED);
373+
done();
374+
});
375+
call.write(testMessage);
376+
call.end();
377+
});
378+
379+
});
380+
289381
describe('Other conditions', () => {
290382
let client: ServiceClient;
291383
let server: Server;

0 commit comments

Comments
 (0)