diff --git a/packages/grpc-js/package.json b/packages/grpc-js/package.json index fd8dd7abf..1dd0f0cbe 100644 --- a/packages/grpc-js/package.json +++ b/packages/grpc-js/package.json @@ -1,6 +1,6 @@ { "name": "@grpc/grpc-js", - "version": "1.12.3", + "version": "1.12.4", "description": "gRPC Library for Node - pure JS implementation", "homepage": "https://grpc.io/", "repository": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js", diff --git a/packages/grpc-js/src/subchannel-call.ts b/packages/grpc-js/src/subchannel-call.ts index bee00119f..6fc1e1eb5 100644 --- a/packages/grpc-js/src/subchannel-call.ts +++ b/packages/grpc-js/src/subchannel-call.ts @@ -140,6 +140,8 @@ export class Http2SubchannelCall implements SubchannelCall { private serverEndedCall = false; + private connectionDropped = false; + constructor( private readonly http2Stream: http2.ClientHttp2Stream, private readonly callEventTracker: CallEventTracker, @@ -240,8 +242,16 @@ export class Http2SubchannelCall implements SubchannelCall { details = 'Stream refused by server'; break; case http2.constants.NGHTTP2_CANCEL: - code = Status.CANCELLED; - details = 'Call cancelled'; + /* Bug reports indicate that Node synthesizes a NGHTTP2_CANCEL + * code from connection drops. We want to prioritize reporting + * an unavailable status when that happens. */ + if (this.connectionDropped) { + code = Status.UNAVAILABLE; + details = 'Connection dropped'; + } else { + code = Status.CANCELLED; + details = 'Call cancelled'; + } break; case http2.constants.NGHTTP2_ENHANCE_YOUR_CALM: code = Status.RESOURCE_EXHAUSTED; @@ -321,10 +331,15 @@ export class Http2SubchannelCall implements SubchannelCall { } public onDisconnect() { - this.endCall({ - code: Status.UNAVAILABLE, - details: 'Connection dropped', - metadata: new Metadata(), + this.connectionDropped = true; + /* Give the call an event loop cycle to finish naturally before reporting + * the disconnection as an error. */ + setImmediate(() => { + this.endCall({ + code: Status.UNAVAILABLE, + details: 'Connection dropped', + metadata: new Metadata(), + }); }); } diff --git a/packages/grpc-js/src/transport.ts b/packages/grpc-js/src/transport.ts index 7a82fd3cc..807705c49 100644 --- a/packages/grpc-js/src/transport.ts +++ b/packages/grpc-js/src/transport.ts @@ -387,17 +387,13 @@ class Http2Transport implements Transport { * Handle connection drops, but not GOAWAYs. */ private handleDisconnect() { - if (this.disconnectHandled) { - return; - } this.clearKeepaliveTimeout(); this.reportDisconnectToOwner(false); - /* Give calls an event loop cycle to finish naturally before reporting the - * disconnnection to them. */ + for (const call of this.activeCalls) { + call.onDisconnect(); + } + // Wait an event loop cycle before destroying the connection setImmediate(() => { - for (const call of this.activeCalls) { - call.onDisconnect(); - } this.session.destroy(); }); }