Skip to content

Commit 0bbfb92

Browse files
authored
Merge pull request #2723 from murgatroid99/grpc-js_http2_error_reporting
grpc-js: Improve reporting of HTTP error codes
2 parents db1c05e + 8754ccb commit 0bbfb92

File tree

2 files changed

+73
-50
lines changed

2 files changed

+73
-50
lines changed

packages/grpc-js/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@grpc/grpc-js",
3-
"version": "1.10.6",
3+
"version": "1.10.7",
44
"description": "gRPC Library for Node - pure JS implementation",
55
"homepage": "https://grpc.io/",
66
"repository": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js",

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

Lines changed: 72 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,39 @@ export interface SubchannelCallInterceptingListener
8282
onReceiveStatus(status: StatusObjectWithRstCode): void;
8383
}
8484

85+
function mapHttpStatusCode(code: number): StatusObject {
86+
const details = `Received HTTP status code ${code}`;
87+
let mappedStatusCode: number;
88+
switch (code) {
89+
// TODO(murgatroid99): handle 100 and 101
90+
case 400:
91+
mappedStatusCode = Status.INTERNAL;
92+
break;
93+
case 401:
94+
mappedStatusCode = Status.UNAUTHENTICATED;
95+
break;
96+
case 403:
97+
mappedStatusCode = Status.PERMISSION_DENIED;
98+
break;
99+
case 404:
100+
mappedStatusCode = Status.UNIMPLEMENTED;
101+
break;
102+
case 429:
103+
case 502:
104+
case 503:
105+
case 504:
106+
mappedStatusCode = Status.UNAVAILABLE;
107+
break;
108+
default:
109+
mappedStatusCode = Status.UNKNOWN;
110+
}
111+
return {
112+
code: mappedStatusCode,
113+
details: details,
114+
metadata: new Metadata()
115+
};
116+
}
117+
85118
export class Http2SubchannelCall implements SubchannelCall {
86119
private decoder = new StreamDecoder();
87120

@@ -98,8 +131,7 @@ export class Http2SubchannelCall implements SubchannelCall {
98131

99132
private unpushedReadMessages: Buffer[] = [];
100133

101-
// Status code mapped from :status. To be used if grpc-status is not received
102-
private mappedStatusCode: Status = Status.UNKNOWN;
134+
private httpStatusCode: number | undefined;
103135

104136
// This is populated (non-null) if and only if the call has ended
105137
private finalStatus: StatusObject | null = null;
@@ -121,29 +153,7 @@ export class Http2SubchannelCall implements SubchannelCall {
121153
headersString += '\t\t' + header + ': ' + headers[header] + '\n';
122154
}
123155
this.trace('Received server headers:\n' + headersString);
124-
switch (headers[':status']) {
125-
// TODO(murgatroid99): handle 100 and 101
126-
case 400:
127-
this.mappedStatusCode = Status.INTERNAL;
128-
break;
129-
case 401:
130-
this.mappedStatusCode = Status.UNAUTHENTICATED;
131-
break;
132-
case 403:
133-
this.mappedStatusCode = Status.PERMISSION_DENIED;
134-
break;
135-
case 404:
136-
this.mappedStatusCode = Status.UNIMPLEMENTED;
137-
break;
138-
case 429:
139-
case 502:
140-
case 503:
141-
case 504:
142-
this.mappedStatusCode = Status.UNAVAILABLE;
143-
break;
144-
default:
145-
this.mappedStatusCode = Status.UNKNOWN;
146-
}
156+
this.httpStatusCode = headers[':status'];
147157

148158
if (flags & http2.constants.NGHTTP2_FLAG_END_STREAM) {
149159
this.handleTrailers(headers);
@@ -208,8 +218,14 @@ export class Http2SubchannelCall implements SubchannelCall {
208218
if (this.finalStatus !== null) {
209219
return;
210220
}
211-
code = Status.INTERNAL;
212-
details = `Received RST_STREAM with code ${http2Stream.rstCode}`;
221+
if (this.httpStatusCode && this.httpStatusCode !== 200) {
222+
const mappedStatus = mapHttpStatusCode(this.httpStatusCode);
223+
code = mappedStatus.code;
224+
details = mappedStatus.details;
225+
} else {
226+
code = Status.INTERNAL;
227+
details = `Received RST_STREAM with code ${http2Stream.rstCode} (Call ended without gRPC status)`;
228+
}
213229
break;
214230
case http2.constants.NGHTTP2_REFUSED_STREAM:
215231
code = Status.UNAVAILABLE;
@@ -421,31 +437,38 @@ export class Http2SubchannelCall implements SubchannelCall {
421437
metadata = new Metadata();
422438
}
423439
const metadataMap = metadata.getMap();
424-
let code: Status = this.mappedStatusCode;
425-
if (
426-
code === Status.UNKNOWN &&
427-
typeof metadataMap['grpc-status'] === 'string'
428-
) {
429-
const receivedStatus = Number(metadataMap['grpc-status']);
430-
if (receivedStatus in Status) {
431-
code = receivedStatus;
432-
this.trace('received status code ' + receivedStatus + ' from server');
433-
}
440+
let status: StatusObject;
441+
if (typeof metadataMap['grpc-status'] === 'string') {
442+
const receivedStatus: Status = Number(metadataMap['grpc-status']);
443+
this.trace('received status code ' + receivedStatus + ' from server');
434444
metadata.remove('grpc-status');
435-
}
436-
let details = '';
437-
if (typeof metadataMap['grpc-message'] === 'string') {
438-
try {
439-
details = decodeURI(metadataMap['grpc-message']);
440-
} catch (e) {
441-
details = metadataMap['grpc-message'];
445+
let details = '';
446+
if (typeof metadataMap['grpc-message'] === 'string') {
447+
try {
448+
details = decodeURI(metadataMap['grpc-message']);
449+
} catch (e) {
450+
details = metadataMap['grpc-message'];
451+
}
452+
metadata.remove('grpc-message');
453+
this.trace(
454+
'received status details string "' + details + '" from server'
455+
);
442456
}
443-
metadata.remove('grpc-message');
444-
this.trace(
445-
'received status details string "' + details + '" from server'
446-
);
457+
status = {
458+
code: receivedStatus,
459+
details: details,
460+
metadata: metadata
461+
};
462+
} else if (this.httpStatusCode) {
463+
status = mapHttpStatusCode(this.httpStatusCode);
464+
status.metadata = metadata;
465+
} else {
466+
status = {
467+
code: Status.UNKNOWN,
468+
details: 'No status information received',
469+
metadata: metadata
470+
};
447471
}
448-
const status: StatusObject = { code, details, metadata };
449472
// This is a no-op if the call was already ended when handling headers.
450473
this.endCall(status);
451474
}

0 commit comments

Comments
 (0)