@@ -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+
85118export 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