Compare commits

...

6 Commits
main ... xh

@ -0,0 +1,3 @@
{
"CurrentProjectSetting": null
}

@ -0,0 +1,6 @@
{
"ExpandedNodes": [
""
],
"PreviewInSolutionExplorer": false
}

Binary file not shown.

@ -0,0 +1,12 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\11855\\Desktop\\\u674E\u5B66\u6210\\lvgl\\",
"Documents": [],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": []
}
]
}

Binary file not shown.

@ -0,0 +1,39 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
.kotlin
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

@ -0,0 +1,3 @@
# 默认忽略的文件
/shelf/
/workspace.xml

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" native2AsciiForPropertiesFiles="true" defaultCharsetForPropertiesFiles="UTF-8">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
<option name="ignoredFiles">
<set>
<option value="$PROJECT_DIR$/src/pom.xml" />
</set>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="openjdk-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

@ -0,0 +1,37 @@
// 在后端API中使用
const FabricClient = require('./fabric-client');
// 在评论发布API中调用
app.post('/api/reviews', async (req, res) => {
const { content, userId, spotId } = req.body;
const reviewId = `review_${Date.now()}`;
const fabricClient = new FabricClient();
const result = await fabricClient.submitReview(reviewId, userId, spotId, content);
if (result.success) {
// 同时将评论原文存入传统数据库
await saveToDatabase({
reviewId,
userId,
spotId,
content,
blockchainHash: result.hash,
timestamp: new Date()
});
res.json({ success: true, reviewId, blockchainTx: result });
} else {
res.status(500).json({ success: false, error: result.error });
}
});
// 验证评论API
app.get('/api/reviews/:reviewId/verify', async (req, res) => {
const { reviewId } = req.params;
const fabricClient = new FabricClient();
const result = await fabricClient.verifyReview(reviewId);
res.json(result);
});

@ -0,0 +1,18 @@
# Fabric 后端连接材料
## 文件说明
- `connection.yaml` - 网络连接配置文件
- `wallet/` - 身份证书目录
- `cert.pem` - 管理员证书(由 Admin@org1.example.com-cert.pem 重命名)
- `priv_sk` - 管理员私钥
- `organizations/` - TLS证书目录用于安全连接
## 使用方式
1. 将整个 backend-materials 目录放在后端项目根目录
2. 在代码中引用相应的证书文件
3. 确保连接配置文件中的路径指向正确的证书位置
## 安全提醒
- 私钥文件 `priv_sk` 需要妥善保管
- 不要将私钥提交到版本控制系统
- 生产环境需要重新生成证书

@ -0,0 +1,44 @@
name: "test-network"
version: "1.0.0"
client:
organization: Org1
connection:
timeout:
peer:
endorser: 300
orderer: 300
organizations:
Org1:
mspid: Org1MSP
peers:
- peer0.org1.example.com
peers:
peer0.org1.example.com:
url: grpc://localhost:7051
grpcOptions:
ssl-target-name-override: peer0.org1.example.com
hostnameOverride: peer0.org1.example.com
tlsCACerts:
path: /home/xwc/go/src/github.com/Pluto200407/fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
orderers:
orderer.example.com:
url: grpc://localhost:7050
grpcOptions:
ssl-target-name-override: orderer.example.com
hostnameOverride: orderer.example.com
tlsCACerts:
path: /home/xwc/go/src/github.com/Pluto200407/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/ca.crt
channels:
mychannel:
orderers:
- orderer.example.com
peers:
peer0.org1.example.com:
endorsingPeer: true
chaincodeQuery: true
ledgerQuery: true
eventSource: true

@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE-----
MIICKDCCAc+gAwIBAgIQK9NVfDZOlmBcVyjUiapiUTAKBggqhkjOPQQDAjBzMQsw
CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy
YW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEcMBoGA1UEAxMTY2Eu
b3JnMS5leGFtcGxlLmNvbTAeFw0yNTEwMjIxMzIxMDBaFw0zNTEwMjAxMzIxMDBa
MGsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1T
YW4gRnJhbmNpc2NvMQ4wDAYDVQQLEwVhZG1pbjEfMB0GA1UEAwwWQWRtaW5Ab3Jn
MS5leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABEaa37gepfav
SVPEKe32Ht1wSKIC7hdgnsunTApTPzSlbIzm2nzMKa9D+F/LQpfis3EOP2papkwZ
cAvgQ03SAHqjTTBLMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMCsGA1Ud
IwQkMCKAIAYX4WiIwuqeErf3mPxfSCQ2wO0+BvyqHqJXWM0Z7CJaMAoGCCqGSM49
BAMCA0cAMEQCIFT05wSs1cG9F/6HTjirylLh5wWJKomVCAJrZPT6v8uQAiBU1GPx
3dw1Tb1UoWph4E6Dl2MQfnR0SotQNYzDuWmSdQ==
-----END CERTIFICATE-----

