This commit is contained in:
Stephen Palmer 2019-06-14 10:43:56 -05:00
Родитель da9a2cbe8c
Коммит df261d82c5
7 изменённых файлов: 653 добавлений и 0 удалений

2
.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,2 @@
node_modules/
.idea

1
.nvmrc Normal file
Просмотреть файл

@ -0,0 +1 @@
v10.16.0

25
README.md Normal file
Просмотреть файл

@ -0,0 +1,25 @@
## Unity Cache Server Diagnostic Tools
### Client Stream Player
#### Usage
`stream_player.js [options] <filePath> [ServerAddress]`
Option | Description
------------------------------- | -----------
-i --iterations <n> | Number of times to send the recorded session to the server (default: 1)
-c --max-concurrency <n> | Number of concurrent connections to make to the server (default: 1)
-d --debug-protocol | Print protocol stream debugging data to the console.
-q --no-verbose | Do not show progress and result statistics.
-h, --help | Show usage information.
#### Description
The stream player can read one ore more recorded client session at `<filePath>`, optionally print the protocol stream to the console, and optionally send the protocol stream to a remote Cache Server at `[ServerAddress]` for e.g. performance load testing.
#### Notes
* If `<filePath>` is a directory, all files within the directory (recursively) will be read and played back. Some rudimentary validation is done on each file to detect whether or not it is a valid client session stream.
* If `[ServerAddress]` is omitted, data will be sent to a temporary "no-op" TCP server. This is useful if you are only concerned with reading the debug protocol stream with the `-d` option.

Просмотреть файл

@ -0,0 +1,83 @@
const { Constants, Helpers } = require('unity-cache-server');
const { Transform } = require('stream');
const crypto = require('crypto');
class ClientStreamDebugger extends Transform {
constructor(options) {
super(options);
this._writeHandlers = {
putStream: this._handleWrite.bind(this),
command: this._handleCommand.bind(this),
version: this._handleVersion.bind(this)
};
this._putHash = null;
this._putSize = 0;
this._putSent = 0;
this._writeHandler = this._writeHandlers.version;
}
_transform(chunk, encoding, callback) {
this._writeHandler(chunk);
this.push(chunk);
callback();
}
/**
*
* @param {Buffer} data
* @private
*/
_handleVersion(data) {
this.emit('debug', [Helpers.readUInt32(data)]);
this._writeHandler = this._writeHandlers.command;
}
/**
*
* @param {Buffer} data
* @private
*/
_handleWrite(data) {
this._putSent += data.length;
this._putHash.update(data, 'ascii');
if(this._putSent === this._putSize) {
this.emit('debug', [`<BLOB ${this._putHash.digest().toString('hex')}>`]);
this._writeHandler = this._writeHandlers.command;
this._putSent = 0;
this._putSize = 0;
}
}
/**
*
* @param {Buffer} data
* @private
*/
_handleCommand(data) {
const cmd = data.slice(0, Math.min(data.length, 2)).toString('ascii');
const eventData = [cmd];
let size, guid, hash = null;
if(data.length > 1) {
if (data.length === 2 + Constants.ID_SIZE) {
guid = Buffer.from(data.slice(2, 2 + Constants.GUID_SIZE));
hash = Buffer.from(data.slice(2 + Constants.HASH_SIZE));
eventData.push(Helpers.GUIDBufferToString(guid));
eventData.push(hash.toString('hex'));
}
else if (data.length === 2 + Constants.SIZE_SIZE) {
size = Helpers.readUInt64(data.slice(2));
this._putSize = size;
this._putHash = crypto.createHash('sha256');
this._writeHandler = this._writeHandlers.putStream;
eventData.push(size.toString());
}
}
this.emit('debug', eventData);
}
}
module.exports = ClientStreamDebugger;

