summaryrefslogtreecommitdiff
path: root/frontend-old/node_modules/@grpc/grpc-js/src/channelz.ts
diff options
context:
space:
mode:
Diffstat (limited to 'frontend-old/node_modules/@grpc/grpc-js/src/channelz.ts')
-rw-r--r--frontend-old/node_modules/@grpc/grpc-js/src/channelz.ts886
1 files changed, 886 insertions, 0 deletions
diff --git a/frontend-old/node_modules/@grpc/grpc-js/src/channelz.ts b/frontend-old/node_modules/@grpc/grpc-js/src/channelz.ts
new file mode 100644
index 0000000..1e2627a
--- /dev/null
+++ b/frontend-old/node_modules/@grpc/grpc-js/src/channelz.ts
@@ -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);
+}