@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg8w7OdT3ZqZvP/CEB
gkRenrKJClD0L5+s5DXRodoUQBihRANCAARGmt+4HqX2r0lTxCnt9h7dcEiiAu4X
YJ7Lp0wKUz80pWyM5tp8zCmvQ/hfy0KX4rNxDj9qWqZMGXAL4ENN0gB6
-----END PRIVATE KEY-----

@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIICWTCCAf6gAwIBAgIRANHzUZ6pt3kNGhlU1P+gjSowCgYIKoZIzj0EAwIwdjEL
MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG
cmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHzAdBgNVBAMTFnRs
c2NhLm9yZzEuZXhhbXBsZS5jb20wHhcNMjUxMDIyMTIzOTAwWhcNMzUxMDIwMTIz
OTAwWjB2MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UE
BxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEfMB0G
A1UEAxMWdGxzY2Eub3JnMS5leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49
AwEHA0IABEWENkbWGmSWJwn0s8KCaObbYAUy7tmkrzYHnx26bAxAIA6sYD+Gzp9y
NmWfnZwhAn9SIqBnZjdlU0tG3f/zaRKjbTBrMA4GA1UdDwEB/wQEAwIBpjAdBgNV
HSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB/zApBgNV
HQ4EIgQg0J5oiI1lZ8QZss91AC5j5+Fh7s0AHK1Nc1Rcr37+GxIwCgYIKoZIzj0E
AwIDSQAwRgIhAPn6z/yWWuS3d47MESUG/VjAbWKNyRVjXRwh7gBwvS48AiEAzS9I
d7EV6hDpVcaaiBgQhTJE0IAMoyv6bSJaXZcKkd4=
-----END CERTIFICATE-----

@ -0,0 +1,267 @@
const { Gateway, Wallets } = require('fabric-network');
const fs = require('fs');
const path = require('path');
const yaml = require('js-yaml');
class FabricClient {
constructor(networkIp, channelName, chaincodeName, walletPath) {
this.gateway = new Gateway();
this.connected = false;
this.networkIp = networkIp;
this.channelName = channelName;
this.chaincodeName = chaincodeName;
// 修复1标准化Windows路径处理反斜杠和空格问题
this.walletPath = path.resolve(walletPath); // 关键:将传入的路径转为绝对路径
}
async connect() {
try {
// 1. 读取连接配置文件(支持绝对路径和相对路径)
let ccpPath;
// 检查传入的是否为绝对路径,否则按相对路径处理
if (path.isAbsolute('backend-materials/connection.yaml')) {
ccpPath = 'backend-materials/connection.yaml';
} else {
ccpPath = path.resolve(__dirname, 'backend-materials/connection.yaml');
}
if (!fs.existsSync(ccpPath)) {
throw new Error(`连接配置文件不存在: ${ccpPath}\n请确认backend-materials文件夹是否在项目根目录`);
}
let ccp = yaml.load(fs.readFileSync(ccpPath, 'utf8'));
// 2. 替换connection.yaml中的IP支持带端口的IP如192.168.170.121:7051
if (this.networkIp) {
// 提取IP去掉可能的端口
const ip = this.networkIp.split(':')[0];
// 替换Peer节点
if (ccp.organizations.Org1?.peers) {
ccp.organizations.Org1.peers.forEach(peer => {
if (ccp.peers[peer]) {
ccp.peers[peer].url = ccp.peers[peer].url.replace('localhost', ip);
}
});
}
// 替换Orderer节点
if (ccp.orderers) {
Object.keys(ccp.orderers).forEach(orderer => {
ccp.orderers[orderer].url = ccp.orderers[orderer].url.replace('localhost', ip);
});
}
}
// 3. 验证并加载证书(增加详细错误提示)
if (!fs.existsSync(this.walletPath)) {
throw new Error(`证书目录不存在: ${this.walletPath}\n请检查路径是否包含空格或特殊字符`);
}
const wallet = await Wallets.newFileSystemWallet(this.walletPath);
const identityLabel = 'admin';
const identityExists = await wallet.get(identityLabel);
if (!identityExists) {
// 检查目录下是否有证书文件
const certPath = path.join(this.walletPath, 'cert.pem');
const keyPath = path.join(this.walletPath, 'priv_sk');
const hasCert = fs.existsSync(certPath);
const hasKey = fs.existsSync(keyPath);
throw new Error(`证书不存在(标识: ${identityLabel}\n` +
`目录: ${this.walletPath}\n` +
`cert.pem存在: ${hasCert}\n` +
`priv_sk存在: ${hasKey}`);
}
// 4. 连接到区块链网络(优化配置)
await this.gateway.connect(ccp, {
wallet,
identity: identityLabel,
discovery: {
enabled: true,
asLocalhost: this.networkIp.includes('localhost') || this.networkIp.includes('127.0.0.1')
} // 本地节点自动适配
});
this.connected = true;
console.log('成功连接到Fabric网络');
} catch (error) {
console.error(`连接失败: ${error.message}`);
throw error;
}
}
// 提交评论到区块链
async submitReview(reviewId, userId, spotId, content) {
if (!this.connected) {
await this.connect();
}
try {
const network = await this.gateway.getNetwork(this.channelName);
const contract = network.getContract(this.chaincodeName);
// 计算哈希值(处理空内容)
const crypto = require('crypto');
const hashValue = crypto.createHash('sha256')
.update(content || '') // 避免空内容报错
.digest('hex');
// 调用链码(增加参数校验)
if (!reviewId || !userId || !spotId) {
throw new Error('评论ID、用户ID、景点ID不能为空');
}
const transactionResponse = await contract.submitTransaction(
'SubmitReview',
reviewId,
userId,
spotId,
hashValue
);
console.log(`评论 ${reviewId} 已成功提交到区块链`);
return {
success: true,
reviewId: reviewId,
transactionId: transactionResponse.toString(),
hash: hashValue,
message: '评论已成功提交到区块链'
};
} catch (error) {
console.error(`提交评论失败: ${error.message}`);
return {
success: false,
error: error.message
};
}
}
// 验证区块链上的评论
async verifyReview(reviewId) {
if (!this.connected) {
await this.connect();
}
try {
if (!reviewId) {
throw new Error('评论ID不能为空');
}
const network = await this.gateway.getNetwork(this.channelName);
const contract = network.getContract(this.chaincodeName);
const result = await contract.evaluateTransaction('VerifyReview', reviewId);
const reviewInfo = JSON.parse(result.toString());
return {
success: true,
verified: true,
review: reviewInfo
};
} catch (error) {
console.error(`验证评论失败: ${error.message}`);
return {
success: false,
verified: false,
error: error.message
};
}
}
// 获取区块链上的所有评论
async getAllReviews() {
if (!this.connected) {
await this.connect();
}
try {
const network = await this.gateway.getNetwork(this.channelName);
const contract = network.getContract(this.chaincodeName);
const result = await contract.evaluateTransaction('GetAllReviews');
const reviews = JSON.parse(result.toString());
return {
success: true,
reviews: reviews
};
} catch (error) {
console.error(`获取评论列表失败: ${error.message}`);
return {
success: false,
error: error.message
};
}
}
disconnect() {
if (this.connected) {
this.gateway.disconnect();
this.connected = false;
console.log('已断开Fabric连接');
}
}
}
// 命令行调用入口(修复参数解析顺序)
async function main() {
const args = process.argv.slice(2);
if (args.length < 1) {
console.error('用法: node fabric-client.js <操作类型> <networkIp> <channelName> <chaincodeName> <walletPath> [业务参数...]');
console.error('示例: node fabric-client.js submitReview 192.168.170.121 mychannel review "C:\\path\\to\\wallet" review1 user1 spot1 "好评"');
process.exit(1);
}
const operation = args[0];
// 修复2严格按照顺序解析参数operation, networkIp, channelName, chaincodeName, walletPath
if (args.length < 5) {
console.error('参数不足至少需要操作类型、networkIp、channelName、chaincodeName、walletPath');
process.exit(1);
}
const [networkIp, channelName, chaincodeName, walletPath] = args.slice(1, 5);
const fabricClient = new FabricClient(networkIp, channelName, chaincodeName, walletPath);
try {
switch (operation) {
case 'submitReview': {
if (args.length < 9) {
console.error('submitReview需要参数reviewId, userId, spotId, content');
process.exit(1);
}
const [reviewId, userId, spotId, content] = args.slice(5, 9);
const result = await fabricClient.submitReview(reviewId, userId, spotId, content);
console.log(JSON.stringify(result));
break;
}
case 'verifyReview': {
if (args.length < 6) {
console.error('verifyReview需要参数reviewId');
process.exit(1);
}
const [reviewId] = args.slice(5, 6);
const result = await fabricClient.verifyReview(reviewId);
console.log(JSON.stringify(result));
break;
}
case 'getAllReviews': {
const result = await fabricClient.getAllReviews();
console.log(JSON.stringify(result));
break;
}
default:
console.error('未知操作类型: 支持 submitReview, verifyReview, getAllReviews');
process.exit(1);
}
} catch (error) {
console.error(JSON.stringify({ success: false, error: error.message }));
} finally {
fabricClient.disconnect();
process.exit(0);
}
}
main();
module.exports = FabricClient;

@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../js-yaml/bin/js-yaml.js" "$@"
else
exec node "$basedir/../js-yaml/bin/js-yaml.js" "$@"
fi

@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\js-yaml\bin\js-yaml.js" %*

@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../js-yaml/bin/js-yaml.js" $args
} else {
& "$basedir/node$exe" "$basedir/../js-yaml/bin/js-yaml.js" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../js-yaml/bin/js-yaml.js" $args
} else {
& "node$exe" "$basedir/../js-yaml/bin/js-yaml.js" $args
}
$ret=$LASTEXITCODE
}
exit $ret

@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../@grpc/proto-loader/build/bin/proto-loader-gen-types.js" "$@"
else
exec node "$basedir/../@grpc/proto-loader/build/bin/proto-loader-gen-types.js" "$@"
fi

@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\@grpc\proto-loader\build\bin\proto-loader-gen-types.js" %*

@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../@grpc/proto-loader/build/bin/proto-loader-gen-types.js" $args
} else {
& "$basedir/node$exe" "$basedir/../@grpc/proto-loader/build/bin/proto-loader-gen-types.js" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../@grpc/proto-loader/build/bin/proto-loader-gen-types.js" $args
} else {
& "node$exe" "$basedir/../@grpc/proto-loader/build/bin/proto-loader-gen-types.js" $args
}
$ret=$LASTEXITCODE
}
exit $ret

File diff suppressed because it is too large Load Diff

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
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.

@ -0,0 +1,79 @@
# Pure JavaScript gRPC Client
## Installation
Node 12 is recommended. The exact set of compatible Node versions can be found in the `engines` field of the `package.json` file.
```sh
npm install @grpc/grpc-js
```
## Documentation
Documentation specifically for the `@grpc/grpc-js` package is currently not available. However, [documentation is available for the `grpc` package](https://grpc.github.io/grpc/node/grpc.html), and the two packages contain mostly the same interface. There are a few notable differences, however, and these differences are noted in the "Migrating from grpc" section below.
## Features
- Clients
- Automatic reconnection
- Servers
- Streaming
- Metadata
- Partial compression support: clients can compress and decompress messages, and servers can decompress request messages
- Pick first and round robin load balancing policies
- Client Interceptors
- Connection Keepalives
- HTTP Connect support (proxies)
If you need a feature from the `grpc` package that is not provided by the `@grpc/grpc-js`, please file a feature request with that information.
This library does not directly handle `.proto` files. To use `.proto` files with this library we recommend using the `@grpc/proto-loader` package.
## Migrating from [`grpc`](https://www.npmjs.com/package/grpc)
`@grpc/grpc-js` is almost a drop-in replacement for `grpc`, but you may need to make a few code changes to use it:
- If you are currently loading `.proto` files using `grpc.load`, that function is not available in this library. You should instead load your `.proto` files using `@grpc/proto-loader` and load the resulting package definition objects into `@grpc/grpc-js` using `grpc.loadPackageDefinition`.
- If you are currently loading packages generated by `grpc-tools`, you should instead generate your files using the `generate_package_definition` option in `grpc-tools`, then load the object exported by the generated file into `@grpc/grpc-js` using `grpc.loadPackageDefinition`.
- If you have a server and you are using `Server#bind` to bind ports, you will need to use `Server#bindAsync` instead.
- If you are using any channel options supported in `grpc` but not supported in `@grpc/grpc-js`, you may need to adjust your code to handle the different behavior. Refer to [the list of supported options](#supported-channel-options) below.
- Refer to the [detailed package comparison](https://github.com/grpc/grpc-node/blob/master/PACKAGE-COMPARISON.md) for more details on the differences between `grpc` and `@grpc/grpc-js`.
## Supported Channel Options
Many channel arguments supported in `grpc` are not supported in `@grpc/grpc-js`. The channel arguments supported by `@grpc/grpc-js` are:
- `grpc.ssl_target_name_override`
- `grpc.primary_user_agent`
- `grpc.secondary_user_agent`
- `grpc.default_authority`
- `grpc.keepalive_time_ms`
- `grpc.keepalive_timeout_ms`
- `grpc.keepalive_permit_without_calls`
- `grpc.service_config`
- `grpc.max_concurrent_streams`
- `grpc.initial_reconnect_backoff_ms`
- `grpc.max_reconnect_backoff_ms`
- `grpc.use_local_subchannel_pool`
- `grpc.max_send_message_length`
- `grpc.max_receive_message_length`
- `grpc.enable_http_proxy`
- `grpc.default_compression_algorithm`
- `grpc.enable_channelz`
- `grpc.dns_min_time_between_resolutions_ms`
- `grpc.enable_retries`
- `grpc.per_rpc_retry_buffer_size`
- `grpc.retry_buffer_size`
- `grpc.service_config_disable_resolution`
- `grpc.client_idle_timeout_ms`
- `grpc-node.max_session_memory`
- `grpc-node.tls_enable_trace`
- `channelOverride`
- `channelFactoryOverride`
## Some Notes on API Guarantees
The public API of this library follows semantic versioning, with some caveats:
- Some methods are prefixed with an underscore. These methods are internal and should not be considered part of the public API.
- The class `Call` is only exposed due to limitations of TypeScript. It should not be considered part of the public API.
- In general, any API that is exposed by this library but is not exposed by the `grpc` library is likely an error and should not be considered part of the public API.
- The `grpc.experimental` namespace contains APIs that have not stabilized. Any API in that namespace may break in any minor version update.

@ -0,0 +1,85 @@
{
"name": "@grpc/grpc-js",
"version": "1.9.15",
"description": "gRPC Library for Node - pure JS implementation",
"homepage": "https://grpc.io/",
"repository": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js",
"main": "build/src/index.js",
"engines": {
"node": "^8.13.0 || >=10.10.0"
},
"keywords": [],
"author": {
"name": "Google Inc."
},
"types": "build/src/index.d.ts",
"license": "Apache-2.0",
"devDependencies": {
"@types/gulp": "^4.0.6",
"@types/gulp-mocha": "0.0.32",
"@types/lodash": "^4.14.186",
"@types/mocha": "^5.2.6",
"@types/ncp": "^2.0.1",
"@types/pify": "^3.0.2",
"@types/semver": "^7.3.9",
"@typescript-eslint/eslint-plugin": "^5.59.11",
"@typescript-eslint/parser": "^5.59.11",
"@typescript-eslint/typescript-estree": "^5.59.11",
"clang-format": "^1.0.55",
"eslint": "^8.42.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^4.2.1",
"execa": "^2.0.3",
"gulp": "^4.0.2",
"gulp-mocha": "^6.0.0",
"lodash": "^4.17.4",
"madge": "^5.0.1",
"mocha-jenkins-reporter": "^0.4.1",
"ncp": "^2.0.0",
"pify": "^4.0.1",
"prettier": "^2.8.8",
"rimraf": "^3.0.2",
"semver": "^7.3.5",
"ts-node": "^10.9.1",
"typescript": "^5.1.3"
},
"contributors": [
{
"name": "Google Inc."
}
],
"scripts": {
"build": "npm run compile",
"clean": "rimraf ./build",
"compile": "tsc -p .",
"format": "clang-format -i -style=\"{Language: JavaScript, BasedOnStyle: Google, ColumnLimit: 80}\" src/*.ts test/*.ts",
"lint": "eslint src/*.ts test/*.ts",
"prepare": "npm run generate-types && npm run compile",
"test": "gulp test",
"check": "npm run lint",
"fix": "eslint --fix src/*.ts test/*.ts",
"pretest": "npm run generate-types && npm run generate-test-types && npm run compile",
"posttest": "npm run check && madge -c ./build/src",
"generate-types": "proto-loader-gen-types --keepCase --longs String --enums String --defaults --oneofs --includeComments --includeDirs proto/ --include-dirs test/fixtures/ -O src/generated/ --grpcLib ../index channelz.proto",
"generate-test-types": "proto-loader-gen-types --keepCase --longs String --enums String --defaults --oneofs --includeComments --include-dirs test/fixtures/ -O test/generated/ --grpcLib ../../src/index test_service.proto"
},
"dependencies": {
"@grpc/proto-loader": "^0.7.8",
"@types/node": ">=12.12.47"
},
"files": [
"src/**/*.ts",
"build/src/**/*.{js,d.ts,js.map}",
"proto/*.proto",
"LICENSE",
"deps/envoy-api/envoy/api/v2/**/*.proto",
"deps/envoy-api/envoy/config/**/*.proto",
"deps/envoy-api/envoy/service/**/*.proto",
"deps/envoy-api/envoy/type/**/*.proto",
"deps/udpa/udpa/**/*.proto",
"deps/googleapis/google/api/*.proto",
"deps/googleapis/google/rpc/*.proto",
"deps/protoc-gen-validate/validate/**/*.proto"
]
}

@ -0,0 +1,564 @@
// Copyright 2018 The 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 defines an interface for exporting monitoring information
// out of gRPC servers. See the full design at
// https://github.com/grpc/proposal/blob/master/A14-channelz.md
//
// The canonical version of this proto can be found at
// https://github.com/grpc/grpc-proto/blob/master/grpc/channelz/v1/channelz.proto
syntax = "proto3";
package grpc.channelz.v1;
import "google/protobuf/any.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/wrappers.proto";
option go_package = "google.golang.org/grpc/channelz/grpc_channelz_v1";
option java_multiple_files = true;
option java_package = "io.grpc.channelz.v1";
option java_outer_classname = "ChannelzProto";
// Channel is a logical grouping of channels, subchannels, and sockets.
message Channel {
// The identifier for this channel. This should bet set.
ChannelRef ref = 1;
// Data specific to this channel.
ChannelData data = 2;
// At most one of 'channel_ref+subchannel_ref' and 'socket' is set.
// 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.
repeated ChannelRef channel_ref = 3;
// 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.
repeated SubchannelRef subchannel_ref = 4;
// There are no ordering guarantees on the order of sockets.
repeated SocketRef socket_ref = 5;
}
// Subchannel is a logical grouping of channels, subchannels, and sockets.
// A subchannel is load balanced over by it's ancestor
message Subchannel {
// The identifier for this channel.
SubchannelRef ref = 1;
// Data specific to this channel.
ChannelData data = 2;
// At most one of 'channel_ref+subchannel_ref' and 'socket' is set.
// 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.
repeated ChannelRef channel_ref = 3;
// 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.
repeated SubchannelRef subchannel_ref = 4;
// There are no ordering guarantees on the order of sockets.
repeated SocketRef socket_ref = 5;
}
// These come from the specified states in this document:
// https://github.com/grpc/grpc/blob/master/doc/connectivity-semantics-and-api.md
message ChannelConnectivityState {
enum State {
UNKNOWN = 0;
IDLE = 1;
CONNECTING = 2;
READY = 3;
TRANSIENT_FAILURE = 4;
SHUTDOWN = 5;
}
State state = 1;
}
// Channel data is data related to a specific Channel or Subchannel.
message ChannelData {
// The connectivity state of the channel or subchannel. Implementations
// should always set this.
ChannelConnectivityState state = 1;
// The target this channel originally tried to connect to. May be absent
string target = 2;
// A trace of recent events on the channel. May be absent.
ChannelTrace trace = 3;
// The number of calls started on the channel
int64 calls_started = 4;
// The number of calls that have completed with an OK status
int64 calls_succeeded = 5;
// The number of calls that have completed with a non-OK status
int64 calls_failed = 6;
// The last time a call was started on the channel.
google.protobuf.Timestamp last_call_started_timestamp = 7;
}
// A trace event is an interesting thing that happened to a channel or
// subchannel, such as creation, address resolution, subchannel creation, etc.
message ChannelTraceEvent {
// High level description of the event.
string description = 1;
// The supported severity levels of trace events.
enum Severity {
CT_UNKNOWN = 0;
CT_INFO = 1;
CT_WARNING = 2;
CT_ERROR = 3;
}
// the severity of the trace event
Severity severity = 2;
// When this event occurred.
google.protobuf.Timestamp timestamp = 3;
// 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.
oneof child_ref {
ChannelRef channel_ref = 4;
SubchannelRef subchannel_ref = 5;
}
}
// ChannelTrace represents the recent events that have occurred on the channel.
message 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.
int64 num_events_logged = 1;
// Time that this channel was created.
google.protobuf.Timestamp creation_timestamp = 2;
// List of events that have occurred on this channel.
repeated ChannelTraceEvent events = 3;
}
// ChannelRef is a reference to a Channel.
message ChannelRef {
// The globally unique id for this channel. Must be a positive number.
int64 channel_id = 1;
// An optional name associated with the channel.
string name = 2;
// Intentionally don't use field numbers from other refs.
reserved 3, 4, 5, 6, 7, 8;
}
// SubchannelRef is a reference to a Subchannel.
message SubchannelRef {
// The globally unique id for this subchannel. Must be a positive number.
int64 subchannel_id = 7;
// An optional name associated with the subchannel.
string name = 8;
// Intentionally don't use field numbers from other refs.
reserved 1, 2, 3, 4, 5, 6;
}
// SocketRef is a reference to a Socket.
message SocketRef {
// The globally unique id for this socket. Must be a positive number.
int64 socket_id = 3;
// An optional name associated with the socket.
string name = 4;
// Intentionally don't use field numbers from other refs.
reserved 1, 2, 5, 6, 7, 8;
}
// ServerRef is a reference to a Server.
message ServerRef {
// A globally unique identifier for this server. Must be a positive number.
int64 server_id = 5;
// An optional name associated with the server.
string name = 6;
// Intentionally don't use field numbers from other refs.
reserved 1, 2, 3, 4, 7, 8;
}
// Server represents a single server. There may be multiple servers in a single
// program.
message Server {
// The identifier for a Server. This should be set.
ServerRef ref = 1;
// The associated data of the Server.
ServerData data = 2;
// The sockets that the server is listening on. There are no ordering
// guarantees. This may be absent.
repeated SocketRef listen_socket = 3;
}
// ServerData is data for a specific Server.
message ServerData {
// A trace of recent events on the server. May be absent.
ChannelTrace trace = 1;
// The number of incoming calls started on the server
int64 calls_started = 2;
// The number of incoming calls that have completed with an OK status
int64 calls_succeeded = 3;
// The number of incoming calls that have a completed with a non-OK status
int64 calls_failed = 4;
// The last time a call was started on the server.
google.protobuf.Timestamp last_call_started_timestamp = 5;
}
// Information about an actual connection. Pronounced "sock-ay".
message Socket {
// The identifier for the Socket.
SocketRef ref = 1;
// Data specific to this Socket.
SocketData data = 2;
// The locally bound address.
Address local = 3;
// The remote bound address. May be absent.
Address remote = 4;
// Security details for this socket. May be absent if not available, or
// there is no security on the socket.
Security security = 5;
// Optional, represents the name of the remote endpoint, if different than
// the original target name.
string remote_name = 6;
}
// 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)
message SocketData {
// The number of streams that have been started.
int64 streams_started = 1;
// 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.
int64 streams_succeeded = 2;
// 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.
int64 streams_failed = 3;
// The number of grpc messages successfully sent on this socket.
int64 messages_sent = 4;
// The number of grpc messages received on this socket.
int64 messages_received = 5;
// The number of keep alives sent. This is typically implemented with HTTP/2
// ping messages.
int64 keep_alives_sent = 6;
// The last time a stream was created by this endpoint. Usually unset for
// servers.
google.protobuf.Timestamp last_local_stream_created_timestamp = 7;
// The last time a stream was created by the remote endpoint. Usually unset
// for clients.
google.protobuf.Timestamp last_remote_stream_created_timestamp = 8;
// The last time a message was sent by this endpoint.
google.protobuf.Timestamp last_message_sent_timestamp = 9;
// The last time a message was received by this endpoint.
google.protobuf.Timestamp last_message_received_timestamp = 10;
// 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.
google.protobuf.Int64Value local_flow_control_window = 11;
// 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.
google.protobuf.Int64Value remote_flow_control_window = 12;
// Socket options set on this socket. May be absent if 'summary' is set
// on GetSocketRequest.
repeated SocketOption option = 13;
}
// Address represents the address used to create the socket.
message Address {
message TcpIpAddress {
// Either the IPv4 or IPv6 address in bytes. Will be either 4 bytes or 16
// bytes in length.
bytes ip_address = 1;
// 0-64k, or -1 if not appropriate.
int32 port = 2;
}
// A Unix Domain Socket address.
message UdsAddress {
string filename = 1;
}
// An address type not included above.
message OtherAddress {
// The human readable version of the value. This value should be set.
string name = 1;
// The actual address message.
google.protobuf.Any value = 2;
}
oneof address {
TcpIpAddress tcpip_address = 1;
UdsAddress uds_address = 2;
OtherAddress other_address = 3;
}
}
// Security represents details about how secure the socket is.
message Security {
message Tls {
oneof cipher_suite {
// The cipher suite name in the RFC 4346 format:
// https://tools.ietf.org/html/rfc4346#appendix-C
string standard_name = 1;
// Some other way to describe the cipher suite if
// the RFC 4346 name is not available.
string other_name = 2;
}
// the certificate used by this endpoint.
bytes local_certificate = 3;
// the certificate used by the remote endpoint.
bytes remote_certificate = 4;
}
message OtherSecurity {
// The human readable version of the value.
string name = 1;
// The actual security details message.
google.protobuf.Any value = 2;
}
oneof model {
Tls tls = 1;
OtherSecurity other = 2;
}
}
// SocketOption represents socket options for a socket. Specifically, these
// are the options returned by getsockopt().
message SocketOption {
// The full name of the socket option. Typically this will be the upper case
// name, such as "SO_REUSEPORT".
string name = 1;
// The human readable value of this socket option. At least one of value or
// additional will be set.
string value = 2;
// Additional data associated with the socket option. At least one of value
// or additional will be set.
google.protobuf.Any additional = 3;
}
// For use with SocketOption's additional field. This is primarily used for
// SO_RCVTIMEO and SO_SNDTIMEO
message SocketOptionTimeout {
google.protobuf.Duration duration = 1;
}
// For use with SocketOption's additional field. This is primarily used for
// SO_LINGER.
message SocketOptionLinger {
// active maps to `struct linger.l_onoff`
bool active = 1;
// duration maps to `struct linger.l_linger`
google.protobuf.Duration duration = 2;
}
// For use with SocketOption's additional field. Tcp info for
// SOL_TCP and TCP_INFO.
message SocketOptionTcpInfo {
uint32 tcpi_state = 1;
uint32 tcpi_ca_state = 2;
uint32 tcpi_retransmits = 3;
uint32 tcpi_probes = 4;
uint32 tcpi_backoff = 5;
uint32 tcpi_options = 6;
uint32 tcpi_snd_wscale = 7;
uint32 tcpi_rcv_wscale = 8;
uint32 tcpi_rto = 9;
uint32 tcpi_ato = 10;
uint32 tcpi_snd_mss = 11;
uint32 tcpi_rcv_mss = 12;
uint32 tcpi_unacked = 13;
uint32 tcpi_sacked = 14;
uint32 tcpi_lost = 15;
uint32 tcpi_retrans = 16;
uint32 tcpi_fackets = 17;
uint32 tcpi_last_data_sent = 18;
uint32 tcpi_last_ack_sent = 19;
uint32 tcpi_last_data_recv = 20;
uint32 tcpi_last_ack_recv = 21;
uint32 tcpi_pmtu = 22;
uint32 tcpi_rcv_ssthresh = 23;
uint32 tcpi_rtt = 24;
uint32 tcpi_rttvar = 25;
uint32 tcpi_snd_ssthresh = 26;
uint32 tcpi_snd_cwnd = 27;
uint32 tcpi_advmss = 28;
uint32 tcpi_reordering = 29;
}
// Channelz is a service exposed by gRPC servers that provides detailed debug
// information.
service Channelz {
// Gets all root channels (i.e. channels the application has directly
// created). This does not include subchannels nor non-top level channels.
rpc GetTopChannels(GetTopChannelsRequest) returns (GetTopChannelsResponse);
// Gets all servers that exist in the process.
rpc GetServers(GetServersRequest) returns (GetServersResponse);
// Returns a single Server, or else a NOT_FOUND code.
rpc GetServer(GetServerRequest) returns (GetServerResponse);
// Gets all server sockets that exist in the process.
rpc GetServerSockets(GetServerSocketsRequest) returns (GetServerSocketsResponse);
// Returns a single Channel, or else a NOT_FOUND code.
rpc GetChannel(GetChannelRequest) returns (GetChannelResponse);
// Returns a single Subchannel, or else a NOT_FOUND code.
rpc GetSubchannel(GetSubchannelRequest) returns (GetSubchannelResponse);
// Returns a single Socket or else a NOT_FOUND code.
rpc GetSocket(GetSocketRequest) returns (GetSocketResponse);
}
message 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.
int64 start_channel_id = 1;
// 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.
int64 max_results = 2;
}
message 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.
repeated Channel channel = 1;
// 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.
bool end = 2;
}
message 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.
int64 start_server_id = 1;
// 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.
int64 max_results = 2;
}
message 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.
repeated Server server = 1;
// 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.
bool end = 2;
}
message GetServerRequest {
// server_id is the identifier of the specific server to get.
int64 server_id = 1;
}
message GetServerResponse {
// The Server that corresponds to the requested server_id. This field
// should be set.
Server server = 1;
}
message GetServerSocketsRequest {
int64 server_id = 1;
// 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.
int64 start_socket_id = 2;
// 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.
int64 max_results = 3;
}
message 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.
repeated SocketRef socket_ref = 1;
// 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.
bool end = 2;
}
message GetChannelRequest {
// channel_id is the identifier of the specific channel to get.
int64 channel_id = 1;
}
message GetChannelResponse {
// The Channel that corresponds to the requested channel_id. This field
// should be set.
Channel channel = 1;
}
message GetSubchannelRequest {
// subchannel_id is the identifier of the specific subchannel to get.
int64 subchannel_id = 1;
}
message GetSubchannelResponse {
// The Subchannel that corresponds to the requested subchannel_id. This
// field should be set.
Subchannel subchannel = 1;
}
message GetSocketRequest {
// socket_id is the identifier of the specific socket to get.
int64 socket_id = 1;
// If true, the response will contain only high level information
// that is inexpensive to obtain. Fields thay may be omitted are
// documented.
bool summary = 2;
}
message GetSocketResponse {
// The Socket that corresponds to the requested socket_id. This field
// should be set.
Socket socket = 1;
}