288
package-lock.json сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,288 @@
{
"name": "ucs-diag-utils",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"requires": {
"color-convert": "^1.9.0"
}
},
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"requires": {
"sprintf-js": "~1.0.2"
}
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"cli-cursor": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
"integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
"requires": {
"restore-cursor": "^2.0.0"
}
},
"cli-spinners": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-1.3.1.tgz",
"integrity": "sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg=="
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
"commander": {
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
"integrity": "sha1-1YuytcHuj4ew00ACfp6U4iLFpCI="
},
"config": {
"version": "1.31.0",
"resolved": "https://registry.npmjs.org/config/-/config-1.31.0.tgz",
"integrity": "sha512-Ep/l9Rd1J9IPueztJfpbOqVzuKHQh4ZODMNt9xqTYdBBNRXbV4oTu34kCkkfdRVcDq0ohtpaeXGgb+c0LQxFRA==",
"requires": {
"json5": "^1.0.1"
}
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
},
"filesize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/filesize/-/filesize-4.1.2.tgz",
"integrity": "sha1-/NVwrxNTzql4l75k9WGDrbmVmUs="
},
"fs-extra": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.0.1.tgz",
"integrity": "sha1-kClAgfl4sfGC80ekQKIJFUNEKFs=",
"requires": {
"graceful-fs": "^4.1.2",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
},
"dependencies": {
"graceful-fs": {
"version": "4.1.15",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
"integrity": "sha1-/7cD4QZuig7qpMi4C6klPu77+wA="
},
"jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
"requires": {
"graceful-fs": "^4.1.6"
}
},
"universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha1-tkb2m+OULavOzJ1mOcgNwQXvqmY="
}
}
},
"graceful-fs": {
"version": "4.1.15",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
"integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA=="
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
},
"ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
},
"js-yaml": {
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
},
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"requires": {
"minimist": "^1.2.0"
}
},
"jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
"requires": {
"graceful-fs": "^4.1.6"
}
},
"lodash": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
},
"log-symbols": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
"integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==",
"requires": {
"chalk": "^2.0.1"
}
},
"lokijs": {
"version": "1.5.6",
"resolved": "https://registry.npmjs.org/lokijs/-/lokijs-1.5.6.tgz",
"integrity": "sha512-xJoDXy8TASTjmXMKr4F8vvNUCu4dqlwY5gmn0g5BajGt1GM3goDCafNiGAh/sfrWgkfWu1J4OfsxWm8yrWweJA=="
},
"mimic-fn": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
"integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
},
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
},
"moment": {
"version": "2.24.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
},
"onetime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
"integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
"requires": {
"mimic-fn": "^1.0.0"
}
},
"ora": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/ora/-/ora-1.4.0.tgz",
"integrity": "sha512-iMK1DOQxzzh2MBlVsU42G80mnrvUhqsMh74phHtDlrcTZPK0pH6o7l7DRshK+0YsxDyEuaOkziVdvM3T0QTzpw==",
"requires": {
"chalk": "^2.1.0",
"cli-cursor": "^2.1.0",
"cli-spinners": "^1.0.1",
"log-symbols": "^2.1.0"
}
},
"progress": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="
},
"restore-cursor": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
"integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
"requires": {
"onetime": "^2.0.0",
"signal-exit": "^3.0.2"
}
},
"signal-exit": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"requires": {
"has-flag": "^3.0.0"
}
},
"unity-cache-server": {
"version": "6.4.0-beta3",
"resolved": "https://registry.npmjs.org/unity-cache-server/-/unity-cache-server-6.4.0-beta3.tgz",
"integrity": "sha512-/H7wNcCgzuLIjopjpIH/4jpV/v6gu4NLzFObAx9dU+4wqbv5uE9RZkb7zi+K5XGkNKalX4+leoB/2KW1U4AeLw==",
"requires": {
"commander": "^2.19.0",
"config": "^1.31.0",
"filesize": "^3.5.11",
"fs-extra": "^5.0.0",
"ip": "^1.1.5",
"js-yaml": "^3.13.1",
"lodash": "^4.17.11",
"lokijs": "^1.5.5",
"moment": "^2.23.0",
"ora": "^1.4.0",
"progress": "^2.0.3",
"uuid": "^3.3.2"
},
"dependencies": {
"filesize": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz",
"integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg=="
},
"fs-extra": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz",
"integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==",
"requires": {
"graceful-fs": "^4.1.2",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
}
}
}
},
"universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
},
"uuid": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
}
}
}

