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
|
// Status code mapped from :status. To be used if grpc-status is not received
|
||||||
private mappedStatusCode: Status = Status.UNKNOWN;
|
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
|
// This is populated (non-null) if and only if the call has ended
|
||||||
private finalStatus: StatusObject | null = null;
|
private finalStatus: StatusObject | null = null;
|
||||||
|
|
||||||
|
|
@ -224,15 +217,11 @@ export class Http2CallStream extends Duplex implements Call {
|
||||||
metadata = new Metadata();
|
metadata = new Metadata();
|
||||||
}
|
}
|
||||||
const status: StatusObject = { code, details, metadata };
|
const status: StatusObject = { code, details, metadata };
|
||||||
this.handlingTrailers = (async () => {
|
|
||||||
let finalStatus;
|
let finalStatus;
|
||||||
try {
|
try {
|
||||||
// Attempt to assign final status.
|
// Attempt to assign final status.
|
||||||
finalStatus = await this.filterStack.receiveTrailers(
|
finalStatus = this.filterStack.receiveTrailers(status);
|
||||||
Promise.resolve(status)
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await this.handlingHeaders;
|
|
||||||
// This is a no-op if the call was already ended when handling headers.
|
// This is a no-op if the call was already ended when handling headers.
|
||||||
this.endCall({
|
this.endCall({
|
||||||
code: Status.INTERNAL,
|
code: Status.INTERNAL,
|
||||||
|
|
@ -241,13 +230,8 @@ export class Http2CallStream extends Duplex implements Call {
|
||||||
});
|
});
|
||||||
return;
|
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;
|
|
||||||
// This is a no-op if the call was already ended when handling headers.
|
// This is a no-op if the call was already ended when handling headers.
|
||||||
this.endCall(finalStatus);
|
this.endCall(finalStatus);
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attachHttp2Stream(stream: http2.ClientHttp2Stream, subchannel: Subchannel): void {
|
attachHttp2Stream(stream: http2.ClientHttp2Stream, subchannel: Subchannel): void {
|
||||||
|
|
@ -297,19 +281,17 @@ export class Http2CallStream extends Duplex implements Call {
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.handlingHeaders = this.filterStack
|
try {
|
||||||
.receiveMetadata(Promise.resolve(metadata))
|
const finalMetadata = this.filterStack.receiveMetadata(metadata);
|
||||||
.then(finalMetadata => {
|
|
||||||
this.emit('metadata', finalMetadata);
|
this.emit('metadata', finalMetadata);
|
||||||
})
|
} catch (error) {
|
||||||
.catch(error => {
|
|
||||||
this.destroyHttp2Stream();
|
this.destroyHttp2Stream();
|
||||||
this.endCall({
|
this.endCall({
|
||||||
code: Status.UNKNOWN,
|
code: Status.UNKNOWN,
|
||||||
details: error.message,
|
details: error.message,
|
||||||
metadata: new Metadata(),
|
metadata: new Metadata(),
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
stream.on('trailers', this.handleTrailers.bind(this));
|
stream.on('trailers', this.handleTrailers.bind(this));
|
||||||
|
|
@ -346,9 +328,6 @@ export class Http2CallStream extends Duplex implements Call {
|
||||||
default:
|
default:
|
||||||
code = Status.INTERNAL;
|
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 a no-op if trailers were received at all.
|
||||||
// This is OK, because status codes emitted here correspond to more
|
// This is OK, because status codes emitted here correspond to more
|
||||||
// catastrophic issues that prevent us from receiving trailers in the
|
// 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 {
|
cancelWithStatus(status: Status, details: string): void {
|
||||||
this.destroyHttp2Stream();
|
this.destroyHttp2Stream();
|
||||||
(async () => {
|
(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() });
|
this.endCall({ code: status, details, metadata: new Metadata() });
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -176,18 +176,17 @@ export class CompressionFilter extends BaseFilter implements Filter {
|
||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
async receiveMetadata(metadata: Promise<Metadata>): Promise<Metadata> {
|
receiveMetadata(metadata: Metadata): Metadata {
|
||||||
const headers: Metadata = await metadata;
|
const receiveEncoding: MetadataValue[] = metadata.get('grpc-encoding');
|
||||||
const receiveEncoding: MetadataValue[] = headers.get('grpc-encoding');
|
|
||||||
if (receiveEncoding.length > 0) {
|
if (receiveEncoding.length > 0) {
|
||||||
const encoding: MetadataValue = receiveEncoding[0];
|
const encoding: MetadataValue = receiveEncoding[0];
|
||||||
if (typeof encoding === 'string') {
|
if (typeof encoding === 'string') {
|
||||||
this.receiveCompression = getCompressionHandler(encoding);
|
this.receiveCompression = getCompressionHandler(encoding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
headers.remove('grpc-encoding');
|
metadata.remove('grpc-encoding');
|
||||||
headers.remove('grpc-accept-encoding');
|
metadata.remove('grpc-accept-encoding');
|
||||||
return headers;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendMessage(message: Promise<WriteObject>): Promise<WriteObject> {
|
async sendMessage(message: Promise<WriteObject>): Promise<WriteObject> {
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,8 @@ export class FilterStack implements Filter {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
receiveMetadata(metadata: Promise<Metadata>) {
|
receiveMetadata(metadata: Metadata) {
|
||||||
let result: Promise<Metadata> = metadata;
|
let result: Metadata = metadata;
|
||||||
|
|
||||||
for (let i = this.filters.length - 1; i >= 0; i--) {
|
for (let i = this.filters.length - 1; i >= 0; i--) {
|
||||||
result = this.filters[i].receiveMetadata(result);
|
result = this.filters[i].receiveMetadata(result);
|
||||||
|
|
@ -62,8 +62,8 @@ export class FilterStack implements Filter {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
receiveTrailers(status: Promise<StatusObject>): Promise<StatusObject> {
|
receiveTrailers(status: StatusObject): StatusObject {
|
||||||
let result: Promise<StatusObject> = status;
|
let result: StatusObject = status;
|
||||||
|
|
||||||
for (let i = this.filters.length - 1; i >= 0; i--) {
|
for (let i = this.filters.length - 1; i >= 0; i--) {
|
||||||
result = this.filters[i].receiveTrailers(result);
|
result = this.filters[i].receiveTrailers(result);
|
||||||
|
|
|
||||||
|
|
@ -25,21 +25,21 @@ import { Metadata } from './metadata';
|
||||||
export interface Filter {
|
export interface Filter {
|
||||||
sendMetadata(metadata: Promise<Metadata>): Promise<Metadata>;
|
sendMetadata(metadata: Promise<Metadata>): Promise<Metadata>;
|
||||||
|
|
||||||
receiveMetadata(metadata: Promise<Metadata>): Promise<Metadata>;
|
receiveMetadata(metadata: Metadata): Metadata;
|
||||||
|
|
||||||
sendMessage(message: Promise<WriteObject>): Promise<WriteObject>;
|
sendMessage(message: Promise<WriteObject>): Promise<WriteObject>;
|
||||||
|
|
||||||
receiveMessage(message: Promise<Buffer>): Promise<Buffer>;
|
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> {
|
async sendMetadata(metadata: Promise<Metadata>): Promise<Metadata> {
|
||||||
return metadata;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
async receiveMetadata(metadata: Promise<Metadata>): Promise<Metadata> {
|
receiveMetadata(metadata: Metadata): Metadata {
|
||||||
return metadata;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,7 +51,7 @@ export abstract class BaseFilter {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
async receiveTrailers(status: Promise<StatusObject>): Promise<StatusObject> {
|
receiveTrailers(status: StatusObject): StatusObject {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,9 @@ import { Status } from './constants';
|
||||||
import { BaseFilter, Filter, FilterFactory } from './filter';
|
import { BaseFilter, Filter, FilterFactory } from './filter';
|
||||||
|
|
||||||
export class MetadataStatusFilter extends BaseFilter implements 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
|
// tslint:disable-next-line:prefer-const
|
||||||
let { code, details, metadata } = await status;
|
let { code, details, metadata } = status;
|
||||||
if (code !== Status.UNKNOWN) {
|
if (code !== Status.UNKNOWN) {
|
||||||
// we already have a known status, so don't assign a new one.
|
// we already have a known status, so don't assign a new one.
|
||||||
return { code, details, metadata };
|
return { code, details, metadata };
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue