@@ -82,6 +82,39 @@ export interface SubchannelCallInterceptingListener
82
82
onReceiveStatus ( status : StatusObjectWithRstCode ) : void ;
83
83
}
84
84
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
+
85
118
export class Http2SubchannelCall implements SubchannelCall {
86
119
private decoder = new StreamDecoder ( ) ;
87
120
@@ -98,8 +131,7 @@ export class Http2SubchannelCall implements SubchannelCall {
98
131
99
132
private unpushedReadMessages : Buffer [ ] = [ ] ;
100
133
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 ;
103
135
104
136
// This is populated (non-null) if and only if the call has ended
105
137
private finalStatus : StatusObject | null = null ;
@@ -121,29 +153,7 @@ export class Http2SubchannelCall implements SubchannelCall {
121
153
headersString += '\t\t' + header + ': ' + headers [ header ] + '\n' ;
122
154
}
123
155
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' ] ;
147
157
148
158
if ( flags & http2 . constants . NGHTTP2_FLAG_END_STREAM ) {
149
159
this . handleTrailers ( headers ) ;
@@ -208,8 +218,14 @@ export class Http2SubchannelCall implements SubchannelCall {
208
218
if ( this . finalStatus !== null ) {
209
219
return ;
210
220
}
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
+ }
213
229
break ;
214
230
case http2 . constants . NGHTTP2_REFUSED_STREAM :
215
231
code = Status . UNAVAILABLE ;
@@ -421,31 +437,38 @@ export class Http2SubchannelCall implements SubchannelCall {
421
437
metadata = new Metadata ( ) ;
422
438
}
423
439
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' ) ;
434
444
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
+ ) ;
442
456
}
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
+ } ;
447
471
}
448
- const status : StatusObject = { code, details, metadata } ;
449
472
// This is a no-op if the call was already ended when handling headers.
450
473
this . endCall ( status ) ;
451
474
}
0 commit comments