grpc-js: Make filter stack handle status in all code paths

This commit is contained in:
Michael Lumish 2020-03-19 09:42:17 -07:00
parent 2ca96a322f
commit a99afaf5eb
4 changed files with 24 additions and 60 deletions

View File

@ -224,7 +224,8 @@ export class Http2CallStream implements Call {
/* Precondition: this.finalStatus !== null */
if (!this.statusOutput) {
this.statusOutput = true;
this.listener!.onReceiveStatus(this.finalStatus!);
const filteredStatus = this.filterStack.receiveTrailers(this.finalStatus!);
this.listener!.onReceiveStatus(filteredStatus);
if (this.subchannel) {
this.subchannel.callUnref();
this.subchannel.removeDisconnectListener(this.disconnectListener);
@ -353,14 +354,26 @@ export class Http2CallStream implements Call {
private handleTrailers(headers: http2.IncomingHttpHeaders) {
this.trace('received HTTP/2 trailing headers frame');
const code: Status = this.mappedStatusCode;
const details = '';
let metadata: Metadata;
try {
metadata = Metadata.fromHttp2Headers(headers);
} catch (e) {
metadata = new Metadata();
}
const metadataMap = metadata.getMap();
let code: Status = this.mappedStatusCode;
if (code === Status.UNKNOWN && typeof metadataMap['grpc-status'] === 'string') {
const receivedStatus = Number(metadataMap['grpc-status']);
if (receivedStatus in Status) {
code = receivedStatus;
}
metadata.remove('grpc-status');
}
let details = '';
if (typeof metadataMap['grpc-message'] === 'string') {
details = decodeURI(metadataMap['grpc-message']);
metadata.remove('grpc-message');
}
const status: StatusObject = { code, details, metadata };
let finalStatus;
try {

View File

@ -32,7 +32,6 @@ import { Status, LogVerbosity } from './constants';
import { FilterStackFactory } from './filter-stack';
import { CallCredentialsFilterFactory } from './call-credentials-filter';
import { DeadlineFilterFactory } from './deadline-filter';
import { MetadataStatusFilterFactory } from './metadata-status-filter';
import { CompressionFilterFactory } from './compression-filter';
import { getDefaultAuthority } from './resolver';
import { LoadBalancingConfig } from './load-balancing-config';
@ -192,7 +191,6 @@ export class ChannelImplementation implements Channel {
this.filterStackFactory = new FilterStackFactory([
new CallCredentialsFilterFactory(this),
new DeadlineFilterFactory(this),
new MetadataStatusFilterFactory(this),
new CompressionFilterFactory(this),
]);
// TODO(murgatroid99): Add more centralized handling of channel options

View File

@ -15,7 +15,7 @@
*
*/
import { Call } from './call-stream';
import { Call, StatusObject } from './call-stream';
import { ConnectivityState, Channel } from './channel';
import { Status } from './constants';
import { BaseFilter, Filter, FilterFactory } from './filter';
@ -82,6 +82,13 @@ export class DeadlineFilter extends BaseFilter implements Filter {
finalMetadata.set('grpc-timeout', timeoutString);
return finalMetadata;
}
receiveTrailers(status: StatusObject) {
if (this.timer) {
clearTimeout(this.timer);
}
return status;
}
}
export class DeadlineFilterFactory implements FilterFactory<DeadlineFilter> {

View File

@ -1,54 +0,0 @@
/*
* Copyright 2019 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Call } from './call-stream';
import { StatusObject } from './call-stream';
import { Channel } from './channel';
import { Status } from './constants';
import { BaseFilter, Filter, FilterFactory } from './filter';
export class MetadataStatusFilter extends BaseFilter implements Filter {
receiveTrailers(status: StatusObject): StatusObject {
// tslint:disable-next-line:prefer-const
let { code, details, metadata } = status;
if (code !== Status.UNKNOWN) {
// we already have a known status, so don't assign a new one.
return { code, details, metadata };
}
const metadataMap = metadata.getMap();
if (typeof metadataMap['grpc-status'] === 'string') {
const receivedCode = Number(metadataMap['grpc-status']);
if (receivedCode in Status) {
code = receivedCode;
}
metadata.remove('grpc-status');
}
if (typeof metadataMap['grpc-message'] === 'string') {
details = decodeURI(metadataMap['grpc-message'] as string);
metadata.remove('grpc-message');
}
return { code, details, metadata };
}
}
export class MetadataStatusFilterFactory
implements FilterFactory<MetadataStatusFilter> {
constructor(private readonly channel: Channel) {}
createFilter(callStream: Call): MetadataStatusFilter {
return new MetadataStatusFilter();
}
}