23
package.json Normal file
Просмотреть файл

@ -0,0 +1,23 @@
{
"name": "ucs-diag-utils",
"version": "1.0.0",
"description": "Diagnostic utilities for the Unity Cache Server (V1)",
"main": "index.js",
"scripts": {},
"repository": {
"type": "git",
"url": "https://github.com/Unity-Technologies/ucs-diag-utils.git"
},
"author": "Stephen Palmer",
"license": "MIT",
"bugs": {
"url": "https://github.com/Unity-Technologies/ucs-diag-utils/issues"
},
"homepage": "https://github.com/Unity-Technologies/ucs-diag-utils",
"dependencies": {
"commander": "^2.20.0",
"filesize": "^4.1.2",
"fs-extra": "^8.0.1",
"unity-cache-server": "^6.4.0-beta3"
}
}

231
stream_player.js Normal file
Просмотреть файл

@ -0,0 +1,231 @@
#!/usr/bin/env node
const program = require('commander');
const net = require('net');
const fs = require('fs-extra');
const filesize = require('filesize');
const crypto = require('crypto');
const ClientStreamDebugger = require('./lib/client_stream_debugger');
const { Constants, Helpers, ServerStreamProcessor, ClientStreamProcessor } = require('unity-cache-server');
program.arguments('<filePath> [ServerAddress]')
.option('-i --iterations <n>', 'Number of times to send the recorded session to the server', 1)
.option('-c --max-concurrency <n>', 'Number of concurrent connections to make to the server', 1)
.option('-d --debug-protocol', 'Print protocol stream debugging data to the console', false)
.option('-q --no-verbose', 'Do not show progress and result statistics')
.action((filePath, serverAddress) => {
const options = {
numIterations: parseInt(program.iterations),
numConcurrent: parseInt(program.maxConcurrency),
verbose: program.verbose,
debugProtocol: program.debugProtocol
};
run(filePath, serverAddress, options)
.then(stats => {
if(options.verbose) {
if(stats.bytesSent > 0) {
const sendTime = stats.sendTime / 1000;
const sendBps = stats.bytesSent / sendTime || 0;
console.log(`Sent ${filesize(stats.bytesSent)} in ${sendTime} seconds (${filesize(sendBps)}/second)`);
}
if(stats.bytesReceived > 0) {
const receiveTime = stats.receiveTime / 1000;
const receiveBps = stats.bytesReceived / receiveTime || 0;
console.log(`Received ${filesize(stats.bytesReceived)} in ${receiveTime} seconds (${filesize(receiveBps)}/second)`);
}
}
})
.catch(err => {
console.log(err);
process.exit(1);
});
});
program.parse(process.argv);
async function run(filePath, serverAddress, options) {
let nullServer = null;
if(!serverAddress) {
nullServer = net.createServer({}, socket => {
socket.on('data', () => {});
});
await new Promise(resolve => {
nullServer.listen(0, "0.0.0.0", () => resolve());
});
}
if(nullServer !== null) {
options.nullServer = true;
const a = nullServer.address();
serverAddress = `${a.address}:${a.port}`;
}
// Gather files
const files = [];
const stat = await fs.stat(filePath);
if(stat.isDirectory()) {
await Helpers.readDir(filePath, f => files.push(f.path));
}
else {
files.push(filePath);
}
// Validate files
const verBuf = Buffer.alloc(Constants.VERSION_SIZE, 'ascii');
for(let i = 0; i < files.length; i++) {
const fd = await fs.open(files[i], "r");
await fs.read(fd, verBuf, 0, Constants.VERSION_SIZE, 0);
if(Helpers.readUInt32(verBuf) !== Constants.PROTOCOL_VERSION) {
if(options.verbose) {
console.log(`Skipping unrecognized file ${files[i]}`);
}
files[i] = null;
}
await fs.close(fd);
}
const jobs = [];
const results = [];
let i = 0;
while(i < options.numIterations) {
files.forEach(f => {
if(f === null) return;
jobs.push((n, t) => {
if(options.verbose) console.log(`[${n}/${t}] Playing ${f}`);
return playStream(f, serverAddress, options)
.then(stats => results.push(stats))
.catch(err => { throw(err); });
});
});
i++;
}
const totalJobs = jobs.length;
let nextJobNum = 0;
while(jobs.length > 0) {
nextJobNum += Math.min(jobs.length, options.numConcurrent);
const next = jobs.splice(0, options.numConcurrent);
await Promise.all(next.map(t => t(nextJobNum, totalJobs)));
}
if(nullServer !== null) nullServer.close();
return results.reduce((prev, cur) => {
cur.bytesSent += prev.bytesSent;
cur.bytesReceived += prev.bytesReceived;
cur.sendTime += prev.sendTime;
cur.receiveTime += prev.receiveTime;
return cur;
}, {
receiveTime: 0,
bytesReceived: 0,
sendTime: 0,
bytesSent: 0
});
}
async function playStream(filePath, serverAddress, options) {
let bytesReceived = 0, fileOpen = false, receiveStartTime, receiveEndTime, sendStartTime, sendEndTime, dataHash;
if(!await fs.pathExists(filePath)) throw new Error(`Cannot find ${filePath}`);
const fileStats = await fs.stat(filePath);
const address = await Helpers.parseAndValidateAddressString(serverAddress, Constants.DEFAULT_PORT);
const client = new net.Socket();
await new Promise(resolve => client.connect(address.port, address.host, () => resolve()));
client.on('error', (err) => {
console.log(err);
});
const fileStream = fs.createReadStream(filePath);
const endClient = () => {
if(options.nullServer || (reqCount === 0 && !fileOpen)) {
process.nextTick(() => client.end(''));
}
};
fileStream.on('open', () => {
fileOpen = true;
sendStartTime = Date.now();
}).on('close', () => {
fileOpen = false;
sendEndTime = Date.now();
endClient();
});
let reqCount = 0;
const ssp = new ServerStreamProcessor();
ssp.once('header', () => {
receiveStartTime = Date.now();
}).on('header', () => {
reqCount--;
if(reqCount === 0) endClient();
}).on('data', (chunk) => {
bytesReceived += chunk.length;
}).on('dataEnd', () => {
receiveEndTime = Date.now();
});
if(options.debugProtocol) {
ssp.on('header', header => {
dataHash = crypto.createHash('sha256');
const debugData = [header.cmd];
if(header.size) {
debugData.push(header.size);
}
debugData.push(Helpers.GUIDBufferToString(header.guid));
debugData.push(header.hash.toString('hex'));
const txt = `<<< ${debugData.join(' ')}`;
if(header.size) {
process.stdout.write(txt);
} else {
console.log(txt)
}
}).on('data', (chunk) => {
dataHash.update(chunk, 'ascii');
}).on('dataEnd', () => {
console.log(` <BLOB ${dataHash.digest().toString('hex')}>`);
});
}
const csp = new ClientStreamProcessor({});
csp.on('cmd', cmd => {
if(cmd[0] === 'g' || cmd === 'ts') reqCount++;
if(cmd === 'te') reqCount --;
});
let stream = fileStream.pipe(csp);
if(options.debugProtocol) {
stream = stream.pipe(new ClientStreamDebugger({}))
.on('debug', data => console.log(`>>> ${data.join(' ')}`));
}
stream.pipe(client, {end: false}).pipe(ssp);
return new Promise(resolve => {
client.on('close', () => {
resolve({
bytesSent: fileStats.size,
bytesReceived: bytesReceived,
sendTime: sendEndTime - sendStartTime,
receiveTime: receiveEndTime - receiveStartTime,
});
});
});
}