Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Node http2 gRPC transport #181

Closed
wants to merge 4 commits into from
Closed
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
8 changes: 5 additions & 3 deletions src/controllers/ExtensionController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
type AuthenticatedClient as DheAuthenticatedClient,
type UnauthenticatedClient as DheUnauthenticatedClient,
} from '@deephaven-enterprise/auth-nodejs';
import { NodeHttp2gRPCTransport } from '../dh/NodeHttp2gRPCTransport';

const logger = new Logger('ExtensionController');

Expand Down Expand Up @@ -333,9 +334,10 @@
assertDefined(this._coreJsApiCache, 'coreJsApiCache');
const dhc = await this._coreJsApiCache.get(url);

const client = new dhc.CoreClient(
url.toString()
) as CoreUnauthenticatedClient;
const client = new dhc.CoreClient(url.toString(), {
debug: true,

Check failure on line 338 in src/controllers/ExtensionController.ts

View workflow job for this annotation

GitHub Actions / call-unit / unit

Object literal may only specify known properties, and 'debug' does not exist in type 'ConnectOptions'.

Check failure on line 338 in src/controllers/ExtensionController.ts

View workflow job for this annotation

GitHub Actions / call-e2e / e2e

Object literal may only specify known properties, and 'debug' does not exist in type 'ConnectOptions'.
transportFactory: NodeHttp2gRPCTransport.factory,
}) as CoreUnauthenticatedClient;

Check failure on line 340 in src/controllers/ExtensionController.ts

View workflow job for this annotation

GitHub Actions / call-e2e / e2e

Object literal may only specify known properties, and 'debug' does not exist in type 'ConnectOptions'.

Check failure on line 340 in src/controllers/ExtensionController.ts

View workflow job for this annotation

GitHub Actions / call-unit / unit

Object literal may only specify known properties, and 'debug' does not exist in type 'ConnectOptions'.

