diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bc45c4e3..8b2dc6a9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,9 +14,9 @@ jobs: strategy: matrix: node_version: - - 14 - 16 - 18 + - 20 name: Node ${{ matrix.node_version }} on ubuntu-latest steps: diff --git a/lib/client/bounded-buffer-readable-stream.js b/lib/client/bounded-buffer-readable-stream.js new file mode 100644 index 00000000..0962b03d --- /dev/null +++ b/lib/client/bounded-buffer-readable-stream.js @@ -0,0 +1,122 @@ +/** + * Pinpoint Node.js Agent + * Copyright 2021-present NAVER Corp. + * Apache License v2.0 + */ + +'use strict' + +const { Readable } = require('node:stream') +const log = require('../utils/logger') + +class BoundedBufferReadableStream { + constructor(constructorOptions) { + this.buffer = [] + this.options = constructorOptions || {} + this.readableStream = this.makeReadableSteam() + this.maxBufferSize = this.options.maxBuffer || 100 + } + + makeReadableSteam() { + const readableStream = new Readable(Object.assign({ + read: () => { + this.readStart() + } + }, this.options)) + + readableStream.on('error', () => { + // https://nodejs.org/api/stream.html#readablepipedestination-options + // `One important caveat is that if the Readable stream emits an error during processing, + // the Writable destination is not closed automatically. + // If an error occurs, it will be necessary to manually close each stream + // in order to prevent memory leaks.` + // for readable steam error memory leak prevention + if (this.writableSteam && typeof this.writableSteam.end === 'function') { + this.writableSteam.end() + } + }) + + readableStream.on('close', () => { + this.needsNewReadableStream = true + }) + + return readableStream + } + + push(data) { + if (this.buffer.length < this.maxBufferSize) { + this.buffer.push(data) + } + + if (this.canStart()) { + this.readStart() + } + } + + canStart() { + return this.readable + } + + readStart() { + if (this.needsNewReadableStream) { + this.readableStream = this.makeReadableSteam() + this._pipe() + } + + this.readable = true + + const length = this.buffer.length + for (let index = 0; index < length; index++) { + if (!this.readableStream.push(this.buffer.shift())) { + return this.readStop() + } + } + } + + readStop() { + this.readable = false + } + + end() { + this.readableStream.end() + } + + pipe(writableFactory) { + this.writableFactory = writableFactory + this._pipe() + } + + _pipe() { + if (typeof this.writableFactory !== 'function') { + return + } + + const writableSteam = this.writableFactory() + if (!writableSteam) { + return + } + + writableSteam.on('error', (error) => { + if (error) { + log.error('writable steam error', error) + } + }) + + writableSteam.on('unpipe', () => { + this.readStop() + }) + + writableSteam.on('close', () => { + + }) + + this.readableStream.pipe(writableSteam) + this.writableSteam = writableSteam + } + + setEncoding(encoding) { + this.readableStream.setEncoding(encoding) + } +} + +module.exports = BoundedBufferReadableStream \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4feebe50..c59d2072 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pinpoint-node-agent", - "version": "0.9.0-next.4", + "version": "1.0.0-next.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "pinpoint-node-agent", - "version": "0.9.0-next.4", + "version": "1.0.0-next.1", "license": "Apache-2.0", "dependencies": { "@grpc/grpc-js": "^1.2.3", @@ -22,7 +22,7 @@ "devDependencies": { "@types/semver": "^7.3.13", "@types/shimmer": "^1.0.2", - "axios": "^1.6.2", + "axios": "^1.6.8", "eslint": "^8.43.0", "eslint-config-prettier": "^3.6.0", "eslint-plugin-import": "^2.25.2", @@ -47,7 +47,7 @@ "typescript": "^4.8.3" }, "engines": { - "node": ">=14.0" + "node": ">=16.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -770,9 +770,9 @@ } }, "node_modules/@types/dockerode": { - "version": "3.3.19", - "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.19.tgz", - "integrity": "sha512-7CC5yIpQi+bHXwDK43b/deYXteP3Lem9gdocVVHJPSRJJLMfbiOchQV3rDmAPkMw+n3GIVj7m1six3JW+VcwwA==", + "version": "3.3.26", + "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.26.tgz", + "integrity": "sha512-/K+I9bGhRO2SvyIHisGeOsy/ypxnWLz8+Rde9S2tNNEKa3r91e0XMYIEq2D+kb7srm7xrmpAR0CDKfXoZOr4OA==", "dev": true, "dependencies": { "@types/docker-modem": "*", @@ -938,16 +938,16 @@ } }, "node_modules/archiver": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.1.tgz", - "integrity": "sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", "dev": true, "dependencies": { "archiver-utils": "^2.1.0", - "async": "^3.2.3", + "async": "^3.2.4", "buffer-crc32": "^0.2.1", "readable-stream": "^3.6.0", - "readdir-glob": "^1.0.0", + "readdir-glob": "^1.1.2", "tar-stream": "^2.2.0", "zip-stream": "^4.1.0" }, @@ -1147,9 +1147,9 @@ "dev": true }, "node_modules/async-lock": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.0.tgz", - "integrity": "sha512-coglx5yIWuetakm3/1dsX9hxCNox22h7+V80RQOu2XUUMidtArxKoZoOtHUPuR84SycKTXzgGzAUR5hJxujyJQ==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", "dev": true }, "node_modules/asynckit": { @@ -1171,12 +1171,12 @@ } }, "node_modules/axios": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", - "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", "dev": true, "dependencies": { - "follow-redirects": "^1.15.0", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -1251,21 +1251,21 @@ "dev": true }, "node_modules/body-parser": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", - "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dev": true, "dependencies": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.10.3", - "raw-body": "2.5.1", + "qs": "6.11.0", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -1570,9 +1570,9 @@ } }, "node_modules/cluster-key-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", - "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -1681,9 +1681,9 @@ } }, "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true, "engines": { "node": ">= 0.6" @@ -1705,9 +1705,9 @@ "dev": true }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "dev": true, "engines": { "node": ">= 0.6" @@ -2081,9 +2081,9 @@ } }, "node_modules/docker-compose": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.24.2.tgz", - "integrity": "sha512-2/WLvA7UZ6A2LDLQrYW0idKipmNBWhtfvrn2yzjC5PnHDzuFVj1zAZN6MJxVMKP0zZH8uzAK6OwVZYHGuyCmTw==", + "version": "0.24.7", + "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.24.7.tgz", + "integrity": "sha512-CdHl9n0S4+bl4i6MaxDQHNjqB1FdvuDirrDTzPKmdiMpheQqCjgsny0GZ2VhvN7qHTY0833lRlKWZgrkn1i6cg==", "dev": true, "dependencies": { "yaml": "^2.2.2" @@ -3015,17 +3015,17 @@ } }, "node_modules/express": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", - "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dev": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.0", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -3041,7 +3041,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.10.3", + "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.18.0", @@ -3297,9 +3297,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "dev": true, "funding": [ { @@ -5271,9 +5271,9 @@ "dev": true }, "node_modules/mysql2": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.6.1.tgz", - "integrity": "sha512-O7FXjLtNkjcMBpLURwkXIhyVbX9i4lq4nNRCykPNOXfceq94kJ0miagmTEGCZieuO8JtwtXaZ41U6KT4eF9y3g==", + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.6.tgz", + "integrity": "sha512-9NYUMLQv6yXnu+5hUh8PZ5CdKoG6VWDzXbojIdTyob8upNZXU3rBNQK9viaEqfgw+LMifhd+53VEZPxZk3bTWA==", "dev": true, "dependencies": { "denque": "^2.1.0", @@ -6149,9 +6149,9 @@ } }, "node_modules/qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, "dependencies": { "side-channel": "^1.0.4" @@ -6193,9 +6193,9 @@ } }, "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dev": true, "dependencies": { "bytes": "3.1.2", @@ -8473,9 +8473,9 @@ } }, "@types/dockerode": { - "version": "3.3.19", - "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.19.tgz", - "integrity": "sha512-7CC5yIpQi+bHXwDK43b/deYXteP3Lem9gdocVVHJPSRJJLMfbiOchQV3rDmAPkMw+n3GIVj7m1six3JW+VcwwA==", + "version": "3.3.26", + "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.26.tgz", + "integrity": "sha512-/K+I9bGhRO2SvyIHisGeOsy/ypxnWLz8+Rde9S2tNNEKa3r91e0XMYIEq2D+kb7srm7xrmpAR0CDKfXoZOr4OA==", "dev": true, "requires": { "@types/docker-modem": "*", @@ -8617,16 +8617,16 @@ } }, "archiver": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.1.tgz", - "integrity": "sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", "dev": true, "requires": { "archiver-utils": "^2.1.0", - "async": "^3.2.3", + "async": "^3.2.4", "buffer-crc32": "^0.2.1", "readable-stream": "^3.6.0", - "readdir-glob": "^1.0.0", + "readdir-glob": "^1.1.2", "tar-stream": "^2.2.0", "zip-stream": "^4.1.0" }, @@ -8788,9 +8788,9 @@ "dev": true }, "async-lock": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.0.tgz", - "integrity": "sha512-coglx5yIWuetakm3/1dsX9hxCNox22h7+V80RQOu2XUUMidtArxKoZoOtHUPuR84SycKTXzgGzAUR5hJxujyJQ==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", "dev": true }, "asynckit": { @@ -8806,12 +8806,12 @@ "dev": true }, "axios": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", - "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", "dev": true, "requires": { - "follow-redirects": "^1.15.0", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -8866,21 +8866,21 @@ "dev": true }, "body-parser": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", - "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dev": true, "requires": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.10.3", - "raw-body": "2.5.1", + "qs": "6.11.0", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -9091,9 +9091,9 @@ } }, "cluster-key-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", - "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", "dev": true }, "co": { @@ -9185,9 +9185,9 @@ } }, "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true }, "convert-source-map": { @@ -9208,9 +9208,9 @@ } }, "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "dev": true }, "cookie-signature": { @@ -9467,9 +9467,9 @@ "dev": true }, "docker-compose": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.24.2.tgz", - "integrity": "sha512-2/WLvA7UZ6A2LDLQrYW0idKipmNBWhtfvrn2yzjC5PnHDzuFVj1zAZN6MJxVMKP0zZH8uzAK6OwVZYHGuyCmTw==", + "version": "0.24.7", + "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.24.7.tgz", + "integrity": "sha512-CdHl9n0S4+bl4i6MaxDQHNjqB1FdvuDirrDTzPKmdiMpheQqCjgsny0GZ2VhvN7qHTY0833lRlKWZgrkn1i6cg==", "dev": true, "requires": { "yaml": "^2.2.2" @@ -10189,17 +10189,17 @@ } }, "express": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", - "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dev": true, "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.0", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -10215,7 +10215,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.10.3", + "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.18.0", @@ -10440,9 +10440,9 @@ "integrity": "sha512-PIaqOGvVH5P+R92Ywy5PumsNEHvondVQh42SGOmkA9A0ZTFbfguzZpjZ/Gy3WVRUqT9Ia8k5tWlJeiZQzRHA7g==" }, "follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "dev": true }, "for-each": { @@ -11863,9 +11863,9 @@ } }, "mysql2": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.6.1.tgz", - "integrity": "sha512-O7FXjLtNkjcMBpLURwkXIhyVbX9i4lq4nNRCykPNOXfceq94kJ0miagmTEGCZieuO8JtwtXaZ41U6KT4eF9y3g==", + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.6.tgz", + "integrity": "sha512-9NYUMLQv6yXnu+5hUh8PZ5CdKoG6VWDzXbojIdTyob8upNZXU3rBNQK9viaEqfgw+LMifhd+53VEZPxZk3bTWA==", "dev": true, "requires": { "denque": "^2.1.0", @@ -12528,9 +12528,9 @@ "dev": true }, "qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, "requires": { "side-channel": "^1.0.4" @@ -12549,9 +12549,9 @@ "dev": true }, "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dev": true, "requires": { "bytes": "3.1.2", diff --git a/package.json b/package.json index 7185cfd4..066b46af 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pinpoint-node-agent", - "version": "0.9.0-next.4", + "version": "1.0.0-next.1", "main": "index.js", "types": "index.d.ts", "type": "commonjs", @@ -23,7 +23,7 @@ }, "homepage": "https://github.com/pinpoint-apm/pinpoint-node-agent", "engines": { - "node": ">=14.0" + "node": ">=16.0" }, "keywords": [ "pinpoint", @@ -64,7 +64,7 @@ "devDependencies": { "@types/semver": "^7.3.13", "@types/shimmer": "^1.0.2", - "axios": "^1.6.2", + "axios": "^1.6.8", "eslint": "^8.43.0", "eslint-config-prettier": "^3.6.0", "eslint-plugin-import": "^2.25.2", diff --git a/test/agent.test.js b/test/agent.test.js index 21f66879..92bce2a0 100644 --- a/test/agent.test.js +++ b/test/agent.test.js @@ -14,5 +14,5 @@ test('Should initialize agent', function (t) { const agent = require('./support/agent-singleton-mock') t.ok(agent) - t.equal(agent.pinpointClient.agentInfo.agentVersion, '0.9.0-next.4', 'agent version from package.json') + t.equal(agent.pinpointClient.agentInfo.agentVersion, '1.0.0-next.1', 'agent version from package.json') }) \ No newline at end of file diff --git a/test/client/bounded-buffer-readable-stream.test.js b/test/client/bounded-buffer-readable-stream.test.js new file mode 100644 index 00000000..397c60ee --- /dev/null +++ b/test/client/bounded-buffer-readable-stream.test.js @@ -0,0 +1,226 @@ +/** + * Pinpoint Node.js Agent + * Copyright 2021-present NAVER Corp. + * Apache License v2.0 + */ + +'use strict' + +const test = require('tape') +const BoundedBufferReadableStream = require('../../lib/client/bounded-buffer-readable-stream') +const { Writable } = require('node:stream') +const semver = require('semver') + +test('no piped readable steam', (t) => { + const readable = new BoundedBufferReadableStream() + readable.push('test1') + readable.push('test2') + readable.push(null) + + t.equal(readable.readableStream.readable, true, 'no writable stream piped readable steam not started') + t.equal(readable.buffer.length, 3, 'no writable stream piped readable steam buffer is not empty') + t.end() +}) + +test('piped readable steam', (t) => { + const readable = new BoundedBufferReadableStream({ encoding: 'utf8' }) + readable.push('test1') + readable.push('test2') + readable.push(null) + + readable.pipe(() => { + const actualSteams = [] + const writable = new Writable({ + write(chunk, encoding, callback) { + actualSteams.push(chunk.toString()) + callback() + } + }) + writable.on('finish', () => { + t.equal(readable.readableStream.readable, false, 'piped readable steam finished') + t.equal(readable.buffer.length, 0, 'piped readable steam buffer is empty') + + t.equal(actualSteams.length, 2, 'piped readable steam') + t.equal(actualSteams[0], 'test1', 'piped readable steam test1') + t.equal(actualSteams[1], 'test2', 'piped readable steam test2') + + t.end() + }) + return writable + }) +}) + +test('reconnect writable stream on piped readable stream', (t) => { + t.plan(10) + const readable = new BoundedBufferReadableStream({ encoding: 'utf8' }) + readable.push('test1') + readable.push('test2') + + readable.readableStream.on('error', () => { + t.fail('piped readable steam error event is not called') + }) + + readable.readableStream.on('end', () => { + t.fail('piped readable steam end event is not called') + }) + + readable.readableStream.on('close', () => { + t.fail('piped readable steam close event is not called') + }) + + readable.readableStream.on('unpipe', () => { + t.fail('piped readable steam unpipe event is not called') + }) + + readable.pipe(() => { + let count = 0 + const actualSteams = [] + const writableStream = new Writable({ + write(chunk, encoding, callback) { + actualSteams.push(chunk.toString()) + callback() + count++ + + if (count === 2) { + // Writable.prototype.pipe is only public method for errorOrDestroy + // Writable.prototype.pipe = function() { + // errorOrDestroy(this, new ERR_STREAM_CANNOT_PIPE()); + // }; + writableStream.pipe() + } + } + }) + writableStream.on('error', (error) => { + t.equal(error.message, 'Cannot pipe, not readable', 'piped writable steam error') + t.equal(readable.readableStream.readable, true, 'piped readable steam is readable on close event') + t.equal(readable.buffer.length, 0, 'piped readable steam buffer is empty') + + t.equal(actualSteams.length, 2, 'piped readable steam') + t.equal(actualSteams[0], 'test1', 'piped readable steam test1') + t.equal(actualSteams[1], 'test2', 'piped readable steam test2') + + t.false(readable.readable, 'piped readable steam is unpiped, so is not readable') + }) + + writableStream.on('finish', () => { + t.fail('piped writable steam finish event is not called') + }) + + writableStream.on('close', () => { + t.equal(readable.readableStream.readable, true, 'piped readable steam is no readable on close event') + t.equal(writableStream.destroyed, true, 'piped writable steam is destroyed on close event') + }) + + writableStream.on('unpipe', () => { + t.true(true, 'piped readable steam unpipe event is not called') + }) + + return writableStream + }) +}) + +test('If the Readable stream emits an error during processing, the writable destination is not closed automatically. If an error occurs, Close each streams, ', async (t) => { + if (semver.satisfies(process.versions.node, '<17.0')) { + t.plan(15) + } else { + t.plan(18) + } + + const readable = new BoundedBufferReadableStream() + readable.push('test1') + readable.push('test2') + readable.readableStream.on('error', (error) => { + t.equal(error.message, 'The "chunk" argument must be of type string or an instance of Buffer or Uint8Array. Received type number (1)', 'readable steam error message check') + t.equal(readable.buffer.length, 0, 'readable steam buffer is empty') + t.equal(readable.readableStream.readable, false, 'the readable of readable steam is false on error event') + if (semver.satisfies(process.versions.node, '>17.0')) { + t.true(readable.readableStream.closed, 'closed property is true on error event of readable steam') + } + t.true(readable.readableStream.destroyed, 'destroyed property is true on error event of readable steam') + }) + readable.readableStream.on('end', () => { + t.fail('readable steam end event is not called') + }) + readable.readableStream.on('close', function () { + t.false(this.readable, 'readable property is false on close event of readable steam') + if (semver.satisfies(process.versions.node, '>17.0')) { + t.true(this.closed, 'closed property is true on close event of readable steam') + } + t.true(this.destroyed, 'destroyed property is true on close event of readable steam') + + // recovery readable steam + // t.true(readable.readableStream.readable, 'readable property is true on close event of recovery readable steam after error') + }) + + t.equal(readable.buffer[0], 'test1', 'buffer[0] is test1') + + readable.pipe(() => { + const actualSteams = [] + const writableStream = new Writable({ + write(chunk, encoding, callback) { + actualSteams.push(chunk.toString()) + callback() + } + }) + + writableStream.on('error', () => { + t.fail('If readable stream occur an error, piped writable stream error event is not called') + }) + + writableStream.on('finish', function () { + t.comment('If readable stream occur an error, piped writable steam is manually must be close') + t.false(this.closed, 'closed property is false on the finish event of writable steam') + t.false(this.destroyed, 'destroyed property is false on the finish event of writable steam') + t.false(this.writable, 'writable property is false on the finish event of writable steam') + }) + + writableStream.on('close', function () { + if (semver.satisfies(process.versions.node, '>17.0')) { + t.true(this.closed, 'closed property is true on the close event of writable steam') + } + t.true(this.destroyed, 'destroyed property is true on the close event of writable steam') + t.false(this.writable, 'writable property is false on the close event of writable steam') + }) + + writableStream.on('unpipe', function () { + t.comment('If readable stream occur an error, piped writable steam unpipe event is must be called') + t.false(this.closed, 'closed property is false on the unpipe event of writable steam') + t.false(this.destroyed, 'destroyed property is false on the unpipe event of writable steam') + t.false(this.writable, 'writable property is false on the unpipe event of writable steam') + }) + + return writableStream + }) + process.nextTick(() => { + const originalReadStart = readable.readStart + // NODE 17+ + // readable stream has only errorOrDestroy method on _read method + // try { + // this._read(state.highWaterMark); + // } catch (err) { + // errorOrDestroy(this, err); + // } + readable.readStart = () => { + process.nextTick(() => { + readable.readStart = originalReadStart + }) + return readable.readableStream.push(1) + } + + readable.readableStream.push('test3') + }) +}) + +test('Max buffer size', (t) => { + const dut = new BoundedBufferReadableStream({ maxBuffer: 2 }) + dut.push('test1') + dut.push('test2') + t.true(dut.buffer.length === 2, 'buffer size is 2') + + dut.push('test3') + t.true(dut.buffer.length === 2, 'buffer size is 2') + t.equal(dut.buffer[0], 'test1', 'buffer[0] is test1') + t.equal(dut.buffer[1], 'test2', 'buffer[1] is test2') + + t.end() +}) \ No newline at end of file diff --git a/test/context/callstack.test.js b/test/context/callstack.test.js index afd9bccd..541a2325 100644 --- a/test/context/callstack.test.js +++ b/test/context/callstack.test.js @@ -13,6 +13,8 @@ const express = require('express') const defaultPredefinedMethodDescriptorRegistry = require('../../lib/constant/default-predefined-method-descriptor-registry') const MethodDescriptorBuilder = require('../../lib/context/method-descriptor-builder') const localStorage = require('../../lib/instrumentation/context/local-storage') +const http = require('http') +const https = require('https') test(`span and spanEvent call stack`, async (t) => { agent.bindHttp() @@ -74,7 +76,7 @@ test(`fix express call stack depth`, async (t) => { let actualBuilder = new MethodDescriptorBuilder('use') .setClassName('Router') - .setLineNumber(54) + .setLineNumber(56) .setFileName('callstack.test.js') let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) let actualSpanEvent = agent.dataSender.mockSpan.spanEventList[0] @@ -85,7 +87,7 @@ test(`fix express call stack depth`, async (t) => { actualBuilder = new MethodDescriptorBuilder('use') .setClassName('Router') - .setLineNumber(55) + .setLineNumber(57) .setFileName('callstack.test.js') actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) actualSpanEvent = agent.dataSender.mockSpan.spanEventList[1] @@ -136,7 +138,11 @@ test('fix express call stack depth without callSite', async (t) => { app.use('/router1', router1) const server = app.listen(TEST_ENV.port, async function () { - const result1 = await axios.get(getServerUrl(`/router1${path}`)) + const result1 = await axios.get(getServerUrl(`/router1${path}`), { + timeout: 1000, + httpAgent: new http.Agent({ keepAlive: false }), + httpsAgent: new https.Agent({ keepAlive: false }), + }) t.ok(result1.status, 200) t.ok(result1.data, 'ok router1') diff --git a/test/instrumentation/module/express.test.js b/test/instrumentation/module/express.test.js index a413a949..9775f65d 100644 --- a/test/instrumentation/module/express.test.js +++ b/test/instrumentation/module/express.test.js @@ -16,6 +16,8 @@ const semver = require('semver') const MethodDescriptorBuilder = require('../../../lib/context/method-descriptor-builder') const DisableTrace = require('../../../lib/context/disable-trace') const transactionIdGenerator = require('../../../lib/context/sequence-generators').transactionIdGenerator +const http = require('http') +const https = require('https') const TEST_ENV = { host: 'localhost', @@ -43,14 +45,14 @@ test(`${testName1} Should record request in basic route`, function (t) { let actualBuilder = new MethodDescriptorBuilder(expected('get', 'app.get')) .setClassName(expected('app', 'Function')) - .setLineNumber(35) + .setLineNumber(37) .setFileName('express.test.js') const actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) let spanEvent = trace.span.spanEventList[0] t.equal(actualMethodDescriptor.apiId, spanEvent.apiId, 'apiId') t.true(actualMethodDescriptor.apiDescriptor.startsWith(expected('app.get', 'Function.app.get')), 'apiDescriptor') t.equal(actualMethodDescriptor.className, expected('app', 'Function'), 'className') - t.equal(actualMethodDescriptor.lineNumber, 35, 'lineNumber') + t.equal(actualMethodDescriptor.lineNumber, 37, 'lineNumber') t.equal(actualMethodDescriptor.methodName, 'get', 'methodName') t.true(actualMethodDescriptor.location.length > 0, 'location') }) @@ -66,14 +68,14 @@ test(`${testName1} Should record request in basic route`, function (t) { let actualBuilder = new MethodDescriptorBuilder(expected('post', 'app.post')) .setClassName(expected('app', 'Function')) - .setLineNumber(60) + .setLineNumber(62) .setFileName('express.test.js') const actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) let spanEvent = trace.span.spanEventList[0] t.equal(actualMethodDescriptor.apiId, spanEvent.apiId, 'apiId') t.true(actualMethodDescriptor.apiDescriptor.startsWith(expected('app.post', 'Function.app.post')), 'apiDescriptor') t.equal(actualMethodDescriptor.className, expected('app', 'Function'), 'className') - t.equal(actualMethodDescriptor.lineNumber, 60, 'lineNumber') + t.equal(actualMethodDescriptor.lineNumber, 62, 'lineNumber') t.equal(actualMethodDescriptor.methodName, 'post', 'methodName') t.true(actualMethodDescriptor.location.endsWith('express.test.js'), 'location') }) @@ -85,14 +87,14 @@ test(`${testName1} Should record request in basic route`, function (t) { agent.callbackTraceClose((trace) => { let actualBuilder = new MethodDescriptorBuilder(expected('get', 'app.get')) .setClassName(expected('app', 'Function')) - .setLineNumber(82) + .setLineNumber(84) .setFileName('express.test.js') const actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) let spanEvent = trace.span.spanEventList[0] t.equal(actualMethodDescriptor.apiId, spanEvent.apiId, 'apiId') t.true(actualMethodDescriptor.apiDescriptor.startsWith(expected('app.get', 'Function.app.get')), 'apiDescriptor') t.equal(actualMethodDescriptor.className, expected('app', 'Function'), 'className') - t.equal(actualMethodDescriptor.lineNumber, 82, 'lineNumber') + t.equal(actualMethodDescriptor.lineNumber, 84, 'lineNumber') t.equal(actualMethodDescriptor.methodName, 'get', 'methodName') t.true(actualMethodDescriptor.location.endsWith('express.test.js'), 'location') }) @@ -167,14 +169,14 @@ function throwHandleTest(trace, t) { let actualBuilder = new MethodDescriptorBuilder(expected('get', 'app.get')) .setClassName(expected('app', 'Function')) - .setLineNumber(105) + .setLineNumber(107) .setFileName('express.test.js') const actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) let spanEvent = trace.span.spanEventList[1] t.equal(actualMethodDescriptor.apiId, spanEvent.apiId, 'apiId') t.true(actualMethodDescriptor.apiDescriptor.startsWith(expected('app.get', 'Function.app.get')), 'apiDescriptor') t.equal(actualMethodDescriptor.className, expected('app', 'Function'), 'className') - t.equal(actualMethodDescriptor.lineNumber, 105, 'lineNumber') + t.equal(actualMethodDescriptor.lineNumber, 107, 'lineNumber') t.equal(actualMethodDescriptor.methodName, 'get', 'methodName') t.true(actualMethodDescriptor.location.endsWith('express.test.js'), 'location') t.equal(spanEvent.sequence, 0, 'sequence') @@ -182,33 +184,33 @@ function throwHandleTest(trace, t) { actualBuilder = new MethodDescriptorBuilder('use') .setClassName('Router') - .setLineNumber(118) + .setLineNumber(120) .setFileName('express.test.js') const actualErrorMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) spanEvent = trace.span.spanEventList[0] t.equal(actualErrorMethodDescriptor.apiId, spanEvent.apiId, 'apiId') t.true(actualErrorMethodDescriptor.apiDescriptor.startsWith('Router.use'), 'apiDescriptor') t.equal(actualErrorMethodDescriptor.className, 'Router', 'className') - t.equal(actualErrorMethodDescriptor.lineNumber, 118, 'lineNumber') + t.equal(actualErrorMethodDescriptor.lineNumber, 120, 'lineNumber') t.equal(actualErrorMethodDescriptor.methodName, 'use', 'methodName') t.true(actualErrorMethodDescriptor.location.endsWith('express.test.js'), 'location') t.equal(spanEvent.sequence, 1, 'sequence') t.equal(spanEvent.depth, 2, 'spanEvent.depth') t.equal(spanEvent.exceptionInfo.intValue, 1, 'error value') - t.true(spanEvent.exceptionInfo.stringValue.endsWith('express.test.js:108:11'), 'error case') + t.true(spanEvent.exceptionInfo.stringValue.endsWith('express.test.js:110:11'), 'error case') } function nextErrorHandleTest(trace, t) { let actualBuilder = new MethodDescriptorBuilder(expected('get', 'app.get')) .setClassName(expected('app', 'Function')) - .setLineNumber(112) + .setLineNumber(114) .setFileName('express.test.js') const actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) let spanEvent = trace.span.spanEventList[1] t.equal(actualMethodDescriptor.apiId, spanEvent.apiId, 'apiId') t.true(actualMethodDescriptor.apiDescriptor.startsWith(expected('app.get', 'Function.app.get')), 'apiDescriptor') t.equal(actualMethodDescriptor.className, expected('app', 'Function'), 'className') - t.equal(actualMethodDescriptor.lineNumber, 112, 'lineNumber') + t.equal(actualMethodDescriptor.lineNumber, 114, 'lineNumber') t.equal(actualMethodDescriptor.methodName, 'get', 'methodName') t.true(actualMethodDescriptor.location.endsWith('express.test.js'), 'location') t.equal(spanEvent.sequence, 0, 'sequence') @@ -216,20 +218,20 @@ function nextErrorHandleTest(trace, t) { actualBuilder = new MethodDescriptorBuilder('use') .setClassName('Router') - .setLineNumber(118) + .setLineNumber(120) .setFileName('express.test.js') const actualErrorMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) spanEvent = trace.span.spanEventList[0] t.equal(actualErrorMethodDescriptor.apiId, spanEvent.apiId, 'apiId') t.true(actualErrorMethodDescriptor.apiDescriptor.startsWith('Router.use'), 'apiDescriptor') t.equal(actualErrorMethodDescriptor.className, 'Router', 'className') - t.equal(actualErrorMethodDescriptor.lineNumber, 118, 'lineNumber') + t.equal(actualErrorMethodDescriptor.lineNumber, 120, 'lineNumber') t.equal(actualErrorMethodDescriptor.methodName, 'use', 'methodName') t.true(actualErrorMethodDescriptor.location.endsWith('express.test.js'), 'location') t.equal(spanEvent.sequence, 1, 'sequence') t.equal(spanEvent.depth, 2, 'spanEvent.depth') t.equal(spanEvent.exceptionInfo.intValue, 1, 'error value') - t.true(spanEvent.exceptionInfo.stringValue.endsWith('express.test.js:115:10'), 'error case') + t.true(spanEvent.exceptionInfo.stringValue.endsWith('express.test.js:117:10'), 'error case') } const testName2 = 'express2' @@ -258,10 +260,18 @@ test(`[${testName2}] Should record request in express.Router`, function (t) { app.use('/express.router2', router2) const server = app.listen(TEST_ENV.port, async function () { - const result1 = await axios.get(getServerUrl(`/express.router1${PATH}`)) + const result1 = await axios.get(getServerUrl(`/express.router1${PATH}`), { + timeout: 1000, + httpAgent: new http.Agent({ keepAlive: false }), + httpsAgent: new https.Agent({ keepAlive: false }), + }) t.ok(result1.status, 200) - const result2 = await axios.get(getServerUrl(`/express.router2${PATH}`)) + const result2 = await axios.get(getServerUrl(`/express.router2${PATH}`), { + timeout: 3000, + httpAgent: new http.Agent({ keepAlive: false }), + httpsAgent: new https.Agent({ keepAlive: false }), + }) t.ok(result2.status, 200) server.close() @@ -357,14 +367,14 @@ test(`${testName5} Should record middleware`, function (t) { agent.callbackTraceClose((trace) => { let actualBuilder = new MethodDescriptorBuilder(expected('get', 'app.get')) .setClassName(expected('app', 'Function')) - .setLineNumber(356) + .setLineNumber(366) .setFileName('express.test.js') const actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) let spanEvent = trace.span.spanEventList[2] t.equal(actualMethodDescriptor.apiId, spanEvent.apiId, 'apiId') t.true(actualMethodDescriptor.apiDescriptor.startsWith(expected('app.get', 'Function.app.get')), 'apiDescriptor') t.equal(actualMethodDescriptor.className, expected('app', 'Function'), 'className') - t.equal(actualMethodDescriptor.lineNumber, 356, 'lineNumber') + t.equal(actualMethodDescriptor.lineNumber, 366, 'lineNumber') t.equal(actualMethodDescriptor.methodName, 'get', 'methodName') t.true(actualMethodDescriptor.location.endsWith('express.test.js'), 'location') t.equal(spanEvent.sequence, 2, 'sequence') @@ -372,14 +382,14 @@ test(`${testName5} Should record middleware`, function (t) { actualBuilder = new MethodDescriptorBuilder('use') .setClassName('Router') - .setLineNumber(346) + .setLineNumber(356) .setFileName('express.test.js') const actualMiddleware1MethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) spanEvent = trace.span.spanEventList[1] t.equal(actualMiddleware1MethodDescriptor.apiId, spanEvent.apiId, 'apiId') t.true(actualMiddleware1MethodDescriptor.apiDescriptor.startsWith('Router.use'), 'apiDescriptor') t.equal(actualMiddleware1MethodDescriptor.className, 'Router', 'className') - t.equal(actualMiddleware1MethodDescriptor.lineNumber, 346, 'lineNumber') + t.equal(actualMiddleware1MethodDescriptor.lineNumber, 356, 'lineNumber') t.equal(actualMiddleware1MethodDescriptor.functionName, 'use', 'functionName') t.true(actualMiddleware1MethodDescriptor.location.endsWith('express.test.js'), 'location') t.equal(spanEvent.sequence, 1, 'sequence') @@ -387,14 +397,14 @@ test(`${testName5} Should record middleware`, function (t) { actualBuilder = new MethodDescriptorBuilder('use') .setClassName('Router') - .setLineNumber(341) + .setLineNumber(351) .setFileName('express.test.js') const actualMiddleware2MethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) spanEvent = trace.span.spanEventList[0] t.equal(actualMiddleware2MethodDescriptor.apiId, spanEvent.apiId, 'apiId') t.true(actualMiddleware2MethodDescriptor.apiDescriptor.startsWith('Router.use'), 'apiDescriptor') t.equal(actualMiddleware2MethodDescriptor.className, 'Router', 'className') - t.equal(actualMiddleware2MethodDescriptor.lineNumber, 341, 'lineNumber') + t.equal(actualMiddleware2MethodDescriptor.lineNumber, 351, 'lineNumber') t.equal(actualMiddleware2MethodDescriptor.methodName, 'use', 'methodName') t.true(actualMiddleware2MethodDescriptor.location.endsWith('express.test.js'), 'location') t.equal(spanEvent.sequence, 0, 'sequence') @@ -405,7 +415,11 @@ test(`${testName5} Should record middleware`, function (t) { }) const server = app.listen(TEST_ENV.port, async function () { - const result1 = await axios.get(getServerUrl(PATH)) + const result1 = await axios.get(getServerUrl(PATH), { + timeout: 3000, + httpAgent: new http.Agent({ keepAlive: false }), + httpsAgent: new https.Agent({ keepAlive: false }), + }) t.ok(result1.status, 200) t.end() @@ -625,7 +639,7 @@ function throwHandleTestWithoutCallSite(trace, t) { t.equal(spanEvent.sequence, 1, 'sequence') t.equal(spanEvent.depth, 2, 'spanEvent.depth') t.equal(spanEvent.exceptionInfo.intValue, 1, 'error value') - t.true(spanEvent.exceptionInfo.stringValue.endsWith('express.test.js:546:11'), 'error case') + t.true(spanEvent.exceptionInfo.stringValue.endsWith('express.test.js:560:11'), 'error case') } function nextErrorHandleTestWithoutCallSite(trace, t) { @@ -651,7 +665,7 @@ function nextErrorHandleTestWithoutCallSite(trace, t) { t.equal(spanEvent.sequence, 1, 'sequence') t.equal(spanEvent.depth, 2, 'spanEvent.depth') t.equal(spanEvent.exceptionInfo.intValue, 1, 'error value') - t.true(spanEvent.exceptionInfo.stringValue.endsWith('express.test.js:553:10'), 'error case') + t.true(spanEvent.exceptionInfo.stringValue.endsWith('express.test.js:567:10'), 'error case') } test('incoming request by Disable Trace requests', (t) => { @@ -664,7 +678,11 @@ test('incoming request by Disable Trace requests', (t) => { app.get('/', async (req, res) => { actualRequestCount++ - const result = await axios.get(getServerUrl('/api')) + const result = await axios.get(getServerUrl('/api'), { + timeout: 3000, + httpAgent: new http.Agent({ keepAlive: false }), + httpsAgent: new https.Agent({ keepAlive: false }), + }) t.equal(result.status, 200, 'api request status code is 200') t.equal(result.data, 'ok /api get', 'api request data is ok /api get') res.send('ok get') @@ -690,7 +708,11 @@ test('incoming request by Disable Trace requests', (t) => { app.get('/api', async (req, res) => { apiRequests.push(req) - const result = await axios.get(getServerUrl('/api2')) + const result = await axios.get(getServerUrl('/api2'), { + timeout: 3000, + httpAgent: new http.Agent({ keepAlive: false }), + httpsAgent: new https.Agent({ keepAlive: false }), + }) t.equal(result.status, 200, 'api2 request status code is 200') t.equal(result.data, 'ok /api2 get', 'api request data is ok /api2 get') res.send('ok /api get') @@ -724,7 +746,11 @@ test('incoming request by Disable Trace requests', (t) => { }) const server = app.listen(TEST_ENV.port, async function () { - const result1 = await axios.get(getServerUrl('/')) + const result1 = await axios.get(getServerUrl('/'), { + timeout: 3000, + httpAgent: new http.Agent({ keepAlive: false }), + httpsAgent: new https.Agent({ keepAlive: false }), + }) t.equal(result1.status, 200, 'first / request status code is 200') let actualTrace = rootPathTraces[0] @@ -750,7 +776,11 @@ test('incoming request by Disable Trace requests', (t) => { t.equal(actualApiRequest.headers['pinpoint-traceid'], actualTrace.traceId.transactionId.toString(), 'pinpoint-traceid header is root transaction id') t.equal(actualTrace.traceId.transactionId.toString(), actualApiTrace.traceId.transactionId.toString(), 'transaction id equals to root transaction id') - const result2 = await axios.get(getServerUrl('/')) + const result2 = await axios.get(getServerUrl('/'), { + timeout: 3000, + httpAgent: new http.Agent({ keepAlive: false }), + httpsAgent: new https.Agent({ keepAlive: false }), + }) t.equal(result2.status, 200, 'second / request status code is 200') actualTrace = rootPathTraces[1] @@ -760,7 +790,11 @@ test('incoming request by Disable Trace requests', (t) => { t.equal(actualApiRequest.url, '/api', 'DisableTrace api request url is /api') t.equal(actualApiRequest.headers['pinpoint-sampled'], 's0', 'DisableTrace api request pinpoint-sampled header is s0') - const result3 = await axios.get(getServerUrl('/')) + const result3 = await axios.get(getServerUrl('/'), { + timeout: 3000, + httpAgent: new http.Agent({ keepAlive: false }), + httpsAgent: new https.Agent({ keepAlive: false }), + }) t.equal(result3.status, 200, 'third / request status code is 200') actualTrace = rootPathTraces[2] @@ -770,7 +804,11 @@ test('incoming request by Disable Trace requests', (t) => { t.equal(actualApiRequest.url, '/api', 'Third request is DisableTrace api request url is /api') t.equal(actualApiRequest.headers['pinpoint-sampled'], 's0', 'Third request is DisableTrace api request pinpoint-sampled header is s0') - const result4 = await axios.get(getServerUrl('/')) + const result4 = await axios.get(getServerUrl('/'), { + timeout: 3000, + httpAgent: new http.Agent({ keepAlive: false }), + httpsAgent: new https.Agent({ keepAlive: false }), + }) t.equal(result4.status, 200, 'third / request status code is 200') t.end() diff --git a/test/instrumentation/module/koa.test.js b/test/instrumentation/module/koa.test.js index fa2352fe..e38c2764 100644 --- a/test/instrumentation/module/koa.test.js +++ b/test/instrumentation/module/koa.test.js @@ -14,6 +14,8 @@ const Router = require('koa-router') const annotationKey = require('../../../lib/constant/annotation-key') const apiMetaService = require('../../../lib/context/api-meta-service') const MethodDescriptorBuilder = require('../../../lib/context/method-descriptor-builder') +const http = require('http') +const https = require('https') const TEST_ENV = { host: 'localhost', @@ -38,7 +40,7 @@ test(`${testName1} Should record request in basic route koa.test.js`, function ( let actualBuilder = new MethodDescriptorBuilder('get') .setClassName('Router') - .setLineNumber(32) + .setLineNumber(34) .setFileName('koa.test.js') const actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) let spanEvent = trace.span.spanEventList[0] @@ -47,7 +49,7 @@ test(`${testName1} Should record request in basic route koa.test.js`, function ( t.equal(spanEvent.annotations[0].value, '/koa-router1', 'parameter value matching') t.true(actualMethodDescriptor.apiDescriptor.startsWith('Router.get'), 'apiDescriptor') t.equal(actualMethodDescriptor.className, 'Router', 'className') - t.equal(actualMethodDescriptor.lineNumber, 32, 'lineNumber') + t.equal(actualMethodDescriptor.lineNumber, 34, 'lineNumber') t.equal(actualMethodDescriptor.methodName, 'get', 'methodName') t.true(actualMethodDescriptor.location.length > 0, 'location') }) @@ -103,7 +105,11 @@ test(`${testName1} Should record request in basic route koa.test.js`, function ( app.use(router.routes()).use(router.allowedMethods()) const server = app.listen(TEST_ENV.port, async () => { - const result1 = await axios.get(getServerUrl(PATH)) + const result1 = await axios.get(getServerUrl(PATH), { + timeout: 3000, + httpAgent: new http.Agent({ keepAlive: false }), + httpsAgent: new https.Agent({ keepAlive: false }), + }) t.ok(result1.status, 200) const result2 = await axios.post(getServerUrl(PATH)) diff --git a/test/instrumentation/module/mysql2.test.js b/test/instrumentation/module/mysql2.test.js index 1e927b7a..49ef7cc6 100644 --- a/test/instrumentation/module/mysql2.test.js +++ b/test/instrumentation/module/mysql2.test.js @@ -283,7 +283,7 @@ test(`Connection Pool with query hooking`, async (t) => { actualBuilder = new MethodDescriptorBuilder('getConnection') .setClassName('Pool') - .setLineNumber(143) + .setLineNumber(144) .setFileName('pool.js') actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) actualSpanEvent = trace.span.spanEventList.find( spanEvent => spanEvent.sequence == 1) @@ -306,7 +306,7 @@ test(`Connection Pool with query hooking`, async (t) => { actualBuilder = new MethodDescriptorBuilder('query') .setClassName('PoolConnection') - .setLineNumber(153) + .setLineNumber(154) .setFileName('pool.js') actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) actualSpanEvent = actualSpanChunk.spanEventList[0] diff --git a/test/instrumentation/pinpoint-http-header.test.js b/test/instrumentation/pinpoint-http-header.test.js index bb01db31..b13d69ee 100644 --- a/test/instrumentation/pinpoint-http-header.test.js +++ b/test/instrumentation/pinpoint-http-header.test.js @@ -7,10 +7,8 @@ const test = require('tape') const axios = require('axios') const express = require('express') - -const { - fixture -} = require('../test-helper') +const http = require('http') +const https = require('https') const agent = require('../support/agent-singleton-mock') const TEST_ENV = { @@ -48,7 +46,11 @@ function outgoingRequest(t, sampling) { actualTrace = agent.currentTraceObject() - const result1 = await axios.get(getServerUrl(OUTGOING_PATH)) + const result1 = await axios.get(getServerUrl(OUTGOING_PATH), { + timeout: 1000, + httpAgent: new http.Agent({ keepAlive: false }), + httpsAgent: new https.Agent({ keepAlive: false }), + }) t.equal(result1.data, 'ok get', 'result equals') res.send('ok get') }) @@ -70,7 +72,11 @@ function outgoingRequest(t, sampling) { }) const server = app.listen(TEST_ENV.port, async () => { - const result1 = await axios.get(getServerUrl(PATH)) + const result1 = await axios.get(getServerUrl(PATH), { + timeout: 1000, + httpAgent: new http.Agent({ keepAlive: false }), + httpsAgent: new https.Agent({ keepAlive: false }), + }) t.ok(result1.status, 200) server.close() @@ -108,6 +114,9 @@ function incomingRequest(t, sampled) { "pinpoint-host": "localhost:3000" }, params: {}, + timeout: 1000, + httpAgent: new http.Agent({ keepAlive: false }), + httpsAgent: new https.Agent({ keepAlive: false }), } const PATH = '/incommingrequest' @@ -117,7 +126,7 @@ function incomingRequest(t, sampled) { const trace = agent.currentTraceObject() const headers = config.headers - if (trace.traceId) { + if (trace.traceId) { expectedTransactionId = trace.traceId.transactionId.toString() expectedSpanId = trace.traceId.spanId t.equal(expectedTransactionId, headers['pinpoint-traceid']) @@ -130,7 +139,11 @@ function incomingRequest(t, sampled) { t.equal(trace.canSampled(), sampled) } - const result1 = await axios.get(getServerUrl(OUTGOING_PATH)) + const result1 = await axios.get(getServerUrl(OUTGOING_PATH), { + timeout: 1000, + httpAgent: new http.Agent({ keepAlive: false }), + httpsAgent: new https.Agent({ keepAlive: false }), + }) t.equal(result1.data, 'ok get', 'result equals') res.send('ok get') }) @@ -169,7 +182,7 @@ function incomingRequest(t, sampled) { const result1 = await axios.get(getServerUrl(PATH), config) t.ok(result1.status, 200) if (sampled) { - t.equal(typeof agent.dataSender.mockSpan.spanId, "string") + t.equal(typeof agent.dataSender.mockSpan.spanId, "string") t.equal(typeof agent.dataSender.mockSpan.parentSpanId, "string") t.equal(typeof agent.dataSender.mockSpan.traceId.transactionId.agentStartTime, "string") t.equal(typeof agent.dataSender.mockSpan.traceId.transactionId.sequence, "string") @@ -205,7 +218,7 @@ test('incomming request by User', (t) => { t.equal(trace.canSampled(), true) t.equal(typeof trace.traceId.transactionId.agentStartTime, "string") t.equal(typeof trace.traceId.transactionId.sequence, "string") - + const result1 = await axios.get(getServerUrl(OUTGOING_PATH)) t.equal(result1.data, 'ok get', 'result equals') res.send('ok get') @@ -239,7 +252,7 @@ test('incomming request by User', (t) => { const server = app.listen(TEST_ENV.port, async () => { const result1 = await axios.get(getServerUrl(PATH)) t.ok(result1.status, 200) - t.equal(typeof agent.dataSender.mockSpan.spanId, "string") + t.equal(typeof agent.dataSender.mockSpan.spanId, "string") t.equal(typeof agent.dataSender.mockSpan.parentSpanId, "string") t.equal(typeof agent.dataSender.mockSpan.traceId.transactionId.agentStartTime, "string") t.equal(typeof agent.dataSender.mockSpan.traceId.transactionId.sequence, "string") diff --git a/test/instrumentation/request-header-utils.test.js b/test/instrumentation/request-header-utils.test.js index 4693b40a..5be2746c 100644 --- a/test/instrumentation/request-header-utils.test.js +++ b/test/instrumentation/request-header-utils.test.js @@ -7,9 +7,11 @@ const test = require('tape') const axios = require('axios') const http = require('http') +const https = require('https') const { fixture } = require('../test-helper') const RequestHeaderUtils = require('../../lib/instrumentation/request-header-utils') const agent = require('../support/agent-singleton-mock') +agent.bindHttp() const PinpointHeader = require('../../lib/constant/http-header').PinpointHeader const localStorage = require('../../lib/instrumentation/context/local-storage') const express = require('express') @@ -53,7 +55,11 @@ test('Should write pinpoint header', async function (t) { }) }) .listen(5005, async function() { - await axios.get(`http://${endPoint}${rpcName}?q=1`) + await axios.get(`http://${endPoint}${rpcName}?q=1`, { + timeout: 1000, + httpAgent: new http.Agent({ keepAlive: false }), + httpsAgent: new https.Agent({ keepAlive: false }), + }) server.close() t.end() }) diff --git a/test/utils/ant-path-matcher.test.js b/test/utils/ant-path-matcher.test.js index 3cbadd0c..c98ee45d 100644 --- a/test/utils/ant-path-matcher.test.js +++ b/test/utils/ant-path-matcher.test.js @@ -8,7 +8,8 @@ const test = require('tape') const AntPathMatcher = require('../../lib/utils/ant-path-matcher') const axios = require('axios') const express = require('express') - +const http = require('http') +const https = require('https') const agent = require('../support/agent-singleton-mock') let pathMatcher = new AntPathMatcher() @@ -237,7 +238,11 @@ async function outgoingRequest(t, path, expectedSampling, expectUnits) { }) const server = app.listen(TEST_ENV.port, async () => { - const result1 = await axios.get(getServerUrl(PATH)) + const result1 = await axios.get(getServerUrl(PATH), { + timeout: 3000, + httpAgent: new http.Agent({ keepAlive: false }), + httpsAgent: new https.Agent({ keepAlive: false }), + }) t.equal(result1.status, 200, `sampling is ${sampling}, response status 200 ok`) if (expectUnits) {