@ -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());
}
}

@ -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;
}
}

@ -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;
}
}

@ -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;
}

@ -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++;
}

@ -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();
}
}

@ -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;
}
}
}

@ -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;
}

@ -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
);
}
}

@ -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);
}

@ -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);
}

@ -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;
}
}

@ -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,
}

@ -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);
}
}

@ -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,
}

@ -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;

@ -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 };
}
}

@ -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();
}
}
}

@ -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';
}

@ -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;
}
}

@ -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;
}

@ -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';

@ -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())
);
}
}

@ -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;
}

@ -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
}
}
}
}

@ -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);
}

@ -0,0 +1,10 @@
// Original file: null
export interface BoolValue {
'value'?: (boolean);
}
export interface BoolValue__Output {
'value': (boolean);
}

@ -0,0 +1,10 @@
// Original file: null
export interface BytesValue {
'value'?: (Buffer | Uint8Array | string);
}
export interface BytesValue__Output {
'value': (Buffer);
}

@ -0,0 +1,10 @@
// Original file: null
export interface DoubleValue {
'value'?: (number | string);
}
export interface DoubleValue__Output {
'value': (number);
}

@ -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);
}

@ -0,0 +1,10 @@
// Original file: null
export interface FloatValue {
'value'?: (number | string);
}
export interface FloatValue__Output {
'value': (number);
}

@ -0,0 +1,10 @@
// Original file: null
export interface Int32Value {
'value'?: (number);
}
export interface Int32Value__Output {
'value': (number);
}

@ -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);
}

@ -0,0 +1,10 @@
// Original file: null
export interface StringValue {
'value'?: (string);
}
export interface StringValue__Output {
'value': (string);
}

@ -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);
}

@ -0,0 +1,10 @@
// Original file: null
export interface UInt32Value {
'value'?: (number);
}
export interface UInt32Value__Output {
'value': (number);
}

@ -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);
}

@ -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";
}

@ -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)[];
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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)[];
}

@ -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";
}

@ -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>
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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";
}

@ -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)[];
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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)[];
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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)[];
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save