// Attach a dispose method so that client caches can dispose of the client
return Object.assign(client, {
Expand Down
139 changes: 139 additions & 0 deletions src/dh/NodeHttp2gRPCTransport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import http2 from 'node:http2';
import type { dh as DhcType } from '@deephaven/jsapi-types';
import { assertDefined } from '../util';

type GrpcTransport = DhcType.grpc.GrpcTransport;

Check failure on line 5 in src/dh/NodeHttp2gRPCTransport.ts

View workflow job for this annotation

GitHub Actions / call-unit / unit

Namespace '"/home/runner/work/vscode-deephaven/vscode-deephaven/node_modules/@deephaven/jsapi-types/dist/index".dh' has no exported member 'grpc'.

Check failure on line 5 in src/dh/NodeHttp2gRPCTransport.ts

View workflow job for this annotation

GitHub Actions / call-e2e / e2e

Namespace '"/home/runner/work/vscode-deephaven/vscode-deephaven/node_modules/@deephaven/jsapi-types/dist/index".dh' has no exported member 'grpc'.

Check failure on line 5 in src/dh/NodeHttp2gRPCTransport.ts

View workflow job for this annotation

GitHub Actions / call-e2e / e2e

Namespace '"/home/runner/work/vscode-deephaven/vscode-deephaven/node_modules/@deephaven/jsapi-types/dist/index".dh' has no exported member 'grpc'.

Check failure on line 5 in src/dh/NodeHttp2gRPCTransport.ts

View workflow job for this annotation

GitHub Actions / call-unit / unit

Namespace '"/home/runner/work/vscode-deephaven/vscode-deephaven/node_modules/@deephaven/jsapi-types/dist/index".dh' has no exported member 'grpc'.
type GrpcTransportFactory = DhcType.grpc.GrpcTransportFactory;

Check failure on line 6 in src/dh/NodeHttp2gRPCTransport.ts

View workflow job for this annotation

GitHub Actions / call-unit / unit

Namespace '"/home/runner/work/vscode-deephaven/vscode-deephaven/node_modules/@deephaven/jsapi-types/dist/index".dh' has no exported member 'grpc'.

Check failure on line 6 in src/dh/NodeHttp2gRPCTransport.ts

View workflow job for this annotation

GitHub Actions / call-e2e / e2e

Namespace '"/home/runner/work/vscode-deephaven/vscode-deephaven/node_modules/@deephaven/jsapi-types/dist/index".dh' has no exported member 'grpc'.

Check failure on line 6 in src/dh/NodeHttp2gRPCTransport.ts

View workflow job for this annotation

GitHub Actions / call-e2e / e2e

Namespace '"/home/runner/work/vscode-deephaven/vscode-deephaven/node_modules/@deephaven/jsapi-types/dist/index".dh' has no exported member 'grpc'.

Check failure on line 6 in src/dh/NodeHttp2gRPCTransport.ts

View workflow job for this annotation

GitHub Actions / call-unit / unit

Namespace '"/home/runner/work/vscode-deephaven/vscode-deephaven/node_modules/@deephaven/jsapi-types/dist/index".dh' has no exported member 'grpc'.
type GrpcTransportOptions = DhcType.grpc.GrpcTransportOptions;

Check failure on line 7 in src/dh/NodeHttp2gRPCTransport.ts

View workflow job for this annotation

GitHub Actions / call-unit / unit

Namespace '"/home/runner/work/vscode-deephaven/vscode-deephaven/node_modules/@deephaven/jsapi-types/dist/index".dh' has no exported member 'grpc'.

Check failure on line 7 in src/dh/NodeHttp2gRPCTransport.ts

View workflow job for this annotation

GitHub Actions / call-e2e / e2e

Namespace '"/home/runner/work/vscode-deephaven/vscode-deephaven/node_modules/@deephaven/jsapi-types/dist/index".dh' has no exported member 'grpc'.

Check failure on line 7 in src/dh/NodeHttp2gRPCTransport.ts

View workflow job for this annotation

GitHub Actions / call-e2e / e2e

Namespace '"/home/runner/work/vscode-deephaven/vscode-deephaven/node_modules/@deephaven/jsapi-types/dist/index".dh' has no exported member 'grpc'.

Check failure on line 7 in src/dh/NodeHttp2gRPCTransport.ts

View workflow job for this annotation

GitHub Actions / call-unit / unit

Namespace '"/home/runner/work/vscode-deephaven/vscode-deephaven/node_modules/@deephaven/jsapi-types/dist/index".dh' has no exported member 'grpc'.

export class NodeHttp2gRPCTransport implements GrpcTransport {
static _sessionMap: Map<string, http2.ClientHttp2Session> = new Map();

/**
* TODO: Cleanup requests similar to https://github.com/deephaven/deephaven-core/blob/c05b35957e466fded4da61154ba106cfc3198bc5/web/client-api/src/main/java/io/deephaven/web/client/api/grpc/MultiplexedWebsocketTransport.java#L129
* Create a Transport instance.
* @param options Transport options.
* @returns Transport instance.
*/
static readonly factory: GrpcTransportFactory = {
create: options => {

Check failure on line 19 in src/dh/NodeHttp2gRPCTransport.ts

View workflow job for this annotation

GitHub Actions / call-unit / unit

Parameter 'options' implicitly has an 'any' type.

Check failure on line 19 in src/dh/NodeHttp2gRPCTransport.ts

View workflow job for this annotation

GitHub Actions / call-e2e / e2e

Parameter 'options' implicitly has an 'any' type.

Check failure on line 19 in src/dh/NodeHttp2gRPCTransport.ts

View workflow job for this annotation

GitHub Actions / call-e2e / e2e

Parameter 'options' implicitly has an 'any' type.

Check failure on line 19 in src/dh/NodeHttp2gRPCTransport.ts

View workflow job for this annotation

GitHub Actions / call-unit / unit

Parameter 'options' implicitly has an 'any' type.
const { origin } = new URL(options.url);

if (!NodeHttp2gRPCTransport._sessionMap.has(origin)) {
const session = http2.connect(origin);
session.on('error', err => {
console.error('Session error', err);
});
NodeHttp2gRPCTransport._sessionMap.set(origin, session);
}

const session = NodeHttp2gRPCTransport._sessionMap.get(origin)!;

return new NodeHttp2gRPCTransport(options, session);
},

get supportsClientStreaming(): boolean {
return false;
},
};

/**
* Private constructor to restrict instantiation to static factory method.
* @param options Transport options.
* @param session node:http2 session.
*/
private constructor(
options: GrpcTransportOptions,
session: http2.ClientHttp2Session
) {
this._options = options;
this._session = session;
}

private readonly _options: GrpcTransportOptions;
private readonly _session: http2.ClientHttp2Session;
private _request: http2.ClientHttp2Stream | null = null;

_createRequest = (
headers: Record<string, string> | null
): http2.ClientHttp2Stream => {
const url = new URL(this._options.url);

const req = this._session.request({
...headers,
// may need to set the :authority header at some point
':method': 'POST',
':path': url.pathname,
});

console.log('[NodeHttp2Transport] _createRequest', url.pathname);

req.on('response', (headers, _flags) => {
const headersRecord: Record<string, string | string[]> = {};

// strip any undefined headers or keys that start with `:`
for (const name in headers) {
if (headers[name] != null && !name.startsWith(':')) {
headersRecord[name] = headers[name];
}
}

this._options.onHeaders(headersRecord, Number(headers[':status']));
});

req.on('data', (chunk: Buffer) => {
this._options.onChunk(chunk);
});
req.on('end', () => {
this._options.onEnd();
});
req.on('error', err => {
this._options.onEnd(err);
});

return req;
};

start(metadata: { [key: string]: string | Array<string> }): void {
console.log('[NodeHttp2Transport] start', metadata.headersMap);

if (this._request != null) {
throw new Error('start called more than once');
}

const headers: Record<string, string> = {};
Object.entries(metadata).forEach(([key, value]) => {
headers[key] = typeof value === 'string' ? value : value.join(', ');
});

this._request = this._createRequest(headers);
}

sendMessage(msgBytes: Uint8Array): void {
console.log('[NodeHttp2Transport] sendMessage', msgBytes);
assertDefined(this._request, '_request');
this._request.write(msgBytes);
}

finishSend(): void {
console.log('[NodeHttp2Transport] finishSend');
assertDefined(this._request, '_request');
this._request.end();
}

cancel(): void {
console.log('[NodeHttp2Transport] cancel');
assertDefined(this._request, '_request');
this._request.close();
}

/**
* Cleanup.
*/
static dispose(): void {
for (const session of NodeHttp2gRPCTransport._sessionMap.values()) {
session.close();
}
NodeHttp2gRPCTransport._sessionMap.clear();
}
}
2 changes: 2 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import * as vscode from 'vscode';
import { ExtensionController } from './controllers';
import { ConfigService } from './services';

process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';

export function activate(context: vscode.ExtensionContext): void {
const controller = new ExtensionController(context, ConfigService);

Expand Down
Loading