mirror of https://github.com/grpc/grpc-node.git
Merge pull request #1705 from murgatroid99/grpc-js_econnreset_unavailable
grpc-js: Speculative fix for ECONNRESET errors
This commit is contained in:
commit
6f0869234c
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@grpc/grpc-js",
|
"name": "@grpc/grpc-js",
|
||||||
"version": "1.2.8",
|
"version": "1.2.9",
|
||||||
"description": "gRPC Library for Node - pure JS implementation",
|
"description": "gRPC Library for Node - pure JS implementation",
|
||||||
"homepage": "https://grpc.io/",
|
"homepage": "https://grpc.io/",
|
||||||
"repository": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js",
|
"repository": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js",
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as http2 from 'http2';
|
import * as http2 from 'http2';
|
||||||
|
import * as os from 'os';
|
||||||
|
|
||||||
import { CallCredentials } from './call-credentials';
|
import { CallCredentials } from './call-credentials';
|
||||||
import { Propagate, Status } from './constants';
|
import { Propagate, Status } from './constants';
|
||||||
|
|
@ -37,8 +38,34 @@ const {
|
||||||
NGHTTP2_CANCEL,
|
NGHTTP2_CANCEL,
|
||||||
} = http2.constants;
|
} = http2.constants;
|
||||||
|
|
||||||
interface NodeError extends Error {
|
/**
|
||||||
|
* https://nodejs.org/api/errors.html#errors_class_systemerror
|
||||||
|
*/
|
||||||
|
interface SystemError extends Error {
|
||||||
|
address?: string;
|
||||||
code: string;
|
code: string;
|
||||||
|
dest?: string;
|
||||||
|
errno: number;
|
||||||
|
info?: object;
|
||||||
|
message: string;
|
||||||
|
path?: string;
|
||||||
|
port?: number;
|
||||||
|
syscall: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should do approximately the same thing as util.getSystemErrorName but the
|
||||||
|
* TypeScript types don't have that function for some reason so I just made my
|
||||||
|
* own.
|
||||||
|
* @param errno
|
||||||
|
*/
|
||||||
|
function getSystemErrorName(errno: number): string {
|
||||||
|
for (const [name, num] of Object.entries(os.constants.errno)) {
|
||||||
|
if (num === errno) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 'Unknown system error ' + errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Deadline = Date | number;
|
export type Deadline = Date | number;
|
||||||
|
|
@ -206,7 +233,7 @@ export class Http2CallStream implements Call {
|
||||||
|
|
||||||
private listener: InterceptingListener | null = null;
|
private listener: InterceptingListener | null = null;
|
||||||
|
|
||||||
private internalErrorMessage: string | null = null;
|
private internalError: SystemError | null = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly methodName: string,
|
private readonly methodName: string,
|
||||||
|
|
@ -567,19 +594,24 @@ export class Http2CallStream implements Call {
|
||||||
break;
|
break;
|
||||||
case http2.constants.NGHTTP2_INTERNAL_ERROR:
|
case http2.constants.NGHTTP2_INTERNAL_ERROR:
|
||||||
code = Status.INTERNAL;
|
code = Status.INTERNAL;
|
||||||
if (this.internalErrorMessage === null) {
|
if (this.internalError === null) {
|
||||||
/* This error code was previously handled in the default case, and
|
/* This error code was previously handled in the default case, and
|
||||||
* there are several instances of it online, so I wanted to
|
* there are several instances of it online, so I wanted to
|
||||||
* preserve the original error message so that people find existing
|
* preserve the original error message so that people find existing
|
||||||
* information in searches, but also include the more recognizable
|
* information in searches, but also include the more recognizable
|
||||||
* "Internal server error" message. */
|
* "Internal server error" message. */
|
||||||
details = `Received RST_STREAM with code ${stream.rstCode} (Internal server error)`;
|
details = `Received RST_STREAM with code ${stream.rstCode} (Internal server error)`;
|
||||||
|
} else {
|
||||||
|
if (this.internalError.errno === os.constants.errno.ECONNRESET) {
|
||||||
|
code = Status.UNAVAILABLE;
|
||||||
|
details = this.internalError.message;
|
||||||
} else {
|
} else {
|
||||||
/* The "Received RST_STREAM with code ..." error is preserved
|
/* The "Received RST_STREAM with code ..." error is preserved
|
||||||
* here for continuity with errors reported online, but the
|
* here for continuity with errors reported online, but the
|
||||||
* error message at the end will probably be more relevant in
|
* error message at the end will probably be more relevant in
|
||||||
* most cases. */
|
* most cases. */
|
||||||
details = `Received RST_STREAM with code ${stream.rstCode} triggered by internal client error: ${this.internalErrorMessage}`;
|
details = `Received RST_STREAM with code ${stream.rstCode} triggered by internal client error: ${this.internalError.message}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
@ -593,7 +625,7 @@ export class Http2CallStream implements Call {
|
||||||
this.endCall({ code, details, metadata: new Metadata() });
|
this.endCall({ code, details, metadata: new Metadata() });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
stream.on('error', (err: NodeError) => {
|
stream.on('error', (err: SystemError) => {
|
||||||
/* We need an error handler here to stop "Uncaught Error" exceptions
|
/* We need an error handler here to stop "Uncaught Error" exceptions
|
||||||
* from bubbling up. However, errors here should all correspond to
|
* from bubbling up. However, errors here should all correspond to
|
||||||
* "close" events, where we will handle the error more granularly */
|
* "close" events, where we will handle the error more granularly */
|
||||||
|
|
@ -602,7 +634,8 @@ export class Http2CallStream implements Call {
|
||||||
* https://github.com/nodejs/node/blob/8b8620d580314050175983402dfddf2674e8e22a/lib/internal/http2/core.js#L2267
|
* https://github.com/nodejs/node/blob/8b8620d580314050175983402dfddf2674e8e22a/lib/internal/http2/core.js#L2267
|
||||||
*/
|
*/
|
||||||
if (err.code !== 'ERR_HTTP2_STREAM_ERROR') {
|
if (err.code !== 'ERR_HTTP2_STREAM_ERROR') {
|
||||||
this.internalErrorMessage = err.message;
|
this.trace('Node error event: message=' + err.message + ' code=' + err.code + ' errno=' + getSystemErrorName(err.errno) + ' syscall=' + err.syscall);
|
||||||
|
this.internalError = err;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!this.pendingRead) {
|
if (!this.pendingRead) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue