Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions packages/grpc-js-core/src/call-credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {map, reduce} from 'lodash';
import {Metadata} from './metadata';

export type CallMetadataGenerator =
(options: Object, cb: (err: Error|null, metadata?: Metadata) => void) =>
(options: {}, cb: (err: Error|null, metadata?: Metadata) => void) =>
void;

/**
Expand All @@ -15,7 +15,7 @@ export interface CallCredentials {
* Asynchronously generates a new Metadata object.
* @param options Options used in generating the Metadata object.
*/
generateMetadata(options: Object): Promise<Metadata>;
generateMetadata(options: {}): Promise<Metadata>;
/**
* Creates a new CallCredentials object from properties of both this and
* another CallCredentials object. This object's metadata generator will be
Expand All @@ -28,7 +28,7 @@ export interface CallCredentials {
class ComposedCallCredentials implements CallCredentials {
constructor(private creds: CallCredentials[]) {}

async generateMetadata(options: Object): Promise<Metadata> {
async generateMetadata(options: {}): Promise<Metadata> {
let base: Metadata = new Metadata();
let generated: Metadata[] = await Promise.all(
map(this.creds, (cred) => cred.generateMetadata(options)));
Expand All @@ -46,7 +46,7 @@ class ComposedCallCredentials implements CallCredentials {
class SingleCallCredentials implements CallCredentials {
constructor(private metadataGenerator: CallMetadataGenerator) {}

async generateMetadata(options: Object): Promise<Metadata> {
async generateMetadata(options: {}): Promise<Metadata> {
return new Promise<Metadata>((resolve, reject) => {
this.metadataGenerator(options, (err, metadata) => {
if (metadata !== undefined) {
Expand All @@ -64,7 +64,7 @@ class SingleCallCredentials implements CallCredentials {
}

class EmptyCallCredentials implements CallCredentials {
async generateMetadata(options: Object): Promise<Metadata> {
async generateMetadata(options: {}): Promise<Metadata> {
return new Metadata();
}

Expand Down
16 changes: 9 additions & 7 deletions packages/grpc-js-core/src/call-stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,10 @@ export class Http2CallStream extends Duplex implements CallStream {
this.cancelWithStatus(Status.UNKNOWN, error.message);
});
});
stream.on('trailers', (headers) => {
stream.on('trailers', (headers: http2.IncomingHttpHeaders) => {
let code: Status = this.mappedStatusCode;
if (headers.hasOwnProperty('grpc-status')) {
let details = '';
if (typeof headers['grpc-status'] === 'string') {
let receivedCode = Number(headers['grpc-status']);
if (receivedCode in Status) {
code = receivedCode;
Expand All @@ -193,9 +194,8 @@ export class Http2CallStream extends Duplex implements CallStream {
}
delete headers['grpc-status'];
}
let details = '';
if (headers.hasOwnProperty('grpc-message')) {
details = decodeURI(headers['grpc-message']);
if (typeof headers['grpc-message'] === 'string') {
details = decodeURI(headers['grpc-message'] as string);
}
let metadata: Metadata;
try {
Expand Down Expand Up @@ -301,7 +301,7 @@ export class Http2CallStream extends Duplex implements CallStream {
}
this.endCall({code: code, details: details, metadata: new Metadata()});
});
stream.on('error', () => {
stream.on('error', (err: Error) => {
this.endCall({
code: Status.INTERNAL,
details: 'Internal HTTP2 error',
Expand All @@ -325,7 +325,9 @@ export class Http2CallStream extends Duplex implements CallStream {

cancelWithStatus(status: Status, details: string): void {
this.endCall({code: status, details: details, metadata: new Metadata()});
if (this.http2Stream !== null) {
// The http2 stream could already have been destroyed if cancelWithStatus
// is called in response to an internal http2 error.
if (this.http2Stream !== null && !this.http2Stream.destroyed) {
/* TODO(murgatroid99): Determine if we want to send different RST_STREAM
* codes based on the status code */
this.http2Stream.rstWithCancel();
Expand Down
13 changes: 7 additions & 6 deletions packages/grpc-js-core/src/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export interface Channel extends EventEmitter {
}

export class Http2Channel extends EventEmitter implements Channel {
private readonly authority: url.URL;
private connectivityState: ConnectivityState = ConnectivityState.IDLE;
/* For now, we have up to one subchannel, which will exist as long as we are
* connecting or trying to connect */
Expand Down Expand Up @@ -134,9 +135,9 @@ export class Http2Channel extends EventEmitter implements Channel {
let subChannel: http2.ClientHttp2Session;
let secureContext = this.credentials.getSecureContext();
if (secureContext === null) {
subChannel = http2.connect(this.address);
subChannel = http2.connect(this.authority);
} else {
subChannel = http2.connect(this.address, {secureContext});
subChannel = http2.connect(this.authority, {secureContext});
}
this.subChannel = subChannel;
let now = new Date();
Expand Down Expand Up @@ -165,14 +166,14 @@ export class Http2Channel extends EventEmitter implements Channel {
}

constructor(
private readonly address: url.URL,
address: string,
public readonly credentials: ChannelCredentials,
private readonly options: ChannelOptions) {
super();
if (credentials.getSecureContext() === null) {
address.protocol = 'http';
this.authority = new url.URL(`http://${address}`);
} else {
address.protocol = 'https';
this.authority = new url.URL(`https://${address}`);
}
this.filterStackFactory = new FilterStackFactory([
new CompressionFilterFactory(this),
Expand All @@ -193,7 +194,7 @@ export class Http2Channel extends EventEmitter implements Channel {
finalMetadata.then(
(metadataValue) => {
let headers = metadataValue.toHttp2Headers();
headers[HTTP2_HEADER_AUTHORITY] = this.address.hostname;
headers[HTTP2_HEADER_AUTHORITY] = this.authority.hostname;
headers[HTTP2_HEADER_CONTENT_TYPE] = 'application/grpc';
headers[HTTP2_HEADER_METHOD] = 'POST';
headers[HTTP2_HEADER_PATH] = methodName;
Expand Down
2 changes: 1 addition & 1 deletion packages/grpc-js-core/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class Client {
}
// TODO(murgatroid99): Figure out how to get version number
// options['grpc.primary_user_agent'] += 'grpc-node/' + version;
this.channel = new Http2Channel(new URL(address), credentials, options);
this.channel = new Http2Channel(address, credentials, options);
}

close(): void {
Expand Down
5 changes: 3 additions & 2 deletions packages/grpc-js-core/src/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ export class Metadata {
* Creates an OutgoingHttpHeaders object that can be used with the http2 API.
*/
toHttp2Headers(): http2.OutgoingHttpHeaders {
// NOTE: Node <8.9 formats http2 headers incorrectly.
const result: http2.OutgoingHttpHeaders = {};
forOwn(this.internalRepr, (values, key) => {
// We assume that the user's interaction with this object is limited to
Expand Down Expand Up @@ -194,15 +195,15 @@ export class Metadata {
values.forEach((value) => {
result.add(key, Buffer.from(value, 'base64'));
});
} else {
} else if (values !== undefined) {
result.add(key, Buffer.from(values, 'base64'));
}
} else {
if (Array.isArray(values)) {
values.forEach((value) => {
result.add(key, value);
});
} else {
} else if (values !== undefined) {
result.add(key, values);
}
}
Expand Down