Skip to content

Commit 3ebec41

Browse files
committed
feature(@nestjs/microservices) allow grpc clients to disconnect
- when client cancels the server stream gets completed too - client introduced disconnection does not throw errors on the client side - release client's event listener on 'end'
1 parent fed2459 commit 3ebec41

2 files changed

Lines changed: 25 additions & 3 deletions

File tree

packages/microservices/client/client-grpc.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,27 @@ export class ClientGrpcProxy extends ClientProxy implements ClientGrpc {
6060
return (...args) => {
6161
return new Observable(observer => {
6262
const call = client[methodName](...args);
63+
let isClientCanceled = false;
6364
call.on('data', (data: any) => observer.next(data));
64-
call.on('error', (error: any) => observer.error(error));
65-
call.on('end', () => observer.complete());
65+
call.on('error', (error: any) => {
66+
if (error.details === 'Cancelled') {
67+
call.destroy();
68+
if ( isClientCanceled ) {
69+
return; // do not error if cancel was inititiated by Client
70+
}
71+
}
72+
observer.error(error);
73+
});
74+
call.on('end', () => {
75+
call.removeAllListeners();
76+
observer.complete();
77+
});
78+
return () => {
79+
if (!call.finished) {
80+
isClientCanceled = true;
81+
call.cancel();
82+
}
83+
};
6684
});
6785
};
6886
}

packages/microservices/server/server-grpc.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { GrpcOptions, MicroserviceOptions } from '../interfaces/microservice-con
44
import { GRPC_DEFAULT_URL } from './../constants';
55
import { CustomTransportStrategy } from './../interfaces';
66
import { Server } from './server';
7+
import { fromEvent } from 'rxjs';
8+
import { takeUntil } from 'rxjs/operators';
79

810
let grpcPackage: any = {};
911

@@ -103,7 +105,9 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
103105
return async (call, callback) => {
104106
const handler = methodHandler(call.request, call.metadata);
105107
const result$ = this.transformToObservable(await handler);
106-
await result$.forEach(data => call.write(data));
108+
await result$.pipe(
109+
takeUntil(fromEvent(call, 'cancelled')),
110+
).forEach(data => call.write(data));
107111
call.end();
108112
};
109113
}

0 commit comments

Comments
 (0)