Initial Commit
This commit is contained in:
45
node_modules/@grpc/grpc-js/src/admin.ts
generated
vendored
Normal file
45
node_modules/@grpc/grpc-js/src/admin.ts
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2021 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 { ServiceDefinition } from './make-client';
|
||||
import { Server, UntypedServiceImplementation } from './server';
|
||||
|
||||
interface GetServiceDefinition {
|
||||
(): ServiceDefinition;
|
||||
}
|
||||
|
||||
interface GetHandlers {
|
||||
(): UntypedServiceImplementation;
|
||||
}
|
||||
|
||||
const registeredAdminServices: {
|
||||
getServiceDefinition: GetServiceDefinition;
|
||||
getHandlers: GetHandlers;
|
||||
}[] = [];
|
||||
|
||||
export function registerAdminService(
|
||||
getServiceDefinition: GetServiceDefinition,
|
||||
getHandlers: GetHandlers
|
||||
) {
|
||||
registeredAdminServices.push({ getServiceDefinition, getHandlers });
|
||||
}
|
||||
|
||||
export function addAdminServicesToServer(server: Server): void {
|
||||
for (const { getServiceDefinition, getHandlers } of registeredAdminServices) {
|
||||
server.addService(getServiceDefinition(), getHandlers());
|
||||
}
|
||||
}
|
||||
196
node_modules/@grpc/grpc-js/src/backoff-timeout.ts
generated
vendored
Normal file
196
node_modules/@grpc/grpc-js/src/backoff-timeout.ts
generated
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
const INITIAL_BACKOFF_MS = 1000;
|
||||
const BACKOFF_MULTIPLIER = 1.6;
|
||||
const MAX_BACKOFF_MS = 120000;
|
||||
const BACKOFF_JITTER = 0.2;
|
||||
|
||||
/**
|
||||
* Get a number uniformly at random in the range [min, max)
|
||||
* @param min
|
||||
* @param max
|
||||
*/
|
||||
function uniformRandom(min: number, max: number) {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
|
||||
export interface BackoffOptions {
|
||||
initialDelay?: number;
|
||||
multiplier?: number;
|
||||
jitter?: number;
|
||||
maxDelay?: number;
|
||||
}
|
||||
|
||||
export class BackoffTimeout {
|
||||
/**
|
||||
* The delay time at the start, and after each reset.
|
||||
*/
|
||||
private readonly initialDelay: number = INITIAL_BACKOFF_MS;
|
||||
/**
|
||||
* The exponential backoff multiplier.
|
||||
*/
|
||||
private readonly multiplier: number = BACKOFF_MULTIPLIER;
|
||||
/**
|
||||
* The maximum delay time
|
||||
*/
|
||||
private readonly maxDelay: number = MAX_BACKOFF_MS;
|
||||
/**
|
||||
* The maximum fraction by which the delay time can randomly vary after
|
||||
* applying the multiplier.
|
||||
*/
|
||||
private readonly jitter: number = BACKOFF_JITTER;
|
||||
/**
|
||||
* The delay time for the next time the timer runs.
|
||||
*/
|
||||
private nextDelay: number;
|
||||
/**
|
||||
* The handle of the underlying timer. If running is false, this value refers
|
||||
* to an object representing a timer that has ended, but it can still be
|
||||
* interacted with without error.
|
||||
*/
|
||||
private timerId: NodeJS.Timeout;
|
||||
/**
|
||||
* Indicates whether the timer is currently running.
|
||||
*/
|
||||
private running = false;
|
||||
/**
|
||||
* Indicates whether the timer should keep the Node process running if no
|
||||
* other async operation is doing so.
|
||||
*/
|
||||
private hasRef = true;
|
||||
/**
|
||||
* The time that the currently running timer was started. Only valid if
|
||||
* running is true.
|
||||
*/
|
||||
private startTime: Date = new Date();
|
||||
/**
|
||||
* The approximate time that the currently running timer will end. Only valid
|
||||
* if running is true.
|
||||
*/
|
||||
private endTime: Date = new Date();
|
||||
|
||||
constructor(private callback: () => void, options?: BackoffOptions) {
|
||||
if (options) {
|
||||
if (options.initialDelay) {
|
||||
this.initialDelay = options.initialDelay;
|
||||
}
|
||||
if (options.multiplier) {
|
||||
this.multiplier = options.multiplier;
|
||||
}
|
||||
if (options.jitter) {
|
||||
this.jitter = options.jitter;
|
||||
}
|
||||
if (options.maxDelay) {
|
||||
this.maxDelay = options.maxDelay;
|
||||
}
|
||||
}
|
||||
this.nextDelay = this.initialDelay;
|
||||
this.timerId = setTimeout(() => {}, 0);
|
||||
clearTimeout(this.timerId);
|
||||
}
|
||||
|
||||
private runTimer(delay: number) {
|
||||
this.endTime = this.startTime;
|
||||
this.endTime.setMilliseconds(this.endTime.getMilliseconds() + this.nextDelay);
|
||||
clearTimeout(this.timerId);
|
||||
this.timerId = setTimeout(() => {
|
||||
this.callback();
|
||||
this.running = false;
|
||||
}, delay);
|
||||
if (!this.hasRef) {
|
||||
this.timerId.unref?.();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the callback after the current amount of delay time
|
||||
*/
|
||||
runOnce() {
|
||||
this.running = true;
|
||||
this.startTime = new Date();
|
||||
this.runTimer(this.nextDelay);
|
||||
const nextBackoff = Math.min(
|
||||
this.nextDelay * this.multiplier,
|
||||
this.maxDelay
|
||||
);
|
||||
const jitterMagnitude = nextBackoff * this.jitter;
|
||||
this.nextDelay =
|
||||
nextBackoff + uniformRandom(-jitterMagnitude, jitterMagnitude);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the timer. The callback will not be called until `runOnce` is called
|
||||
* again.
|
||||
*/
|
||||
stop() {
|
||||
clearTimeout(this.timerId);
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the delay time to its initial value. If the timer is still running,
|
||||
* retroactively apply that reset to the current timer.
|
||||
*/
|
||||
reset() {
|
||||
this.nextDelay = this.initialDelay;
|
||||
if (this.running) {
|
||||
const now = new Date();
|
||||
const newEndTime = this.startTime;
|
||||
newEndTime.setMilliseconds(newEndTime.getMilliseconds() + this.nextDelay);
|
||||
clearTimeout(this.timerId);
|
||||
if (now < newEndTime) {
|
||||
this.runTimer(newEndTime.getTime() - now.getTime());
|
||||
} else {
|
||||
this.running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the timer is currently running.
|
||||
*/
|
||||
isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set that while the timer is running, it should keep the Node process
|
||||
* running.
|
||||
*/
|
||||
ref() {
|
||||
this.hasRef = true;
|
||||
this.timerId.ref?.();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set that while the timer is running, it should not keep the Node process
|
||||
* running.
|
||||
*/
|
||||
unref() {
|
||||
this.hasRef = false;
|
||||
this.timerId.unref?.();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the approximate timestamp of when the timer will fire. Only valid if
|
||||
* this.isRunning() is true.
|
||||
*/
|
||||
getEndTime() {
|
||||
return this.endTime;
|
||||
}
|
||||
}
|
||||
226
node_modules/@grpc/grpc-js/src/call-credentials.ts
generated
vendored
Normal file
226
node_modules/@grpc/grpc-js/src/call-credentials.ts
generated
vendored
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* 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 { Metadata } from './metadata';
|
||||
|
||||
export interface CallMetadataOptions {
|
||||
service_url: string;
|
||||
}
|
||||
|
||||
export type CallMetadataGenerator = (
|
||||
options: CallMetadataOptions,
|
||||
cb: (err: Error | null, metadata?: Metadata) => void
|
||||
) => void;
|
||||
|
||||
// google-auth-library pre-v2.0.0 does not have getRequestHeaders
|
||||
// but has getRequestMetadata, which is deprecated in v2.0.0
|
||||
export interface OldOAuth2Client {
|
||||
getRequestMetadata: (
|
||||
url: string,
|
||||
callback: (
|
||||
err: Error | null,
|
||||
headers?: {
|
||||
[index: string]: string;
|
||||
}
|
||||
) => void
|
||||
) => void;
|
||||
}
|
||||
|
||||
export interface CurrentOAuth2Client {
|
||||
getRequestHeaders: (url?: string) => Promise<{ [index: string]: string }>;
|
||||
}
|
||||
|
||||
export type OAuth2Client = OldOAuth2Client | CurrentOAuth2Client;
|
||||
|
||||
function isCurrentOauth2Client(
|
||||
client: OAuth2Client
|
||||
): client is CurrentOAuth2Client {
|
||||
return (
|
||||
'getRequestHeaders' in client &&
|
||||
typeof client.getRequestHeaders === 'function'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that represents a generic method of adding authentication-related
|
||||
* metadata on a per-request basis.
|
||||
*/
|
||||
export abstract class CallCredentials {
|
||||
/**
|
||||
* Asynchronously generates a new Metadata object.
|
||||
* @param options Options used in generating the Metadata object.
|
||||
*/
|
||||
abstract generateMetadata(options: CallMetadataOptions): Promise<Metadata>;
|
||||
/**
|
||||
* Creates a new CallCredentials object from properties of both this and
|
||||
* another CallCredentials object. This object's metadata generator will be
|
||||
* called first.
|
||||
* @param callCredentials The other CallCredentials object.
|
||||
*/
|
||||
abstract compose(callCredentials: CallCredentials): CallCredentials;
|
||||
|
||||
/**
|
||||
* Check whether two call credentials objects are equal. Separate
|
||||
* SingleCallCredentials with identical metadata generator functions are
|
||||
* equal.
|
||||
* @param other The other CallCredentials object to compare with.
|
||||
*/
|
||||
abstract _equals(other: CallCredentials): boolean;
|
||||
|
||||
/**
|
||||
* Creates a new CallCredentials object from a given function that generates
|
||||
* Metadata objects.
|
||||
* @param metadataGenerator A function that accepts a set of options, and
|
||||
* generates a Metadata object based on these options, which is passed back
|
||||
* to the caller via a supplied (err, metadata) callback.
|
||||
*/
|
||||
static createFromMetadataGenerator(
|
||||
metadataGenerator: CallMetadataGenerator
|
||||
): CallCredentials {
|
||||
return new SingleCallCredentials(metadataGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a gRPC credential from a Google credential object.
|
||||
* @param googleCredentials The authentication client to use.
|
||||
* @return The resulting CallCredentials object.
|
||||
*/
|
||||
static createFromGoogleCredential(
|
||||
googleCredentials: OAuth2Client
|
||||
): CallCredentials {
|
||||
return CallCredentials.createFromMetadataGenerator((options, callback) => {
|
||||
let getHeaders: Promise<{ [index: string]: string }>;
|
||||
if (isCurrentOauth2Client(googleCredentials)) {
|
||||
getHeaders = googleCredentials.getRequestHeaders(options.service_url);
|
||||
} else {
|
||||
getHeaders = new Promise((resolve, reject) => {
|
||||
googleCredentials.getRequestMetadata(
|
||||
options.service_url,
|
||||
(err, headers) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
if (!headers) {
|
||||
reject(new Error('Headers not set by metadata plugin'));
|
||||
return;
|
||||
}
|
||||
resolve(headers);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
getHeaders.then(
|
||||
headers => {
|
||||
const metadata = new Metadata();
|
||||
for (const key of Object.keys(headers)) {
|
||||
metadata.add(key, headers[key]);
|
||||
}
|
||||
callback(null, metadata);
|
||||
},
|
||||
err => {
|
||||
callback(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
static createEmpty(): CallCredentials {
|
||||
return new EmptyCallCredentials();
|
||||
}
|
||||
}
|
||||
|
||||
class ComposedCallCredentials extends CallCredentials {
|
||||
constructor(private creds: CallCredentials[]) {
|
||||
super();
|
||||
}
|
||||
|
||||
async generateMetadata(options: CallMetadataOptions): Promise<Metadata> {
|
||||
const base: Metadata = new Metadata();
|
||||
const generated: Metadata[] = await Promise.all(
|
||||
this.creds.map(cred => cred.generateMetadata(options))
|
||||
);
|
||||
for (const gen of generated) {
|
||||
base.merge(gen);
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
compose(other: CallCredentials): CallCredentials {
|
||||
return new ComposedCallCredentials(this.creds.concat([other]));
|
||||
}
|
||||
|
||||
_equals(other: CallCredentials): boolean {
|
||||
if (this === other) {
|
||||
return true;
|
||||
}
|
||||
if (other instanceof ComposedCallCredentials) {
|
||||
return this.creds.every((value, index) =>
|
||||
value._equals(other.creds[index])
|
||||
);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SingleCallCredentials extends CallCredentials {
|
||||
constructor(private metadataGenerator: CallMetadataGenerator) {
|
||||
super();
|
||||
}
|
||||
|
||||
generateMetadata(options: CallMetadataOptions): Promise<Metadata> {
|
||||
return new Promise<Metadata>((resolve, reject) => {
|
||||
this.metadataGenerator(options, (err, metadata) => {
|
||||
if (metadata !== undefined) {
|
||||
resolve(metadata);
|
||||
} else {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
compose(other: CallCredentials): CallCredentials {
|
||||
return new ComposedCallCredentials([this, other]);
|
||||
}
|
||||
|
||||
_equals(other: CallCredentials): boolean {
|
||||
if (this === other) {
|
||||
return true;
|
||||
}
|
||||
if (other instanceof SingleCallCredentials) {
|
||||
return this.metadataGenerator === other.metadataGenerator;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class EmptyCallCredentials extends CallCredentials {
|
||||
generateMetadata(options: CallMetadataOptions): Promise<Metadata> {
|
||||
return Promise.resolve(new Metadata());
|
||||
}
|
||||
|
||||
compose(other: CallCredentials): CallCredentials {
|
||||
return other;
|
||||
}
|
||||
|
||||
_equals(other: CallCredentials): boolean {
|
||||
return other instanceof EmptyCallCredentials;
|
||||
}
|
||||
}
|
||||
173
node_modules/@grpc/grpc-js/src/call-interface.ts
generated
vendored
Normal file
173
node_modules/@grpc/grpc-js/src/call-interface.ts
generated
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright 2022 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 { CallCredentials } from './call-credentials';
|
||||
import { Status } from './constants';
|
||||
import { Deadline } from './deadline';
|
||||
import { Metadata } from './metadata';
|
||||
import { ServerSurfaceCall } from './server-call';
|
||||
|
||||
export interface CallStreamOptions {
|
||||
deadline: Deadline;
|
||||
flags: number;
|
||||
host: string;
|
||||
parentCall: ServerSurfaceCall | null;
|
||||
}
|
||||
|
||||
export type PartialCallStreamOptions = Partial<CallStreamOptions>;
|
||||
|
||||
export interface StatusObject {
|
||||
code: Status;
|
||||
details: string;
|
||||
metadata: Metadata;
|
||||
}
|
||||
|
||||
export type PartialStatusObject = Pick<StatusObject, 'code' | 'details'> & {
|
||||
metadata: Metadata | null;
|
||||
};
|
||||
|
||||
export const enum WriteFlags {
|
||||
BufferHint = 1,
|
||||
NoCompress = 2,
|
||||
WriteThrough = 4,
|
||||
}
|
||||
|
||||
export interface WriteObject {
|
||||
message: Buffer;
|
||||
flags?: number;
|
||||
}
|
||||
|
||||
export interface MetadataListener {
|
||||
(metadata: Metadata, next: (metadata: Metadata) => void): void;
|
||||
}
|
||||
|
||||
export interface MessageListener {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(message: any, next: (message: any) => void): void;
|
||||
}
|
||||
|
||||
export interface StatusListener {
|
||||
(status: StatusObject, next: (status: StatusObject) => void): void;
|
||||
}
|
||||
|
||||
export interface FullListener {
|
||||
onReceiveMetadata: MetadataListener;
|
||||
onReceiveMessage: MessageListener;
|
||||
onReceiveStatus: StatusListener;
|
||||
}
|
||||
|
||||
export type Listener = Partial<FullListener>;
|
||||
|
||||
/**
|
||||
* An object with methods for handling the responses to a call.
|
||||
*/
|
||||
export interface InterceptingListener {
|
||||
onReceiveMetadata(metadata: Metadata): void;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
onReceiveMessage(message: any): void;
|
||||
onReceiveStatus(status: StatusObject): void;
|
||||
}
|
||||
|
||||
export function isInterceptingListener(
|
||||
listener: Listener | InterceptingListener
|
||||
): listener is InterceptingListener {
|
||||
return (
|
||||
listener.onReceiveMetadata !== undefined &&
|
||||
listener.onReceiveMetadata.length === 1
|
||||
);
|
||||
}
|
||||
|
||||
export class InterceptingListenerImpl implements InterceptingListener {
|
||||
private processingMetadata = false;
|
||||
private hasPendingMessage = false;
|
||||
private pendingMessage: any;
|
||||
private processingMessage = false;
|
||||
private pendingStatus: StatusObject | null = null;
|
||||
constructor(
|
||||
private listener: FullListener,
|
||||
private nextListener: InterceptingListener
|
||||
) {}
|
||||
|
||||
private processPendingMessage() {
|
||||
if (this.hasPendingMessage) {
|
||||
this.nextListener.onReceiveMessage(this.pendingMessage);
|
||||
this.pendingMessage = null;
|
||||
this.hasPendingMessage = false;
|
||||
}
|
||||
}
|
||||
|
||||
private processPendingStatus() {
|
||||
if (this.pendingStatus) {
|
||||
this.nextListener.onReceiveStatus(this.pendingStatus);
|
||||
}
|
||||
}
|
||||
|
||||
onReceiveMetadata(metadata: Metadata): void {
|
||||
this.processingMetadata = true;
|
||||
this.listener.onReceiveMetadata(metadata, metadata => {
|
||||
this.processingMetadata = false;
|
||||
this.nextListener.onReceiveMetadata(metadata);
|
||||
this.processPendingMessage();
|
||||
this.processPendingStatus();
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
onReceiveMessage(message: any): void {
|
||||
/* If this listener processes messages asynchronously, the last message may
|
||||
* be reordered with respect to the status */
|
||||
this.processingMessage = true;
|
||||
this.listener.onReceiveMessage(message, msg => {
|
||||
this.processingMessage = false;
|
||||
if (this.processingMetadata) {
|
||||
this.pendingMessage = msg;
|
||||
this.hasPendingMessage = true;
|
||||
} else {
|
||||
this.nextListener.onReceiveMessage(msg);
|
||||
this.processPendingStatus();
|
||||
}
|
||||
});
|
||||
}
|
||||
onReceiveStatus(status: StatusObject): void {
|
||||
this.listener.onReceiveStatus(status, processedStatus => {
|
||||
if (this.processingMetadata || this.processingMessage) {
|
||||
this.pendingStatus = processedStatus;
|
||||
} else {
|
||||
this.nextListener.onReceiveStatus(processedStatus);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export interface WriteCallback {
|
||||
(error?: Error | null): void;
|
||||
}
|
||||
|
||||
export interface MessageContext {
|
||||
callback?: WriteCallback;
|
||||
flags?: number;
|
||||
}
|
||||
|
||||
export interface Call {
|
||||
cancelWithStatus(status: Status, details: string): void;
|
||||
getPeer(): string;
|
||||
start(metadata: Metadata, listener: InterceptingListener): void;
|
||||
sendMessageWithContext(context: MessageContext, message: Buffer): void;
|
||||
startRead(): void;
|
||||
halfClose(): void;
|
||||
getCallNumber(): number;
|
||||
setCredentials(credentials: CallCredentials): void;
|
||||
}
|
||||
22
node_modules/@grpc/grpc-js/src/call-number.ts
generated
vendored
Normal file
22
node_modules/@grpc/grpc-js/src/call-number.ts
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2022 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.
|
||||
*
|
||||
*/
|
||||
|
||||
let nextCallNumber = 0;
|
||||
|
||||
export function getNextCallNumber() {
|
||||
return nextCallNumber++;
|
||||
}
|
||||
200
node_modules/@grpc/grpc-js/src/call.ts
generated
vendored
Normal file
200
node_modules/@grpc/grpc-js/src/call.ts
generated
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* 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 { EventEmitter } from 'events';
|
||||
import { Duplex, Readable, Writable } from 'stream';
|
||||
|
||||
import { StatusObject, MessageContext } from './call-interface';
|
||||
import { Status } from './constants';
|
||||
import { EmitterAugmentation1 } from './events';
|
||||
import { Metadata } from './metadata';
|
||||
import { ObjectReadable, ObjectWritable, WriteCallback } from './object-stream';
|
||||
import { InterceptingCallInterface } from './client-interceptors';
|
||||
|
||||
/**
|
||||
* A type extending the built-in Error object with additional fields.
|
||||
*/
|
||||
export type ServiceError = StatusObject & Error;
|
||||
|
||||
/**
|
||||
* A base type for all user-facing values returned by client-side method calls.
|
||||
*/
|
||||
export type SurfaceCall = {
|
||||
call?: InterceptingCallInterface;
|
||||
cancel(): void;
|
||||
getPeer(): string;
|
||||
} & EmitterAugmentation1<'metadata', Metadata> &
|
||||
EmitterAugmentation1<'status', StatusObject> &
|
||||
EventEmitter;
|
||||
|
||||
/**
|
||||
* A type representing the return value of a unary method call.
|
||||
*/
|
||||
export type ClientUnaryCall = SurfaceCall;
|
||||
|
||||
/**
|
||||
* A type representing the return value of a server stream method call.
|
||||
*/
|
||||
export type ClientReadableStream<ResponseType> = {
|
||||
deserialize: (chunk: Buffer) => ResponseType;
|
||||
} & SurfaceCall &
|
||||
ObjectReadable<ResponseType>;
|
||||
|
||||
/**
|
||||
* A type representing the return value of a client stream method call.
|
||||
*/
|
||||
export type ClientWritableStream<RequestType> = {
|
||||
serialize: (value: RequestType) => Buffer;
|
||||
} & SurfaceCall &
|
||||
ObjectWritable<RequestType>;
|
||||
|
||||
/**
|
||||
* A type representing the return value of a bidirectional stream method call.
|
||||
*/
|
||||
export type ClientDuplexStream<RequestType, ResponseType> =
|
||||
ClientWritableStream<RequestType> & ClientReadableStream<ResponseType>;
|
||||
|
||||
/**
|
||||
* Construct a ServiceError from a StatusObject. This function exists primarily
|
||||
* as an attempt to make the error stack trace clearly communicate that the
|
||||
* error is not necessarily a problem in gRPC itself.
|
||||
* @param status
|
||||
*/
|
||||
export function callErrorFromStatus(
|
||||
status: StatusObject,
|
||||
callerStack: string
|
||||
): ServiceError {
|
||||
const message = `${status.code} ${Status[status.code]}: ${status.details}`;
|
||||
const error = new Error(message);
|
||||
const stack = `${error.stack}\nfor call at\n${callerStack}`;
|
||||
return Object.assign(new Error(message), status, { stack });
|
||||
}
|
||||
|
||||
export class ClientUnaryCallImpl
|
||||
extends EventEmitter
|
||||
implements ClientUnaryCall
|
||||
{
|
||||
public call?: InterceptingCallInterface;
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
cancel(): void {
|
||||
this.call?.cancelWithStatus(Status.CANCELLED, 'Cancelled on client');
|
||||
}
|
||||
|
||||
getPeer(): string {
|
||||
return this.call?.getPeer() ?? 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientReadableStreamImpl<ResponseType>
|
||||
extends Readable
|
||||
implements ClientReadableStream<ResponseType>
|
||||
{
|
||||
public call?: InterceptingCallInterface;
|
||||
constructor(readonly deserialize: (chunk: Buffer) => ResponseType) {
|
||||
super({ objectMode: true });
|
||||
}
|
||||
|
||||
cancel(): void {
|
||||
this.call?.cancelWithStatus(Status.CANCELLED, 'Cancelled on client');
|
||||
}
|
||||
|
||||
getPeer(): string {
|
||||
return this.call?.getPeer() ?? 'unknown';
|
||||
}
|
||||
|
||||
_read(_size: number): void {
|
||||
this.call?.startRead();
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientWritableStreamImpl<RequestType>
|
||||
extends Writable
|
||||
implements ClientWritableStream<RequestType>
|
||||
{
|
||||
public call?: InterceptingCallInterface;
|
||||
constructor(readonly serialize: (value: RequestType) => Buffer) {
|
||||
super({ objectMode: true });
|
||||
}
|
||||
|
||||
cancel(): void {
|
||||
this.call?.cancelWithStatus(Status.CANCELLED, 'Cancelled on client');
|
||||
}
|
||||
|
||||
getPeer(): string {
|
||||
return this.call?.getPeer() ?? 'unknown';
|
||||
}
|
||||
|
||||
_write(chunk: RequestType, encoding: string, cb: WriteCallback) {
|
||||
const context: MessageContext = {
|
||||
callback: cb,
|
||||
};
|
||||
const flags = Number(encoding);
|
||||
if (!Number.isNaN(flags)) {
|
||||
context.flags = flags;
|
||||
}
|
||||
this.call?.sendMessageWithContext(context, chunk);
|
||||
}
|
||||
|
||||
_final(cb: Function) {
|
||||
this.call?.halfClose();
|
||||
cb();
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientDuplexStreamImpl<RequestType, ResponseType>
|
||||
extends Duplex
|
||||
implements ClientDuplexStream<RequestType, ResponseType>
|
||||
{
|
||||
public call?: InterceptingCallInterface;
|
||||
constructor(
|
||||
readonly serialize: (value: RequestType) => Buffer,
|
||||
readonly deserialize: (chunk: Buffer) => ResponseType
|
||||
) {
|
||||
super({ objectMode: true });
|
||||
}
|
||||
|
||||
cancel(): void {
|
||||
this.call?.cancelWithStatus(Status.CANCELLED, 'Cancelled on client');
|
||||
}
|
||||
|
||||
getPeer(): string {
|
||||
return this.call?.getPeer() ?? 'unknown';
|
||||
}
|
||||
|
||||
_read(_size: number): void {
|
||||
this.call?.startRead();
|
||||
}
|
||||
|
||||
_write(chunk: RequestType, encoding: string, cb: WriteCallback) {
|
||||
const context: MessageContext = {
|
||||
callback: cb,
|
||||
};
|
||||
const flags = Number(encoding);
|
||||
if (!Number.isNaN(flags)) {
|
||||
context.flags = flags;
|
||||
}
|
||||
this.call?.sendMessageWithContext(context, chunk);
|
||||
}
|
||||
|
||||
_final(cb: Function) {
|
||||
this.call?.halfClose();
|
||||
cb();
|
||||
}
|
||||
}
|
||||
267
node_modules/@grpc/grpc-js/src/channel-credentials.ts
generated
vendored
Normal file
267
node_modules/@grpc/grpc-js/src/channel-credentials.ts
generated
vendored
Normal file
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* 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 {
|
||||
ConnectionOptions,
|
||||
createSecureContext,
|
||||
PeerCertificate,
|
||||
SecureContext,
|
||||
} from 'tls';
|
||||
|
||||
import { CallCredentials } from './call-credentials';
|
||||
import { CIPHER_SUITES, getDefaultRootsData } from './tls-helpers';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function verifyIsBufferOrNull(obj: any, friendlyName: string): void {
|
||||
if (obj && !(obj instanceof Buffer)) {
|
||||
throw new TypeError(`${friendlyName}, if provided, must be a Buffer.`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A callback that will receive the expected hostname and presented peer
|
||||
* certificate as parameters. The callback should return an error to
|
||||
* indicate that the presented certificate is considered invalid and
|
||||
* otherwise returned undefined.
|
||||
*/
|
||||
export type CheckServerIdentityCallback = (
|
||||
hostname: string,
|
||||
cert: PeerCertificate
|
||||
) => Error | undefined;
|
||||
|
||||
/**
|
||||
* Additional peer verification options that can be set when creating
|
||||
* SSL credentials.
|
||||
*/
|
||||
export interface VerifyOptions {
|
||||
/**
|
||||
* If set, this callback will be invoked after the usual hostname verification
|
||||
* has been performed on the peer certificate.
|
||||
*/
|
||||
checkServerIdentity?: CheckServerIdentityCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that contains credentials for communicating over a channel, as well
|
||||
* as a set of per-call credentials, which are applied to every method call made
|
||||
* over a channel initialized with an instance of this class.
|
||||
*/
|
||||
export abstract class ChannelCredentials {
|
||||
protected callCredentials: CallCredentials;
|
||||
|
||||
protected constructor(callCredentials?: CallCredentials) {
|
||||
this.callCredentials = callCredentials || CallCredentials.createEmpty();
|
||||
}
|
||||
/**
|
||||
* Returns a copy of this object with the included set of per-call credentials
|
||||
* expanded to include callCredentials.
|
||||
* @param callCredentials A CallCredentials object to associate with this
|
||||
* instance.
|
||||
*/
|
||||
abstract compose(callCredentials: CallCredentials): ChannelCredentials;
|
||||
|
||||
/**
|
||||
* Gets the set of per-call credentials associated with this instance.
|
||||
*/
|
||||
_getCallCredentials(): CallCredentials {
|
||||
return this.callCredentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a SecureContext object generated from input parameters if this
|
||||
* instance was created with createSsl, or null if this instance was created
|
||||
* with createInsecure.
|
||||
*/
|
||||
abstract _getConnectionOptions(): ConnectionOptions | null;
|
||||
|
||||
/**
|
||||
* Indicates whether this credentials object creates a secure channel.
|
||||
*/
|
||||
abstract _isSecure(): boolean;
|
||||
|
||||
/**
|
||||
* Check whether two channel credentials objects are equal. Two secure
|
||||
* credentials are equal if they were constructed with the same parameters.
|
||||
* @param other The other ChannelCredentials Object
|
||||
*/
|
||||
abstract _equals(other: ChannelCredentials): boolean;
|
||||
|
||||
/**
|
||||
* Return a new ChannelCredentials instance with a given set of credentials.
|
||||
* The resulting instance can be used to construct a Channel that communicates
|
||||
* over TLS.
|
||||
* @param rootCerts The root certificate data.
|
||||
* @param privateKey The client certificate private key, if available.
|
||||
* @param certChain The client certificate key chain, if available.
|
||||
* @param verifyOptions Additional options to modify certificate verification
|
||||
*/
|
||||
static createSsl(
|
||||
rootCerts?: Buffer | null,
|
||||
privateKey?: Buffer | null,
|
||||
certChain?: Buffer | null,
|
||||
verifyOptions?: VerifyOptions
|
||||
): ChannelCredentials {
|
||||
verifyIsBufferOrNull(rootCerts, 'Root certificate');
|
||||
verifyIsBufferOrNull(privateKey, 'Private key');
|
||||
verifyIsBufferOrNull(certChain, 'Certificate chain');
|
||||
if (privateKey && !certChain) {
|
||||
throw new Error(
|
||||
'Private key must be given with accompanying certificate chain'
|
||||
);
|
||||
}
|
||||
if (!privateKey && certChain) {
|
||||
throw new Error(
|
||||
'Certificate chain must be given with accompanying private key'
|
||||
);
|
||||
}
|
||||
const secureContext = createSecureContext({
|
||||
ca: rootCerts ?? getDefaultRootsData() ?? undefined,
|
||||
key: privateKey ?? undefined,
|
||||
cert: certChain ?? undefined,
|
||||
ciphers: CIPHER_SUITES,
|
||||
});
|
||||
return new SecureChannelCredentialsImpl(secureContext, verifyOptions ?? {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new ChannelCredentials instance with credentials created using
|
||||
* the provided secureContext. The resulting instances can be used to
|
||||
* construct a Channel that communicates over TLS. gRPC will not override
|
||||
* anything in the provided secureContext, so the environment variables
|
||||
* GRPC_SSL_CIPHER_SUITES and GRPC_DEFAULT_SSL_ROOTS_FILE_PATH will
|
||||
* not be applied.
|
||||
* @param secureContext The return value of tls.createSecureContext()
|
||||
* @param verifyOptions Additional options to modify certificate verification
|
||||
*/
|
||||
static createFromSecureContext(
|
||||
secureContext: SecureContext,
|
||||
verifyOptions?: VerifyOptions
|
||||
): ChannelCredentials {
|
||||
return new SecureChannelCredentialsImpl(secureContext, verifyOptions ?? {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new ChannelCredentials instance with no credentials.
|
||||
*/
|
||||
static createInsecure(): ChannelCredentials {
|
||||
return new InsecureChannelCredentialsImpl();
|
||||
}
|
||||
}
|
||||
|
||||
class InsecureChannelCredentialsImpl extends ChannelCredentials {
|
||||
constructor(callCredentials?: CallCredentials) {
|
||||
super(callCredentials);
|
||||
}
|
||||
|
||||
compose(callCredentials: CallCredentials): never {
|
||||
throw new Error('Cannot compose insecure credentials');
|
||||
}
|
||||
|
||||
_getConnectionOptions(): ConnectionOptions | null {
|
||||
return null;
|
||||
}
|
||||
_isSecure(): boolean {
|
||||
return false;
|
||||
}
|
||||
_equals(other: ChannelCredentials): boolean {
|
||||
return other instanceof InsecureChannelCredentialsImpl;
|
||||
}
|
||||
}
|
||||
|
||||
class SecureChannelCredentialsImpl extends ChannelCredentials {
|
||||
connectionOptions: ConnectionOptions;
|
||||
|
||||
constructor(
|
||||
private secureContext: SecureContext,
|
||||
private verifyOptions: VerifyOptions
|
||||
) {
|
||||
super();
|
||||
this.connectionOptions = {
|
||||
secureContext,
|
||||
};
|
||||
// Node asserts that this option is a function, so we cannot pass undefined
|
||||
if (verifyOptions?.checkServerIdentity) {
|
||||
this.connectionOptions.checkServerIdentity =
|
||||
verifyOptions.checkServerIdentity;
|
||||
}
|
||||
}
|
||||
|
||||
compose(callCredentials: CallCredentials): ChannelCredentials {
|
||||
const combinedCallCredentials =
|
||||
this.callCredentials.compose(callCredentials);
|
||||
return new ComposedChannelCredentialsImpl(this, combinedCallCredentials);
|
||||
}
|
||||
|
||||
_getConnectionOptions(): ConnectionOptions | null {
|
||||
// Copy to prevent callers from mutating this.connectionOptions
|
||||
return { ...this.connectionOptions };
|
||||
}
|
||||
_isSecure(): boolean {
|
||||
return true;
|
||||
}
|
||||
_equals(other: ChannelCredentials): boolean {
|
||||
if (this === other) {
|
||||
return true;
|
||||
}
|
||||
if (other instanceof SecureChannelCredentialsImpl) {
|
||||
return (
|
||||
this.secureContext === other.secureContext &&
|
||||
this.verifyOptions.checkServerIdentity ===
|
||||
other.verifyOptions.checkServerIdentity
|
||||
);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ComposedChannelCredentialsImpl extends ChannelCredentials {
|
||||
constructor(
|
||||
private channelCredentials: SecureChannelCredentialsImpl,
|
||||
callCreds: CallCredentials
|
||||
) {
|
||||
super(callCreds);
|
||||
}
|
||||
compose(callCredentials: CallCredentials) {
|
||||
const combinedCallCredentials =
|
||||
this.callCredentials.compose(callCredentials);
|
||||
return new ComposedChannelCredentialsImpl(
|
||||
this.channelCredentials,
|
||||
combinedCallCredentials
|
||||
);
|
||||
}
|
||||
|
||||
_getConnectionOptions(): ConnectionOptions | null {
|
||||
return this.channelCredentials._getConnectionOptions();
|
||||
}
|
||||
_isSecure(): boolean {
|
||||
return true;
|
||||
}
|
||||
_equals(other: ChannelCredentials): boolean {
|
||||
if (this === other) {
|
||||
return true;
|
||||
}
|
||||
if (other instanceof ComposedChannelCredentialsImpl) {
|
||||
return (
|
||||
this.channelCredentials._equals(other.channelCredentials) &&
|
||||
this.callCredentials._equals(other.callCredentials)
|
||||
);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
119
node_modules/@grpc/grpc-js/src/channel-options.ts
generated
vendored
Normal file
119
node_modules/@grpc/grpc-js/src/channel-options.ts
generated
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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 { CompressionAlgorithms } from './compression-algorithms';
|
||||
|
||||
/**
|
||||
* An interface that contains options used when initializing a Channel instance.
|
||||
*/
|
||||
export interface ChannelOptions {
|
||||
'grpc.ssl_target_name_override'?: string;
|
||||
'grpc.primary_user_agent'?: string;
|
||||
'grpc.secondary_user_agent'?: string;
|
||||
'grpc.default_authority'?: string;
|
||||
'grpc.keepalive_time_ms'?: number;
|
||||
'grpc.keepalive_timeout_ms'?: number;
|
||||
'grpc.keepalive_permit_without_calls'?: number;
|
||||
'grpc.service_config'?: string;
|
||||
'grpc.max_concurrent_streams'?: number;
|
||||
'grpc.initial_reconnect_backoff_ms'?: number;
|
||||
'grpc.max_reconnect_backoff_ms'?: number;
|
||||
'grpc.use_local_subchannel_pool'?: number;
|
||||
'grpc.max_send_message_length'?: number;
|
||||
'grpc.max_receive_message_length'?: number;
|
||||
'grpc.enable_http_proxy'?: number;
|
||||
/* http_connect_target and http_connect_creds are used for passing data
|
||||
* around internally, and should not be documented as public-facing options
|
||||
*/
|
||||
'grpc.http_connect_target'?: string;
|
||||
'grpc.http_connect_creds'?: string;
|
||||
'grpc.default_compression_algorithm'?: CompressionAlgorithms;
|
||||
'grpc.enable_channelz'?: number;
|
||||
'grpc.dns_min_time_between_resolutions_ms'?: number;
|
||||
'grpc.enable_retries'?: number;
|
||||
'grpc.per_rpc_retry_buffer_size'?: number;
|
||||
/* This option is pattered like a core option, but the core does not have
|
||||
* this option. It is closely related to the option
|
||||
* grpc.per_rpc_retry_buffer_size, which is in the core. The core will likely
|
||||
* implement this functionality using the ResourceQuota mechanism, so there
|
||||
* will probably not be any collision or other inconsistency. */
|
||||
'grpc.retry_buffer_size'?: number;
|
||||
'grpc.max_connection_age_ms'?: number;
|
||||
'grpc.max_connection_age_grace_ms'?: number;
|
||||
'grpc-node.max_session_memory'?: number;
|
||||
'grpc.service_config_disable_resolution'?: number;
|
||||
'grpc.client_idle_timeout_ms'?: number;
|
||||
/**
|
||||
* Set the enableTrace option in TLS clients and servers
|
||||
*/
|
||||
'grpc-node.tls_enable_trace'?: number;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is for checking provided options at runtime. This is an object for
|
||||
* easier membership checking.
|
||||
*/
|
||||
export const recognizedOptions = {
|
||||
'grpc.ssl_target_name_override': true,
|
||||
'grpc.primary_user_agent': true,
|
||||
'grpc.secondary_user_agent': true,
|
||||
'grpc.default_authority': true,
|
||||
'grpc.keepalive_time_ms': true,
|
||||
'grpc.keepalive_timeout_ms': true,
|
||||
'grpc.keepalive_permit_without_calls': true,
|
||||
'grpc.service_config': true,
|
||||
'grpc.max_concurrent_streams': true,
|
||||
'grpc.initial_reconnect_backoff_ms': true,
|
||||
'grpc.max_reconnect_backoff_ms': true,
|
||||
'grpc.use_local_subchannel_pool': true,
|
||||
'grpc.max_send_message_length': true,
|
||||
'grpc.max_receive_message_length': true,
|
||||
'grpc.enable_http_proxy': true,
|
||||
'grpc.enable_channelz': true,
|
||||
'grpc.dns_min_time_between_resolutions_ms': true,
|
||||
'grpc.enable_retries': true,
|
||||
'grpc.per_rpc_retry_buffer_size': true,
|
||||
'grpc.retry_buffer_size': true,
|
||||
'grpc.max_connection_age_ms': true,
|
||||
'grpc.max_connection_age_grace_ms': true,
|
||||
'grpc-node.max_session_memory': true,
|
||||
'grpc.service_config_disable_resolution': true,
|
||||
'grpc.client_idle_timeout_ms': true,
|
||||
'grpc-node.tls_enable_trace': true,
|
||||
};
|
||||
|
||||
export function channelOptionsEqual(
|
||||
options1: ChannelOptions,
|
||||
options2: ChannelOptions
|
||||
) {
|
||||
const keys1 = Object.keys(options1).sort();
|
||||
const keys2 = Object.keys(options2).sort();
|
||||
if (keys1.length !== keys2.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < keys1.length; i += 1) {
|
||||
if (keys1[i] !== keys2[i]) {
|
||||
return false;
|
||||
}
|
||||
if (options1[keys1[i]] !== options2[keys2[i]]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
174
node_modules/@grpc/grpc-js/src/channel.ts
generated
vendored
Normal file
174
node_modules/@grpc/grpc-js/src/channel.ts
generated
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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 { ChannelCredentials } from './channel-credentials';
|
||||
import { ChannelOptions } from './channel-options';
|
||||
import { ServerSurfaceCall } from './server-call';
|
||||
|
||||
import { ConnectivityState } from './connectivity-state';
|
||||
import { ChannelRef } from './channelz';
|
||||
import { Call } from './call-interface';
|
||||
import { InternalChannel } from './internal-channel';
|
||||
import { Deadline } from './deadline';
|
||||
|
||||
/**
|
||||
* An interface that represents a communication channel to a server specified
|
||||
* by a given address.
|
||||
*/
|
||||
export interface Channel {
|
||||
/**
|
||||
* Close the channel. This has the same functionality as the existing
|
||||
* grpc.Client.prototype.close
|
||||
*/
|
||||
close(): void;
|
||||
/**
|
||||
* Return the target that this channel connects to
|
||||
*/
|
||||
getTarget(): string;
|
||||
/**
|
||||
* Get the channel's current connectivity state. This method is here mainly
|
||||
* because it is in the existing internal Channel class, and there isn't
|
||||
* another good place to put it.
|
||||
* @param tryToConnect If true, the channel will start connecting if it is
|
||||
* idle. Otherwise, idle channels will only start connecting when a
|
||||
* call starts.
|
||||
*/
|
||||
getConnectivityState(tryToConnect: boolean): ConnectivityState;
|
||||
/**
|
||||
* Watch for connectivity state changes. This is also here mainly because
|
||||
* it is in the existing external Channel class.
|
||||
* @param currentState The state to watch for transitions from. This should
|
||||
* always be populated by calling getConnectivityState immediately
|
||||
* before.
|
||||
* @param deadline A deadline for waiting for a state change
|
||||
* @param callback Called with no error when a state change, or with an
|
||||
* error if the deadline passes without a state change.
|
||||
*/
|
||||
watchConnectivityState(
|
||||
currentState: ConnectivityState,
|
||||
deadline: Date | number,
|
||||
callback: (error?: Error) => void
|
||||
): void;
|
||||
/**
|
||||
* Get the channelz reference object for this channel. A request to the
|
||||
* channelz service for the id in this object will provide information
|
||||
* about this channel.
|
||||
*/
|
||||
getChannelzRef(): ChannelRef;
|
||||
/**
|
||||
* Create a call object. Call is an opaque type that is used by the Client
|
||||
* class. This function is called by the gRPC library when starting a
|
||||
* request. Implementers should return an instance of Call that is returned
|
||||
* from calling createCall on an instance of the provided Channel class.
|
||||
* @param method The full method string to request.
|
||||
* @param deadline The call deadline
|
||||
* @param host A host string override for making the request
|
||||
* @param parentCall A server call to propagate some information from
|
||||
* @param propagateFlags A bitwise combination of elements of grpc.propagate
|
||||
* that indicates what information to propagate from parentCall.
|
||||
*/
|
||||
createCall(
|
||||
method: string,
|
||||
deadline: Deadline,
|
||||
host: string | null | undefined,
|
||||
parentCall: ServerSurfaceCall | null,
|
||||
propagateFlags: number | null | undefined
|
||||
): Call;
|
||||
}
|
||||
|
||||
export class ChannelImplementation implements Channel {
|
||||
private internalChannel: InternalChannel;
|
||||
|
||||
constructor(
|
||||
target: string,
|
||||
credentials: ChannelCredentials,
|
||||
options: ChannelOptions
|
||||
) {
|
||||
if (typeof target !== 'string') {
|
||||
throw new TypeError('Channel target must be a string');
|
||||
}
|
||||
if (!(credentials instanceof ChannelCredentials)) {
|
||||
throw new TypeError(
|
||||
'Channel credentials must be a ChannelCredentials object'
|
||||
);
|
||||
}
|
||||
if (options) {
|
||||
if (typeof options !== 'object') {
|
||||
throw new TypeError('Channel options must be an object');
|
||||
}
|
||||
}
|
||||
|
||||
this.internalChannel = new InternalChannel(target, credentials, options);
|
||||
}
|
||||
|
||||
close() {
|
||||
this.internalChannel.close();
|
||||
}
|
||||
|
||||
getTarget() {
|
||||
return this.internalChannel.getTarget();
|
||||
}
|
||||
|
||||
getConnectivityState(tryToConnect: boolean) {
|
||||
return this.internalChannel.getConnectivityState(tryToConnect);
|
||||
}
|
||||
|
||||
watchConnectivityState(
|
||||
currentState: ConnectivityState,
|
||||
deadline: Date | number,
|
||||
callback: (error?: Error) => void
|
||||
): void {
|
||||
this.internalChannel.watchConnectivityState(
|
||||
currentState,
|
||||
deadline,
|
||||
callback
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the channelz reference object for this channel. The returned value is
|
||||
* garbage if channelz is disabled for this channel.
|
||||
* @returns
|
||||
*/
|
||||
getChannelzRef() {
|
||||
return this.internalChannel.getChannelzRef();
|
||||
}
|
||||
|
||||
createCall(
|
||||
method: string,
|
||||
deadline: Deadline,
|
||||
host: string | null | undefined,
|
||||
parentCall: ServerSurfaceCall | null,
|
||||
propagateFlags: number | null | undefined
|
||||
): Call {
|
||||
if (typeof method !== 'string') {
|
||||
throw new TypeError('Channel#createCall: method must be a string');
|
||||
}
|
||||
if (!(typeof deadline === 'number' || deadline instanceof Date)) {
|
||||
throw new TypeError(
|
||||
'Channel#createCall: deadline must be a number or Date'
|
||||
);
|
||||
}
|
||||
return this.internalChannel.createCall(
|
||||
method,
|
||||
deadline,
|
||||
host,
|
||||
parentCall,
|
||||
propagateFlags
|
||||
);
|
||||
}
|
||||
}
|
||||
886
node_modules/@grpc/grpc-js/src/channelz.ts
generated
vendored
Normal file
886
node_modules/@grpc/grpc-js/src/channelz.ts
generated
vendored
Normal file
@@ -0,0 +1,886 @@
|
||||
/*
|
||||
* Copyright 2021 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 { isIPv4, isIPv6 } from 'net';
|
||||
import { ConnectivityState } from './connectivity-state';
|
||||
import { Status } from './constants';
|
||||
import { Timestamp } from './generated/google/protobuf/Timestamp';
|
||||
import { Channel as ChannelMessage } from './generated/grpc/channelz/v1/Channel';
|
||||
import { ChannelConnectivityState__Output } from './generated/grpc/channelz/v1/ChannelConnectivityState';
|
||||
import { ChannelRef as ChannelRefMessage } from './generated/grpc/channelz/v1/ChannelRef';
|
||||
import { ChannelTrace } from './generated/grpc/channelz/v1/ChannelTrace';
|
||||
import { GetChannelRequest__Output } from './generated/grpc/channelz/v1/GetChannelRequest';
|
||||
import { GetChannelResponse } from './generated/grpc/channelz/v1/GetChannelResponse';
|
||||
import { sendUnaryData, ServerUnaryCall } from './server-call';
|
||||
import { ServerRef as ServerRefMessage } from './generated/grpc/channelz/v1/ServerRef';
|
||||
import { SocketRef as SocketRefMessage } from './generated/grpc/channelz/v1/SocketRef';
|
||||
import {
|
||||
isTcpSubchannelAddress,
|
||||
SubchannelAddress,
|
||||
} from './subchannel-address';
|
||||
import { SubchannelRef as SubchannelRefMessage } from './generated/grpc/channelz/v1/SubchannelRef';
|
||||
import { GetServerRequest__Output } from './generated/grpc/channelz/v1/GetServerRequest';
|
||||
import { GetServerResponse } from './generated/grpc/channelz/v1/GetServerResponse';
|
||||
import { Server as ServerMessage } from './generated/grpc/channelz/v1/Server';
|
||||
import { GetServersRequest__Output } from './generated/grpc/channelz/v1/GetServersRequest';
|
||||
import { GetServersResponse } from './generated/grpc/channelz/v1/GetServersResponse';
|
||||
import { GetTopChannelsRequest__Output } from './generated/grpc/channelz/v1/GetTopChannelsRequest';
|
||||
import { GetTopChannelsResponse } from './generated/grpc/channelz/v1/GetTopChannelsResponse';
|
||||
import { GetSubchannelRequest__Output } from './generated/grpc/channelz/v1/GetSubchannelRequest';
|
||||
import { GetSubchannelResponse } from './generated/grpc/channelz/v1/GetSubchannelResponse';
|
||||
import { Subchannel as SubchannelMessage } from './generated/grpc/channelz/v1/Subchannel';
|
||||
import { GetSocketRequest__Output } from './generated/grpc/channelz/v1/GetSocketRequest';
|
||||
import { GetSocketResponse } from './generated/grpc/channelz/v1/GetSocketResponse';
|
||||
import { Socket as SocketMessage } from './generated/grpc/channelz/v1/Socket';
|
||||
import { Address } from './generated/grpc/channelz/v1/Address';
|
||||
import { Security } from './generated/grpc/channelz/v1/Security';
|
||||
import { GetServerSocketsRequest__Output } from './generated/grpc/channelz/v1/GetServerSocketsRequest';
|
||||
import { GetServerSocketsResponse } from './generated/grpc/channelz/v1/GetServerSocketsResponse';
|
||||
import {
|
||||
ChannelzDefinition,
|
||||
ChannelzHandlers,
|
||||
} from './generated/grpc/channelz/v1/Channelz';
|
||||
import { ProtoGrpcType as ChannelzProtoGrpcType } from './generated/channelz';
|
||||
import type { loadSync } from '@grpc/proto-loader';
|
||||
import { registerAdminService } from './admin';
|
||||
import { loadPackageDefinition } from './make-client';
|
||||
|
||||
export type TraceSeverity =
|
||||
| 'CT_UNKNOWN'
|
||||
| 'CT_INFO'
|
||||
| 'CT_WARNING'
|
||||
| 'CT_ERROR';
|
||||
|
||||
export interface ChannelRef {
|
||||
kind: 'channel';
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface SubchannelRef {
|
||||
kind: 'subchannel';
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface ServerRef {
|
||||
kind: 'server';
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface SocketRef {
|
||||
kind: 'socket';
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
function channelRefToMessage(ref: ChannelRef): ChannelRefMessage {
|
||||
return {
|
||||
channel_id: ref.id,
|
||||
name: ref.name,
|
||||
};
|
||||
}
|
||||
|
||||
function subchannelRefToMessage(ref: SubchannelRef): SubchannelRefMessage {
|
||||
return {
|
||||
subchannel_id: ref.id,
|
||||
name: ref.name,
|
||||
};
|
||||
}
|
||||
|
||||
function serverRefToMessage(ref: ServerRef): ServerRefMessage {
|
||||
return {
|
||||
server_id: ref.id,
|
||||
};
|
||||
}
|
||||
|
||||
function socketRefToMessage(ref: SocketRef): SocketRefMessage {
|
||||
return {
|
||||
socket_id: ref.id,
|
||||
name: ref.name,
|
||||
};
|
||||
}
|
||||
|
||||
interface TraceEvent {
|
||||
description: string;
|
||||
severity: TraceSeverity;
|
||||
timestamp: Date;
|
||||
childChannel?: ChannelRef;
|
||||
childSubchannel?: SubchannelRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* The loose upper bound on the number of events that should be retained in a
|
||||
* trace. This may be exceeded by up to a factor of 2. Arbitrarily chosen as a
|
||||
* number that should be large enough to contain the recent relevant
|
||||
* information, but small enough to not use excessive memory.
|
||||
*/
|
||||
const TARGET_RETAINED_TRACES = 32;
|
||||
|
||||
export class ChannelzTrace {
|
||||
events: TraceEvent[] = [];
|
||||
creationTimestamp: Date;
|
||||
eventsLogged = 0;
|
||||
|
||||
constructor() {
|
||||
this.creationTimestamp = new Date();
|
||||
}
|
||||
|
||||
addTrace(
|
||||
severity: TraceSeverity,
|
||||
description: string,
|
||||
child?: ChannelRef | SubchannelRef
|
||||
) {
|
||||
const timestamp = new Date();
|
||||
this.events.push({
|
||||
description: description,
|
||||
severity: severity,
|
||||
timestamp: timestamp,
|
||||
childChannel: child?.kind === 'channel' ? child : undefined,
|
||||
childSubchannel: child?.kind === 'subchannel' ? child : undefined,
|
||||
});
|
||||
// Whenever the trace array gets too large, discard the first half
|
||||
if (this.events.length >= TARGET_RETAINED_TRACES * 2) {
|
||||
this.events = this.events.slice(TARGET_RETAINED_TRACES);
|
||||
}
|
||||
this.eventsLogged += 1;
|
||||
}
|
||||
|
||||
getTraceMessage(): ChannelTrace {
|
||||
return {
|
||||
creation_timestamp: dateToProtoTimestamp(this.creationTimestamp),
|
||||
num_events_logged: this.eventsLogged,
|
||||
events: this.events.map(event => {
|
||||
return {
|
||||
description: event.description,
|
||||
severity: event.severity,
|
||||
timestamp: dateToProtoTimestamp(event.timestamp),
|
||||
channel_ref: event.childChannel
|
||||
? channelRefToMessage(event.childChannel)
|
||||
: null,
|
||||
subchannel_ref: event.childSubchannel
|
||||
? subchannelRefToMessage(event.childSubchannel)
|
||||
: null,
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class ChannelzChildrenTracker {
|
||||
private channelChildren: Map<number, { ref: ChannelRef; count: number }> =
|
||||
new Map<number, { ref: ChannelRef; count: number }>();
|
||||
private subchannelChildren: Map<
|
||||
number,
|
||||
{ ref: SubchannelRef; count: number }
|
||||
> = new Map<number, { ref: SubchannelRef; count: number }>();
|
||||
private socketChildren: Map<number, { ref: SocketRef; count: number }> =
|
||||
new Map<number, { ref: SocketRef; count: number }>();
|
||||
|
||||
refChild(child: ChannelRef | SubchannelRef | SocketRef) {
|
||||
switch (child.kind) {
|
||||
case 'channel': {
|
||||
const trackedChild = this.channelChildren.get(child.id) ?? {
|
||||
ref: child,
|
||||
count: 0,
|
||||
};
|
||||
trackedChild.count += 1;
|
||||
this.channelChildren.set(child.id, trackedChild);
|
||||
break;
|
||||
}
|
||||
case 'subchannel': {
|
||||
const trackedChild = this.subchannelChildren.get(child.id) ?? {
|
||||
ref: child,
|
||||
count: 0,
|
||||
};
|
||||
trackedChild.count += 1;
|
||||
this.subchannelChildren.set(child.id, trackedChild);
|
||||
break;
|
||||
}
|
||||
case 'socket': {
|
||||
const trackedChild = this.socketChildren.get(child.id) ?? {
|
||||
ref: child,
|
||||
count: 0,
|
||||
};
|
||||
trackedChild.count += 1;
|
||||
this.socketChildren.set(child.id, trackedChild);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unrefChild(child: ChannelRef | SubchannelRef | SocketRef) {
|
||||
switch (child.kind) {
|
||||
case 'channel': {
|
||||
const trackedChild = this.channelChildren.get(child.id);
|
||||
if (trackedChild !== undefined) {
|
||||
trackedChild.count -= 1;
|
||||
if (trackedChild.count === 0) {
|
||||
this.channelChildren.delete(child.id);
|
||||
} else {
|
||||
this.channelChildren.set(child.id, trackedChild);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'subchannel': {
|
||||
const trackedChild = this.subchannelChildren.get(child.id);
|
||||
if (trackedChild !== undefined) {
|
||||
trackedChild.count -= 1;
|
||||
if (trackedChild.count === 0) {
|
||||
this.subchannelChildren.delete(child.id);
|
||||
} else {
|
||||
this.subchannelChildren.set(child.id, trackedChild);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'socket': {
|
||||
const trackedChild = this.socketChildren.get(child.id);
|
||||
if (trackedChild !== undefined) {
|
||||
trackedChild.count -= 1;
|
||||
if (trackedChild.count === 0) {
|
||||
this.socketChildren.delete(child.id);
|
||||
} else {
|
||||
this.socketChildren.set(child.id, trackedChild);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getChildLists(): ChannelzChildren {
|
||||
const channels: ChannelRef[] = [];
|
||||
for (const { ref } of this.channelChildren.values()) {
|
||||
channels.push(ref);
|
||||
}
|
||||
const subchannels: SubchannelRef[] = [];
|
||||
for (const { ref } of this.subchannelChildren.values()) {
|
||||
subchannels.push(ref);
|
||||
}
|
||||
const sockets: SocketRef[] = [];
|
||||
for (const { ref } of this.socketChildren.values()) {
|
||||
sockets.push(ref);
|
||||
}
|
||||
return { channels, subchannels, sockets };
|
||||
}
|
||||
}
|
||||
|
||||
export class ChannelzCallTracker {
|
||||
callsStarted = 0;
|
||||
callsSucceeded = 0;
|
||||
callsFailed = 0;
|
||||
lastCallStartedTimestamp: Date | null = null;
|
||||
|
||||
addCallStarted() {
|
||||
this.callsStarted += 1;
|
||||
this.lastCallStartedTimestamp = new Date();
|
||||
}
|
||||
addCallSucceeded() {
|
||||
this.callsSucceeded += 1;
|
||||
}
|
||||
addCallFailed() {
|
||||
this.callsFailed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
export interface ChannelzChildren {
|
||||
channels: ChannelRef[];
|
||||
subchannels: SubchannelRef[];
|
||||
sockets: SocketRef[];
|
||||
}
|
||||
|
||||
export interface ChannelInfo {
|
||||
target: string;
|
||||
state: ConnectivityState;
|
||||
trace: ChannelzTrace;
|
||||
callTracker: ChannelzCallTracker;
|
||||
children: ChannelzChildren;
|
||||
}
|
||||
|
||||
export type SubchannelInfo = ChannelInfo;
|
||||
|
||||
export interface ServerInfo {
|
||||
trace: ChannelzTrace;
|
||||
callTracker: ChannelzCallTracker;
|
||||
listenerChildren: ChannelzChildren;
|
||||
sessionChildren: ChannelzChildren;
|
||||
}
|
||||
|
||||
export interface TlsInfo {
|
||||
cipherSuiteStandardName: string | null;
|
||||
cipherSuiteOtherName: string | null;
|
||||
localCertificate: Buffer | null;
|
||||
remoteCertificate: Buffer | null;
|
||||
}
|
||||
|
||||
export interface SocketInfo {
|
||||
localAddress: SubchannelAddress | null;
|
||||
remoteAddress: SubchannelAddress | null;
|
||||
security: TlsInfo | null;
|
||||
remoteName: string | null;
|
||||
streamsStarted: number;
|
||||
streamsSucceeded: number;
|
||||
streamsFailed: number;
|
||||
messagesSent: number;
|
||||
messagesReceived: number;
|
||||
keepAlivesSent: number;
|
||||
lastLocalStreamCreatedTimestamp: Date | null;
|
||||
lastRemoteStreamCreatedTimestamp: Date | null;
|
||||
lastMessageSentTimestamp: Date | null;
|
||||
lastMessageReceivedTimestamp: Date | null;
|
||||
localFlowControlWindow: number | null;
|
||||
remoteFlowControlWindow: number | null;
|
||||
}
|
||||
|
||||
interface ChannelEntry {
|
||||
ref: ChannelRef;
|
||||
getInfo(): ChannelInfo;
|
||||
}
|
||||
|
||||
interface SubchannelEntry {
|
||||
ref: SubchannelRef;
|
||||
getInfo(): SubchannelInfo;
|
||||
}
|
||||
|
||||
interface ServerEntry {
|
||||
ref: ServerRef;
|
||||
getInfo(): ServerInfo;
|
||||
}
|
||||
|
||||
interface SocketEntry {
|
||||
ref: SocketRef;
|
||||
getInfo(): SocketInfo;
|
||||
}
|
||||
|
||||
let nextId = 1;
|
||||
|
||||
function getNextId(): number {
|
||||
return nextId++;
|
||||
}
|
||||
|
||||
const channels: (ChannelEntry | undefined)[] = [];
|
||||
const subchannels: (SubchannelEntry | undefined)[] = [];
|
||||
const servers: (ServerEntry | undefined)[] = [];
|
||||
const sockets: (SocketEntry | undefined)[] = [];
|
||||
|
||||
export function registerChannelzChannel(
|
||||
name: string,
|
||||
getInfo: () => ChannelInfo,
|
||||
channelzEnabled: boolean
|
||||
): ChannelRef {
|
||||
const id = getNextId();
|
||||
const ref: ChannelRef = { id, name, kind: 'channel' };
|
||||
if (channelzEnabled) {
|
||||
channels[id] = { ref, getInfo };
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
export function registerChannelzSubchannel(
|
||||
name: string,
|
||||
getInfo: () => SubchannelInfo,
|
||||
channelzEnabled: boolean
|
||||
): SubchannelRef {
|
||||
const id = getNextId();
|
||||
const ref: SubchannelRef = { id, name, kind: 'subchannel' };
|
||||
if (channelzEnabled) {
|
||||
subchannels[id] = { ref, getInfo };
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
export function registerChannelzServer(
|
||||
getInfo: () => ServerInfo,
|
||||
channelzEnabled: boolean
|
||||
): ServerRef {
|
||||
const id = getNextId();
|
||||
const ref: ServerRef = { id, kind: 'server' };
|
||||
if (channelzEnabled) {
|
||||
servers[id] = { ref, getInfo };
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
export function registerChannelzSocket(
|
||||
name: string,
|
||||
getInfo: () => SocketInfo,
|
||||
channelzEnabled: boolean
|
||||
): SocketRef {
|
||||
const id = getNextId();
|
||||
const ref: SocketRef = { id, name, kind: 'socket' };
|
||||
if (channelzEnabled) {
|
||||
sockets[id] = { ref, getInfo };
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
export function unregisterChannelzRef(
|
||||
ref: ChannelRef | SubchannelRef | ServerRef | SocketRef
|
||||
) {
|
||||
switch (ref.kind) {
|
||||
case 'channel':
|
||||
delete channels[ref.id];
|
||||
return;
|
||||
case 'subchannel':
|
||||
delete subchannels[ref.id];
|
||||
return;
|
||||
case 'server':
|
||||
delete servers[ref.id];
|
||||
return;
|
||||
case 'socket':
|
||||
delete sockets[ref.id];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single section of an IPv6 address as two bytes
|
||||
* @param addressSection A hexadecimal string of length up to 4
|
||||
* @returns The pair of bytes representing this address section
|
||||
*/
|
||||
function parseIPv6Section(addressSection: string): [number, number] {
|
||||
const numberValue = Number.parseInt(addressSection, 16);
|
||||
return [(numberValue / 256) | 0, numberValue % 256];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a chunk of an IPv6 address string to some number of bytes
|
||||
* @param addressChunk Some number of segments of up to 4 hexadecimal
|
||||
* characters each, joined by colons.
|
||||
* @returns The list of bytes representing this address chunk
|
||||
*/
|
||||
function parseIPv6Chunk(addressChunk: string): number[] {
|
||||
if (addressChunk === '') {
|
||||
return [];
|
||||
}
|
||||
const bytePairs = addressChunk
|
||||
.split(':')
|
||||
.map(section => parseIPv6Section(section));
|
||||
const result: number[] = [];
|
||||
return result.concat(...bytePairs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an IPv4 or IPv6 address from string representation to binary
|
||||
* representation
|
||||
* @param ipAddress an IP address in standard IPv4 or IPv6 text format
|
||||
* @returns
|
||||
*/
|
||||
function ipAddressStringToBuffer(ipAddress: string): Buffer | null {
|
||||
if (isIPv4(ipAddress)) {
|
||||
return Buffer.from(
|
||||
Uint8Array.from(
|
||||
ipAddress.split('.').map(segment => Number.parseInt(segment))
|
||||
)
|
||||
);
|
||||
} else if (isIPv6(ipAddress)) {
|
||||
let leftSection: string;
|
||||
let rightSection: string;
|
||||
const doubleColonIndex = ipAddress.indexOf('::');
|
||||
if (doubleColonIndex === -1) {
|
||||
leftSection = ipAddress;
|
||||
rightSection = '';
|
||||
} else {
|
||||
leftSection = ipAddress.substring(0, doubleColonIndex);
|
||||
rightSection = ipAddress.substring(doubleColonIndex + 2);
|
||||
}
|
||||
const leftBuffer = Buffer.from(parseIPv6Chunk(leftSection));
|
||||
const rightBuffer = Buffer.from(parseIPv6Chunk(rightSection));
|
||||
const middleBuffer = Buffer.alloc(
|
||||
16 - leftBuffer.length - rightBuffer.length,
|
||||
0
|
||||
);
|
||||
return Buffer.concat([leftBuffer, middleBuffer, rightBuffer]);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function connectivityStateToMessage(
|
||||
state: ConnectivityState
|
||||
): ChannelConnectivityState__Output {
|
||||
switch (state) {
|
||||
case ConnectivityState.CONNECTING:
|
||||
return {
|
||||
state: 'CONNECTING',
|
||||
};
|
||||
case ConnectivityState.IDLE:
|
||||
return {
|
||||
state: 'IDLE',
|
||||
};
|
||||
case ConnectivityState.READY:
|
||||
return {
|
||||
state: 'READY',
|
||||
};
|
||||
case ConnectivityState.SHUTDOWN:
|
||||
return {
|
||||
state: 'SHUTDOWN',
|
||||
};
|
||||
case ConnectivityState.TRANSIENT_FAILURE:
|
||||
return {
|
||||
state: 'TRANSIENT_FAILURE',
|
||||
};
|
||||
default:
|
||||
return {
|
||||
state: 'UNKNOWN',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function dateToProtoTimestamp(date?: Date | null): Timestamp | null {
|
||||
if (!date) {
|
||||
return null;
|
||||
}
|
||||
const millisSinceEpoch = date.getTime();
|
||||
return {
|
||||
seconds: (millisSinceEpoch / 1000) | 0,
|
||||
nanos: (millisSinceEpoch % 1000) * 1_000_000,
|
||||
};
|
||||
}
|
||||
|
||||
function getChannelMessage(channelEntry: ChannelEntry): ChannelMessage {
|
||||
const resolvedInfo = channelEntry.getInfo();
|
||||
return {
|
||||
ref: channelRefToMessage(channelEntry.ref),
|
||||
data: {
|
||||
target: resolvedInfo.target,
|
||||
state: connectivityStateToMessage(resolvedInfo.state),
|
||||
calls_started: resolvedInfo.callTracker.callsStarted,
|
||||
calls_succeeded: resolvedInfo.callTracker.callsSucceeded,
|
||||
calls_failed: resolvedInfo.callTracker.callsFailed,
|
||||
last_call_started_timestamp: dateToProtoTimestamp(
|
||||
resolvedInfo.callTracker.lastCallStartedTimestamp
|
||||
),
|
||||
trace: resolvedInfo.trace.getTraceMessage(),
|
||||
},
|
||||
channel_ref: resolvedInfo.children.channels.map(ref =>
|
||||
channelRefToMessage(ref)
|
||||
),
|
||||
subchannel_ref: resolvedInfo.children.subchannels.map(ref =>
|
||||
subchannelRefToMessage(ref)
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
function GetChannel(
|
||||
call: ServerUnaryCall<GetChannelRequest__Output, GetChannelResponse>,
|
||||
callback: sendUnaryData<GetChannelResponse>
|
||||
): void {
|
||||
const channelId = Number.parseInt(call.request.channel_id);
|
||||
const channelEntry = channels[channelId];
|
||||
if (channelEntry === undefined) {
|
||||
callback({
|
||||
code: Status.NOT_FOUND,
|
||||
details: 'No channel data found for id ' + channelId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
callback(null, { channel: getChannelMessage(channelEntry) });
|
||||
}
|
||||
|
||||
function GetTopChannels(
|
||||
call: ServerUnaryCall<GetTopChannelsRequest__Output, GetTopChannelsResponse>,
|
||||
callback: sendUnaryData<GetTopChannelsResponse>
|
||||
): void {
|
||||
const maxResults = Number.parseInt(call.request.max_results);
|
||||
const resultList: ChannelMessage[] = [];
|
||||
let i = Number.parseInt(call.request.start_channel_id);
|
||||
for (; i < channels.length; i++) {
|
||||
const channelEntry = channels[i];
|
||||
if (channelEntry === undefined) {
|
||||
continue;
|
||||
}
|
||||
resultList.push(getChannelMessage(channelEntry));
|
||||
if (resultList.length >= maxResults) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
callback(null, {
|
||||
channel: resultList,
|
||||
end: i >= servers.length,
|
||||
});
|
||||
}
|
||||
|
||||
function getServerMessage(serverEntry: ServerEntry): ServerMessage {
|
||||
const resolvedInfo = serverEntry.getInfo();
|
||||
return {
|
||||
ref: serverRefToMessage(serverEntry.ref),
|
||||
data: {
|
||||
calls_started: resolvedInfo.callTracker.callsStarted,
|
||||
calls_succeeded: resolvedInfo.callTracker.callsSucceeded,
|
||||
calls_failed: resolvedInfo.callTracker.callsFailed,
|
||||
last_call_started_timestamp: dateToProtoTimestamp(
|
||||
resolvedInfo.callTracker.lastCallStartedTimestamp
|
||||
),
|
||||
trace: resolvedInfo.trace.getTraceMessage(),
|
||||
},
|
||||
listen_socket: resolvedInfo.listenerChildren.sockets.map(ref =>
|
||||
socketRefToMessage(ref)
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
function GetServer(
|
||||
call: ServerUnaryCall<GetServerRequest__Output, GetServerResponse>,
|
||||
callback: sendUnaryData<GetServerResponse>
|
||||
): void {
|
||||
const serverId = Number.parseInt(call.request.server_id);
|
||||
const serverEntry = servers[serverId];
|
||||
if (serverEntry === undefined) {
|
||||
callback({
|
||||
code: Status.NOT_FOUND,
|
||||
details: 'No server data found for id ' + serverId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
callback(null, { server: getServerMessage(serverEntry) });
|
||||
}
|
||||
|
||||
function GetServers(
|
||||
call: ServerUnaryCall<GetServersRequest__Output, GetServersResponse>,
|
||||
callback: sendUnaryData<GetServersResponse>
|
||||
): void {
|
||||
const maxResults = Number.parseInt(call.request.max_results);
|
||||
const resultList: ServerMessage[] = [];
|
||||
let i = Number.parseInt(call.request.start_server_id);
|
||||
for (; i < servers.length; i++) {
|
||||
const serverEntry = servers[i];
|
||||
if (serverEntry === undefined) {
|
||||
continue;
|
||||
}
|
||||
resultList.push(getServerMessage(serverEntry));
|
||||
if (resultList.length >= maxResults) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
callback(null, {
|
||||
server: resultList,
|
||||
end: i >= servers.length,
|
||||
});
|
||||
}
|
||||
|
||||
function GetSubchannel(
|
||||
call: ServerUnaryCall<GetSubchannelRequest__Output, GetSubchannelResponse>,
|
||||
callback: sendUnaryData<GetSubchannelResponse>
|
||||
): void {
|
||||
const subchannelId = Number.parseInt(call.request.subchannel_id);
|
||||
const subchannelEntry = subchannels[subchannelId];
|
||||
if (subchannelEntry === undefined) {
|
||||
callback({
|
||||
code: Status.NOT_FOUND,
|
||||
details: 'No subchannel data found for id ' + subchannelId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const resolvedInfo = subchannelEntry.getInfo();
|
||||
const subchannelMessage: SubchannelMessage = {
|
||||
ref: subchannelRefToMessage(subchannelEntry.ref),
|
||||
data: {
|
||||
target: resolvedInfo.target,
|
||||
state: connectivityStateToMessage(resolvedInfo.state),
|
||||
calls_started: resolvedInfo.callTracker.callsStarted,
|
||||
calls_succeeded: resolvedInfo.callTracker.callsSucceeded,
|
||||
calls_failed: resolvedInfo.callTracker.callsFailed,
|
||||
last_call_started_timestamp: dateToProtoTimestamp(
|
||||
resolvedInfo.callTracker.lastCallStartedTimestamp
|
||||
),
|
||||
trace: resolvedInfo.trace.getTraceMessage(),
|
||||
},
|
||||
socket_ref: resolvedInfo.children.sockets.map(ref =>
|
||||
socketRefToMessage(ref)
|
||||
),
|
||||
};
|
||||
callback(null, { subchannel: subchannelMessage });
|
||||
}
|
||||
|
||||
function subchannelAddressToAddressMessage(
|
||||
subchannelAddress: SubchannelAddress
|
||||
): Address {
|
||||
if (isTcpSubchannelAddress(subchannelAddress)) {
|
||||
return {
|
||||
address: 'tcpip_address',
|
||||
tcpip_address: {
|
||||
ip_address:
|
||||
ipAddressStringToBuffer(subchannelAddress.host) ?? undefined,
|
||||
port: subchannelAddress.port,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
address: 'uds_address',
|
||||
uds_address: {
|
||||
filename: subchannelAddress.path,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function GetSocket(
|
||||
call: ServerUnaryCall<GetSocketRequest__Output, GetSocketResponse>,
|
||||
callback: sendUnaryData<GetSocketResponse>
|
||||
): void {
|
||||
const socketId = Number.parseInt(call.request.socket_id);
|
||||
const socketEntry = sockets[socketId];
|
||||
if (socketEntry === undefined) {
|
||||
callback({
|
||||
code: Status.NOT_FOUND,
|
||||
details: 'No socket data found for id ' + socketId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const resolvedInfo = socketEntry.getInfo();
|
||||
const securityMessage: Security | null = resolvedInfo.security
|
||||
? {
|
||||
model: 'tls',
|
||||
tls: {
|
||||
cipher_suite: resolvedInfo.security.cipherSuiteStandardName
|
||||
? 'standard_name'
|
||||
: 'other_name',
|
||||
standard_name:
|
||||
resolvedInfo.security.cipherSuiteStandardName ?? undefined,
|
||||
other_name: resolvedInfo.security.cipherSuiteOtherName ?? undefined,
|
||||
local_certificate:
|
||||
resolvedInfo.security.localCertificate ?? undefined,
|
||||
remote_certificate:
|
||||
resolvedInfo.security.remoteCertificate ?? undefined,
|
||||
},
|
||||
}
|
||||
: null;
|
||||
const socketMessage: SocketMessage = {
|
||||
ref: socketRefToMessage(socketEntry.ref),
|
||||
local: resolvedInfo.localAddress
|
||||
? subchannelAddressToAddressMessage(resolvedInfo.localAddress)
|
||||
: null,
|
||||
remote: resolvedInfo.remoteAddress
|
||||
? subchannelAddressToAddressMessage(resolvedInfo.remoteAddress)
|
||||
: null,
|
||||
remote_name: resolvedInfo.remoteName ?? undefined,
|
||||
security: securityMessage,
|
||||
data: {
|
||||
keep_alives_sent: resolvedInfo.keepAlivesSent,
|
||||
streams_started: resolvedInfo.streamsStarted,
|
||||
streams_succeeded: resolvedInfo.streamsSucceeded,
|
||||
streams_failed: resolvedInfo.streamsFailed,
|
||||
last_local_stream_created_timestamp: dateToProtoTimestamp(
|
||||
resolvedInfo.lastLocalStreamCreatedTimestamp
|
||||
),
|
||||
last_remote_stream_created_timestamp: dateToProtoTimestamp(
|
||||
resolvedInfo.lastRemoteStreamCreatedTimestamp
|
||||
),
|
||||
messages_received: resolvedInfo.messagesReceived,
|
||||
messages_sent: resolvedInfo.messagesSent,
|
||||
last_message_received_timestamp: dateToProtoTimestamp(
|
||||
resolvedInfo.lastMessageReceivedTimestamp
|
||||
),
|
||||
last_message_sent_timestamp: dateToProtoTimestamp(
|
||||
resolvedInfo.lastMessageSentTimestamp
|
||||
),
|
||||
local_flow_control_window: resolvedInfo.localFlowControlWindow
|
||||
? { value: resolvedInfo.localFlowControlWindow }
|
||||
: null,
|
||||
remote_flow_control_window: resolvedInfo.remoteFlowControlWindow
|
||||
? { value: resolvedInfo.remoteFlowControlWindow }
|
||||
: null,
|
||||
},
|
||||
};
|
||||
callback(null, { socket: socketMessage });
|
||||
}
|
||||
|
||||
function GetServerSockets(
|
||||
call: ServerUnaryCall<
|
||||
GetServerSocketsRequest__Output,
|
||||
GetServerSocketsResponse
|
||||
>,
|
||||
callback: sendUnaryData<GetServerSocketsResponse>
|
||||
): void {
|
||||
const serverId = Number.parseInt(call.request.server_id);
|
||||
const serverEntry = servers[serverId];
|
||||
if (serverEntry === undefined) {
|
||||
callback({
|
||||
code: Status.NOT_FOUND,
|
||||
details: 'No server data found for id ' + serverId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const startId = Number.parseInt(call.request.start_socket_id);
|
||||
const maxResults = Number.parseInt(call.request.max_results);
|
||||
const resolvedInfo = serverEntry.getInfo();
|
||||
// If we wanted to include listener sockets in the result, this line would
|
||||
// instead say
|
||||
// const allSockets = resolvedInfo.listenerChildren.sockets.concat(resolvedInfo.sessionChildren.sockets).sort((ref1, ref2) => ref1.id - ref2.id);
|
||||
const allSockets = resolvedInfo.sessionChildren.sockets.sort(
|
||||
(ref1, ref2) => ref1.id - ref2.id
|
||||
);
|
||||
const resultList: SocketRefMessage[] = [];
|
||||
let i = 0;
|
||||
for (; i < allSockets.length; i++) {
|
||||
if (allSockets[i].id >= startId) {
|
||||
resultList.push(socketRefToMessage(allSockets[i]));
|
||||
if (resultList.length >= maxResults) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
callback(null, {
|
||||
socket_ref: resultList,
|
||||
end: i >= allSockets.length,
|
||||
});
|
||||
}
|
||||
|
||||
export function getChannelzHandlers(): ChannelzHandlers {
|
||||
return {
|
||||
GetChannel,
|
||||
GetTopChannels,
|
||||
GetServer,
|
||||
GetServers,
|
||||
GetSubchannel,
|
||||
GetSocket,
|
||||
GetServerSockets,
|
||||
};
|
||||
}
|
||||
|
||||
let loadedChannelzDefinition: ChannelzDefinition | null = null;
|
||||
|
||||
export function getChannelzServiceDefinition(): ChannelzDefinition {
|
||||
if (loadedChannelzDefinition) {
|
||||
return loadedChannelzDefinition;
|
||||
}
|
||||
/* The purpose of this complexity is to avoid loading @grpc/proto-loader at
|
||||
* runtime for users who will not use/enable channelz. */
|
||||
const loaderLoadSync = require('@grpc/proto-loader')
|
||||
.loadSync as typeof loadSync;
|
||||
const loadedProto = loaderLoadSync('channelz.proto', {
|
||||
keepCase: true,
|
||||
longs: String,
|
||||
enums: String,
|
||||
defaults: true,
|
||||
oneofs: true,
|
||||
includeDirs: [`${__dirname}/../../proto`],
|
||||
});
|
||||
const channelzGrpcObject = loadPackageDefinition(
|
||||
loadedProto
|
||||
) as unknown as ChannelzProtoGrpcType;
|
||||
loadedChannelzDefinition =
|
||||
channelzGrpcObject.grpc.channelz.v1.Channelz.service;
|
||||
return loadedChannelzDefinition;
|
||||
}
|
||||
|
||||
export function setup() {
|
||||
registerAdminService(getChannelzServiceDefinition, getChannelzHandlers);
|
||||
}
|
||||
577
node_modules/@grpc/grpc-js/src/client-interceptors.ts
generated
vendored
Normal file
577
node_modules/@grpc/grpc-js/src/client-interceptors.ts
generated
vendored
Normal file
@@ -0,0 +1,577 @@
|
||||
/*
|
||||
* 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 { Metadata } from './metadata';
|
||||
import {
|
||||
StatusObject,
|
||||
Listener,
|
||||
MetadataListener,
|
||||
MessageListener,
|
||||
StatusListener,
|
||||
FullListener,
|
||||
InterceptingListener,
|
||||
InterceptingListenerImpl,
|
||||
isInterceptingListener,
|
||||
MessageContext,
|
||||
Call,
|
||||
} from './call-interface';
|
||||
import { Status } from './constants';
|
||||
import { Channel } from './channel';
|
||||
import { CallOptions } from './client';
|
||||
import { ClientMethodDefinition } from './make-client';
|
||||
import { getErrorMessage } from './error';
|
||||
|
||||
/**
|
||||
* Error class associated with passing both interceptors and interceptor
|
||||
* providers to a client constructor or as call options.
|
||||
*/
|
||||
export class InterceptorConfigurationError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = 'InterceptorConfigurationError';
|
||||
Error.captureStackTrace(this, InterceptorConfigurationError);
|
||||
}
|
||||
}
|
||||
|
||||
export interface MetadataRequester {
|
||||
(
|
||||
metadata: Metadata,
|
||||
listener: InterceptingListener,
|
||||
next: (
|
||||
metadata: Metadata,
|
||||
listener: InterceptingListener | Listener
|
||||
) => void
|
||||
): void;
|
||||
}
|
||||
|
||||
export interface MessageRequester {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(message: any, next: (message: any) => void): void;
|
||||
}
|
||||
|
||||
export interface CloseRequester {
|
||||
(next: () => void): void;
|
||||
}
|
||||
|
||||
export interface CancelRequester {
|
||||
(next: () => void): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* An object with methods for intercepting and modifying outgoing call operations.
|
||||
*/
|
||||
export interface FullRequester {
|
||||
start: MetadataRequester;
|
||||
sendMessage: MessageRequester;
|
||||
halfClose: CloseRequester;
|
||||
cancel: CancelRequester;
|
||||
}
|
||||
|
||||
export type Requester = Partial<FullRequester>;
|
||||
|
||||
export class ListenerBuilder {
|
||||
private metadata: MetadataListener | undefined = undefined;
|
||||
private message: MessageListener | undefined = undefined;
|
||||
private status: StatusListener | undefined = undefined;
|
||||
|
||||
withOnReceiveMetadata(onReceiveMetadata: MetadataListener): this {
|
||||
this.metadata = onReceiveMetadata;
|
||||
return this;
|
||||
}
|
||||
|
||||
withOnReceiveMessage(onReceiveMessage: MessageListener): this {
|
||||
this.message = onReceiveMessage;
|
||||
return this;
|
||||
}
|
||||
|
||||
withOnReceiveStatus(onReceiveStatus: StatusListener): this {
|
||||
this.status = onReceiveStatus;
|
||||
return this;
|
||||
}
|
||||
|
||||
build(): Listener {
|
||||
return {
|
||||
onReceiveMetadata: this.metadata,
|
||||
onReceiveMessage: this.message,
|
||||
onReceiveStatus: this.status,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class RequesterBuilder {
|
||||
private start: MetadataRequester | undefined = undefined;
|
||||
private message: MessageRequester | undefined = undefined;
|
||||
private halfClose: CloseRequester | undefined = undefined;
|
||||
private cancel: CancelRequester | undefined = undefined;
|
||||
|
||||
withStart(start: MetadataRequester): this {
|
||||
this.start = start;
|
||||
return this;
|
||||
}
|
||||
|
||||
withSendMessage(sendMessage: MessageRequester): this {
|
||||
this.message = sendMessage;
|
||||
return this;
|
||||
}
|
||||
|
||||
withHalfClose(halfClose: CloseRequester): this {
|
||||
this.halfClose = halfClose;
|
||||
return this;
|
||||
}
|
||||
|
||||
withCancel(cancel: CancelRequester): this {
|
||||
this.cancel = cancel;
|
||||
return this;
|
||||
}
|
||||
|
||||
build(): Requester {
|
||||
return {
|
||||
start: this.start,
|
||||
sendMessage: this.message,
|
||||
halfClose: this.halfClose,
|
||||
cancel: this.cancel,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Listener with a default pass-through implementation of each method. Used
|
||||
* for filling out Listeners with some methods omitted.
|
||||
*/
|
||||
const defaultListener: FullListener = {
|
||||
onReceiveMetadata: (metadata, next) => {
|
||||
next(metadata);
|
||||
},
|
||||
onReceiveMessage: (message, next) => {
|
||||
next(message);
|
||||
},
|
||||
onReceiveStatus: (status, next) => {
|
||||
next(status);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* A Requester with a default pass-through implementation of each method. Used
|
||||
* for filling out Requesters with some methods omitted.
|
||||
*/
|
||||
const defaultRequester: FullRequester = {
|
||||
start: (metadata, listener, next) => {
|
||||
next(metadata, listener);
|
||||
},
|
||||
sendMessage: (message, next) => {
|
||||
next(message);
|
||||
},
|
||||
halfClose: next => {
|
||||
next();
|
||||
},
|
||||
cancel: next => {
|
||||
next();
|
||||
},
|
||||
};
|
||||
|
||||
export interface InterceptorOptions extends CallOptions {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
method_definition: ClientMethodDefinition<any, any>;
|
||||
}
|
||||
|
||||
export interface InterceptingCallInterface {
|
||||
cancelWithStatus(status: Status, details: string): void;
|
||||
getPeer(): string;
|
||||
start(metadata: Metadata, listener?: Partial<InterceptingListener>): void;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
sendMessageWithContext(context: MessageContext, message: any): void;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
sendMessage(message: any): void;
|
||||
startRead(): void;
|
||||
halfClose(): void;
|
||||
}
|
||||
|
||||
export class InterceptingCall implements InterceptingCallInterface {
|
||||
/**
|
||||
* The requester that this InterceptingCall uses to modify outgoing operations
|
||||
*/
|
||||
private requester: FullRequester;
|
||||
/**
|
||||
* Indicates that metadata has been passed to the requester's start
|
||||
* method but it has not been passed to the corresponding next callback
|
||||
*/
|
||||
private processingMetadata = false;
|
||||
/**
|
||||
* Message context for a pending message that is waiting for
|
||||
*/
|
||||
private pendingMessageContext: MessageContext | null = null;
|
||||
private pendingMessage: any;
|
||||
/**
|
||||
* Indicates that a message has been passed to the requester's sendMessage
|
||||
* method but it has not been passed to the corresponding next callback
|
||||
*/
|
||||
private processingMessage = false;
|
||||
/**
|
||||
* Indicates that a status was received but could not be propagated because
|
||||
* a message was still being processed.
|
||||
*/
|
||||
private pendingHalfClose = false;
|
||||
constructor(
|
||||
private nextCall: InterceptingCallInterface,
|
||||
requester?: Requester
|
||||
) {
|
||||
if (requester) {
|
||||
this.requester = {
|
||||
start: requester.start ?? defaultRequester.start,
|
||||
sendMessage: requester.sendMessage ?? defaultRequester.sendMessage,
|
||||
halfClose: requester.halfClose ?? defaultRequester.halfClose,
|
||||
cancel: requester.cancel ?? defaultRequester.cancel,
|
||||
};
|
||||
} else {
|
||||
this.requester = defaultRequester;
|
||||
}
|
||||
}
|
||||
|
||||
cancelWithStatus(status: Status, details: string) {
|
||||
this.requester.cancel(() => {
|
||||
this.nextCall.cancelWithStatus(status, details);
|
||||
});
|
||||
}
|
||||
|
||||
getPeer() {
|
||||
return this.nextCall.getPeer();
|
||||
}
|
||||
|
||||
private processPendingMessage() {
|
||||
if (this.pendingMessageContext) {
|
||||
this.nextCall.sendMessageWithContext(
|
||||
this.pendingMessageContext,
|
||||
this.pendingMessage
|
||||
);
|
||||
this.pendingMessageContext = null;
|
||||
this.pendingMessage = null;
|
||||
}
|
||||
}
|
||||
|
||||
private processPendingHalfClose() {
|
||||
if (this.pendingHalfClose) {
|
||||
this.nextCall.halfClose();
|
||||
}
|
||||
}
|
||||
|
||||
start(
|
||||
metadata: Metadata,
|
||||
interceptingListener?: Partial<InterceptingListener>
|
||||
): void {
|
||||
const fullInterceptingListener: InterceptingListener = {
|
||||
onReceiveMetadata:
|
||||
interceptingListener?.onReceiveMetadata?.bind(interceptingListener) ??
|
||||
(metadata => {}),
|
||||
onReceiveMessage:
|
||||
interceptingListener?.onReceiveMessage?.bind(interceptingListener) ??
|
||||
(message => {}),
|
||||
onReceiveStatus:
|
||||
interceptingListener?.onReceiveStatus?.bind(interceptingListener) ??
|
||||
(status => {}),
|
||||
};
|
||||
this.processingMetadata = true;
|
||||
this.requester.start(metadata, fullInterceptingListener, (md, listener) => {
|
||||
this.processingMetadata = false;
|
||||
let finalInterceptingListener: InterceptingListener;
|
||||
if (isInterceptingListener(listener)) {
|
||||
finalInterceptingListener = listener;
|
||||
} else {
|
||||
const fullListener: FullListener = {
|
||||
onReceiveMetadata:
|
||||
listener.onReceiveMetadata ?? defaultListener.onReceiveMetadata,
|
||||
onReceiveMessage:
|
||||
listener.onReceiveMessage ?? defaultListener.onReceiveMessage,
|
||||
onReceiveStatus:
|
||||
listener.onReceiveStatus ?? defaultListener.onReceiveStatus,
|
||||
};
|
||||
finalInterceptingListener = new InterceptingListenerImpl(
|
||||
fullListener,
|
||||
fullInterceptingListener
|
||||
);
|
||||
}
|
||||
this.nextCall.start(md, finalInterceptingListener);
|
||||
this.processPendingMessage();
|
||||
this.processPendingHalfClose();
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
sendMessageWithContext(context: MessageContext, message: any): void {
|
||||
this.processingMessage = true;
|
||||
this.requester.sendMessage(message, finalMessage => {
|
||||
this.processingMessage = false;
|
||||
if (this.processingMetadata) {
|
||||
this.pendingMessageContext = context;
|
||||
this.pendingMessage = message;
|
||||
} else {
|
||||
this.nextCall.sendMessageWithContext(context, finalMessage);
|
||||
this.processPendingHalfClose();
|
||||
}
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
sendMessage(message: any): void {
|
||||
this.sendMessageWithContext({}, message);
|
||||
}
|
||||
startRead(): void {
|
||||
this.nextCall.startRead();
|
||||
}
|
||||
halfClose(): void {
|
||||
this.requester.halfClose(() => {
|
||||
if (this.processingMetadata || this.processingMessage) {
|
||||
this.pendingHalfClose = true;
|
||||
} else {
|
||||
this.nextCall.halfClose();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getCall(channel: Channel, path: string, options: CallOptions): Call {
|
||||
const deadline = options.deadline ?? Infinity;
|
||||
const host = options.host;
|
||||
const parent = options.parent ?? null;
|
||||
const propagateFlags = options.propagate_flags;
|
||||
const credentials = options.credentials;
|
||||
const call = channel.createCall(path, deadline, host, parent, propagateFlags);
|
||||
if (credentials) {
|
||||
call.setCredentials(credentials);
|
||||
}
|
||||
return call;
|
||||
}
|
||||
|
||||
/**
|
||||
* InterceptingCall implementation that directly owns the underlying Call
|
||||
* object and handles serialization and deseraizliation.
|
||||
*/
|
||||
class BaseInterceptingCall implements InterceptingCallInterface {
|
||||
constructor(
|
||||
protected call: Call,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
protected methodDefinition: ClientMethodDefinition<any, any>
|
||||
) {}
|
||||
cancelWithStatus(status: Status, details: string): void {
|
||||
this.call.cancelWithStatus(status, details);
|
||||
}
|
||||
getPeer(): string {
|
||||
return this.call.getPeer();
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
sendMessageWithContext(context: MessageContext, message: any): void {
|
||||
let serialized: Buffer;
|
||||
try {
|
||||
serialized = this.methodDefinition.requestSerialize(message);
|
||||
} catch (e) {
|
||||
this.call.cancelWithStatus(
|
||||
Status.INTERNAL,
|
||||
`Request message serialization failure: ${getErrorMessage(e)}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
this.call.sendMessageWithContext(context, serialized);
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
sendMessage(message: any) {
|
||||
this.sendMessageWithContext({}, message);
|
||||
}
|
||||
start(
|
||||
metadata: Metadata,
|
||||
interceptingListener?: Partial<InterceptingListener>
|
||||
): void {
|
||||
let readError: StatusObject | null = null;
|
||||
this.call.start(metadata, {
|
||||
onReceiveMetadata: metadata => {
|
||||
interceptingListener?.onReceiveMetadata?.(metadata);
|
||||
},
|
||||
onReceiveMessage: message => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let deserialized: any;
|
||||
try {
|
||||
deserialized = this.methodDefinition.responseDeserialize(message);
|
||||
} catch (e) {
|
||||
readError = {
|
||||
code: Status.INTERNAL,
|
||||
details: `Response message parsing error: ${getErrorMessage(e)}`,
|
||||
metadata: new Metadata(),
|
||||
};
|
||||
this.call.cancelWithStatus(readError.code, readError.details);
|
||||
return;
|
||||
}
|
||||
interceptingListener?.onReceiveMessage?.(deserialized);
|
||||
},
|
||||
onReceiveStatus: status => {
|
||||
if (readError) {
|
||||
interceptingListener?.onReceiveStatus?.(readError);
|
||||
} else {
|
||||
interceptingListener?.onReceiveStatus?.(status);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
startRead() {
|
||||
this.call.startRead();
|
||||
}
|
||||
halfClose(): void {
|
||||
this.call.halfClose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* BaseInterceptingCall with special-cased behavior for methods with unary
|
||||
* responses.
|
||||
*/
|
||||
class BaseUnaryInterceptingCall
|
||||
extends BaseInterceptingCall
|
||||
implements InterceptingCallInterface
|
||||
{
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
constructor(call: Call, methodDefinition: ClientMethodDefinition<any, any>) {
|
||||
super(call, methodDefinition);
|
||||
}
|
||||
start(metadata: Metadata, listener?: Partial<InterceptingListener>): void {
|
||||
let receivedMessage = false;
|
||||
const wrapperListener: InterceptingListener = {
|
||||
onReceiveMetadata:
|
||||
listener?.onReceiveMetadata?.bind(listener) ?? (metadata => {}),
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
onReceiveMessage: (message: any) => {
|
||||
receivedMessage = true;
|
||||
listener?.onReceiveMessage?.(message);
|
||||
},
|
||||
onReceiveStatus: (status: StatusObject) => {
|
||||
if (!receivedMessage) {
|
||||
listener?.onReceiveMessage?.(null);
|
||||
}
|
||||
listener?.onReceiveStatus?.(status);
|
||||
},
|
||||
};
|
||||
super.start(metadata, wrapperListener);
|
||||
this.call.startRead();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* BaseInterceptingCall with special-cased behavior for methods with streaming
|
||||
* responses.
|
||||
*/
|
||||
class BaseStreamingInterceptingCall
|
||||
extends BaseInterceptingCall
|
||||
implements InterceptingCallInterface {}
|
||||
|
||||
function getBottomInterceptingCall(
|
||||
channel: Channel,
|
||||
options: InterceptorOptions,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
methodDefinition: ClientMethodDefinition<any, any>
|
||||
) {
|
||||
const call = getCall(channel, methodDefinition.path, options);
|
||||
if (methodDefinition.responseStream) {
|
||||
return new BaseStreamingInterceptingCall(call, methodDefinition);
|
||||
} else {
|
||||
return new BaseUnaryInterceptingCall(call, methodDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
export interface NextCall {
|
||||
(options: InterceptorOptions): InterceptingCallInterface;
|
||||
}
|
||||
|
||||
export interface Interceptor {
|
||||
(options: InterceptorOptions, nextCall: NextCall): InterceptingCall;
|
||||
}
|
||||
|
||||
export interface InterceptorProvider {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(methodDefinition: ClientMethodDefinition<any, any>): Interceptor;
|
||||
}
|
||||
|
||||
export interface InterceptorArguments {
|
||||
clientInterceptors: Interceptor[];
|
||||
clientInterceptorProviders: InterceptorProvider[];
|
||||
callInterceptors: Interceptor[];
|
||||
callInterceptorProviders: InterceptorProvider[];
|
||||
}
|
||||
|
||||
export function getInterceptingCall(
|
||||
interceptorArgs: InterceptorArguments,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
methodDefinition: ClientMethodDefinition<any, any>,
|
||||
options: CallOptions,
|
||||
channel: Channel
|
||||
): InterceptingCallInterface {
|
||||
if (
|
||||
interceptorArgs.clientInterceptors.length > 0 &&
|
||||
interceptorArgs.clientInterceptorProviders.length > 0
|
||||
) {
|
||||
throw new InterceptorConfigurationError(
|
||||
'Both interceptors and interceptor_providers were passed as options ' +
|
||||
'to the client constructor. Only one of these is allowed.'
|
||||
);
|
||||
}
|
||||
if (
|
||||
interceptorArgs.callInterceptors.length > 0 &&
|
||||
interceptorArgs.callInterceptorProviders.length > 0
|
||||
) {
|
||||
throw new InterceptorConfigurationError(
|
||||
'Both interceptors and interceptor_providers were passed as call ' +
|
||||
'options. Only one of these is allowed.'
|
||||
);
|
||||
}
|
||||
let interceptors: Interceptor[] = [];
|
||||
// Interceptors passed to the call override interceptors passed to the client constructor
|
||||
if (
|
||||
interceptorArgs.callInterceptors.length > 0 ||
|
||||
interceptorArgs.callInterceptorProviders.length > 0
|
||||
) {
|
||||
interceptors = ([] as Interceptor[])
|
||||
.concat(
|
||||
interceptorArgs.callInterceptors,
|
||||
interceptorArgs.callInterceptorProviders.map(provider =>
|
||||
provider(methodDefinition)
|
||||
)
|
||||
)
|
||||
.filter(interceptor => interceptor);
|
||||
// Filter out falsy values when providers return nothing
|
||||
} else {
|
||||
interceptors = ([] as Interceptor[])
|
||||
.concat(
|
||||
interceptorArgs.clientInterceptors,
|
||||
interceptorArgs.clientInterceptorProviders.map(provider =>
|
||||
provider(methodDefinition)
|
||||
)
|
||||
)
|
||||
.filter(interceptor => interceptor);
|
||||
// Filter out falsy values when providers return nothing
|
||||
}
|
||||
const interceptorOptions = Object.assign({}, options, {
|
||||
method_definition: methodDefinition,
|
||||
});
|
||||
/* For each interceptor in the list, the nextCall function passed to it is
|
||||
* based on the next interceptor in the list, using a nextCall function
|
||||
* constructed with the following interceptor in the list, and so on. The
|
||||
* initialValue, which is effectively at the end of the list, is a nextCall
|
||||
* function that invokes getBottomInterceptingCall, the result of which
|
||||
* handles (de)serialization and also gets the underlying call from the
|
||||
* channel. */
|
||||
const getCall: NextCall = interceptors.reduceRight<NextCall>(
|
||||
(nextCall: NextCall, nextInterceptor: Interceptor) => {
|
||||
return currentOptions => nextInterceptor(currentOptions, nextCall);
|
||||
},
|
||||
(finalOptions: InterceptorOptions) =>
|
||||
getBottomInterceptingCall(channel, finalOptions, methodDefinition)
|
||||
);
|
||||
return getCall(interceptorOptions);
|
||||
}
|
||||
715
node_modules/@grpc/grpc-js/src/client.ts
generated
vendored
Normal file
715
node_modules/@grpc/grpc-js/src/client.ts
generated
vendored
Normal file
@@ -0,0 +1,715 @@
|
||||
/*
|
||||
* 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 {
|
||||
ClientDuplexStream,
|
||||
ClientDuplexStreamImpl,
|
||||
ClientReadableStream,
|
||||
ClientReadableStreamImpl,
|
||||
ClientUnaryCall,
|
||||
ClientUnaryCallImpl,
|
||||
ClientWritableStream,
|
||||
ClientWritableStreamImpl,
|
||||
ServiceError,
|
||||
callErrorFromStatus,
|
||||
SurfaceCall,
|
||||
} from './call';
|
||||
import { CallCredentials } from './call-credentials';
|
||||
import { StatusObject } from './call-interface';
|
||||
import { Channel, ChannelImplementation } from './channel';
|
||||
import { ConnectivityState } from './connectivity-state';
|
||||
import { ChannelCredentials } from './channel-credentials';
|
||||
import { ChannelOptions } from './channel-options';
|
||||
import { Status } from './constants';
|
||||
import { Metadata } from './metadata';
|
||||
import { ClientMethodDefinition } from './make-client';
|
||||
import {
|
||||
getInterceptingCall,
|
||||
Interceptor,
|
||||
InterceptorProvider,
|
||||
InterceptorArguments,
|
||||
InterceptingCallInterface,
|
||||
} from './client-interceptors';
|
||||
import {
|
||||
ServerUnaryCall,
|
||||
ServerReadableStream,
|
||||
ServerWritableStream,
|
||||
ServerDuplexStream,
|
||||
} from './server-call';
|
||||
import { Deadline } from './deadline';
|
||||
|
||||
const CHANNEL_SYMBOL = Symbol();
|
||||
const INTERCEPTOR_SYMBOL = Symbol();
|
||||
const INTERCEPTOR_PROVIDER_SYMBOL = Symbol();
|
||||
const CALL_INVOCATION_TRANSFORMER_SYMBOL = Symbol();
|
||||
|
||||
function isFunction<ResponseType>(
|
||||
arg: Metadata | CallOptions | UnaryCallback<ResponseType> | undefined
|
||||
): arg is UnaryCallback<ResponseType> {
|
||||
return typeof arg === 'function';
|
||||
}
|
||||
|
||||
export interface UnaryCallback<ResponseType> {
|
||||
(err: ServiceError | null, value?: ResponseType): void;
|
||||
}
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
export interface CallOptions {
|
||||
deadline?: Deadline;
|
||||
host?: string;
|
||||
parent?:
|
||||
| ServerUnaryCall<any, any>
|
||||
| ServerReadableStream<any, any>
|
||||
| ServerWritableStream<any, any>
|
||||
| ServerDuplexStream<any, any>;
|
||||
propagate_flags?: number;
|
||||
credentials?: CallCredentials;
|
||||
interceptors?: Interceptor[];
|
||||
interceptor_providers?: InterceptorProvider[];
|
||||
}
|
||||
/* eslint-enable @typescript-eslint/no-explicit-any */
|
||||
|
||||
export interface CallProperties<RequestType, ResponseType> {
|
||||
argument?: RequestType;
|
||||
metadata: Metadata;
|
||||
call: SurfaceCall;
|
||||
channel: Channel;
|
||||
methodDefinition: ClientMethodDefinition<RequestType, ResponseType>;
|
||||
callOptions: CallOptions;
|
||||
callback?: UnaryCallback<ResponseType>;
|
||||
}
|
||||
|
||||
export interface CallInvocationTransformer {
|
||||
(callProperties: CallProperties<any, any>): CallProperties<any, any>; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
}
|
||||
|
||||
export type ClientOptions = Partial<ChannelOptions> & {
|
||||
channelOverride?: Channel;
|
||||
channelFactoryOverride?: (
|
||||
address: string,
|
||||
credentials: ChannelCredentials,
|
||||
options: ClientOptions
|
||||
) => Channel;
|
||||
interceptors?: Interceptor[];
|
||||
interceptor_providers?: InterceptorProvider[];
|
||||
callInvocationTransformer?: CallInvocationTransformer;
|
||||
};
|
||||
|
||||
function getErrorStackString(error: Error): string {
|
||||
return error.stack!.split('\n').slice(1).join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic gRPC client. Primarily useful as a base class for all generated
|
||||
* clients.
|
||||
*/
|
||||
export class Client {
|
||||
private readonly [CHANNEL_SYMBOL]: Channel;
|
||||
private readonly [INTERCEPTOR_SYMBOL]: Interceptor[];
|
||||
private readonly [INTERCEPTOR_PROVIDER_SYMBOL]: InterceptorProvider[];
|
||||
private readonly [CALL_INVOCATION_TRANSFORMER_SYMBOL]?: CallInvocationTransformer;
|
||||
constructor(
|
||||
address: string,
|
||||
credentials: ChannelCredentials,
|
||||
options: ClientOptions = {}
|
||||
) {
|
||||
options = Object.assign({}, options);
|
||||
this[INTERCEPTOR_SYMBOL] = options.interceptors ?? [];
|
||||
delete options.interceptors;
|
||||
this[INTERCEPTOR_PROVIDER_SYMBOL] = options.interceptor_providers ?? [];
|
||||
delete options.interceptor_providers;
|
||||
if (
|
||||
this[INTERCEPTOR_SYMBOL].length > 0 &&
|
||||
this[INTERCEPTOR_PROVIDER_SYMBOL].length > 0
|
||||
) {
|
||||
throw new Error(
|
||||
'Both interceptors and interceptor_providers were passed as options ' +
|
||||
'to the client constructor. Only one of these is allowed.'
|
||||
);
|
||||
}
|
||||
this[CALL_INVOCATION_TRANSFORMER_SYMBOL] =
|
||||
options.callInvocationTransformer;
|
||||
delete options.callInvocationTransformer;
|
||||
if (options.channelOverride) {
|
||||
this[CHANNEL_SYMBOL] = options.channelOverride;
|
||||
} else if (options.channelFactoryOverride) {
|
||||
const channelFactoryOverride = options.channelFactoryOverride;
|
||||
delete options.channelFactoryOverride;
|
||||
this[CHANNEL_SYMBOL] = channelFactoryOverride(
|
||||
address,
|
||||
credentials,
|
||||
options
|
||||
);
|
||||
} else {
|
||||
this[CHANNEL_SYMBOL] = new ChannelImplementation(
|
||||
address,
|
||||
credentials,
|
||||
options
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
close(): void {
|
||||
this[CHANNEL_SYMBOL].close();
|
||||
}
|
||||
|
||||
getChannel(): Channel {
|
||||
return this[CHANNEL_SYMBOL];
|
||||
}
|
||||
|
||||
waitForReady(deadline: Deadline, callback: (error?: Error) => void): void {
|
||||
const checkState = (err?: Error) => {
|
||||
if (err) {
|
||||
callback(new Error('Failed to connect before the deadline'));
|
||||
return;
|
||||
}
|
||||
let newState;
|
||||
try {
|
||||
newState = this[CHANNEL_SYMBOL].getConnectivityState(true);
|
||||
} catch (e) {
|
||||
callback(new Error('The channel has been closed'));
|
||||
return;
|
||||
}
|
||||
if (newState === ConnectivityState.READY) {
|
||||
callback();
|
||||
} else {
|
||||
try {
|
||||
this[CHANNEL_SYMBOL].watchConnectivityState(
|
||||
newState,
|
||||
deadline,
|
||||
checkState
|
||||
);
|
||||
} catch (e) {
|
||||
callback(new Error('The channel has been closed'));
|
||||
}
|
||||
}
|
||||
};
|
||||
setImmediate(checkState);
|
||||
}
|
||||
|
||||
private checkOptionalUnaryResponseArguments<ResponseType>(
|
||||
arg1: Metadata | CallOptions | UnaryCallback<ResponseType>,
|
||||
arg2?: CallOptions | UnaryCallback<ResponseType>,
|
||||
arg3?: UnaryCallback<ResponseType>
|
||||
): {
|
||||
metadata: Metadata;
|
||||
options: CallOptions;
|
||||
callback: UnaryCallback<ResponseType>;
|
||||
} {
|
||||
if (isFunction(arg1)) {
|
||||
return { metadata: new Metadata(), options: {}, callback: arg1 };
|
||||
} else if (isFunction(arg2)) {
|
||||
if (arg1 instanceof Metadata) {
|
||||
return { metadata: arg1, options: {}, callback: arg2 };
|
||||
} else {
|
||||
return { metadata: new Metadata(), options: arg1, callback: arg2 };
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
!(
|
||||
arg1 instanceof Metadata &&
|
||||
arg2 instanceof Object &&
|
||||
isFunction(arg3)
|
||||
)
|
||||
) {
|
||||
throw new Error('Incorrect arguments passed');
|
||||
}
|
||||
return { metadata: arg1, options: arg2, callback: arg3 };
|
||||
}
|
||||
}
|
||||
|
||||
makeUnaryRequest<RequestType, ResponseType>(
|
||||
method: string,
|
||||
serialize: (value: RequestType) => Buffer,
|
||||
deserialize: (value: Buffer) => ResponseType,
|
||||
argument: RequestType,
|
||||
metadata: Metadata,
|
||||
options: CallOptions,
|
||||
callback: UnaryCallback<ResponseType>
|
||||
): ClientUnaryCall;
|
||||
makeUnaryRequest<RequestType, ResponseType>(
|
||||
method: string,
|
||||
serialize: (value: RequestType) => Buffer,
|
||||
deserialize: (value: Buffer) => ResponseType,
|
||||
argument: RequestType,
|
||||
metadata: Metadata,
|
||||
callback: UnaryCallback<ResponseType>
|
||||
): ClientUnaryCall;
|
||||
makeUnaryRequest<RequestType, ResponseType>(
|
||||
method: string,
|
||||
serialize: (value: RequestType) => Buffer,
|
||||
deserialize: (value: Buffer) => ResponseType,
|
||||
argument: RequestType,
|
||||
options: CallOptions,
|
||||
callback: UnaryCallback<ResponseType>
|
||||
): ClientUnaryCall;
|
||||
makeUnaryRequest<RequestType, ResponseType>(
|
||||
method: string,
|
||||
serialize: (value: RequestType) => Buffer,
|
||||
deserialize: (value: Buffer) => ResponseType,
|
||||
argument: RequestType,
|
||||
callback: UnaryCallback<ResponseType>
|
||||
): ClientUnaryCall;
|
||||
makeUnaryRequest<RequestType, ResponseType>(
|
||||
method: string,
|
||||
serialize: (value: RequestType) => Buffer,
|
||||
deserialize: (value: Buffer) => ResponseType,
|
||||
argument: RequestType,
|
||||
metadata: Metadata | CallOptions | UnaryCallback<ResponseType>,
|
||||
options?: CallOptions | UnaryCallback<ResponseType>,
|
||||
callback?: UnaryCallback<ResponseType>
|
||||
): ClientUnaryCall {
|
||||
const checkedArguments =
|
||||
this.checkOptionalUnaryResponseArguments<ResponseType>(
|
||||
metadata,
|
||||
options,
|
||||
callback
|
||||
);
|
||||
const methodDefinition: ClientMethodDefinition<RequestType, ResponseType> =
|
||||
{
|
||||
path: method,
|
||||
requestStream: false,
|
||||
responseStream: false,
|
||||
requestSerialize: serialize,
|
||||
responseDeserialize: deserialize,
|
||||
};
|
||||
let callProperties: CallProperties<RequestType, ResponseType> = {
|
||||
argument: argument,
|
||||
metadata: checkedArguments.metadata,
|
||||
call: new ClientUnaryCallImpl(),
|
||||
channel: this[CHANNEL_SYMBOL],
|
||||
methodDefinition: methodDefinition,
|
||||
callOptions: checkedArguments.options,
|
||||
callback: checkedArguments.callback,
|
||||
};
|
||||
if (this[CALL_INVOCATION_TRANSFORMER_SYMBOL]) {
|
||||
callProperties = this[CALL_INVOCATION_TRANSFORMER_SYMBOL]!(
|
||||
callProperties
|
||||
) as CallProperties<RequestType, ResponseType>;
|
||||
}
|
||||
const emitter: ClientUnaryCall = callProperties.call;
|
||||
const interceptorArgs: InterceptorArguments = {
|
||||
clientInterceptors: this[INTERCEPTOR_SYMBOL],
|
||||
clientInterceptorProviders: this[INTERCEPTOR_PROVIDER_SYMBOL],
|
||||
callInterceptors: callProperties.callOptions.interceptors ?? [],
|
||||
callInterceptorProviders:
|
||||
callProperties.callOptions.interceptor_providers ?? [],
|
||||
};
|
||||
const call: InterceptingCallInterface = getInterceptingCall(
|
||||
interceptorArgs,
|
||||
callProperties.methodDefinition,
|
||||
callProperties.callOptions,
|
||||
callProperties.channel
|
||||
);
|
||||
/* This needs to happen before the emitter is used. Unfortunately we can't
|
||||
* enforce this with the type system. We need to construct this emitter
|
||||
* before calling the CallInvocationTransformer, and we need to create the
|
||||
* call after that. */
|
||||
emitter.call = call;
|
||||
let responseMessage: ResponseType | null = null;
|
||||
let receivedStatus = false;
|
||||
let callerStackError: Error | null = new Error();
|
||||
call.start(callProperties.metadata, {
|
||||
onReceiveMetadata: metadata => {
|
||||
emitter.emit('metadata', metadata);
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
onReceiveMessage(message: any) {
|
||||
if (responseMessage !== null) {
|
||||
call.cancelWithStatus(Status.INTERNAL, 'Too many responses received');
|
||||
}
|
||||
responseMessage = message;
|
||||
},
|
||||
onReceiveStatus(status: StatusObject) {
|
||||
if (receivedStatus) {
|
||||
return;
|
||||
}
|
||||
receivedStatus = true;
|
||||
if (status.code === Status.OK) {
|
||||
if (responseMessage === null) {
|
||||
const callerStack = getErrorStackString(callerStackError!);
|
||||
callProperties.callback!(
|
||||
callErrorFromStatus(
|
||||
{
|
||||
code: Status.INTERNAL,
|
||||
details: 'No message received',
|
||||
metadata: status.metadata,
|
||||
},
|
||||
callerStack
|
||||
)
|
||||
);
|
||||
} else {
|
||||
callProperties.callback!(null, responseMessage);
|
||||
}
|
||||
} else {
|
||||
const callerStack = getErrorStackString(callerStackError!);
|
||||
callProperties.callback!(callErrorFromStatus(status, callerStack));
|
||||
}
|
||||
/* Avoid retaining the callerStackError object in the call context of
|
||||
* the status event handler. */
|
||||
callerStackError = null;
|
||||
emitter.emit('status', status);
|
||||
},
|
||||
});
|
||||
call.sendMessage(argument);
|
||||
call.halfClose();
|
||||
return emitter;
|
||||
}
|
||||
|
||||
makeClientStreamRequest<RequestType, ResponseType>(
|
||||
method: string,
|
||||
serialize: (value: RequestType) => Buffer,
|
||||
deserialize: (value: Buffer) => ResponseType,
|
||||
metadata: Metadata,
|
||||
options: CallOptions,
|
||||
callback: UnaryCallback<ResponseType>
|
||||
): ClientWritableStream<RequestType>;
|
||||
makeClientStreamRequest<RequestType, ResponseType>(
|
||||
method: string,
|
||||
serialize: (value: RequestType) => Buffer,
|
||||
deserialize: (value: Buffer) => ResponseType,
|
||||
metadata: Metadata,
|
||||
callback: UnaryCallback<ResponseType>
|
||||
): ClientWritableStream<RequestType>;
|
||||
makeClientStreamRequest<RequestType, ResponseType>(
|
||||
method: string,
|
||||
serialize: (value: RequestType) => Buffer,
|
||||
deserialize: (value: Buffer) => ResponseType,
|
||||
options: CallOptions,
|
||||
callback: UnaryCallback<ResponseType>
|
||||
): ClientWritableStream<RequestType>;
|
||||
makeClientStreamRequest<RequestType, ResponseType>(
|
||||
method: string,
|
||||
serialize: (value: RequestType) => Buffer,
|
||||
deserialize: (value: Buffer) => ResponseType,
|
||||
callback: UnaryCallback<ResponseType>
|
||||
): ClientWritableStream<RequestType>;
|
||||
makeClientStreamRequest<RequestType, ResponseType>(
|
||||
method: string,
|
||||
serialize: (value: RequestType) => Buffer,
|
||||
deserialize: (value: Buffer) => ResponseType,
|
||||
metadata: Metadata | CallOptions | UnaryCallback<ResponseType>,
|
||||
options?: CallOptions | UnaryCallback<ResponseType>,
|
||||
callback?: UnaryCallback<ResponseType>
|
||||
): ClientWritableStream<RequestType> {
|
||||
const checkedArguments =
|
||||
this.checkOptionalUnaryResponseArguments<ResponseType>(
|
||||
metadata,
|
||||
options,
|
||||
callback
|
||||
);
|
||||
const methodDefinition: ClientMethodDefinition<RequestType, ResponseType> =
|
||||
{
|
||||
path: method,
|
||||
requestStream: true,
|
||||
responseStream: false,
|
||||
requestSerialize: serialize,
|
||||
responseDeserialize: deserialize,
|
||||
};
|
||||
let callProperties: CallProperties<RequestType, ResponseType> = {
|
||||
metadata: checkedArguments.metadata,
|
||||
call: new ClientWritableStreamImpl<RequestType>(serialize),
|
||||
channel: this[CHANNEL_SYMBOL],
|
||||
methodDefinition: methodDefinition,
|
||||
callOptions: checkedArguments.options,
|
||||
callback: checkedArguments.callback,
|
||||
};
|
||||
if (this[CALL_INVOCATION_TRANSFORMER_SYMBOL]) {
|
||||
callProperties = this[CALL_INVOCATION_TRANSFORMER_SYMBOL]!(
|
||||
callProperties
|
||||
) as CallProperties<RequestType, ResponseType>;
|
||||
}
|
||||
const emitter: ClientWritableStream<RequestType> =
|
||||
callProperties.call as ClientWritableStream<RequestType>;
|
||||
const interceptorArgs: InterceptorArguments = {
|
||||
clientInterceptors: this[INTERCEPTOR_SYMBOL],
|
||||
clientInterceptorProviders: this[INTERCEPTOR_PROVIDER_SYMBOL],
|
||||
callInterceptors: callProperties.callOptions.interceptors ?? [],
|
||||
callInterceptorProviders:
|
||||
callProperties.callOptions.interceptor_providers ?? [],
|
||||
};
|
||||
const call: InterceptingCallInterface = getInterceptingCall(
|
||||
interceptorArgs,
|
||||
callProperties.methodDefinition,
|
||||
callProperties.callOptions,
|
||||
callProperties.channel
|
||||
);
|
||||
/* This needs to happen before the emitter is used. Unfortunately we can't
|
||||
* enforce this with the type system. We need to construct this emitter
|
||||
* before calling the CallInvocationTransformer, and we need to create the
|
||||
* call after that. */
|
||||
emitter.call = call;
|
||||
let responseMessage: ResponseType | null = null;
|
||||
let receivedStatus = false;
|
||||
let callerStackError: Error | null = new Error();
|
||||
call.start(callProperties.metadata, {
|
||||
onReceiveMetadata: metadata => {
|
||||
emitter.emit('metadata', metadata);
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
onReceiveMessage(message: any) {
|
||||
if (responseMessage !== null) {
|
||||
call.cancelWithStatus(Status.INTERNAL, 'Too many responses received');
|
||||
}
|
||||
responseMessage = message;
|
||||
},
|
||||
onReceiveStatus(status: StatusObject) {
|
||||
if (receivedStatus) {
|
||||
return;
|
||||
}
|
||||
receivedStatus = true;
|
||||
if (status.code === Status.OK) {
|
||||
if (responseMessage === null) {
|
||||
const callerStack = getErrorStackString(callerStackError!);
|
||||
callProperties.callback!(
|
||||
callErrorFromStatus(
|
||||
{
|
||||
code: Status.INTERNAL,
|
||||
details: 'No message received',
|
||||
metadata: status.metadata,
|
||||
},
|
||||
callerStack
|
||||
)
|
||||
);
|
||||
} else {
|
||||
callProperties.callback!(null, responseMessage);
|
||||
}
|
||||
} else {
|
||||
const callerStack = getErrorStackString(callerStackError!);
|
||||
callProperties.callback!(callErrorFromStatus(status, callerStack));
|
||||
}
|
||||
/* Avoid retaining the callerStackError object in the call context of
|
||||
* the status event handler. */
|
||||
callerStackError = null;
|
||||
emitter.emit('status', status);
|
||||
},
|
||||
});
|
||||
return emitter;
|
||||
}
|
||||
|
||||
private checkMetadataAndOptions(
|
||||
arg1?: Metadata | CallOptions,
|
||||
arg2?: CallOptions
|
||||
): { metadata: Metadata; options: CallOptions } {
|
||||
let metadata: Metadata;
|
||||
let options: CallOptions;
|
||||
if (arg1 instanceof Metadata) {
|
||||
metadata = arg1;
|
||||
if (arg2) {
|
||||
options = arg2;
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
} else {
|
||||
if (arg1) {
|
||||
options = arg1;
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
metadata = new Metadata();
|
||||
}
|
||||
return { metadata, options };
|
||||
}
|
||||
|
||||
makeServerStreamRequest<RequestType, ResponseType>(
|
||||
method: string,
|
||||
serialize: (value: RequestType) => Buffer,
|
||||
deserialize: (value: Buffer) => ResponseType,
|
||||
argument: RequestType,
|
||||
metadata: Metadata,
|
||||
options?: CallOptions
|
||||
): ClientReadableStream<ResponseType>;
|
||||
makeServerStreamRequest<RequestType, ResponseType>(
|
||||
method: string,
|
||||
serialize: (value: RequestType) => Buffer,
|
||||
deserialize: (value: Buffer) => ResponseType,
|
||||
argument: RequestType,
|
||||
options?: CallOptions
|
||||
): ClientReadableStream<ResponseType>;
|
||||
makeServerStreamRequest<RequestType, ResponseType>(
|
||||
method: string,
|
||||
serialize: (value: RequestType) => Buffer,
|
||||
deserialize: (value: Buffer) => ResponseType,
|
||||
argument: RequestType,
|
||||
metadata?: Metadata | CallOptions,
|
||||
options?: CallOptions
|
||||
): ClientReadableStream<ResponseType> {
|
||||
const checkedArguments = this.checkMetadataAndOptions(metadata, options);
|
||||
const methodDefinition: ClientMethodDefinition<RequestType, ResponseType> =
|
||||
{
|
||||
path: method,
|
||||
requestStream: false,
|
||||
responseStream: true,
|
||||
requestSerialize: serialize,
|
||||
responseDeserialize: deserialize,
|
||||
};
|
||||
let callProperties: CallProperties<RequestType, ResponseType> = {
|
||||
argument: argument,
|
||||
metadata: checkedArguments.metadata,
|
||||
call: new ClientReadableStreamImpl<ResponseType>(deserialize),
|
||||
channel: this[CHANNEL_SYMBOL],
|
||||
methodDefinition: methodDefinition,
|
||||
callOptions: checkedArguments.options,
|
||||
};
|
||||
if (this[CALL_INVOCATION_TRANSFORMER_SYMBOL]) {
|
||||
callProperties = this[CALL_INVOCATION_TRANSFORMER_SYMBOL]!(
|
||||
callProperties
|
||||
) as CallProperties<RequestType, ResponseType>;
|
||||
}
|
||||
const stream: ClientReadableStream<ResponseType> =
|
||||
callProperties.call as ClientReadableStream<ResponseType>;
|
||||
const interceptorArgs: InterceptorArguments = {
|
||||
clientInterceptors: this[INTERCEPTOR_SYMBOL],
|
||||
clientInterceptorProviders: this[INTERCEPTOR_PROVIDER_SYMBOL],
|
||||
callInterceptors: callProperties.callOptions.interceptors ?? [],
|
||||
callInterceptorProviders:
|
||||
callProperties.callOptions.interceptor_providers ?? [],
|
||||
};
|
||||
const call: InterceptingCallInterface = getInterceptingCall(
|
||||
interceptorArgs,
|
||||
callProperties.methodDefinition,
|
||||
callProperties.callOptions,
|
||||
callProperties.channel
|
||||
);
|
||||
/* This needs to happen before the emitter is used. Unfortunately we can't
|
||||
* enforce this with the type system. We need to construct this emitter
|
||||
* before calling the CallInvocationTransformer, and we need to create the
|
||||
* call after that. */
|
||||
stream.call = call;
|
||||
let receivedStatus = false;
|
||||
let callerStackError: Error | null = new Error();
|
||||
call.start(callProperties.metadata, {
|
||||
onReceiveMetadata(metadata: Metadata) {
|
||||
stream.emit('metadata', metadata);
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
onReceiveMessage(message: any) {
|
||||
stream.push(message);
|
||||
},
|
||||
onReceiveStatus(status: StatusObject) {
|
||||
if (receivedStatus) {
|
||||
return;
|
||||
}
|
||||
receivedStatus = true;
|
||||
stream.push(null);
|
||||
if (status.code !== Status.OK) {
|
||||
const callerStack = getErrorStackString(callerStackError!);
|
||||
stream.emit('error', callErrorFromStatus(status, callerStack));
|
||||
}
|
||||
/* Avoid retaining the callerStackError object in the call context of
|
||||
* the status event handler. */
|
||||
callerStackError = null;
|
||||
stream.emit('status', status);
|
||||
},
|
||||
});
|
||||
call.sendMessage(argument);
|
||||
call.halfClose();
|
||||
return stream;
|
||||
}
|
||||
|
||||
makeBidiStreamRequest<RequestType, ResponseType>(
|
||||
method: string,
|
||||
serialize: (value: RequestType) => Buffer,
|
||||
deserialize: (value: Buffer) => ResponseType,
|
||||
metadata: Metadata,
|
||||
options?: CallOptions
|
||||
): ClientDuplexStream<RequestType, ResponseType>;
|
||||
makeBidiStreamRequest<RequestType, ResponseType>(
|
||||
method: string,
|
||||
serialize: (value: RequestType) => Buffer,
|
||||
deserialize: (value: Buffer) => ResponseType,
|
||||
options?: CallOptions
|
||||
): ClientDuplexStream<RequestType, ResponseType>;
|
||||
makeBidiStreamRequest<RequestType, ResponseType>(
|
||||
method: string,
|
||||
serialize: (value: RequestType) => Buffer,
|
||||
deserialize: (value: Buffer) => ResponseType,
|
||||
metadata?: Metadata | CallOptions,
|
||||
options?: CallOptions
|
||||
): ClientDuplexStream<RequestType, ResponseType> {
|
||||
const checkedArguments = this.checkMetadataAndOptions(metadata, options);
|
||||
const methodDefinition: ClientMethodDefinition<RequestType, ResponseType> =
|
||||
{
|
||||
path: method,
|
||||
requestStream: true,
|
||||
responseStream: true,
|
||||
requestSerialize: serialize,
|
||||
responseDeserialize: deserialize,
|
||||
};
|
||||
let callProperties: CallProperties<RequestType, ResponseType> = {
|
||||
metadata: checkedArguments.metadata,
|
||||
call: new ClientDuplexStreamImpl<RequestType, ResponseType>(
|
||||
serialize,
|
||||
deserialize
|
||||
),
|
||||
channel: this[CHANNEL_SYMBOL],
|
||||
methodDefinition: methodDefinition,
|
||||
callOptions: checkedArguments.options,
|
||||
};
|
||||
if (this[CALL_INVOCATION_TRANSFORMER_SYMBOL]) {
|
||||
callProperties = this[CALL_INVOCATION_TRANSFORMER_SYMBOL]!(
|
||||
callProperties
|
||||
) as CallProperties<RequestType, ResponseType>;
|
||||
}
|
||||
const stream: ClientDuplexStream<RequestType, ResponseType> =
|
||||
callProperties.call as ClientDuplexStream<RequestType, ResponseType>;
|
||||
const interceptorArgs: InterceptorArguments = {
|
||||
clientInterceptors: this[INTERCEPTOR_SYMBOL],
|
||||
clientInterceptorProviders: this[INTERCEPTOR_PROVIDER_SYMBOL],
|
||||
callInterceptors: callProperties.callOptions.interceptors ?? [],
|
||||
callInterceptorProviders:
|
||||
callProperties.callOptions.interceptor_providers ?? [],
|
||||
};
|
||||
const call: InterceptingCallInterface = getInterceptingCall(
|
||||
interceptorArgs,
|
||||
callProperties.methodDefinition,
|
||||
callProperties.callOptions,
|
||||
callProperties.channel
|
||||
);
|
||||
/* This needs to happen before the emitter is used. Unfortunately we can't
|
||||
* enforce this with the type system. We need to construct this emitter
|
||||
* before calling the CallInvocationTransformer, and we need to create the
|
||||
* call after that. */
|
||||
stream.call = call;
|
||||
let receivedStatus = false;
|
||||
let callerStackError: Error | null = new Error();
|
||||
call.start(callProperties.metadata, {
|
||||
onReceiveMetadata(metadata: Metadata) {
|
||||
stream.emit('metadata', metadata);
|
||||
},
|
||||
onReceiveMessage(message: Buffer) {
|
||||
stream.push(message);
|
||||
},
|
||||
onReceiveStatus(status: StatusObject) {
|
||||
if (receivedStatus) {
|
||||
return;
|
||||
}
|
||||
receivedStatus = true;
|
||||
stream.push(null);
|
||||
if (status.code !== Status.OK) {
|
||||
const callerStack = getErrorStackString(callerStackError!);
|
||||
stream.emit('error', callErrorFromStatus(status, callerStack));
|
||||
}
|
||||
/* Avoid retaining the callerStackError object in the call context of
|
||||
* the status event handler. */
|
||||
callerStackError = null;
|
||||
stream.emit('status', status);
|
||||
},
|
||||
});
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
22
node_modules/@grpc/grpc-js/src/compression-algorithms.ts
generated
vendored
Normal file
22
node_modules/@grpc/grpc-js/src/compression-algorithms.ts
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*
|
||||
*/
|
||||
|
||||
export enum CompressionAlgorithms {
|
||||
identity = 0,
|
||||
deflate = 1,
|
||||
gzip = 2,
|
||||
}
|
||||
350
node_modules/@grpc/grpc-js/src/compression-filter.ts
generated
vendored
Normal file
350
node_modules/@grpc/grpc-js/src/compression-filter.ts
generated
vendored
Normal file
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
* 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 * as zlib from 'zlib';
|
||||
|
||||
import { WriteObject, WriteFlags } from './call-interface';
|
||||
import { Channel } from './channel';
|
||||
import { ChannelOptions } from './channel-options';
|
||||
import { CompressionAlgorithms } from './compression-algorithms';
|
||||
import { DEFAULT_MAX_RECEIVE_MESSAGE_LENGTH, LogVerbosity, Status } from './constants';
|
||||
import { BaseFilter, Filter, FilterFactory } from './filter';
|
||||
import * as logging from './logging';
|
||||
import { Metadata, MetadataValue } from './metadata';
|
||||
|
||||
const isCompressionAlgorithmKey = (
|
||||
key: number
|
||||
): key is CompressionAlgorithms => {
|
||||
return (
|
||||
typeof key === 'number' && typeof CompressionAlgorithms[key] === 'string'
|
||||
);
|
||||
};
|
||||
|
||||
type CompressionAlgorithm = keyof typeof CompressionAlgorithms;
|
||||
|
||||
type SharedCompressionFilterConfig = {
|
||||
serverSupportedEncodingHeader?: string;
|
||||
};
|
||||
|
||||
abstract class CompressionHandler {
|
||||
protected abstract compressMessage(message: Buffer): Promise<Buffer>;
|
||||
protected abstract decompressMessage(data: Buffer): Promise<Buffer>;
|
||||
/**
|
||||
* @param message Raw uncompressed message bytes
|
||||
* @param compress Indicates whether the message should be compressed
|
||||
* @return Framed message, compressed if applicable
|
||||
*/
|
||||
async writeMessage(message: Buffer, compress: boolean): Promise<Buffer> {
|
||||
let messageBuffer = message;
|
||||
if (compress) {
|
||||
messageBuffer = await this.compressMessage(messageBuffer);
|
||||
}
|
||||
const output = Buffer.allocUnsafe(messageBuffer.length + 5);
|
||||
output.writeUInt8(compress ? 1 : 0, 0);
|
||||
output.writeUInt32BE(messageBuffer.length, 1);
|
||||
messageBuffer.copy(output, 5);
|
||||
return output;
|
||||
}
|
||||
/**
|
||||
* @param data Framed message, possibly compressed
|
||||
* @return Uncompressed message
|
||||
*/
|
||||
async readMessage(data: Buffer): Promise<Buffer> {
|
||||
const compressed = data.readUInt8(0) === 1;
|
||||
let messageBuffer = data.slice(5);
|
||||
if (compressed) {
|
||||
messageBuffer = await this.decompressMessage(messageBuffer);
|
||||
}
|
||||
return messageBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
class IdentityHandler extends CompressionHandler {
|
||||
async compressMessage(message: Buffer) {
|
||||
return message;
|
||||
}
|
||||
|
||||
async writeMessage(message: Buffer, compress: boolean): Promise<Buffer> {
|
||||
const output = Buffer.allocUnsafe(message.length + 5);
|
||||
/* With "identity" compression, messages should always be marked as
|
||||
* uncompressed */
|
||||
output.writeUInt8(0, 0);
|
||||
output.writeUInt32BE(message.length, 1);
|
||||
message.copy(output, 5);
|
||||
return output;
|
||||
}
|
||||
|
||||
decompressMessage(message: Buffer): Promise<Buffer> {
|
||||
return Promise.reject<Buffer>(
|
||||
new Error(
|
||||
'Received compressed message but "grpc-encoding" header was identity'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DeflateHandler extends CompressionHandler {
|
||||
constructor(private maxRecvMessageLength: number) {
|
||||
super();
|
||||
}
|
||||
|
||||
compressMessage(message: Buffer) {
|
||||
return new Promise<Buffer>((resolve, reject) => {
|
||||
zlib.deflate(message, (err, output) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(output);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
decompressMessage(message: Buffer) {
|
||||
return new Promise<Buffer>((resolve, reject) => {
|
||||
let totalLength = 0;
|
||||
const messageParts: Buffer[] = [];
|
||||
const decompresser = zlib.createInflate();
|
||||
decompresser.on('data', (chunk: Buffer) => {
|
||||
messageParts.push(chunk);
|
||||
totalLength += chunk.byteLength;
|
||||
if (this.maxRecvMessageLength !== -1 && totalLength > this.maxRecvMessageLength) {
|
||||
decompresser.destroy();
|
||||
reject({
|
||||
code: Status.RESOURCE_EXHAUSTED,
|
||||
details: `Received message that decompresses to a size larger than ${this.maxRecvMessageLength}`
|
||||
});
|
||||
}
|
||||
});
|
||||
decompresser.on('end', () => {
|
||||
resolve(Buffer.concat(messageParts));
|
||||
});
|
||||
decompresser.write(message);
|
||||
decompresser.end();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class GzipHandler extends CompressionHandler {
|
||||
constructor(private maxRecvMessageLength: number) {
|
||||
super();
|
||||
}
|
||||
|
||||
compressMessage(message: Buffer) {
|
||||
return new Promise<Buffer>((resolve, reject) => {
|
||||
zlib.gzip(message, (err, output) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(output);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
decompressMessage(message: Buffer) {
|
||||
return new Promise<Buffer>((resolve, reject) => {
|
||||
let totalLength = 0;
|
||||
const messageParts: Buffer[] = [];
|
||||
const decompresser = zlib.createGunzip();
|
||||
decompresser.on('data', (chunk: Buffer) => {
|
||||
messageParts.push(chunk);
|
||||
totalLength += chunk.byteLength;
|
||||
if (this.maxRecvMessageLength !== -1 && totalLength > this.maxRecvMessageLength) {
|
||||
decompresser.destroy();
|
||||
reject({
|
||||
code: Status.RESOURCE_EXHAUSTED,
|
||||
details: `Received message that decompresses to a size larger than ${this.maxRecvMessageLength}`
|
||||
});
|
||||
}
|
||||
});
|
||||
decompresser.on('end', () => {
|
||||
resolve(Buffer.concat(messageParts));
|
||||
});
|
||||
decompresser.write(message);
|
||||
decompresser.end();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class UnknownHandler extends CompressionHandler {
|
||||
constructor(private readonly compressionName: string) {
|
||||
super();
|
||||
}
|
||||
compressMessage(message: Buffer): Promise<Buffer> {
|
||||
return Promise.reject<Buffer>(
|
||||
new Error(
|
||||
`Received message compressed with unsupported compression method ${this.compressionName}`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
decompressMessage(message: Buffer): Promise<Buffer> {
|
||||
// This should be unreachable
|
||||
return Promise.reject<Buffer>(
|
||||
new Error(`Compression method not supported: ${this.compressionName}`)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function getCompressionHandler(compressionName: string, maxReceiveMessageSize: number): CompressionHandler {
|
||||
switch (compressionName) {
|
||||
case 'identity':
|
||||
return new IdentityHandler();
|
||||
case 'deflate':
|
||||
return new DeflateHandler(maxReceiveMessageSize);
|
||||
case 'gzip':
|
||||
return new GzipHandler(maxReceiveMessageSize);
|
||||
default:
|
||||
return new UnknownHandler(compressionName);
|
||||
}
|
||||
}
|
||||
|
||||
export class CompressionFilter extends BaseFilter implements Filter {
|
||||
private sendCompression: CompressionHandler = new IdentityHandler();
|
||||
private receiveCompression: CompressionHandler = new IdentityHandler();
|
||||
private currentCompressionAlgorithm: CompressionAlgorithm = 'identity';
|
||||
private maxReceiveMessageLength: number;
|
||||
|
||||
constructor(
|
||||
channelOptions: ChannelOptions,
|
||||
private sharedFilterConfig: SharedCompressionFilterConfig
|
||||
) {
|
||||
super();
|
||||
|
||||
const compressionAlgorithmKey =
|
||||
channelOptions['grpc.default_compression_algorithm'];
|
||||
this.maxReceiveMessageLength = channelOptions['grpc.max_receive_message_length'] ?? DEFAULT_MAX_RECEIVE_MESSAGE_LENGTH
|
||||
if (compressionAlgorithmKey !== undefined) {
|
||||
if (isCompressionAlgorithmKey(compressionAlgorithmKey)) {
|
||||
const clientSelectedEncoding = CompressionAlgorithms[
|
||||
compressionAlgorithmKey
|
||||
] as CompressionAlgorithm;
|
||||
const serverSupportedEncodings =
|
||||
sharedFilterConfig.serverSupportedEncodingHeader?.split(',');
|
||||
/**
|
||||
* There are two possible situations here:
|
||||
* 1) We don't have any info yet from the server about what compression it supports
|
||||
* In that case we should just use what the client tells us to use
|
||||
* 2) We've previously received a response from the server including a grpc-accept-encoding header
|
||||
* In that case we only want to use the encoding chosen by the client if the server supports it
|
||||
*/
|
||||
if (
|
||||
!serverSupportedEncodings ||
|
||||
serverSupportedEncodings.includes(clientSelectedEncoding)
|
||||
) {
|
||||
this.currentCompressionAlgorithm = clientSelectedEncoding;
|
||||
this.sendCompression = getCompressionHandler(
|
||||
this.currentCompressionAlgorithm,
|
||||
-1
|
||||
);
|
||||
}
|
||||
} else {
|
||||
logging.log(
|
||||
LogVerbosity.ERROR,
|
||||
`Invalid value provided for grpc.default_compression_algorithm option: ${compressionAlgorithmKey}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async sendMetadata(metadata: Promise<Metadata>): Promise<Metadata> {
|
||||
const headers: Metadata = await metadata;
|
||||
headers.set('grpc-accept-encoding', 'identity,deflate,gzip');
|
||||
headers.set('accept-encoding', 'identity');
|
||||
|
||||
// No need to send the header if it's "identity" - behavior is identical; save the bandwidth
|
||||
if (this.currentCompressionAlgorithm === 'identity') {
|
||||
headers.remove('grpc-encoding');
|
||||
} else {
|
||||
headers.set('grpc-encoding', this.currentCompressionAlgorithm);
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
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, this.maxReceiveMessageLength);
|
||||
}
|
||||
}
|
||||
metadata.remove('grpc-encoding');
|
||||
|
||||
/* Check to see if the compression we're using to send messages is supported by the server
|
||||
* If not, reset the sendCompression filter and have it use the default IdentityHandler */
|
||||
const serverSupportedEncodingsHeader = metadata.get(
|
||||
'grpc-accept-encoding'
|
||||
)[0] as string | undefined;
|
||||
if (serverSupportedEncodingsHeader) {
|
||||
this.sharedFilterConfig.serverSupportedEncodingHeader =
|
||||
serverSupportedEncodingsHeader;
|
||||
const serverSupportedEncodings =
|
||||
serverSupportedEncodingsHeader.split(',');
|
||||
|
||||
if (
|
||||
!serverSupportedEncodings.includes(this.currentCompressionAlgorithm)
|
||||
) {
|
||||
this.sendCompression = new IdentityHandler();
|
||||
this.currentCompressionAlgorithm = 'identity';
|
||||
}
|
||||
}
|
||||
metadata.remove('grpc-accept-encoding');
|
||||
return metadata;
|
||||
}
|
||||
|
||||
async sendMessage(message: Promise<WriteObject>): Promise<WriteObject> {
|
||||
/* This filter is special. The input message is the bare message bytes,
|
||||
* and the output is a framed and possibly compressed message. For this
|
||||
* reason, this filter should be at the bottom of the filter stack */
|
||||
const resolvedMessage: WriteObject = await message;
|
||||
let compress: boolean;
|
||||
if (this.sendCompression instanceof IdentityHandler) {
|
||||
compress = false;
|
||||
} else {
|
||||
compress = ((resolvedMessage.flags ?? 0) & WriteFlags.NoCompress) === 0;
|
||||
}
|
||||
|
||||
return {
|
||||
message: await this.sendCompression.writeMessage(
|
||||
resolvedMessage.message,
|
||||
compress
|
||||
),
|
||||
flags: resolvedMessage.flags,
|
||||
};
|
||||
}
|
||||
|
||||
async receiveMessage(message: Promise<Buffer>) {
|
||||
/* This filter is also special. The input message is framed and possibly
|
||||
* compressed, and the output message is deframed and uncompressed. So
|
||||
* this is another reason that this filter should be at the bottom of the
|
||||
* filter stack. */
|
||||
return this.receiveCompression.readMessage(await message);
|
||||
}
|
||||
}
|
||||
|
||||
export class CompressionFilterFactory
|
||||
implements FilterFactory<CompressionFilter>
|
||||
{
|
||||
private sharedFilterConfig: SharedCompressionFilterConfig = {};
|
||||
constructor(channel: Channel, private readonly options: ChannelOptions) {}
|
||||
createFilter(): CompressionFilter {
|
||||
return new CompressionFilter(this.options, this.sharedFilterConfig);
|
||||
}
|
||||
}
|
||||
24
node_modules/@grpc/grpc-js/src/connectivity-state.ts
generated
vendored
Normal file
24
node_modules/@grpc/grpc-js/src/connectivity-state.ts
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*
|
||||
*/
|
||||
|
||||
export enum ConnectivityState {
|
||||
IDLE,
|
||||
CONNECTING,
|
||||
READY,
|
||||
TRANSIENT_FAILURE,
|
||||
SHUTDOWN,
|
||||
}
|
||||
66
node_modules/@grpc/grpc-js/src/constants.ts
generated
vendored
Normal file
66
node_modules/@grpc/grpc-js/src/constants.ts
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
export enum Status {
|
||||
OK = 0,
|
||||
CANCELLED,
|
||||
UNKNOWN,
|
||||
INVALID_ARGUMENT,
|
||||
DEADLINE_EXCEEDED,
|
||||
NOT_FOUND,
|
||||
ALREADY_EXISTS,
|
||||
PERMISSION_DENIED,
|
||||
RESOURCE_EXHAUSTED,
|
||||
FAILED_PRECONDITION,
|
||||
ABORTED,
|
||||
OUT_OF_RANGE,
|
||||
UNIMPLEMENTED,
|
||||
INTERNAL,
|
||||
UNAVAILABLE,
|
||||
DATA_LOSS,
|
||||
UNAUTHENTICATED,
|
||||
}
|
||||
|
||||
export enum LogVerbosity {
|
||||
DEBUG = 0,
|
||||
INFO,
|
||||
ERROR,
|
||||
NONE,
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE: This enum is not currently used in any implemented API in this
|
||||
* library. It is included only for type parity with the other implementation.
|
||||
*/
|
||||
export enum Propagate {
|
||||
DEADLINE = 1,
|
||||
CENSUS_STATS_CONTEXT = 2,
|
||||
CENSUS_TRACING_CONTEXT = 4,
|
||||
CANCELLATION = 8,
|
||||
// https://github.com/grpc/grpc/blob/master/include/grpc/impl/codegen/propagation_bits.h#L43
|
||||
DEFAULTS = 0xffff |
|
||||
Propagate.DEADLINE |
|
||||
Propagate.CENSUS_STATS_CONTEXT |
|
||||
Propagate.CENSUS_TRACING_CONTEXT |
|
||||
Propagate.CANCELLATION,
|
||||
}
|
||||
|
||||
// -1 means unlimited
|
||||
export const DEFAULT_MAX_SEND_MESSAGE_LENGTH = -1;
|
||||
|
||||
// 4 MB default
|
||||
export const DEFAULT_MAX_RECEIVE_MESSAGE_LENGTH = 4 * 1024 * 1024;
|
||||
43
node_modules/@grpc/grpc-js/src/control-plane-status.ts
generated
vendored
Normal file
43
node_modules/@grpc/grpc-js/src/control-plane-status.ts
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2022 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 { Status } from './constants';
|
||||
|
||||
const INAPPROPRIATE_CONTROL_PLANE_CODES: Status[] = [
|
||||
Status.OK,
|
||||
Status.INVALID_ARGUMENT,
|
||||
Status.NOT_FOUND,
|
||||
Status.ALREADY_EXISTS,
|
||||
Status.FAILED_PRECONDITION,
|
||||
Status.ABORTED,
|
||||
Status.OUT_OF_RANGE,
|
||||
Status.DATA_LOSS,
|
||||
];
|
||||
|
||||
export function restrictControlPlaneStatusCode(
|
||||
code: Status,
|
||||
details: string
|
||||
): { code: Status; details: string } {
|
||||
if (INAPPROPRIATE_CONTROL_PLANE_CODES.includes(code)) {
|
||||
return {
|
||||
code: Status.INTERNAL,
|
||||
details: `Invalid status from control plane: ${code} ${Status[code]} ${details}`,
|
||||
};
|
||||
} else {
|
||||
return { code, details };
|
||||
}
|
||||
}
|
||||
95
node_modules/@grpc/grpc-js/src/deadline.ts
generated
vendored
Normal file
95
node_modules/@grpc/grpc-js/src/deadline.ts
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
export type Deadline = Date | number;
|
||||
|
||||
export function minDeadline(...deadlineList: Deadline[]): Deadline {
|
||||
let minValue = Infinity;
|
||||
for (const deadline of deadlineList) {
|
||||
const deadlineMsecs =
|
||||
deadline instanceof Date ? deadline.getTime() : deadline;
|
||||
if (deadlineMsecs < minValue) {
|
||||
minValue = deadlineMsecs;
|
||||
}
|
||||
}
|
||||
return minValue;
|
||||
}
|
||||
|
||||
const units: Array<[string, number]> = [
|
||||
['m', 1],
|
||||
['S', 1000],
|
||||
['M', 60 * 1000],
|
||||
['H', 60 * 60 * 1000],
|
||||
];
|
||||
|
||||
export function getDeadlineTimeoutString(deadline: Deadline) {
|
||||
const now = new Date().getTime();
|
||||
if (deadline instanceof Date) {
|
||||
deadline = deadline.getTime();
|
||||
}
|
||||
const timeoutMs = Math.max(deadline - now, 0);
|
||||
for (const [unit, factor] of units) {
|
||||
const amount = timeoutMs / factor;
|
||||
if (amount < 1e8) {
|
||||
return String(Math.ceil(amount)) + unit;
|
||||
}
|
||||
}
|
||||
throw new Error('Deadline is too far in the future');
|
||||
}
|
||||
|
||||
/**
|
||||
* See https://nodejs.org/api/timers.html#settimeoutcallback-delay-args
|
||||
* In particular, "When delay is larger than 2147483647 or less than 1, the
|
||||
* delay will be set to 1. Non-integer delays are truncated to an integer."
|
||||
* This number of milliseconds is almost 25 days.
|
||||
*/
|
||||
const MAX_TIMEOUT_TIME = 2147483647;
|
||||
|
||||
/**
|
||||
* Get the timeout value that should be passed to setTimeout now for the timer
|
||||
* to end at the deadline. For any deadline before now, the timer should end
|
||||
* immediately, represented by a value of 0. For any deadline more than
|
||||
* MAX_TIMEOUT_TIME milliseconds in the future, a timer cannot be set that will
|
||||
* end at that time, so it is treated as infinitely far in the future.
|
||||
* @param deadline
|
||||
* @returns
|
||||
*/
|
||||
export function getRelativeTimeout(deadline: Deadline) {
|
||||
const deadlineMs = deadline instanceof Date ? deadline.getTime() : deadline;
|
||||
const now = new Date().getTime();
|
||||
const timeout = deadlineMs - now;
|
||||
if (timeout < 0) {
|
||||
return 0;
|
||||
} else if (timeout > MAX_TIMEOUT_TIME) {
|
||||
return Infinity;
|
||||
} else {
|
||||
return timeout;
|
||||
}
|
||||
}
|
||||
|
||||
export function deadlineToString(deadline: Deadline): string {
|
||||
if (deadline instanceof Date) {
|
||||
return deadline.toISOString();
|
||||
} else {
|
||||
const dateDeadline = new Date(deadline);
|
||||
if (Number.isNaN(dateDeadline.getTime())) {
|
||||
return '' + deadline;
|
||||
} else {
|
||||
return dateDeadline.toISOString();
|
||||
}
|
||||
}
|
||||
}
|
||||
36
node_modules/@grpc/grpc-js/src/duration.ts
generated
vendored
Normal file
36
node_modules/@grpc/grpc-js/src/duration.ts
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2022 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.
|
||||
*
|
||||
*/
|
||||
|
||||
export interface Duration {
|
||||
seconds: number;
|
||||
nanos: number;
|
||||
}
|
||||
|
||||
export function msToDuration(millis: number): Duration {
|
||||
return {
|
||||
seconds: (millis / 1000) | 0,
|
||||
nanos: ((millis % 1000) * 1_000_000) | 0,
|
||||
};
|
||||
}
|
||||
|
||||
export function durationToMs(duration: Duration): number {
|
||||
return (duration.seconds * 1000 + duration.nanos / 1_000_000) | 0;
|
||||
}
|
||||
|
||||
export function isDuration(value: any): value is Duration {
|
||||
return typeof value.seconds === 'number' && typeof value.nanos === 'number';
|
||||
}
|
||||
37
node_modules/@grpc/grpc-js/src/error.ts
generated
vendored
Normal file
37
node_modules/@grpc/grpc-js/src/error.ts
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2022 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.
|
||||
*
|
||||
*/
|
||||
|
||||
export function getErrorMessage(error: unknown): string {
|
||||
if (error instanceof Error) {
|
||||
return error.message;
|
||||
} else {
|
||||
return String(error);
|
||||
}
|
||||
}
|
||||
|
||||
export function getErrorCode(error: unknown): number | null {
|
||||
if (
|
||||
typeof error === 'object' &&
|
||||
error !== null &&
|
||||
'code' in error &&
|
||||
typeof (error as Record<string, unknown>).code === 'number'
|
||||
) {
|
||||
return (error as Record<string, number>).code;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
26
node_modules/@grpc/grpc-js/src/events.ts
generated
vendored
Normal file
26
node_modules/@grpc/grpc-js/src/events.ts
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
export interface EmitterAugmentation1<Name extends string | symbol, Arg> {
|
||||
addListener(event: Name, listener: (arg1: Arg) => void): this;
|
||||
emit(event: Name, arg1: Arg): boolean;
|
||||
on(event: Name, listener: (arg1: Arg) => void): this;
|
||||
once(event: Name, listener: (arg1: Arg) => void): this;
|
||||
prependListener(event: Name, listener: (arg1: Arg) => void): this;
|
||||
prependOnceListener(event: Name, listener: (arg1: Arg) => void): this;
|
||||
removeListener(event: Name, listener: (arg1: Arg) => void): this;
|
||||
}
|
||||
48
node_modules/@grpc/grpc-js/src/experimental.ts
generated
vendored
Normal file
48
node_modules/@grpc/grpc-js/src/experimental.ts
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
export { trace, log } from './logging';
|
||||
export {
|
||||
Resolver,
|
||||
ResolverListener,
|
||||
registerResolver,
|
||||
ConfigSelector,
|
||||
createResolver,
|
||||
} from './resolver';
|
||||
export { GrpcUri, uriToString } from './uri-parser';
|
||||
export { Duration, durationToMs } from './duration';
|
||||
export { ServiceConfig, MethodConfig, RetryPolicy } from './service-config';
|
||||
export { BackoffTimeout } from './backoff-timeout';
|
||||
export {
|
||||
LoadBalancer,
|
||||
LoadBalancingConfig,
|
||||
ChannelControlHelper,
|
||||
createChildChannelControlHelper,
|
||||
registerLoadBalancerType,
|
||||
getFirstUsableConfig,
|
||||
validateLoadBalancingConfig,
|
||||
} from './load-balancer';
|
||||
export {
|
||||
SubchannelAddress,
|
||||
subchannelAddressToString,
|
||||
} from './subchannel-address';
|
||||
export { ChildLoadBalancerHandler } from './load-balancer-child-handler';
|
||||
export {
|
||||
Picker,
|
||||
UnavailablePicker,
|
||||
QueuePicker,
|
||||
PickResult,
|
||||
PickArgs,
|
||||
PickResultType,
|
||||
} from './picker';
|
||||
export { Call as CallStream } from './call-interface';
|
||||
export { Filter, BaseFilter, FilterFactory } from './filter';
|
||||
export { FilterStackFactory } from './filter-stack';
|
||||
export { registerAdminService } from './admin';
|
||||
export {
|
||||
SubchannelInterface,
|
||||
BaseSubchannelWrapper,
|
||||
ConnectivityStateListener,
|
||||
} from './subchannel-interface';
|
||||
export {
|
||||
OutlierDetectionLoadBalancingConfig,
|
||||
SuccessRateEjectionConfig,
|
||||
FailurePercentageEjectionConfig,
|
||||
} from './load-balancer-outlier-detection';
|
||||
100
node_modules/@grpc/grpc-js/src/filter-stack.ts
generated
vendored
Normal file
100
node_modules/@grpc/grpc-js/src/filter-stack.ts
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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 { StatusObject, WriteObject } from './call-interface';
|
||||
import { Filter, FilterFactory } from './filter';
|
||||
import { Metadata } from './metadata';
|
||||
|
||||
export class FilterStack implements Filter {
|
||||
constructor(private readonly filters: Filter[]) {}
|
||||
|
||||
sendMetadata(metadata: Promise<Metadata>): Promise<Metadata> {
|
||||
let result: Promise<Metadata> = metadata;
|
||||
|
||||
for (let i = 0; i < this.filters.length; i++) {
|
||||
result = this.filters[i].sendMetadata(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
receiveMetadata(metadata: Metadata) {
|
||||
let result: Metadata = metadata;
|
||||
|
||||
for (let i = this.filters.length - 1; i >= 0; i--) {
|
||||
result = this.filters[i].receiveMetadata(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
sendMessage(message: Promise<WriteObject>): Promise<WriteObject> {
|
||||
let result: Promise<WriteObject> = message;
|
||||
|
||||
for (let i = 0; i < this.filters.length; i++) {
|
||||
result = this.filters[i].sendMessage(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
receiveMessage(message: Promise<Buffer>): Promise<Buffer> {
|
||||
let result: Promise<Buffer> = message;
|
||||
|
||||
for (let i = this.filters.length - 1; i >= 0; i--) {
|
||||
result = this.filters[i].receiveMessage(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
receiveTrailers(status: StatusObject): StatusObject {
|
||||
let result: StatusObject = status;
|
||||
|
||||
for (let i = this.filters.length - 1; i >= 0; i--) {
|
||||
result = this.filters[i].receiveTrailers(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
push(filters: Filter[]) {
|
||||
this.filters.unshift(...filters);
|
||||
}
|
||||
|
||||
getFilters(): Filter[] {
|
||||
return this.filters;
|
||||
}
|
||||
}
|
||||
|
||||
export class FilterStackFactory implements FilterFactory<FilterStack> {
|
||||
constructor(private readonly factories: Array<FilterFactory<Filter>>) {}
|
||||
|
||||
push(filterFactories: FilterFactory<Filter>[]) {
|
||||
this.factories.unshift(...filterFactories);
|
||||
}
|
||||
|
||||
clone(): FilterStackFactory {
|
||||
return new FilterStackFactory([...this.factories]);
|
||||
}
|
||||
|
||||
createFilter(): FilterStack {
|
||||
return new FilterStack(
|
||||
this.factories.map(factory => factory.createFilter())
|
||||
);
|
||||
}
|
||||
}
|
||||
63
node_modules/@grpc/grpc-js/src/filter.ts
generated
vendored
Normal file
63
node_modules/@grpc/grpc-js/src/filter.ts
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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 { StatusObject, WriteObject } from './call-interface';
|
||||
import { Metadata } from './metadata';
|
||||
|
||||
/**
|
||||
* Filter classes represent related per-call logic and state that is primarily
|
||||
* used to modify incoming and outgoing data. All async filters can be
|
||||
* rejected. The rejection error must be a StatusObject, and a rejection will
|
||||
* cause the call to end with that status.
|
||||
*/
|
||||
export interface Filter {
|
||||
sendMetadata(metadata: Promise<Metadata>): Promise<Metadata>;
|
||||
|
||||
receiveMetadata(metadata: Metadata): Metadata;
|
||||
|
||||
sendMessage(message: Promise<WriteObject>): Promise<WriteObject>;
|
||||
|
||||
receiveMessage(message: Promise<Buffer>): Promise<Buffer>;
|
||||
|
||||
receiveTrailers(status: StatusObject): StatusObject;
|
||||
}
|
||||
|
||||
export abstract class BaseFilter implements Filter {
|
||||
async sendMetadata(metadata: Promise<Metadata>): Promise<Metadata> {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
receiveMetadata(metadata: Metadata): Metadata {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
async sendMessage(message: Promise<WriteObject>): Promise<WriteObject> {
|
||||
return message;
|
||||
}
|
||||
|
||||
async receiveMessage(message: Promise<Buffer>): Promise<Buffer> {
|
||||
return message;
|
||||
}
|
||||
|
||||
receiveTrailers(status: StatusObject): StatusObject {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
export interface FilterFactory<T extends Filter> {
|
||||
createFilter(): T;
|
||||
}
|
||||
73
node_modules/@grpc/grpc-js/src/generated/channelz.ts
generated
vendored
Normal file
73
node_modules/@grpc/grpc-js/src/generated/channelz.ts
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
import type * as grpc from '../index';
|
||||
import type { MessageTypeDefinition } from '@grpc/proto-loader';
|
||||
|
||||
import type { ChannelzClient as _grpc_channelz_v1_ChannelzClient, ChannelzDefinition as _grpc_channelz_v1_ChannelzDefinition } from './grpc/channelz/v1/Channelz';
|
||||
|
||||
type SubtypeConstructor<Constructor extends new (...args: any) => any, Subtype> = {
|
||||
new(...args: ConstructorParameters<Constructor>): Subtype;
|
||||
};
|
||||
|
||||
export interface ProtoGrpcType {
|
||||
google: {
|
||||
protobuf: {
|
||||
Any: MessageTypeDefinition
|
||||
BoolValue: MessageTypeDefinition
|
||||
BytesValue: MessageTypeDefinition
|
||||
DoubleValue: MessageTypeDefinition
|
||||
Duration: MessageTypeDefinition
|
||||
FloatValue: MessageTypeDefinition
|
||||
Int32Value: MessageTypeDefinition
|
||||
Int64Value: MessageTypeDefinition
|
||||
StringValue: MessageTypeDefinition
|
||||
Timestamp: MessageTypeDefinition
|
||||
UInt32Value: MessageTypeDefinition
|
||||
UInt64Value: MessageTypeDefinition
|
||||
}
|
||||
}
|
||||
grpc: {
|
||||
channelz: {
|
||||
v1: {
|
||||
Address: MessageTypeDefinition
|
||||
Channel: MessageTypeDefinition
|
||||
ChannelConnectivityState: MessageTypeDefinition
|
||||
ChannelData: MessageTypeDefinition
|
||||
ChannelRef: MessageTypeDefinition
|
||||
ChannelTrace: MessageTypeDefinition
|
||||
ChannelTraceEvent: MessageTypeDefinition
|
||||
/**
|
||||
* Channelz is a service exposed by gRPC servers that provides detailed debug
|
||||
* information.
|
||||
*/
|
||||
Channelz: SubtypeConstructor<typeof grpc.Client, _grpc_channelz_v1_ChannelzClient> & { service: _grpc_channelz_v1_ChannelzDefinition }
|
||||
GetChannelRequest: MessageTypeDefinition
|
||||
GetChannelResponse: MessageTypeDefinition
|
||||
GetServerRequest: MessageTypeDefinition
|
||||
GetServerResponse: MessageTypeDefinition
|
||||
GetServerSocketsRequest: MessageTypeDefinition
|
||||
GetServerSocketsResponse: MessageTypeDefinition
|
||||
GetServersRequest: MessageTypeDefinition
|
||||
GetServersResponse: MessageTypeDefinition
|
||||
GetSocketRequest: MessageTypeDefinition
|
||||
GetSocketResponse: MessageTypeDefinition
|
||||
GetSubchannelRequest: MessageTypeDefinition
|
||||
GetSubchannelResponse: MessageTypeDefinition
|
||||
GetTopChannelsRequest: MessageTypeDefinition
|
||||
GetTopChannelsResponse: MessageTypeDefinition
|
||||
Security: MessageTypeDefinition
|
||||
Server: MessageTypeDefinition
|
||||
ServerData: MessageTypeDefinition
|
||||
ServerRef: MessageTypeDefinition
|
||||
Socket: MessageTypeDefinition
|
||||
SocketData: MessageTypeDefinition
|
||||
SocketOption: MessageTypeDefinition
|
||||
SocketOptionLinger: MessageTypeDefinition
|
||||
SocketOptionTcpInfo: MessageTypeDefinition
|
||||
SocketOptionTimeout: MessageTypeDefinition
|
||||
SocketRef: MessageTypeDefinition
|
||||
Subchannel: MessageTypeDefinition
|
||||
SubchannelRef: MessageTypeDefinition
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
13
node_modules/@grpc/grpc-js/src/generated/google/protobuf/Any.ts
generated
vendored
Normal file
13
node_modules/@grpc/grpc-js/src/generated/google/protobuf/Any.ts
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// Original file: null
|
||||
|
||||
import type { AnyExtension } from '@grpc/proto-loader';
|
||||
|
||||
export type Any = AnyExtension | {
|
||||
type_url: string;
|
||||
value: Buffer | Uint8Array | string;
|
||||
}
|
||||
|
||||
export interface Any__Output {
|
||||
'type_url': (string);
|
||||
'value': (Buffer);
|
||||
}
|
||||
10
node_modules/@grpc/grpc-js/src/generated/google/protobuf/BoolValue.ts
generated
vendored
Normal file
10
node_modules/@grpc/grpc-js/src/generated/google/protobuf/BoolValue.ts
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// Original file: null
|
||||
|
||||
|
||||
export interface BoolValue {
|
||||
'value'?: (boolean);
|
||||
}
|
||||
|
||||
export interface BoolValue__Output {
|
||||
'value': (boolean);
|
||||
}
|
||||
10
node_modules/@grpc/grpc-js/src/generated/google/protobuf/BytesValue.ts
generated
vendored
Normal file
10
node_modules/@grpc/grpc-js/src/generated/google/protobuf/BytesValue.ts
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// Original file: null
|
||||
|
||||
|
||||
export interface BytesValue {
|
||||
'value'?: (Buffer | Uint8Array | string);
|
||||
}
|
||||
|
||||
export interface BytesValue__Output {
|
||||
'value': (Buffer);
|
||||
}
|
||||
10
node_modules/@grpc/grpc-js/src/generated/google/protobuf/DoubleValue.ts
generated
vendored
Normal file
10
node_modules/@grpc/grpc-js/src/generated/google/protobuf/DoubleValue.ts
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// Original file: null
|
||||
|
||||
|
||||
export interface DoubleValue {
|
||||
'value'?: (number | string);
|
||||
}
|
||||
|
||||
export interface DoubleValue__Output {
|
||||
'value': (number);
|
||||
}
|
||||
13
node_modules/@grpc/grpc-js/src/generated/google/protobuf/Duration.ts
generated
vendored
Normal file
13
node_modules/@grpc/grpc-js/src/generated/google/protobuf/Duration.ts
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// Original file: null
|
||||
|
||||
import type { Long } from '@grpc/proto-loader';
|
||||
|
||||
export interface Duration {
|
||||
'seconds'?: (number | string | Long);
|
||||
'nanos'?: (number);
|
||||
}
|
||||
|
||||
export interface Duration__Output {
|
||||
'seconds': (string);
|
||||
'nanos': (number);
|
||||
}
|
||||
10
node_modules/@grpc/grpc-js/src/generated/google/protobuf/FloatValue.ts
generated
vendored
Normal file
10
node_modules/@grpc/grpc-js/src/generated/google/protobuf/FloatValue.ts
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// Original file: null
|
||||
|
||||
|
||||
export interface FloatValue {
|
||||
'value'?: (number | string);
|
||||
}
|
||||
|
||||
export interface FloatValue__Output {
|
||||
'value': (number);
|
||||
}
|
||||
10
node_modules/@grpc/grpc-js/src/generated/google/protobuf/Int32Value.ts
generated
vendored
Normal file
10
node_modules/@grpc/grpc-js/src/generated/google/protobuf/Int32Value.ts
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// Original file: null
|
||||
|
||||
|
||||
export interface Int32Value {
|
||||
'value'?: (number);
|
||||
}
|
||||
|
||||
export interface Int32Value__Output {
|
||||
'value': (number);
|
||||
}
|
||||
11
node_modules/@grpc/grpc-js/src/generated/google/protobuf/Int64Value.ts
generated
vendored
Normal file
11
node_modules/@grpc/grpc-js/src/generated/google/protobuf/Int64Value.ts
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// Original file: null
|
||||
|
||||
import type { Long } from '@grpc/proto-loader';
|
||||
|
||||
export interface Int64Value {
|
||||
'value'?: (number | string | Long);
|
||||
}
|
||||
|
||||
export interface Int64Value__Output {
|
||||
'value': (string);
|
||||
}
|
||||
10
node_modules/@grpc/grpc-js/src/generated/google/protobuf/StringValue.ts
generated
vendored
Normal file
10
node_modules/@grpc/grpc-js/src/generated/google/protobuf/StringValue.ts
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// Original file: null
|
||||
|
||||
|
||||
export interface StringValue {
|
||||
'value'?: (string);
|
||||
}
|
||||
|
||||
export interface StringValue__Output {
|
||||
'value': (string);
|
||||
}
|
||||
13
node_modules/@grpc/grpc-js/src/generated/google/protobuf/Timestamp.ts
generated
vendored
Normal file
13
node_modules/@grpc/grpc-js/src/generated/google/protobuf/Timestamp.ts
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// Original file: null
|
||||
|
||||
import type { Long } from '@grpc/proto-loader';
|
||||
|
||||
export interface Timestamp {
|
||||
'seconds'?: (number | string | Long);
|
||||
'nanos'?: (number);
|
||||
}
|
||||
|
||||
export interface Timestamp__Output {
|
||||
'seconds': (string);
|
||||
'nanos': (number);
|
||||
}
|
||||
10
node_modules/@grpc/grpc-js/src/generated/google/protobuf/UInt32Value.ts
generated
vendored
Normal file
10
node_modules/@grpc/grpc-js/src/generated/google/protobuf/UInt32Value.ts
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// Original file: null
|
||||
|
||||
|
||||
export interface UInt32Value {
|
||||
'value'?: (number);
|
||||
}
|
||||
|
||||
export interface UInt32Value__Output {
|
||||
'value': (number);
|
||||
}
|
||||
11
node_modules/@grpc/grpc-js/src/generated/google/protobuf/UInt64Value.ts
generated
vendored
Normal file
11
node_modules/@grpc/grpc-js/src/generated/google/protobuf/UInt64Value.ts
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// Original file: null
|
||||
|
||||
import type { Long } from '@grpc/proto-loader';
|
||||
|
||||
export interface UInt64Value {
|
||||
'value'?: (number | string | Long);
|
||||
}
|
||||
|
||||
export interface UInt64Value__Output {
|
||||
'value': (string);
|
||||
}
|
||||
89
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/Address.ts
generated
vendored
Normal file
89
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/Address.ts
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Any as _google_protobuf_Any, Any__Output as _google_protobuf_Any__Output } from '../../../google/protobuf/Any';
|
||||
|
||||
/**
|
||||
* An address type not included above.
|
||||
*/
|
||||
export interface _grpc_channelz_v1_Address_OtherAddress {
|
||||
/**
|
||||
* The human readable version of the value. This value should be set.
|
||||
*/
|
||||
'name'?: (string);
|
||||
/**
|
||||
* The actual address message.
|
||||
*/
|
||||
'value'?: (_google_protobuf_Any | null);
|
||||
}
|
||||
|
||||
/**
|
||||
* An address type not included above.
|
||||
*/
|
||||
export interface _grpc_channelz_v1_Address_OtherAddress__Output {
|
||||
/**
|
||||
* The human readable version of the value. This value should be set.
|
||||
*/
|
||||
'name': (string);
|
||||
/**
|
||||
* The actual address message.
|
||||
*/
|
||||
'value': (_google_protobuf_Any__Output | null);
|
||||
}
|
||||
|
||||
export interface _grpc_channelz_v1_Address_TcpIpAddress {
|
||||
/**
|
||||
* Either the IPv4 or IPv6 address in bytes. Will be either 4 bytes or 16
|
||||
* bytes in length.
|
||||
*/
|
||||
'ip_address'?: (Buffer | Uint8Array | string);
|
||||
/**
|
||||
* 0-64k, or -1 if not appropriate.
|
||||
*/
|
||||
'port'?: (number);
|
||||
}
|
||||
|
||||
export interface _grpc_channelz_v1_Address_TcpIpAddress__Output {
|
||||
/**
|
||||
* Either the IPv4 or IPv6 address in bytes. Will be either 4 bytes or 16
|
||||
* bytes in length.
|
||||
*/
|
||||
'ip_address': (Buffer);
|
||||
/**
|
||||
* 0-64k, or -1 if not appropriate.
|
||||
*/
|
||||
'port': (number);
|
||||
}
|
||||
|
||||
/**
|
||||
* A Unix Domain Socket address.
|
||||
*/
|
||||
export interface _grpc_channelz_v1_Address_UdsAddress {
|
||||
'filename'?: (string);
|
||||
}
|
||||
|
||||
/**
|
||||
* A Unix Domain Socket address.
|
||||
*/
|
||||
export interface _grpc_channelz_v1_Address_UdsAddress__Output {
|
||||
'filename': (string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Address represents the address used to create the socket.
|
||||
*/
|
||||
export interface Address {
|
||||
'tcpip_address'?: (_grpc_channelz_v1_Address_TcpIpAddress | null);
|
||||
'uds_address'?: (_grpc_channelz_v1_Address_UdsAddress | null);
|
||||
'other_address'?: (_grpc_channelz_v1_Address_OtherAddress | null);
|
||||
'address'?: "tcpip_address"|"uds_address"|"other_address";
|
||||
}
|
||||
|
||||
/**
|
||||
* Address represents the address used to create the socket.
|
||||
*/
|
||||
export interface Address__Output {
|
||||
'tcpip_address'?: (_grpc_channelz_v1_Address_TcpIpAddress__Output | null);
|
||||
'uds_address'?: (_grpc_channelz_v1_Address_UdsAddress__Output | null);
|
||||
'other_address'?: (_grpc_channelz_v1_Address_OtherAddress__Output | null);
|
||||
'address': "tcpip_address"|"uds_address"|"other_address";
|
||||
}
|
||||
68
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/Channel.ts
generated
vendored
Normal file
68
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/Channel.ts
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { ChannelRef as _grpc_channelz_v1_ChannelRef, ChannelRef__Output as _grpc_channelz_v1_ChannelRef__Output } from '../../../grpc/channelz/v1/ChannelRef';
|
||||
import type { ChannelData as _grpc_channelz_v1_ChannelData, ChannelData__Output as _grpc_channelz_v1_ChannelData__Output } from '../../../grpc/channelz/v1/ChannelData';
|
||||
import type { SubchannelRef as _grpc_channelz_v1_SubchannelRef, SubchannelRef__Output as _grpc_channelz_v1_SubchannelRef__Output } from '../../../grpc/channelz/v1/SubchannelRef';
|
||||
import type { SocketRef as _grpc_channelz_v1_SocketRef, SocketRef__Output as _grpc_channelz_v1_SocketRef__Output } from '../../../grpc/channelz/v1/SocketRef';
|
||||
|
||||
/**
|
||||
* Channel is a logical grouping of channels, subchannels, and sockets.
|
||||
*/
|
||||
export interface Channel {
|
||||
/**
|
||||
* The identifier for this channel. This should bet set.
|
||||
*/
|
||||
'ref'?: (_grpc_channelz_v1_ChannelRef | null);
|
||||
/**
|
||||
* Data specific to this channel.
|
||||
*/
|
||||
'data'?: (_grpc_channelz_v1_ChannelData | null);
|
||||
/**
|
||||
* There are no ordering guarantees on the order of channel refs.
|
||||
* There may not be cycles in the ref graph.
|
||||
* A channel ref may be present in more than one channel or subchannel.
|
||||
*/
|
||||
'channel_ref'?: (_grpc_channelz_v1_ChannelRef)[];
|
||||
/**
|
||||
* At most one of 'channel_ref+subchannel_ref' and 'socket' is set.
|
||||
* There are no ordering guarantees on the order of subchannel refs.
|
||||
* There may not be cycles in the ref graph.
|
||||
* A sub channel ref may be present in more than one channel or subchannel.
|
||||
*/
|
||||
'subchannel_ref'?: (_grpc_channelz_v1_SubchannelRef)[];
|
||||
/**
|
||||
* There are no ordering guarantees on the order of sockets.
|
||||
*/
|
||||
'socket_ref'?: (_grpc_channelz_v1_SocketRef)[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel is a logical grouping of channels, subchannels, and sockets.
|
||||
*/
|
||||
export interface Channel__Output {
|
||||
/**
|
||||
* The identifier for this channel. This should bet set.
|
||||
*/
|
||||
'ref': (_grpc_channelz_v1_ChannelRef__Output | null);
|
||||
/**
|
||||
* Data specific to this channel.
|
||||
*/
|
||||
'data': (_grpc_channelz_v1_ChannelData__Output | null);
|
||||
/**
|
||||
* There are no ordering guarantees on the order of channel refs.
|
||||
* There may not be cycles in the ref graph.
|
||||
* A channel ref may be present in more than one channel or subchannel.
|
||||
*/
|
||||
'channel_ref': (_grpc_channelz_v1_ChannelRef__Output)[];
|
||||
/**
|
||||
* At most one of 'channel_ref+subchannel_ref' and 'socket' is set.
|
||||
* There are no ordering guarantees on the order of subchannel refs.
|
||||
* There may not be cycles in the ref graph.
|
||||
* A sub channel ref may be present in more than one channel or subchannel.
|
||||
*/
|
||||
'subchannel_ref': (_grpc_channelz_v1_SubchannelRef__Output)[];
|
||||
/**
|
||||
* There are no ordering guarantees on the order of sockets.
|
||||
*/
|
||||
'socket_ref': (_grpc_channelz_v1_SocketRef__Output)[];
|
||||
}
|
||||
45
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/ChannelConnectivityState.ts
generated
vendored
Normal file
45
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/ChannelConnectivityState.ts
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
export const _grpc_channelz_v1_ChannelConnectivityState_State = {
|
||||
UNKNOWN: 'UNKNOWN',
|
||||
IDLE: 'IDLE',
|
||||
CONNECTING: 'CONNECTING',
|
||||
READY: 'READY',
|
||||
TRANSIENT_FAILURE: 'TRANSIENT_FAILURE',
|
||||
SHUTDOWN: 'SHUTDOWN',
|
||||
} as const;
|
||||
|
||||
export type _grpc_channelz_v1_ChannelConnectivityState_State =
|
||||
| 'UNKNOWN'
|
||||
| 0
|
||||
| 'IDLE'
|
||||
| 1
|
||||
| 'CONNECTING'
|
||||
| 2
|
||||
| 'READY'
|
||||
| 3
|
||||
| 'TRANSIENT_FAILURE'
|
||||
| 4
|
||||
| 'SHUTDOWN'
|
||||
| 5
|
||||
|
||||
export type _grpc_channelz_v1_ChannelConnectivityState_State__Output = typeof _grpc_channelz_v1_ChannelConnectivityState_State[keyof typeof _grpc_channelz_v1_ChannelConnectivityState_State]
|
||||
|
||||
/**
|
||||
* These come from the specified states in this document:
|
||||
* https://github.com/grpc/grpc/blob/master/doc/connectivity-semantics-and-api.md
|
||||
*/
|
||||
export interface ChannelConnectivityState {
|
||||
'state'?: (_grpc_channelz_v1_ChannelConnectivityState_State);
|
||||
}
|
||||
|
||||
/**
|
||||
* These come from the specified states in this document:
|
||||
* https://github.com/grpc/grpc/blob/master/doc/connectivity-semantics-and-api.md
|
||||
*/
|
||||
export interface ChannelConnectivityState__Output {
|
||||
'state': (_grpc_channelz_v1_ChannelConnectivityState_State__Output);
|
||||
}
|
||||
76
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/ChannelData.ts
generated
vendored
Normal file
76
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/ChannelData.ts
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { ChannelConnectivityState as _grpc_channelz_v1_ChannelConnectivityState, ChannelConnectivityState__Output as _grpc_channelz_v1_ChannelConnectivityState__Output } from '../../../grpc/channelz/v1/ChannelConnectivityState';
|
||||
import type { ChannelTrace as _grpc_channelz_v1_ChannelTrace, ChannelTrace__Output as _grpc_channelz_v1_ChannelTrace__Output } from '../../../grpc/channelz/v1/ChannelTrace';
|
||||
import type { Timestamp as _google_protobuf_Timestamp, Timestamp__Output as _google_protobuf_Timestamp__Output } from '../../../google/protobuf/Timestamp';
|
||||
import type { Long } from '@grpc/proto-loader';
|
||||
|
||||
/**
|
||||
* Channel data is data related to a specific Channel or Subchannel.
|
||||
*/
|
||||
export interface ChannelData {
|
||||
/**
|
||||
* The connectivity state of the channel or subchannel. Implementations
|
||||
* should always set this.
|
||||
*/
|
||||
'state'?: (_grpc_channelz_v1_ChannelConnectivityState | null);
|
||||
/**
|
||||
* The target this channel originally tried to connect to. May be absent
|
||||
*/
|
||||
'target'?: (string);
|
||||
/**
|
||||
* A trace of recent events on the channel. May be absent.
|
||||
*/
|
||||
'trace'?: (_grpc_channelz_v1_ChannelTrace | null);
|
||||
/**
|
||||
* The number of calls started on the channel
|
||||
*/
|
||||
'calls_started'?: (number | string | Long);
|
||||
/**
|
||||
* The number of calls that have completed with an OK status
|
||||
*/
|
||||
'calls_succeeded'?: (number | string | Long);
|
||||
/**
|
||||
* The number of calls that have completed with a non-OK status
|
||||
*/
|
||||
'calls_failed'?: (number | string | Long);
|
||||
/**
|
||||
* The last time a call was started on the channel.
|
||||
*/
|
||||
'last_call_started_timestamp'?: (_google_protobuf_Timestamp | null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel data is data related to a specific Channel or Subchannel.
|
||||
*/
|
||||
export interface ChannelData__Output {
|
||||
/**
|
||||
* The connectivity state of the channel or subchannel. Implementations
|
||||
* should always set this.
|
||||
*/
|
||||
'state': (_grpc_channelz_v1_ChannelConnectivityState__Output | null);
|
||||
/**
|
||||
* The target this channel originally tried to connect to. May be absent
|
||||
*/
|
||||
'target': (string);
|
||||
/**
|
||||
* A trace of recent events on the channel. May be absent.
|
||||
*/
|
||||
'trace': (_grpc_channelz_v1_ChannelTrace__Output | null);
|
||||
/**
|
||||
* The number of calls started on the channel
|
||||
*/
|
||||
'calls_started': (string);
|
||||
/**
|
||||
* The number of calls that have completed with an OK status
|
||||
*/
|
||||
'calls_succeeded': (string);
|
||||
/**
|
||||
* The number of calls that have completed with a non-OK status
|
||||
*/
|
||||
'calls_failed': (string);
|
||||
/**
|
||||
* The last time a call was started on the channel.
|
||||
*/
|
||||
'last_call_started_timestamp': (_google_protobuf_Timestamp__Output | null);
|
||||
}
|
||||
31
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/ChannelRef.ts
generated
vendored
Normal file
31
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/ChannelRef.ts
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Long } from '@grpc/proto-loader';
|
||||
|
||||
/**
|
||||
* ChannelRef is a reference to a Channel.
|
||||
*/
|
||||
export interface ChannelRef {
|
||||
/**
|
||||
* The globally unique id for this channel. Must be a positive number.
|
||||
*/
|
||||
'channel_id'?: (number | string | Long);
|
||||
/**
|
||||
* An optional name associated with the channel.
|
||||
*/
|
||||
'name'?: (string);
|
||||
}
|
||||
|
||||
/**
|
||||
* ChannelRef is a reference to a Channel.
|
||||
*/
|
||||
export interface ChannelRef__Output {
|
||||
/**
|
||||
* The globally unique id for this channel. Must be a positive number.
|
||||
*/
|
||||
'channel_id': (string);
|
||||
/**
|
||||
* An optional name associated with the channel.
|
||||
*/
|
||||
'name': (string);
|
||||
}
|
||||
45
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/ChannelTrace.ts
generated
vendored
Normal file
45
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/ChannelTrace.ts
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Timestamp as _google_protobuf_Timestamp, Timestamp__Output as _google_protobuf_Timestamp__Output } from '../../../google/protobuf/Timestamp';
|
||||
import type { ChannelTraceEvent as _grpc_channelz_v1_ChannelTraceEvent, ChannelTraceEvent__Output as _grpc_channelz_v1_ChannelTraceEvent__Output } from '../../../grpc/channelz/v1/ChannelTraceEvent';
|
||||
import type { Long } from '@grpc/proto-loader';
|
||||
|
||||
/**
|
||||
* ChannelTrace represents the recent events that have occurred on the channel.
|
||||
*/
|
||||
export interface ChannelTrace {
|
||||
/**
|
||||
* Number of events ever logged in this tracing object. This can differ from
|
||||
* events.size() because events can be overwritten or garbage collected by
|
||||
* implementations.
|
||||
*/
|
||||
'num_events_logged'?: (number | string | Long);
|
||||
/**
|
||||
* Time that this channel was created.
|
||||
*/
|
||||
'creation_timestamp'?: (_google_protobuf_Timestamp | null);
|
||||
/**
|
||||
* List of events that have occurred on this channel.
|
||||
*/
|
||||
'events'?: (_grpc_channelz_v1_ChannelTraceEvent)[];
|
||||
}
|
||||
|
||||
/**
|
||||
* ChannelTrace represents the recent events that have occurred on the channel.
|
||||
*/
|
||||
export interface ChannelTrace__Output {
|
||||
/**
|
||||
* Number of events ever logged in this tracing object. This can differ from
|
||||
* events.size() because events can be overwritten or garbage collected by
|
||||
* implementations.
|
||||
*/
|
||||
'num_events_logged': (string);
|
||||
/**
|
||||
* Time that this channel was created.
|
||||
*/
|
||||
'creation_timestamp': (_google_protobuf_Timestamp__Output | null);
|
||||
/**
|
||||
* List of events that have occurred on this channel.
|
||||
*/
|
||||
'events': (_grpc_channelz_v1_ChannelTraceEvent__Output)[];
|
||||
}
|
||||
91
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/ChannelTraceEvent.ts
generated
vendored
Normal file
91
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/ChannelTraceEvent.ts
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Timestamp as _google_protobuf_Timestamp, Timestamp__Output as _google_protobuf_Timestamp__Output } from '../../../google/protobuf/Timestamp';
|
||||
import type { ChannelRef as _grpc_channelz_v1_ChannelRef, ChannelRef__Output as _grpc_channelz_v1_ChannelRef__Output } from '../../../grpc/channelz/v1/ChannelRef';
|
||||
import type { SubchannelRef as _grpc_channelz_v1_SubchannelRef, SubchannelRef__Output as _grpc_channelz_v1_SubchannelRef__Output } from '../../../grpc/channelz/v1/SubchannelRef';
|
||||
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
/**
|
||||
* The supported severity levels of trace events.
|
||||
*/
|
||||
export const _grpc_channelz_v1_ChannelTraceEvent_Severity = {
|
||||
CT_UNKNOWN: 'CT_UNKNOWN',
|
||||
CT_INFO: 'CT_INFO',
|
||||
CT_WARNING: 'CT_WARNING',
|
||||
CT_ERROR: 'CT_ERROR',
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* The supported severity levels of trace events.
|
||||
*/
|
||||
export type _grpc_channelz_v1_ChannelTraceEvent_Severity =
|
||||
| 'CT_UNKNOWN'
|
||||
| 0
|
||||
| 'CT_INFO'
|
||||
| 1
|
||||
| 'CT_WARNING'
|
||||
| 2
|
||||
| 'CT_ERROR'
|
||||
| 3
|
||||
|
||||
/**
|
||||
* The supported severity levels of trace events.
|
||||
*/
|
||||
export type _grpc_channelz_v1_ChannelTraceEvent_Severity__Output = typeof _grpc_channelz_v1_ChannelTraceEvent_Severity[keyof typeof _grpc_channelz_v1_ChannelTraceEvent_Severity]
|
||||
|
||||
/**
|
||||
* A trace event is an interesting thing that happened to a channel or
|
||||
* subchannel, such as creation, address resolution, subchannel creation, etc.
|
||||
*/
|
||||
export interface ChannelTraceEvent {
|
||||
/**
|
||||
* High level description of the event.
|
||||
*/
|
||||
'description'?: (string);
|
||||
/**
|
||||
* the severity of the trace event
|
||||
*/
|
||||
'severity'?: (_grpc_channelz_v1_ChannelTraceEvent_Severity);
|
||||
/**
|
||||
* When this event occurred.
|
||||
*/
|
||||
'timestamp'?: (_google_protobuf_Timestamp | null);
|
||||
'channel_ref'?: (_grpc_channelz_v1_ChannelRef | null);
|
||||
'subchannel_ref'?: (_grpc_channelz_v1_SubchannelRef | null);
|
||||
/**
|
||||
* ref of referenced channel or subchannel.
|
||||
* Optional, only present if this event refers to a child object. For example,
|
||||
* this field would be filled if this trace event was for a subchannel being
|
||||
* created.
|
||||
*/
|
||||
'child_ref'?: "channel_ref"|"subchannel_ref";
|
||||
}
|
||||
|
||||
/**
|
||||
* A trace event is an interesting thing that happened to a channel or
|
||||
* subchannel, such as creation, address resolution, subchannel creation, etc.
|
||||
*/
|
||||
export interface ChannelTraceEvent__Output {
|
||||
/**
|
||||
* High level description of the event.
|
||||
*/
|
||||
'description': (string);
|
||||
/**
|
||||
* the severity of the trace event
|
||||
*/
|
||||
'severity': (_grpc_channelz_v1_ChannelTraceEvent_Severity__Output);
|
||||
/**
|
||||
* When this event occurred.
|
||||
*/
|
||||
'timestamp': (_google_protobuf_Timestamp__Output | null);
|
||||
'channel_ref'?: (_grpc_channelz_v1_ChannelRef__Output | null);
|
||||
'subchannel_ref'?: (_grpc_channelz_v1_SubchannelRef__Output | null);
|
||||
/**
|
||||
* ref of referenced channel or subchannel.
|
||||
* Optional, only present if this event refers to a child object. For example,
|
||||
* this field would be filled if this trace event was for a subchannel being
|
||||
* created.
|
||||
*/
|
||||
'child_ref': "channel_ref"|"subchannel_ref";
|
||||
}
|
||||
178
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/Channelz.ts
generated
vendored
Normal file
178
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/Channelz.ts
generated
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type * as grpc from '../../../../index'
|
||||
import type { MethodDefinition } from '@grpc/proto-loader'
|
||||
import type { GetChannelRequest as _grpc_channelz_v1_GetChannelRequest, GetChannelRequest__Output as _grpc_channelz_v1_GetChannelRequest__Output } from '../../../grpc/channelz/v1/GetChannelRequest';
|
||||
import type { GetChannelResponse as _grpc_channelz_v1_GetChannelResponse, GetChannelResponse__Output as _grpc_channelz_v1_GetChannelResponse__Output } from '../../../grpc/channelz/v1/GetChannelResponse';
|
||||
import type { GetServerRequest as _grpc_channelz_v1_GetServerRequest, GetServerRequest__Output as _grpc_channelz_v1_GetServerRequest__Output } from '../../../grpc/channelz/v1/GetServerRequest';
|
||||
import type { GetServerResponse as _grpc_channelz_v1_GetServerResponse, GetServerResponse__Output as _grpc_channelz_v1_GetServerResponse__Output } from '../../../grpc/channelz/v1/GetServerResponse';
|
||||
import type { GetServerSocketsRequest as _grpc_channelz_v1_GetServerSocketsRequest, GetServerSocketsRequest__Output as _grpc_channelz_v1_GetServerSocketsRequest__Output } from '../../../grpc/channelz/v1/GetServerSocketsRequest';
|
||||
import type { GetServerSocketsResponse as _grpc_channelz_v1_GetServerSocketsResponse, GetServerSocketsResponse__Output as _grpc_channelz_v1_GetServerSocketsResponse__Output } from '../../../grpc/channelz/v1/GetServerSocketsResponse';
|
||||
import type { GetServersRequest as _grpc_channelz_v1_GetServersRequest, GetServersRequest__Output as _grpc_channelz_v1_GetServersRequest__Output } from '../../../grpc/channelz/v1/GetServersRequest';
|
||||
import type { GetServersResponse as _grpc_channelz_v1_GetServersResponse, GetServersResponse__Output as _grpc_channelz_v1_GetServersResponse__Output } from '../../../grpc/channelz/v1/GetServersResponse';
|
||||
import type { GetSocketRequest as _grpc_channelz_v1_GetSocketRequest, GetSocketRequest__Output as _grpc_channelz_v1_GetSocketRequest__Output } from '../../../grpc/channelz/v1/GetSocketRequest';
|
||||
import type { GetSocketResponse as _grpc_channelz_v1_GetSocketResponse, GetSocketResponse__Output as _grpc_channelz_v1_GetSocketResponse__Output } from '../../../grpc/channelz/v1/GetSocketResponse';
|
||||
import type { GetSubchannelRequest as _grpc_channelz_v1_GetSubchannelRequest, GetSubchannelRequest__Output as _grpc_channelz_v1_GetSubchannelRequest__Output } from '../../../grpc/channelz/v1/GetSubchannelRequest';
|
||||
import type { GetSubchannelResponse as _grpc_channelz_v1_GetSubchannelResponse, GetSubchannelResponse__Output as _grpc_channelz_v1_GetSubchannelResponse__Output } from '../../../grpc/channelz/v1/GetSubchannelResponse';
|
||||
import type { GetTopChannelsRequest as _grpc_channelz_v1_GetTopChannelsRequest, GetTopChannelsRequest__Output as _grpc_channelz_v1_GetTopChannelsRequest__Output } from '../../../grpc/channelz/v1/GetTopChannelsRequest';
|
||||
import type { GetTopChannelsResponse as _grpc_channelz_v1_GetTopChannelsResponse, GetTopChannelsResponse__Output as _grpc_channelz_v1_GetTopChannelsResponse__Output } from '../../../grpc/channelz/v1/GetTopChannelsResponse';
|
||||
|
||||
/**
|
||||
* Channelz is a service exposed by gRPC servers that provides detailed debug
|
||||
* information.
|
||||
*/
|
||||
export interface ChannelzClient extends grpc.Client {
|
||||
/**
|
||||
* Returns a single Channel, or else a NOT_FOUND code.
|
||||
*/
|
||||
GetChannel(argument: _grpc_channelz_v1_GetChannelRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetChannelResponse__Output>): grpc.ClientUnaryCall;
|
||||
GetChannel(argument: _grpc_channelz_v1_GetChannelRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_grpc_channelz_v1_GetChannelResponse__Output>): grpc.ClientUnaryCall;
|
||||
GetChannel(argument: _grpc_channelz_v1_GetChannelRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetChannelResponse__Output>): grpc.ClientUnaryCall;
|
||||
GetChannel(argument: _grpc_channelz_v1_GetChannelRequest, callback: grpc.requestCallback<_grpc_channelz_v1_GetChannelResponse__Output>): grpc.ClientUnaryCall;
|
||||
|
||||
/**
|
||||
* Returns a single Server, or else a NOT_FOUND code.
|
||||
*/
|
||||
GetServer(argument: _grpc_channelz_v1_GetServerRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetServerResponse__Output>): grpc.ClientUnaryCall;
|
||||
GetServer(argument: _grpc_channelz_v1_GetServerRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_grpc_channelz_v1_GetServerResponse__Output>): grpc.ClientUnaryCall;
|
||||
GetServer(argument: _grpc_channelz_v1_GetServerRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetServerResponse__Output>): grpc.ClientUnaryCall;
|
||||
GetServer(argument: _grpc_channelz_v1_GetServerRequest, callback: grpc.requestCallback<_grpc_channelz_v1_GetServerResponse__Output>): grpc.ClientUnaryCall;
|
||||
/**
|
||||
* Returns a single Server, or else a NOT_FOUND code.
|
||||
*/
|
||||
getServer(argument: _grpc_channelz_v1_GetServerRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetServerResponse__Output>): grpc.ClientUnaryCall;
|
||||
getServer(argument: _grpc_channelz_v1_GetServerRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_grpc_channelz_v1_GetServerResponse__Output>): grpc.ClientUnaryCall;
|
||||
getServer(argument: _grpc_channelz_v1_GetServerRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetServerResponse__Output>): grpc.ClientUnaryCall;
|
||||
getServer(argument: _grpc_channelz_v1_GetServerRequest, callback: grpc.requestCallback<_grpc_channelz_v1_GetServerResponse__Output>): grpc.ClientUnaryCall;
|
||||
|
||||
/**
|
||||
* Gets all server sockets that exist in the process.
|
||||
*/
|
||||
GetServerSockets(argument: _grpc_channelz_v1_GetServerSocketsRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetServerSocketsResponse__Output>): grpc.ClientUnaryCall;
|
||||
GetServerSockets(argument: _grpc_channelz_v1_GetServerSocketsRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_grpc_channelz_v1_GetServerSocketsResponse__Output>): grpc.ClientUnaryCall;
|
||||
GetServerSockets(argument: _grpc_channelz_v1_GetServerSocketsRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetServerSocketsResponse__Output>): grpc.ClientUnaryCall;
|
||||
GetServerSockets(argument: _grpc_channelz_v1_GetServerSocketsRequest, callback: grpc.requestCallback<_grpc_channelz_v1_GetServerSocketsResponse__Output>): grpc.ClientUnaryCall;
|
||||
/**
|
||||
* Gets all server sockets that exist in the process.
|
||||
*/
|
||||
getServerSockets(argument: _grpc_channelz_v1_GetServerSocketsRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetServerSocketsResponse__Output>): grpc.ClientUnaryCall;
|
||||
getServerSockets(argument: _grpc_channelz_v1_GetServerSocketsRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_grpc_channelz_v1_GetServerSocketsResponse__Output>): grpc.ClientUnaryCall;
|
||||
getServerSockets(argument: _grpc_channelz_v1_GetServerSocketsRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetServerSocketsResponse__Output>): grpc.ClientUnaryCall;
|
||||
getServerSockets(argument: _grpc_channelz_v1_GetServerSocketsRequest, callback: grpc.requestCallback<_grpc_channelz_v1_GetServerSocketsResponse__Output>): grpc.ClientUnaryCall;
|
||||
|
||||
/**
|
||||
* Gets all servers that exist in the process.
|
||||
*/
|
||||
GetServers(argument: _grpc_channelz_v1_GetServersRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetServersResponse__Output>): grpc.ClientUnaryCall;
|
||||
GetServers(argument: _grpc_channelz_v1_GetServersRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_grpc_channelz_v1_GetServersResponse__Output>): grpc.ClientUnaryCall;
|
||||
GetServers(argument: _grpc_channelz_v1_GetServersRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetServersResponse__Output>): grpc.ClientUnaryCall;
|
||||
GetServers(argument: _grpc_channelz_v1_GetServersRequest, callback: grpc.requestCallback<_grpc_channelz_v1_GetServersResponse__Output>): grpc.ClientUnaryCall;
|
||||
/**
|
||||
* Gets all servers that exist in the process.
|
||||
*/
|
||||
getServers(argument: _grpc_channelz_v1_GetServersRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetServersResponse__Output>): grpc.ClientUnaryCall;
|
||||
getServers(argument: _grpc_channelz_v1_GetServersRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_grpc_channelz_v1_GetServersResponse__Output>): grpc.ClientUnaryCall;
|
||||
getServers(argument: _grpc_channelz_v1_GetServersRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetServersResponse__Output>): grpc.ClientUnaryCall;
|
||||
getServers(argument: _grpc_channelz_v1_GetServersRequest, callback: grpc.requestCallback<_grpc_channelz_v1_GetServersResponse__Output>): grpc.ClientUnaryCall;
|
||||
|
||||
/**
|
||||
* Returns a single Socket or else a NOT_FOUND code.
|
||||
*/
|
||||
GetSocket(argument: _grpc_channelz_v1_GetSocketRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetSocketResponse__Output>): grpc.ClientUnaryCall;
|
||||
GetSocket(argument: _grpc_channelz_v1_GetSocketRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_grpc_channelz_v1_GetSocketResponse__Output>): grpc.ClientUnaryCall;
|
||||
GetSocket(argument: _grpc_channelz_v1_GetSocketRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetSocketResponse__Output>): grpc.ClientUnaryCall;
|
||||
GetSocket(argument: _grpc_channelz_v1_GetSocketRequest, callback: grpc.requestCallback<_grpc_channelz_v1_GetSocketResponse__Output>): grpc.ClientUnaryCall;
|
||||
/**
|
||||
* Returns a single Socket or else a NOT_FOUND code.
|
||||
*/
|
||||
getSocket(argument: _grpc_channelz_v1_GetSocketRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetSocketResponse__Output>): grpc.ClientUnaryCall;
|
||||
getSocket(argument: _grpc_channelz_v1_GetSocketRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_grpc_channelz_v1_GetSocketResponse__Output>): grpc.ClientUnaryCall;
|
||||
getSocket(argument: _grpc_channelz_v1_GetSocketRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetSocketResponse__Output>): grpc.ClientUnaryCall;
|
||||
getSocket(argument: _grpc_channelz_v1_GetSocketRequest, callback: grpc.requestCallback<_grpc_channelz_v1_GetSocketResponse__Output>): grpc.ClientUnaryCall;
|
||||
|
||||
/**
|
||||
* Returns a single Subchannel, or else a NOT_FOUND code.
|
||||
*/
|
||||
GetSubchannel(argument: _grpc_channelz_v1_GetSubchannelRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetSubchannelResponse__Output>): grpc.ClientUnaryCall;
|
||||
GetSubchannel(argument: _grpc_channelz_v1_GetSubchannelRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_grpc_channelz_v1_GetSubchannelResponse__Output>): grpc.ClientUnaryCall;
|
||||
GetSubchannel(argument: _grpc_channelz_v1_GetSubchannelRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetSubchannelResponse__Output>): grpc.ClientUnaryCall;
|
||||
GetSubchannel(argument: _grpc_channelz_v1_GetSubchannelRequest, callback: grpc.requestCallback<_grpc_channelz_v1_GetSubchannelResponse__Output>): grpc.ClientUnaryCall;
|
||||
/**
|
||||
* Returns a single Subchannel, or else a NOT_FOUND code.
|
||||
*/
|
||||
getSubchannel(argument: _grpc_channelz_v1_GetSubchannelRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetSubchannelResponse__Output>): grpc.ClientUnaryCall;
|
||||
getSubchannel(argument: _grpc_channelz_v1_GetSubchannelRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_grpc_channelz_v1_GetSubchannelResponse__Output>): grpc.ClientUnaryCall;
|
||||
getSubchannel(argument: _grpc_channelz_v1_GetSubchannelRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetSubchannelResponse__Output>): grpc.ClientUnaryCall;
|
||||
getSubchannel(argument: _grpc_channelz_v1_GetSubchannelRequest, callback: grpc.requestCallback<_grpc_channelz_v1_GetSubchannelResponse__Output>): grpc.ClientUnaryCall;
|
||||
|
||||
/**
|
||||
* Gets all root channels (i.e. channels the application has directly
|
||||
* created). This does not include subchannels nor non-top level channels.
|
||||
*/
|
||||
GetTopChannels(argument: _grpc_channelz_v1_GetTopChannelsRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetTopChannelsResponse__Output>): grpc.ClientUnaryCall;
|
||||
GetTopChannels(argument: _grpc_channelz_v1_GetTopChannelsRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_grpc_channelz_v1_GetTopChannelsResponse__Output>): grpc.ClientUnaryCall;
|
||||
GetTopChannels(argument: _grpc_channelz_v1_GetTopChannelsRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetTopChannelsResponse__Output>): grpc.ClientUnaryCall;
|
||||
GetTopChannels(argument: _grpc_channelz_v1_GetTopChannelsRequest, callback: grpc.requestCallback<_grpc_channelz_v1_GetTopChannelsResponse__Output>): grpc.ClientUnaryCall;
|
||||
/**
|
||||
* Gets all root channels (i.e. channels the application has directly
|
||||
* created). This does not include subchannels nor non-top level channels.
|
||||
*/
|
||||
getTopChannels(argument: _grpc_channelz_v1_GetTopChannelsRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetTopChannelsResponse__Output>): grpc.ClientUnaryCall;
|
||||
getTopChannels(argument: _grpc_channelz_v1_GetTopChannelsRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_grpc_channelz_v1_GetTopChannelsResponse__Output>): grpc.ClientUnaryCall;
|
||||
getTopChannels(argument: _grpc_channelz_v1_GetTopChannelsRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_grpc_channelz_v1_GetTopChannelsResponse__Output>): grpc.ClientUnaryCall;
|
||||
getTopChannels(argument: _grpc_channelz_v1_GetTopChannelsRequest, callback: grpc.requestCallback<_grpc_channelz_v1_GetTopChannelsResponse__Output>): grpc.ClientUnaryCall;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Channelz is a service exposed by gRPC servers that provides detailed debug
|
||||
* information.
|
||||
*/
|
||||
export interface ChannelzHandlers extends grpc.UntypedServiceImplementation {
|
||||
/**
|
||||
* Returns a single Channel, or else a NOT_FOUND code.
|
||||
*/
|
||||
GetChannel: grpc.handleUnaryCall<_grpc_channelz_v1_GetChannelRequest__Output, _grpc_channelz_v1_GetChannelResponse>;
|
||||
|
||||
/**
|
||||
* Returns a single Server, or else a NOT_FOUND code.
|
||||
*/
|
||||
GetServer: grpc.handleUnaryCall<_grpc_channelz_v1_GetServerRequest__Output, _grpc_channelz_v1_GetServerResponse>;
|
||||
|
||||
/**
|
||||
* Gets all server sockets that exist in the process.
|
||||
*/
|
||||
GetServerSockets: grpc.handleUnaryCall<_grpc_channelz_v1_GetServerSocketsRequest__Output, _grpc_channelz_v1_GetServerSocketsResponse>;
|
||||
|
||||
/**
|
||||
* Gets all servers that exist in the process.
|
||||
*/
|
||||
GetServers: grpc.handleUnaryCall<_grpc_channelz_v1_GetServersRequest__Output, _grpc_channelz_v1_GetServersResponse>;
|
||||
|
||||
/**
|
||||
* Returns a single Socket or else a NOT_FOUND code.
|
||||
*/
|
||||
GetSocket: grpc.handleUnaryCall<_grpc_channelz_v1_GetSocketRequest__Output, _grpc_channelz_v1_GetSocketResponse>;
|
||||
|
||||
/**
|
||||
* Returns a single Subchannel, or else a NOT_FOUND code.
|
||||
*/
|
||||
GetSubchannel: grpc.handleUnaryCall<_grpc_channelz_v1_GetSubchannelRequest__Output, _grpc_channelz_v1_GetSubchannelResponse>;
|
||||
|
||||
/**
|
||||
* Gets all root channels (i.e. channels the application has directly
|
||||
* created). This does not include subchannels nor non-top level channels.
|
||||
*/
|
||||
GetTopChannels: grpc.handleUnaryCall<_grpc_channelz_v1_GetTopChannelsRequest__Output, _grpc_channelz_v1_GetTopChannelsResponse>;
|
||||
|
||||
}
|
||||
|
||||
export interface ChannelzDefinition extends grpc.ServiceDefinition {
|
||||
GetChannel: MethodDefinition<_grpc_channelz_v1_GetChannelRequest, _grpc_channelz_v1_GetChannelResponse, _grpc_channelz_v1_GetChannelRequest__Output, _grpc_channelz_v1_GetChannelResponse__Output>
|
||||
GetServer: MethodDefinition<_grpc_channelz_v1_GetServerRequest, _grpc_channelz_v1_GetServerResponse, _grpc_channelz_v1_GetServerRequest__Output, _grpc_channelz_v1_GetServerResponse__Output>
|
||||
GetServerSockets: MethodDefinition<_grpc_channelz_v1_GetServerSocketsRequest, _grpc_channelz_v1_GetServerSocketsResponse, _grpc_channelz_v1_GetServerSocketsRequest__Output, _grpc_channelz_v1_GetServerSocketsResponse__Output>
|
||||
GetServers: MethodDefinition<_grpc_channelz_v1_GetServersRequest, _grpc_channelz_v1_GetServersResponse, _grpc_channelz_v1_GetServersRequest__Output, _grpc_channelz_v1_GetServersResponse__Output>
|
||||
GetSocket: MethodDefinition<_grpc_channelz_v1_GetSocketRequest, _grpc_channelz_v1_GetSocketResponse, _grpc_channelz_v1_GetSocketRequest__Output, _grpc_channelz_v1_GetSocketResponse__Output>
|
||||
GetSubchannel: MethodDefinition<_grpc_channelz_v1_GetSubchannelRequest, _grpc_channelz_v1_GetSubchannelResponse, _grpc_channelz_v1_GetSubchannelRequest__Output, _grpc_channelz_v1_GetSubchannelResponse__Output>
|
||||
GetTopChannels: MethodDefinition<_grpc_channelz_v1_GetTopChannelsRequest, _grpc_channelz_v1_GetTopChannelsResponse, _grpc_channelz_v1_GetTopChannelsRequest__Output, _grpc_channelz_v1_GetTopChannelsResponse__Output>
|
||||
}
|
||||
17
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetChannelRequest.ts
generated
vendored
Normal file
17
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetChannelRequest.ts
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Long } from '@grpc/proto-loader';
|
||||
|
||||
export interface GetChannelRequest {
|
||||
/**
|
||||
* channel_id is the identifier of the specific channel to get.
|
||||
*/
|
||||
'channel_id'?: (number | string | Long);
|
||||
}
|
||||
|
||||
export interface GetChannelRequest__Output {
|
||||
/**
|
||||
* channel_id is the identifier of the specific channel to get.
|
||||
*/
|
||||
'channel_id': (string);
|
||||
}
|
||||
19
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetChannelResponse.ts
generated
vendored
Normal file
19
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetChannelResponse.ts
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Channel as _grpc_channelz_v1_Channel, Channel__Output as _grpc_channelz_v1_Channel__Output } from '../../../grpc/channelz/v1/Channel';
|
||||
|
||||
export interface GetChannelResponse {
|
||||
/**
|
||||
* The Channel that corresponds to the requested channel_id. This field
|
||||
* should be set.
|
||||
*/
|
||||
'channel'?: (_grpc_channelz_v1_Channel | null);
|
||||
}
|
||||
|
||||
export interface GetChannelResponse__Output {
|
||||
/**
|
||||
* The Channel that corresponds to the requested channel_id. This field
|
||||
* should be set.
|
||||
*/
|
||||
'channel': (_grpc_channelz_v1_Channel__Output | null);
|
||||
}
|
||||
17
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetServerRequest.ts
generated
vendored
Normal file
17
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetServerRequest.ts
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Long } from '@grpc/proto-loader';
|
||||
|
||||
export interface GetServerRequest {
|
||||
/**
|
||||
* server_id is the identifier of the specific server to get.
|
||||
*/
|
||||
'server_id'?: (number | string | Long);
|
||||
}
|
||||
|
||||
export interface GetServerRequest__Output {
|
||||
/**
|
||||
* server_id is the identifier of the specific server to get.
|
||||
*/
|
||||
'server_id': (string);
|
||||
}
|
||||
19
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetServerResponse.ts
generated
vendored
Normal file
19
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetServerResponse.ts
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Server as _grpc_channelz_v1_Server, Server__Output as _grpc_channelz_v1_Server__Output } from '../../../grpc/channelz/v1/Server';
|
||||
|
||||
export interface GetServerResponse {
|
||||
/**
|
||||
* The Server that corresponds to the requested server_id. This field
|
||||
* should be set.
|
||||
*/
|
||||
'server'?: (_grpc_channelz_v1_Server | null);
|
||||
}
|
||||
|
||||
export interface GetServerResponse__Output {
|
||||
/**
|
||||
* The Server that corresponds to the requested server_id. This field
|
||||
* should be set.
|
||||
*/
|
||||
'server': (_grpc_channelz_v1_Server__Output | null);
|
||||
}
|
||||
39
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetServerSocketsRequest.ts
generated
vendored
Normal file
39
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetServerSocketsRequest.ts
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Long } from '@grpc/proto-loader';
|
||||
|
||||
export interface GetServerSocketsRequest {
|
||||
'server_id'?: (number | string | Long);
|
||||
/**
|
||||
* start_socket_id indicates that only sockets at or above this id should be
|
||||
* included in the results.
|
||||
* To request the first page, this must be set to 0. To request
|
||||
* subsequent pages, the client generates this value by adding 1 to
|
||||
* the highest seen result ID.
|
||||
*/
|
||||
'start_socket_id'?: (number | string | Long);
|
||||
/**
|
||||
* If non-zero, the server will return a page of results containing
|
||||
* at most this many items. If zero, the server will choose a
|
||||
* reasonable page size. Must never be negative.
|
||||
*/
|
||||
'max_results'?: (number | string | Long);
|
||||
}
|
||||
|
||||
export interface GetServerSocketsRequest__Output {
|
||||
'server_id': (string);
|
||||
/**
|
||||
* start_socket_id indicates that only sockets at or above this id should be
|
||||
* included in the results.
|
||||
* To request the first page, this must be set to 0. To request
|
||||
* subsequent pages, the client generates this value by adding 1 to
|
||||
* the highest seen result ID.
|
||||
*/
|
||||
'start_socket_id': (string);
|
||||
/**
|
||||
* If non-zero, the server will return a page of results containing
|
||||
* at most this many items. If zero, the server will choose a
|
||||
* reasonable page size. Must never be negative.
|
||||
*/
|
||||
'max_results': (string);
|
||||
}
|
||||
33
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetServerSocketsResponse.ts
generated
vendored
Normal file
33
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetServerSocketsResponse.ts
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { SocketRef as _grpc_channelz_v1_SocketRef, SocketRef__Output as _grpc_channelz_v1_SocketRef__Output } from '../../../grpc/channelz/v1/SocketRef';
|
||||
|
||||
export interface GetServerSocketsResponse {
|
||||
/**
|
||||
* list of socket refs that the connection detail service knows about. Sorted in
|
||||
* ascending socket_id order.
|
||||
* Must contain at least 1 result, otherwise 'end' must be true.
|
||||
*/
|
||||
'socket_ref'?: (_grpc_channelz_v1_SocketRef)[];
|
||||
/**
|
||||
* If set, indicates that the list of sockets is the final list. Requesting
|
||||
* more sockets will only return more if they are created after this RPC
|
||||
* completes.
|
||||
*/
|
||||
'end'?: (boolean);
|
||||
}
|
||||
|
||||
export interface GetServerSocketsResponse__Output {
|
||||
/**
|
||||
* list of socket refs that the connection detail service knows about. Sorted in
|
||||
* ascending socket_id order.
|
||||
* Must contain at least 1 result, otherwise 'end' must be true.
|
||||
*/
|
||||
'socket_ref': (_grpc_channelz_v1_SocketRef__Output)[];
|
||||
/**
|
||||
* If set, indicates that the list of sockets is the final list. Requesting
|
||||
* more sockets will only return more if they are created after this RPC
|
||||
* completes.
|
||||
*/
|
||||
'end': (boolean);
|
||||
}
|
||||
37
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetServersRequest.ts
generated
vendored
Normal file
37
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetServersRequest.ts
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Long } from '@grpc/proto-loader';
|
||||
|
||||
export interface GetServersRequest {
|
||||
/**
|
||||
* start_server_id indicates that only servers at or above this id should be
|
||||
* included in the results.
|
||||
* To request the first page, this must be set to 0. To request
|
||||
* subsequent pages, the client generates this value by adding 1 to
|
||||
* the highest seen result ID.
|
||||
*/
|
||||
'start_server_id'?: (number | string | Long);
|
||||
/**
|
||||
* If non-zero, the server will return a page of results containing
|
||||
* at most this many items. If zero, the server will choose a
|
||||
* reasonable page size. Must never be negative.
|
||||
*/
|
||||
'max_results'?: (number | string | Long);
|
||||
}
|
||||
|
||||
export interface GetServersRequest__Output {
|
||||
/**
|
||||
* start_server_id indicates that only servers at or above this id should be
|
||||
* included in the results.
|
||||
* To request the first page, this must be set to 0. To request
|
||||
* subsequent pages, the client generates this value by adding 1 to
|
||||
* the highest seen result ID.
|
||||
*/
|
||||
'start_server_id': (string);
|
||||
/**
|
||||
* If non-zero, the server will return a page of results containing
|
||||
* at most this many items. If zero, the server will choose a
|
||||
* reasonable page size. Must never be negative.
|
||||
*/
|
||||
'max_results': (string);
|
||||
}
|
||||
33
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetServersResponse.ts
generated
vendored
Normal file
33
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetServersResponse.ts
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Server as _grpc_channelz_v1_Server, Server__Output as _grpc_channelz_v1_Server__Output } from '../../../grpc/channelz/v1/Server';
|
||||
|
||||
export interface GetServersResponse {
|
||||
/**
|
||||
* list of servers that the connection detail service knows about. Sorted in
|
||||
* ascending server_id order.
|
||||
* Must contain at least 1 result, otherwise 'end' must be true.
|
||||
*/
|
||||
'server'?: (_grpc_channelz_v1_Server)[];
|
||||
/**
|
||||
* If set, indicates that the list of servers is the final list. Requesting
|
||||
* more servers will only return more if they are created after this RPC
|
||||
* completes.
|
||||
*/
|
||||
'end'?: (boolean);
|
||||
}
|
||||
|
||||
export interface GetServersResponse__Output {
|
||||
/**
|
||||
* list of servers that the connection detail service knows about. Sorted in
|
||||
* ascending server_id order.
|
||||
* Must contain at least 1 result, otherwise 'end' must be true.
|
||||
*/
|
||||
'server': (_grpc_channelz_v1_Server__Output)[];
|
||||
/**
|
||||
* If set, indicates that the list of servers is the final list. Requesting
|
||||
* more servers will only return more if they are created after this RPC
|
||||
* completes.
|
||||
*/
|
||||
'end': (boolean);
|
||||
}
|
||||
29
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetSocketRequest.ts
generated
vendored
Normal file
29
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetSocketRequest.ts
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Long } from '@grpc/proto-loader';
|
||||
|
||||
export interface GetSocketRequest {
|
||||
/**
|
||||
* socket_id is the identifier of the specific socket to get.
|
||||
*/
|
||||
'socket_id'?: (number | string | Long);
|
||||
/**
|
||||
* If true, the response will contain only high level information
|
||||
* that is inexpensive to obtain. Fields thay may be omitted are
|
||||
* documented.
|
||||
*/
|
||||
'summary'?: (boolean);
|
||||
}
|
||||
|
||||
export interface GetSocketRequest__Output {
|
||||
/**
|
||||
* socket_id is the identifier of the specific socket to get.
|
||||
*/
|
||||
'socket_id': (string);
|
||||
/**
|
||||
* If true, the response will contain only high level information
|
||||
* that is inexpensive to obtain. Fields thay may be omitted are
|
||||
* documented.
|
||||
*/
|
||||
'summary': (boolean);
|
||||
}
|
||||
19
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetSocketResponse.ts
generated
vendored
Normal file
19
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetSocketResponse.ts
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Socket as _grpc_channelz_v1_Socket, Socket__Output as _grpc_channelz_v1_Socket__Output } from '../../../grpc/channelz/v1/Socket';
|
||||
|
||||
export interface GetSocketResponse {
|
||||
/**
|
||||
* The Socket that corresponds to the requested socket_id. This field
|
||||
* should be set.
|
||||
*/
|
||||
'socket'?: (_grpc_channelz_v1_Socket | null);
|
||||
}
|
||||
|
||||
export interface GetSocketResponse__Output {
|
||||
/**
|
||||
* The Socket that corresponds to the requested socket_id. This field
|
||||
* should be set.
|
||||
*/
|
||||
'socket': (_grpc_channelz_v1_Socket__Output | null);
|
||||
}
|
||||
17
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetSubchannelRequest.ts
generated
vendored
Normal file
17
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetSubchannelRequest.ts
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Long } from '@grpc/proto-loader';
|
||||
|
||||
export interface GetSubchannelRequest {
|
||||
/**
|
||||
* subchannel_id is the identifier of the specific subchannel to get.
|
||||
*/
|
||||
'subchannel_id'?: (number | string | Long);
|
||||
}
|
||||
|
||||
export interface GetSubchannelRequest__Output {
|
||||
/**
|
||||
* subchannel_id is the identifier of the specific subchannel to get.
|
||||
*/
|
||||
'subchannel_id': (string);
|
||||
}
|
||||
19
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetSubchannelResponse.ts
generated
vendored
Normal file
19
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetSubchannelResponse.ts
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Subchannel as _grpc_channelz_v1_Subchannel, Subchannel__Output as _grpc_channelz_v1_Subchannel__Output } from '../../../grpc/channelz/v1/Subchannel';
|
||||
|
||||
export interface GetSubchannelResponse {
|
||||
/**
|
||||
* The Subchannel that corresponds to the requested subchannel_id. This
|
||||
* field should be set.
|
||||
*/
|
||||
'subchannel'?: (_grpc_channelz_v1_Subchannel | null);
|
||||
}
|
||||
|
||||
export interface GetSubchannelResponse__Output {
|
||||
/**
|
||||
* The Subchannel that corresponds to the requested subchannel_id. This
|
||||
* field should be set.
|
||||
*/
|
||||
'subchannel': (_grpc_channelz_v1_Subchannel__Output | null);
|
||||
}
|
||||
37
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetTopChannelsRequest.ts
generated
vendored
Normal file
37
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetTopChannelsRequest.ts
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Long } from '@grpc/proto-loader';
|
||||
|
||||
export interface GetTopChannelsRequest {
|
||||
/**
|
||||
* start_channel_id indicates that only channels at or above this id should be
|
||||
* included in the results.
|
||||
* To request the first page, this should be set to 0. To request
|
||||
* subsequent pages, the client generates this value by adding 1 to
|
||||
* the highest seen result ID.
|
||||
*/
|
||||
'start_channel_id'?: (number | string | Long);
|
||||
/**
|
||||
* If non-zero, the server will return a page of results containing
|
||||
* at most this many items. If zero, the server will choose a
|
||||
* reasonable page size. Must never be negative.
|
||||
*/
|
||||
'max_results'?: (number | string | Long);
|
||||
}
|
||||
|
||||
export interface GetTopChannelsRequest__Output {
|
||||
/**
|
||||
* start_channel_id indicates that only channels at or above this id should be
|
||||
* included in the results.
|
||||
* To request the first page, this should be set to 0. To request
|
||||
* subsequent pages, the client generates this value by adding 1 to
|
||||
* the highest seen result ID.
|
||||
*/
|
||||
'start_channel_id': (string);
|
||||
/**
|
||||
* If non-zero, the server will return a page of results containing
|
||||
* at most this many items. If zero, the server will choose a
|
||||
* reasonable page size. Must never be negative.
|
||||
*/
|
||||
'max_results': (string);
|
||||
}
|
||||
33
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetTopChannelsResponse.ts
generated
vendored
Normal file
33
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/GetTopChannelsResponse.ts
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Channel as _grpc_channelz_v1_Channel, Channel__Output as _grpc_channelz_v1_Channel__Output } from '../../../grpc/channelz/v1/Channel';
|
||||
|
||||
export interface GetTopChannelsResponse {
|
||||
/**
|
||||
* list of channels that the connection detail service knows about. Sorted in
|
||||
* ascending channel_id order.
|
||||
* Must contain at least 1 result, otherwise 'end' must be true.
|
||||
*/
|
||||
'channel'?: (_grpc_channelz_v1_Channel)[];
|
||||
/**
|
||||
* If set, indicates that the list of channels is the final list. Requesting
|
||||
* more channels can only return more if they are created after this RPC
|
||||
* completes.
|
||||
*/
|
||||
'end'?: (boolean);
|
||||
}
|
||||
|
||||
export interface GetTopChannelsResponse__Output {
|
||||
/**
|
||||
* list of channels that the connection detail service knows about. Sorted in
|
||||
* ascending channel_id order.
|
||||
* Must contain at least 1 result, otherwise 'end' must be true.
|
||||
*/
|
||||
'channel': (_grpc_channelz_v1_Channel__Output)[];
|
||||
/**
|
||||
* If set, indicates that the list of channels is the final list. Requesting
|
||||
* more channels can only return more if they are created after this RPC
|
||||
* completes.
|
||||
*/
|
||||
'end': (boolean);
|
||||
}
|
||||
87
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/Security.ts
generated
vendored
Normal file
87
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/Security.ts
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Any as _google_protobuf_Any, Any__Output as _google_protobuf_Any__Output } from '../../../google/protobuf/Any';
|
||||
|
||||
export interface _grpc_channelz_v1_Security_OtherSecurity {
|
||||
/**
|
||||
* The human readable version of the value.
|
||||
*/
|
||||
'name'?: (string);
|
||||
/**
|
||||
* The actual security details message.
|
||||
*/
|
||||
'value'?: (_google_protobuf_Any | null);
|
||||
}
|
||||
|
||||
export interface _grpc_channelz_v1_Security_OtherSecurity__Output {
|
||||
/**
|
||||
* The human readable version of the value.
|
||||
*/
|
||||
'name': (string);
|
||||
/**
|
||||
* The actual security details message.
|
||||
*/
|
||||
'value': (_google_protobuf_Any__Output | null);
|
||||
}
|
||||
|
||||
export interface _grpc_channelz_v1_Security_Tls {
|
||||
/**
|
||||
* The cipher suite name in the RFC 4346 format:
|
||||
* https://tools.ietf.org/html/rfc4346#appendix-C
|
||||
*/
|
||||
'standard_name'?: (string);
|
||||
/**
|
||||
* Some other way to describe the cipher suite if
|
||||
* the RFC 4346 name is not available.
|
||||
*/
|
||||
'other_name'?: (string);
|
||||
/**
|
||||
* the certificate used by this endpoint.
|
||||
*/
|
||||
'local_certificate'?: (Buffer | Uint8Array | string);
|
||||
/**
|
||||
* the certificate used by the remote endpoint.
|
||||
*/
|
||||
'remote_certificate'?: (Buffer | Uint8Array | string);
|
||||
'cipher_suite'?: "standard_name"|"other_name";
|
||||
}
|
||||
|
||||
export interface _grpc_channelz_v1_Security_Tls__Output {
|
||||
/**
|
||||
* The cipher suite name in the RFC 4346 format:
|
||||
* https://tools.ietf.org/html/rfc4346#appendix-C
|
||||
*/
|
||||
'standard_name'?: (string);
|
||||
/**
|
||||
* Some other way to describe the cipher suite if
|
||||
* the RFC 4346 name is not available.
|
||||
*/
|
||||
'other_name'?: (string);
|
||||
/**
|
||||
* the certificate used by this endpoint.
|
||||
*/
|
||||
'local_certificate': (Buffer);
|
||||
/**
|
||||
* the certificate used by the remote endpoint.
|
||||
*/
|
||||
'remote_certificate': (Buffer);
|
||||
'cipher_suite': "standard_name"|"other_name";
|
||||
}
|
||||
|
||||
/**
|
||||
* Security represents details about how secure the socket is.
|
||||
*/
|
||||
export interface Security {
|
||||
'tls'?: (_grpc_channelz_v1_Security_Tls | null);
|
||||
'other'?: (_grpc_channelz_v1_Security_OtherSecurity | null);
|
||||
'model'?: "tls"|"other";
|
||||
}
|
||||
|
||||
/**
|
||||
* Security represents details about how secure the socket is.
|
||||
*/
|
||||
export interface Security__Output {
|
||||
'tls'?: (_grpc_channelz_v1_Security_Tls__Output | null);
|
||||
'other'?: (_grpc_channelz_v1_Security_OtherSecurity__Output | null);
|
||||
'model': "tls"|"other";
|
||||
}
|
||||
45
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/Server.ts
generated
vendored
Normal file
45
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/Server.ts
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { ServerRef as _grpc_channelz_v1_ServerRef, ServerRef__Output as _grpc_channelz_v1_ServerRef__Output } from '../../../grpc/channelz/v1/ServerRef';
|
||||
import type { ServerData as _grpc_channelz_v1_ServerData, ServerData__Output as _grpc_channelz_v1_ServerData__Output } from '../../../grpc/channelz/v1/ServerData';
|
||||
import type { SocketRef as _grpc_channelz_v1_SocketRef, SocketRef__Output as _grpc_channelz_v1_SocketRef__Output } from '../../../grpc/channelz/v1/SocketRef';
|
||||
|
||||
/**
|
||||
* Server represents a single server. There may be multiple servers in a single
|
||||
* program.
|
||||
*/
|
||||
export interface Server {
|
||||
/**
|
||||
* The identifier for a Server. This should be set.
|
||||
*/
|
||||
'ref'?: (_grpc_channelz_v1_ServerRef | null);
|
||||
/**
|
||||
* The associated data of the Server.
|
||||
*/
|
||||
'data'?: (_grpc_channelz_v1_ServerData | null);
|
||||
/**
|
||||
* The sockets that the server is listening on. There are no ordering
|
||||
* guarantees. This may be absent.
|
||||
*/
|
||||
'listen_socket'?: (_grpc_channelz_v1_SocketRef)[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Server represents a single server. There may be multiple servers in a single
|
||||
* program.
|
||||
*/
|
||||
export interface Server__Output {
|
||||
/**
|
||||
* The identifier for a Server. This should be set.
|
||||
*/
|
||||
'ref': (_grpc_channelz_v1_ServerRef__Output | null);
|
||||
/**
|
||||
* The associated data of the Server.
|
||||
*/
|
||||
'data': (_grpc_channelz_v1_ServerData__Output | null);
|
||||
/**
|
||||
* The sockets that the server is listening on. There are no ordering
|
||||
* guarantees. This may be absent.
|
||||
*/
|
||||
'listen_socket': (_grpc_channelz_v1_SocketRef__Output)[];
|
||||
}
|
||||
57
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/ServerData.ts
generated
vendored
Normal file
57
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/ServerData.ts
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { ChannelTrace as _grpc_channelz_v1_ChannelTrace, ChannelTrace__Output as _grpc_channelz_v1_ChannelTrace__Output } from '../../../grpc/channelz/v1/ChannelTrace';
|
||||
import type { Timestamp as _google_protobuf_Timestamp, Timestamp__Output as _google_protobuf_Timestamp__Output } from '../../../google/protobuf/Timestamp';
|
||||
import type { Long } from '@grpc/proto-loader';
|
||||
|
||||
/**
|
||||
* ServerData is data for a specific Server.
|
||||
*/
|
||||
export interface ServerData {
|
||||
/**
|
||||
* A trace of recent events on the server. May be absent.
|
||||
*/
|
||||
'trace'?: (_grpc_channelz_v1_ChannelTrace | null);
|
||||
/**
|
||||
* The number of incoming calls started on the server
|
||||
*/
|
||||
'calls_started'?: (number | string | Long);
|
||||
/**
|
||||
* The number of incoming calls that have completed with an OK status
|
||||
*/
|
||||
'calls_succeeded'?: (number | string | Long);
|
||||
/**
|
||||
* The number of incoming calls that have a completed with a non-OK status
|
||||
*/
|
||||
'calls_failed'?: (number | string | Long);
|
||||
/**
|
||||
* The last time a call was started on the server.
|
||||
*/
|
||||
'last_call_started_timestamp'?: (_google_protobuf_Timestamp | null);
|
||||
}
|
||||
|
||||
/**
|
||||
* ServerData is data for a specific Server.
|
||||
*/
|
||||
export interface ServerData__Output {
|
||||
/**
|
||||
* A trace of recent events on the server. May be absent.
|
||||
*/
|
||||
'trace': (_grpc_channelz_v1_ChannelTrace__Output | null);
|
||||
/**
|
||||
* The number of incoming calls started on the server
|
||||
*/
|
||||
'calls_started': (string);
|
||||
/**
|
||||
* The number of incoming calls that have completed with an OK status
|
||||
*/
|
||||
'calls_succeeded': (string);
|
||||
/**
|
||||
* The number of incoming calls that have a completed with a non-OK status
|
||||
*/
|
||||
'calls_failed': (string);
|
||||
/**
|
||||
* The last time a call was started on the server.
|
||||
*/
|
||||
'last_call_started_timestamp': (_google_protobuf_Timestamp__Output | null);
|
||||
}
|
||||
31
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/ServerRef.ts
generated
vendored
Normal file
31
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/ServerRef.ts
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Long } from '@grpc/proto-loader';
|
||||
|
||||
/**
|
||||
* ServerRef is a reference to a Server.
|
||||
*/
|
||||
export interface ServerRef {
|
||||
/**
|
||||
* A globally unique identifier for this server. Must be a positive number.
|
||||
*/
|
||||
'server_id'?: (number | string | Long);
|
||||
/**
|
||||
* An optional name associated with the server.
|
||||
*/
|
||||
'name'?: (string);
|
||||
}
|
||||
|
||||
/**
|
||||
* ServerRef is a reference to a Server.
|
||||
*/
|
||||
export interface ServerRef__Output {
|
||||
/**
|
||||
* A globally unique identifier for this server. Must be a positive number.
|
||||
*/
|
||||
'server_id': (string);
|
||||
/**
|
||||
* An optional name associated with the server.
|
||||
*/
|
||||
'name': (string);
|
||||
}
|
||||
70
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/Socket.ts
generated
vendored
Normal file
70
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/Socket.ts
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { SocketRef as _grpc_channelz_v1_SocketRef, SocketRef__Output as _grpc_channelz_v1_SocketRef__Output } from '../../../grpc/channelz/v1/SocketRef';
|
||||
import type { SocketData as _grpc_channelz_v1_SocketData, SocketData__Output as _grpc_channelz_v1_SocketData__Output } from '../../../grpc/channelz/v1/SocketData';
|
||||
import type { Address as _grpc_channelz_v1_Address, Address__Output as _grpc_channelz_v1_Address__Output } from '../../../grpc/channelz/v1/Address';
|
||||
import type { Security as _grpc_channelz_v1_Security, Security__Output as _grpc_channelz_v1_Security__Output } from '../../../grpc/channelz/v1/Security';
|
||||
|
||||
/**
|
||||
* Information about an actual connection. Pronounced "sock-ay".
|
||||
*/
|
||||
export interface Socket {
|
||||
/**
|
||||
* The identifier for the Socket.
|
||||
*/
|
||||
'ref'?: (_grpc_channelz_v1_SocketRef | null);
|
||||
/**
|
||||
* Data specific to this Socket.
|
||||
*/
|
||||
'data'?: (_grpc_channelz_v1_SocketData | null);
|
||||
/**
|
||||
* The locally bound address.
|
||||
*/
|
||||
'local'?: (_grpc_channelz_v1_Address | null);
|
||||
/**
|
||||
* The remote bound address. May be absent.
|
||||
*/
|
||||
'remote'?: (_grpc_channelz_v1_Address | null);
|
||||
/**
|
||||
* Security details for this socket. May be absent if not available, or
|
||||
* there is no security on the socket.
|
||||
*/
|
||||
'security'?: (_grpc_channelz_v1_Security | null);
|
||||
/**
|
||||
* Optional, represents the name of the remote endpoint, if different than
|
||||
* the original target name.
|
||||
*/
|
||||
'remote_name'?: (string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Information about an actual connection. Pronounced "sock-ay".
|
||||
*/
|
||||
export interface Socket__Output {
|
||||
/**
|
||||
* The identifier for the Socket.
|
||||
*/
|
||||
'ref': (_grpc_channelz_v1_SocketRef__Output | null);
|
||||
/**
|
||||
* Data specific to this Socket.
|
||||
*/
|
||||
'data': (_grpc_channelz_v1_SocketData__Output | null);
|
||||
/**
|
||||
* The locally bound address.
|
||||
*/
|
||||
'local': (_grpc_channelz_v1_Address__Output | null);
|
||||
/**
|
||||
* The remote bound address. May be absent.
|
||||
*/
|
||||
'remote': (_grpc_channelz_v1_Address__Output | null);
|
||||
/**
|
||||
* Security details for this socket. May be absent if not available, or
|
||||
* there is no security on the socket.
|
||||
*/
|
||||
'security': (_grpc_channelz_v1_Security__Output | null);
|
||||
/**
|
||||
* Optional, represents the name of the remote endpoint, if different than
|
||||
* the original target name.
|
||||
*/
|
||||
'remote_name': (string);
|
||||
}
|
||||
150
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/SocketData.ts
generated
vendored
Normal file
150
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/SocketData.ts
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Timestamp as _google_protobuf_Timestamp, Timestamp__Output as _google_protobuf_Timestamp__Output } from '../../../google/protobuf/Timestamp';
|
||||
import type { Int64Value as _google_protobuf_Int64Value, Int64Value__Output as _google_protobuf_Int64Value__Output } from '../../../google/protobuf/Int64Value';
|
||||
import type { SocketOption as _grpc_channelz_v1_SocketOption, SocketOption__Output as _grpc_channelz_v1_SocketOption__Output } from '../../../grpc/channelz/v1/SocketOption';
|
||||
import type { Long } from '@grpc/proto-loader';
|
||||
|
||||
/**
|
||||
* SocketData is data associated for a specific Socket. The fields present
|
||||
* are specific to the implementation, so there may be minor differences in
|
||||
* the semantics. (e.g. flow control windows)
|
||||
*/
|
||||
export interface SocketData {
|
||||
/**
|
||||
* The number of streams that have been started.
|
||||
*/
|
||||
'streams_started'?: (number | string | Long);
|
||||
/**
|
||||
* The number of streams that have ended successfully:
|
||||
* On client side, received frame with eos bit set;
|
||||
* On server side, sent frame with eos bit set.
|
||||
*/
|
||||
'streams_succeeded'?: (number | string | Long);
|
||||
/**
|
||||
* The number of streams that have ended unsuccessfully:
|
||||
* On client side, ended without receiving frame with eos bit set;
|
||||
* On server side, ended without sending frame with eos bit set.
|
||||
*/
|
||||
'streams_failed'?: (number | string | Long);
|
||||
/**
|
||||
* The number of grpc messages successfully sent on this socket.
|
||||
*/
|
||||
'messages_sent'?: (number | string | Long);
|
||||
/**
|
||||
* The number of grpc messages received on this socket.
|
||||
*/
|
||||
'messages_received'?: (number | string | Long);
|
||||
/**
|
||||
* The number of keep alives sent. This is typically implemented with HTTP/2
|
||||
* ping messages.
|
||||
*/
|
||||
'keep_alives_sent'?: (number | string | Long);
|
||||
/**
|
||||
* The last time a stream was created by this endpoint. Usually unset for
|
||||
* servers.
|
||||
*/
|
||||
'last_local_stream_created_timestamp'?: (_google_protobuf_Timestamp | null);
|
||||
/**
|
||||
* The last time a stream was created by the remote endpoint. Usually unset
|
||||
* for clients.
|
||||
*/
|
||||
'last_remote_stream_created_timestamp'?: (_google_protobuf_Timestamp | null);
|
||||
/**
|
||||
* The last time a message was sent by this endpoint.
|
||||
*/
|
||||
'last_message_sent_timestamp'?: (_google_protobuf_Timestamp | null);
|
||||
/**
|
||||
* The last time a message was received by this endpoint.
|
||||
*/
|
||||
'last_message_received_timestamp'?: (_google_protobuf_Timestamp | null);
|
||||
/**
|
||||
* The amount of window, granted to the local endpoint by the remote endpoint.
|
||||
* This may be slightly out of date due to network latency. This does NOT
|
||||
* include stream level or TCP level flow control info.
|
||||
*/
|
||||
'local_flow_control_window'?: (_google_protobuf_Int64Value | null);
|
||||
/**
|
||||
* The amount of window, granted to the remote endpoint by the local endpoint.
|
||||
* This may be slightly out of date due to network latency. This does NOT
|
||||
* include stream level or TCP level flow control info.
|
||||
*/
|
||||
'remote_flow_control_window'?: (_google_protobuf_Int64Value | null);
|
||||
/**
|
||||
* Socket options set on this socket. May be absent if 'summary' is set
|
||||
* on GetSocketRequest.
|
||||
*/
|
||||
'option'?: (_grpc_channelz_v1_SocketOption)[];
|
||||
}
|
||||
|
||||
/**
|
||||
* SocketData is data associated for a specific Socket. The fields present
|
||||
* are specific to the implementation, so there may be minor differences in
|
||||
* the semantics. (e.g. flow control windows)
|
||||
*/
|
||||
export interface SocketData__Output {
|
||||
/**
|
||||
* The number of streams that have been started.
|
||||
*/
|
||||
'streams_started': (string);
|
||||
/**
|
||||
* The number of streams that have ended successfully:
|
||||
* On client side, received frame with eos bit set;
|
||||
* On server side, sent frame with eos bit set.
|
||||
*/
|
||||
'streams_succeeded': (string);
|
||||
/**
|
||||
* The number of streams that have ended unsuccessfully:
|
||||
* On client side, ended without receiving frame with eos bit set;
|
||||
* On server side, ended without sending frame with eos bit set.
|
||||
*/
|
||||
'streams_failed': (string);
|
||||
/**
|
||||
* The number of grpc messages successfully sent on this socket.
|
||||
*/
|
||||
'messages_sent': (string);
|
||||
/**
|
||||
* The number of grpc messages received on this socket.
|
||||
*/
|
||||
'messages_received': (string);
|
||||
/**
|
||||
* The number of keep alives sent. This is typically implemented with HTTP/2
|
||||
* ping messages.
|
||||
*/
|
||||
'keep_alives_sent': (string);
|
||||
/**
|
||||
* The last time a stream was created by this endpoint. Usually unset for
|
||||
* servers.
|
||||
*/
|
||||
'last_local_stream_created_timestamp': (_google_protobuf_Timestamp__Output | null);
|
||||
/**
|
||||
* The last time a stream was created by the remote endpoint. Usually unset
|
||||
* for clients.
|
||||
*/
|
||||
'last_remote_stream_created_timestamp': (_google_protobuf_Timestamp__Output | null);
|
||||
/**
|
||||
* The last time a message was sent by this endpoint.
|
||||
*/
|
||||
'last_message_sent_timestamp': (_google_protobuf_Timestamp__Output | null);
|
||||
/**
|
||||
* The last time a message was received by this endpoint.
|
||||
*/
|
||||
'last_message_received_timestamp': (_google_protobuf_Timestamp__Output | null);
|
||||
/**
|
||||
* The amount of window, granted to the local endpoint by the remote endpoint.
|
||||
* This may be slightly out of date due to network latency. This does NOT
|
||||
* include stream level or TCP level flow control info.
|
||||
*/
|
||||
'local_flow_control_window': (_google_protobuf_Int64Value__Output | null);
|
||||
/**
|
||||
* The amount of window, granted to the remote endpoint by the local endpoint.
|
||||
* This may be slightly out of date due to network latency. This does NOT
|
||||
* include stream level or TCP level flow control info.
|
||||
*/
|
||||
'remote_flow_control_window': (_google_protobuf_Int64Value__Output | null);
|
||||
/**
|
||||
* Socket options set on this socket. May be absent if 'summary' is set
|
||||
* on GetSocketRequest.
|
||||
*/
|
||||
'option': (_grpc_channelz_v1_SocketOption__Output)[];
|
||||
}
|
||||
47
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/SocketOption.ts
generated
vendored
Normal file
47
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/SocketOption.ts
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Any as _google_protobuf_Any, Any__Output as _google_protobuf_Any__Output } from '../../../google/protobuf/Any';
|
||||
|
||||
/**
|
||||
* SocketOption represents socket options for a socket. Specifically, these
|
||||
* are the options returned by getsockopt().
|
||||
*/
|
||||
export interface SocketOption {
|
||||
/**
|
||||
* The full name of the socket option. Typically this will be the upper case
|
||||
* name, such as "SO_REUSEPORT".
|
||||
*/
|
||||
'name'?: (string);
|
||||
/**
|
||||
* The human readable value of this socket option. At least one of value or
|
||||
* additional will be set.
|
||||
*/
|
||||
'value'?: (string);
|
||||
/**
|
||||
* Additional data associated with the socket option. At least one of value
|
||||
* or additional will be set.
|
||||
*/
|
||||
'additional'?: (_google_protobuf_Any | null);
|
||||
}
|
||||
|
||||
/**
|
||||
* SocketOption represents socket options for a socket. Specifically, these
|
||||
* are the options returned by getsockopt().
|
||||
*/
|
||||
export interface SocketOption__Output {
|
||||
/**
|
||||
* The full name of the socket option. Typically this will be the upper case
|
||||
* name, such as "SO_REUSEPORT".
|
||||
*/
|
||||
'name': (string);
|
||||
/**
|
||||
* The human readable value of this socket option. At least one of value or
|
||||
* additional will be set.
|
||||
*/
|
||||
'value': (string);
|
||||
/**
|
||||
* Additional data associated with the socket option. At least one of value
|
||||
* or additional will be set.
|
||||
*/
|
||||
'additional': (_google_protobuf_Any__Output | null);
|
||||
}
|
||||
33
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/SocketOptionLinger.ts
generated
vendored
Normal file
33
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/SocketOptionLinger.ts
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Duration as _google_protobuf_Duration, Duration__Output as _google_protobuf_Duration__Output } from '../../../google/protobuf/Duration';
|
||||
|
||||
/**
|
||||
* For use with SocketOption's additional field. This is primarily used for
|
||||
* SO_LINGER.
|
||||
*/
|
||||
export interface SocketOptionLinger {
|
||||
/**
|
||||
* active maps to `struct linger.l_onoff`
|
||||
*/
|
||||
'active'?: (boolean);
|
||||
/**
|
||||
* duration maps to `struct linger.l_linger`
|
||||
*/
|
||||
'duration'?: (_google_protobuf_Duration | null);
|
||||
}
|
||||
|
||||
/**
|
||||
* For use with SocketOption's additional field. This is primarily used for
|
||||
* SO_LINGER.
|
||||
*/
|
||||
export interface SocketOptionLinger__Output {
|
||||
/**
|
||||
* active maps to `struct linger.l_onoff`
|
||||
*/
|
||||
'active': (boolean);
|
||||
/**
|
||||
* duration maps to `struct linger.l_linger`
|
||||
*/
|
||||
'duration': (_google_protobuf_Duration__Output | null);
|
||||
}
|
||||
74
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/SocketOptionTcpInfo.ts
generated
vendored
Normal file
74
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/SocketOptionTcpInfo.ts
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
|
||||
/**
|
||||
* For use with SocketOption's additional field. Tcp info for
|
||||
* SOL_TCP and TCP_INFO.
|
||||
*/
|
||||
export interface SocketOptionTcpInfo {
|
||||
'tcpi_state'?: (number);
|
||||
'tcpi_ca_state'?: (number);
|
||||
'tcpi_retransmits'?: (number);
|
||||
'tcpi_probes'?: (number);
|
||||
'tcpi_backoff'?: (number);
|
||||
'tcpi_options'?: (number);
|
||||
'tcpi_snd_wscale'?: (number);
|
||||
'tcpi_rcv_wscale'?: (number);
|
||||
'tcpi_rto'?: (number);
|
||||
'tcpi_ato'?: (number);
|
||||
'tcpi_snd_mss'?: (number);
|
||||
'tcpi_rcv_mss'?: (number);
|
||||
'tcpi_unacked'?: (number);
|
||||
'tcpi_sacked'?: (number);
|
||||
'tcpi_lost'?: (number);
|
||||
'tcpi_retrans'?: (number);
|
||||
'tcpi_fackets'?: (number);
|
||||
'tcpi_last_data_sent'?: (number);
|
||||
'tcpi_last_ack_sent'?: (number);
|
||||
'tcpi_last_data_recv'?: (number);
|
||||
'tcpi_last_ack_recv'?: (number);
|
||||
'tcpi_pmtu'?: (number);
|
||||
'tcpi_rcv_ssthresh'?: (number);
|
||||
'tcpi_rtt'?: (number);
|
||||
'tcpi_rttvar'?: (number);
|
||||
'tcpi_snd_ssthresh'?: (number);
|
||||
'tcpi_snd_cwnd'?: (number);
|
||||
'tcpi_advmss'?: (number);
|
||||
'tcpi_reordering'?: (number);
|
||||
}
|
||||
|
||||
/**
|
||||
* For use with SocketOption's additional field. Tcp info for
|
||||
* SOL_TCP and TCP_INFO.
|
||||
*/
|
||||
export interface SocketOptionTcpInfo__Output {
|
||||
'tcpi_state': (number);
|
||||
'tcpi_ca_state': (number);
|
||||
'tcpi_retransmits': (number);
|
||||
'tcpi_probes': (number);
|
||||
'tcpi_backoff': (number);
|
||||
'tcpi_options': (number);
|
||||
'tcpi_snd_wscale': (number);
|
||||
'tcpi_rcv_wscale': (number);
|
||||
'tcpi_rto': (number);
|
||||
'tcpi_ato': (number);
|
||||
'tcpi_snd_mss': (number);
|
||||
'tcpi_rcv_mss': (number);
|
||||
'tcpi_unacked': (number);
|
||||
'tcpi_sacked': (number);
|
||||
'tcpi_lost': (number);
|
||||
'tcpi_retrans': (number);
|
||||
'tcpi_fackets': (number);
|
||||
'tcpi_last_data_sent': (number);
|
||||
'tcpi_last_ack_sent': (number);
|
||||
'tcpi_last_data_recv': (number);
|
||||
'tcpi_last_ack_recv': (number);
|
||||
'tcpi_pmtu': (number);
|
||||
'tcpi_rcv_ssthresh': (number);
|
||||
'tcpi_rtt': (number);
|
||||
'tcpi_rttvar': (number);
|
||||
'tcpi_snd_ssthresh': (number);
|
||||
'tcpi_snd_cwnd': (number);
|
||||
'tcpi_advmss': (number);
|
||||
'tcpi_reordering': (number);
|
||||
}
|
||||
19
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/SocketOptionTimeout.ts
generated
vendored
Normal file
19
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/SocketOptionTimeout.ts
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Duration as _google_protobuf_Duration, Duration__Output as _google_protobuf_Duration__Output } from '../../../google/protobuf/Duration';
|
||||
|
||||
/**
|
||||
* For use with SocketOption's additional field. This is primarily used for
|
||||
* SO_RCVTIMEO and SO_SNDTIMEO
|
||||
*/
|
||||
export interface SocketOptionTimeout {
|
||||
'duration'?: (_google_protobuf_Duration | null);
|
||||
}
|
||||
|
||||
/**
|
||||
* For use with SocketOption's additional field. This is primarily used for
|
||||
* SO_RCVTIMEO and SO_SNDTIMEO
|
||||
*/
|
||||
export interface SocketOptionTimeout__Output {
|
||||
'duration': (_google_protobuf_Duration__Output | null);
|
||||
}
|
||||
31
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/SocketRef.ts
generated
vendored
Normal file
31
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/SocketRef.ts
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Long } from '@grpc/proto-loader';
|
||||
|
||||
/**
|
||||
* SocketRef is a reference to a Socket.
|
||||
*/
|
||||
export interface SocketRef {
|
||||
/**
|
||||
* The globally unique id for this socket. Must be a positive number.
|
||||
*/
|
||||
'socket_id'?: (number | string | Long);
|
||||
/**
|
||||
* An optional name associated with the socket.
|
||||
*/
|
||||
'name'?: (string);
|
||||
}
|
||||
|
||||
/**
|
||||
* SocketRef is a reference to a Socket.
|
||||
*/
|
||||
export interface SocketRef__Output {
|
||||
/**
|
||||
* The globally unique id for this socket. Must be a positive number.
|
||||
*/
|
||||
'socket_id': (string);
|
||||
/**
|
||||
* An optional name associated with the socket.
|
||||
*/
|
||||
'name': (string);
|
||||
}
|
||||
70
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/Subchannel.ts
generated
vendored
Normal file
70
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/Subchannel.ts
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { SubchannelRef as _grpc_channelz_v1_SubchannelRef, SubchannelRef__Output as _grpc_channelz_v1_SubchannelRef__Output } from '../../../grpc/channelz/v1/SubchannelRef';
|
||||
import type { ChannelData as _grpc_channelz_v1_ChannelData, ChannelData__Output as _grpc_channelz_v1_ChannelData__Output } from '../../../grpc/channelz/v1/ChannelData';
|
||||
import type { ChannelRef as _grpc_channelz_v1_ChannelRef, ChannelRef__Output as _grpc_channelz_v1_ChannelRef__Output } from '../../../grpc/channelz/v1/ChannelRef';
|
||||
import type { SocketRef as _grpc_channelz_v1_SocketRef, SocketRef__Output as _grpc_channelz_v1_SocketRef__Output } from '../../../grpc/channelz/v1/SocketRef';
|
||||
|
||||
/**
|
||||
* Subchannel is a logical grouping of channels, subchannels, and sockets.
|
||||
* A subchannel is load balanced over by it's ancestor
|
||||
*/
|
||||
export interface Subchannel {
|
||||
/**
|
||||
* The identifier for this channel.
|
||||
*/
|
||||
'ref'?: (_grpc_channelz_v1_SubchannelRef | null);
|
||||
/**
|
||||
* Data specific to this channel.
|
||||
*/
|
||||
'data'?: (_grpc_channelz_v1_ChannelData | null);
|
||||
/**
|
||||
* There are no ordering guarantees on the order of channel refs.
|
||||
* There may not be cycles in the ref graph.
|
||||
* A channel ref may be present in more than one channel or subchannel.
|
||||
*/
|
||||
'channel_ref'?: (_grpc_channelz_v1_ChannelRef)[];
|
||||
/**
|
||||
* At most one of 'channel_ref+subchannel_ref' and 'socket' is set.
|
||||
* There are no ordering guarantees on the order of subchannel refs.
|
||||
* There may not be cycles in the ref graph.
|
||||
* A sub channel ref may be present in more than one channel or subchannel.
|
||||
*/
|
||||
'subchannel_ref'?: (_grpc_channelz_v1_SubchannelRef)[];
|
||||
/**
|
||||
* There are no ordering guarantees on the order of sockets.
|
||||
*/
|
||||
'socket_ref'?: (_grpc_channelz_v1_SocketRef)[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Subchannel is a logical grouping of channels, subchannels, and sockets.
|
||||
* A subchannel is load balanced over by it's ancestor
|
||||
*/
|
||||
export interface Subchannel__Output {
|
||||
/**
|
||||
* The identifier for this channel.
|
||||
*/
|
||||
'ref': (_grpc_channelz_v1_SubchannelRef__Output | null);
|
||||
/**
|
||||
* Data specific to this channel.
|
||||
*/
|
||||
'data': (_grpc_channelz_v1_ChannelData__Output | null);
|
||||
/**
|
||||
* There are no ordering guarantees on the order of channel refs.
|
||||
* There may not be cycles in the ref graph.
|
||||
* A channel ref may be present in more than one channel or subchannel.
|
||||
*/
|
||||
'channel_ref': (_grpc_channelz_v1_ChannelRef__Output)[];
|
||||
/**
|
||||
* At most one of 'channel_ref+subchannel_ref' and 'socket' is set.
|
||||
* There are no ordering guarantees on the order of subchannel refs.
|
||||
* There may not be cycles in the ref graph.
|
||||
* A sub channel ref may be present in more than one channel or subchannel.
|
||||
*/
|
||||
'subchannel_ref': (_grpc_channelz_v1_SubchannelRef__Output)[];
|
||||
/**
|
||||
* There are no ordering guarantees on the order of sockets.
|
||||
*/
|
||||
'socket_ref': (_grpc_channelz_v1_SocketRef__Output)[];
|
||||
}
|
||||
31
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/SubchannelRef.ts
generated
vendored
Normal file
31
node_modules/@grpc/grpc-js/src/generated/grpc/channelz/v1/SubchannelRef.ts
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// Original file: proto/channelz.proto
|
||||
|
||||
import type { Long } from '@grpc/proto-loader';
|
||||
|
||||
/**
|
||||
* SubchannelRef is a reference to a Subchannel.
|
||||
*/
|
||||
export interface SubchannelRef {
|
||||
/**
|
||||
* The globally unique id for this subchannel. Must be a positive number.
|
||||
*/
|
||||
'subchannel_id'?: (number | string | Long);
|
||||
/**
|
||||
* An optional name associated with the subchannel.
|
||||
*/
|
||||
'name'?: (string);
|
||||
}
|
||||
|
||||
/**
|
||||
* SubchannelRef is a reference to a Subchannel.
|
||||
*/
|
||||
export interface SubchannelRef__Output {
|
||||
/**
|
||||
* The globally unique id for this subchannel. Must be a positive number.
|
||||
*/
|
||||
'subchannel_id': (string);
|
||||
/**
|
||||
* An optional name associated with the subchannel.
|
||||
*/
|
||||
'name': (string);
|
||||
}
|
||||
311
node_modules/@grpc/grpc-js/src/http_proxy.ts
generated
vendored
Normal file
311
node_modules/@grpc/grpc-js/src/http_proxy.ts
generated
vendored
Normal file
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
* 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 { log } from './logging';
|
||||
import { LogVerbosity } from './constants';
|
||||
import { getDefaultAuthority } from './resolver';
|
||||
import { Socket } from 'net';
|
||||
import * as http from 'http';
|
||||
import * as tls from 'tls';
|
||||
import * as logging from './logging';
|
||||
import {
|
||||
SubchannelAddress,
|
||||
isTcpSubchannelAddress,
|
||||
subchannelAddressToString,
|
||||
} from './subchannel-address';
|
||||
import { ChannelOptions } from './channel-options';
|
||||
import { GrpcUri, parseUri, splitHostPort, uriToString } from './uri-parser';
|
||||
import { URL } from 'url';
|
||||
import { DEFAULT_PORT } from './resolver-dns';
|
||||
|
||||
const TRACER_NAME = 'proxy';
|
||||
|
||||
function trace(text: string): void {
|
||||
logging.trace(LogVerbosity.DEBUG, TRACER_NAME, text);
|
||||
}
|
||||
|
||||
interface ProxyInfo {
|
||||
address?: string;
|
||||
creds?: string;
|
||||
}
|
||||
|
||||
function getProxyInfo(): ProxyInfo {
|
||||
let proxyEnv = '';
|
||||
let envVar = '';
|
||||
/* Prefer using 'grpc_proxy'. Fallback on 'http_proxy' if it is not set.
|
||||
* Also prefer using 'https_proxy' with fallback on 'http_proxy'. The
|
||||
* fallback behavior can be removed if there's a demand for it.
|
||||
*/
|
||||
if (process.env.grpc_proxy) {
|
||||
envVar = 'grpc_proxy';
|
||||
proxyEnv = process.env.grpc_proxy;
|
||||
} else if (process.env.https_proxy) {
|
||||
envVar = 'https_proxy';
|
||||
proxyEnv = process.env.https_proxy;
|
||||
} else if (process.env.http_proxy) {
|
||||
envVar = 'http_proxy';
|
||||
proxyEnv = process.env.http_proxy;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
let proxyUrl: URL;
|
||||
try {
|
||||
proxyUrl = new URL(proxyEnv);
|
||||
} catch (e) {
|
||||
log(LogVerbosity.ERROR, `cannot parse value of "${envVar}" env var`);
|
||||
return {};
|
||||
}
|
||||
if (proxyUrl.protocol !== 'http:') {
|
||||
log(
|
||||
LogVerbosity.ERROR,
|
||||
`"${proxyUrl.protocol}" scheme not supported in proxy URI`
|
||||
);
|
||||
return {};
|
||||
}
|
||||
let userCred: string | null = null;
|
||||
if (proxyUrl.username) {
|
||||
if (proxyUrl.password) {
|
||||
log(LogVerbosity.INFO, 'userinfo found in proxy URI');
|
||||
userCred = `${proxyUrl.username}:${proxyUrl.password}`;
|
||||
} else {
|
||||
userCred = proxyUrl.username;
|
||||
}
|
||||
}
|
||||
const hostname = proxyUrl.hostname;
|
||||
let port = proxyUrl.port;
|
||||
/* The proxy URL uses the scheme "http:", which has a default port number of
|
||||
* 80. We need to set that explicitly here if it is omitted because otherwise
|
||||
* it will use gRPC's default port 443. */
|
||||
if (port === '') {
|
||||
port = '80';
|
||||
}
|
||||
const result: ProxyInfo = {
|
||||
address: `${hostname}:${port}`,
|
||||
};
|
||||
if (userCred) {
|
||||
result.creds = userCred;
|
||||
}
|
||||
trace(
|
||||
'Proxy server ' + result.address + ' set by environment variable ' + envVar
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
function getNoProxyHostList(): string[] {
|
||||
/* Prefer using 'no_grpc_proxy'. Fallback on 'no_proxy' if it is not set. */
|
||||
let noProxyStr: string | undefined = process.env.no_grpc_proxy;
|
||||
let envVar = 'no_grpc_proxy';
|
||||
if (!noProxyStr) {
|
||||
noProxyStr = process.env.no_proxy;
|
||||
envVar = 'no_proxy';
|
||||
}
|
||||
if (noProxyStr) {
|
||||
trace('No proxy server list set by environment variable ' + envVar);
|
||||
return noProxyStr.split(',');
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export interface ProxyMapResult {
|
||||
target: GrpcUri;
|
||||
extraOptions: ChannelOptions;
|
||||
}
|
||||
|
||||
export function mapProxyName(
|
||||
target: GrpcUri,
|
||||
options: ChannelOptions
|
||||
): ProxyMapResult {
|
||||
const noProxyResult: ProxyMapResult = {
|
||||
target: target,
|
||||
extraOptions: {},
|
||||
};
|
||||
if ((options['grpc.enable_http_proxy'] ?? 1) === 0) {
|
||||
return noProxyResult;
|
||||
}
|
||||
if (target.scheme === 'unix') {
|
||||
return noProxyResult;
|
||||
}
|
||||
const proxyInfo = getProxyInfo();
|
||||
if (!proxyInfo.address) {
|
||||
return noProxyResult;
|
||||
}
|
||||
const hostPort = splitHostPort(target.path);
|
||||
if (!hostPort) {
|
||||
return noProxyResult;
|
||||
}
|
||||
const serverHost = hostPort.host;
|
||||
for (const host of getNoProxyHostList()) {
|
||||
if (host === serverHost) {
|
||||
trace(
|
||||
'Not using proxy for target in no_proxy list: ' + uriToString(target)
|
||||
);
|
||||
return noProxyResult;
|
||||
}
|
||||
}
|
||||
const extraOptions: ChannelOptions = {
|
||||
'grpc.http_connect_target': uriToString(target),
|
||||
};
|
||||
if (proxyInfo.creds) {
|
||||
extraOptions['grpc.http_connect_creds'] = proxyInfo.creds;
|
||||
}
|
||||
return {
|
||||
target: {
|
||||
scheme: 'dns',
|
||||
path: proxyInfo.address,
|
||||
},
|
||||
extraOptions: extraOptions,
|
||||
};
|
||||
}
|
||||
|
||||
export interface ProxyConnectionResult {
|
||||
socket?: Socket;
|
||||
realTarget?: GrpcUri;
|
||||
}
|
||||
|
||||
export function getProxiedConnection(
|
||||
address: SubchannelAddress,
|
||||
channelOptions: ChannelOptions,
|
||||
connectionOptions: tls.ConnectionOptions
|
||||
): Promise<ProxyConnectionResult> {
|
||||
if (!('grpc.http_connect_target' in channelOptions)) {
|
||||
return Promise.resolve<ProxyConnectionResult>({});
|
||||
}
|
||||
const realTarget = channelOptions['grpc.http_connect_target'] as string;
|
||||
const parsedTarget = parseUri(realTarget);
|
||||
if (parsedTarget === null) {
|
||||
return Promise.resolve<ProxyConnectionResult>({});
|
||||
}
|
||||
const splitHostPost = splitHostPort(parsedTarget.path);
|
||||
if (splitHostPost === null) {
|
||||
return Promise.resolve<ProxyConnectionResult>({});
|
||||
}
|
||||
const hostPort = `${splitHostPost.host}:${
|
||||
splitHostPost.port ?? DEFAULT_PORT
|
||||
}`;
|
||||
const options: http.RequestOptions = {
|
||||
method: 'CONNECT',
|
||||
path: hostPort,
|
||||
};
|
||||
const headers: http.OutgoingHttpHeaders = {
|
||||
Host: hostPort,
|
||||
};
|
||||
// Connect to the subchannel address as a proxy
|
||||
if (isTcpSubchannelAddress(address)) {
|
||||
options.host = address.host;
|
||||
options.port = address.port;
|
||||
} else {
|
||||
options.socketPath = address.path;
|
||||
}
|
||||
if ('grpc.http_connect_creds' in channelOptions) {
|
||||
headers['Proxy-Authorization'] =
|
||||
'Basic ' +
|
||||
Buffer.from(channelOptions['grpc.http_connect_creds'] as string).toString(
|
||||
'base64'
|
||||
);
|
||||
}
|
||||
options.headers = headers;
|
||||
const proxyAddressString = subchannelAddressToString(address);
|
||||
trace('Using proxy ' + proxyAddressString + ' to connect to ' + options.path);
|
||||
return new Promise<ProxyConnectionResult>((resolve, reject) => {
|
||||
const request = http.request(options);
|
||||
request.once('connect', (res, socket, head) => {
|
||||
request.removeAllListeners();
|
||||
socket.removeAllListeners();
|
||||
if (res.statusCode === 200) {
|
||||
trace(
|
||||
'Successfully connected to ' +
|
||||
options.path +
|
||||
' through proxy ' +
|
||||
proxyAddressString
|
||||
);
|
||||
if ('secureContext' in connectionOptions) {
|
||||
/* The proxy is connecting to a TLS server, so upgrade this socket
|
||||
* connection to a TLS connection.
|
||||
* This is a workaround for https://github.com/nodejs/node/issues/32922
|
||||
* See https://github.com/grpc/grpc-node/pull/1369 for more info. */
|
||||
const targetPath = getDefaultAuthority(parsedTarget);
|
||||
const hostPort = splitHostPort(targetPath);
|
||||
const remoteHost = hostPort?.host ?? targetPath;
|
||||
|
||||
const cts = tls.connect(
|
||||
{
|
||||
host: remoteHost,
|
||||
servername: remoteHost,
|
||||
socket: socket,
|
||||
...connectionOptions,
|
||||
},
|
||||
() => {
|
||||
trace(
|
||||
'Successfully established a TLS connection to ' +
|
||||
options.path +
|
||||
' through proxy ' +
|
||||
proxyAddressString
|
||||
);
|
||||
resolve({ socket: cts, realTarget: parsedTarget });
|
||||
}
|
||||
);
|
||||
cts.on('error', (error: Error) => {
|
||||
trace(
|
||||
'Failed to establish a TLS connection to ' +
|
||||
options.path +
|
||||
' through proxy ' +
|
||||
proxyAddressString +
|
||||
' with error ' +
|
||||
error.message
|
||||
);
|
||||
reject();
|
||||
});
|
||||
} else {
|
||||
trace(
|
||||
'Successfully established a plaintext connection to ' +
|
||||
options.path +
|
||||
' through proxy ' +
|
||||
proxyAddressString
|
||||
);
|
||||
resolve({
|
||||
socket,
|
||||
realTarget: parsedTarget,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
log(
|
||||
LogVerbosity.ERROR,
|
||||
'Failed to connect to ' +
|
||||
options.path +
|
||||
' through proxy ' +
|
||||
proxyAddressString +
|
||||
' with status ' +
|
||||
res.statusCode
|
||||
);
|
||||
reject();
|
||||
}
|
||||
});
|
||||
request.once('error', err => {
|
||||
request.removeAllListeners();
|
||||
log(
|
||||
LogVerbosity.ERROR,
|
||||
'Failed to connect to proxy ' +
|
||||
proxyAddressString +
|
||||
' with error ' +
|
||||
err.message
|
||||
);
|
||||
reject();
|
||||
});
|
||||
request.end();
|
||||
});
|
||||
}
|
||||
284
node_modules/@grpc/grpc-js/src/index.ts
generated
vendored
Normal file
284
node_modules/@grpc/grpc-js/src/index.ts
generated
vendored
Normal file
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* 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 {
|
||||
ClientDuplexStream,
|
||||
ClientReadableStream,
|
||||
ClientUnaryCall,
|
||||
ClientWritableStream,
|
||||
ServiceError,
|
||||
} from './call';
|
||||
import { CallCredentials, OAuth2Client } from './call-credentials';
|
||||
import { StatusObject } from './call-interface';
|
||||
import { Channel, ChannelImplementation } from './channel';
|
||||
import { CompressionAlgorithms } from './compression-algorithms';
|
||||
import { ConnectivityState } from './connectivity-state';
|
||||
import { ChannelCredentials } from './channel-credentials';
|
||||
import {
|
||||
CallOptions,
|
||||
Client,
|
||||
ClientOptions,
|
||||
CallInvocationTransformer,
|
||||
CallProperties,
|
||||
UnaryCallback,
|
||||
} from './client';
|
||||
import { LogVerbosity, Status, Propagate } from './constants';
|
||||
import * as logging from './logging';
|
||||
import {
|
||||
Deserialize,
|
||||
loadPackageDefinition,
|
||||
makeClientConstructor,
|
||||
MethodDefinition,
|
||||
Serialize,
|
||||
ServiceDefinition,
|
||||
} from './make-client';
|
||||
import { Metadata, MetadataOptions, MetadataValue } from './metadata';
|
||||
import {
|
||||
Server,
|
||||
UntypedHandleCall,
|
||||
UntypedServiceImplementation,
|
||||
} from './server';
|
||||
import { KeyCertPair, ServerCredentials } from './server-credentials';
|
||||
import { StatusBuilder } from './status-builder';
|
||||
import {
|
||||
handleBidiStreamingCall,
|
||||
handleServerStreamingCall,
|
||||
handleClientStreamingCall,
|
||||
handleUnaryCall,
|
||||
sendUnaryData,
|
||||
ServerUnaryCall,
|
||||
ServerReadableStream,
|
||||
ServerWritableStream,
|
||||
ServerDuplexStream,
|
||||
ServerErrorResponse,
|
||||
} from './server-call';
|
||||
|
||||
export { OAuth2Client };
|
||||
|
||||
/**** Client Credentials ****/
|
||||
|
||||
// Using assign only copies enumerable properties, which is what we want
|
||||
export const credentials = {
|
||||
/**
|
||||
* Combine a ChannelCredentials with any number of CallCredentials into a
|
||||
* single ChannelCredentials object.
|
||||
* @param channelCredentials The ChannelCredentials object.
|
||||
* @param callCredentials Any number of CallCredentials objects.
|
||||
* @return The resulting ChannelCredentials object.
|
||||
*/
|
||||
combineChannelCredentials: (
|
||||
channelCredentials: ChannelCredentials,
|
||||
...callCredentials: CallCredentials[]
|
||||
): ChannelCredentials => {
|
||||
return callCredentials.reduce(
|
||||
(acc, other) => acc.compose(other),
|
||||
channelCredentials
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Combine any number of CallCredentials into a single CallCredentials
|
||||
* object.
|
||||
* @param first The first CallCredentials object.
|
||||
* @param additional Any number of additional CallCredentials objects.
|
||||
* @return The resulting CallCredentials object.
|
||||
*/
|
||||
combineCallCredentials: (
|
||||
first: CallCredentials,
|
||||
...additional: CallCredentials[]
|
||||
): CallCredentials => {
|
||||
return additional.reduce((acc, other) => acc.compose(other), first);
|
||||
},
|
||||
|
||||
// from channel-credentials.ts
|
||||
createInsecure: ChannelCredentials.createInsecure,
|
||||
createSsl: ChannelCredentials.createSsl,
|
||||
createFromSecureContext: ChannelCredentials.createFromSecureContext,
|
||||
|
||||
// from call-credentials.ts
|
||||
createFromMetadataGenerator: CallCredentials.createFromMetadataGenerator,
|
||||
createFromGoogleCredential: CallCredentials.createFromGoogleCredential,
|
||||
createEmpty: CallCredentials.createEmpty,
|
||||
};
|
||||
|
||||
/**** Metadata ****/
|
||||
|
||||
export { Metadata, MetadataOptions, MetadataValue };
|
||||
|
||||
/**** Constants ****/
|
||||
|
||||
export {
|
||||
LogVerbosity as logVerbosity,
|
||||
Status as status,
|
||||
ConnectivityState as connectivityState,
|
||||
Propagate as propagate,
|
||||
CompressionAlgorithms as compressionAlgorithms,
|
||||
// TODO: Other constants as well
|
||||
};
|
||||
|
||||
/**** Client ****/
|
||||
|
||||
export {
|
||||
Client,
|
||||
ClientOptions,
|
||||
loadPackageDefinition,
|
||||
makeClientConstructor,
|
||||
makeClientConstructor as makeGenericClientConstructor,
|
||||
CallProperties,
|
||||
CallInvocationTransformer,
|
||||
ChannelImplementation as Channel,
|
||||
Channel as ChannelInterface,
|
||||
UnaryCallback as requestCallback,
|
||||
};
|
||||
|
||||
/**
|
||||
* Close a Client object.
|
||||
* @param client The client to close.
|
||||
*/
|
||||
export const closeClient = (client: Client) => client.close();
|
||||
|
||||
export const waitForClientReady = (
|
||||
client: Client,
|
||||
deadline: Date | number,
|
||||
callback: (error?: Error) => void
|
||||
) => client.waitForReady(deadline, callback);
|
||||
|
||||
/* Interfaces */
|
||||
|
||||
export {
|
||||
sendUnaryData,
|
||||
ChannelCredentials,
|
||||
CallCredentials,
|
||||
Deadline,
|
||||
Serialize as serialize,
|
||||
Deserialize as deserialize,
|
||||
ClientUnaryCall,
|
||||
ClientReadableStream,
|
||||
ClientWritableStream,
|
||||
ClientDuplexStream,
|
||||
CallOptions,
|
||||
MethodDefinition,
|
||||
StatusObject,
|
||||
ServiceError,
|
||||
ServerUnaryCall,
|
||||
ServerReadableStream,
|
||||
ServerWritableStream,
|
||||
ServerDuplexStream,
|
||||
ServerErrorResponse,
|
||||
ServiceDefinition,
|
||||
UntypedHandleCall,
|
||||
UntypedServiceImplementation,
|
||||
};
|
||||
|
||||
/**** Server ****/
|
||||
|
||||
export {
|
||||
handleBidiStreamingCall,
|
||||
handleServerStreamingCall,
|
||||
handleUnaryCall,
|
||||
handleClientStreamingCall,
|
||||
};
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
export type Call =
|
||||
| ClientUnaryCall
|
||||
| ClientReadableStream<any>
|
||||
| ClientWritableStream<any>
|
||||
| ClientDuplexStream<any, any>;
|
||||
/* eslint-enable @typescript-eslint/no-explicit-any */
|
||||
|
||||
/**** Unimplemented function stubs ****/
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
export const loadObject = (value: any, options: any): never => {
|
||||
throw new Error(
|
||||
'Not available in this library. Use @grpc/proto-loader and loadPackageDefinition instead'
|
||||
);
|
||||
};
|
||||
|
||||
export const load = (filename: any, format: any, options: any): never => {
|
||||
throw new Error(
|
||||
'Not available in this library. Use @grpc/proto-loader and loadPackageDefinition instead'
|
||||
);
|
||||
};
|
||||
|
||||
export const setLogger = (logger: Partial<Console>): void => {
|
||||
logging.setLogger(logger);
|
||||
};
|
||||
|
||||
export const setLogVerbosity = (verbosity: LogVerbosity): void => {
|
||||
logging.setLoggerVerbosity(verbosity);
|
||||
};
|
||||
|
||||
export { Server };
|
||||
export { ServerCredentials };
|
||||
export { KeyCertPair };
|
||||
|
||||
export const getClientChannel = (client: Client) => {
|
||||
return Client.prototype.getChannel.call(client);
|
||||
};
|
||||
|
||||
export { StatusBuilder };
|
||||
|
||||
export { Listener, InterceptingListener } from './call-interface';
|
||||
|
||||
export {
|
||||
Requester,
|
||||
ListenerBuilder,
|
||||
RequesterBuilder,
|
||||
Interceptor,
|
||||
InterceptorOptions,
|
||||
InterceptorProvider,
|
||||
InterceptingCall,
|
||||
InterceptorConfigurationError,
|
||||
NextCall,
|
||||
} from './client-interceptors';
|
||||
|
||||
export {
|
||||
GrpcObject,
|
||||
ServiceClientConstructor,
|
||||
ProtobufTypeDefinition,
|
||||
} from './make-client';
|
||||
|
||||
export { ChannelOptions } from './channel-options';
|
||||
|
||||
export { getChannelzServiceDefinition, getChannelzHandlers } from './channelz';
|
||||
|
||||
export { addAdminServicesToServer } from './admin';
|
||||
|
||||
import * as experimental from './experimental';
|
||||
export { experimental };
|
||||
|
||||
import * as resolver_dns from './resolver-dns';
|
||||
import * as resolver_uds from './resolver-uds';
|
||||
import * as resolver_ip from './resolver-ip';
|
||||
import * as load_balancer_pick_first from './load-balancer-pick-first';
|
||||
import * as load_balancer_round_robin from './load-balancer-round-robin';
|
||||
import * as load_balancer_outlier_detection from './load-balancer-outlier-detection';
|
||||
import * as channelz from './channelz';
|
||||
import { Deadline } from './deadline';
|
||||
|
||||
(() => {
|
||||
resolver_dns.setup();
|
||||
resolver_uds.setup();
|
||||
resolver_ip.setup();
|
||||
load_balancer_pick_first.setup();
|
||||
load_balancer_round_robin.setup();
|
||||
load_balancer_outlier_detection.setup();
|
||||
channelz.setup();
|
||||
})();
|
||||
830
node_modules/@grpc/grpc-js/src/internal-channel.ts
generated
vendored
Normal file
830
node_modules/@grpc/grpc-js/src/internal-channel.ts
generated
vendored
Normal file
@@ -0,0 +1,830 @@
|
||||
/*
|
||||
* 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 { ChannelCredentials } from './channel-credentials';
|
||||
import { ChannelOptions } from './channel-options';
|
||||
import { ResolvingLoadBalancer } from './resolving-load-balancer';
|
||||
import { SubchannelPool, getSubchannelPool } from './subchannel-pool';
|
||||
import { ChannelControlHelper } from './load-balancer';
|
||||
import { UnavailablePicker, Picker, QueuePicker } from './picker';
|
||||
import { Metadata } from './metadata';
|
||||
import { Status, LogVerbosity, Propagate } from './constants';
|
||||
import { FilterStackFactory } from './filter-stack';
|
||||
import { CompressionFilterFactory } from './compression-filter';
|
||||
import {
|
||||
CallConfig,
|
||||
ConfigSelector,
|
||||
getDefaultAuthority,
|
||||
mapUriDefaultScheme,
|
||||
} from './resolver';
|
||||
import { trace } from './logging';
|
||||
import { SubchannelAddress } from './subchannel-address';
|
||||
import { mapProxyName } from './http_proxy';
|
||||
import { GrpcUri, parseUri, uriToString } from './uri-parser';
|
||||
import { ServerSurfaceCall } from './server-call';
|
||||
|
||||
import { ConnectivityState } from './connectivity-state';
|
||||
import {
|
||||
ChannelInfo,
|
||||
ChannelRef,
|
||||
ChannelzCallTracker,
|
||||
ChannelzChildrenTracker,
|
||||
ChannelzTrace,
|
||||
registerChannelzChannel,
|
||||
SubchannelRef,
|
||||
unregisterChannelzRef,
|
||||
} from './channelz';
|
||||
import { LoadBalancingCall } from './load-balancing-call';
|
||||
import { CallCredentials } from './call-credentials';
|
||||
import { Call, CallStreamOptions, StatusObject } from './call-interface';
|
||||
import { Deadline, deadlineToString } from './deadline';
|
||||
import { ResolvingCall } from './resolving-call';
|
||||
import { getNextCallNumber } from './call-number';
|
||||
import { restrictControlPlaneStatusCode } from './control-plane-status';
|
||||
import {
|
||||
MessageBufferTracker,
|
||||
RetryingCall,
|
||||
RetryThrottler,
|
||||
} from './retrying-call';
|
||||
import {
|
||||
BaseSubchannelWrapper,
|
||||
ConnectivityStateListener,
|
||||
SubchannelInterface,
|
||||
} from './subchannel-interface';
|
||||
|
||||
/**
|
||||
* See https://nodejs.org/api/timers.html#timers_setinterval_callback_delay_args
|
||||
*/
|
||||
const MAX_TIMEOUT_TIME = 2147483647;
|
||||
|
||||
const MIN_IDLE_TIMEOUT_MS = 1000;
|
||||
|
||||
// 30 minutes
|
||||
const DEFAULT_IDLE_TIMEOUT_MS = 30 * 60 * 1000;
|
||||
|
||||
interface ConnectivityStateWatcher {
|
||||
currentState: ConnectivityState;
|
||||
timer: NodeJS.Timeout | null;
|
||||
callback: (error?: Error) => void;
|
||||
}
|
||||
|
||||
interface NoneConfigResult {
|
||||
type: 'NONE';
|
||||
}
|
||||
|
||||
interface SuccessConfigResult {
|
||||
type: 'SUCCESS';
|
||||
config: CallConfig;
|
||||
}
|
||||
|
||||
interface ErrorConfigResult {
|
||||
type: 'ERROR';
|
||||
error: StatusObject;
|
||||
}
|
||||
|
||||
type GetConfigResult =
|
||||
| NoneConfigResult
|
||||
| SuccessConfigResult
|
||||
| ErrorConfigResult;
|
||||
|
||||
const RETRY_THROTTLER_MAP: Map<string, RetryThrottler> = new Map();
|
||||
|
||||
const DEFAULT_RETRY_BUFFER_SIZE_BYTES = 1 << 24; // 16 MB
|
||||
const DEFAULT_PER_RPC_RETRY_BUFFER_SIZE_BYTES = 1 << 20; // 1 MB
|
||||
|
||||
class ChannelSubchannelWrapper
|
||||
extends BaseSubchannelWrapper
|
||||
implements SubchannelInterface
|
||||
{
|
||||
private refCount = 0;
|
||||
private subchannelStateListener: ConnectivityStateListener;
|
||||
constructor(
|
||||
childSubchannel: SubchannelInterface,
|
||||
private channel: InternalChannel
|
||||
) {
|
||||
super(childSubchannel);
|
||||
this.subchannelStateListener = (
|
||||
subchannel,
|
||||
previousState,
|
||||
newState,
|
||||
keepaliveTime
|
||||
) => {
|
||||
channel.throttleKeepalive(keepaliveTime);
|
||||
};
|
||||
childSubchannel.addConnectivityStateListener(this.subchannelStateListener);
|
||||
}
|
||||
|
||||
ref(): void {
|
||||
this.child.ref();
|
||||
this.refCount += 1;
|
||||
}
|
||||
|
||||
unref(): void {
|
||||
this.child.unref();
|
||||
this.refCount -= 1;
|
||||
if (this.refCount <= 0) {
|
||||
this.child.removeConnectivityStateListener(this.subchannelStateListener);
|
||||
this.channel.removeWrappedSubchannel(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class InternalChannel {
|
||||
private readonly resolvingLoadBalancer: ResolvingLoadBalancer;
|
||||
private readonly subchannelPool: SubchannelPool;
|
||||
private connectivityState: ConnectivityState = ConnectivityState.IDLE;
|
||||
private currentPicker: Picker = new UnavailablePicker();
|
||||
/**
|
||||
* Calls queued up to get a call config. Should only be populated before the
|
||||
* first time the resolver returns a result, which includes the ConfigSelector.
|
||||
*/
|
||||
private configSelectionQueue: ResolvingCall[] = [];
|
||||
private pickQueue: LoadBalancingCall[] = [];
|
||||
private connectivityStateWatchers: ConnectivityStateWatcher[] = [];
|
||||
private readonly defaultAuthority: string;
|
||||
private readonly filterStackFactory: FilterStackFactory;
|
||||
private readonly target: GrpcUri;
|
||||
/**
|
||||
* This timer does not do anything on its own. Its purpose is to hold the
|
||||
* event loop open while there are any pending calls for the channel that
|
||||
* have not yet been assigned to specific subchannels. In other words,
|
||||
* the invariant is that callRefTimer is reffed if and only if pickQueue
|
||||
* is non-empty.
|
||||
*/
|
||||
private readonly callRefTimer: NodeJS.Timeout;
|
||||
private configSelector: ConfigSelector | null = null;
|
||||
/**
|
||||
* This is the error from the name resolver if it failed most recently. It
|
||||
* is only used to end calls that start while there is no config selector
|
||||
* and the name resolver is in backoff, so it should be nulled if
|
||||
* configSelector becomes set or the channel state becomes anything other
|
||||
* than TRANSIENT_FAILURE.
|
||||
*/
|
||||
private currentResolutionError: StatusObject | null = null;
|
||||
private readonly retryBufferTracker: MessageBufferTracker;
|
||||
private keepaliveTime: number;
|
||||
private readonly wrappedSubchannels: Set<ChannelSubchannelWrapper> =
|
||||
new Set();
|
||||
|
||||
private callCount = 0;
|
||||
private idleTimer: NodeJS.Timeout | null = null;
|
||||
private readonly idleTimeoutMs: number;
|
||||
private lastActivityTimestamp: Date;
|
||||
|
||||
// Channelz info
|
||||
private readonly channelzEnabled: boolean = true;
|
||||
private readonly originalTarget: string;
|
||||
private readonly channelzRef: ChannelRef;
|
||||
private readonly channelzTrace: ChannelzTrace;
|
||||
private readonly callTracker = new ChannelzCallTracker();
|
||||
private readonly childrenTracker = new ChannelzChildrenTracker();
|
||||
|
||||
constructor(
|
||||
target: string,
|
||||
private readonly credentials: ChannelCredentials,
|
||||
private readonly options: ChannelOptions
|
||||
) {
|
||||
if (typeof target !== 'string') {
|
||||
throw new TypeError('Channel target must be a string');
|
||||
}
|
||||
if (!(credentials instanceof ChannelCredentials)) {
|
||||
throw new TypeError(
|
||||
'Channel credentials must be a ChannelCredentials object'
|
||||
);
|
||||
}
|
||||
if (options) {
|
||||
if (typeof options !== 'object') {
|
||||
throw new TypeError('Channel options must be an object');
|
||||
}
|
||||
}
|
||||
this.originalTarget = target;
|
||||
const originalTargetUri = parseUri(target);
|
||||
if (originalTargetUri === null) {
|
||||
throw new Error(`Could not parse target name "${target}"`);
|
||||
}
|
||||
/* This ensures that the target has a scheme that is registered with the
|
||||
* resolver */
|
||||
const defaultSchemeMapResult = mapUriDefaultScheme(originalTargetUri);
|
||||
if (defaultSchemeMapResult === null) {
|
||||
throw new Error(
|
||||
`Could not find a default scheme for target name "${target}"`
|
||||
);
|
||||
}
|
||||
|
||||
this.callRefTimer = setInterval(() => {}, MAX_TIMEOUT_TIME);
|
||||
this.callRefTimer.unref?.();
|
||||
|
||||
if (this.options['grpc.enable_channelz'] === 0) {
|
||||
this.channelzEnabled = false;
|
||||
}
|
||||
|
||||
this.channelzTrace = new ChannelzTrace();
|
||||
this.channelzRef = registerChannelzChannel(
|
||||
target,
|
||||
() => this.getChannelzInfo(),
|
||||
this.channelzEnabled
|
||||
);
|
||||
if (this.channelzEnabled) {
|
||||
this.channelzTrace.addTrace('CT_INFO', 'Channel created');
|
||||
}
|
||||
|
||||
if (this.options['grpc.default_authority']) {
|
||||
this.defaultAuthority = this.options['grpc.default_authority'] as string;
|
||||
} else {
|
||||
this.defaultAuthority = getDefaultAuthority(defaultSchemeMapResult);
|
||||
}
|
||||
const proxyMapResult = mapProxyName(defaultSchemeMapResult, options);
|
||||
this.target = proxyMapResult.target;
|
||||
this.options = Object.assign({}, this.options, proxyMapResult.extraOptions);
|
||||
|
||||
/* The global boolean parameter to getSubchannelPool has the inverse meaning to what
|
||||
* the grpc.use_local_subchannel_pool channel option means. */
|
||||
this.subchannelPool = getSubchannelPool(
|
||||
(options['grpc.use_local_subchannel_pool'] ?? 0) === 0
|
||||
);
|
||||
this.retryBufferTracker = new MessageBufferTracker(
|
||||
options['grpc.retry_buffer_size'] ?? DEFAULT_RETRY_BUFFER_SIZE_BYTES,
|
||||
options['grpc.per_rpc_retry_buffer_size'] ??
|
||||
DEFAULT_PER_RPC_RETRY_BUFFER_SIZE_BYTES
|
||||
);
|
||||
this.keepaliveTime = options['grpc.keepalive_time_ms'] ?? -1;
|
||||
this.idleTimeoutMs = Math.max(
|
||||
options['grpc.client_idle_timeout_ms'] ?? DEFAULT_IDLE_TIMEOUT_MS,
|
||||
MIN_IDLE_TIMEOUT_MS
|
||||
);
|
||||
const channelControlHelper: ChannelControlHelper = {
|
||||
createSubchannel: (
|
||||
subchannelAddress: SubchannelAddress,
|
||||
subchannelArgs: ChannelOptions
|
||||
) => {
|
||||
const subchannel = this.subchannelPool.getOrCreateSubchannel(
|
||||
this.target,
|
||||
subchannelAddress,
|
||||
Object.assign({}, this.options, subchannelArgs),
|
||||
this.credentials
|
||||
);
|
||||
subchannel.throttleKeepalive(this.keepaliveTime);
|
||||
if (this.channelzEnabled) {
|
||||
this.channelzTrace.addTrace(
|
||||
'CT_INFO',
|
||||
'Created subchannel or used existing subchannel',
|
||||
subchannel.getChannelzRef()
|
||||
);
|
||||
}
|
||||
const wrappedSubchannel = new ChannelSubchannelWrapper(
|
||||
subchannel,
|
||||
this
|
||||
);
|
||||
this.wrappedSubchannels.add(wrappedSubchannel);
|
||||
return wrappedSubchannel;
|
||||
},
|
||||
updateState: (connectivityState: ConnectivityState, picker: Picker) => {
|
||||
this.currentPicker = picker;
|
||||
const queueCopy = this.pickQueue.slice();
|
||||
this.pickQueue = [];
|
||||
if (queueCopy.length > 0) {
|
||||
this.callRefTimerUnref();
|
||||
}
|
||||
for (const call of queueCopy) {
|
||||
call.doPick();
|
||||
}
|
||||
this.updateState(connectivityState);
|
||||
},
|
||||
requestReresolution: () => {
|
||||
// This should never be called.
|
||||
throw new Error(
|
||||
'Resolving load balancer should never call requestReresolution'
|
||||
);
|
||||
},
|
||||
addChannelzChild: (child: ChannelRef | SubchannelRef) => {
|
||||
if (this.channelzEnabled) {
|
||||
this.childrenTracker.refChild(child);
|
||||
}
|
||||
},
|
||||
removeChannelzChild: (child: ChannelRef | SubchannelRef) => {
|
||||
if (this.channelzEnabled) {
|
||||
this.childrenTracker.unrefChild(child);
|
||||
}
|
||||
},
|
||||
};
|
||||
this.resolvingLoadBalancer = new ResolvingLoadBalancer(
|
||||
this.target,
|
||||
channelControlHelper,
|
||||
options,
|
||||
(serviceConfig, configSelector) => {
|
||||
if (serviceConfig.retryThrottling) {
|
||||
RETRY_THROTTLER_MAP.set(
|
||||
this.getTarget(),
|
||||
new RetryThrottler(
|
||||
serviceConfig.retryThrottling.maxTokens,
|
||||
serviceConfig.retryThrottling.tokenRatio,
|
||||
RETRY_THROTTLER_MAP.get(this.getTarget())
|
||||
)
|
||||
);
|
||||
} else {
|
||||
RETRY_THROTTLER_MAP.delete(this.getTarget());
|
||||
}
|
||||
if (this.channelzEnabled) {
|
||||
this.channelzTrace.addTrace(
|
||||
'CT_INFO',
|
||||
'Address resolution succeeded'
|
||||
);
|
||||
}
|
||||
this.configSelector = configSelector;
|
||||
this.currentResolutionError = null;
|
||||
/* We process the queue asynchronously to ensure that the corresponding
|
||||
* load balancer update has completed. */
|
||||
process.nextTick(() => {
|
||||
const localQueue = this.configSelectionQueue;
|
||||
this.configSelectionQueue = [];
|
||||
if (localQueue.length > 0) {
|
||||
this.callRefTimerUnref();
|
||||
}
|
||||
for (const call of localQueue) {
|
||||
call.getConfig();
|
||||
}
|
||||
});
|
||||
},
|
||||
status => {
|
||||
if (this.channelzEnabled) {
|
||||
this.channelzTrace.addTrace(
|
||||
'CT_WARNING',
|
||||
'Address resolution failed with code ' +
|
||||
status.code +
|
||||
' and details "' +
|
||||
status.details +
|
||||
'"'
|
||||
);
|
||||
}
|
||||
if (this.configSelectionQueue.length > 0) {
|
||||
this.trace(
|
||||
'Name resolution failed with calls queued for config selection'
|
||||
);
|
||||
}
|
||||
if (this.configSelector === null) {
|
||||
this.currentResolutionError = {
|
||||
...restrictControlPlaneStatusCode(status.code, status.details),
|
||||
metadata: status.metadata,
|
||||
};
|
||||
}
|
||||
const localQueue = this.configSelectionQueue;
|
||||
this.configSelectionQueue = [];
|
||||
if (localQueue.length > 0) {
|
||||
this.callRefTimerUnref();
|
||||
}
|
||||
for (const call of localQueue) {
|
||||
call.reportResolverError(status);
|
||||
}
|
||||
}
|
||||
);
|
||||
this.filterStackFactory = new FilterStackFactory([
|
||||
new CompressionFilterFactory(this, this.options),
|
||||
]);
|
||||
this.trace(
|
||||
'Channel constructed with options ' +
|
||||
JSON.stringify(options, undefined, 2)
|
||||
);
|
||||
const error = new Error();
|
||||
trace(
|
||||
LogVerbosity.DEBUG,
|
||||
'channel_stacktrace',
|
||||
'(' +
|
||||
this.channelzRef.id +
|
||||
') ' +
|
||||
'Channel constructed \n' +
|
||||
error.stack?.substring(error.stack.indexOf('\n') + 1)
|
||||
);
|
||||
this.lastActivityTimestamp = new Date();
|
||||
}
|
||||
|
||||
private getChannelzInfo(): ChannelInfo {
|
||||
return {
|
||||
target: this.originalTarget,
|
||||
state: this.connectivityState,
|
||||
trace: this.channelzTrace,
|
||||
callTracker: this.callTracker,
|
||||
children: this.childrenTracker.getChildLists(),
|
||||
};
|
||||
}
|
||||
|
||||
private trace(text: string, verbosityOverride?: LogVerbosity) {
|
||||
trace(
|
||||
verbosityOverride ?? LogVerbosity.DEBUG,
|
||||
'channel',
|
||||
'(' + this.channelzRef.id + ') ' + uriToString(this.target) + ' ' + text
|
||||
);
|
||||
}
|
||||
|
||||
private callRefTimerRef() {
|
||||
// If the hasRef function does not exist, always run the code
|
||||
if (!this.callRefTimer.hasRef?.()) {
|
||||
this.trace(
|
||||
'callRefTimer.ref | configSelectionQueue.length=' +
|
||||
this.configSelectionQueue.length +
|
||||
' pickQueue.length=' +
|
||||
this.pickQueue.length
|
||||
);
|
||||
this.callRefTimer.ref?.();
|
||||
}
|
||||
}
|
||||
|
||||
private callRefTimerUnref() {
|
||||
// If the hasRef function does not exist, always run the code
|
||||
if (!this.callRefTimer.hasRef || this.callRefTimer.hasRef()) {
|
||||
this.trace(
|
||||
'callRefTimer.unref | configSelectionQueue.length=' +
|
||||
this.configSelectionQueue.length +
|
||||
' pickQueue.length=' +
|
||||
this.pickQueue.length
|
||||
);
|
||||
this.callRefTimer.unref?.();
|
||||
}
|
||||
}
|
||||
|
||||
private removeConnectivityStateWatcher(
|
||||
watcherObject: ConnectivityStateWatcher
|
||||
) {
|
||||
const watcherIndex = this.connectivityStateWatchers.findIndex(
|
||||
value => value === watcherObject
|
||||
);
|
||||
if (watcherIndex >= 0) {
|
||||
this.connectivityStateWatchers.splice(watcherIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private updateState(newState: ConnectivityState): void {
|
||||
trace(
|
||||
LogVerbosity.DEBUG,
|
||||
'connectivity_state',
|
||||
'(' +
|
||||
this.channelzRef.id +
|
||||
') ' +
|
||||
uriToString(this.target) +
|
||||
' ' +
|
||||
ConnectivityState[this.connectivityState] +
|
||||
' -> ' +
|
||||
ConnectivityState[newState]
|
||||
);
|
||||
if (this.channelzEnabled) {
|
||||
this.channelzTrace.addTrace(
|
||||
'CT_INFO',
|
||||
'Connectivity state change to ' + ConnectivityState[newState]
|
||||
);
|
||||
}
|
||||
this.connectivityState = newState;
|
||||
const watchersCopy = this.connectivityStateWatchers.slice();
|
||||
for (const watcherObject of watchersCopy) {
|
||||
if (newState !== watcherObject.currentState) {
|
||||
if (watcherObject.timer) {
|
||||
clearTimeout(watcherObject.timer);
|
||||
}
|
||||
this.removeConnectivityStateWatcher(watcherObject);
|
||||
watcherObject.callback();
|
||||
}
|
||||
}
|
||||
if (newState !== ConnectivityState.TRANSIENT_FAILURE) {
|
||||
this.currentResolutionError = null;
|
||||
}
|
||||
}
|
||||
|
||||
throttleKeepalive(newKeepaliveTime: number) {
|
||||
if (newKeepaliveTime > this.keepaliveTime) {
|
||||
this.keepaliveTime = newKeepaliveTime;
|
||||
for (const wrappedSubchannel of this.wrappedSubchannels) {
|
||||
wrappedSubchannel.throttleKeepalive(newKeepaliveTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removeWrappedSubchannel(wrappedSubchannel: ChannelSubchannelWrapper) {
|
||||
this.wrappedSubchannels.delete(wrappedSubchannel);
|
||||
}
|
||||
|
||||
doPick(metadata: Metadata, extraPickInfo: { [key: string]: string }) {
|
||||
return this.currentPicker.pick({
|
||||
metadata: metadata,
|
||||
extraPickInfo: extraPickInfo,
|
||||
});
|
||||
}
|
||||
|
||||
queueCallForPick(call: LoadBalancingCall) {
|
||||
this.pickQueue.push(call);
|
||||
this.callRefTimerRef();
|
||||
}
|
||||
|
||||
getConfig(method: string, metadata: Metadata): GetConfigResult {
|
||||
this.resolvingLoadBalancer.exitIdle();
|
||||
if (this.configSelector) {
|
||||
return {
|
||||
type: 'SUCCESS',
|
||||
config: this.configSelector(method, metadata),
|
||||
};
|
||||
} else {
|
||||
if (this.currentResolutionError) {
|
||||
return {
|
||||
type: 'ERROR',
|
||||
error: this.currentResolutionError,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
type: 'NONE',
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
queueCallForConfig(call: ResolvingCall) {
|
||||
this.configSelectionQueue.push(call);
|
||||
this.callRefTimerRef();
|
||||
}
|
||||
|
||||
private enterIdle() {
|
||||
this.resolvingLoadBalancer.destroy();
|
||||
this.updateState(ConnectivityState.IDLE);
|
||||
this.currentPicker = new QueuePicker(this.resolvingLoadBalancer);
|
||||
if (this.idleTimer) {
|
||||
clearTimeout(this.idleTimer);
|
||||
this.idleTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private startIdleTimeout(timeoutMs: number) {
|
||||
this.idleTimer = setTimeout(() => {
|
||||
if (this.callCount > 0) {
|
||||
/* If there is currently a call, the channel will not go idle for a
|
||||
* period of at least idleTimeoutMs, so check again after that time.
|
||||
*/
|
||||
this.startIdleTimeout(this.idleTimeoutMs);
|
||||
return;
|
||||
}
|
||||
const now = new Date();
|
||||
const timeSinceLastActivity = now.valueOf() - this.lastActivityTimestamp.valueOf();
|
||||
if (timeSinceLastActivity >= this.idleTimeoutMs) {
|
||||
this.trace(
|
||||
'Idle timer triggered after ' +
|
||||
this.idleTimeoutMs +
|
||||
'ms of inactivity'
|
||||
);
|
||||
this.enterIdle();
|
||||
} else {
|
||||
/* Whenever the timer fires with the latest activity being too recent,
|
||||
* set the timer again for the time when the time since the last
|
||||
* activity is equal to the timeout. This should result in the timer
|
||||
* firing no more than once every idleTimeoutMs/2 on average. */
|
||||
this.startIdleTimeout(this.idleTimeoutMs - timeSinceLastActivity);
|
||||
}
|
||||
}, timeoutMs);
|
||||
this.idleTimer.unref?.();
|
||||
}
|
||||
|
||||
private maybeStartIdleTimer() {
|
||||
if (this.connectivityState !== ConnectivityState.SHUTDOWN && !this.idleTimer) {
|
||||
this.startIdleTimeout(this.idleTimeoutMs);
|
||||
}
|
||||
}
|
||||
|
||||
private onCallStart() {
|
||||
if (this.channelzEnabled) {
|
||||
this.callTracker.addCallStarted();
|
||||
}
|
||||
this.callCount += 1;
|
||||
}
|
||||
|
||||
private onCallEnd(status: StatusObject) {
|
||||
if (this.channelzEnabled) {
|
||||
if (status.code === Status.OK) {
|
||||
this.callTracker.addCallSucceeded();
|
||||
} else {
|
||||
this.callTracker.addCallFailed();
|
||||
}
|
||||
}
|
||||
this.callCount -= 1;
|
||||
this.lastActivityTimestamp = new Date();
|
||||
this.maybeStartIdleTimer();
|
||||
}
|
||||
|
||||
createLoadBalancingCall(
|
||||
callConfig: CallConfig,
|
||||
method: string,
|
||||
host: string,
|
||||
credentials: CallCredentials,
|
||||
deadline: Deadline
|
||||
): LoadBalancingCall {
|
||||
const callNumber = getNextCallNumber();
|
||||
this.trace(
|
||||
'createLoadBalancingCall [' + callNumber + '] method="' + method + '"'
|
||||
);
|
||||
return new LoadBalancingCall(
|
||||
this,
|
||||
callConfig,
|
||||
method,
|
||||
host,
|
||||
credentials,
|
||||
deadline,
|
||||
callNumber
|
||||
);
|
||||
}
|
||||
|
||||
createRetryingCall(
|
||||
callConfig: CallConfig,
|
||||
method: string,
|
||||
host: string,
|
||||
credentials: CallCredentials,
|
||||
deadline: Deadline
|
||||
): RetryingCall {
|
||||
const callNumber = getNextCallNumber();
|
||||
this.trace(
|
||||
'createRetryingCall [' + callNumber + '] method="' + method + '"'
|
||||
);
|
||||
return new RetryingCall(
|
||||
this,
|
||||
callConfig,
|
||||
method,
|
||||
host,
|
||||
credentials,
|
||||
deadline,
|
||||
callNumber,
|
||||
this.retryBufferTracker,
|
||||
RETRY_THROTTLER_MAP.get(this.getTarget())
|
||||
);
|
||||
}
|
||||
|
||||
createInnerCall(
|
||||
callConfig: CallConfig,
|
||||
method: string,
|
||||
host: string,
|
||||
credentials: CallCredentials,
|
||||
deadline: Deadline
|
||||
): Call {
|
||||
// Create a RetryingCall if retries are enabled
|
||||
if (this.options['grpc.enable_retries'] === 0) {
|
||||
return this.createLoadBalancingCall(
|
||||
callConfig,
|
||||
method,
|
||||
host,
|
||||
credentials,
|
||||
deadline
|
||||
);
|
||||
} else {
|
||||
return this.createRetryingCall(
|
||||
callConfig,
|
||||
method,
|
||||
host,
|
||||
credentials,
|
||||
deadline
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
createResolvingCall(
|
||||
method: string,
|
||||
deadline: Deadline,
|
||||
host: string | null | undefined,
|
||||
parentCall: ServerSurfaceCall | null,
|
||||
propagateFlags: number | null | undefined
|
||||
): ResolvingCall {
|
||||
const callNumber = getNextCallNumber();
|
||||
this.trace(
|
||||
'createResolvingCall [' +
|
||||
callNumber +
|
||||
'] method="' +
|
||||
method +
|
||||
'", deadline=' +
|
||||
deadlineToString(deadline)
|
||||
);
|
||||
const finalOptions: CallStreamOptions = {
|
||||
deadline: deadline,
|
||||
flags: propagateFlags ?? Propagate.DEFAULTS,
|
||||
host: host ?? this.defaultAuthority,
|
||||
parentCall: parentCall,
|
||||
};
|
||||
|
||||
const call = new ResolvingCall(
|
||||
this,
|
||||
method,
|
||||
finalOptions,
|
||||
this.filterStackFactory.clone(),
|
||||
this.credentials._getCallCredentials(),
|
||||
callNumber
|
||||
);
|
||||
|
||||
this.onCallStart();
|
||||
call.addStatusWatcher(status => {
|
||||
this.onCallEnd(status);
|
||||
});
|
||||
return call;
|
||||
}
|
||||
|
||||
close() {
|
||||
this.resolvingLoadBalancer.destroy();
|
||||
this.updateState(ConnectivityState.SHUTDOWN);
|
||||
clearInterval(this.callRefTimer);
|
||||
if (this.idleTimer) {
|
||||
clearTimeout(this.idleTimer);
|
||||
}
|
||||
if (this.channelzEnabled) {
|
||||
unregisterChannelzRef(this.channelzRef);
|
||||
}
|
||||
|
||||
this.subchannelPool.unrefUnusedSubchannels();
|
||||
}
|
||||
|
||||
getTarget() {
|
||||
return uriToString(this.target);
|
||||
}
|
||||
|
||||
getConnectivityState(tryToConnect: boolean) {
|
||||
const connectivityState = this.connectivityState;
|
||||
if (tryToConnect) {
|
||||
this.resolvingLoadBalancer.exitIdle();
|
||||
this.lastActivityTimestamp = new Date();
|
||||
this.maybeStartIdleTimer();
|
||||
}
|
||||
return connectivityState;
|
||||
}
|
||||
|
||||
watchConnectivityState(
|
||||
currentState: ConnectivityState,
|
||||
deadline: Date | number,
|
||||
callback: (error?: Error) => void
|
||||
): void {
|
||||
if (this.connectivityState === ConnectivityState.SHUTDOWN) {
|
||||
throw new Error('Channel has been shut down');
|
||||
}
|
||||
let timer = null;
|
||||
if (deadline !== Infinity) {
|
||||
const deadlineDate: Date =
|
||||
deadline instanceof Date ? deadline : new Date(deadline);
|
||||
const now = new Date();
|
||||
if (deadline === -Infinity || deadlineDate <= now) {
|
||||
process.nextTick(
|
||||
callback,
|
||||
new Error('Deadline passed without connectivity state change')
|
||||
);
|
||||
return;
|
||||
}
|
||||
timer = setTimeout(() => {
|
||||
this.removeConnectivityStateWatcher(watcherObject);
|
||||
callback(
|
||||
new Error('Deadline passed without connectivity state change')
|
||||
);
|
||||
}, deadlineDate.getTime() - now.getTime());
|
||||
}
|
||||
const watcherObject = {
|
||||
currentState,
|
||||
callback,
|
||||
timer,
|
||||
};
|
||||
this.connectivityStateWatchers.push(watcherObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the channelz reference object for this channel. The returned value is
|
||||
* garbage if channelz is disabled for this channel.
|
||||
* @returns
|
||||
*/
|
||||
getChannelzRef() {
|
||||
return this.channelzRef;
|
||||
}
|
||||
|
||||
createCall(
|
||||
method: string,
|
||||
deadline: Deadline,
|
||||
host: string | null | undefined,
|
||||
parentCall: ServerSurfaceCall | null,
|
||||
propagateFlags: number | null | undefined
|
||||
): Call {
|
||||
if (typeof method !== 'string') {
|
||||
throw new TypeError('Channel#createCall: method must be a string');
|
||||
}
|
||||
if (!(typeof deadline === 'number' || deadline instanceof Date)) {
|
||||
throw new TypeError(
|
||||
'Channel#createCall: deadline must be a number or Date'
|
||||
);
|
||||
}
|
||||
if (this.connectivityState === ConnectivityState.SHUTDOWN) {
|
||||
throw new Error('Channel has been shut down');
|
||||
}
|
||||
return this.createResolvingCall(
|
||||
method,
|
||||
deadline,
|
||||
host,
|
||||
parentCall,
|
||||
propagateFlags
|
||||
);
|
||||
}
|
||||
}
|
||||
169
node_modules/@grpc/grpc-js/src/load-balancer-child-handler.ts
generated
vendored
Normal file
169
node_modules/@grpc/grpc-js/src/load-balancer-child-handler.ts
generated
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright 2020 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 {
|
||||
LoadBalancer,
|
||||
ChannelControlHelper,
|
||||
LoadBalancingConfig,
|
||||
createLoadBalancer,
|
||||
} from './load-balancer';
|
||||
import { SubchannelAddress } from './subchannel-address';
|
||||
import { ChannelOptions } from './channel-options';
|
||||
import { ConnectivityState } from './connectivity-state';
|
||||
import { Picker } from './picker';
|
||||
import { ChannelRef, SubchannelRef } from './channelz';
|
||||
import { SubchannelInterface } from './subchannel-interface';
|
||||
|
||||
const TYPE_NAME = 'child_load_balancer_helper';
|
||||
|
||||
export class ChildLoadBalancerHandler implements LoadBalancer {
|
||||
private currentChild: LoadBalancer | null = null;
|
||||
private pendingChild: LoadBalancer | null = null;
|
||||
private latestConfig: LoadBalancingConfig | null = null;
|
||||
|
||||
private ChildPolicyHelper = class {
|
||||
private child: LoadBalancer | null = null;
|
||||
constructor(private parent: ChildLoadBalancerHandler) {}
|
||||
createSubchannel(
|
||||
subchannelAddress: SubchannelAddress,
|
||||
subchannelArgs: ChannelOptions
|
||||
): SubchannelInterface {
|
||||
return this.parent.channelControlHelper.createSubchannel(
|
||||
subchannelAddress,
|
||||
subchannelArgs
|
||||
);
|
||||
}
|
||||
updateState(connectivityState: ConnectivityState, picker: Picker): void {
|
||||
if (this.calledByPendingChild()) {
|
||||
if (connectivityState === ConnectivityState.CONNECTING) {
|
||||
return;
|
||||
}
|
||||
this.parent.currentChild?.destroy();
|
||||
this.parent.currentChild = this.parent.pendingChild;
|
||||
this.parent.pendingChild = null;
|
||||
} else if (!this.calledByCurrentChild()) {
|
||||
return;
|
||||
}
|
||||
this.parent.channelControlHelper.updateState(connectivityState, picker);
|
||||
}
|
||||
requestReresolution(): void {
|
||||
const latestChild = this.parent.pendingChild ?? this.parent.currentChild;
|
||||
if (this.child === latestChild) {
|
||||
this.parent.channelControlHelper.requestReresolution();
|
||||
}
|
||||
}
|
||||
setChild(newChild: LoadBalancer) {
|
||||
this.child = newChild;
|
||||
}
|
||||
addChannelzChild(child: ChannelRef | SubchannelRef) {
|
||||
this.parent.channelControlHelper.addChannelzChild(child);
|
||||
}
|
||||
removeChannelzChild(child: ChannelRef | SubchannelRef) {
|
||||
this.parent.channelControlHelper.removeChannelzChild(child);
|
||||
}
|
||||
|
||||
private calledByPendingChild(): boolean {
|
||||
return this.child === this.parent.pendingChild;
|
||||
}
|
||||
private calledByCurrentChild(): boolean {
|
||||
return this.child === this.parent.currentChild;
|
||||
}
|
||||
};
|
||||
|
||||
constructor(private readonly channelControlHelper: ChannelControlHelper) {}
|
||||
|
||||
protected configUpdateRequiresNewPolicyInstance(
|
||||
oldConfig: LoadBalancingConfig,
|
||||
newConfig: LoadBalancingConfig
|
||||
): boolean {
|
||||
return oldConfig.getLoadBalancerName() !== newConfig.getLoadBalancerName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prerequisites: lbConfig !== null and lbConfig.name is registered
|
||||
* @param addressList
|
||||
* @param lbConfig
|
||||
* @param attributes
|
||||
*/
|
||||
updateAddressList(
|
||||
addressList: SubchannelAddress[],
|
||||
lbConfig: LoadBalancingConfig,
|
||||
attributes: { [key: string]: unknown }
|
||||
): void {
|
||||
let childToUpdate: LoadBalancer;
|
||||
if (
|
||||
this.currentChild === null ||
|
||||
this.latestConfig === null ||
|
||||
this.configUpdateRequiresNewPolicyInstance(this.latestConfig, lbConfig)
|
||||
) {
|
||||
const newHelper = new this.ChildPolicyHelper(this);
|
||||
const newChild = createLoadBalancer(lbConfig, newHelper)!;
|
||||
newHelper.setChild(newChild);
|
||||
if (this.currentChild === null) {
|
||||
this.currentChild = newChild;
|
||||
childToUpdate = this.currentChild;
|
||||
} else {
|
||||
if (this.pendingChild) {
|
||||
this.pendingChild.destroy();
|
||||
}
|
||||
this.pendingChild = newChild;
|
||||
childToUpdate = this.pendingChild;
|
||||
}
|
||||
} else {
|
||||
if (this.pendingChild === null) {
|
||||
childToUpdate = this.currentChild;
|
||||
} else {
|
||||
childToUpdate = this.pendingChild;
|
||||
}
|
||||
}
|
||||
this.latestConfig = lbConfig;
|
||||
childToUpdate.updateAddressList(addressList, lbConfig, attributes);
|
||||
}
|
||||
exitIdle(): void {
|
||||
if (this.currentChild) {
|
||||
this.currentChild.exitIdle();
|
||||
if (this.pendingChild) {
|
||||
this.pendingChild.exitIdle();
|
||||
}
|
||||
}
|
||||
}
|
||||
resetBackoff(): void {
|
||||
if (this.currentChild) {
|
||||
this.currentChild.resetBackoff();
|
||||
if (this.pendingChild) {
|
||||
this.pendingChild.resetBackoff();
|
||||
}
|
||||
}
|
||||
}
|
||||
destroy(): void {
|
||||
/* Note: state updates are only propagated from the child balancer if that
|
||||
* object is equal to this.currentChild or this.pendingChild. Since this
|
||||
* function sets both of those to null, no further state updates will
|
||||
* occur after this function returns. */
|
||||
if (this.currentChild) {
|
||||
this.currentChild.destroy();
|
||||
this.currentChild = null;
|
||||
}
|
||||
if (this.pendingChild) {
|
||||
this.pendingChild.destroy();
|
||||
this.pendingChild = null;
|
||||
}
|
||||
}
|
||||
getTypeName(): string {
|
||||
return TYPE_NAME;
|
||||
}
|
||||
}
|
||||
882
node_modules/@grpc/grpc-js/src/load-balancer-outlier-detection.ts
generated
vendored
Normal file
882
node_modules/@grpc/grpc-js/src/load-balancer-outlier-detection.ts
generated
vendored
Normal file
@@ -0,0 +1,882 @@
|
||||
/*
|
||||
* Copyright 2022 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 { ChannelOptions } from './channel-options';
|
||||
import { ConnectivityState } from './connectivity-state';
|
||||
import { LogVerbosity, Status } from './constants';
|
||||
import { durationToMs, isDuration, msToDuration } from './duration';
|
||||
import {
|
||||
ChannelControlHelper,
|
||||
createChildChannelControlHelper,
|
||||
registerLoadBalancerType,
|
||||
} from './experimental';
|
||||
import {
|
||||
getFirstUsableConfig,
|
||||
LoadBalancer,
|
||||
LoadBalancingConfig,
|
||||
validateLoadBalancingConfig,
|
||||
} from './load-balancer';
|
||||
import { ChildLoadBalancerHandler } from './load-balancer-child-handler';
|
||||
import { PickArgs, Picker, PickResult, PickResultType } from './picker';
|
||||
import {
|
||||
SubchannelAddress,
|
||||
subchannelAddressToString,
|
||||
} from './subchannel-address';
|
||||
import {
|
||||
BaseSubchannelWrapper,
|
||||
ConnectivityStateListener,
|
||||
SubchannelInterface,
|
||||
} from './subchannel-interface';
|
||||
import * as logging from './logging';
|
||||
|
||||
const TRACER_NAME = 'outlier_detection';
|
||||
|
||||
function trace(text: string): void {
|
||||
logging.trace(LogVerbosity.DEBUG, TRACER_NAME, text);
|
||||
}
|
||||
|
||||
const TYPE_NAME = 'outlier_detection';
|
||||
|
||||
const OUTLIER_DETECTION_ENABLED =
|
||||
(process.env.GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION ?? 'true') === 'true';
|
||||
|
||||
export interface SuccessRateEjectionConfig {
|
||||
readonly stdev_factor: number;
|
||||
readonly enforcement_percentage: number;
|
||||
readonly minimum_hosts: number;
|
||||
readonly request_volume: number;
|
||||
}
|
||||
|
||||
export interface FailurePercentageEjectionConfig {
|
||||
readonly threshold: number;
|
||||
readonly enforcement_percentage: number;
|
||||
readonly minimum_hosts: number;
|
||||
readonly request_volume: number;
|
||||
}
|
||||
|
||||
const defaultSuccessRateEjectionConfig: SuccessRateEjectionConfig = {
|
||||
stdev_factor: 1900,
|
||||
enforcement_percentage: 100,
|
||||
minimum_hosts: 5,
|
||||
request_volume: 100,
|
||||
};
|
||||
|
||||
const defaultFailurePercentageEjectionConfig: FailurePercentageEjectionConfig =
|
||||
{
|
||||
threshold: 85,
|
||||
enforcement_percentage: 100,
|
||||
minimum_hosts: 5,
|
||||
request_volume: 50,
|
||||
};
|
||||
|
||||
type TypeofValues =
|
||||
| 'object'
|
||||
| 'boolean'
|
||||
| 'function'
|
||||
| 'number'
|
||||
| 'string'
|
||||
| 'undefined';
|
||||
|
||||
function validateFieldType(
|
||||
obj: any,
|
||||
fieldName: string,
|
||||
expectedType: TypeofValues,
|
||||
objectName?: string
|
||||
) {
|
||||
if (fieldName in obj && typeof obj[fieldName] !== expectedType) {
|
||||
const fullFieldName = objectName ? `${objectName}.${fieldName}` : fieldName;
|
||||
throw new Error(
|
||||
`outlier detection config ${fullFieldName} parse error: expected ${expectedType}, got ${typeof obj[
|
||||
fieldName
|
||||
]}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function validatePositiveDuration(
|
||||
obj: any,
|
||||
fieldName: string,
|
||||
objectName?: string
|
||||
) {
|
||||
const fullFieldName = objectName ? `${objectName}.${fieldName}` : fieldName;
|
||||
if (fieldName in obj) {
|
||||
if (!isDuration(obj[fieldName])) {
|
||||
throw new Error(
|
||||
`outlier detection config ${fullFieldName} parse error: expected Duration, got ${typeof obj[
|
||||
fieldName
|
||||
]}`
|
||||
);
|
||||
}
|
||||
if (
|
||||
!(
|
||||
obj[fieldName].seconds >= 0 &&
|
||||
obj[fieldName].seconds <= 315_576_000_000 &&
|
||||
obj[fieldName].nanos >= 0 &&
|
||||
obj[fieldName].nanos <= 999_999_999
|
||||
)
|
||||
) {
|
||||
throw new Error(
|
||||
`outlier detection config ${fullFieldName} parse error: values out of range for non-negative Duaration`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validatePercentage(obj: any, fieldName: string, objectName?: string) {
|
||||
const fullFieldName = objectName ? `${objectName}.${fieldName}` : fieldName;
|
||||
validateFieldType(obj, fieldName, 'number', objectName);
|
||||
if (fieldName in obj && !(obj[fieldName] >= 0 && obj[fieldName] <= 100)) {
|
||||
throw new Error(
|
||||
`outlier detection config ${fullFieldName} parse error: value out of range for percentage (0-100)`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class OutlierDetectionLoadBalancingConfig
|
||||
implements LoadBalancingConfig
|
||||
{
|
||||
private readonly intervalMs: number;
|
||||
private readonly baseEjectionTimeMs: number;
|
||||
private readonly maxEjectionTimeMs: number;
|
||||
private readonly maxEjectionPercent: number;
|
||||
private readonly successRateEjection: SuccessRateEjectionConfig | null;
|
||||
private readonly failurePercentageEjection: FailurePercentageEjectionConfig | null;
|
||||
|
||||
constructor(
|
||||
intervalMs: number | null,
|
||||
baseEjectionTimeMs: number | null,
|
||||
maxEjectionTimeMs: number | null,
|
||||
maxEjectionPercent: number | null,
|
||||
successRateEjection: Partial<SuccessRateEjectionConfig> | null,
|
||||
failurePercentageEjection: Partial<FailurePercentageEjectionConfig> | null,
|
||||
private readonly childPolicy: LoadBalancingConfig[]
|
||||
) {
|
||||
if (
|
||||
childPolicy.length > 0 &&
|
||||
childPolicy[0].getLoadBalancerName() === 'pick_first'
|
||||
) {
|
||||
throw new Error(
|
||||
'outlier_detection LB policy cannot have a pick_first child policy'
|
||||
);
|
||||
}
|
||||
this.intervalMs = intervalMs ?? 10_000;
|
||||
this.baseEjectionTimeMs = baseEjectionTimeMs ?? 30_000;
|
||||
this.maxEjectionTimeMs = maxEjectionTimeMs ?? 300_000;
|
||||
this.maxEjectionPercent = maxEjectionPercent ?? 10;
|
||||
this.successRateEjection = successRateEjection
|
||||
? { ...defaultSuccessRateEjectionConfig, ...successRateEjection }
|
||||
: null;
|
||||
this.failurePercentageEjection = failurePercentageEjection
|
||||
? {
|
||||
...defaultFailurePercentageEjectionConfig,
|
||||
...failurePercentageEjection,
|
||||
}
|
||||
: null;
|
||||
}
|
||||
getLoadBalancerName(): string {
|
||||
return TYPE_NAME;
|
||||
}
|
||||
toJsonObject(): object {
|
||||
return {
|
||||
interval: msToDuration(this.intervalMs),
|
||||
base_ejection_time: msToDuration(this.baseEjectionTimeMs),
|
||||
max_ejection_time: msToDuration(this.maxEjectionTimeMs),
|
||||
max_ejection_percent: this.maxEjectionPercent,
|
||||
success_rate_ejection: this.successRateEjection,
|
||||
failure_percentage_ejection: this.failurePercentageEjection,
|
||||
child_policy: this.childPolicy.map(policy => policy.toJsonObject()),
|
||||
};
|
||||
}
|
||||
|
||||
getIntervalMs(): number {
|
||||
return this.intervalMs;
|
||||
}
|
||||
getBaseEjectionTimeMs(): number {
|
||||
return this.baseEjectionTimeMs;
|
||||
}
|
||||
getMaxEjectionTimeMs(): number {
|
||||
return this.maxEjectionTimeMs;
|
||||
}
|
||||
getMaxEjectionPercent(): number {
|
||||
return this.maxEjectionPercent;
|
||||
}
|
||||
getSuccessRateEjectionConfig(): SuccessRateEjectionConfig | null {
|
||||
return this.successRateEjection;
|
||||
}
|
||||
getFailurePercentageEjectionConfig(): FailurePercentageEjectionConfig | null {
|
||||
return this.failurePercentageEjection;
|
||||
}
|
||||
getChildPolicy(): LoadBalancingConfig[] {
|
||||
return this.childPolicy;
|
||||
}
|
||||
|
||||
copyWithChildPolicy(
|
||||
childPolicy: LoadBalancingConfig[]
|
||||
): OutlierDetectionLoadBalancingConfig {
|
||||
return new OutlierDetectionLoadBalancingConfig(
|
||||
this.intervalMs,
|
||||
this.baseEjectionTimeMs,
|
||||
this.maxEjectionTimeMs,
|
||||
this.maxEjectionPercent,
|
||||
this.successRateEjection,
|
||||
this.failurePercentageEjection,
|
||||
childPolicy
|
||||
);
|
||||
}
|
||||
|
||||
static createFromJson(obj: any): OutlierDetectionLoadBalancingConfig {
|
||||
validatePositiveDuration(obj, 'interval');
|
||||
validatePositiveDuration(obj, 'base_ejection_time');
|
||||
validatePositiveDuration(obj, 'max_ejection_time');
|
||||
validatePercentage(obj, 'max_ejection_percent');
|
||||
if ('success_rate_ejection' in obj) {
|
||||
if (typeof obj.success_rate_ejection !== 'object') {
|
||||
throw new Error(
|
||||
'outlier detection config success_rate_ejection must be an object'
|
||||
);
|
||||
}
|
||||
validateFieldType(
|
||||
obj.success_rate_ejection,
|
||||
'stdev_factor',
|
||||
'number',
|
||||
'success_rate_ejection'
|
||||
);
|
||||
validatePercentage(
|
||||
obj.success_rate_ejection,
|
||||
'enforcement_percentage',
|
||||
'success_rate_ejection'
|
||||
);
|
||||
validateFieldType(
|
||||
obj.success_rate_ejection,
|
||||
'minimum_hosts',
|
||||
'number',
|
||||
'success_rate_ejection'
|
||||
);
|
||||
validateFieldType(
|
||||
obj.success_rate_ejection,
|
||||
'request_volume',
|
||||
'number',
|
||||
'success_rate_ejection'
|
||||
);
|
||||
}
|
||||
if ('failure_percentage_ejection' in obj) {
|
||||
if (typeof obj.failure_percentage_ejection !== 'object') {
|
||||
throw new Error(
|
||||
'outlier detection config failure_percentage_ejection must be an object'
|
||||
);
|
||||
}
|
||||
validatePercentage(
|
||||
obj.failure_percentage_ejection,
|
||||
'threshold',
|
||||
'failure_percentage_ejection'
|
||||
);
|
||||
validatePercentage(
|
||||
obj.failure_percentage_ejection,
|
||||
'enforcement_percentage',
|
||||
'failure_percentage_ejection'
|
||||
);
|
||||
validateFieldType(
|
||||
obj.failure_percentage_ejection,
|
||||
'minimum_hosts',
|
||||
'number',
|
||||
'failure_percentage_ejection'
|
||||
);
|
||||
validateFieldType(
|
||||
obj.failure_percentage_ejection,
|
||||
'request_volume',
|
||||
'number',
|
||||
'failure_percentage_ejection'
|
||||
);
|
||||
}
|
||||
|
||||
return new OutlierDetectionLoadBalancingConfig(
|
||||
obj.interval ? durationToMs(obj.interval) : null,
|
||||
obj.base_ejection_time ? durationToMs(obj.base_ejection_time) : null,
|
||||
obj.max_ejection_time ? durationToMs(obj.max_ejection_time) : null,
|
||||
obj.max_ejection_percent ?? null,
|
||||
obj.success_rate_ejection,
|
||||
obj.failure_percentage_ejection,
|
||||
obj.child_policy.map(validateLoadBalancingConfig)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OutlierDetectionSubchannelWrapper
|
||||
extends BaseSubchannelWrapper
|
||||
implements SubchannelInterface
|
||||
{
|
||||
private childSubchannelState: ConnectivityState;
|
||||
private stateListeners: ConnectivityStateListener[] = [];
|
||||
private ejected = false;
|
||||
private refCount = 0;
|
||||
constructor(
|
||||
childSubchannel: SubchannelInterface,
|
||||
private mapEntry?: MapEntry
|
||||
) {
|
||||
super(childSubchannel);
|
||||
this.childSubchannelState = childSubchannel.getConnectivityState();
|
||||
childSubchannel.addConnectivityStateListener(
|
||||
(subchannel, previousState, newState, keepaliveTime) => {
|
||||
this.childSubchannelState = newState;
|
||||
if (!this.ejected) {
|
||||
for (const listener of this.stateListeners) {
|
||||
listener(this, previousState, newState, keepaliveTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
getConnectivityState(): ConnectivityState {
|
||||
if (this.ejected) {
|
||||
return ConnectivityState.TRANSIENT_FAILURE;
|
||||
} else {
|
||||
return this.childSubchannelState;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener function to be called whenever the wrapper's
|
||||
* connectivity state changes.
|
||||
* @param listener
|
||||
*/
|
||||
addConnectivityStateListener(listener: ConnectivityStateListener) {
|
||||
this.stateListeners.push(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a listener previously added with `addConnectivityStateListener`
|
||||
* @param listener A reference to a function previously passed to
|
||||
* `addConnectivityStateListener`
|
||||
*/
|
||||
removeConnectivityStateListener(listener: ConnectivityStateListener) {
|
||||
const listenerIndex = this.stateListeners.indexOf(listener);
|
||||
if (listenerIndex > -1) {
|
||||
this.stateListeners.splice(listenerIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
ref() {
|
||||
this.child.ref();
|
||||
this.refCount += 1;
|
||||
}
|
||||
|
||||
unref() {
|
||||
this.child.unref();
|
||||
this.refCount -= 1;
|
||||
if (this.refCount <= 0) {
|
||||
if (this.mapEntry) {
|
||||
const index = this.mapEntry.subchannelWrappers.indexOf(this);
|
||||
if (index >= 0) {
|
||||
this.mapEntry.subchannelWrappers.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eject() {
|
||||
this.ejected = true;
|
||||
for (const listener of this.stateListeners) {
|
||||
listener(
|
||||
this,
|
||||
this.childSubchannelState,
|
||||
ConnectivityState.TRANSIENT_FAILURE,
|
||||
-1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
uneject() {
|
||||
this.ejected = false;
|
||||
for (const listener of this.stateListeners) {
|
||||
listener(
|
||||
this,
|
||||
ConnectivityState.TRANSIENT_FAILURE,
|
||||
this.childSubchannelState,
|
||||
-1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
getMapEntry(): MapEntry | undefined {
|
||||
return this.mapEntry;
|
||||
}
|
||||
|
||||
getWrappedSubchannel(): SubchannelInterface {
|
||||
return this.child;
|
||||
}
|
||||
}
|
||||
|
||||
interface CallCountBucket {
|
||||
success: number;
|
||||
failure: number;
|
||||
}
|
||||
|
||||
function createEmptyBucket(): CallCountBucket {
|
||||
return {
|
||||
success: 0,
|
||||
failure: 0,
|
||||
};
|
||||
}
|
||||
|
||||
class CallCounter {
|
||||
private activeBucket: CallCountBucket = createEmptyBucket();
|
||||
private inactiveBucket: CallCountBucket = createEmptyBucket();
|
||||
addSuccess() {
|
||||
this.activeBucket.success += 1;
|
||||
}
|
||||
addFailure() {
|
||||
this.activeBucket.failure += 1;
|
||||
}
|
||||
switchBuckets() {
|
||||
this.inactiveBucket = this.activeBucket;
|
||||
this.activeBucket = createEmptyBucket();
|
||||
}
|
||||
getLastSuccesses() {
|
||||
return this.inactiveBucket.success;
|
||||
}
|
||||
getLastFailures() {
|
||||
return this.inactiveBucket.failure;
|
||||
}
|
||||
}
|
||||
|
||||
interface MapEntry {
|
||||
counter: CallCounter;
|
||||
currentEjectionTimestamp: Date | null;
|
||||
ejectionTimeMultiplier: number;
|
||||
subchannelWrappers: OutlierDetectionSubchannelWrapper[];
|
||||
}
|
||||
|
||||
class OutlierDetectionPicker implements Picker {
|
||||
constructor(private wrappedPicker: Picker, private countCalls: boolean) {}
|
||||
pick(pickArgs: PickArgs): PickResult {
|
||||
const wrappedPick = this.wrappedPicker.pick(pickArgs);
|
||||
if (wrappedPick.pickResultType === PickResultType.COMPLETE) {
|
||||
const subchannelWrapper =
|
||||
wrappedPick.subchannel as OutlierDetectionSubchannelWrapper;
|
||||
const mapEntry = subchannelWrapper.getMapEntry();
|
||||
if (mapEntry) {
|
||||
let onCallEnded = wrappedPick.onCallEnded;
|
||||
if (this.countCalls) {
|
||||
onCallEnded = statusCode => {
|
||||
if (statusCode === Status.OK) {
|
||||
mapEntry.counter.addSuccess();
|
||||
} else {
|
||||
mapEntry.counter.addFailure();
|
||||
}
|
||||
wrappedPick.onCallEnded?.(statusCode);
|
||||
};
|
||||
}
|
||||
return {
|
||||
...wrappedPick,
|
||||
subchannel: subchannelWrapper.getWrappedSubchannel(),
|
||||
onCallEnded: onCallEnded,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...wrappedPick,
|
||||
subchannel: subchannelWrapper.getWrappedSubchannel(),
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return wrappedPick;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class OutlierDetectionLoadBalancer implements LoadBalancer {
|
||||
private childBalancer: ChildLoadBalancerHandler;
|
||||
private addressMap: Map<string, MapEntry> = new Map<string, MapEntry>();
|
||||
private latestConfig: OutlierDetectionLoadBalancingConfig | null = null;
|
||||
private ejectionTimer: NodeJS.Timeout;
|
||||
private timerStartTime: Date | null = null;
|
||||
|
||||
constructor(channelControlHelper: ChannelControlHelper) {
|
||||
this.childBalancer = new ChildLoadBalancerHandler(
|
||||
createChildChannelControlHelper(channelControlHelper, {
|
||||
createSubchannel: (
|
||||
subchannelAddress: SubchannelAddress,
|
||||
subchannelArgs: ChannelOptions
|
||||
) => {
|
||||
const originalSubchannel = channelControlHelper.createSubchannel(
|
||||
subchannelAddress,
|
||||
subchannelArgs
|
||||
);
|
||||
const mapEntry = this.addressMap.get(
|
||||
subchannelAddressToString(subchannelAddress)
|
||||
);
|
||||
const subchannelWrapper = new OutlierDetectionSubchannelWrapper(
|
||||
originalSubchannel,
|
||||
mapEntry
|
||||
);
|
||||
if (mapEntry?.currentEjectionTimestamp !== null) {
|
||||
// If the address is ejected, propagate that to the new subchannel wrapper
|
||||
subchannelWrapper.eject();
|
||||
}
|
||||
mapEntry?.subchannelWrappers.push(subchannelWrapper);
|
||||
return subchannelWrapper;
|
||||
},
|
||||
updateState: (connectivityState: ConnectivityState, picker: Picker) => {
|
||||
if (connectivityState === ConnectivityState.READY) {
|
||||
channelControlHelper.updateState(
|
||||
connectivityState,
|
||||
new OutlierDetectionPicker(picker, this.isCountingEnabled())
|
||||
);
|
||||
} else {
|
||||
channelControlHelper.updateState(connectivityState, picker);
|
||||
}
|
||||
},
|
||||
})
|
||||
);
|
||||
this.ejectionTimer = setInterval(() => {}, 0);
|
||||
clearInterval(this.ejectionTimer);
|
||||
}
|
||||
|
||||
private isCountingEnabled(): boolean {
|
||||
return (
|
||||
this.latestConfig !== null &&
|
||||
(this.latestConfig.getSuccessRateEjectionConfig() !== null ||
|
||||
this.latestConfig.getFailurePercentageEjectionConfig() !== null)
|
||||
);
|
||||
}
|
||||
|
||||
private getCurrentEjectionPercent() {
|
||||
let ejectionCount = 0;
|
||||
for (const mapEntry of this.addressMap.values()) {
|
||||
if (mapEntry.currentEjectionTimestamp !== null) {
|
||||
ejectionCount += 1;
|
||||
}
|
||||
}
|
||||
return (ejectionCount * 100) / this.addressMap.size;
|
||||
}
|
||||
|
||||
private runSuccessRateCheck(ejectionTimestamp: Date) {
|
||||
if (!this.latestConfig) {
|
||||
return;
|
||||
}
|
||||
const successRateConfig = this.latestConfig.getSuccessRateEjectionConfig();
|
||||
if (!successRateConfig) {
|
||||
return;
|
||||
}
|
||||
trace('Running success rate check');
|
||||
// Step 1
|
||||
const targetRequestVolume = successRateConfig.request_volume;
|
||||
let addresesWithTargetVolume = 0;
|
||||
const successRates: number[] = [];
|
||||
for (const [address, mapEntry] of this.addressMap) {
|
||||
const successes = mapEntry.counter.getLastSuccesses();
|
||||
const failures = mapEntry.counter.getLastFailures();
|
||||
trace(
|
||||
'Stats for ' +
|
||||
address +
|
||||
': successes=' +
|
||||
successes +
|
||||
' failures=' +
|
||||
failures +
|
||||
' targetRequestVolume=' +
|
||||
targetRequestVolume
|
||||
);
|
||||
if (successes + failures >= targetRequestVolume) {
|
||||
addresesWithTargetVolume += 1;
|
||||
successRates.push(successes / (successes + failures));
|
||||
}
|
||||
}
|
||||
trace(
|
||||
'Found ' +
|
||||
addresesWithTargetVolume +
|
||||
' success rate candidates; currentEjectionPercent=' +
|
||||
this.getCurrentEjectionPercent() +
|
||||
' successRates=[' +
|
||||
successRates +
|
||||
']'
|
||||
);
|
||||
if (addresesWithTargetVolume < successRateConfig.minimum_hosts) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2
|
||||
const successRateMean =
|
||||
successRates.reduce((a, b) => a + b) / successRates.length;
|
||||
let successRateDeviationSum = 0;
|
||||
for (const rate of successRates) {
|
||||
const deviation = rate - successRateMean;
|
||||
successRateDeviationSum += deviation * deviation;
|
||||
}
|
||||
const successRateVariance = successRateDeviationSum / successRates.length;
|
||||
const successRateStdev = Math.sqrt(successRateVariance);
|
||||
const ejectionThreshold =
|
||||
successRateMean -
|
||||
successRateStdev * (successRateConfig.stdev_factor / 1000);
|
||||
trace(
|
||||
'stdev=' + successRateStdev + ' ejectionThreshold=' + ejectionThreshold
|
||||
);
|
||||
|
||||
// Step 3
|
||||
for (const [address, mapEntry] of this.addressMap.entries()) {
|
||||
// Step 3.i
|
||||
if (
|
||||
this.getCurrentEjectionPercent() >=
|
||||
this.latestConfig.getMaxEjectionPercent()
|
||||
) {
|
||||
break;
|
||||
}
|
||||
// Step 3.ii
|
||||
const successes = mapEntry.counter.getLastSuccesses();
|
||||
const failures = mapEntry.counter.getLastFailures();
|
||||
if (successes + failures < targetRequestVolume) {
|
||||
continue;
|
||||
}
|
||||
// Step 3.iii
|
||||
const successRate = successes / (successes + failures);
|
||||
trace('Checking candidate ' + address + ' successRate=' + successRate);
|
||||
if (successRate < ejectionThreshold) {
|
||||
const randomNumber = Math.random() * 100;
|
||||
trace(
|
||||
'Candidate ' +
|
||||
address +
|
||||
' randomNumber=' +
|
||||
randomNumber +
|
||||
' enforcement_percentage=' +
|
||||
successRateConfig.enforcement_percentage
|
||||
);
|
||||
if (randomNumber < successRateConfig.enforcement_percentage) {
|
||||
trace('Ejecting candidate ' + address);
|
||||
this.eject(mapEntry, ejectionTimestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private runFailurePercentageCheck(ejectionTimestamp: Date) {
|
||||
if (!this.latestConfig) {
|
||||
return;
|
||||
}
|
||||
const failurePercentageConfig =
|
||||
this.latestConfig.getFailurePercentageEjectionConfig();
|
||||
if (!failurePercentageConfig) {
|
||||
return;
|
||||
}
|
||||
trace(
|
||||
'Running failure percentage check. threshold=' +
|
||||
failurePercentageConfig.threshold +
|
||||
' request volume threshold=' +
|
||||
failurePercentageConfig.request_volume
|
||||
);
|
||||
// Step 1
|
||||
let addressesWithTargetVolume = 0;
|
||||
for (const mapEntry of this.addressMap.values()) {
|
||||
const successes = mapEntry.counter.getLastSuccesses();
|
||||
const failures = mapEntry.counter.getLastFailures();
|
||||
if (successes + failures >= failurePercentageConfig.request_volume) {
|
||||
addressesWithTargetVolume += 1;
|
||||
}
|
||||
}
|
||||
if (addressesWithTargetVolume < failurePercentageConfig.minimum_hosts) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2
|
||||
for (const [address, mapEntry] of this.addressMap.entries()) {
|
||||
// Step 2.i
|
||||
if (
|
||||
this.getCurrentEjectionPercent() >=
|
||||
this.latestConfig.getMaxEjectionPercent()
|
||||
) {
|
||||
break;
|
||||
}
|
||||
// Step 2.ii
|
||||
const successes = mapEntry.counter.getLastSuccesses();
|
||||
const failures = mapEntry.counter.getLastFailures();
|
||||
trace('Candidate successes=' + successes + ' failures=' + failures);
|
||||
if (successes + failures < failurePercentageConfig.request_volume) {
|
||||
continue;
|
||||
}
|
||||
// Step 2.iii
|
||||
const failurePercentage = (failures * 100) / (failures + successes);
|
||||
if (failurePercentage > failurePercentageConfig.threshold) {
|
||||
const randomNumber = Math.random() * 100;
|
||||
trace(
|
||||
'Candidate ' +
|
||||
address +
|
||||
' randomNumber=' +
|
||||
randomNumber +
|
||||
' enforcement_percentage=' +
|
||||
failurePercentageConfig.enforcement_percentage
|
||||
);
|
||||
if (randomNumber < failurePercentageConfig.enforcement_percentage) {
|
||||
trace('Ejecting candidate ' + address);
|
||||
this.eject(mapEntry, ejectionTimestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private eject(mapEntry: MapEntry, ejectionTimestamp: Date) {
|
||||
mapEntry.currentEjectionTimestamp = new Date();
|
||||
mapEntry.ejectionTimeMultiplier += 1;
|
||||
for (const subchannelWrapper of mapEntry.subchannelWrappers) {
|
||||
subchannelWrapper.eject();
|
||||
}
|
||||
}
|
||||
|
||||
private uneject(mapEntry: MapEntry) {
|
||||
mapEntry.currentEjectionTimestamp = null;
|
||||
for (const subchannelWrapper of mapEntry.subchannelWrappers) {
|
||||
subchannelWrapper.uneject();
|
||||
}
|
||||
}
|
||||
|
||||
private switchAllBuckets() {
|
||||
for (const mapEntry of this.addressMap.values()) {
|
||||
mapEntry.counter.switchBuckets();
|
||||
}
|
||||
}
|
||||
|
||||
private startTimer(delayMs: number) {
|
||||
this.ejectionTimer = setTimeout(() => this.runChecks(), delayMs);
|
||||
this.ejectionTimer.unref?.();
|
||||
}
|
||||
|
||||
private runChecks() {
|
||||
const ejectionTimestamp = new Date();
|
||||
trace('Ejection timer running');
|
||||
|
||||
this.switchAllBuckets();
|
||||
|
||||
if (!this.latestConfig) {
|
||||
return;
|
||||
}
|
||||
this.timerStartTime = ejectionTimestamp;
|
||||
this.startTimer(this.latestConfig.getIntervalMs());
|
||||
|
||||
this.runSuccessRateCheck(ejectionTimestamp);
|
||||
this.runFailurePercentageCheck(ejectionTimestamp);
|
||||
|
||||
for (const [address, mapEntry] of this.addressMap.entries()) {
|
||||
if (mapEntry.currentEjectionTimestamp === null) {
|
||||
if (mapEntry.ejectionTimeMultiplier > 0) {
|
||||
mapEntry.ejectionTimeMultiplier -= 1;
|
||||
}
|
||||
} else {
|
||||
const baseEjectionTimeMs = this.latestConfig.getBaseEjectionTimeMs();
|
||||
const maxEjectionTimeMs = this.latestConfig.getMaxEjectionTimeMs();
|
||||
const returnTime = new Date(
|
||||
mapEntry.currentEjectionTimestamp.getTime()
|
||||
);
|
||||
returnTime.setMilliseconds(
|
||||
returnTime.getMilliseconds() +
|
||||
Math.min(
|
||||
baseEjectionTimeMs * mapEntry.ejectionTimeMultiplier,
|
||||
Math.max(baseEjectionTimeMs, maxEjectionTimeMs)
|
||||
)
|
||||
);
|
||||
if (returnTime < new Date()) {
|
||||
trace('Unejecting ' + address);
|
||||
this.uneject(mapEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateAddressList(
|
||||
addressList: SubchannelAddress[],
|
||||
lbConfig: LoadBalancingConfig,
|
||||
attributes: { [key: string]: unknown }
|
||||
): void {
|
||||
if (!(lbConfig instanceof OutlierDetectionLoadBalancingConfig)) {
|
||||
return;
|
||||
}
|
||||
const subchannelAddresses = new Set<string>();
|
||||
for (const address of addressList) {
|
||||
subchannelAddresses.add(subchannelAddressToString(address));
|
||||
}
|
||||
for (const address of subchannelAddresses) {
|
||||
if (!this.addressMap.has(address)) {
|
||||
trace('Adding map entry for ' + address);
|
||||
this.addressMap.set(address, {
|
||||
counter: new CallCounter(),
|
||||
currentEjectionTimestamp: null,
|
||||
ejectionTimeMultiplier: 0,
|
||||
subchannelWrappers: [],
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const key of this.addressMap.keys()) {
|
||||
if (!subchannelAddresses.has(key)) {
|
||||
trace('Removing map entry for ' + key);
|
||||
this.addressMap.delete(key);
|
||||
}
|
||||
}
|
||||
const childPolicy: LoadBalancingConfig = getFirstUsableConfig(
|
||||
lbConfig.getChildPolicy(),
|
||||
true
|
||||
);
|
||||
this.childBalancer.updateAddressList(addressList, childPolicy, attributes);
|
||||
|
||||
if (
|
||||
lbConfig.getSuccessRateEjectionConfig() ||
|
||||
lbConfig.getFailurePercentageEjectionConfig()
|
||||
) {
|
||||
if (this.timerStartTime) {
|
||||
trace('Previous timer existed. Replacing timer');
|
||||
clearTimeout(this.ejectionTimer);
|
||||
const remainingDelay =
|
||||
lbConfig.getIntervalMs() -
|
||||
(new Date().getTime() - this.timerStartTime.getTime());
|
||||
this.startTimer(remainingDelay);
|
||||
} else {
|
||||
trace('Starting new timer');
|
||||
this.timerStartTime = new Date();
|
||||
this.startTimer(lbConfig.getIntervalMs());
|
||||
this.switchAllBuckets();
|
||||
}
|
||||
} else {
|
||||
trace('Counting disabled. Cancelling timer.');
|
||||
this.timerStartTime = null;
|
||||
clearTimeout(this.ejectionTimer);
|
||||
for (const mapEntry of this.addressMap.values()) {
|
||||
this.uneject(mapEntry);
|
||||
mapEntry.ejectionTimeMultiplier = 0;
|
||||
}
|
||||
}
|
||||
|
||||
this.latestConfig = lbConfig;
|
||||
}
|
||||
exitIdle(): void {
|
||||
this.childBalancer.exitIdle();
|
||||
}
|
||||
resetBackoff(): void {
|
||||
this.childBalancer.resetBackoff();
|
||||
}
|
||||
destroy(): void {
|
||||
clearTimeout(this.ejectionTimer);
|
||||
this.childBalancer.destroy();
|
||||
}
|
||||
getTypeName(): string {
|
||||
return TYPE_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
export function setup() {
|
||||
if (OUTLIER_DETECTION_ENABLED) {
|
||||
registerLoadBalancerType(
|
||||
TYPE_NAME,
|
||||
OutlierDetectionLoadBalancer,
|
||||
OutlierDetectionLoadBalancingConfig
|
||||
);
|
||||
}
|
||||
}
|
||||
487
node_modules/@grpc/grpc-js/src/load-balancer-pick-first.ts
generated
vendored
Normal file
487
node_modules/@grpc/grpc-js/src/load-balancer-pick-first.ts
generated
vendored
Normal file
@@ -0,0 +1,487 @@
|
||||
/*
|
||||
* 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 {
|
||||
LoadBalancer,
|
||||
ChannelControlHelper,
|
||||
LoadBalancingConfig,
|
||||
registerDefaultLoadBalancerType,
|
||||
registerLoadBalancerType,
|
||||
} from './load-balancer';
|
||||
import { ConnectivityState } from './connectivity-state';
|
||||
import {
|
||||
QueuePicker,
|
||||
Picker,
|
||||
PickArgs,
|
||||
CompletePickResult,
|
||||
PickResultType,
|
||||
UnavailablePicker,
|
||||
} from './picker';
|
||||
import { SubchannelAddress } from './subchannel-address';
|
||||
import * as logging from './logging';
|
||||
import { LogVerbosity } from './constants';
|
||||
import {
|
||||
SubchannelInterface,
|
||||
ConnectivityStateListener,
|
||||
} from './subchannel-interface';
|
||||
|
||||
const TRACER_NAME = 'pick_first';
|
||||
|
||||
function trace(text: string): void {
|
||||
logging.trace(LogVerbosity.DEBUG, TRACER_NAME, text);
|
||||
}
|
||||
|
||||
const TYPE_NAME = 'pick_first';
|
||||
|
||||
/**
|
||||
* Delay after starting a connection on a subchannel before starting a
|
||||
* connection on the next subchannel in the list, for Happy Eyeballs algorithm.
|
||||
*/
|
||||
const CONNECTION_DELAY_INTERVAL_MS = 250;
|
||||
|
||||
export class PickFirstLoadBalancingConfig implements LoadBalancingConfig {
|
||||
constructor(private readonly shuffleAddressList: boolean) {}
|
||||
|
||||
getLoadBalancerName(): string {
|
||||
return TYPE_NAME;
|
||||
}
|
||||
|
||||
toJsonObject(): object {
|
||||
return {
|
||||
[TYPE_NAME]: {
|
||||
shuffleAddressList: this.shuffleAddressList,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
getShuffleAddressList() {
|
||||
return this.shuffleAddressList;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
static createFromJson(obj: any) {
|
||||
if (
|
||||
'shuffleAddressList' in obj &&
|
||||
!(typeof obj.shuffleAddressList === 'boolean')
|
||||
) {
|
||||
throw new Error(
|
||||
'pick_first config field shuffleAddressList must be a boolean if provided'
|
||||
);
|
||||
}
|
||||
return new PickFirstLoadBalancingConfig(obj.shuffleAddressList === true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Picker for a `PickFirstLoadBalancer` in the READY state. Always returns the
|
||||
* picked subchannel.
|
||||
*/
|
||||
class PickFirstPicker implements Picker {
|
||||
constructor(private subchannel: SubchannelInterface) {}
|
||||
|
||||
pick(pickArgs: PickArgs): CompletePickResult {
|
||||
return {
|
||||
pickResultType: PickResultType.COMPLETE,
|
||||
subchannel: this.subchannel,
|
||||
status: null,
|
||||
onCallStarted: null,
|
||||
onCallEnded: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
interface SubchannelChild {
|
||||
subchannel: SubchannelInterface;
|
||||
hasReportedTransientFailure: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new array with the elements of the input array in a random order
|
||||
* @param list The input array
|
||||
* @returns A shuffled array of the elements of list
|
||||
*/
|
||||
export function shuffled<T>(list: T[]): T[] {
|
||||
const result = list.slice();
|
||||
for (let i = result.length - 1; i > 1; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
const temp = result[i];
|
||||
result[i] = result[j];
|
||||
result[j] = temp;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export class PickFirstLoadBalancer implements LoadBalancer {
|
||||
/**
|
||||
* The list of subchannels this load balancer is currently attempting to
|
||||
* connect to.
|
||||
*/
|
||||
private children: SubchannelChild[] = [];
|
||||
/**
|
||||
* The current connectivity state of the load balancer.
|
||||
*/
|
||||
private currentState: ConnectivityState = ConnectivityState.IDLE;
|
||||
/**
|
||||
* The index within the `subchannels` array of the subchannel with the most
|
||||
* recently started connection attempt.
|
||||
*/
|
||||
private currentSubchannelIndex = 0;
|
||||
/**
|
||||
* The currently picked subchannel used for making calls. Populated if
|
||||
* and only if the load balancer's current state is READY. In that case,
|
||||
* the subchannel's current state is also READY.
|
||||
*/
|
||||
private currentPick: SubchannelInterface | null = null;
|
||||
/**
|
||||
* Listener callback attached to each subchannel in the `subchannels` list
|
||||
* while establishing a connection.
|
||||
*/
|
||||
private subchannelStateListener: ConnectivityStateListener = (
|
||||
subchannel,
|
||||
previousState,
|
||||
newState,
|
||||
keepaliveTime,
|
||||
errorMessage
|
||||
) => {
|
||||
this.onSubchannelStateUpdate(subchannel, previousState, newState, errorMessage);
|
||||
};
|
||||
/**
|
||||
* Timer reference for the timer tracking when to start
|
||||
*/
|
||||
private connectionDelayTimeout: NodeJS.Timeout;
|
||||
|
||||
private triedAllSubchannels = false;
|
||||
|
||||
/**
|
||||
* The LB policy enters sticky TRANSIENT_FAILURE mode when all
|
||||
* subchannels have failed to connect at least once, and it stays in that
|
||||
* mode until a connection attempt is successful. While in sticky TF mode,
|
||||
* the LB policy continuously attempts to connect to all of its subchannels.
|
||||
*/
|
||||
private stickyTransientFailureMode = false;
|
||||
|
||||
/**
|
||||
* Indicates whether we called channelControlHelper.requestReresolution since
|
||||
* the last call to updateAddressList
|
||||
*/
|
||||
private requestedResolutionSinceLastUpdate = false;
|
||||
|
||||
/**
|
||||
* The most recent error reported by any subchannel as it transitioned to
|
||||
* TRANSIENT_FAILURE.
|
||||
*/
|
||||
private lastError: string | null = null;
|
||||
|
||||
private latestAddressList: SubchannelAddress[] | null = null;
|
||||
|
||||
/**
|
||||
* Load balancer that attempts to connect to each backend in the address list
|
||||
* in order, and picks the first one that connects, using it for every
|
||||
* request.
|
||||
* @param channelControlHelper `ChannelControlHelper` instance provided by
|
||||
* this load balancer's owner.
|
||||
*/
|
||||
constructor(private readonly channelControlHelper: ChannelControlHelper) {
|
||||
this.connectionDelayTimeout = setTimeout(() => {}, 0);
|
||||
clearTimeout(this.connectionDelayTimeout);
|
||||
}
|
||||
|
||||
private allChildrenHaveReportedTF(): boolean {
|
||||
return this.children.every(child => child.hasReportedTransientFailure);
|
||||
}
|
||||
|
||||
private calculateAndReportNewState() {
|
||||
if (this.currentPick) {
|
||||
this.updateState(
|
||||
ConnectivityState.READY,
|
||||
new PickFirstPicker(this.currentPick)
|
||||
);
|
||||
} else if (this.children.length === 0) {
|
||||
this.updateState(ConnectivityState.IDLE, new QueuePicker(this));
|
||||
} else {
|
||||
if (this.stickyTransientFailureMode) {
|
||||
this.updateState(
|
||||
ConnectivityState.TRANSIENT_FAILURE,
|
||||
new UnavailablePicker({details: `No connection established. Last error: ${this.lastError}`})
|
||||
);
|
||||
} else {
|
||||
this.updateState(ConnectivityState.CONNECTING, new QueuePicker(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private requestReresolution() {
|
||||
this.requestedResolutionSinceLastUpdate = true;
|
||||
this.channelControlHelper.requestReresolution();
|
||||
}
|
||||
|
||||
private maybeEnterStickyTransientFailureMode() {
|
||||
if (!this.allChildrenHaveReportedTF()) {
|
||||
return;
|
||||
}
|
||||
if (!this.requestedResolutionSinceLastUpdate) {
|
||||
/* Each time we get an update we reset each subchannel's
|
||||
* hasReportedTransientFailure flag, so the next time we get to this
|
||||
* point after that, each subchannel has reported TRANSIENT_FAILURE
|
||||
* at least once since then. That is the trigger for requesting
|
||||
* reresolution, whether or not the LB policy is already in sticky TF
|
||||
* mode. */
|
||||
this.requestReresolution();
|
||||
}
|
||||
if (this.stickyTransientFailureMode) {
|
||||
return;
|
||||
}
|
||||
this.stickyTransientFailureMode = true;
|
||||
for (const { subchannel } of this.children) {
|
||||
subchannel.startConnecting();
|
||||
}
|
||||
this.calculateAndReportNewState();
|
||||
}
|
||||
|
||||
private removeCurrentPick() {
|
||||
if (this.currentPick !== null) {
|
||||
/* Unref can cause a state change, which can cause a change in the value
|
||||
* of this.currentPick, so we hold a local reference to make sure that
|
||||
* does not impact this function. */
|
||||
const currentPick = this.currentPick;
|
||||
this.currentPick = null;
|
||||
currentPick.unref();
|
||||
currentPick.removeConnectivityStateListener(this.subchannelStateListener);
|
||||
this.channelControlHelper.removeChannelzChild(
|
||||
currentPick.getChannelzRef()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private onSubchannelStateUpdate(
|
||||
subchannel: SubchannelInterface,
|
||||
previousState: ConnectivityState,
|
||||
newState: ConnectivityState,
|
||||
errorMessage?: string
|
||||
) {
|
||||
if (this.currentPick?.realSubchannelEquals(subchannel)) {
|
||||
if (newState !== ConnectivityState.READY) {
|
||||
this.removeCurrentPick();
|
||||
this.calculateAndReportNewState();
|
||||
this.requestReresolution();
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (const [index, child] of this.children.entries()) {
|
||||
if (subchannel.realSubchannelEquals(child.subchannel)) {
|
||||
if (newState === ConnectivityState.READY) {
|
||||
this.pickSubchannel(child.subchannel);
|
||||
}
|
||||
if (newState === ConnectivityState.TRANSIENT_FAILURE) {
|
||||
child.hasReportedTransientFailure = true;
|
||||
if (errorMessage) {
|
||||
this.lastError = errorMessage;
|
||||
}
|
||||
this.maybeEnterStickyTransientFailureMode();
|
||||
if (index === this.currentSubchannelIndex) {
|
||||
this.startNextSubchannelConnecting(index + 1);
|
||||
}
|
||||
}
|
||||
child.subchannel.startConnecting();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private startNextSubchannelConnecting(startIndex: number) {
|
||||
clearTimeout(this.connectionDelayTimeout);
|
||||
if (this.triedAllSubchannels) {
|
||||
return;
|
||||
}
|
||||
for (const [index, child] of this.children.entries()) {
|
||||
if (index >= startIndex) {
|
||||
const subchannelState = child.subchannel.getConnectivityState();
|
||||
if (
|
||||
subchannelState === ConnectivityState.IDLE ||
|
||||
subchannelState === ConnectivityState.CONNECTING
|
||||
) {
|
||||
this.startConnecting(index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.triedAllSubchannels = true;
|
||||
this.maybeEnterStickyTransientFailureMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Have a single subchannel in the `subchannels` list start connecting.
|
||||
* @param subchannelIndex The index into the `subchannels` list.
|
||||
*/
|
||||
private startConnecting(subchannelIndex: number) {
|
||||
clearTimeout(this.connectionDelayTimeout);
|
||||
this.currentSubchannelIndex = subchannelIndex;
|
||||
if (
|
||||
this.children[subchannelIndex].subchannel.getConnectivityState() ===
|
||||
ConnectivityState.IDLE
|
||||
) {
|
||||
trace(
|
||||
'Start connecting to subchannel with address ' +
|
||||
this.children[subchannelIndex].subchannel.getAddress()
|
||||
);
|
||||
process.nextTick(() => {
|
||||
this.children[subchannelIndex]?.subchannel.startConnecting();
|
||||
});
|
||||
}
|
||||
this.connectionDelayTimeout = setTimeout(() => {
|
||||
this.startNextSubchannelConnecting(subchannelIndex + 1);
|
||||
}, CONNECTION_DELAY_INTERVAL_MS).unref?.();
|
||||
}
|
||||
|
||||
private pickSubchannel(subchannel: SubchannelInterface) {
|
||||
if (this.currentPick && subchannel.realSubchannelEquals(this.currentPick)) {
|
||||
return;
|
||||
}
|
||||
trace('Pick subchannel with address ' + subchannel.getAddress());
|
||||
this.stickyTransientFailureMode = false;
|
||||
if (this.currentPick !== null) {
|
||||
this.currentPick.unref();
|
||||
this.channelControlHelper.removeChannelzChild(
|
||||
this.currentPick.getChannelzRef()
|
||||
);
|
||||
this.currentPick.removeConnectivityStateListener(
|
||||
this.subchannelStateListener
|
||||
);
|
||||
}
|
||||
this.currentPick = subchannel;
|
||||
subchannel.ref();
|
||||
this.channelControlHelper.addChannelzChild(subchannel.getChannelzRef());
|
||||
this.resetSubchannelList();
|
||||
clearTimeout(this.connectionDelayTimeout);
|
||||
this.calculateAndReportNewState();
|
||||
}
|
||||
|
||||
private updateState(newState: ConnectivityState, picker: Picker) {
|
||||
trace(
|
||||
ConnectivityState[this.currentState] +
|
||||
' -> ' +
|
||||
ConnectivityState[newState]
|
||||
);
|
||||
this.currentState = newState;
|
||||
this.channelControlHelper.updateState(newState, picker);
|
||||
}
|
||||
|
||||
private resetSubchannelList() {
|
||||
for (const child of this.children) {
|
||||
if (!(this.currentPick && child.subchannel.realSubchannelEquals(this.currentPick))) {
|
||||
/* The connectivity state listener is the same whether the subchannel
|
||||
* is in the list of children or it is the currentPick, so if it is in
|
||||
* both, removing it here would cause problems. In particular, that
|
||||
* always happens immediately after the subchannel is picked. */
|
||||
child.subchannel.removeConnectivityStateListener(
|
||||
this.subchannelStateListener
|
||||
);
|
||||
}
|
||||
/* Refs are counted independently for the children list and the
|
||||
* currentPick, so we call unref whether or not the child is the
|
||||
* currentPick. Channelz child references are also refcounted, so
|
||||
* removeChannelzChild can be handled the same way. */
|
||||
child.subchannel.unref();
|
||||
this.channelControlHelper.removeChannelzChild(
|
||||
child.subchannel.getChannelzRef()
|
||||
);
|
||||
}
|
||||
this.currentSubchannelIndex = 0;
|
||||
this.children = [];
|
||||
this.triedAllSubchannels = false;
|
||||
this.requestedResolutionSinceLastUpdate = false;
|
||||
}
|
||||
|
||||
private connectToAddressList(addressList: SubchannelAddress[]) {
|
||||
const newChildrenList = addressList.map(address => ({
|
||||
subchannel: this.channelControlHelper.createSubchannel(address, {}),
|
||||
hasReportedTransientFailure: false,
|
||||
}));
|
||||
/* Ref each subchannel before resetting the list, to ensure that
|
||||
* subchannels shared between the list don't drop to 0 refs during the
|
||||
* transition. */
|
||||
for (const { subchannel } of newChildrenList) {
|
||||
subchannel.ref();
|
||||
this.channelControlHelper.addChannelzChild(subchannel.getChannelzRef());
|
||||
}
|
||||
this.resetSubchannelList();
|
||||
this.children = newChildrenList;
|
||||
for (const { subchannel } of this.children) {
|
||||
subchannel.addConnectivityStateListener(this.subchannelStateListener);
|
||||
if (subchannel.getConnectivityState() === ConnectivityState.READY) {
|
||||
this.pickSubchannel(subchannel);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (const child of this.children) {
|
||||
if (
|
||||
child.subchannel.getConnectivityState() ===
|
||||
ConnectivityState.TRANSIENT_FAILURE
|
||||
) {
|
||||
child.hasReportedTransientFailure = true;
|
||||
}
|
||||
}
|
||||
this.startNextSubchannelConnecting(0);
|
||||
this.calculateAndReportNewState();
|
||||
}
|
||||
|
||||
updateAddressList(
|
||||
addressList: SubchannelAddress[],
|
||||
lbConfig: LoadBalancingConfig
|
||||
): void {
|
||||
if (!(lbConfig instanceof PickFirstLoadBalancingConfig)) {
|
||||
return;
|
||||
}
|
||||
/* Previously, an update would be discarded if it was identical to the
|
||||
* previous update, to minimize churn. Now the DNS resolver is
|
||||
* rate-limited, so that is less of a concern. */
|
||||
if (lbConfig.getShuffleAddressList()) {
|
||||
addressList = shuffled(addressList);
|
||||
}
|
||||
this.latestAddressList = addressList;
|
||||
this.connectToAddressList(addressList);
|
||||
}
|
||||
|
||||
exitIdle() {
|
||||
if (this.currentState === ConnectivityState.IDLE && this.latestAddressList) {
|
||||
this.connectToAddressList(this.latestAddressList);
|
||||
}
|
||||
}
|
||||
|
||||
resetBackoff() {
|
||||
/* The pick first load balancer does not have a connection backoff, so this
|
||||
* does nothing */
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.resetSubchannelList();
|
||||
this.removeCurrentPick();
|
||||
}
|
||||
|
||||
getTypeName(): string {
|
||||
return TYPE_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
export function setup(): void {
|
||||
registerLoadBalancerType(
|
||||
TYPE_NAME,
|
||||
PickFirstLoadBalancer,
|
||||
PickFirstLoadBalancingConfig
|
||||
);
|
||||
registerDefaultLoadBalancerType(TYPE_NAME);
|
||||
}
|
||||
249
node_modules/@grpc/grpc-js/src/load-balancer-round-robin.ts
generated
vendored
Normal file
249
node_modules/@grpc/grpc-js/src/load-balancer-round-robin.ts
generated
vendored
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* 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 {
|
||||
LoadBalancer,
|
||||
ChannelControlHelper,
|
||||
LoadBalancingConfig,
|
||||
registerLoadBalancerType,
|
||||
} from './load-balancer';
|
||||
import { ConnectivityState } from './connectivity-state';
|
||||
import {
|
||||
QueuePicker,
|
||||
Picker,
|
||||
PickArgs,
|
||||
CompletePickResult,
|
||||
PickResultType,
|
||||
UnavailablePicker,
|
||||
} from './picker';
|
||||
import {
|
||||
SubchannelAddress,
|
||||
subchannelAddressToString,
|
||||
} from './subchannel-address';
|
||||
import * as logging from './logging';
|
||||
import { LogVerbosity } from './constants';
|
||||
import {
|
||||
ConnectivityStateListener,
|
||||
SubchannelInterface,
|
||||
} from './subchannel-interface';
|
||||
|
||||
const TRACER_NAME = 'round_robin';
|
||||
|
||||
function trace(text: string): void {
|
||||
logging.trace(LogVerbosity.DEBUG, TRACER_NAME, text);
|
||||
}
|
||||
|
||||
const TYPE_NAME = 'round_robin';
|
||||
|
||||
class RoundRobinLoadBalancingConfig implements LoadBalancingConfig {
|
||||
getLoadBalancerName(): string {
|
||||
return TYPE_NAME;
|
||||
}
|
||||
|
||||
constructor() {}
|
||||
|
||||
toJsonObject(): object {
|
||||
return {
|
||||
[TYPE_NAME]: {},
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
static createFromJson(obj: any) {
|
||||
return new RoundRobinLoadBalancingConfig();
|
||||
}
|
||||
}
|
||||
|
||||
class RoundRobinPicker implements Picker {
|
||||
constructor(
|
||||
private readonly subchannelList: SubchannelInterface[],
|
||||
private nextIndex = 0
|
||||
) {}
|
||||
|
||||
pick(pickArgs: PickArgs): CompletePickResult {
|
||||
const pickedSubchannel = this.subchannelList[this.nextIndex];
|
||||
this.nextIndex = (this.nextIndex + 1) % this.subchannelList.length;
|
||||
return {
|
||||
pickResultType: PickResultType.COMPLETE,
|
||||
subchannel: pickedSubchannel,
|
||||
status: null,
|
||||
onCallStarted: null,
|
||||
onCallEnded: null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check what the next subchannel returned would be. Used by the load
|
||||
* balancer implementation to preserve this part of the picker state if
|
||||
* possible when a subchannel connects or disconnects.
|
||||
*/
|
||||
peekNextSubchannel(): SubchannelInterface {
|
||||
return this.subchannelList[this.nextIndex];
|
||||
}
|
||||
}
|
||||
|
||||
export class RoundRobinLoadBalancer implements LoadBalancer {
|
||||
private subchannels: SubchannelInterface[] = [];
|
||||
|
||||
private currentState: ConnectivityState = ConnectivityState.IDLE;
|
||||
|
||||
private subchannelStateListener: ConnectivityStateListener;
|
||||
|
||||
private currentReadyPicker: RoundRobinPicker | null = null;
|
||||
|
||||
private lastError: string | null = null;
|
||||
|
||||
constructor(private readonly channelControlHelper: ChannelControlHelper) {
|
||||
this.subchannelStateListener = (
|
||||
subchannel: SubchannelInterface,
|
||||
previousState: ConnectivityState,
|
||||
newState: ConnectivityState,
|
||||
keepaliveTime: number,
|
||||
errorMessage?: string
|
||||
) => {
|
||||
this.calculateAndUpdateState();
|
||||
if (
|
||||
newState === ConnectivityState.TRANSIENT_FAILURE ||
|
||||
newState === ConnectivityState.IDLE
|
||||
) {
|
||||
if (errorMessage) {
|
||||
this.lastError = errorMessage;
|
||||
}
|
||||
this.channelControlHelper.requestReresolution();
|
||||
subchannel.startConnecting();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private countSubchannelsWithState(state: ConnectivityState) {
|
||||
return this.subchannels.filter(
|
||||
subchannel => subchannel.getConnectivityState() === state
|
||||
).length;
|
||||
}
|
||||
|
||||
private calculateAndUpdateState() {
|
||||
if (this.countSubchannelsWithState(ConnectivityState.READY) > 0) {
|
||||
const readySubchannels = this.subchannels.filter(
|
||||
subchannel =>
|
||||
subchannel.getConnectivityState() === ConnectivityState.READY
|
||||
);
|
||||
let index = 0;
|
||||
if (this.currentReadyPicker !== null) {
|
||||
index = readySubchannels.indexOf(
|
||||
this.currentReadyPicker.peekNextSubchannel()
|
||||
);
|
||||
if (index < 0) {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
this.updateState(
|
||||
ConnectivityState.READY,
|
||||
new RoundRobinPicker(readySubchannels, index)
|
||||
);
|
||||
} else if (
|
||||
this.countSubchannelsWithState(ConnectivityState.CONNECTING) > 0
|
||||
) {
|
||||
this.updateState(ConnectivityState.CONNECTING, new QueuePicker(this));
|
||||
} else if (
|
||||
this.countSubchannelsWithState(ConnectivityState.TRANSIENT_FAILURE) > 0
|
||||
) {
|
||||
this.updateState(
|
||||
ConnectivityState.TRANSIENT_FAILURE,
|
||||
new UnavailablePicker({details: `No connection established. Last error: ${this.lastError}`})
|
||||
);
|
||||
} else {
|
||||
this.updateState(ConnectivityState.IDLE, new QueuePicker(this));
|
||||
}
|
||||
}
|
||||
|
||||
private updateState(newState: ConnectivityState, picker: Picker) {
|
||||
trace(
|
||||
ConnectivityState[this.currentState] +
|
||||
' -> ' +
|
||||
ConnectivityState[newState]
|
||||
);
|
||||
if (newState === ConnectivityState.READY) {
|
||||
this.currentReadyPicker = picker as RoundRobinPicker;
|
||||
} else {
|
||||
this.currentReadyPicker = null;
|
||||
}
|
||||
this.currentState = newState;
|
||||
this.channelControlHelper.updateState(newState, picker);
|
||||
}
|
||||
|
||||
private resetSubchannelList() {
|
||||
for (const subchannel of this.subchannels) {
|
||||
subchannel.removeConnectivityStateListener(this.subchannelStateListener);
|
||||
subchannel.unref();
|
||||
this.channelControlHelper.removeChannelzChild(
|
||||
subchannel.getChannelzRef()
|
||||
);
|
||||
}
|
||||
this.subchannels = [];
|
||||
}
|
||||
|
||||
updateAddressList(
|
||||
addressList: SubchannelAddress[],
|
||||
lbConfig: LoadBalancingConfig
|
||||
): void {
|
||||
this.resetSubchannelList();
|
||||
trace(
|
||||
'Connect to address list ' +
|
||||
addressList.map(address => subchannelAddressToString(address))
|
||||
);
|
||||
this.subchannels = addressList.map(address =>
|
||||
this.channelControlHelper.createSubchannel(address, {})
|
||||
);
|
||||
for (const subchannel of this.subchannels) {
|
||||
subchannel.ref();
|
||||
subchannel.addConnectivityStateListener(this.subchannelStateListener);
|
||||
this.channelControlHelper.addChannelzChild(subchannel.getChannelzRef());
|
||||
const subchannelState = subchannel.getConnectivityState();
|
||||
if (
|
||||
subchannelState === ConnectivityState.IDLE ||
|
||||
subchannelState === ConnectivityState.TRANSIENT_FAILURE
|
||||
) {
|
||||
subchannel.startConnecting();
|
||||
}
|
||||
}
|
||||
this.calculateAndUpdateState();
|
||||
}
|
||||
|
||||
exitIdle(): void {
|
||||
for (const subchannel of this.subchannels) {
|
||||
subchannel.startConnecting();
|
||||
}
|
||||
}
|
||||
resetBackoff(): void {
|
||||
/* The pick first load balancer does not have a connection backoff, so this
|
||||
* does nothing */
|
||||
}
|
||||
destroy(): void {
|
||||
this.resetSubchannelList();
|
||||
}
|
||||
getTypeName(): string {
|
||||
return TYPE_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
export function setup() {
|
||||
registerLoadBalancerType(
|
||||
TYPE_NAME,
|
||||
RoundRobinLoadBalancer,
|
||||
RoundRobinLoadBalancingConfig
|
||||
);
|
||||
}
|
||||
230
node_modules/@grpc/grpc-js/src/load-balancer.ts
generated
vendored
Normal file
230
node_modules/@grpc/grpc-js/src/load-balancer.ts
generated
vendored
Normal file
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* 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 { ChannelOptions } from './channel-options';
|
||||
import { SubchannelAddress } from './subchannel-address';
|
||||
import { ConnectivityState } from './connectivity-state';
|
||||
import { Picker } from './picker';
|
||||
import { ChannelRef, SubchannelRef } from './channelz';
|
||||
import { SubchannelInterface } from './subchannel-interface';
|
||||
|
||||
/**
|
||||
* A collection of functions associated with a channel that a load balancer
|
||||
* can call as necessary.
|
||||
*/
|
||||
export interface ChannelControlHelper {
|
||||
/**
|
||||
* Returns a subchannel connected to the specified address.
|
||||
* @param subchannelAddress The address to connect to
|
||||
* @param subchannelArgs Extra channel arguments specified by the load balancer
|
||||
*/
|
||||
createSubchannel(
|
||||
subchannelAddress: SubchannelAddress,
|
||||
subchannelArgs: ChannelOptions
|
||||
): SubchannelInterface;
|
||||
/**
|
||||
* Passes a new subchannel picker up to the channel. This is called if either
|
||||
* the connectivity state changes or if a different picker is needed for any
|
||||
* other reason.
|
||||
* @param connectivityState New connectivity state
|
||||
* @param picker New picker
|
||||
*/
|
||||
updateState(connectivityState: ConnectivityState, picker: Picker): void;
|
||||
/**
|
||||
* Request new data from the resolver.
|
||||
*/
|
||||
requestReresolution(): void;
|
||||
addChannelzChild(child: ChannelRef | SubchannelRef): void;
|
||||
removeChannelzChild(child: ChannelRef | SubchannelRef): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a child ChannelControlHelper that overrides some methods of the
|
||||
* parent while letting others pass through to the parent unmodified. This
|
||||
* allows other code to create these children without needing to know about
|
||||
* all of the methods to be passed through.
|
||||
* @param parent
|
||||
* @param overrides
|
||||
*/
|
||||
export function createChildChannelControlHelper(
|
||||
parent: ChannelControlHelper,
|
||||
overrides: Partial<ChannelControlHelper>
|
||||
): ChannelControlHelper {
|
||||
return {
|
||||
createSubchannel:
|
||||
overrides.createSubchannel?.bind(overrides) ??
|
||||
parent.createSubchannel.bind(parent),
|
||||
updateState:
|
||||
overrides.updateState?.bind(overrides) ?? parent.updateState.bind(parent),
|
||||
requestReresolution:
|
||||
overrides.requestReresolution?.bind(overrides) ??
|
||||
parent.requestReresolution.bind(parent),
|
||||
addChannelzChild:
|
||||
overrides.addChannelzChild?.bind(overrides) ??
|
||||
parent.addChannelzChild.bind(parent),
|
||||
removeChannelzChild:
|
||||
overrides.removeChannelzChild?.bind(overrides) ??
|
||||
parent.removeChannelzChild.bind(parent),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks one or more connected subchannels and determines which subchannel
|
||||
* each request should use.
|
||||
*/
|
||||
export interface LoadBalancer {
|
||||
/**
|
||||
* Gives the load balancer a new list of addresses to start connecting to.
|
||||
* The load balancer will start establishing connections with the new list,
|
||||
* but will continue using any existing connections until the new connections
|
||||
* are established
|
||||
* @param addressList The new list of addresses to connect to
|
||||
* @param lbConfig The load balancing config object from the service config,
|
||||
* if one was provided
|
||||
*/
|
||||
updateAddressList(
|
||||
addressList: SubchannelAddress[],
|
||||
lbConfig: LoadBalancingConfig,
|
||||
attributes: { [key: string]: unknown }
|
||||
): void;
|
||||
/**
|
||||
* If the load balancer is currently in the IDLE state, start connecting.
|
||||
*/
|
||||
exitIdle(): void;
|
||||
/**
|
||||
* If the load balancer is currently in the CONNECTING or TRANSIENT_FAILURE
|
||||
* state, reset the current connection backoff timeout to its base value and
|
||||
* transition to CONNECTING if in TRANSIENT_FAILURE.
|
||||
*/
|
||||
resetBackoff(): void;
|
||||
/**
|
||||
* The load balancer unrefs all of its subchannels and stops calling methods
|
||||
* of its channel control helper.
|
||||
*/
|
||||
destroy(): void;
|
||||
/**
|
||||
* Get the type name for this load balancer type. Must be constant across an
|
||||
* entire load balancer implementation class and must match the name that the
|
||||
* balancer implementation class was registered with.
|
||||
*/
|
||||
getTypeName(): string;
|
||||
}
|
||||
|
||||
export interface LoadBalancerConstructor {
|
||||
new (channelControlHelper: ChannelControlHelper): LoadBalancer;
|
||||
}
|
||||
|
||||
export interface LoadBalancingConfig {
|
||||
getLoadBalancerName(): string;
|
||||
toJsonObject(): object;
|
||||
}
|
||||
|
||||
export interface LoadBalancingConfigConstructor {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
new (...args: any): LoadBalancingConfig;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
createFromJson(obj: any): LoadBalancingConfig;
|
||||
}
|
||||
|
||||
const registeredLoadBalancerTypes: {
|
||||
[name: string]: {
|
||||
LoadBalancer: LoadBalancerConstructor;
|
||||
LoadBalancingConfig: LoadBalancingConfigConstructor;
|
||||
};
|
||||
} = {};
|
||||
|
||||
let defaultLoadBalancerType: string | null = null;
|
||||
|
||||
export function registerLoadBalancerType(
|
||||
typeName: string,
|
||||
loadBalancerType: LoadBalancerConstructor,
|
||||
loadBalancingConfigType: LoadBalancingConfigConstructor
|
||||
) {
|
||||
registeredLoadBalancerTypes[typeName] = {
|
||||
LoadBalancer: loadBalancerType,
|
||||
LoadBalancingConfig: loadBalancingConfigType,
|
||||
};
|
||||
}
|
||||
|
||||
export function registerDefaultLoadBalancerType(typeName: string) {
|
||||
defaultLoadBalancerType = typeName;
|
||||
}
|
||||
|
||||
export function createLoadBalancer(
|
||||
config: LoadBalancingConfig,
|
||||
channelControlHelper: ChannelControlHelper
|
||||
): LoadBalancer | null {
|
||||
const typeName = config.getLoadBalancerName();
|
||||
if (typeName in registeredLoadBalancerTypes) {
|
||||
return new registeredLoadBalancerTypes[typeName].LoadBalancer(
|
||||
channelControlHelper
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function isLoadBalancerNameRegistered(typeName: string): boolean {
|
||||
return typeName in registeredLoadBalancerTypes;
|
||||
}
|
||||
|
||||
export function getFirstUsableConfig(
|
||||
configs: LoadBalancingConfig[],
|
||||
fallbackTodefault?: true
|
||||
): LoadBalancingConfig;
|
||||
export function getFirstUsableConfig(
|
||||
configs: LoadBalancingConfig[],
|
||||
fallbackTodefault = false
|
||||
): LoadBalancingConfig | null {
|
||||
for (const config of configs) {
|
||||
if (config.getLoadBalancerName() in registeredLoadBalancerTypes) {
|
||||
return config;
|
||||
}
|
||||
}
|
||||
if (fallbackTodefault) {
|
||||
if (defaultLoadBalancerType) {
|
||||
return new registeredLoadBalancerTypes[
|
||||
defaultLoadBalancerType
|
||||
]!.LoadBalancingConfig();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function validateLoadBalancingConfig(obj: any): LoadBalancingConfig {
|
||||
if (!(obj !== null && typeof obj === 'object')) {
|
||||
throw new Error('Load balancing config must be an object');
|
||||
}
|
||||
const keys = Object.keys(obj);
|
||||
if (keys.length !== 1) {
|
||||
throw new Error(
|
||||
'Provided load balancing config has multiple conflicting entries'
|
||||
);
|
||||
}
|
||||
const typeName = keys[0];
|
||||
if (typeName in registeredLoadBalancerTypes) {
|
||||
return registeredLoadBalancerTypes[
|
||||
typeName
|
||||
].LoadBalancingConfig.createFromJson(obj[typeName]);
|
||||
} else {
|
||||
throw new Error(`Unrecognized load balancing config name ${typeName}`);
|
||||
}
|
||||
}
|
||||
351
node_modules/@grpc/grpc-js/src/load-balancing-call.ts
generated
vendored
Normal file
351
node_modules/@grpc/grpc-js/src/load-balancing-call.ts
generated
vendored
Normal file
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
* Copyright 2022 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 { CallCredentials } from './call-credentials';
|
||||
import {
|
||||
Call,
|
||||
InterceptingListener,
|
||||
MessageContext,
|
||||
StatusObject,
|
||||
} from './call-interface';
|
||||
import { SubchannelCall } from './subchannel-call';
|
||||
import { ConnectivityState } from './connectivity-state';
|
||||
import { LogVerbosity, Status } from './constants';
|
||||
import { Deadline, getDeadlineTimeoutString } from './deadline';
|
||||
import { InternalChannel } from './internal-channel';
|
||||
import { Metadata } from './metadata';
|
||||
import { PickResultType } from './picker';
|
||||
import { CallConfig } from './resolver';
|
||||
import { splitHostPort } from './uri-parser';
|
||||
import * as logging from './logging';
|
||||
import { restrictControlPlaneStatusCode } from './control-plane-status';
|
||||
import * as http2 from 'http2';
|
||||
|
||||
const TRACER_NAME = 'load_balancing_call';
|
||||
|
||||
export type RpcProgress = 'NOT_STARTED' | 'DROP' | 'REFUSED' | 'PROCESSED';
|
||||
|
||||
export interface StatusObjectWithProgress extends StatusObject {
|
||||
progress: RpcProgress;
|
||||
}
|
||||
|
||||
export interface LoadBalancingCallInterceptingListener
|
||||
extends InterceptingListener {
|
||||
onReceiveStatus(status: StatusObjectWithProgress): void;
|
||||
}
|
||||
|
||||
export class LoadBalancingCall implements Call {
|
||||
private child: SubchannelCall | null = null;
|
||||
private readPending = false;
|
||||
private pendingMessage: { context: MessageContext; message: Buffer } | null =
|
||||
null;
|
||||
private pendingHalfClose = false;
|
||||
private ended = false;
|
||||
private serviceUrl: string;
|
||||
private metadata: Metadata | null = null;
|
||||
private listener: InterceptingListener | null = null;
|
||||
private onCallEnded: ((statusCode: Status) => void) | null = null;
|
||||
constructor(
|
||||
private readonly channel: InternalChannel,
|
||||
private readonly callConfig: CallConfig,
|
||||
private readonly methodName: string,
|
||||
private readonly host: string,
|
||||
private readonly credentials: CallCredentials,
|
||||
private readonly deadline: Deadline,
|
||||
private readonly callNumber: number
|
||||
) {
|
||||
const splitPath: string[] = this.methodName.split('/');
|
||||
let serviceName = '';
|
||||
/* The standard path format is "/{serviceName}/{methodName}", so if we split
|
||||
* by '/', the first item should be empty and the second should be the
|
||||
* service name */
|
||||
if (splitPath.length >= 2) {
|
||||
serviceName = splitPath[1];
|
||||
}
|
||||
const hostname = splitHostPort(this.host)?.host ?? 'localhost';
|
||||
/* Currently, call credentials are only allowed on HTTPS connections, so we
|
||||
* can assume that the scheme is "https" */
|
||||
this.serviceUrl = `https://${hostname}/${serviceName}`;
|
||||
}
|
||||
|
||||
private trace(text: string): void {
|
||||
logging.trace(
|
||||
LogVerbosity.DEBUG,
|
||||
TRACER_NAME,
|
||||
'[' + this.callNumber + '] ' + text
|
||||
);
|
||||
}
|
||||
|
||||
private outputStatus(status: StatusObject, progress: RpcProgress) {
|
||||
if (!this.ended) {
|
||||
this.ended = true;
|
||||
this.trace(
|
||||
'ended with status: code=' +
|
||||
status.code +
|
||||
' details="' +
|
||||
status.details +
|
||||
'"'
|
||||
);
|
||||
const finalStatus = { ...status, progress };
|
||||
this.listener?.onReceiveStatus(finalStatus);
|
||||
this.onCallEnded?.(finalStatus.code);
|
||||
}
|
||||
}
|
||||
|
||||
doPick() {
|
||||
if (this.ended) {
|
||||
return;
|
||||
}
|
||||
if (!this.metadata) {
|
||||
throw new Error('doPick called before start');
|
||||
}
|
||||
this.trace('Pick called');
|
||||
const pickResult = this.channel.doPick(
|
||||
this.metadata,
|
||||
this.callConfig.pickInformation
|
||||
);
|
||||
const subchannelString = pickResult.subchannel
|
||||
? '(' +
|
||||
pickResult.subchannel.getChannelzRef().id +
|
||||
') ' +
|
||||
pickResult.subchannel.getAddress()
|
||||
: '' + pickResult.subchannel;
|
||||
this.trace(
|
||||
'Pick result: ' +
|
||||
PickResultType[pickResult.pickResultType] +
|
||||
' subchannel: ' +
|
||||
subchannelString +
|
||||
' status: ' +
|
||||
pickResult.status?.code +
|
||||
' ' +
|
||||
pickResult.status?.details
|
||||
);
|
||||
switch (pickResult.pickResultType) {
|
||||
case PickResultType.COMPLETE:
|
||||
this.credentials
|
||||
.generateMetadata({ service_url: this.serviceUrl })
|
||||
.then(
|
||||
credsMetadata => {
|
||||
/* If this call was cancelled (e.g. by the deadline) before
|
||||
* metadata generation finished, we shouldn't do anything with
|
||||
* it. */
|
||||
if (this.ended) {
|
||||
this.trace('Credentials metadata generation finished after call ended');
|
||||
return;
|
||||
}
|
||||
const finalMetadata = this.metadata!.clone();
|
||||
finalMetadata.merge(credsMetadata);
|
||||
if (finalMetadata.get('authorization').length > 1) {
|
||||
this.outputStatus(
|
||||
{
|
||||
code: Status.INTERNAL,
|
||||
details:
|
||||
'"authorization" metadata cannot have multiple values',
|
||||
metadata: new Metadata(),
|
||||
},
|
||||
'PROCESSED'
|
||||
);
|
||||
}
|
||||
if (
|
||||
pickResult.subchannel!.getConnectivityState() !==
|
||||
ConnectivityState.READY
|
||||
) {
|
||||
this.trace(
|
||||
'Picked subchannel ' +
|
||||
subchannelString +
|
||||
' has state ' +
|
||||
ConnectivityState[
|
||||
pickResult.subchannel!.getConnectivityState()
|
||||
] +
|
||||
' after getting credentials metadata. Retrying pick'
|
||||
);
|
||||
this.doPick();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.deadline !== Infinity) {
|
||||
finalMetadata.set(
|
||||
'grpc-timeout',
|
||||
getDeadlineTimeoutString(this.deadline)
|
||||
);
|
||||
}
|
||||
try {
|
||||
this.child = pickResult
|
||||
.subchannel!.getRealSubchannel()
|
||||
.createCall(finalMetadata, this.host, this.methodName, {
|
||||
onReceiveMetadata: metadata => {
|
||||
this.trace('Received metadata');
|
||||
this.listener!.onReceiveMetadata(metadata);
|
||||
},
|
||||
onReceiveMessage: message => {
|
||||
this.trace('Received message');
|
||||
this.listener!.onReceiveMessage(message);
|
||||
},
|
||||
onReceiveStatus: status => {
|
||||
this.trace('Received status');
|
||||
if (
|
||||
status.rstCode ===
|
||||
http2.constants.NGHTTP2_REFUSED_STREAM
|
||||
) {
|
||||
this.outputStatus(status, 'REFUSED');
|
||||
} else {
|
||||
this.outputStatus(status, 'PROCESSED');
|
||||
}
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
this.trace(
|
||||
'Failed to start call on picked subchannel ' +
|
||||
subchannelString +
|
||||
' with error ' +
|
||||
(error as Error).message
|
||||
);
|
||||
this.outputStatus(
|
||||
{
|
||||
code: Status.INTERNAL,
|
||||
details:
|
||||
'Failed to start HTTP/2 stream with error ' +
|
||||
(error as Error).message,
|
||||
metadata: new Metadata(),
|
||||
},
|
||||
'NOT_STARTED'
|
||||
);
|
||||
return;
|
||||
}
|
||||
this.callConfig.onCommitted?.();
|
||||
pickResult.onCallStarted?.();
|
||||
this.onCallEnded = pickResult.onCallEnded;
|
||||
this.trace(
|
||||
'Created child call [' + this.child.getCallNumber() + ']'
|
||||
);
|
||||
if (this.readPending) {
|
||||
this.child.startRead();
|
||||
}
|
||||
if (this.pendingMessage) {
|
||||
this.child.sendMessageWithContext(
|
||||
this.pendingMessage.context,
|
||||
this.pendingMessage.message
|
||||
);
|
||||
}
|
||||
if (this.pendingHalfClose) {
|
||||
this.child.halfClose();
|
||||
}
|
||||
},
|
||||
(error: Error & { code: number }) => {
|
||||
// We assume the error code isn't 0 (Status.OK)
|
||||
const { code, details } = restrictControlPlaneStatusCode(
|
||||
typeof error.code === 'number' ? error.code : Status.UNKNOWN,
|
||||
`Getting metadata from plugin failed with error: ${error.message}`
|
||||
);
|
||||
this.outputStatus(
|
||||
{
|
||||
code: code,
|
||||
details: details,
|
||||
metadata: new Metadata(),
|
||||
},
|
||||
'PROCESSED'
|
||||
);
|
||||
}
|
||||
);
|
||||
break;
|
||||
case PickResultType.DROP:
|
||||
const { code, details } = restrictControlPlaneStatusCode(
|
||||
pickResult.status!.code,
|
||||
pickResult.status!.details
|
||||
);
|
||||
setImmediate(() => {
|
||||
this.outputStatus(
|
||||
{ code, details, metadata: pickResult.status!.metadata },
|
||||
'DROP'
|
||||
);
|
||||
});
|
||||
break;
|
||||
case PickResultType.TRANSIENT_FAILURE:
|
||||
if (this.metadata.getOptions().waitForReady) {
|
||||
this.channel.queueCallForPick(this);
|
||||
} else {
|
||||
const { code, details } = restrictControlPlaneStatusCode(
|
||||
pickResult.status!.code,
|
||||
pickResult.status!.details
|
||||
);
|
||||
setImmediate(() => {
|
||||
this.outputStatus(
|
||||
{ code, details, metadata: pickResult.status!.metadata },
|
||||
'PROCESSED'
|
||||
);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case PickResultType.QUEUE:
|
||||
this.channel.queueCallForPick(this);
|
||||
}
|
||||
}
|
||||
|
||||
cancelWithStatus(status: Status, details: string): void {
|
||||
this.trace(
|
||||
'cancelWithStatus code: ' + status + ' details: "' + details + '"'
|
||||
);
|
||||
this.child?.cancelWithStatus(status, details);
|
||||
this.outputStatus(
|
||||
{ code: status, details: details, metadata: new Metadata() },
|
||||
'PROCESSED'
|
||||
);
|
||||
}
|
||||
getPeer(): string {
|
||||
return this.child?.getPeer() ?? this.channel.getTarget();
|
||||
}
|
||||
start(
|
||||
metadata: Metadata,
|
||||
listener: LoadBalancingCallInterceptingListener
|
||||
): void {
|
||||
this.trace('start called');
|
||||
this.listener = listener;
|
||||
this.metadata = metadata;
|
||||
this.doPick();
|
||||
}
|
||||
sendMessageWithContext(context: MessageContext, message: Buffer): void {
|
||||
this.trace('write() called with message of length ' + message.length);
|
||||
if (this.child) {
|
||||
this.child.sendMessageWithContext(context, message);
|
||||
} else {
|
||||
this.pendingMessage = { context, message };
|
||||
}
|
||||
}
|
||||
startRead(): void {
|
||||
this.trace('startRead called');
|
||||
if (this.child) {
|
||||
this.child.startRead();
|
||||
} else {
|
||||
this.readPending = true;
|
||||
}
|
||||
}
|
||||
halfClose(): void {
|
||||
this.trace('halfClose called');
|
||||
if (this.child) {
|
||||
this.child.halfClose();
|
||||
} else {
|
||||
this.pendingHalfClose = true;
|
||||
}
|
||||
}
|
||||
setCredentials(credentials: CallCredentials): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getCallNumber(): number {
|
||||
return this.callNumber;
|
||||
}
|
||||
}
|
||||
123
node_modules/@grpc/grpc-js/src/logging.ts
generated
vendored
Normal file
123
node_modules/@grpc/grpc-js/src/logging.ts
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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 { LogVerbosity } from './constants';
|
||||
import { pid } from 'process';
|
||||
|
||||
const clientVersion = require('../../package.json').version;
|
||||
|
||||
const DEFAULT_LOGGER: Partial<Console> = {
|
||||
error: (message?: any, ...optionalParams: any[]) => {
|
||||
console.error('E ' + message, ...optionalParams);
|
||||
},
|
||||
info: (message?: any, ...optionalParams: any[]) => {
|
||||
console.error('I ' + message, ...optionalParams);
|
||||
},
|
||||
debug: (message?: any, ...optionalParams: any[]) => {
|
||||
console.error('D ' + message, ...optionalParams);
|
||||
},
|
||||
};
|
||||
|
||||
let _logger: Partial<Console> = DEFAULT_LOGGER;
|
||||
let _logVerbosity: LogVerbosity = LogVerbosity.ERROR;
|
||||
|
||||
const verbosityString =
|
||||
process.env.GRPC_NODE_VERBOSITY ?? process.env.GRPC_VERBOSITY ?? '';
|
||||
|
||||
switch (verbosityString.toUpperCase()) {
|
||||
case 'DEBUG':
|
||||
_logVerbosity = LogVerbosity.DEBUG;
|
||||
break;
|
||||
case 'INFO':
|
||||
_logVerbosity = LogVerbosity.INFO;
|
||||
break;
|
||||
case 'ERROR':
|
||||
_logVerbosity = LogVerbosity.ERROR;
|
||||
break;
|
||||
case 'NONE':
|
||||
_logVerbosity = LogVerbosity.NONE;
|
||||
break;
|
||||
default:
|
||||
// Ignore any other values
|
||||
}
|
||||
|
||||
export const getLogger = (): Partial<Console> => {
|
||||
return _logger;
|
||||
};
|
||||
|
||||
export const setLogger = (logger: Partial<Console>): void => {
|
||||
_logger = logger;
|
||||
};
|
||||
|
||||
export const setLoggerVerbosity = (verbosity: LogVerbosity): void => {
|
||||
_logVerbosity = verbosity;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const log = (severity: LogVerbosity, ...args: any[]): void => {
|
||||
let logFunction: typeof DEFAULT_LOGGER.error;
|
||||
if (severity >= _logVerbosity) {
|
||||
switch (severity) {
|
||||
case LogVerbosity.DEBUG:
|
||||
logFunction = _logger.debug;
|
||||
break;
|
||||
case LogVerbosity.INFO:
|
||||
logFunction = _logger.info;
|
||||
break;
|
||||
case LogVerbosity.ERROR:
|
||||
logFunction = _logger.error;
|
||||
break;
|
||||
}
|
||||
/* Fall back to _logger.error when other methods are not available for
|
||||
* compatiblity with older behavior that always logged to _logger.error */
|
||||
if (!logFunction) {
|
||||
logFunction = _logger.error;
|
||||
}
|
||||
if (logFunction) {
|
||||
logFunction.bind(_logger)(...args);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const tracersString =
|
||||
process.env.GRPC_NODE_TRACE ?? process.env.GRPC_TRACE ?? '';
|
||||
const enabledTracers = new Set<string>();
|
||||
const disabledTracers = new Set<string>();
|
||||
for (const tracerName of tracersString.split(',')) {
|
||||
if (tracerName.startsWith('-')) {
|
||||
disabledTracers.add(tracerName.substring(1));
|
||||
} else {
|
||||
enabledTracers.add(tracerName);
|
||||
}
|
||||
}
|
||||
const allEnabled = enabledTracers.has('all');
|
||||
|
||||
export function trace(
|
||||
severity: LogVerbosity,
|
||||
tracer: string,
|
||||
text: string
|
||||
): void {
|
||||
if (isTracerEnabled(tracer)) {
|
||||
log(severity, new Date().toISOString() + ' | v' + clientVersion + ' ' + pid + ' | ' + tracer + ' | ' + text);
|
||||
}
|
||||
}
|
||||
|
||||
export function isTracerEnabled(tracer: string): boolean {
|
||||
return (
|
||||
!disabledTracers.has(tracer) && (allEnabled || enabledTracers.has(tracer))
|
||||
);
|
||||
}
|
||||
238
node_modules/@grpc/grpc-js/src/make-client.ts
generated
vendored
Normal file
238
node_modules/@grpc/grpc-js/src/make-client.ts
generated
vendored
Normal file
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* 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 { ChannelCredentials } from './channel-credentials';
|
||||
import { ChannelOptions } from './channel-options';
|
||||
import { Client } from './client';
|
||||
import { UntypedServiceImplementation } from './server';
|
||||
|
||||
export interface Serialize<T> {
|
||||
(value: T): Buffer;
|
||||
}
|
||||
|
||||
export interface Deserialize<T> {
|
||||
(bytes: Buffer): T;
|
||||
}
|
||||
|
||||
export interface ClientMethodDefinition<RequestType, ResponseType> {
|
||||
path: string;
|
||||
requestStream: boolean;
|
||||
responseStream: boolean;
|
||||
requestSerialize: Serialize<RequestType>;
|
||||
responseDeserialize: Deserialize<ResponseType>;
|
||||
originalName?: string;
|
||||
}
|
||||
|
||||
export interface ServerMethodDefinition<RequestType, ResponseType> {
|
||||
path: string;
|
||||
requestStream: boolean;
|
||||
responseStream: boolean;
|
||||
responseSerialize: Serialize<ResponseType>;
|
||||
requestDeserialize: Deserialize<RequestType>;
|
||||
originalName?: string;
|
||||
}
|
||||
|
||||
export interface MethodDefinition<RequestType, ResponseType>
|
||||
extends ClientMethodDefinition<RequestType, ResponseType>,
|
||||
ServerMethodDefinition<RequestType, ResponseType> {}
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
export type ServiceDefinition<
|
||||
ImplementationType = UntypedServiceImplementation
|
||||
> = {
|
||||
readonly [index in keyof ImplementationType]: MethodDefinition<any, any>;
|
||||
};
|
||||
/* eslint-enable @typescript-eslint/no-explicit-any */
|
||||
|
||||
export interface ProtobufTypeDefinition {
|
||||
format: string;
|
||||
type: object;
|
||||
fileDescriptorProtos: Buffer[];
|
||||
}
|
||||
|
||||
export interface PackageDefinition {
|
||||
[index: string]: ServiceDefinition | ProtobufTypeDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map with short names for each of the requester maker functions. Used in
|
||||
* makeClientConstructor
|
||||
* @private
|
||||
*/
|
||||
const requesterFuncs = {
|
||||
unary: Client.prototype.makeUnaryRequest,
|
||||
server_stream: Client.prototype.makeServerStreamRequest,
|
||||
client_stream: Client.prototype.makeClientStreamRequest,
|
||||
bidi: Client.prototype.makeBidiStreamRequest,
|
||||
};
|
||||
|
||||
export interface ServiceClient extends Client {
|
||||
[methodName: string]: Function;
|
||||
}
|
||||
|
||||
export interface ServiceClientConstructor {
|
||||
new (
|
||||
address: string,
|
||||
credentials: ChannelCredentials,
|
||||
options?: Partial<ChannelOptions>
|
||||
): ServiceClient;
|
||||
service: ServiceDefinition;
|
||||
serviceName: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if given key is included in the blacklisted
|
||||
* keys.
|
||||
* @param key key for check, string.
|
||||
*/
|
||||
function isPrototypePolluted(key: string): boolean {
|
||||
return ['__proto__', 'prototype', 'constructor'].includes(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a constructor for a client with the given methods, as specified in
|
||||
* the methods argument. The resulting class will have an instance method for
|
||||
* each method in the service, which is a partial application of one of the
|
||||
* [Client]{@link grpc.Client} request methods, depending on `requestSerialize`
|
||||
* and `responseSerialize`, with the `method`, `serialize`, and `deserialize`
|
||||
* arguments predefined.
|
||||
* @param methods An object mapping method names to
|
||||
* method attributes
|
||||
* @param serviceName The fully qualified name of the service
|
||||
* @param classOptions An options object.
|
||||
* @return New client constructor, which is a subclass of
|
||||
* {@link grpc.Client}, and has the same arguments as that constructor.
|
||||
*/
|
||||
export function makeClientConstructor(
|
||||
methods: ServiceDefinition,
|
||||
serviceName: string,
|
||||
classOptions?: {}
|
||||
): ServiceClientConstructor {
|
||||
if (!classOptions) {
|
||||
classOptions = {};
|
||||
}
|
||||
|
||||
class ServiceClientImpl extends Client implements ServiceClient {
|
||||
static service: ServiceDefinition;
|
||||
static serviceName: string;
|
||||
[methodName: string]: Function;
|
||||
}
|
||||
|
||||
Object.keys(methods).forEach(name => {
|
||||
if (isPrototypePolluted(name)) {
|
||||
return;
|
||||
}
|
||||
const attrs = methods[name];
|
||||
let methodType: keyof typeof requesterFuncs;
|
||||
// TODO(murgatroid99): Verify that we don't need this anymore
|
||||
if (typeof name === 'string' && name.charAt(0) === '$') {
|
||||
throw new Error('Method names cannot start with $');
|
||||
}
|
||||
if (attrs.requestStream) {
|
||||
if (attrs.responseStream) {
|
||||
methodType = 'bidi';
|
||||
} else {
|
||||
methodType = 'client_stream';
|
||||
}
|
||||
} else {
|
||||
if (attrs.responseStream) {
|
||||
methodType = 'server_stream';
|
||||
} else {
|
||||
methodType = 'unary';
|
||||
}
|
||||
}
|
||||
const serialize = attrs.requestSerialize;
|
||||
const deserialize = attrs.responseDeserialize;
|
||||
const methodFunc = partial(
|
||||
requesterFuncs[methodType],
|
||||
attrs.path,
|
||||
serialize,
|
||||
deserialize
|
||||
);
|
||||
ServiceClientImpl.prototype[name] = methodFunc;
|
||||
// Associate all provided attributes with the method
|
||||
Object.assign(ServiceClientImpl.prototype[name], attrs);
|
||||
if (attrs.originalName && !isPrototypePolluted(attrs.originalName)) {
|
||||
ServiceClientImpl.prototype[attrs.originalName] =
|
||||
ServiceClientImpl.prototype[name];
|
||||
}
|
||||
});
|
||||
|
||||
ServiceClientImpl.service = methods;
|
||||
ServiceClientImpl.serviceName = serviceName;
|
||||
|
||||
return ServiceClientImpl;
|
||||
}
|
||||
|
||||
function partial(
|
||||
fn: Function,
|
||||
path: string,
|
||||
serialize: Function,
|
||||
deserialize: Function
|
||||
): Function {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return function (this: any, ...args: any[]) {
|
||||
return fn.call(this, path, serialize, deserialize, ...args);
|
||||
};
|
||||
}
|
||||
|
||||
export interface GrpcObject {
|
||||
[index: string]:
|
||||
| GrpcObject
|
||||
| ServiceClientConstructor
|
||||
| ProtobufTypeDefinition;
|
||||
}
|
||||
|
||||
function isProtobufTypeDefinition(
|
||||
obj: ServiceDefinition | ProtobufTypeDefinition
|
||||
): obj is ProtobufTypeDefinition {
|
||||
return 'format' in obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a gRPC package definition as a gRPC object hierarchy.
|
||||
* @param packageDef The package definition object.
|
||||
* @return The resulting gRPC object.
|
||||
*/
|
||||
export function loadPackageDefinition(
|
||||
packageDef: PackageDefinition
|
||||
): GrpcObject {
|
||||
const result: GrpcObject = {};
|
||||
for (const serviceFqn in packageDef) {
|
||||
if (Object.prototype.hasOwnProperty.call(packageDef, serviceFqn)) {
|
||||
const service = packageDef[serviceFqn];
|
||||
const nameComponents = serviceFqn.split('.');
|
||||
if (nameComponents.some((comp: string) => isPrototypePolluted(comp))) {
|
||||
continue;
|
||||
}
|
||||
const serviceName = nameComponents[nameComponents.length - 1];
|
||||
let current = result;
|
||||
for (const packageName of nameComponents.slice(0, -1)) {
|
||||
if (!current[packageName]) {
|
||||
current[packageName] = {};
|
||||
}
|
||||
current = current[packageName] as GrpcObject;
|
||||
}
|
||||
if (isProtobufTypeDefinition(service)) {
|
||||
current[serviceName] = service;
|
||||
} else {
|
||||
current[serviceName] = makeClientConstructor(service, serviceName, {});
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
298
node_modules/@grpc/grpc-js/src/metadata.ts
generated
vendored
Normal file
298
node_modules/@grpc/grpc-js/src/metadata.ts
generated
vendored
Normal file
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* 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 * as http2 from 'http2';
|
||||
import { log } from './logging';
|
||||
import { LogVerbosity } from './constants';
|
||||
import { getErrorMessage } from './error';
|
||||
const LEGAL_KEY_REGEX = /^[0-9a-z_.-]+$/;
|
||||
const LEGAL_NON_BINARY_VALUE_REGEX = /^[ -~]*$/;
|
||||
|
||||
export type MetadataValue = string | Buffer;
|
||||
export type MetadataObject = Map<string, MetadataValue[]>;
|
||||
|
||||
function isLegalKey(key: string): boolean {
|
||||
return LEGAL_KEY_REGEX.test(key);
|
||||
}
|
||||
|
||||
function isLegalNonBinaryValue(value: string): boolean {
|
||||
return LEGAL_NON_BINARY_VALUE_REGEX.test(value);
|
||||
}
|
||||
|
||||
function isBinaryKey(key: string): boolean {
|
||||
return key.endsWith('-bin');
|
||||
}
|
||||
|
||||
function isCustomMetadata(key: string): boolean {
|
||||
return !key.startsWith('grpc-');
|
||||
}
|
||||
|
||||
function normalizeKey(key: string): string {
|
||||
return key.toLowerCase();
|
||||
}
|
||||
|
||||
function validate(key: string, value?: MetadataValue): void {
|
||||
if (!isLegalKey(key)) {
|
||||
throw new Error('Metadata key "' + key + '" contains illegal characters');
|
||||
}
|
||||
|
||||
if (value !== null && value !== undefined) {
|
||||
if (isBinaryKey(key)) {
|
||||
if (!Buffer.isBuffer(value)) {
|
||||
throw new Error("keys that end with '-bin' must have Buffer values");
|
||||
}
|
||||
} else {
|
||||
if (Buffer.isBuffer(value)) {
|
||||
throw new Error(
|
||||
"keys that don't end with '-bin' must have String values"
|
||||
);
|
||||
}
|
||||
if (!isLegalNonBinaryValue(value)) {
|
||||
throw new Error(
|
||||
'Metadata string value "' + value + '" contains illegal characters'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface MetadataOptions {
|
||||
/* Signal that the request is idempotent. Defaults to false */
|
||||
idempotentRequest?: boolean;
|
||||
/* Signal that the call should not return UNAVAILABLE before it has
|
||||
* started. Defaults to false. */
|
||||
waitForReady?: boolean;
|
||||
/* Signal that the call is cacheable. GRPC is free to use GET verb.
|
||||
* Defaults to false */
|
||||
cacheableRequest?: boolean;
|
||||
/* Signal that the initial metadata should be corked. Defaults to false. */
|
||||
corked?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* A class for storing metadata. Keys are normalized to lowercase ASCII.
|
||||
*/
|
||||
export class Metadata {
|
||||
protected internalRepr: MetadataObject = new Map<string, MetadataValue[]>();
|
||||
private options: MetadataOptions;
|
||||
|
||||
constructor(options: MetadataOptions = {}) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given value for the given key by replacing any other values
|
||||
* associated with that key. Normalizes the key.
|
||||
* @param key The key to whose value should be set.
|
||||
* @param value The value to set. Must be a buffer if and only
|
||||
* if the normalized key ends with '-bin'.
|
||||
*/
|
||||
set(key: string, value: MetadataValue): void {
|
||||
key = normalizeKey(key);
|
||||
validate(key, value);
|
||||
this.internalRepr.set(key, [value]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given value for the given key by appending to a list of previous
|
||||
* values associated with that key. Normalizes the key.
|
||||
* @param key The key for which a new value should be appended.
|
||||
* @param value The value to add. Must be a buffer if and only
|
||||
* if the normalized key ends with '-bin'.
|
||||
*/
|
||||
add(key: string, value: MetadataValue): void {
|
||||
key = normalizeKey(key);
|
||||
validate(key, value);
|
||||
|
||||
const existingValue: MetadataValue[] | undefined =
|
||||
this.internalRepr.get(key);
|
||||
|
||||
if (existingValue === undefined) {
|
||||
this.internalRepr.set(key, [value]);
|
||||
} else {
|
||||
existingValue.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given key and any associated values. Normalizes the key.
|
||||
* @param key The key whose values should be removed.
|
||||
*/
|
||||
remove(key: string): void {
|
||||
key = normalizeKey(key);
|
||||
// validate(key);
|
||||
this.internalRepr.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of all values associated with the key. Normalizes the key.
|
||||
* @param key The key whose value should be retrieved.
|
||||
* @return A list of values associated with the given key.
|
||||
*/
|
||||
get(key: string): MetadataValue[] {
|
||||
key = normalizeKey(key);
|
||||
// validate(key);
|
||||
return this.internalRepr.get(key) || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a plain object mapping each key to the first value associated with it.
|
||||
* This reflects the most common way that people will want to see metadata.
|
||||
* @return A key/value mapping of the metadata.
|
||||
*/
|
||||
getMap(): { [key: string]: MetadataValue } {
|
||||
const result: { [key: string]: MetadataValue } = {};
|
||||
|
||||
for (const [key, values] of this.internalRepr) {
|
||||
if (values.length > 0) {
|
||||
const v = values[0];
|
||||
result[key] = Buffer.isBuffer(v) ? Buffer.from(v) : v;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones the metadata object.
|
||||
* @return The newly cloned object.
|
||||
*/
|
||||
clone(): Metadata {
|
||||
const newMetadata = new Metadata(this.options);
|
||||
const newInternalRepr = newMetadata.internalRepr;
|
||||
|
||||
for (const [key, value] of this.internalRepr) {
|
||||
const clonedValue: MetadataValue[] = value.map(v => {
|
||||
if (Buffer.isBuffer(v)) {
|
||||
return Buffer.from(v);
|
||||
} else {
|
||||
return v;
|
||||
}
|
||||
});
|
||||
|
||||
newInternalRepr.set(key, clonedValue);
|
||||
}
|
||||
|
||||
return newMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges all key-value pairs from a given Metadata object into this one.
|
||||
* If both this object and the given object have values in the same key,
|
||||
* values from the other Metadata object will be appended to this object's
|
||||
* values.
|
||||
* @param other A Metadata object.
|
||||
*/
|
||||
merge(other: Metadata): void {
|
||||
for (const [key, values] of other.internalRepr) {
|
||||
const mergedValue: MetadataValue[] = (
|
||||
this.internalRepr.get(key) || []
|
||||
).concat(values);
|
||||
|
||||
this.internalRepr.set(key, mergedValue);
|
||||
}
|
||||
}
|
||||
|
||||
setOptions(options: MetadataOptions) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
getOptions(): MetadataOptions {
|
||||
return this.options;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = {};
|
||||
|
||||
for (const [key, values] of this.internalRepr) {
|
||||
// We assume that the user's interaction with this object is limited to
|
||||
// through its public API (i.e. keys and values are already validated).
|
||||
result[key] = values.map(bufToString);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This modifies the behavior of JSON.stringify to show an object
|
||||
* representation of the metadata map.
|
||||
*/
|
||||
toJSON() {
|
||||
const result: { [key: string]: MetadataValue[] } = {};
|
||||
for (const [key, values] of this.internalRepr) {
|
||||
result[key] = values;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new Metadata object based fields in a given IncomingHttpHeaders
|
||||
* object.
|
||||
* @param headers An IncomingHttpHeaders object.
|
||||
*/
|
||||
static fromHttp2Headers(headers: http2.IncomingHttpHeaders): Metadata {
|
||||
const result = new Metadata();
|
||||
for (const key of Object.keys(headers)) {
|
||||
// Reserved headers (beginning with `:`) are not valid keys.
|
||||
if (key.charAt(0) === ':') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const values = headers[key];
|
||||
|
||||
try {
|
||||
if (isBinaryKey(key)) {
|
||||
if (Array.isArray(values)) {
|
||||
values.forEach(value => {
|
||||
result.add(key, Buffer.from(value, 'base64'));
|
||||
});
|
||||
} else if (values !== undefined) {
|
||||
if (isCustomMetadata(key)) {
|
||||
values.split(',').forEach(v => {
|
||||
result.add(key, Buffer.from(v.trim(), 'base64'));
|
||||
});
|
||||
} else {
|
||||
result.add(key, Buffer.from(values, 'base64'));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (Array.isArray(values)) {
|
||||
values.forEach(value => {
|
||||
result.add(key, value);
|
||||
});
|
||||
} else if (values !== undefined) {
|
||||
result.add(key, values);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
const message = `Failed to add metadata entry ${key}: ${values}. ${getErrorMessage(
|
||||
error
|
||||
)}. For more information see https://github.com/grpc/grpc-node/issues/1173`;
|
||||
log(LogVerbosity.ERROR, message);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
const bufToString = (val: string | Buffer): string => {
|
||||
return Buffer.isBuffer(val) ? val.toString('base64') : val;
|
||||
};
|
||||
66
node_modules/@grpc/grpc-js/src/object-stream.ts
generated
vendored
Normal file
66
node_modules/@grpc/grpc-js/src/object-stream.ts
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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 { Readable, Writable } from 'stream';
|
||||
import { EmitterAugmentation1 } from './events';
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
export type WriteCallback = (error: Error | null | undefined) => void;
|
||||
|
||||
export interface IntermediateObjectReadable<T> extends Readable {
|
||||
read(size?: number): any & T;
|
||||
}
|
||||
|
||||
export type ObjectReadable<T> = {
|
||||
read(size?: number): T;
|
||||
} & EmitterAugmentation1<'data', T> &
|
||||
IntermediateObjectReadable<T>;
|
||||
|
||||
export interface IntermediateObjectWritable<T> extends Writable {
|
||||
_write(chunk: any & T, encoding: string, callback: Function): void;
|
||||
write(chunk: any & T, cb?: WriteCallback): boolean;
|
||||
write(chunk: any & T, encoding?: any, cb?: WriteCallback): boolean;
|
||||
setDefaultEncoding(encoding: string): this;
|
||||
end(): ReturnType<Writable['end']> extends Writable ? this : void;
|
||||
end(
|
||||
chunk: any & T,
|
||||
cb?: Function
|
||||
): ReturnType<Writable['end']> extends Writable ? this : void;
|
||||
end(
|
||||
chunk: any & T,
|
||||
encoding?: any,
|
||||
cb?: Function
|
||||
): ReturnType<Writable['end']> extends Writable ? this : void;
|
||||
}
|
||||
|
||||
export interface ObjectWritable<T> extends IntermediateObjectWritable<T> {
|
||||
_write(chunk: T, encoding: string, callback: Function): void;
|
||||
write(chunk: T, cb?: Function): boolean;
|
||||
write(chunk: T, encoding?: any, cb?: Function): boolean;
|
||||
setDefaultEncoding(encoding: string): this;
|
||||
end(): ReturnType<Writable['end']> extends Writable ? this : void;
|
||||
end(
|
||||
chunk: T,
|
||||
cb?: Function
|
||||
): ReturnType<Writable['end']> extends Writable ? this : void;
|
||||
end(
|
||||
chunk: T,
|
||||
encoding?: any,
|
||||
cb?: Function
|
||||
): ReturnType<Writable['end']> extends Writable ? this : void;
|
||||
}
|
||||
146
node_modules/@grpc/grpc-js/src/picker.ts
generated
vendored
Normal file
146
node_modules/@grpc/grpc-js/src/picker.ts
generated
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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 { StatusObject } from './call-interface';
|
||||
import { Metadata } from './metadata';
|
||||
import { Status } from './constants';
|
||||
import { LoadBalancer } from './load-balancer';
|
||||
import { SubchannelInterface } from './subchannel-interface';
|
||||
|
||||
export enum PickResultType {
|
||||
COMPLETE,
|
||||
QUEUE,
|
||||
TRANSIENT_FAILURE,
|
||||
DROP,
|
||||
}
|
||||
|
||||
export interface PickResult {
|
||||
pickResultType: PickResultType;
|
||||
/**
|
||||
* The subchannel to use as the transport for the call. Only meaningful if
|
||||
* `pickResultType` is COMPLETE. If null, indicates that the call should be
|
||||
* dropped.
|
||||
*/
|
||||
subchannel: SubchannelInterface | null;
|
||||
/**
|
||||
* The status object to end the call with. Populated if and only if
|
||||
* `pickResultType` is TRANSIENT_FAILURE.
|
||||
*/
|
||||
status: StatusObject | null;
|
||||
onCallStarted: (() => void) | null;
|
||||
onCallEnded: ((statusCode: Status) => void) | null;
|
||||
}
|
||||
|
||||
export interface CompletePickResult extends PickResult {
|
||||
pickResultType: PickResultType.COMPLETE;
|
||||
subchannel: SubchannelInterface | null;
|
||||
status: null;
|
||||
onCallStarted: (() => void) | null;
|
||||
onCallEnded: ((statusCode: Status) => void) | null;
|
||||
}
|
||||
|
||||
export interface QueuePickResult extends PickResult {
|
||||
pickResultType: PickResultType.QUEUE;
|
||||
subchannel: null;
|
||||
status: null;
|
||||
onCallStarted: null;
|
||||
onCallEnded: null;
|
||||
}
|
||||
|
||||
export interface TransientFailurePickResult extends PickResult {
|
||||
pickResultType: PickResultType.TRANSIENT_FAILURE;
|
||||
subchannel: null;
|
||||
status: StatusObject;
|
||||
onCallStarted: null;
|
||||
onCallEnded: null;
|
||||
}
|
||||
|
||||
export interface DropCallPickResult extends PickResult {
|
||||
pickResultType: PickResultType.DROP;
|
||||
subchannel: null;
|
||||
status: StatusObject;
|
||||
onCallStarted: null;
|
||||
onCallEnded: null;
|
||||
}
|
||||
|
||||
export interface PickArgs {
|
||||
metadata: Metadata;
|
||||
extraPickInfo: { [key: string]: string };
|
||||
}
|
||||
|
||||
/**
|
||||
* A proxy object representing the momentary state of a load balancer. Picks
|
||||
* subchannels or returns other information based on that state. Should be
|
||||
* replaced every time the load balancer changes state.
|
||||
*/
|
||||
export interface Picker {
|
||||
pick(pickArgs: PickArgs): PickResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* A standard picker representing a load balancer in the TRANSIENT_FAILURE
|
||||
* state. Always responds to every pick request with an UNAVAILABLE status.
|
||||
*/
|
||||
export class UnavailablePicker implements Picker {
|
||||
private status: StatusObject;
|
||||
constructor(status?: Partial<StatusObject>) {
|
||||
this.status = {
|
||||
code: Status.UNAVAILABLE,
|
||||
details: 'No connection established',
|
||||
metadata: new Metadata(),
|
||||
...status,
|
||||
};
|
||||
}
|
||||
pick(pickArgs: PickArgs): TransientFailurePickResult {
|
||||
return {
|
||||
pickResultType: PickResultType.TRANSIENT_FAILURE,
|
||||
subchannel: null,
|
||||
status: this.status,
|
||||
onCallStarted: null,
|
||||
onCallEnded: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A standard picker representing a load balancer in the IDLE or CONNECTING
|
||||
* state. Always responds to every pick request with a QUEUE pick result
|
||||
* indicating that the pick should be tried again with the next `Picker`. Also
|
||||
* reports back to the load balancer that a connection should be established
|
||||
* once any pick is attempted.
|
||||
*/
|
||||
export class QueuePicker {
|
||||
private calledExitIdle = false;
|
||||
// Constructed with a load balancer. Calls exitIdle on it the first time pick is called
|
||||
constructor(private loadBalancer: LoadBalancer) {}
|
||||
|
||||
pick(pickArgs: PickArgs): QueuePickResult {
|
||||
if (!this.calledExitIdle) {
|
||||
process.nextTick(() => {
|
||||
this.loadBalancer.exitIdle();
|
||||
});
|
||||
this.calledExitIdle = true;
|
||||
}
|
||||
return {
|
||||
pickResultType: PickResultType.QUEUE,
|
||||
subchannel: null,
|
||||
status: null,
|
||||
onCallStarted: null,
|
||||
onCallEnded: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
412
node_modules/@grpc/grpc-js/src/resolver-dns.ts
generated
vendored
Normal file
412
node_modules/@grpc/grpc-js/src/resolver-dns.ts
generated
vendored
Normal file
@@ -0,0 +1,412 @@
|
||||
/*
|
||||
* 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 {
|
||||
Resolver,
|
||||
ResolverListener,
|
||||
registerResolver,
|
||||
registerDefaultScheme,
|
||||
} from './resolver';
|
||||
import * as dns from 'dns';
|
||||
import * as util from 'util';
|
||||
import { extractAndSelectServiceConfig, ServiceConfig } from './service-config';
|
||||
import { Status } from './constants';
|
||||
import { StatusObject } from './call-interface';
|
||||
import { Metadata } from './metadata';
|
||||
import * as logging from './logging';
|
||||
import { LogVerbosity } from './constants';
|
||||
import { SubchannelAddress, TcpSubchannelAddress } from './subchannel-address';
|
||||
import { GrpcUri, uriToString, splitHostPort } from './uri-parser';
|
||||
import { isIPv6, isIPv4 } from 'net';
|
||||
import { ChannelOptions } from './channel-options';
|
||||
import { BackoffOptions, BackoffTimeout } from './backoff-timeout';
|
||||
|
||||
const TRACER_NAME = 'dns_resolver';
|
||||
|
||||
function trace(text: string): void {
|
||||
logging.trace(LogVerbosity.DEBUG, TRACER_NAME, text);
|
||||
}
|
||||
|
||||
/**
|
||||
* The default TCP port to connect to if not explicitly specified in the target.
|
||||
*/
|
||||
export const DEFAULT_PORT = 443;
|
||||
|
||||
const DEFAULT_MIN_TIME_BETWEEN_RESOLUTIONS_MS = 30_000;
|
||||
|
||||
const resolveTxtPromise = util.promisify(dns.resolveTxt);
|
||||
const dnsLookupPromise = util.promisify(dns.lookup);
|
||||
|
||||
/**
|
||||
* Merge any number of arrays into a single alternating array
|
||||
* @param arrays
|
||||
*/
|
||||
function mergeArrays<T>(...arrays: T[][]): T[] {
|
||||
const result: T[] = [];
|
||||
for (
|
||||
let i = 0;
|
||||
i <
|
||||
Math.max.apply(
|
||||
null,
|
||||
arrays.map(array => array.length)
|
||||
);
|
||||
i++
|
||||
) {
|
||||
for (const array of arrays) {
|
||||
if (i < array.length) {
|
||||
result.push(array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolver implementation that handles DNS names and IP addresses.
|
||||
*/
|
||||
class DnsResolver implements Resolver {
|
||||
private readonly ipResult: SubchannelAddress[] | null;
|
||||
private readonly dnsHostname: string | null;
|
||||
private readonly port: number | null;
|
||||
/**
|
||||
* Minimum time between resolutions, measured as the time between starting
|
||||
* successive resolution requests. Only applies to successful resolutions.
|
||||
* Failures are handled by the backoff timer.
|
||||
*/
|
||||
private readonly minTimeBetweenResolutionsMs: number;
|
||||
private pendingLookupPromise: Promise<dns.LookupAddress[]> | null = null;
|
||||
private pendingTxtPromise: Promise<string[][]> | null = null;
|
||||
private latestLookupResult: TcpSubchannelAddress[] | null = null;
|
||||
private latestServiceConfig: ServiceConfig | null = null;
|
||||
private latestServiceConfigError: StatusObject | null = null;
|
||||
private percentage: number;
|
||||
private defaultResolutionError: StatusObject;
|
||||
private backoff: BackoffTimeout;
|
||||
private continueResolving = false;
|
||||
private nextResolutionTimer: NodeJS.Timeout;
|
||||
private isNextResolutionTimerRunning = false;
|
||||
private isServiceConfigEnabled = true;
|
||||
private returnedIpResult = false;
|
||||
constructor(
|
||||
private target: GrpcUri,
|
||||
private listener: ResolverListener,
|
||||
channelOptions: ChannelOptions
|
||||
) {
|
||||
trace('Resolver constructed for target ' + uriToString(target));
|
||||
const hostPort = splitHostPort(target.path);
|
||||
if (hostPort === null) {
|
||||
this.ipResult = null;
|
||||
this.dnsHostname = null;
|
||||
this.port = null;
|
||||
} else {
|
||||
if (isIPv4(hostPort.host) || isIPv6(hostPort.host)) {
|
||||
this.ipResult = [
|
||||
{
|
||||
host: hostPort.host,
|
||||
port: hostPort.port ?? DEFAULT_PORT,
|
||||
},
|
||||
];
|
||||
this.dnsHostname = null;
|
||||
this.port = null;
|
||||
} else {
|
||||
this.ipResult = null;
|
||||
this.dnsHostname = hostPort.host;
|
||||
this.port = hostPort.port ?? DEFAULT_PORT;
|
||||
}
|
||||
}
|
||||
this.percentage = Math.random() * 100;
|
||||
|
||||
if (channelOptions['grpc.service_config_disable_resolution'] === 1) {
|
||||
this.isServiceConfigEnabled = false;
|
||||
}
|
||||
|
||||
this.defaultResolutionError = {
|
||||
code: Status.UNAVAILABLE,
|
||||
details: `Name resolution failed for target ${uriToString(this.target)}`,
|
||||
metadata: new Metadata(),
|
||||
};
|
||||
|
||||
const backoffOptions: BackoffOptions = {
|
||||
initialDelay: channelOptions['grpc.initial_reconnect_backoff_ms'],
|
||||
maxDelay: channelOptions['grpc.max_reconnect_backoff_ms'],
|
||||
};
|
||||
|
||||
this.backoff = new BackoffTimeout(() => {
|
||||
if (this.continueResolving) {
|
||||
this.startResolutionWithBackoff();
|
||||
}
|
||||
}, backoffOptions);
|
||||
this.backoff.unref();
|
||||
|
||||
this.minTimeBetweenResolutionsMs =
|
||||
channelOptions['grpc.dns_min_time_between_resolutions_ms'] ??
|
||||
DEFAULT_MIN_TIME_BETWEEN_RESOLUTIONS_MS;
|
||||
this.nextResolutionTimer = setTimeout(() => {}, 0);
|
||||
clearTimeout(this.nextResolutionTimer);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the target is an IP address, just provide that address as a result.
|
||||
* Otherwise, initiate A, AAAA, and TXT lookups
|
||||
*/
|
||||
private startResolution() {
|
||||
if (this.ipResult !== null) {
|
||||
if (!this.returnedIpResult) {
|
||||
trace('Returning IP address for target ' + uriToString(this.target));
|
||||
setImmediate(() => {
|
||||
this.listener.onSuccessfulResolution(
|
||||
this.ipResult!,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
{}
|
||||
);
|
||||
});
|
||||
this.returnedIpResult = true;
|
||||
}
|
||||
this.backoff.stop();
|
||||
this.backoff.reset();
|
||||
this.stopNextResolutionTimer();
|
||||
return;
|
||||
}
|
||||
if (this.dnsHostname === null) {
|
||||
trace('Failed to parse DNS address ' + uriToString(this.target));
|
||||
setImmediate(() => {
|
||||
this.listener.onError({
|
||||
code: Status.UNAVAILABLE,
|
||||
details: `Failed to parse DNS address ${uriToString(this.target)}`,
|
||||
metadata: new Metadata(),
|
||||
});
|
||||
});
|
||||
this.stopNextResolutionTimer();
|
||||
} else {
|
||||
if (this.pendingLookupPromise !== null) {
|
||||
return;
|
||||
}
|
||||
trace('Looking up DNS hostname ' + this.dnsHostname);
|
||||
/* We clear out latestLookupResult here to ensure that it contains the
|
||||
* latest result since the last time we started resolving. That way, the
|
||||
* TXT resolution handler can use it, but only if it finishes second. We
|
||||
* don't clear out any previous service config results because it's
|
||||
* better to use a service config that's slightly out of date than to
|
||||
* revert to an effectively blank one. */
|
||||
this.latestLookupResult = null;
|
||||
const hostname: string = this.dnsHostname;
|
||||
/* We lookup both address families here and then split them up later
|
||||
* because when looking up a single family, dns.lookup outputs an error
|
||||
* if the name exists but there are no records for that family, and that
|
||||
* error is indistinguishable from other kinds of errors */
|
||||
this.pendingLookupPromise = dnsLookupPromise(hostname, { all: true });
|
||||
this.pendingLookupPromise.then(
|
||||
addressList => {
|
||||
if (this.pendingLookupPromise === null) {
|
||||
return;
|
||||
}
|
||||
this.pendingLookupPromise = null;
|
||||
this.backoff.reset();
|
||||
this.backoff.stop();
|
||||
const ip4Addresses: dns.LookupAddress[] = addressList.filter(
|
||||
addr => addr.family === 4
|
||||
);
|
||||
const ip6Addresses: dns.LookupAddress[] = addressList.filter(
|
||||
addr => addr.family === 6
|
||||
);
|
||||
this.latestLookupResult = mergeArrays(ip6Addresses, ip4Addresses).map(
|
||||
addr => ({ host: addr.address, port: +this.port! })
|
||||
);
|
||||
const allAddressesString: string =
|
||||
'[' +
|
||||
this.latestLookupResult
|
||||
.map(addr => addr.host + ':' + addr.port)
|
||||
.join(',') +
|
||||
']';
|
||||
trace(
|
||||
'Resolved addresses for target ' +
|
||||
uriToString(this.target) +
|
||||
': ' +
|
||||
allAddressesString
|
||||
);
|
||||
if (this.latestLookupResult.length === 0) {
|
||||
this.listener.onError(this.defaultResolutionError);
|
||||
return;
|
||||
}
|
||||
/* If the TXT lookup has not yet finished, both of the last two
|
||||
* arguments will be null, which is the equivalent of getting an
|
||||
* empty TXT response. When the TXT lookup does finish, its handler
|
||||
* can update the service config by using the same address list */
|
||||
this.listener.onSuccessfulResolution(
|
||||
this.latestLookupResult,
|
||||
this.latestServiceConfig,
|
||||
this.latestServiceConfigError,
|
||||
null,
|
||||
{}
|
||||
);
|
||||
},
|
||||
err => {
|
||||
if (this.pendingLookupPromise === null) {
|
||||
return;
|
||||
}
|
||||
trace(
|
||||
'Resolution error for target ' +
|
||||
uriToString(this.target) +
|
||||
': ' +
|
||||
(err as Error).message
|
||||
);
|
||||
this.pendingLookupPromise = null;
|
||||
this.stopNextResolutionTimer();
|
||||
this.listener.onError(this.defaultResolutionError);
|
||||
}
|
||||
);
|
||||
/* If there already is a still-pending TXT resolution, we can just use
|
||||
* that result when it comes in */
|
||||
if (this.isServiceConfigEnabled && this.pendingTxtPromise === null) {
|
||||
/* We handle the TXT query promise differently than the others because
|
||||
* the name resolution attempt as a whole is a success even if the TXT
|
||||
* lookup fails */
|
||||
this.pendingTxtPromise = resolveTxtPromise(hostname);
|
||||
this.pendingTxtPromise.then(
|
||||
txtRecord => {
|
||||
if (this.pendingTxtPromise === null) {
|
||||
return;
|
||||
}
|
||||
this.pendingTxtPromise = null;
|
||||
try {
|
||||
this.latestServiceConfig = extractAndSelectServiceConfig(
|
||||
txtRecord,
|
||||
this.percentage
|
||||
);
|
||||
} catch (err) {
|
||||
this.latestServiceConfigError = {
|
||||
code: Status.UNAVAILABLE,
|
||||
details: `Parsing service config failed with error ${
|
||||
(err as Error).message
|
||||
}`,
|
||||
metadata: new Metadata(),
|
||||
};
|
||||
}
|
||||
if (this.latestLookupResult !== null) {
|
||||
/* We rely here on the assumption that calling this function with
|
||||
* identical parameters will be essentialy idempotent, and calling
|
||||
* it with the same address list and a different service config
|
||||
* should result in a fast and seamless switchover. */
|
||||
this.listener.onSuccessfulResolution(
|
||||
this.latestLookupResult,
|
||||
this.latestServiceConfig,
|
||||
this.latestServiceConfigError,
|
||||
null,
|
||||
{}
|
||||
);
|
||||
}
|
||||
},
|
||||
err => {
|
||||
/* If TXT lookup fails we should do nothing, which means that we
|
||||
* continue to use the result of the most recent successful lookup,
|
||||
* or the default null config object if there has never been a
|
||||
* successful lookup. We do not set the latestServiceConfigError
|
||||
* here because that is specifically used for response validation
|
||||
* errors. We still need to handle this error so that it does not
|
||||
* bubble up as an unhandled promise rejection. */
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private startNextResolutionTimer() {
|
||||
clearTimeout(this.nextResolutionTimer);
|
||||
this.nextResolutionTimer = setTimeout(() => {
|
||||
this.stopNextResolutionTimer();
|
||||
if (this.continueResolving) {
|
||||
this.startResolutionWithBackoff();
|
||||
}
|
||||
}, this.minTimeBetweenResolutionsMs).unref?.();
|
||||
this.isNextResolutionTimerRunning = true;
|
||||
}
|
||||
|
||||
private stopNextResolutionTimer() {
|
||||
clearTimeout(this.nextResolutionTimer);
|
||||
this.isNextResolutionTimerRunning = false;
|
||||
}
|
||||
|
||||
private startResolutionWithBackoff() {
|
||||
if (this.pendingLookupPromise === null) {
|
||||
this.continueResolving = false;
|
||||
this.backoff.runOnce();
|
||||
this.startNextResolutionTimer();
|
||||
this.startResolution();
|
||||
}
|
||||
}
|
||||
|
||||
updateResolution() {
|
||||
/* If there is a pending lookup, just let it finish. Otherwise, if the
|
||||
* nextResolutionTimer or backoff timer is running, set the
|
||||
* continueResolving flag to resolve when whichever of those timers
|
||||
* fires. Otherwise, start resolving immediately. */
|
||||
if (this.pendingLookupPromise === null) {
|
||||
if (this.isNextResolutionTimerRunning || this.backoff.isRunning()) {
|
||||
if (this.isNextResolutionTimerRunning) {
|
||||
trace('resolution update delayed by "min time between resolutions" rate limit');
|
||||
} else {
|
||||
trace('resolution update delayed by backoff timer until ' + this.backoff.getEndTime().toISOString());
|
||||
}
|
||||
this.continueResolving = true;
|
||||
} else {
|
||||
this.startResolutionWithBackoff();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the resolver to the same state it had when it was created. In-flight
|
||||
* DNS requests cannot be cancelled, but they are discarded and their results
|
||||
* will be ignored.
|
||||
*/
|
||||
destroy() {
|
||||
this.continueResolving = false;
|
||||
this.backoff.reset();
|
||||
this.backoff.stop();
|
||||
this.stopNextResolutionTimer();
|
||||
this.pendingLookupPromise = null;
|
||||
this.pendingTxtPromise = null;
|
||||
this.latestLookupResult = null;
|
||||
this.latestServiceConfig = null;
|
||||
this.latestServiceConfigError = null;
|
||||
this.returnedIpResult = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default authority for the given target. For IP targets, that is
|
||||
* the IP address. For DNS targets, it is the hostname.
|
||||
* @param target
|
||||
*/
|
||||
static getDefaultAuthority(target: GrpcUri): string {
|
||||
return target.path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the DNS resolver class by registering it as the handler for the
|
||||
* "dns:" prefix and as the default resolver.
|
||||
*/
|
||||
export function setup(): void {
|
||||
registerResolver('dns', DnsResolver);
|
||||
registerDefaultScheme('dns');
|
||||
}
|
||||
|
||||
export interface DnsUrl {
|
||||
host: string;
|
||||
port?: string;
|
||||
}
|
||||
120
node_modules/@grpc/grpc-js/src/resolver-ip.ts
generated
vendored
Normal file
120
node_modules/@grpc/grpc-js/src/resolver-ip.ts
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2021 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 { isIPv4, isIPv6 } from 'net';
|
||||
import { StatusObject } from './call-interface';
|
||||
import { ChannelOptions } from './channel-options';
|
||||
import { LogVerbosity, Status } from './constants';
|
||||
import { Metadata } from './metadata';
|
||||
import { registerResolver, Resolver, ResolverListener } from './resolver';
|
||||
import { SubchannelAddress } from './subchannel-address';
|
||||
import { GrpcUri, splitHostPort, uriToString } from './uri-parser';
|
||||
import * as logging from './logging';
|
||||
|
||||
const TRACER_NAME = 'ip_resolver';
|
||||
|
||||
function trace(text: string): void {
|
||||
logging.trace(LogVerbosity.DEBUG, TRACER_NAME, text);
|
||||
}
|
||||
|
||||
const IPV4_SCHEME = 'ipv4';
|
||||
const IPV6_SCHEME = 'ipv6';
|
||||
|
||||
/**
|
||||
* The default TCP port to connect to if not explicitly specified in the target.
|
||||
*/
|
||||
const DEFAULT_PORT = 443;
|
||||
|
||||
class IpResolver implements Resolver {
|
||||
private addresses: SubchannelAddress[] = [];
|
||||
private error: StatusObject | null = null;
|
||||
private hasReturnedResult = false;
|
||||
constructor(
|
||||
target: GrpcUri,
|
||||
private listener: ResolverListener,
|
||||
channelOptions: ChannelOptions
|
||||
) {
|
||||
trace('Resolver constructed for target ' + uriToString(target));
|
||||
const addresses: SubchannelAddress[] = [];
|
||||
if (!(target.scheme === IPV4_SCHEME || target.scheme === IPV6_SCHEME)) {
|
||||
this.error = {
|
||||
code: Status.UNAVAILABLE,
|
||||
details: `Unrecognized scheme ${target.scheme} in IP resolver`,
|
||||
metadata: new Metadata(),
|
||||
};
|
||||
return;
|
||||
}
|
||||
const pathList = target.path.split(',');
|
||||
for (const path of pathList) {
|
||||
const hostPort = splitHostPort(path);
|
||||
if (hostPort === null) {
|
||||
this.error = {
|
||||
code: Status.UNAVAILABLE,
|
||||
details: `Failed to parse ${target.scheme} address ${path}`,
|
||||
metadata: new Metadata(),
|
||||
};
|
||||
return;
|
||||
}
|
||||
if (
|
||||
(target.scheme === IPV4_SCHEME && !isIPv4(hostPort.host)) ||
|
||||
(target.scheme === IPV6_SCHEME && !isIPv6(hostPort.host))
|
||||
) {
|
||||
this.error = {
|
||||
code: Status.UNAVAILABLE,
|
||||
details: `Failed to parse ${target.scheme} address ${path}`,
|
||||
metadata: new Metadata(),
|
||||
};
|
||||
return;
|
||||
}
|
||||
addresses.push({
|
||||
host: hostPort.host,
|
||||
port: hostPort.port ?? DEFAULT_PORT,
|
||||
});
|
||||
}
|
||||
this.addresses = addresses;
|
||||
trace('Parsed ' + target.scheme + ' address list ' + this.addresses);
|
||||
}
|
||||
updateResolution(): void {
|
||||
if (!this.hasReturnedResult) {
|
||||
this.hasReturnedResult = true;
|
||||
process.nextTick(() => {
|
||||
if (this.error) {
|
||||
this.listener.onError(this.error);
|
||||
} else {
|
||||
this.listener.onSuccessfulResolution(
|
||||
this.addresses,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
{}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
destroy(): void {
|
||||
this.hasReturnedResult = false;
|
||||
}
|
||||
|
||||
static getDefaultAuthority(target: GrpcUri): string {
|
||||
return target.path.split(',')[0];
|
||||
}
|
||||
}
|
||||
|
||||
export function setup() {
|
||||
registerResolver(IPV4_SCHEME, IpResolver);
|
||||
registerResolver(IPV6_SCHEME, IpResolver);
|
||||
}
|
||||
63
node_modules/@grpc/grpc-js/src/resolver-uds.ts
generated
vendored
Normal file
63
node_modules/@grpc/grpc-js/src/resolver-uds.ts
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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 { Resolver, ResolverListener, registerResolver } from './resolver';
|
||||
import { SubchannelAddress } from './subchannel-address';
|
||||
import { GrpcUri } from './uri-parser';
|
||||
import { ChannelOptions } from './channel-options';
|
||||
|
||||
class UdsResolver implements Resolver {
|
||||
private addresses: SubchannelAddress[] = [];
|
||||
private hasReturnedResult = false;
|
||||
constructor(
|
||||
target: GrpcUri,
|
||||
private listener: ResolverListener,
|
||||
channelOptions: ChannelOptions
|
||||
) {
|
||||
let path: string;
|
||||
if (target.authority === '') {
|
||||
path = '/' + target.path;
|
||||
} else {
|
||||
path = target.path;
|
||||
}
|
||||
this.addresses = [{ path }];
|
||||
}
|
||||
updateResolution(): void {
|
||||
if (!this.hasReturnedResult) {
|
||||
this.hasReturnedResult = true;
|
||||
process.nextTick(
|
||||
this.listener.onSuccessfulResolution,
|
||||
this.addresses,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
{}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
// This resolver owns no resources, so we do nothing here.
|
||||
}
|
||||
|
||||
static getDefaultAuthority(target: GrpcUri): string {
|
||||
return 'localhost';
|
||||
}
|
||||
}
|
||||
|
||||
export function setup() {
|
||||
registerResolver('unix', UdsResolver);
|
||||
}
|
||||
180
node_modules/@grpc/grpc-js/src/resolver.ts
generated
vendored
Normal file
180
node_modules/@grpc/grpc-js/src/resolver.ts
generated
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* 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 { MethodConfig, ServiceConfig } from './service-config';
|
||||
import { StatusObject } from './call-interface';
|
||||
import { SubchannelAddress } from './subchannel-address';
|
||||
import { GrpcUri, uriToString } from './uri-parser';
|
||||
import { ChannelOptions } from './channel-options';
|
||||
import { Metadata } from './metadata';
|
||||
import { Status } from './constants';
|
||||
import { Filter, FilterFactory } from './filter';
|
||||
|
||||
export interface CallConfig {
|
||||
methodConfig: MethodConfig;
|
||||
onCommitted?: () => void;
|
||||
pickInformation: { [key: string]: string };
|
||||
status: Status;
|
||||
dynamicFilterFactories: FilterFactory<Filter>[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a configuration for a method given the name and metadata. Defined in
|
||||
* https://github.com/grpc/proposal/blob/master/A31-xds-timeout-support-and-config-selector.md#new-functionality-in-grpc
|
||||
*/
|
||||
export interface ConfigSelector {
|
||||
(methodName: string, metadata: Metadata): CallConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener object passed to the resolver's constructor that provides name
|
||||
* resolution updates back to the resolver's owner.
|
||||
*/
|
||||
export interface ResolverListener {
|
||||
/**
|
||||
* Called whenever the resolver has new name resolution results to report
|
||||
* @param addressList The new list of backend addresses
|
||||
* @param serviceConfig The new service configuration corresponding to the
|
||||
* `addressList`. Will be `null` if no service configuration was
|
||||
* retrieved or if the service configuration was invalid
|
||||
* @param serviceConfigError If non-`null`, indicates that the retrieved
|
||||
* service configuration was invalid
|
||||
*/
|
||||
onSuccessfulResolution(
|
||||
addressList: SubchannelAddress[],
|
||||
serviceConfig: ServiceConfig | null,
|
||||
serviceConfigError: StatusObject | null,
|
||||
configSelector: ConfigSelector | null,
|
||||
attributes: { [key: string]: unknown }
|
||||
): void;
|
||||
/**
|
||||
* Called whenever a name resolution attempt fails.
|
||||
* @param error Describes how resolution failed
|
||||
*/
|
||||
onError(error: StatusObject): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A resolver class that handles one or more of the name syntax schemes defined
|
||||
* in the [gRPC Name Resolution document](https://github.com/grpc/grpc/blob/master/doc/naming.md)
|
||||
*/
|
||||
export interface Resolver {
|
||||
/**
|
||||
* Indicates that the caller wants new name resolution data. Calling this
|
||||
* function may eventually result in calling one of the `ResolverListener`
|
||||
* functions, but that is not guaranteed. Those functions will never be
|
||||
* called synchronously with the constructor or updateResolution.
|
||||
*/
|
||||
updateResolution(): void;
|
||||
|
||||
/**
|
||||
* Discard all resources owned by the resolver. A later call to
|
||||
* `updateResolution` should reinitialize those resources. No
|
||||
* `ResolverListener` callbacks should be called after `destroy` is called
|
||||
* until `updateResolution` is called again.
|
||||
*/
|
||||
destroy(): void;
|
||||
}
|
||||
|
||||
export interface ResolverConstructor {
|
||||
new (
|
||||
target: GrpcUri,
|
||||
listener: ResolverListener,
|
||||
channelOptions: ChannelOptions
|
||||
): Resolver;
|
||||
/**
|
||||
* Get the default authority for a target. This loosely corresponds to that
|
||||
* target's hostname. Throws an error if this resolver class cannot parse the
|
||||
* `target`.
|
||||
* @param target
|
||||
*/
|
||||
getDefaultAuthority(target: GrpcUri): string;
|
||||
}
|
||||
|
||||
const registeredResolvers: { [scheme: string]: ResolverConstructor } = {};
|
||||
let defaultScheme: string | null = null;
|
||||
|
||||
/**
|
||||
* Register a resolver class to handle target names prefixed with the `prefix`
|
||||
* string. This prefix should correspond to a URI scheme name listed in the
|
||||
* [gRPC Name Resolution document](https://github.com/grpc/grpc/blob/master/doc/naming.md)
|
||||
* @param prefix
|
||||
* @param resolverClass
|
||||
*/
|
||||
export function registerResolver(
|
||||
scheme: string,
|
||||
resolverClass: ResolverConstructor
|
||||
) {
|
||||
registeredResolvers[scheme] = resolverClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a default resolver to handle target names that do not start with
|
||||
* any registered prefix.
|
||||
* @param resolverClass
|
||||
*/
|
||||
export function registerDefaultScheme(scheme: string) {
|
||||
defaultScheme = scheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a name resolver for the specified target, if possible. Throws an
|
||||
* error if no such name resolver can be created.
|
||||
* @param target
|
||||
* @param listener
|
||||
*/
|
||||
export function createResolver(
|
||||
target: GrpcUri,
|
||||
listener: ResolverListener,
|
||||
options: ChannelOptions
|
||||
): Resolver {
|
||||
if (target.scheme !== undefined && target.scheme in registeredResolvers) {
|
||||
return new registeredResolvers[target.scheme](target, listener, options);
|
||||
} else {
|
||||
throw new Error(
|
||||
`No resolver could be created for target ${uriToString(target)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default authority for the specified target, if possible. Throws an
|
||||
* error if no registered name resolver can parse that target string.
|
||||
* @param target
|
||||
*/
|
||||
export function getDefaultAuthority(target: GrpcUri): string {
|
||||
if (target.scheme !== undefined && target.scheme in registeredResolvers) {
|
||||
return registeredResolvers[target.scheme].getDefaultAuthority(target);
|
||||
} else {
|
||||
throw new Error(`Invalid target ${uriToString(target)}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function mapUriDefaultScheme(target: GrpcUri): GrpcUri | null {
|
||||
if (target.scheme === undefined || !(target.scheme in registeredResolvers)) {
|
||||
if (defaultScheme !== null) {
|
||||
return {
|
||||
scheme: defaultScheme,
|
||||
authority: undefined,
|
||||
path: uriToString(target),
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
331
node_modules/@grpc/grpc-js/src/resolving-call.ts
generated
vendored
Normal file
331
node_modules/@grpc/grpc-js/src/resolving-call.ts
generated
vendored
Normal file
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
* Copyright 2022 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 { CallCredentials } from './call-credentials';
|
||||
import {
|
||||
Call,
|
||||
CallStreamOptions,
|
||||
InterceptingListener,
|
||||
MessageContext,
|
||||
StatusObject,
|
||||
} from './call-interface';
|
||||
import { LogVerbosity, Propagate, Status } from './constants';
|
||||
import {
|
||||
Deadline,
|
||||
deadlineToString,
|
||||
getRelativeTimeout,
|
||||
minDeadline,
|
||||
} from './deadline';
|
||||
import { FilterStack, FilterStackFactory } from './filter-stack';
|
||||
import { InternalChannel } from './internal-channel';
|
||||
import { Metadata } from './metadata';
|
||||
import * as logging from './logging';
|
||||
import { restrictControlPlaneStatusCode } from './control-plane-status';
|
||||
|
||||
const TRACER_NAME = 'resolving_call';
|
||||
|
||||
export class ResolvingCall implements Call {
|
||||
private child: Call | null = null;
|
||||
private readPending = false;
|
||||
private pendingMessage: { context: MessageContext; message: Buffer } | null =
|
||||
null;
|
||||
private pendingHalfClose = false;
|
||||
private ended = false;
|
||||
private readFilterPending = false;
|
||||
private writeFilterPending = false;
|
||||
private pendingChildStatus: StatusObject | null = null;
|
||||
private metadata: Metadata | null = null;
|
||||
private listener: InterceptingListener | null = null;
|
||||
private deadline: Deadline;
|
||||
private host: string;
|
||||
private statusWatchers: ((status: StatusObject) => void)[] = [];
|
||||
private deadlineTimer: NodeJS.Timeout = setTimeout(() => {}, 0);
|
||||
private filterStack: FilterStack | null = null;
|
||||
|
||||
constructor(
|
||||
private readonly channel: InternalChannel,
|
||||
private readonly method: string,
|
||||
options: CallStreamOptions,
|
||||
private readonly filterStackFactory: FilterStackFactory,
|
||||
private credentials: CallCredentials,
|
||||
private callNumber: number
|
||||
) {
|
||||
this.deadline = options.deadline;
|
||||
this.host = options.host;
|
||||
if (options.parentCall) {
|
||||
if (options.flags & Propagate.CANCELLATION) {
|
||||
options.parentCall.on('cancelled', () => {
|
||||
this.cancelWithStatus(Status.CANCELLED, 'Cancelled by parent call');
|
||||
});
|
||||
}
|
||||
if (options.flags & Propagate.DEADLINE) {
|
||||
this.trace(
|
||||
'Propagating deadline from parent: ' +
|
||||
options.parentCall.getDeadline()
|
||||
);
|
||||
this.deadline = minDeadline(
|
||||
this.deadline,
|
||||
options.parentCall.getDeadline()
|
||||
);
|
||||
}
|
||||
}
|
||||
this.trace('Created');
|
||||
this.runDeadlineTimer();
|
||||
}
|
||||
|
||||
private trace(text: string): void {
|
||||
logging.trace(
|
||||
LogVerbosity.DEBUG,
|
||||
TRACER_NAME,
|
||||
'[' + this.callNumber + '] ' + text
|
||||
);
|
||||
}
|
||||
|
||||
private runDeadlineTimer() {
|
||||
clearTimeout(this.deadlineTimer);
|
||||
this.trace('Deadline: ' + deadlineToString(this.deadline));
|
||||
const timeout = getRelativeTimeout(this.deadline);
|
||||
if (timeout !== Infinity) {
|
||||
this.trace('Deadline will be reached in ' + timeout + 'ms');
|
||||
const handleDeadline = () => {
|
||||
this.cancelWithStatus(Status.DEADLINE_EXCEEDED, 'Deadline exceeded');
|
||||
};
|
||||
if (timeout <= 0) {
|
||||
process.nextTick(handleDeadline);
|
||||
} else {
|
||||
this.deadlineTimer = setTimeout(handleDeadline, timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private outputStatus(status: StatusObject) {
|
||||
if (!this.ended) {
|
||||
this.ended = true;
|
||||
if (!this.filterStack) {
|
||||
this.filterStack = this.filterStackFactory.createFilter();
|
||||
}
|
||||
clearTimeout(this.deadlineTimer);
|
||||
const filteredStatus = this.filterStack.receiveTrailers(status);
|
||||
this.trace(
|
||||
'ended with status: code=' +
|
||||
filteredStatus.code +
|
||||
' details="' +
|
||||
filteredStatus.details +
|
||||
'"'
|
||||
);
|
||||
this.statusWatchers.forEach(watcher => watcher(filteredStatus));
|
||||
process.nextTick(() => {
|
||||
this.listener?.onReceiveStatus(filteredStatus);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private sendMessageOnChild(context: MessageContext, message: Buffer): void {
|
||||
if (!this.child) {
|
||||
throw new Error('sendMessageonChild called with child not populated');
|
||||
}
|
||||
const child = this.child;
|
||||
this.writeFilterPending = true;
|
||||
this.filterStack!.sendMessage(
|
||||
Promise.resolve({ message: message, flags: context.flags })
|
||||
).then(
|
||||
filteredMessage => {
|
||||
this.writeFilterPending = false;
|
||||
child.sendMessageWithContext(context, filteredMessage.message);
|
||||
if (this.pendingHalfClose) {
|
||||
child.halfClose();
|
||||
}
|
||||
},
|
||||
(status: StatusObject) => {
|
||||
this.cancelWithStatus(status.code, status.details);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
getConfig(): void {
|
||||
if (this.ended) {
|
||||
return;
|
||||
}
|
||||
if (!this.metadata || !this.listener) {
|
||||
throw new Error('getConfig called before start');
|
||||
}
|
||||
const configResult = this.channel.getConfig(this.method, this.metadata);
|
||||
if (configResult.type === 'NONE') {
|
||||
this.channel.queueCallForConfig(this);
|
||||
return;
|
||||
} else if (configResult.type === 'ERROR') {
|
||||
if (this.metadata.getOptions().waitForReady) {
|
||||
this.channel.queueCallForConfig(this);
|
||||
} else {
|
||||
this.outputStatus(configResult.error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// configResult.type === 'SUCCESS'
|
||||
const config = configResult.config;
|
||||
if (config.status !== Status.OK) {
|
||||
const { code, details } = restrictControlPlaneStatusCode(
|
||||
config.status,
|
||||
'Failed to route call to method ' + this.method
|
||||
);
|
||||
this.outputStatus({
|
||||
code: code,
|
||||
details: details,
|
||||
metadata: new Metadata(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.methodConfig.timeout) {
|
||||
const configDeadline = new Date();
|
||||
configDeadline.setSeconds(
|
||||
configDeadline.getSeconds() + config.methodConfig.timeout.seconds
|
||||
);
|
||||
configDeadline.setMilliseconds(
|
||||
configDeadline.getMilliseconds() +
|
||||
config.methodConfig.timeout.nanos / 1_000_000
|
||||
);
|
||||
this.deadline = minDeadline(this.deadline, configDeadline);
|
||||
this.runDeadlineTimer();
|
||||
}
|
||||
|
||||
this.filterStackFactory.push(config.dynamicFilterFactories);
|
||||
this.filterStack = this.filterStackFactory.createFilter();
|
||||
this.filterStack.sendMetadata(Promise.resolve(this.metadata)).then(
|
||||
filteredMetadata => {
|
||||
this.child = this.channel.createInnerCall(
|
||||
config,
|
||||
this.method,
|
||||
this.host,
|
||||
this.credentials,
|
||||
this.deadline
|
||||
);
|
||||
this.trace('Created child [' + this.child.getCallNumber() + ']');
|
||||
this.child.start(filteredMetadata, {
|
||||
onReceiveMetadata: metadata => {
|
||||
this.trace('Received metadata');
|
||||
this.listener!.onReceiveMetadata(
|
||||
this.filterStack!.receiveMetadata(metadata)
|
||||
);
|
||||
},
|
||||
onReceiveMessage: message => {
|
||||
this.trace('Received message');
|
||||
this.readFilterPending = true;
|
||||
this.filterStack!.receiveMessage(message).then(
|
||||
filteredMesssage => {
|
||||
this.trace('Finished filtering received message');
|
||||
this.readFilterPending = false;
|
||||
this.listener!.onReceiveMessage(filteredMesssage);
|
||||
if (this.pendingChildStatus) {
|
||||
this.outputStatus(this.pendingChildStatus);
|
||||
}
|
||||
},
|
||||
(status: StatusObject) => {
|
||||
this.cancelWithStatus(status.code, status.details);
|
||||
}
|
||||
);
|
||||
},
|
||||
onReceiveStatus: status => {
|
||||
this.trace('Received status');
|
||||
if (this.readFilterPending) {
|
||||
this.pendingChildStatus = status;
|
||||
} else {
|
||||
this.outputStatus(status);
|
||||
}
|
||||
},
|
||||
});
|
||||
if (this.readPending) {
|
||||
this.child.startRead();
|
||||
}
|
||||
if (this.pendingMessage) {
|
||||
this.sendMessageOnChild(
|
||||
this.pendingMessage.context,
|
||||
this.pendingMessage.message
|
||||
);
|
||||
} else if (this.pendingHalfClose) {
|
||||
this.child.halfClose();
|
||||
}
|
||||
},
|
||||
(status: StatusObject) => {
|
||||
this.outputStatus(status);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
reportResolverError(status: StatusObject) {
|
||||
if (this.metadata?.getOptions().waitForReady) {
|
||||
this.channel.queueCallForConfig(this);
|
||||
} else {
|
||||
this.outputStatus(status);
|
||||
}
|
||||
}
|
||||
cancelWithStatus(status: Status, details: string): void {
|
||||
this.trace(
|
||||
'cancelWithStatus code: ' + status + ' details: "' + details + '"'
|
||||
);
|
||||
this.child?.cancelWithStatus(status, details);
|
||||
this.outputStatus({
|
||||
code: status,
|
||||
details: details,
|
||||
metadata: new Metadata(),
|
||||
});
|
||||
}
|
||||
getPeer(): string {
|
||||
return this.child?.getPeer() ?? this.channel.getTarget();
|
||||
}
|
||||
start(metadata: Metadata, listener: InterceptingListener): void {
|
||||
this.trace('start called');
|
||||
this.metadata = metadata.clone();
|
||||
this.listener = listener;
|
||||
this.getConfig();
|
||||
}
|
||||
sendMessageWithContext(context: MessageContext, message: Buffer): void {
|
||||
this.trace('write() called with message of length ' + message.length);
|
||||
if (this.child) {
|
||||
this.sendMessageOnChild(context, message);
|
||||
} else {
|
||||
this.pendingMessage = { context, message };
|
||||
}
|
||||
}
|
||||
startRead(): void {
|
||||
this.trace('startRead called');
|
||||
if (this.child) {
|
||||
this.child.startRead();
|
||||
} else {
|
||||
this.readPending = true;
|
||||
}
|
||||
}
|
||||
halfClose(): void {
|
||||
this.trace('halfClose called');
|
||||
if (this.child && !this.writeFilterPending) {
|
||||
this.child.halfClose();
|
||||
} else {
|
||||
this.pendingHalfClose = true;
|
||||
}
|
||||
}
|
||||
setCredentials(credentials: CallCredentials): void {
|
||||
this.credentials = this.credentials.compose(credentials);
|
||||
}
|
||||
|
||||
addStatusWatcher(watcher: (status: StatusObject) => void) {
|
||||
this.statusWatchers.push(watcher);
|
||||
}
|
||||
|
||||
getCallNumber(): number {
|
||||
return this.callNumber;
|
||||
}
|
||||
}
|
||||
403
node_modules/@grpc/grpc-js/src/resolving-load-balancer.ts
generated
vendored
Normal file
403
node_modules/@grpc/grpc-js/src/resolving-load-balancer.ts
generated
vendored
Normal file
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
* 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 {
|
||||
ChannelControlHelper,
|
||||
LoadBalancer,
|
||||
LoadBalancingConfig,
|
||||
getFirstUsableConfig,
|
||||
} from './load-balancer';
|
||||
import {
|
||||
MethodConfig,
|
||||
ServiceConfig,
|
||||
validateServiceConfig,
|
||||
} from './service-config';
|
||||
import { ConnectivityState } from './connectivity-state';
|
||||
import { ConfigSelector, createResolver, Resolver } from './resolver';
|
||||
import { ServiceError } from './call';
|
||||
import { Picker, UnavailablePicker, QueuePicker } from './picker';
|
||||
import { BackoffOptions, BackoffTimeout } from './backoff-timeout';
|
||||
import { Status } from './constants';
|
||||
import { StatusObject } from './call-interface';
|
||||
import { Metadata } from './metadata';
|
||||
import * as logging from './logging';
|
||||
import { LogVerbosity } from './constants';
|
||||
import { SubchannelAddress } from './subchannel-address';
|
||||
import { GrpcUri, uriToString } from './uri-parser';
|
||||
import { ChildLoadBalancerHandler } from './load-balancer-child-handler';
|
||||
import { ChannelOptions } from './channel-options';
|
||||
|
||||
const TRACER_NAME = 'resolving_load_balancer';
|
||||
|
||||
function trace(text: string): void {
|
||||
logging.trace(LogVerbosity.DEBUG, TRACER_NAME, text);
|
||||
}
|
||||
|
||||
type NameMatchLevel = 'EMPTY' | 'SERVICE' | 'SERVICE_AND_METHOD';
|
||||
|
||||
/**
|
||||
* Name match levels in order from most to least specific. This is the order in
|
||||
* which searches will be performed.
|
||||
*/
|
||||
const NAME_MATCH_LEVEL_ORDER: NameMatchLevel[] = [
|
||||
'SERVICE_AND_METHOD',
|
||||
'SERVICE',
|
||||
'EMPTY',
|
||||
];
|
||||
|
||||
function hasMatchingName(
|
||||
service: string,
|
||||
method: string,
|
||||
methodConfig: MethodConfig,
|
||||
matchLevel: NameMatchLevel
|
||||
): boolean {
|
||||
for (const name of methodConfig.name) {
|
||||
switch (matchLevel) {
|
||||
case 'EMPTY':
|
||||
if (!name.service && !name.method) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 'SERVICE':
|
||||
if (name.service === service && !name.method) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 'SERVICE_AND_METHOD':
|
||||
if (name.service === service && name.method === method) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function findMatchingConfig(
|
||||
service: string,
|
||||
method: string,
|
||||
methodConfigs: MethodConfig[],
|
||||
matchLevel: NameMatchLevel
|
||||
): MethodConfig | null {
|
||||
for (const config of methodConfigs) {
|
||||
if (hasMatchingName(service, method, config, matchLevel)) {
|
||||
return config;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getDefaultConfigSelector(
|
||||
serviceConfig: ServiceConfig | null
|
||||
): ConfigSelector {
|
||||
return function defaultConfigSelector(
|
||||
methodName: string,
|
||||
metadata: Metadata
|
||||
) {
|
||||
const splitName = methodName.split('/').filter(x => x.length > 0);
|
||||
const service = splitName[0] ?? '';
|
||||
const method = splitName[1] ?? '';
|
||||
if (serviceConfig && serviceConfig.methodConfig) {
|
||||
/* Check for the following in order, and return the first method
|
||||
* config that matches:
|
||||
* 1. A name that exactly matches the service and method
|
||||
* 2. A name with no method set that matches the service
|
||||
* 3. An empty name
|
||||
*/
|
||||
for (const matchLevel of NAME_MATCH_LEVEL_ORDER) {
|
||||
const matchingConfig = findMatchingConfig(
|
||||
service,
|
||||
method,
|
||||
serviceConfig.methodConfig,
|
||||
matchLevel
|
||||
);
|
||||
if (matchingConfig) {
|
||||
return {
|
||||
methodConfig: matchingConfig,
|
||||
pickInformation: {},
|
||||
status: Status.OK,
|
||||
dynamicFilterFactories: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
methodConfig: { name: [] },
|
||||
pickInformation: {},
|
||||
status: Status.OK,
|
||||
dynamicFilterFactories: [],
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface ResolutionCallback {
|
||||
(serviceConfig: ServiceConfig, configSelector: ConfigSelector): void;
|
||||
}
|
||||
|
||||
export interface ResolutionFailureCallback {
|
||||
(status: StatusObject): void;
|
||||
}
|
||||
|
||||
export class ResolvingLoadBalancer implements LoadBalancer {
|
||||
/**
|
||||
* The resolver class constructed for the target address.
|
||||
*/
|
||||
private readonly innerResolver: Resolver;
|
||||
|
||||
private readonly childLoadBalancer: ChildLoadBalancerHandler;
|
||||
private latestChildState: ConnectivityState = ConnectivityState.IDLE;
|
||||
private latestChildPicker: Picker = new QueuePicker(this);
|
||||
/**
|
||||
* This resolving load balancer's current connectivity state.
|
||||
*/
|
||||
private currentState: ConnectivityState = ConnectivityState.IDLE;
|
||||
private readonly defaultServiceConfig: ServiceConfig;
|
||||
/**
|
||||
* The service config object from the last successful resolution, if
|
||||
* available. A value of null indicates that we have not yet received a valid
|
||||
* service config from the resolver.
|
||||
*/
|
||||
private previousServiceConfig: ServiceConfig | null = null;
|
||||
|
||||
/**
|
||||
* The backoff timer for handling name resolution failures.
|
||||
*/
|
||||
private readonly backoffTimeout: BackoffTimeout;
|
||||
|
||||
/**
|
||||
* Indicates whether we should attempt to resolve again after the backoff
|
||||
* timer runs out.
|
||||
*/
|
||||
private continueResolving = false;
|
||||
|
||||
/**
|
||||
* Wrapper class that behaves like a `LoadBalancer` and also handles name
|
||||
* resolution internally.
|
||||
* @param target The address of the backend to connect to.
|
||||
* @param channelControlHelper `ChannelControlHelper` instance provided by
|
||||
* this load balancer's owner.
|
||||
* @param defaultServiceConfig The default service configuration to be used
|
||||
* if none is provided by the name resolver. A `null` value indicates
|
||||
* that the default behavior should be the default unconfigured behavior.
|
||||
* In practice, that means using the "pick first" load balancer
|
||||
* implmentation
|
||||
*/
|
||||
constructor(
|
||||
private readonly target: GrpcUri,
|
||||
private readonly channelControlHelper: ChannelControlHelper,
|
||||
channelOptions: ChannelOptions,
|
||||
private readonly onSuccessfulResolution: ResolutionCallback,
|
||||
private readonly onFailedResolution: ResolutionFailureCallback
|
||||
) {
|
||||
if (channelOptions['grpc.service_config']) {
|
||||
this.defaultServiceConfig = validateServiceConfig(
|
||||
JSON.parse(channelOptions['grpc.service_config']!)
|
||||
);
|
||||
} else {
|
||||
this.defaultServiceConfig = {
|
||||
loadBalancingConfig: [],
|
||||
methodConfig: [],
|
||||
};
|
||||
}
|
||||
this.updateState(ConnectivityState.IDLE, new QueuePicker(this));
|
||||
this.childLoadBalancer = new ChildLoadBalancerHandler({
|
||||
createSubchannel:
|
||||
channelControlHelper.createSubchannel.bind(channelControlHelper),
|
||||
requestReresolution: () => {
|
||||
/* If the backoffTimeout is running, we're still backing off from
|
||||
* making resolve requests, so we shouldn't make another one here.
|
||||
* In that case, the backoff timer callback will call
|
||||
* updateResolution */
|
||||
if (this.backoffTimeout.isRunning()) {
|
||||
trace('requestReresolution delayed by backoff timer until ' + this.backoffTimeout.getEndTime().toISOString());
|
||||
this.continueResolving = true;
|
||||
} else {
|
||||
this.updateResolution();
|
||||
}
|
||||
},
|
||||
updateState: (newState: ConnectivityState, picker: Picker) => {
|
||||
this.latestChildState = newState;
|
||||
this.latestChildPicker = picker;
|
||||
this.updateState(newState, picker);
|
||||
},
|
||||
addChannelzChild:
|
||||
channelControlHelper.addChannelzChild.bind(channelControlHelper),
|
||||
removeChannelzChild:
|
||||
channelControlHelper.removeChannelzChild.bind(channelControlHelper),
|
||||
});
|
||||
this.innerResolver = createResolver(
|
||||
target,
|
||||
{
|
||||
onSuccessfulResolution: (
|
||||
addressList: SubchannelAddress[],
|
||||
serviceConfig: ServiceConfig | null,
|
||||
serviceConfigError: ServiceError | null,
|
||||
configSelector: ConfigSelector | null,
|
||||
attributes: { [key: string]: unknown }
|
||||
) => {
|
||||
this.backoffTimeout.stop();
|
||||
this.backoffTimeout.reset();
|
||||
let workingServiceConfig: ServiceConfig | null = null;
|
||||
/* This first group of conditionals implements the algorithm described
|
||||
* in https://github.com/grpc/proposal/blob/master/A21-service-config-error-handling.md
|
||||
* in the section called "Behavior on receiving a new gRPC Config".
|
||||
*/
|
||||
if (serviceConfig === null) {
|
||||
// Step 4 and 5
|
||||
if (serviceConfigError === null) {
|
||||
// Step 5
|
||||
this.previousServiceConfig = null;
|
||||
workingServiceConfig = this.defaultServiceConfig;
|
||||
} else {
|
||||
// Step 4
|
||||
if (this.previousServiceConfig === null) {
|
||||
// Step 4.ii
|
||||
this.handleResolutionFailure(serviceConfigError);
|
||||
} else {
|
||||
// Step 4.i
|
||||
workingServiceConfig = this.previousServiceConfig;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Step 3
|
||||
workingServiceConfig = serviceConfig;
|
||||
this.previousServiceConfig = serviceConfig;
|
||||
}
|
||||
const workingConfigList =
|
||||
workingServiceConfig?.loadBalancingConfig ?? [];
|
||||
const loadBalancingConfig = getFirstUsableConfig(
|
||||
workingConfigList,
|
||||
true
|
||||
);
|
||||
if (loadBalancingConfig === null) {
|
||||
// There were load balancing configs but none are supported. This counts as a resolution failure
|
||||
this.handleResolutionFailure({
|
||||
code: Status.UNAVAILABLE,
|
||||
details:
|
||||
'All load balancer options in service config are not compatible',
|
||||
metadata: new Metadata(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.childLoadBalancer.updateAddressList(
|
||||
addressList,
|
||||
loadBalancingConfig,
|
||||
attributes
|
||||
);
|
||||
const finalServiceConfig =
|
||||
workingServiceConfig ?? this.defaultServiceConfig;
|
||||
this.onSuccessfulResolution(
|
||||
finalServiceConfig,
|
||||
configSelector ?? getDefaultConfigSelector(finalServiceConfig)
|
||||
);
|
||||
},
|
||||
onError: (error: StatusObject) => {
|
||||
this.handleResolutionFailure(error);
|
||||
},
|
||||
},
|
||||
channelOptions
|
||||
);
|
||||
const backoffOptions: BackoffOptions = {
|
||||
initialDelay: channelOptions['grpc.initial_reconnect_backoff_ms'],
|
||||
maxDelay: channelOptions['grpc.max_reconnect_backoff_ms'],
|
||||
};
|
||||
this.backoffTimeout = new BackoffTimeout(() => {
|
||||
if (this.continueResolving) {
|
||||
this.updateResolution();
|
||||
this.continueResolving = false;
|
||||
} else {
|
||||
this.updateState(this.latestChildState, this.latestChildPicker);
|
||||
}
|
||||
}, backoffOptions);
|
||||
this.backoffTimeout.unref();
|
||||
}
|
||||
|
||||
private updateResolution() {
|
||||
this.innerResolver.updateResolution();
|
||||
if (this.currentState === ConnectivityState.IDLE) {
|
||||
this.updateState(ConnectivityState.CONNECTING, new QueuePicker(this));
|
||||
}
|
||||
this.backoffTimeout.runOnce();
|
||||
}
|
||||
|
||||
private updateState(connectivityState: ConnectivityState, picker: Picker) {
|
||||
trace(
|
||||
uriToString(this.target) +
|
||||
' ' +
|
||||
ConnectivityState[this.currentState] +
|
||||
' -> ' +
|
||||
ConnectivityState[connectivityState]
|
||||
);
|
||||
// Ensure that this.exitIdle() is called by the picker
|
||||
if (connectivityState === ConnectivityState.IDLE) {
|
||||
picker = new QueuePicker(this);
|
||||
}
|
||||
this.currentState = connectivityState;
|
||||
this.channelControlHelper.updateState(connectivityState, picker);
|
||||
}
|
||||
|
||||
private handleResolutionFailure(error: StatusObject) {
|
||||
if (this.latestChildState === ConnectivityState.IDLE) {
|
||||
this.updateState(
|
||||
ConnectivityState.TRANSIENT_FAILURE,
|
||||
new UnavailablePicker(error)
|
||||
);
|
||||
this.onFailedResolution(error);
|
||||
}
|
||||
}
|
||||
|
||||
exitIdle() {
|
||||
if (
|
||||
this.currentState === ConnectivityState.IDLE ||
|
||||
this.currentState === ConnectivityState.TRANSIENT_FAILURE
|
||||
) {
|
||||
if (this.backoffTimeout.isRunning()) {
|
||||
this.continueResolving = true;
|
||||
} else {
|
||||
this.updateResolution();
|
||||
}
|
||||
}
|
||||
this.childLoadBalancer.exitIdle();
|
||||
}
|
||||
|
||||
updateAddressList(
|
||||
addressList: SubchannelAddress[],
|
||||
lbConfig: LoadBalancingConfig | null
|
||||
): never {
|
||||
throw new Error('updateAddressList not supported on ResolvingLoadBalancer');
|
||||
}
|
||||
|
||||
resetBackoff() {
|
||||
this.backoffTimeout.reset();
|
||||
this.childLoadBalancer.resetBackoff();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.childLoadBalancer.destroy();
|
||||
this.innerResolver.destroy();
|
||||
this.backoffTimeout.reset();
|
||||
this.backoffTimeout.stop();
|
||||
this.latestChildState = ConnectivityState.IDLE;
|
||||
this.latestChildPicker = new QueuePicker(this);
|
||||
this.currentState = ConnectivityState.IDLE;
|
||||
this.previousServiceConfig = null;
|
||||
this.continueResolving = false;
|
||||
}
|
||||
|
||||
getTypeName() {
|
||||
return 'resolving_load_balancer';
|
||||
}
|
||||
}
|
||||
821
node_modules/@grpc/grpc-js/src/retrying-call.ts
generated
vendored
Normal file
821
node_modules/@grpc/grpc-js/src/retrying-call.ts
generated
vendored
Normal file
@@ -0,0 +1,821 @@
|
||||
/*
|
||||
* Copyright 2022 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 { CallCredentials } from './call-credentials';
|
||||
import { LogVerbosity, Status } from './constants';
|
||||
import { Deadline } from './deadline';
|
||||
import { Metadata } from './metadata';
|
||||
import { CallConfig } from './resolver';
|
||||
import * as logging from './logging';
|
||||
import {
|
||||
Call,
|
||||
InterceptingListener,
|
||||
MessageContext,
|
||||
StatusObject,
|
||||
WriteCallback,
|
||||
WriteObject,
|
||||
} from './call-interface';
|
||||
import {
|
||||
LoadBalancingCall,
|
||||
StatusObjectWithProgress,
|
||||
} from './load-balancing-call';
|
||||
import { InternalChannel } from './internal-channel';
|
||||
|
||||
const TRACER_NAME = 'retrying_call';
|
||||
|
||||
export class RetryThrottler {
|
||||
private tokens: number;
|
||||
constructor(
|
||||
private readonly maxTokens: number,
|
||||
private readonly tokenRatio: number,
|
||||
previousRetryThrottler?: RetryThrottler
|
||||
) {
|
||||
if (previousRetryThrottler) {
|
||||
/* When carrying over tokens from a previous config, rescale them to the
|
||||
* new max value */
|
||||
this.tokens =
|
||||
previousRetryThrottler.tokens *
|
||||
(maxTokens / previousRetryThrottler.maxTokens);
|
||||
} else {
|
||||
this.tokens = maxTokens;
|
||||
}
|
||||
}
|
||||
|
||||
addCallSucceeded() {
|
||||
this.tokens = Math.max(this.tokens + this.tokenRatio, this.maxTokens);
|
||||
}
|
||||
|
||||
addCallFailed() {
|
||||
this.tokens = Math.min(this.tokens - 1, 0);
|
||||
}
|
||||
|
||||
canRetryCall() {
|
||||
return this.tokens > this.maxTokens / 2;
|
||||
}
|
||||
}
|
||||
|
||||
export class MessageBufferTracker {
|
||||
private totalAllocated = 0;
|
||||
private allocatedPerCall: Map<number, number> = new Map<number, number>();
|
||||
|
||||
constructor(private totalLimit: number, private limitPerCall: number) {}
|
||||
|
||||
allocate(size: number, callId: number): boolean {
|
||||
const currentPerCall = this.allocatedPerCall.get(callId) ?? 0;
|
||||
if (
|
||||
this.limitPerCall - currentPerCall < size ||
|
||||
this.totalLimit - this.totalAllocated < size
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
this.allocatedPerCall.set(callId, currentPerCall + size);
|
||||
this.totalAllocated += size;
|
||||
return true;
|
||||
}
|
||||
|
||||
free(size: number, callId: number) {
|
||||
if (this.totalAllocated < size) {
|
||||
throw new Error(
|
||||
`Invalid buffer allocation state: call ${callId} freed ${size} > total allocated ${this.totalAllocated}`
|
||||
);
|
||||
}
|
||||
this.totalAllocated -= size;
|
||||
const currentPerCall = this.allocatedPerCall.get(callId) ?? 0;
|
||||
if (currentPerCall < size) {
|
||||
throw new Error(
|
||||
`Invalid buffer allocation state: call ${callId} freed ${size} > allocated for call ${currentPerCall}`
|
||||
);
|
||||
}
|
||||
this.allocatedPerCall.set(callId, currentPerCall - size);
|
||||
}
|
||||
|
||||
freeAll(callId: number) {
|
||||
const currentPerCall = this.allocatedPerCall.get(callId) ?? 0;
|
||||
if (this.totalAllocated < currentPerCall) {
|
||||
throw new Error(
|
||||
`Invalid buffer allocation state: call ${callId} allocated ${currentPerCall} > total allocated ${this.totalAllocated}`
|
||||
);
|
||||
}
|
||||
this.totalAllocated -= currentPerCall;
|
||||
this.allocatedPerCall.delete(callId);
|
||||
}
|
||||
}
|
||||
|
||||
type UnderlyingCallState = 'ACTIVE' | 'COMPLETED';
|
||||
|
||||
interface UnderlyingCall {
|
||||
state: UnderlyingCallState;
|
||||
call: LoadBalancingCall;
|
||||
nextMessageToSend: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* A retrying call can be in one of these states:
|
||||
* RETRY: Retries are configured and new attempts may be sent
|
||||
* HEDGING: Hedging is configured and new attempts may be sent
|
||||
* TRANSPARENT_ONLY: Neither retries nor hedging are configured, and
|
||||
* transparent retry attempts may still be sent
|
||||
* COMMITTED: One attempt is committed, and no new attempts will be
|
||||
* sent
|
||||
*/
|
||||
type RetryingCallState = 'RETRY' | 'HEDGING' | 'TRANSPARENT_ONLY' | 'COMMITTED';
|
||||
|
||||
/**
|
||||
* The different types of objects that can be stored in the write buffer, with
|
||||
* the following meanings:
|
||||
* MESSAGE: This is a message to be sent.
|
||||
* HALF_CLOSE: When this entry is reached, the calls should send a half-close.
|
||||
* FREED: This slot previously contained a message that has been sent on all
|
||||
* child calls and is no longer needed.
|
||||
*/
|
||||
type WriteBufferEntryType = 'MESSAGE' | 'HALF_CLOSE' | 'FREED';
|
||||
|
||||
/**
|
||||
* Entry in the buffer of messages to send to the remote end.
|
||||
*/
|
||||
interface WriteBufferEntry {
|
||||
entryType: WriteBufferEntryType;
|
||||
/**
|
||||
* Message to send.
|
||||
* Only populated if entryType is MESSAGE.
|
||||
*/
|
||||
message?: WriteObject;
|
||||
/**
|
||||
* Callback to call after sending the message.
|
||||
* Only populated if entryType is MESSAGE and the call is in the COMMITTED
|
||||
* state.
|
||||
*/
|
||||
callback?: WriteCallback;
|
||||
/**
|
||||
* Indicates whether the message is allocated in the buffer tracker. Ignored
|
||||
* if entryType is not MESSAGE. Should be the return value of
|
||||
* bufferTracker.allocate.
|
||||
*/
|
||||
allocated: boolean;
|
||||
}
|
||||
|
||||
const PREVIONS_RPC_ATTEMPTS_METADATA_KEY = 'grpc-previous-rpc-attempts';
|
||||
|
||||
export class RetryingCall implements Call {
|
||||
private state: RetryingCallState;
|
||||
private listener: InterceptingListener | null = null;
|
||||
private initialMetadata: Metadata | null = null;
|
||||
private underlyingCalls: UnderlyingCall[] = [];
|
||||
private writeBuffer: WriteBufferEntry[] = [];
|
||||
/**
|
||||
* The offset of message indices in the writeBuffer. For example, if
|
||||
* writeBufferOffset is 10, message 10 is in writeBuffer[0] and message 15
|
||||
* is in writeBuffer[5].
|
||||
*/
|
||||
private writeBufferOffset = 0;
|
||||
/**
|
||||
* Tracks whether a read has been started, so that we know whether to start
|
||||
* reads on new child calls. This only matters for the first read, because
|
||||
* once a message comes in the child call becomes committed and there will
|
||||
* be no new child calls.
|
||||
*/
|
||||
private readStarted = false;
|
||||
private transparentRetryUsed = false;
|
||||
/**
|
||||
* Number of attempts so far
|
||||
*/
|
||||
private attempts = 0;
|
||||
private hedgingTimer: NodeJS.Timeout | null = null;
|
||||
private committedCallIndex: number | null = null;
|
||||
private initialRetryBackoffSec = 0;
|
||||
private nextRetryBackoffSec = 0;
|
||||
constructor(
|
||||
private readonly channel: InternalChannel,
|
||||
private readonly callConfig: CallConfig,
|
||||
private readonly methodName: string,
|
||||
private readonly host: string,
|
||||
private readonly credentials: CallCredentials,
|
||||
private readonly deadline: Deadline,
|
||||
private readonly callNumber: number,
|
||||
private readonly bufferTracker: MessageBufferTracker,
|
||||
private readonly retryThrottler?: RetryThrottler
|
||||
) {
|
||||
if (callConfig.methodConfig.retryPolicy) {
|
||||
this.state = 'RETRY';
|
||||
const retryPolicy = callConfig.methodConfig.retryPolicy;
|
||||
this.nextRetryBackoffSec = this.initialRetryBackoffSec = Number(
|
||||
retryPolicy.initialBackoff.substring(
|
||||
0,
|
||||
retryPolicy.initialBackoff.length - 1
|
||||
)
|
||||
);
|
||||
} else if (callConfig.methodConfig.hedgingPolicy) {
|
||||
this.state = 'HEDGING';
|
||||
} else {
|
||||
this.state = 'TRANSPARENT_ONLY';
|
||||
}
|
||||
}
|
||||
getCallNumber(): number {
|
||||
return this.callNumber;
|
||||
}
|
||||
|
||||
private trace(text: string): void {
|
||||
logging.trace(
|
||||
LogVerbosity.DEBUG,
|
||||
TRACER_NAME,
|
||||
'[' + this.callNumber + '] ' + text
|
||||
);
|
||||
}
|
||||
|
||||
private reportStatus(statusObject: StatusObject) {
|
||||
this.trace(
|
||||
'ended with status: code=' +
|
||||
statusObject.code +
|
||||
' details="' +
|
||||
statusObject.details +
|
||||
'"'
|
||||
);
|
||||
this.bufferTracker.freeAll(this.callNumber);
|
||||
this.writeBufferOffset = this.writeBufferOffset + this.writeBuffer.length;
|
||||
this.writeBuffer = [];
|
||||
process.nextTick(() => {
|
||||
// Explicitly construct status object to remove progress field
|
||||
this.listener?.onReceiveStatus({
|
||||
code: statusObject.code,
|
||||
details: statusObject.details,
|
||||
metadata: statusObject.metadata,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
cancelWithStatus(status: Status, details: string): void {
|
||||
this.trace(
|
||||
'cancelWithStatus code: ' + status + ' details: "' + details + '"'
|
||||
);
|
||||
this.reportStatus({ code: status, details, metadata: new Metadata() });
|
||||
for (const { call } of this.underlyingCalls) {
|
||||
call.cancelWithStatus(status, details);
|
||||
}
|
||||
}
|
||||
getPeer(): string {
|
||||
if (this.committedCallIndex !== null) {
|
||||
return this.underlyingCalls[this.committedCallIndex].call.getPeer();
|
||||
} else {
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
private getBufferEntry(messageIndex: number): WriteBufferEntry {
|
||||
return (
|
||||
this.writeBuffer[messageIndex - this.writeBufferOffset] ?? {
|
||||
entryType: 'FREED',
|
||||
allocated: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private getNextBufferIndex() {
|
||||
return this.writeBufferOffset + this.writeBuffer.length;
|
||||
}
|
||||
|
||||
private clearSentMessages() {
|
||||
if (this.state !== 'COMMITTED') {
|
||||
return;
|
||||
}
|
||||
const earliestNeededMessageIndex =
|
||||
this.underlyingCalls[this.committedCallIndex!].nextMessageToSend;
|
||||
for (
|
||||
let messageIndex = this.writeBufferOffset;
|
||||
messageIndex < earliestNeededMessageIndex;
|
||||
messageIndex++
|
||||
) {
|
||||
const bufferEntry = this.getBufferEntry(messageIndex);
|
||||
if (bufferEntry.allocated) {
|
||||
this.bufferTracker.free(
|
||||
bufferEntry.message!.message.length,
|
||||
this.callNumber
|
||||
);
|
||||
}
|
||||
}
|
||||
this.writeBuffer = this.writeBuffer.slice(
|
||||
earliestNeededMessageIndex - this.writeBufferOffset
|
||||
);
|
||||
this.writeBufferOffset = earliestNeededMessageIndex;
|
||||
}
|
||||
|
||||
private commitCall(index: number) {
|
||||
if (this.state === 'COMMITTED') {
|
||||
return;
|
||||
}
|
||||
if (this.underlyingCalls[index].state === 'COMPLETED') {
|
||||
return;
|
||||
}
|
||||
this.trace(
|
||||
'Committing call [' +
|
||||
this.underlyingCalls[index].call.getCallNumber() +
|
||||
'] at index ' +
|
||||
index
|
||||
);
|
||||
this.state = 'COMMITTED';
|
||||
this.committedCallIndex = index;
|
||||
for (let i = 0; i < this.underlyingCalls.length; i++) {
|
||||
if (i === index) {
|
||||
continue;
|
||||
}
|
||||
if (this.underlyingCalls[i].state === 'COMPLETED') {
|
||||
continue;
|
||||
}
|
||||
this.underlyingCalls[i].state = 'COMPLETED';
|
||||
this.underlyingCalls[i].call.cancelWithStatus(
|
||||
Status.CANCELLED,
|
||||
'Discarded in favor of other hedged attempt'
|
||||
);
|
||||
}
|
||||
this.clearSentMessages();
|
||||
}
|
||||
|
||||
private commitCallWithMostMessages() {
|
||||
if (this.state === 'COMMITTED') {
|
||||
return;
|
||||
}
|
||||
let mostMessages = -1;
|
||||
let callWithMostMessages = -1;
|
||||
for (const [index, childCall] of this.underlyingCalls.entries()) {
|
||||
if (
|
||||
childCall.state === 'ACTIVE' &&
|
||||
childCall.nextMessageToSend > mostMessages
|
||||
) {
|
||||
mostMessages = childCall.nextMessageToSend;
|
||||
callWithMostMessages = index;
|
||||
}
|
||||
}
|
||||
if (callWithMostMessages === -1) {
|
||||
/* There are no active calls, disable retries to force the next call that
|
||||
* is started to be committed. */
|
||||
this.state = 'TRANSPARENT_ONLY';
|
||||
} else {
|
||||
this.commitCall(callWithMostMessages);
|
||||
}
|
||||
}
|
||||
|
||||
private isStatusCodeInList(list: (Status | string)[], code: Status) {
|
||||
return list.some(
|
||||
value =>
|
||||
value === code ||
|
||||
value.toString().toLowerCase() === Status[code].toLowerCase()
|
||||
);
|
||||
}
|
||||
|
||||
private getNextRetryBackoffMs() {
|
||||
const retryPolicy = this.callConfig?.methodConfig.retryPolicy;
|
||||
if (!retryPolicy) {
|
||||
return 0;
|
||||
}
|
||||
const nextBackoffMs = Math.random() * this.nextRetryBackoffSec * 1000;
|
||||
const maxBackoffSec = Number(
|
||||
retryPolicy.maxBackoff.substring(0, retryPolicy.maxBackoff.length - 1)
|
||||
);
|
||||
this.nextRetryBackoffSec = Math.min(
|
||||
this.nextRetryBackoffSec * retryPolicy.backoffMultiplier,
|
||||
maxBackoffSec
|
||||
);
|
||||
return nextBackoffMs;
|
||||
}
|
||||
|
||||
private maybeRetryCall(
|
||||
pushback: number | null,
|
||||
callback: (retried: boolean) => void
|
||||
) {
|
||||
if (this.state !== 'RETRY') {
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
const retryPolicy = this.callConfig!.methodConfig.retryPolicy!;
|
||||
if (this.attempts >= Math.min(retryPolicy.maxAttempts, 5)) {
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
let retryDelayMs: number;
|
||||
if (pushback === null) {
|
||||
retryDelayMs = this.getNextRetryBackoffMs();
|
||||
} else if (pushback < 0) {
|
||||
this.state = 'TRANSPARENT_ONLY';
|
||||
callback(false);
|
||||
return;
|
||||
} else {
|
||||
retryDelayMs = pushback;
|
||||
this.nextRetryBackoffSec = this.initialRetryBackoffSec;
|
||||
}
|
||||
setTimeout(() => {
|
||||
if (this.state !== 'RETRY') {
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
if (this.retryThrottler?.canRetryCall() ?? true) {
|
||||
callback(true);
|
||||
this.attempts += 1;
|
||||
this.startNewAttempt();
|
||||
}
|
||||
}, retryDelayMs);
|
||||
}
|
||||
|
||||
private countActiveCalls(): number {
|
||||
let count = 0;
|
||||
for (const call of this.underlyingCalls) {
|
||||
if (call?.state === 'ACTIVE') {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private handleProcessedStatus(
|
||||
status: StatusObject,
|
||||
callIndex: number,
|
||||
pushback: number | null
|
||||
) {
|
||||
switch (this.state) {
|
||||
case 'COMMITTED':
|
||||
case 'TRANSPARENT_ONLY':
|
||||
this.commitCall(callIndex);
|
||||
this.reportStatus(status);
|
||||
break;
|
||||
case 'HEDGING':
|
||||
if (
|
||||
this.isStatusCodeInList(
|
||||
this.callConfig!.methodConfig.hedgingPolicy!.nonFatalStatusCodes ??
|
||||
[],
|
||||
status.code
|
||||
)
|
||||
) {
|
||||
this.retryThrottler?.addCallFailed();
|
||||
let delayMs: number;
|
||||
if (pushback === null) {
|
||||
delayMs = 0;
|
||||
} else if (pushback < 0) {
|
||||
this.state = 'TRANSPARENT_ONLY';
|
||||
this.commitCall(callIndex);
|
||||
this.reportStatus(status);
|
||||
return;
|
||||
} else {
|
||||
delayMs = pushback;
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.maybeStartHedgingAttempt();
|
||||
// If after trying to start a call there are no active calls, this was the last one
|
||||
if (this.countActiveCalls() === 0) {
|
||||
this.commitCall(callIndex);
|
||||
this.reportStatus(status);
|
||||
}
|
||||
}, delayMs);
|
||||
} else {
|
||||
this.commitCall(callIndex);
|
||||
this.reportStatus(status);
|
||||
}
|
||||
break;
|
||||
case 'RETRY':
|
||||
if (
|
||||
this.isStatusCodeInList(
|
||||
this.callConfig!.methodConfig.retryPolicy!.retryableStatusCodes,
|
||||
status.code
|
||||
)
|
||||
) {
|
||||
this.retryThrottler?.addCallFailed();
|
||||
this.maybeRetryCall(pushback, retried => {
|
||||
if (!retried) {
|
||||
this.commitCall(callIndex);
|
||||
this.reportStatus(status);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.commitCall(callIndex);
|
||||
this.reportStatus(status);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private getPushback(metadata: Metadata): number | null {
|
||||
const mdValue = metadata.get('grpc-retry-pushback-ms');
|
||||
if (mdValue.length === 0) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return parseInt(mdValue[0] as string);
|
||||
} catch (e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private handleChildStatus(
|
||||
status: StatusObjectWithProgress,
|
||||
callIndex: number
|
||||
) {
|
||||
if (this.underlyingCalls[callIndex].state === 'COMPLETED') {
|
||||
return;
|
||||
}
|
||||
this.trace(
|
||||
'state=' +
|
||||
this.state +
|
||||
' handling status with progress ' +
|
||||
status.progress +
|
||||
' from child [' +
|
||||
this.underlyingCalls[callIndex].call.getCallNumber() +
|
||||
'] in state ' +
|
||||
this.underlyingCalls[callIndex].state
|
||||
);
|
||||
this.underlyingCalls[callIndex].state = 'COMPLETED';
|
||||
if (status.code === Status.OK) {
|
||||
this.retryThrottler?.addCallSucceeded();
|
||||
this.commitCall(callIndex);
|
||||
this.reportStatus(status);
|
||||
return;
|
||||
}
|
||||
if (this.state === 'COMMITTED') {
|
||||
this.reportStatus(status);
|
||||
return;
|
||||
}
|
||||
const pushback = this.getPushback(status.metadata);
|
||||
switch (status.progress) {
|
||||
case 'NOT_STARTED':
|
||||
// RPC never leaves the client, always safe to retry
|
||||
this.startNewAttempt();
|
||||
break;
|
||||
case 'REFUSED':
|
||||
// RPC reaches the server library, but not the server application logic
|
||||
if (this.transparentRetryUsed) {
|
||||
this.handleProcessedStatus(status, callIndex, pushback);
|
||||
} else {
|
||||
this.transparentRetryUsed = true;
|
||||
this.startNewAttempt();
|
||||
}
|
||||
break;
|
||||
case 'DROP':
|
||||
this.commitCall(callIndex);
|
||||
this.reportStatus(status);
|
||||
break;
|
||||
case 'PROCESSED':
|
||||
this.handleProcessedStatus(status, callIndex, pushback);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private maybeStartHedgingAttempt() {
|
||||
if (this.state !== 'HEDGING') {
|
||||
return;
|
||||
}
|
||||
if (!this.callConfig.methodConfig.hedgingPolicy) {
|
||||
return;
|
||||
}
|
||||
const hedgingPolicy = this.callConfig.methodConfig.hedgingPolicy;
|
||||
if (this.attempts >= Math.min(hedgingPolicy.maxAttempts, 5)) {
|
||||
return;
|
||||
}
|
||||
this.attempts += 1;
|
||||
this.startNewAttempt();
|
||||
this.maybeStartHedgingTimer();
|
||||
}
|
||||
|
||||
private maybeStartHedgingTimer() {
|
||||
if (this.hedgingTimer) {
|
||||
clearTimeout(this.hedgingTimer);
|
||||
}
|
||||
if (this.state !== 'HEDGING') {
|
||||
return;
|
||||
}
|
||||
if (!this.callConfig.methodConfig.hedgingPolicy) {
|
||||
return;
|
||||
}
|
||||
const hedgingPolicy = this.callConfig.methodConfig.hedgingPolicy;
|
||||
if (this.attempts >= Math.min(hedgingPolicy.maxAttempts, 5)) {
|
||||
return;
|
||||
}
|
||||
const hedgingDelayString = hedgingPolicy.hedgingDelay ?? '0s';
|
||||
const hedgingDelaySec = Number(
|
||||
hedgingDelayString.substring(0, hedgingDelayString.length - 1)
|
||||
);
|
||||
this.hedgingTimer = setTimeout(() => {
|
||||
this.maybeStartHedgingAttempt();
|
||||
}, hedgingDelaySec * 1000);
|
||||
this.hedgingTimer.unref?.();
|
||||
}
|
||||
|
||||
private startNewAttempt() {
|
||||
const child = this.channel.createLoadBalancingCall(
|
||||
this.callConfig,
|
||||
this.methodName,
|
||||
this.host,
|
||||
this.credentials,
|
||||
this.deadline
|
||||
);
|
||||
this.trace(
|
||||
'Created child call [' +
|
||||
child.getCallNumber() +
|
||||
'] for attempt ' +
|
||||
this.attempts
|
||||
);
|
||||
const index = this.underlyingCalls.length;
|
||||
this.underlyingCalls.push({
|
||||
state: 'ACTIVE',
|
||||
call: child,
|
||||
nextMessageToSend: 0,
|
||||
});
|
||||
const previousAttempts = this.attempts - 1;
|
||||
const initialMetadata = this.initialMetadata!.clone();
|
||||
if (previousAttempts > 0) {
|
||||
initialMetadata.set(
|
||||
PREVIONS_RPC_ATTEMPTS_METADATA_KEY,
|
||||
`${previousAttempts}`
|
||||
);
|
||||
}
|
||||
let receivedMetadata = false;
|
||||
child.start(initialMetadata, {
|
||||
onReceiveMetadata: metadata => {
|
||||
this.trace(
|
||||
'Received metadata from child [' + child.getCallNumber() + ']'
|
||||
);
|
||||
this.commitCall(index);
|
||||
receivedMetadata = true;
|
||||
if (previousAttempts > 0) {
|
||||
metadata.set(
|
||||
PREVIONS_RPC_ATTEMPTS_METADATA_KEY,
|
||||
`${previousAttempts}`
|
||||
);
|
||||
}
|
||||
if (this.underlyingCalls[index].state === 'ACTIVE') {
|
||||
this.listener!.onReceiveMetadata(metadata);
|
||||
}
|
||||
},
|
||||
onReceiveMessage: message => {
|
||||
this.trace(
|
||||
'Received message from child [' + child.getCallNumber() + ']'
|
||||
);
|
||||
this.commitCall(index);
|
||||
if (this.underlyingCalls[index].state === 'ACTIVE') {
|
||||
this.listener!.onReceiveMessage(message);
|
||||
}
|
||||
},
|
||||
onReceiveStatus: status => {
|
||||
this.trace(
|
||||
'Received status from child [' + child.getCallNumber() + ']'
|
||||
);
|
||||
if (!receivedMetadata && previousAttempts > 0) {
|
||||
status.metadata.set(
|
||||
PREVIONS_RPC_ATTEMPTS_METADATA_KEY,
|
||||
`${previousAttempts}`
|
||||
);
|
||||
}
|
||||
this.handleChildStatus(status, index);
|
||||
},
|
||||
});
|
||||
this.sendNextChildMessage(index);
|
||||
if (this.readStarted) {
|
||||
child.startRead();
|
||||
}
|
||||
}
|
||||
|
||||
start(metadata: Metadata, listener: InterceptingListener): void {
|
||||
this.trace('start called');
|
||||
this.listener = listener;
|
||||
this.initialMetadata = metadata;
|
||||
this.attempts += 1;
|
||||
this.startNewAttempt();
|
||||
this.maybeStartHedgingTimer();
|
||||
}
|
||||
|
||||
private handleChildWriteCompleted(childIndex: number) {
|
||||
const childCall = this.underlyingCalls[childIndex];
|
||||
const messageIndex = childCall.nextMessageToSend;
|
||||
this.getBufferEntry(messageIndex).callback?.();
|
||||
this.clearSentMessages();
|
||||
childCall.nextMessageToSend += 1;
|
||||
this.sendNextChildMessage(childIndex);
|
||||
}
|
||||
|
||||
private sendNextChildMessage(childIndex: number) {
|
||||
const childCall = this.underlyingCalls[childIndex];
|
||||
if (childCall.state === 'COMPLETED') {
|
||||
return;
|
||||
}
|
||||
if (this.getBufferEntry(childCall.nextMessageToSend)) {
|
||||
const bufferEntry = this.getBufferEntry(childCall.nextMessageToSend);
|
||||
switch (bufferEntry.entryType) {
|
||||
case 'MESSAGE':
|
||||
childCall.call.sendMessageWithContext(
|
||||
{
|
||||
callback: error => {
|
||||
// Ignore error
|
||||
this.handleChildWriteCompleted(childIndex);
|
||||
},
|
||||
},
|
||||
bufferEntry.message!.message
|
||||
);
|
||||
break;
|
||||
case 'HALF_CLOSE':
|
||||
childCall.nextMessageToSend += 1;
|
||||
childCall.call.halfClose();
|
||||
break;
|
||||
case 'FREED':
|
||||
// Should not be possible
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sendMessageWithContext(context: MessageContext, message: Buffer): void {
|
||||
this.trace('write() called with message of length ' + message.length);
|
||||
const writeObj: WriteObject = {
|
||||
message,
|
||||
flags: context.flags,
|
||||
};
|
||||
const messageIndex = this.getNextBufferIndex();
|
||||
const bufferEntry: WriteBufferEntry = {
|
||||
entryType: 'MESSAGE',
|
||||
message: writeObj,
|
||||
allocated: this.bufferTracker.allocate(message.length, this.callNumber),
|
||||
};
|
||||
this.writeBuffer.push(bufferEntry);
|
||||
if (bufferEntry.allocated) {
|
||||
context.callback?.();
|
||||
for (const [callIndex, call] of this.underlyingCalls.entries()) {
|
||||
if (
|
||||
call.state === 'ACTIVE' &&
|
||||
call.nextMessageToSend === messageIndex
|
||||
) {
|
||||
call.call.sendMessageWithContext(
|
||||
{
|
||||
callback: error => {
|
||||
// Ignore error
|
||||
this.handleChildWriteCompleted(callIndex);
|
||||
},
|
||||
},
|
||||
message
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.commitCallWithMostMessages();
|
||||
// commitCallWithMostMessages can fail if we are between ping attempts
|
||||
if (this.committedCallIndex === null) {
|
||||
return;
|
||||
}
|
||||
const call = this.underlyingCalls[this.committedCallIndex];
|
||||
bufferEntry.callback = context.callback;
|
||||
if (call.state === 'ACTIVE' && call.nextMessageToSend === messageIndex) {
|
||||
call.call.sendMessageWithContext(
|
||||
{
|
||||
callback: error => {
|
||||
// Ignore error
|
||||
this.handleChildWriteCompleted(this.committedCallIndex!);
|
||||
},
|
||||
},
|
||||
message
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
startRead(): void {
|
||||
this.trace('startRead called');
|
||||
this.readStarted = true;
|
||||
for (const underlyingCall of this.underlyingCalls) {
|
||||
if (underlyingCall?.state === 'ACTIVE') {
|
||||
underlyingCall.call.startRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
halfClose(): void {
|
||||
this.trace('halfClose called');
|
||||
const halfCloseIndex = this.getNextBufferIndex();
|
||||
this.writeBuffer.push({
|
||||
entryType: 'HALF_CLOSE',
|
||||
allocated: false,
|
||||
});
|
||||
for (const call of this.underlyingCalls) {
|
||||
if (
|
||||
call?.state === 'ACTIVE' &&
|
||||
call.nextMessageToSend === halfCloseIndex
|
||||
) {
|
||||
call.nextMessageToSend += 1;
|
||||
call.call.halfClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
setCredentials(newCredentials: CallCredentials): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
getMethod(): string {
|
||||
return this.methodName;
|
||||
}
|
||||
getHost(): string {
|
||||
return this.host;
|
||||
}
|
||||
}
|
||||
1022
node_modules/@grpc/grpc-js/src/server-call.ts
generated
vendored
Normal file
1022
node_modules/@grpc/grpc-js/src/server-call.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
108
node_modules/@grpc/grpc-js/src/server-credentials.ts
generated
vendored
Normal file
108
node_modules/@grpc/grpc-js/src/server-credentials.ts
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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 { SecureServerOptions } from 'http2';
|
||||
import { CIPHER_SUITES, getDefaultRootsData } from './tls-helpers';
|
||||
|
||||
export interface KeyCertPair {
|
||||
private_key: Buffer;
|
||||
cert_chain: Buffer;
|
||||
}
|
||||
|
||||
export abstract class ServerCredentials {
|
||||
abstract _isSecure(): boolean;
|
||||
abstract _getSettings(): SecureServerOptions | null;
|
||||
|
||||
static createInsecure(): ServerCredentials {
|
||||
return new InsecureServerCredentials();
|
||||
}
|
||||
|
||||
static createSsl(
|
||||
rootCerts: Buffer | null,
|
||||
keyCertPairs: KeyCertPair[],
|
||||
checkClientCertificate = false
|
||||
): ServerCredentials {
|
||||
if (rootCerts !== null && !Buffer.isBuffer(rootCerts)) {
|
||||
throw new TypeError('rootCerts must be null or a Buffer');
|
||||
}
|
||||
|
||||
if (!Array.isArray(keyCertPairs)) {
|
||||
throw new TypeError('keyCertPairs must be an array');
|
||||
}
|
||||
|
||||
if (typeof checkClientCertificate !== 'boolean') {
|
||||
throw new TypeError('checkClientCertificate must be a boolean');
|
||||
}
|
||||
|
||||
const cert = [];
|
||||
const key = [];
|
||||
|
||||
for (let i = 0; i < keyCertPairs.length; i++) {
|
||||
const pair = keyCertPairs[i];
|
||||
|
||||
if (pair === null || typeof pair !== 'object') {
|
||||
throw new TypeError(`keyCertPair[${i}] must be an object`);
|
||||
}
|
||||
|
||||
if (!Buffer.isBuffer(pair.private_key)) {
|
||||
throw new TypeError(`keyCertPair[${i}].private_key must be a Buffer`);
|
||||
}
|
||||
|
||||
if (!Buffer.isBuffer(pair.cert_chain)) {
|
||||
throw new TypeError(`keyCertPair[${i}].cert_chain must be a Buffer`);
|
||||
}
|
||||
|
||||
cert.push(pair.cert_chain);
|
||||
key.push(pair.private_key);
|
||||
}
|
||||
|
||||
return new SecureServerCredentials({
|
||||
ca: rootCerts || getDefaultRootsData() || undefined,
|
||||
cert,
|
||||
key,
|
||||
requestCert: checkClientCertificate,
|
||||
ciphers: CIPHER_SUITES,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class InsecureServerCredentials extends ServerCredentials {
|
||||
_isSecure(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
_getSettings(): null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class SecureServerCredentials extends ServerCredentials {
|
||||
private options: SecureServerOptions;
|
||||
|
||||
constructor(options: SecureServerOptions) {
|
||||
super();
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
_isSecure(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
_getSettings(): SecureServerOptions {
|
||||
return this.options;
|
||||
}
|
||||
}
|
||||
1300
node_modules/@grpc/grpc-js/src/server.ts
generated
vendored
Normal file
1300
node_modules/@grpc/grpc-js/src/server.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
542
node_modules/@grpc/grpc-js/src/service-config.ts
generated
vendored
Normal file
542
node_modules/@grpc/grpc-js/src/service-config.ts
generated
vendored
Normal file
@@ -0,0 +1,542 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/* This file implements gRFC A2 and the service config spec:
|
||||
* https://github.com/grpc/proposal/blob/master/A2-service-configs-in-dns.md
|
||||
* https://github.com/grpc/grpc/blob/master/doc/service_config.md. Each
|
||||
* function here takes an object with unknown structure and returns its
|
||||
* specific object type if the input has the right structure, and throws an
|
||||
* error otherwise. */
|
||||
|
||||
/* The any type is purposely used here. All functions validate their input at
|
||||
* runtime */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
import * as os from 'os';
|
||||
import { Status } from './constants';
|
||||
import { Duration } from './duration';
|
||||
import {
|
||||
LoadBalancingConfig,
|
||||
validateLoadBalancingConfig,
|
||||
} from './load-balancer';
|
||||
|
||||
export interface MethodConfigName {
|
||||
service?: string;
|
||||
method?: string;
|
||||
}
|
||||
|
||||
export interface RetryPolicy {
|
||||
maxAttempts: number;
|
||||
initialBackoff: string;
|
||||
maxBackoff: string;
|
||||
backoffMultiplier: number;
|
||||
retryableStatusCodes: (Status | string)[];
|
||||
}
|
||||
|
||||
export interface HedgingPolicy {
|
||||
maxAttempts: number;
|
||||
hedgingDelay?: string;
|
||||
nonFatalStatusCodes?: (Status | string)[];
|
||||
}
|
||||
|
||||
export interface MethodConfig {
|
||||
name: MethodConfigName[];
|
||||
waitForReady?: boolean;
|
||||
timeout?: Duration;
|
||||
maxRequestBytes?: number;
|
||||
maxResponseBytes?: number;
|
||||
retryPolicy?: RetryPolicy;
|
||||
hedgingPolicy?: HedgingPolicy;
|
||||
}
|
||||
|
||||
export interface RetryThrottling {
|
||||
maxTokens: number;
|
||||
tokenRatio: number;
|
||||
}
|
||||
|
||||
export interface ServiceConfig {
|
||||
loadBalancingPolicy?: string;
|
||||
loadBalancingConfig: LoadBalancingConfig[];
|
||||
methodConfig: MethodConfig[];
|
||||
retryThrottling?: RetryThrottling;
|
||||
}
|
||||
|
||||
export interface ServiceConfigCanaryConfig {
|
||||
clientLanguage?: string[];
|
||||
percentage?: number;
|
||||
clientHostname?: string[];
|
||||
serviceConfig: ServiceConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recognizes a number with up to 9 digits after the decimal point, followed by
|
||||
* an "s", representing a number of seconds.
|
||||
*/
|
||||
const DURATION_REGEX = /^\d+(\.\d{1,9})?s$/;
|
||||
|
||||
/**
|
||||
* Client language name used for determining whether this client matches a
|
||||
* `ServiceConfigCanaryConfig`'s `clientLanguage` list.
|
||||
*/
|
||||
const CLIENT_LANGUAGE_STRING = 'node';
|
||||
|
||||
function validateName(obj: any): MethodConfigName {
|
||||
// In this context, and unset field and '' are considered the same
|
||||
if ('service' in obj && obj.service !== '') {
|
||||
if (typeof obj.service !== 'string') {
|
||||
throw new Error(
|
||||
`Invalid method config name: invalid service: expected type string, got ${typeof obj.service}`
|
||||
);
|
||||
}
|
||||
if ('method' in obj && obj.method !== '') {
|
||||
if (typeof obj.method !== 'string') {
|
||||
throw new Error(
|
||||
`Invalid method config name: invalid method: expected type string, got ${typeof obj.service}`
|
||||
);
|
||||
}
|
||||
return {
|
||||
service: obj.service,
|
||||
method: obj.method,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
service: obj.service,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
if ('method' in obj && obj.method !== undefined) {
|
||||
throw new Error(
|
||||
`Invalid method config name: method set with empty or unset service`
|
||||
);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function validateRetryPolicy(obj: any): RetryPolicy {
|
||||
if (
|
||||
!('maxAttempts' in obj) ||
|
||||
!Number.isInteger(obj.maxAttempts) ||
|
||||
obj.maxAttempts < 2
|
||||
) {
|
||||
throw new Error(
|
||||
'Invalid method config retry policy: maxAttempts must be an integer at least 2'
|
||||
);
|
||||
}
|
||||
if (
|
||||
!('initialBackoff' in obj) ||
|
||||
typeof obj.initialBackoff !== 'string' ||
|
||||
!DURATION_REGEX.test(obj.initialBackoff)
|
||||
) {
|
||||
throw new Error(
|
||||
'Invalid method config retry policy: initialBackoff must be a string consisting of a positive integer followed by s'
|
||||
);
|
||||
}
|
||||
if (
|
||||
!('maxBackoff' in obj) ||
|
||||
typeof obj.maxBackoff !== 'string' ||
|
||||
!DURATION_REGEX.test(obj.maxBackoff)
|
||||
) {
|
||||
throw new Error(
|
||||
'Invalid method config retry policy: maxBackoff must be a string consisting of a positive integer followed by s'
|
||||
);
|
||||
}
|
||||
if (
|
||||
!('backoffMultiplier' in obj) ||
|
||||
typeof obj.backoffMultiplier !== 'number' ||
|
||||
obj.backoffMultiplier <= 0
|
||||
) {
|
||||
throw new Error(
|
||||
'Invalid method config retry policy: backoffMultiplier must be a number greater than 0'
|
||||
);
|
||||
}
|
||||
if (
|
||||
!('retryableStatusCodes' in obj && Array.isArray(obj.retryableStatusCodes))
|
||||
) {
|
||||
throw new Error(
|
||||
'Invalid method config retry policy: retryableStatusCodes is required'
|
||||
);
|
||||
}
|
||||
if (obj.retryableStatusCodes.length === 0) {
|
||||
throw new Error(
|
||||
'Invalid method config retry policy: retryableStatusCodes must be non-empty'
|
||||
);
|
||||
}
|
||||
for (const value of obj.retryableStatusCodes) {
|
||||
if (typeof value === 'number') {
|
||||
if (!Object.values(Status).includes(value)) {
|
||||
throw new Error(
|
||||
'Invalid method config retry policy: retryableStatusCodes value not in status code range'
|
||||
);
|
||||
}
|
||||
} else if (typeof value === 'string') {
|
||||
if (!Object.values(Status).includes(value.toUpperCase())) {
|
||||
throw new Error(
|
||||
'Invalid method config retry policy: retryableStatusCodes value not a status code name'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
'Invalid method config retry policy: retryableStatusCodes value must be a string or number'
|
||||
);
|
||||
}
|
||||
}
|
||||
return {
|
||||
maxAttempts: obj.maxAttempts,
|
||||
initialBackoff: obj.initialBackoff,
|
||||
maxBackoff: obj.maxBackoff,
|
||||
backoffMultiplier: obj.backoffMultiplier,
|
||||
retryableStatusCodes: obj.retryableStatusCodes,
|
||||
};
|
||||
}
|
||||
|
||||
function validateHedgingPolicy(obj: any): HedgingPolicy {
|
||||
if (
|
||||
!('maxAttempts' in obj) ||
|
||||
!Number.isInteger(obj.maxAttempts) ||
|
||||
obj.maxAttempts < 2
|
||||
) {
|
||||
throw new Error(
|
||||
'Invalid method config hedging policy: maxAttempts must be an integer at least 2'
|
||||
);
|
||||
}
|
||||
if (
|
||||
'hedgingDelay' in obj &&
|
||||
(typeof obj.hedgingDelay !== 'string' ||
|
||||
!DURATION_REGEX.test(obj.hedgingDelay))
|
||||
) {
|
||||
throw new Error(
|
||||
'Invalid method config hedging policy: hedgingDelay must be a string consisting of a positive integer followed by s'
|
||||
);
|
||||
}
|
||||
if ('nonFatalStatusCodes' in obj && Array.isArray(obj.nonFatalStatusCodes)) {
|
||||
for (const value of obj.nonFatalStatusCodes) {
|
||||
if (typeof value === 'number') {
|
||||
if (!Object.values(Status).includes(value)) {
|
||||
throw new Error(
|
||||
'Invlid method config hedging policy: nonFatalStatusCodes value not in status code range'
|
||||
);
|
||||
}
|
||||
} else if (typeof value === 'string') {
|
||||
if (!Object.values(Status).includes(value.toUpperCase())) {
|
||||
throw new Error(
|
||||
'Invlid method config hedging policy: nonFatalStatusCodes value not a status code name'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
'Invlid method config hedging policy: nonFatalStatusCodes value must be a string or number'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
const result: HedgingPolicy = {
|
||||
maxAttempts: obj.maxAttempts,
|
||||
};
|
||||
if (obj.hedgingDelay) {
|
||||
result.hedgingDelay = obj.hedgingDelay;
|
||||
}
|
||||
if (obj.nonFatalStatusCodes) {
|
||||
result.nonFatalStatusCodes = obj.nonFatalStatusCodes;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function validateMethodConfig(obj: any): MethodConfig {
|
||||
const result: MethodConfig = {
|
||||
name: [],
|
||||
};
|
||||
if (!('name' in obj) || !Array.isArray(obj.name)) {
|
||||
throw new Error('Invalid method config: invalid name array');
|
||||
}
|
||||
for (const name of obj.name) {
|
||||
result.name.push(validateName(name));
|
||||
}
|
||||
if ('waitForReady' in obj) {
|
||||
if (typeof obj.waitForReady !== 'boolean') {
|
||||
throw new Error('Invalid method config: invalid waitForReady');
|
||||
}
|
||||
result.waitForReady = obj.waitForReady;
|
||||
}
|
||||
if ('timeout' in obj) {
|
||||
if (typeof obj.timeout === 'object') {
|
||||
if (
|
||||
!('seconds' in obj.timeout) ||
|
||||
!(typeof obj.timeout.seconds === 'number')
|
||||
) {
|
||||
throw new Error('Invalid method config: invalid timeout.seconds');
|
||||
}
|
||||
if (
|
||||
!('nanos' in obj.timeout) ||
|
||||
!(typeof obj.timeout.nanos === 'number')
|
||||
) {
|
||||
throw new Error('Invalid method config: invalid timeout.nanos');
|
||||
}
|
||||
result.timeout = obj.timeout;
|
||||
} else if (
|
||||
typeof obj.timeout === 'string' &&
|
||||
DURATION_REGEX.test(obj.timeout)
|
||||
) {
|
||||
const timeoutParts = obj.timeout
|
||||
.substring(0, obj.timeout.length - 1)
|
||||
.split('.');
|
||||
result.timeout = {
|
||||
seconds: timeoutParts[0] | 0,
|
||||
nanos: (timeoutParts[1] ?? 0) | 0,
|
||||
};
|
||||
} else {
|
||||
throw new Error('Invalid method config: invalid timeout');
|
||||
}
|
||||
}
|
||||
if ('maxRequestBytes' in obj) {
|
||||
if (typeof obj.maxRequestBytes !== 'number') {
|
||||
throw new Error('Invalid method config: invalid maxRequestBytes');
|
||||
}
|
||||
result.maxRequestBytes = obj.maxRequestBytes;
|
||||
}
|
||||
if ('maxResponseBytes' in obj) {
|
||||
if (typeof obj.maxResponseBytes !== 'number') {
|
||||
throw new Error('Invalid method config: invalid maxRequestBytes');
|
||||
}
|
||||
result.maxResponseBytes = obj.maxResponseBytes;
|
||||
}
|
||||
if ('retryPolicy' in obj) {
|
||||
if ('hedgingPolicy' in obj) {
|
||||
throw new Error(
|
||||
'Invalid method config: retryPolicy and hedgingPolicy cannot both be specified'
|
||||
);
|
||||
} else {
|
||||
result.retryPolicy = validateRetryPolicy(obj.retryPolicy);
|
||||
}
|
||||
} else if ('hedgingPolicy' in obj) {
|
||||
result.hedgingPolicy = validateHedgingPolicy(obj.hedgingPolicy);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function validateRetryThrottling(obj: any): RetryThrottling {
|
||||
if (
|
||||
!('maxTokens' in obj) ||
|
||||
typeof obj.maxTokens !== 'number' ||
|
||||
obj.maxTokens <= 0 ||
|
||||
obj.maxTokens > 1000
|
||||
) {
|
||||
throw new Error(
|
||||
'Invalid retryThrottling: maxTokens must be a number in (0, 1000]'
|
||||
);
|
||||
}
|
||||
if (
|
||||
!('tokenRatio' in obj) ||
|
||||
typeof obj.tokenRatio !== 'number' ||
|
||||
obj.tokenRatio <= 0
|
||||
) {
|
||||
throw new Error(
|
||||
'Invalid retryThrottling: tokenRatio must be a number greater than 0'
|
||||
);
|
||||
}
|
||||
return {
|
||||
maxTokens: +(obj.maxTokens as number).toFixed(3),
|
||||
tokenRatio: +(obj.tokenRatio as number).toFixed(3),
|
||||
};
|
||||
}
|
||||
|
||||
export function validateServiceConfig(obj: any): ServiceConfig {
|
||||
const result: ServiceConfig = {
|
||||
loadBalancingConfig: [],
|
||||
methodConfig: [],
|
||||
};
|
||||
if ('loadBalancingPolicy' in obj) {
|
||||
if (typeof obj.loadBalancingPolicy === 'string') {
|
||||
result.loadBalancingPolicy = obj.loadBalancingPolicy;
|
||||
} else {
|
||||
throw new Error('Invalid service config: invalid loadBalancingPolicy');
|
||||
}
|
||||
}
|
||||
if ('loadBalancingConfig' in obj) {
|
||||
if (Array.isArray(obj.loadBalancingConfig)) {
|
||||
for (const config of obj.loadBalancingConfig) {
|
||||
result.loadBalancingConfig.push(validateLoadBalancingConfig(config));
|
||||
}
|
||||
} else {
|
||||
throw new Error('Invalid service config: invalid loadBalancingConfig');
|
||||
}
|
||||
}
|
||||
if ('methodConfig' in obj) {
|
||||
if (Array.isArray(obj.methodConfig)) {
|
||||
for (const methodConfig of obj.methodConfig) {
|
||||
result.methodConfig.push(validateMethodConfig(methodConfig));
|
||||
}
|
||||
}
|
||||
}
|
||||
if ('retryThrottling' in obj) {
|
||||
result.retryThrottling = validateRetryThrottling(obj.retryThrottling);
|
||||
}
|
||||
// Validate method name uniqueness
|
||||
const seenMethodNames: MethodConfigName[] = [];
|
||||
for (const methodConfig of result.methodConfig) {
|
||||
for (const name of methodConfig.name) {
|
||||
for (const seenName of seenMethodNames) {
|
||||
if (
|
||||
name.service === seenName.service &&
|
||||
name.method === seenName.method
|
||||
) {
|
||||
throw new Error(
|
||||
`Invalid service config: duplicate name ${name.service}/${name.method}`
|
||||
);
|
||||
}
|
||||
}
|
||||
seenMethodNames.push(name);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function validateCanaryConfig(obj: any): ServiceConfigCanaryConfig {
|
||||
if (!('serviceConfig' in obj)) {
|
||||
throw new Error('Invalid service config choice: missing service config');
|
||||
}
|
||||
const result: ServiceConfigCanaryConfig = {
|
||||
serviceConfig: validateServiceConfig(obj.serviceConfig),
|
||||
};
|
||||
if ('clientLanguage' in obj) {
|
||||
if (Array.isArray(obj.clientLanguage)) {
|
||||
result.clientLanguage = [];
|
||||
for (const lang of obj.clientLanguage) {
|
||||
if (typeof lang === 'string') {
|
||||
result.clientLanguage.push(lang);
|
||||
} else {
|
||||
throw new Error(
|
||||
'Invalid service config choice: invalid clientLanguage'
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new Error('Invalid service config choice: invalid clientLanguage');
|
||||
}
|
||||
}
|
||||
if ('clientHostname' in obj) {
|
||||
if (Array.isArray(obj.clientHostname)) {
|
||||
result.clientHostname = [];
|
||||
for (const lang of obj.clientHostname) {
|
||||
if (typeof lang === 'string') {
|
||||
result.clientHostname.push(lang);
|
||||
} else {
|
||||
throw new Error(
|
||||
'Invalid service config choice: invalid clientHostname'
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new Error('Invalid service config choice: invalid clientHostname');
|
||||
}
|
||||
}
|
||||
if ('percentage' in obj) {
|
||||
if (
|
||||
typeof obj.percentage === 'number' &&
|
||||
0 <= obj.percentage &&
|
||||
obj.percentage <= 100
|
||||
) {
|
||||
result.percentage = obj.percentage;
|
||||
} else {
|
||||
throw new Error('Invalid service config choice: invalid percentage');
|
||||
}
|
||||
}
|
||||
// Validate that no unexpected fields are present
|
||||
const allowedFields = [
|
||||
'clientLanguage',
|
||||
'percentage',
|
||||
'clientHostname',
|
||||
'serviceConfig',
|
||||
];
|
||||
for (const field in obj) {
|
||||
if (!allowedFields.includes(field)) {
|
||||
throw new Error(
|
||||
`Invalid service config choice: unexpected field ${field}`
|
||||
);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function validateAndSelectCanaryConfig(
|
||||
obj: any,
|
||||
percentage: number
|
||||
): ServiceConfig {
|
||||
if (!Array.isArray(obj)) {
|
||||
throw new Error('Invalid service config list');
|
||||
}
|
||||
for (const config of obj) {
|
||||
const validatedConfig = validateCanaryConfig(config);
|
||||
/* For each field, we check if it is present, then only discard the
|
||||
* config if the field value does not match the current client */
|
||||
if (
|
||||
typeof validatedConfig.percentage === 'number' &&
|
||||
percentage > validatedConfig.percentage
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if (Array.isArray(validatedConfig.clientHostname)) {
|
||||
let hostnameMatched = false;
|
||||
for (const hostname of validatedConfig.clientHostname) {
|
||||
if (hostname === os.hostname()) {
|
||||
hostnameMatched = true;
|
||||
}
|
||||
}
|
||||
if (!hostnameMatched) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (Array.isArray(validatedConfig.clientLanguage)) {
|
||||
let languageMatched = false;
|
||||
for (const language of validatedConfig.clientLanguage) {
|
||||
if (language === CLIENT_LANGUAGE_STRING) {
|
||||
languageMatched = true;
|
||||
}
|
||||
}
|
||||
if (!languageMatched) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return validatedConfig.serviceConfig;
|
||||
}
|
||||
throw new Error('No matching service config found');
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the "grpc_config" record among the TXT records, parse its value as JSON, validate its contents,
|
||||
* and select a service config with selection fields that all match this client. Most of these steps
|
||||
* can fail with an error; the caller must handle any errors thrown this way.
|
||||
* @param txtRecord The TXT record array that is output from a successful call to dns.resolveTxt
|
||||
* @param percentage A number chosen from the range [0, 100) that is used to select which config to use
|
||||
* @return The service configuration to use, given the percentage value, or null if the service config
|
||||
* data has a valid format but none of the options match the current client.
|
||||
*/
|
||||
export function extractAndSelectServiceConfig(
|
||||
txtRecord: string[][],
|
||||
percentage: number
|
||||
): ServiceConfig | null {
|
||||
for (const record of txtRecord) {
|
||||
if (record.length > 0 && record[0].startsWith('grpc_config=')) {
|
||||
/* Treat the list of strings in this record as a single string and remove
|
||||
* "grpc_config=" from the beginning. The rest should be a JSON string */
|
||||
const recordString = record.join('').substring('grpc_config='.length);
|
||||
const recordJson: any = JSON.parse(recordString);
|
||||
return validateAndSelectCanaryConfig(recordJson, percentage);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
80
node_modules/@grpc/grpc-js/src/status-builder.ts
generated
vendored
Normal file
80
node_modules/@grpc/grpc-js/src/status-builder.ts
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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 { StatusObject } from './call-interface';
|
||||
import { Status } from './constants';
|
||||
import { Metadata } from './metadata';
|
||||
|
||||
/**
|
||||
* A builder for gRPC status objects.
|
||||
*/
|
||||
export class StatusBuilder {
|
||||
private code: Status | null;
|
||||
private details: string | null;
|
||||
private metadata: Metadata | null;
|
||||
|
||||
constructor() {
|
||||
this.code = null;
|
||||
this.details = null;
|
||||
this.metadata = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a status code to the builder.
|
||||
*/
|
||||
withCode(code: Status): this {
|
||||
this.code = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds details to the builder.
|
||||
*/
|
||||
withDetails(details: string): this {
|
||||
this.details = details;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds metadata to the builder.
|
||||
*/
|
||||
withMetadata(metadata: Metadata): this {
|
||||
this.metadata = metadata;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the status object.
|
||||
*/
|
||||
build(): Partial<StatusObject> {
|
||||
const status: Partial<StatusObject> = {};
|
||||
|
||||
if (this.code !== null) {
|
||||
status.code = this.code;
|
||||
}
|
||||
|
||||
if (this.details !== null) {
|
||||
status.details = this.details;
|
||||
}
|
||||
|
||||
if (this.metadata !== null) {
|
||||
status.metadata = this.metadata;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
}
|
||||
110
node_modules/@grpc/grpc-js/src/stream-decoder.ts
generated
vendored
Normal file
110
node_modules/@grpc/grpc-js/src/stream-decoder.ts
generated
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
enum ReadState {
|
||||
NO_DATA,
|
||||
READING_SIZE,
|
||||
READING_MESSAGE,
|
||||
}
|
||||
|
||||
export class StreamDecoder {
|
||||
private readState: ReadState = ReadState.NO_DATA;
|
||||
private readCompressFlag: Buffer = Buffer.alloc(1);
|
||||
private readPartialSize: Buffer = Buffer.alloc(4);
|
||||
private readSizeRemaining = 4;
|
||||
private readMessageSize = 0;
|
||||
private readPartialMessage: Buffer[] = [];
|
||||
private readMessageRemaining = 0;
|
||||
|
||||
constructor(private maxReadMessageLength: number) {}
|
||||
|
||||
write(data: Buffer): Buffer[] {
|
||||
let readHead = 0;
|
||||
let toRead: number;
|
||||
const result: Buffer[] = [];
|
||||
|
||||
while (readHead < data.length) {
|
||||
switch (this.readState) {
|
||||
case ReadState.NO_DATA:
|
||||
this.readCompressFlag = data.slice(readHead, readHead + 1);
|
||||
readHead += 1;
|
||||
this.readState = ReadState.READING_SIZE;
|
||||
this.readPartialSize.fill(0);
|
||||
this.readSizeRemaining = 4;
|
||||
this.readMessageSize = 0;
|
||||
this.readMessageRemaining = 0;
|
||||
this.readPartialMessage = [];
|
||||
break;
|
||||
case ReadState.READING_SIZE:
|
||||
toRead = Math.min(data.length - readHead, this.readSizeRemaining);
|
||||
data.copy(
|
||||
this.readPartialSize,
|
||||
4 - this.readSizeRemaining,
|
||||
readHead,
|
||||
readHead + toRead
|
||||
);
|
||||
this.readSizeRemaining -= toRead;
|
||||
readHead += toRead;
|
||||
// readSizeRemaining >=0 here
|
||||
if (this.readSizeRemaining === 0) {
|
||||
this.readMessageSize = this.readPartialSize.readUInt32BE(0);
|
||||
if (this.maxReadMessageLength !== -1 && this.readMessageSize > this.maxReadMessageLength) {
|
||||
throw new Error(`Received message larger than max (${this.readMessageSize} vs ${this.maxReadMessageLength})`);
|
||||
}
|
||||
this.readMessageRemaining = this.readMessageSize;
|
||||
if (this.readMessageRemaining > 0) {
|
||||
this.readState = ReadState.READING_MESSAGE;
|
||||
} else {
|
||||
const message = Buffer.concat(
|
||||
[this.readCompressFlag, this.readPartialSize],
|
||||
5
|
||||
);
|
||||
|
||||
this.readState = ReadState.NO_DATA;
|
||||
result.push(message);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ReadState.READING_MESSAGE:
|
||||
toRead = Math.min(data.length - readHead, this.readMessageRemaining);
|
||||
this.readPartialMessage.push(data.slice(readHead, readHead + toRead));
|
||||
this.readMessageRemaining -= toRead;
|
||||
readHead += toRead;
|
||||
// readMessageRemaining >=0 here
|
||||
if (this.readMessageRemaining === 0) {
|
||||
// At this point, we have read a full message
|
||||
const framedMessageBuffers = [
|
||||
this.readCompressFlag,
|
||||
this.readPartialSize,
|
||||
].concat(this.readPartialMessage);
|
||||
const framedMessage = Buffer.concat(
|
||||
framedMessageBuffers,
|
||||
this.readMessageSize + 5
|
||||
);
|
||||
|
||||
this.readState = ReadState.NO_DATA;
|
||||
result.push(framedMessage);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unexpected read state');
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
88
node_modules/@grpc/grpc-js/src/subchannel-address.ts
generated
vendored
Normal file
88
node_modules/@grpc/grpc-js/src/subchannel-address.ts
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2021 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 { isIP } from 'net';
|
||||
|
||||
export interface TcpSubchannelAddress {
|
||||
port: number;
|
||||
host: string;
|
||||
}
|
||||
|
||||
export interface IpcSubchannelAddress {
|
||||
path: string;
|
||||
}
|
||||
/**
|
||||
* This represents a single backend address to connect to. This interface is a
|
||||
* subset of net.SocketConnectOpts, i.e. the options described at
|
||||
* https://nodejs.org/api/net.html#net_socket_connect_options_connectlistener.
|
||||
* Those are in turn a subset of the options that can be passed to http2.connect.
|
||||
*/
|
||||
|
||||
export type SubchannelAddress = TcpSubchannelAddress | IpcSubchannelAddress;
|
||||
|
||||
export function isTcpSubchannelAddress(
|
||||
address: SubchannelAddress
|
||||
): address is TcpSubchannelAddress {
|
||||
return 'port' in address;
|
||||
}
|
||||
|
||||
export function subchannelAddressEqual(
|
||||
address1?: SubchannelAddress,
|
||||
address2?: SubchannelAddress
|
||||
): boolean {
|
||||
if (!address1 && !address2) {
|
||||
return true;
|
||||
}
|
||||
if (!address1 || !address2) {
|
||||
return false;
|
||||
}
|
||||
if (isTcpSubchannelAddress(address1)) {
|
||||
return (
|
||||
isTcpSubchannelAddress(address2) &&
|
||||
address1.host === address2.host &&
|
||||
address1.port === address2.port
|
||||
);
|
||||
} else {
|
||||
return !isTcpSubchannelAddress(address2) && address1.path === address2.path;
|
||||
}
|
||||
}
|
||||
|
||||
export function subchannelAddressToString(address: SubchannelAddress): string {
|
||||
if (isTcpSubchannelAddress(address)) {
|
||||
return address.host + ':' + address.port;
|
||||
} else {
|
||||
return address.path;
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_PORT = 443;
|
||||
|
||||
export function stringToSubchannelAddress(
|
||||
addressString: string,
|
||||
port?: number
|
||||
): SubchannelAddress {
|
||||
if (isIP(addressString)) {
|
||||
return {
|
||||
host: addressString,
|
||||
port: port ?? DEFAULT_PORT,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
path: addressString,
|
||||
};
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user