mirror of https://github.com/grpc/grpc-node.git
Make some filter types synchronous
This commit is contained in:
parent
97ed462a00
commit
caa07ef883
|
|
@ -103,13 +103,6 @@ export class Http2CallStream extends Duplex implements Call {
|
|||
// Status code mapped from :status. To be used if grpc-status is not received
|
||||
private mappedStatusCode: Status = Status.UNKNOWN;
|
||||
|
||||
// Promise objects that are re-assigned to resolving promises when headers
|
||||
// or trailers received. Processing headers/trailers is asynchronous, so we
|
||||
// can use these objects to await their completion. This helps us establish
|
||||
// order of precedence when obtaining the status of the call.
|
||||
private handlingHeaders = Promise.resolve();
|
||||
private handlingTrailers = Promise.resolve();
|
||||
|
||||
// This is populated (non-null) if and only if the call has ended
|
||||
private finalStatus: StatusObject | null = null;
|
||||
|
||||
|
|
@ -224,30 +217,21 @@ export class Http2CallStream extends Duplex implements Call {
|
|||
metadata = new Metadata();
|
||||
}
|
||||
const status: StatusObject = { code, details, metadata };
|
||||
this.handlingTrailers = (async () => {
|
||||
let finalStatus;
|
||||
try {
|
||||
// Attempt to assign final status.
|
||||
finalStatus = await this.filterStack.receiveTrailers(
|
||||
Promise.resolve(status)
|
||||
);
|
||||
} catch (error) {
|
||||
await this.handlingHeaders;
|
||||
// This is a no-op if the call was already ended when handling headers.
|
||||
this.endCall({
|
||||
code: Status.INTERNAL,
|
||||
details: 'Failed to process received status',
|
||||
metadata: new Metadata(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
// It's possible that headers were received but not fully handled yet.
|
||||
// Give the headers handler an opportunity to end the call first,
|
||||
// if an error occurred.
|
||||
await this.handlingHeaders;
|
||||
let finalStatus;
|
||||
try {
|
||||
// Attempt to assign final status.
|
||||
finalStatus = this.filterStack.receiveTrailers(status);
|
||||
} catch (error) {
|
||||
// This is a no-op if the call was already ended when handling headers.
|
||||
this.endCall(finalStatus);
|
||||
})();
|
||||
this.endCall({
|
||||
code: Status.INTERNAL,
|
||||
details: 'Failed to process received status',
|
||||
metadata: new Metadata(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
// This is a no-op if the call was already ended when handling headers.
|
||||
this.endCall(finalStatus);
|
||||
}
|
||||
|
||||
attachHttp2Stream(stream: http2.ClientHttp2Stream, subchannel: Subchannel): void {
|
||||
|
|
@ -297,19 +281,17 @@ export class Http2CallStream extends Duplex implements Call {
|
|||
});
|
||||
return;
|
||||
}
|
||||
this.handlingHeaders = this.filterStack
|
||||
.receiveMetadata(Promise.resolve(metadata))
|
||||
.then(finalMetadata => {
|
||||
this.emit('metadata', finalMetadata);
|
||||
})
|
||||
.catch(error => {
|
||||
this.destroyHttp2Stream();
|
||||
this.endCall({
|
||||
code: Status.UNKNOWN,
|
||||
details: error.message,
|
||||
metadata: new Metadata(),
|
||||
});
|
||||
try {
|
||||
const finalMetadata = this.filterStack.receiveMetadata(metadata);
|
||||
this.emit('metadata', finalMetadata);
|
||||
} catch (error) {
|
||||
this.destroyHttp2Stream();
|
||||
this.endCall({
|
||||
code: Status.UNKNOWN,
|
||||
details: error.message,
|
||||
metadata: new Metadata(),
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
stream.on('trailers', this.handleTrailers.bind(this));
|
||||
|
|
@ -346,9 +328,6 @@ export class Http2CallStream extends Duplex implements Call {
|
|||
default:
|
||||
code = Status.INTERNAL;
|
||||
}
|
||||
// This guarantees that if trailers were received, the value of the
|
||||
// 'grpc-status' header takes precedence for emitted status data.
|
||||
await this.handlingTrailers;
|
||||
// This is a no-op if trailers were received at all.
|
||||
// This is OK, because status codes emitted here correspond to more
|
||||
// catastrophic issues that prevent us from receiving trailers in the
|
||||
|
|
@ -392,9 +371,6 @@ export class Http2CallStream extends Duplex implements Call {
|
|||
cancelWithStatus(status: Status, details: string): void {
|
||||
this.destroyHttp2Stream();
|
||||
(async () => {
|
||||
// If trailers are currently being processed, the call should be ended
|
||||
// by handleTrailers instead.
|
||||
await this.handlingTrailers;
|
||||
this.endCall({ code: status, details, metadata: new Metadata() });
|
||||
})();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -176,18 +176,17 @@ export class CompressionFilter extends BaseFilter implements Filter {
|
|||
return headers;
|
||||
}
|
||||
|
||||
async receiveMetadata(metadata: Promise<Metadata>): Promise<Metadata> {
|
||||
const headers: Metadata = await metadata;
|
||||
const receiveEncoding: MetadataValue[] = headers.get('grpc-encoding');
|
||||
receiveMetadata(metadata: Metadata): Metadata {
|
||||
const receiveEncoding: MetadataValue[] = metadata.get('grpc-encoding');
|
||||
if (receiveEncoding.length > 0) {
|
||||
const encoding: MetadataValue = receiveEncoding[0];
|
||||
if (typeof encoding === 'string') {
|
||||
this.receiveCompression = getCompressionHandler(encoding);
|
||||
}
|
||||
}
|
||||
headers.remove('grpc-encoding');
|
||||
headers.remove('grpc-accept-encoding');
|
||||
return headers;
|
||||
metadata.remove('grpc-encoding');
|
||||
metadata.remove('grpc-accept-encoding');
|
||||
return metadata;
|
||||
}
|
||||
|
||||
async sendMessage(message: Promise<WriteObject>): Promise<WriteObject> {
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ export class FilterStack implements Filter {
|
|||
return result;
|
||||
}
|
||||
|
||||
receiveMetadata(metadata: Promise<Metadata>) {
|
||||
let result: Promise<Metadata> = metadata;
|
||||
receiveMetadata(metadata: Metadata) {
|
||||
let result: Metadata = metadata;
|
||||
|
||||
for (let i = this.filters.length - 1; i >= 0; i--) {
|
||||
result = this.filters[i].receiveMetadata(result);
|
||||
|
|
@ -62,8 +62,8 @@ export class FilterStack implements Filter {
|
|||
return result;
|
||||
}
|
||||
|
||||
receiveTrailers(status: Promise<StatusObject>): Promise<StatusObject> {
|
||||
let result: Promise<StatusObject> = status;
|
||||
receiveTrailers(status: StatusObject): StatusObject {
|
||||
let result: StatusObject = status;
|
||||
|
||||
for (let i = this.filters.length - 1; i >= 0; i--) {
|
||||
result = this.filters[i].receiveTrailers(result);
|
||||
|
|
|
|||
|
|
@ -25,21 +25,21 @@ import { Metadata } from './metadata';
|
|||
export interface Filter {
|
||||
sendMetadata(metadata: Promise<Metadata>): Promise<Metadata>;
|
||||
|
||||
receiveMetadata(metadata: Promise<Metadata>): Promise<Metadata>;
|
||||
receiveMetadata(metadata: Metadata): Metadata;
|
||||
|
||||
sendMessage(message: Promise<WriteObject>): Promise<WriteObject>;
|
||||
|
||||
receiveMessage(message: Promise<Buffer>): Promise<Buffer>;
|
||||
|
||||
receiveTrailers(status: Promise<StatusObject>): Promise<StatusObject>;
|
||||
receiveTrailers(status: StatusObject): StatusObject;
|
||||
}
|
||||
|
||||
export abstract class BaseFilter {
|
||||
export abstract class BaseFilter implements Filter {
|
||||
async sendMetadata(metadata: Promise<Metadata>): Promise<Metadata> {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
async receiveMetadata(metadata: Promise<Metadata>): Promise<Metadata> {
|
||||
receiveMetadata(metadata: Metadata): Metadata {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
|
|
@ -51,7 +51,7 @@ export abstract class BaseFilter {
|
|||
return message;
|
||||
}
|
||||
|
||||
async receiveTrailers(status: Promise<StatusObject>): Promise<StatusObject> {
|
||||
receiveTrailers(status: StatusObject): StatusObject {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ import { Status } from './constants';
|
|||
import { BaseFilter, Filter, FilterFactory } from './filter';
|
||||
|
||||
export class MetadataStatusFilter extends BaseFilter implements Filter {
|
||||
async receiveTrailers(status: Promise<StatusObject>): Promise<StatusObject> {
|
||||
receiveTrailers(status: StatusObject): StatusObject {
|
||||
// tslint:disable-next-line:prefer-const
|
||||
let { code, details, metadata } = await status;
|
||||
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 };
|
||||
|
|
|
|||
Loading…
Reference in New Issue