├── .github └── workflows │ └── flowzone.yml ├── .gitignore ├── .husky └── pre-commit ├── .npmrc ├── .versionbot └── CHANGELOG.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── repo.yml ├── src └── index.ts ├── test └── index.ts ├── tsconfig.dev.json ├── tsconfig.json └── typings └── basic-auth-parser.d.ts /.github/workflows/flowzone.yml: -------------------------------------------------------------------------------- 1 | name: Flowzone 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize, closed] 6 | branches: [main, master] 7 | 8 | jobs: 9 | flowzone: 10 | name: Flowzone 11 | uses: product-os/flowzone/.github/workflows/flowzone.yml@master 12 | secrets: inherit 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /node_modules 3 | package-lock.json 4 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npx --no lint-staged 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.versionbot/CHANGELOG.yml: -------------------------------------------------------------------------------- 1 | - commits: 2 | - subject: Update npm to v11 3 | hash: f4dab65eb98be1788bdbcb004d81a3f9c0f760da 4 | body: | 5 | Update npm from 10.9.2 to 11.2.0 6 | footer: 7 | Change-type: patch 8 | change-type: patch 9 | author: balena-renovate[bot] 10 | nested: [] 11 | version: 4.0.8 12 | title: "" 13 | date: 2025-04-10T15:21:45.809Z 14 | - commits: 15 | - subject: Change the build output folder to "build" 16 | hash: acc983ace3c28e5642094bc169d3115ea8aef0f0 17 | body: "" 18 | footer: 19 | Change-type: patch 20 | change-type: patch 21 | author: Thodoris Greasidis 22 | nested: [] 23 | - subject: Reduce the published files 24 | hash: 392f06691a754bf23637f462af9088462b51b2c4 25 | body: "" 26 | footer: 27 | Change-type: patch 28 | change-type: patch 29 | author: Thodoris Greasidis 30 | nested: [] 31 | version: 4.0.7 32 | title: "" 33 | date: 2025-04-10T12:30:18.478Z 34 | - commits: 35 | - subject: Fix signature for `connect` to enable downstream extension 36 | hash: 409a4c469d9681f782a0cf89b6887e8312d6bda9 37 | body: "" 38 | footer: 39 | Change-type: patch 40 | change-type: patch 41 | author: Pagan Gazzard 42 | nested: [] 43 | version: 4.0.6 44 | title: "" 45 | date: 2025-04-10T12:23:15.513Z 46 | - commits: 47 | - subject: Fix main/types entry points 48 | hash: 0a486ce75f2f5b1b5fd834a6e07b3dfd346e1a30 49 | body: "" 50 | footer: 51 | Change-type: patch 52 | change-type: patch 53 | author: Pagan Gazzard 54 | nested: [] 55 | version: 4.0.5 56 | title: "" 57 | date: 2025-04-10T07:06:23.653Z 58 | - commits: 59 | - subject: Update dependency mocha to v11 60 | hash: 3f88a638bc5c6ae0c80a23d73e3f81b96b94af91 61 | body: | 62 | Update mocha from 10.8.2 to 11.1.0 63 | footer: 64 | Change-type: patch 65 | change-type: patch 66 | author: balena-renovate[bot] 67 | nested: [] 68 | version: 4.0.4 69 | title: "" 70 | date: 2025-04-02T14:05:14.321Z 71 | - commits: 72 | - subject: Update dependency @balena/lint to v9 73 | hash: 0a742e4188ab2915b7c7010184484acfaedbe697 74 | body: | 75 | Update @balena/lint from 8.2.8 to 9.1.4 76 | footer: 77 | Change-type: patch 78 | change-type: patch 79 | author: balena-renovate[bot] 80 | nested: [] 81 | version: 4.0.3 82 | title: "" 83 | date: 2025-03-25T17:09:19.413Z 84 | - commits: 85 | - subject: Update dependencies and switch to Flowzone 86 | hash: 6dad57baf131b030d5a056d26d6b667d651e8fe6 87 | body: "" 88 | footer: 89 | Change-type: patch 90 | change-type: patch 91 | author: Josh Bowling 92 | nested: [] 93 | version: 4.0.2 94 | title: "" 95 | date: 2024-07-23T00:12:25.416Z 96 | - commits: 97 | - subject: Delete .github directory 98 | hash: 1776efbdc4bf897ed0feaed4554065a5c99337af 99 | body: "" 100 | footer: 101 | Change-type: patch 102 | change-type: patch 103 | author: dfunckt 104 | version: 4.0.1 105 | date: 2021-07-01T05:05:07.293Z 106 | - commits: 107 | - subject: convert promise chains to async/await 108 | hash: 9884b5555932b5fb4cb754716825723831b79b53 109 | body: "" 110 | footer: 111 | Change-type: minor 112 | change-type: minor 113 | Signed-off-by: Will Boyce 114 | signed-off-by: Will Boyce 115 | author: Will Boyce 116 | - subject: update deps and target node12/es2019 117 | hash: 5064d66eb86411589de3846b689fafa27b542254 118 | body: "" 119 | footer: 120 | Change-type: major 121 | change-type: major 122 | Signed-off-by: Will Boyce 123 | signed-off-by: Will Boyce 124 | author: Will Boyce 125 | version: 4.0.0 126 | date: 2019-11-19T14:42:08.941Z 127 | - commits: 128 | - subject: Switch to `eventemitter3.EventEmitter` from the stdlib EventEmitter 129 | hash: 87ab668c5f16456e8d71f625c3fab507ffc8c161 130 | body: "" 131 | footer: 132 | Change-type: major 133 | change-type: major 134 | Signed-off-by: Will Boyce 135 | signed-off-by: Will Boyce 136 | author: Will Boyce 137 | version: 3.0.0 138 | date: 2019-04-17T10:19:39.204Z 139 | - commits: 140 | - subject: "rename: Move to `balena-io-modules` org" 141 | hash: 5aaab516f26d99b907ccdec162f96cb3da60efa7 142 | body: "" 143 | footer: 144 | Change-type: patch 145 | change-type: patch 146 | Signed-off-by: Will Boyce 147 | signed-off-by: Will Boyce 148 | author: Will Boyce 149 | version: 2.1.2 150 | date: 2019-04-01T11:39:07.428Z 151 | - commits: 152 | - subject: "rename: s/resin/balena" 153 | hash: fec6eb56329c54bb1b217f5ef0aa12f477e9f879 154 | body: "" 155 | footer: 156 | Change-type: patch 157 | change-type: patch 158 | Signed-off-by: Will Boyce 159 | signed-off-by: Will Boyce 160 | author: Will Boyce 161 | - subject: "npm: Update dependencies and remove `package-lock.json`" 162 | hash: 0651567ddb8cf447d07594068edec92a810c9c81 163 | body: "" 164 | footer: 165 | Change-type: patch 166 | change-type: patch 167 | Signed-off-by: Will Boyce 168 | signed-off-by: Will Boyce 169 | author: Will Boyce 170 | - subject: "codeowners: Add @wrboyce, @Page-, and @hedss" 171 | hash: 26c06c38efa1dc3c1aa69e15adc389b4713e69ae 172 | body: "" 173 | footer: 174 | Change-type: patch 175 | change-type: patch 176 | Signed-off-by: Will Boyce 177 | signed-off-by: Will Boyce 178 | author: Will Boyce 179 | - subject: "versionbot: Add CHANGELOG.yml (for nested changelogs)" 180 | hash: 2e806d65865aac350e1f2d0244aba12eb2d805b9 181 | body: "" 182 | footer: 183 | Change-type: patch 184 | change-type: patch 185 | Signed-off-by: Will Boyce 186 | signed-off-by: Will Boyce 187 | author: Will Boyce 188 | version: 2.1.1 189 | date: 2019-04-01T11:02:08.114Z 190 | - commits: 191 | - author: Will Boyce 192 | body: "" 193 | footers: 194 | change-type: minor 195 | signed-off-by: Will Boyce 196 | hash: f4169a37fd3f50a4153f3271f526623a0091687a 197 | subject: improve error handling 198 | - author: Will Boyce 199 | body: "" 200 | footers: 201 | change-type: patch 202 | signed-off-by: Will Boyce 203 | hash: 33e7467baf0a088108d11359decada4e3b4eadd7 204 | subject: update dependencies 205 | - author: Will Boyce 206 | body: "" 207 | footers: 208 | change-type: patch 209 | signed-off-by: Will Boyce 210 | hash: e88e76667cb17357aebdfe79f1edf674ae309276 211 | subject: enforce `prettier` coding standards 212 | date: 2018-11-22T11:41:20Z 213 | version: 2.1.0 214 | - commits: 215 | - author: Will Boyce 216 | body: "" 217 | footers: 218 | change-type: major 219 | signed-off-by: Will Boyce 220 | hash: 69a3ffd2702c9786a32a2e823d3094d26c05f26e 221 | subject: "middleware-handler: Use custom, simpler, middleware handler" 222 | date: 2018-10-08T15:58:48Z 223 | version: 2.0.0 224 | - commits: 225 | - author: Will Boyce 226 | body: "" 227 | footers: 228 | change-type: minor 229 | signed-off-by: Will Boyce 230 | hash: e10b4c7793f7badaeaf92abe242c6b8276609719 231 | subject: update npm dependencies & fix vulnerabilities 232 | date: 2018-10-08T10:57:35Z 233 | version: 1.5.0 234 | - commits: 235 | - author: Will Boyce 236 | body: "" 237 | footers: 238 | change-type: patch 239 | hash: 958fd47fa52a7fbaa169464813083021b5eb4d01 240 | subject: Relicense under Apache 2.0 241 | date: 2018-04-16T14:39:35Z 242 | version: 1.4.1 243 | - commits: 244 | - author: Will Boyce 245 | body: "" 246 | footers: 247 | change-type: patch 248 | hash: ea9d2bc446255dc722a8bfd71705fe5741d8a314 249 | subject: Generate/distribute source map 250 | - author: Will Boyce 251 | body: "" 252 | footers: 253 | change-type: patch 254 | hash: 7e50e6b736ac97c14c5da471c5c9822b1fe9746d 255 | subject: Run tests with type-checker 256 | - author: Will Boyce 257 | body: "" 258 | footers: 259 | change-type: minor 260 | hash: d63133bcebdcb2d20866d7e7a48ebd42323ab0e3 261 | subject: Tunnel.listen port argument should be string or number 262 | date: 2018-04-10T11:31:47Z 263 | version: 1.4.0 264 | - commits: 265 | - author: Will Boyce 266 | body: "" 267 | footers: 268 | change-type: patch 269 | hash: 8d4549fdb0802ffdc7fe42f3b1cf2474b7220ff3 270 | subject: Use node8 image for building distributed package 271 | date: 2018-04-09T16:33:17Z 272 | version: 1.3.2 273 | - commits: 274 | - author: Will Boyce 275 | body: "" 276 | footers: 277 | change-type: patch 278 | hash: 6ecd0b17daac338d43ea4a881f60d12c32b70b1d 279 | subject: Run `build` script automatically in `prepare` process 280 | date: 2018-04-09T16:02:26Z 281 | version: 1.3.1 282 | - commits: 283 | - author: Will Boyce 284 | body: "" 285 | footers: 286 | change-type: minor 287 | hash: 1a2339b9551913d8bac4535c78984a1944247bf1 288 | subject: distribute generated typings file 289 | - author: Will Boyce 290 | body: "" 291 | footers: 292 | change-type: minor 293 | hash: c0620d7d03d58b9b658273ad64af2a7c44013ca8 294 | subject: add package-lock, publish from node8 build 295 | - author: Will Boyce 296 | body: "" 297 | footers: 298 | change-type: patch 299 | hash: 4c723ee86117d7fdad8138f30c5370ec4ec89279 300 | subject: clean up tests 301 | date: 2018-04-05T09:30:07Z 302 | version: 1.3.0 303 | - commits: 304 | - author: Will Boyce 305 | body: "" 306 | footers: 307 | change-type: minor 308 | hash: 4daead56a215ae0f4f969ca0f188055992b2d1bf 309 | subject: TypeScript re-write 310 | date: 2017-12-11T13:01:38Z 311 | version: 1.2.0 312 | - commits: 313 | - author: Will Boyce 314 | body: |- 315 | * Run CI tests against node versions 4.x, 6.x and 8.x 316 | * Publish 4.x builds of tagged versions to npm 317 | footers: 318 | change-type: patch 319 | hash: 2ce07f86a8093cbc200532bba697ab4cff36f1eb 320 | subject: Setup CircleCI for auto-publishing 321 | date: 2017-11-28T23:41:02Z 322 | version: 1.1.2 323 | - commits: 324 | - author: Will Boyce 325 | body: "" 326 | footers: 327 | change-type: patch 328 | hash: 19e1d14555086de76d7f37e3deeda858ebd5f76a 329 | subject: pin `@types/bluebird` to v3.5.16 330 | date: 2017-11-27T17:46:16Z 331 | version: 1.1.1 332 | - commits: 333 | - author: Will Boyce 334 | body: "" 335 | footers: 336 | change-type: minor 337 | hash: 0ebaf5d6f482e9d24de4c8c7ab32c6c666379585 338 | subject: Add TypeScript typings 339 | date: 2017-11-06T19:16:52Z 340 | version: 1.1.0 341 | - commits: 342 | - author: Will Boyce 343 | body: "" 344 | hash: ff5af7a0d81680689ce800b46cc8faeb2f82fd2d 345 | subject: update `index.js` 346 | - author: Will Boyce 347 | body: "" 348 | footers: 349 | change-type: patch 350 | hash: 4f38e47053480c89398bbfb6bf7a3921513647c7 351 | subject: use `catch-uncommitted` in circle 352 | date: 2017-10-10T11:40:15Z 353 | version: 1.0.1 354 | - commits: 355 | - author: Will Boyce 356 | body: "" 357 | footers: 358 | change-type: major 359 | hash: bf0a336dc8dca0ed1b5a8323adf515663c4637a7 360 | subject: Allow for use of custom tunnel.connect function 361 | - author: Will Boyce 362 | body: "" 363 | footers: 364 | change-type: patch 365 | hash: d7344167015afbda8bf659490999cdb1ab44b713 366 | subject: update dependencies 367 | - author: Will Boyce 368 | body: "" 369 | footers: 370 | change-type: minor 371 | hash: 1ad665e5931d873b35327f7d89972249ea60f0f9 372 | subject: Update `bluebird` to 3.x 373 | date: 2017-10-10T09:31:18Z 374 | version: 1.0.0 375 | - commits: 376 | - author: Will Boyce 377 | body: "" 378 | footers: 379 | change-type: patch 380 | hash: 6acae5e420cbd5c2e47e6ae878966a3cbecba236 381 | subject: Preparation for Versionbot. 382 | date: 2017-10-03T16:06:27Z 383 | version: 0.2.3 384 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file 4 | automatically by Versionist. DO NOT EDIT THIS FILE MANUALLY! 5 | This project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | ## 4.0.8 - 2025-04-10 8 | 9 | * Update npm to v11 [balena-renovate[bot]] 10 | 11 | ## 4.0.7 - 2025-04-10 12 | 13 | * Change the build output folder to "build" [Thodoris Greasidis] 14 | * Reduce the published files [Thodoris Greasidis] 15 | 16 | ## 4.0.6 - 2025-04-10 17 | 18 | * Fix signature for `connect` to enable downstream extension [Pagan Gazzard] 19 | 20 | ## 4.0.5 - 2025-04-10 21 | 22 | * Fix main/types entry points [Pagan Gazzard] 23 | 24 | ## 4.0.4 - 2025-04-02 25 | 26 | * Update dependency mocha to v11 [balena-renovate[bot]] 27 | 28 | ## 4.0.3 - 2025-03-25 29 | 30 | * Update dependency @balena/lint to v9 [balena-renovate[bot]] 31 | 32 | ## 4.0.2 - 2024-07-23 33 | 34 | * Update dependencies and switch to Flowzone [Josh Bowling] 35 | 36 | # v4.0.1 37 | ## (2021-07-01) 38 | 39 | * Delete .github directory [dfunckt] 40 | 41 | ## 4.0.0 - 2019-11-19 42 | 43 | * Convert promise chains to async/await [Will Boyce] 44 | * Update deps and target node12/es2019 [Will Boyce] 45 | 46 | ## 3.0.0 - 2019-04-17 47 | 48 | * Switch to `eventemitter3.EventEmitter` from the stdlib EventEmitter [Will Boyce] 49 | 50 | ## 2.1.2 - 2019-04-01 51 | 52 | * Rename: Move to `balena-io-modules` org [Will Boyce] 53 | 54 | ## 2.1.1 - 2019-04-01 55 | 56 | * Rename: s/resin/balena [Will Boyce] 57 | * Npm: Update dependencies and remove `package-lock.json` [Will Boyce] 58 | * Codeowners: Add @wrboyce, @Page-, and @hedss [Will Boyce] 59 | * Versionbot: Add CHANGELOG.yml (for nested changelogs) [Will Boyce] 60 | 61 | ## 2.1.0 - 2018-11-22 62 | 63 | * Enforce `prettier` coding standards [Will Boyce] 64 | * Update dependencies [Will Boyce] 65 | * Improve error handling [Will Boyce] 66 | 67 | ## v2.0.0 - 2018-10-08 68 | 69 | * Middleware-handler: Use custom, simpler, middleware handler [Will Boyce] 70 | 71 | ## v1.5.0 - 2018-10-08 72 | 73 | * Update npm dependencies & fix vulnerabilities [Will Boyce] 74 | 75 | ## v1.4.1 - 2018-04-16 76 | 77 | * Relicense under Apache 2.0 #26 [Will Boyce] 78 | 79 | ## v1.4.0 - 2018-04-10 80 | 81 | * Tunnel.listen port argument should be string or number #25 [Will Boyce] 82 | * Run tests with type-checker #25 [Will Boyce] 83 | * Generate/distribute source map #25 [Will Boyce] 84 | 85 | ## v1.3.2 - 2018-04-09 86 | 87 | * Use node8 image for building distributed package #24 [Will Boyce] 88 | 89 | ## v1.3.1 - 2018-04-09 90 | 91 | * Run `build` script automatically in `prepare` process #23 [Will Boyce] 92 | 93 | ## v1.3.0 - 2018-04-05 94 | 95 | * Clean up tests #22 [Will Boyce] 96 | * Add package-lock, publish from node8 build #22 [Will Boyce] 97 | * Distribute generated typings file #22 [Will Boyce] 98 | 99 | ## v1.2.0 - 2017-12-11 100 | 101 | * TypeScript re-write #20 [Will Boyce] 102 | 103 | ## v1.1.2 - 2017-11-28 104 | 105 | * Setup CircleCI for auto-publishing #18 [Will Boyce] 106 | 107 | ## v1.1.1 - 2017-11-27 108 | 109 | * Pin `@types/bluebird` to v3.5.16 #17 [Will Boyce] 110 | 111 | ## v1.1.0 - 2017-11-06 112 | 113 | * Add TypeScript typings #16 [Will Boyce] 114 | 115 | ## v1.0.1 - 2017-10-10 116 | 117 | * Use `catch-uncommitted` in circle #15 [Will Boyce] 118 | 119 | ## v1.0.0 - 2017-10-10 120 | 121 | * Update `bluebird` to 3.x #14 [Will Boyce] 122 | * Update dependencies #14 [Will Boyce] 123 | * Allow for use of custom tunnel.connect function #14 [Will Boyce] 124 | 125 | ## v0.2.3 126 | 127 | * Add tests for half-closed connections 128 | 129 | ## v0.2.2 130 | 131 | * Destroy both client<->tunnel and tunnel<->server sockets when either client or server sends a FIN packet 132 | 133 | ## v0.2.1 134 | 135 | * Handle exception when domain does not exist. 136 | * Made sure tests must pass in order to publish. 137 | * Made sure tests are run against latest code changes. 138 | 139 | ## v0.2.0 140 | 141 | * Added support for half-open connections. 142 | 143 | ## v0.1.1 144 | 145 | * Emit connect/connected/error events for logging and error handling 146 | * Add "close" method for closing the tunnel. 147 | 148 | ## v0.1.0 149 | 150 | * Send HTTP/1.0 200 message on success to support old nc versions. 151 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | node-tunnel 2 | ----------- 3 | 4 | [![npm version](https://badge.fury.io/js/node-tunnel.svg)](http://npmjs.org/package/node-tunnel) 5 | [![dependencies](https://david-dm.org/balena-io-modules/node-tunnel.png)](https://david-dm.org/balena-io-modules/node-tunnel.png) 6 | 7 | HTTP tunneling proxy library. 8 | 9 | Installation 10 | ------------ 11 | 12 | Install `node-tunnel` by running: 13 | 14 | ```sh 15 | $ npm install node-tunnel 16 | ``` 17 | 18 | Documentation 19 | ------------- 20 | 21 | 22 | * [Tunnel](#tunnel) 23 | * [.connect(port, host, client, req)](#tunnel.connect) 24 | * [.use( function(req, cltSocket, head, next) )](#tunnel.use) 25 | * [.listen(port)](#tunnel.listen) 26 | * [basicAuth(req, cltSocket, head, next)](#basic_auth) 27 | 28 | 29 | ### Tunnel 30 | 31 | 32 | ### Tunnel.connect(port, host, client, req) 33 | Function that creates a connection between the tunnel and the target server. 34 | It defaults to `Promise.method(net.connect)` which returns `Promise`. 35 | 36 | **Kind**: method of [Tunnel](#tunnel) 37 | **Summary**: Establish the upstream connection. 38 | **Access:** public 39 | 40 | **Example** 41 | ```js 42 | // Create a tunnel with a custom connect method 43 | tunnel = new Tunnel(); 44 | tunnel.connect = (port, host, client, req) => { 45 | console.log(`Establishing tunnel to ${host}:${port}...`); 46 | return Promise.method(net.connect); 47 | }; 48 | ``` 49 | 50 | 51 | ### Tunnel.use( function(req, cltSocket, head, next) ) 52 | Use a middleware function for rewriting request destination (by changing req.url), 53 | add authorization or filter connections to only certain hosts and ports. 54 | 55 | The parameters are the same as the [http](https://nodejs.org/api/http.html#http_event_connect) module passes on "connect" event, 56 | plus a callback function similar to [express](http://expressjs.com) middleware. 57 | 58 | Keep in mind that express middleware do not work with in conjunction with this module. 59 | 60 | **Kind**: method of [Tunnel](#tunnel) 61 | **Summary**: Use a middleware. 62 | **Access:** public 63 | 64 | **Example** 65 | ```js 66 | // Start a tunneling proxy on port 3128 67 | tunnel = new Tunnel(); 68 | tunnel.use( function(req, cltSocket, head, next) { 69 | // Send all connections to port 80 of localhost. 70 | req.url = "http://localhost:80"; 71 | next(); 72 | } ); 73 | tunnel.listen(3128) 74 | ``` 75 | 76 | ### Tunnel.listen(port, [cb]) 77 | Start listening on the given port. An optional callback function is called when tunnel is ready to listen. 78 | 79 | **Kind**: method of [Tunnel](#tunnel) 80 | **Summary**: Start listening. 81 | **Access:** public 82 | **Example** 83 | ```js 84 | tunnel = new Tunnel(); 85 | tunnel.listen(3128, function() { 86 | console.log("Tunnel listening on port 3128"); 87 | }); 88 | ``` 89 | 90 | 91 | ### basicAuth(req, cltSocket, head, next) 92 | Parses Proxy-Authorization header and sets req.auth.username and req.auth.password properties. 93 | 94 | Further middleware should be added to accept or reject connections based on this authentication information. 95 | **Kind**: method of [Tunnel](#tunnel) 96 | **Summary**: Parse Proxy-Authorization header. 97 | **Access:** public 98 | **Example** 99 | ```js 100 | tunnel = new Tunnel(); 101 | tunnel.use(basicAuth); 102 | tunnel.use( function(req, cltSocket, head, next) { 103 | if (req.auth.username != "user" || req.auth.password != "password") { 104 | cltSocket.end() // close connection 105 | return; // no further middleware need to be called 106 | } 107 | next(); 108 | } ); 109 | tunnel.listen(3128, function() { 110 | console.log("Tunnel listening on port 3128"); 111 | }); 112 | ``` 113 | 114 | Support 115 | ------- 116 | 117 | If you're having any problem, please [raise an issue](https://github.com/balena-io-modules/node-tunnel/issues/new) on GitHub and the Balena team will be happy to help. 118 | 119 | Tests 120 | ----- 121 | 122 | Run the test suite by doing: 123 | 124 | ```sh 125 | $ npm install && npm test 126 | ``` 127 | 128 | Contribute 129 | ---------- 130 | 131 | - Issue Tracker: [github.com/balena-io-modules/node-tunnel/issues](https://github.com/balena-io-modules/node-tunnel/issues) 132 | - Source Code: [github.com/balena-io-modules/node-tunnel](https://github.com/balena-io-modules/node-tunnel) 133 | 134 | Before submitting a PR, please make sure that you include tests, and that [coffeelint](http://www.coffeelint.org/) runs without any warning: 135 | 136 | License 137 | ------- 138 | 139 | The project is licensed under the MIT license. 140 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-tunnel", 3 | "version": "4.0.8", 4 | "description": "Http tunneling proxy", 5 | "main": "build/index.js", 6 | "types": "build/index.d.ts", 7 | "repository": { 8 | "type": "git", 9 | "url": "git@github.com:balena-io-modules/node-tunnel" 10 | }, 11 | "files": [ 12 | "build/", 13 | "typings/" 14 | ], 15 | "engines": { 16 | "node": "^22.0.0", 17 | "npm": "^11.0.0" 18 | }, 19 | "engineStrict": true, 20 | "author": "Aleksis Brezas ", 21 | "contributors": [ 22 | "Will Boyce " 23 | ], 24 | "scripts": { 25 | "lint": "balena-lint -t tsconfig.dev.json src test typings", 26 | "lint-fix": "balena-lint -t tsconfig.dev.json --fix src test typings", 27 | "build": "tsc", 28 | "pretest": "npm run build", 29 | "test": "mocha test/index.ts", 30 | "posttest": "npm run lint", 31 | "prepare": "node -e \"try { (await import('husky')).default() } catch (e) { if (e.code !== 'ERR_MODULE_NOT_FOUND') throw e }\" --input-type module && npm run build" 32 | }, 33 | "license": "Apache-2.0", 34 | "dependencies": { 35 | "basic-auth-parser": "^0.0.2", 36 | "eventemitter3": "^5.0.1" 37 | }, 38 | "devDependencies": { 39 | "@balena/lint": "^9.2.2", 40 | "@types/chai": "^4.3.20", 41 | "@types/mocha": "^10.0.10", 42 | "@types/node": "^20.17.30", 43 | "@types/request-promise": "^4.1.51", 44 | "chai": "^4.5.0", 45 | "husky": "^9.1.7", 46 | "lint-staged": "^15.5.0", 47 | "mocha": "^11.2.0", 48 | "request": "^2.88.2", 49 | "request-promise": "^4.2.6", 50 | "ts-node": "^10.9.2", 51 | "typescript": "^5.8.3" 52 | }, 53 | "lint-staged": { 54 | "*.ts": [ 55 | "balena-lint -t tsconfig.dev.json --fix" 56 | ] 57 | }, 58 | "mocha": { 59 | "require": "ts-node/register/transpile-only", 60 | "recursive": true 61 | }, 62 | "versionist": { 63 | "publishedAt": "2025-04-10T15:21:45.887Z" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /repo.yml: -------------------------------------------------------------------------------- 1 | type: node 2 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Balena Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | import basicAuthParser from 'basic-auth-parser'; 17 | import { EventEmitter } from 'eventemitter3'; 18 | import * as http from 'http'; 19 | import * as net from 'net'; 20 | import * as url from 'url'; 21 | 22 | import { version } from '../package.json'; 23 | 24 | export type Middleware = ( 25 | req: Request, 26 | cltSocket: net.Socket, 27 | head: Buffer, 28 | next: () => void, 29 | ) => void; 30 | 31 | export type NetConnectPromise = ( 32 | port: number, 33 | hostname: string, 34 | cltSocket: net.Socket, 35 | req: Request, 36 | ) => Promise; 37 | 38 | interface ConnectSocketOptions { 39 | cltSocket: net.Socket; 40 | hostname: string; 41 | port: number; 42 | head: Buffer; 43 | connect: NetConnectPromise; 44 | req: Request; 45 | } 46 | 47 | // Connect an http socket to another tcp server. 48 | // Based on tunneling proxy code from https://nodejs.org/api/http.html 49 | const connectSocket = async ({ 50 | cltSocket, 51 | hostname, 52 | port, 53 | head, 54 | connect, 55 | req, 56 | }: ConnectSocketOptions) => { 57 | try { 58 | const srvSocket = await connect(port, hostname, cltSocket, req); 59 | cltSocket.write( 60 | `HTTP/1.0 200 Connection Established\r\nProxy-agent: balena-io/node-tunnel (v${version})\r\n\r\n`, 61 | ); 62 | srvSocket.write(head); 63 | srvSocket.pipe(cltSocket, { end: false }); 64 | cltSocket.pipe(srvSocket, { end: false }); 65 | 66 | try { 67 | await new Promise((resolve, reject) => { 68 | cltSocket.on('error', reject); 69 | srvSocket.on('error', reject); 70 | cltSocket.on('end', resolve); 71 | srvSocket.on('end', resolve); 72 | }); 73 | } finally { 74 | srvSocket.destroy(); 75 | cltSocket.destroy(); 76 | } 77 | } catch (err) { 78 | if (cltSocket.writable) { 79 | cltSocket.end('HTTP/1.0 500 Internal Server Error\r\n'); 80 | } 81 | if (!cltSocket.destroyed) { 82 | cltSocket.destroy(); 83 | } 84 | throw err; 85 | } 86 | }; 87 | 88 | // Create an http CONNECT tunneling proxy 89 | // Expressjs-like middleware can be used to change destination (by modifying req.url) 90 | // or for filtering requests (for example by terminating a socket early.) 91 | // 92 | // Returns an object with methods "listen" to start listening on a port, 93 | // and "use" for adding middleware. 94 | // 95 | // Middleware are functions of the form (request, controlSocket, head, next). 96 | export class Request extends http.IncomingMessage { 97 | public auth?: ReturnType; 98 | } 99 | 100 | export class Tunnel extends EventEmitter { 101 | private readonly stack: Middleware[] = []; 102 | private readonly server = http.createServer((_req, res) => { 103 | res.writeHead(405, { 'Content-Type': 'text/plain' }); 104 | res.end('Method not allowed'); 105 | }); 106 | 107 | constructor() { 108 | super(); 109 | 110 | this.use(basicAuth); 111 | this.server.on( 112 | 'connect', 113 | async (req: Request, cltSocket: net.Socket, head: Buffer) => { 114 | try { 115 | await this.handleMiddleware(req, cltSocket, head); 116 | const { hostname, port } = url.parse(`http://${req.url}`); 117 | if (hostname == null || port == null) { 118 | throw new Error('Invalid Request: Hostname or Port missing'); 119 | } 120 | await connectSocket({ 121 | cltSocket, 122 | hostname, 123 | port: parseInt(port, 10), 124 | head, 125 | connect: this.connect, 126 | req, 127 | }); 128 | this.emit('connect', hostname, port, head); 129 | } catch (err) { 130 | this.emit('error', err); 131 | cltSocket.destroy(); 132 | } 133 | }, 134 | ); 135 | } 136 | 137 | public use(middleware: Middleware) { 138 | this.stack.push(middleware); 139 | } 140 | 141 | private async handleMiddleware( 142 | req: Request, 143 | cltSocket: net.Socket, 144 | head: Buffer, 145 | ) { 146 | await new Promise((resolve, reject) => { 147 | let index = 0; 148 | 149 | const next = (err?: Error) => { 150 | const middleware = this.stack[index++]; 151 | if (err != null) { 152 | reject(err); 153 | } else if (middleware != null) { 154 | try { 155 | middleware(req, cltSocket, head, next); 156 | } catch (middlewareErr) { 157 | reject( 158 | new Error( 159 | `Failed to execute tunnel middleware: ${middlewareErr}`, 160 | ), 161 | ); 162 | } 163 | } else { 164 | resolve(true); 165 | } 166 | }; 167 | 168 | next(); 169 | }); 170 | } 171 | 172 | protected async connect( 173 | port: number, 174 | host: string, 175 | // eslint-disable-next-line @typescript-eslint/no-unused-vars -- These are placeholders to match the `NetConnectPromise` signature for downstream extension 176 | _cltSocket: net.Socket, 177 | // eslint-disable-next-line @typescript-eslint/no-unused-vars -- These are placeholders to match the `NetConnectPromise` signature for downstream extension 178 | _req: Request, 179 | ): Promise { 180 | return new Promise((resolve, reject) => { 181 | const socket = net.connect(port, host); 182 | socket.on('connect', () => { 183 | resolve(socket); 184 | }); 185 | socket.on('error', (err) => { 186 | reject(err); 187 | }); 188 | }); 189 | } 190 | 191 | public listen = this.server.listen.bind(this.server); 192 | public close = this.server.close.bind(this.server); 193 | } 194 | 195 | // Proxy authorization middleware for http tunnel. 196 | export const basicAuth: Middleware = (req, _cltSocket, _head, next) => { 197 | const proxyAuth = req.headers['proxy-authorization']; 198 | if (proxyAuth != null) { 199 | req.auth = basicAuthParser(proxyAuth); 200 | } 201 | next(); 202 | }; 203 | -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Balena Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | import { expect } from 'chai'; 17 | import net from 'net'; 18 | import rp from 'request-promise'; 19 | 20 | const request = rp.defaults({ 21 | resolveWithFullResponse: true, 22 | simple: false, 23 | }); 24 | 25 | import * as nodeTunnel from '../src/index'; 26 | 27 | const PORT = '3128'; 28 | 29 | describe('tunnel', function () { 30 | describe('proxy', function () { 31 | // Sometimes connection takes a few seconds to be established and tests fail, 32 | // so set a generous timeout here. 33 | this.timeout(10000); 34 | before(function (done) { 35 | this.tunnel = new nodeTunnel.Tunnel(); 36 | return this.tunnel.listen(PORT, done); 37 | }); 38 | 39 | after(function () { 40 | return this.tunnel.close(); 41 | }); 42 | 43 | it('should proxy http requests', async () => { 44 | const opts = { 45 | url: 'https://api.balena-cloud.com/ping', 46 | proxy: `http://localhost:${PORT}`, 47 | tunnel: true, 48 | }; 49 | 50 | return request.get(opts).then((res) => { 51 | expect(res.statusCode).to.equal(200); 52 | expect(res.body).to.equal('OK'); 53 | }); 54 | }); 55 | }); 56 | 57 | describe('events', function () { 58 | // Some systems have huge dns lookup timeout, so set a timeout of 1 minute. 59 | // I suggest you have options timeout:1 on /etc/resolv.conf so this test runs faster. 60 | this.timeout(60000); 61 | 62 | let events: any[] = []; 63 | before(function (done) { 64 | this.tunnel = new nodeTunnel.Tunnel(); 65 | this.tunnel.on('connect', (...args: any[]) => 66 | events.push({ 67 | name: 'connect', 68 | data: args, 69 | }), 70 | ); 71 | this.tunnel.on('error', (...args: any[]) => 72 | events.push({ 73 | name: 'error', 74 | data: args, 75 | }), 76 | ); 77 | return this.tunnel.listen(PORT, done); 78 | }); 79 | 80 | after(function () { 81 | return this.tunnel.close(); 82 | }); 83 | 84 | it('should generate connect event on success', async () => { 85 | events = []; 86 | 87 | const opts = { 88 | url: 'https://api.balena-cloud.com/ping', 89 | proxy: `http://localhost:${PORT}`, 90 | tunnel: true, 91 | }; 92 | 93 | return request(opts) 94 | .promise() 95 | .delay(500) 96 | .then(() => { 97 | expect(events.length).to.equal(1); 98 | expect(events[0]).to.have.property('name').that.equals('connect'); 99 | expect(events[0]).to.have.property('data'); 100 | expect(events[0].data.length).to.equal(3); 101 | expect(events[0].data[0]).to.equal('api.balena-cloud.com'); 102 | expect(events[0].data[1]).to.equal('443'); 103 | expect(events[0].data[2]).to.be.instanceof(Buffer); 104 | }); 105 | }); 106 | 107 | it('should generate connect and error events on error', async () => { 108 | events = []; 109 | 110 | const opts = { 111 | url: 'https://api.balenanosuchdomain.error/ping', 112 | proxy: `http://localhost:${PORT}`, 113 | tunnel: true, 114 | }; 115 | 116 | return request(opts).catch(() => { 117 | expect(events.length).to.equal(1); 118 | expect(events[0]).to.have.property('name').that.equals('error'); 119 | expect(events[0]).to.have.property('data'); 120 | expect(events[0].data.length).to.equal(1); 121 | expect(events[0].data[0]).to.be.instanceof(Error); 122 | }); 123 | }); 124 | }); 125 | 126 | describe('half-close connections between tunnel and server', function () { 127 | // The test server listening on 8080 does not send a FIN packet back when it receives 128 | // one from VPN tunnel (allowHalfOpen setting). The VPN tunnel should anyway close the 129 | // connection fully from its side, or else the connection it will remain bound, indefinitely, 130 | // to the node process with a FIN_WAIT_2 state, therefore wasting resources. 131 | // 132 | // If the timeout is hit during tests then the VPN tunnel can potentially leak connections. 133 | this.timeout(5000); 134 | 135 | // tunnel <-> server connection socket 136 | let sock: net.Socket; 137 | const serverPort = 8080; 138 | const connectStr = `CONNECT localhost:${serverPort} HTTP/1.0\r\nHost: localhost:${serverPort}\r\n\r\n`; 139 | 140 | beforeEach(function (done) { 141 | this.tunnel = new nodeTunnel.Tunnel(); 142 | this.tunnel.connect = async ( 143 | port: number, 144 | host: string, 145 | ): Promise => { 146 | return new Promise((resolve, reject) => { 147 | sock = net.connect(port, host); 148 | sock.on('connect', () => { 149 | resolve(sock); 150 | }); 151 | sock.on('error', (err) => { 152 | reject(err); 153 | }); 154 | }); 155 | }; 156 | this.tunnel.listen(PORT, done); 157 | }); 158 | 159 | afterEach(function () { 160 | this.tunnel.close(); 161 | this.server.close(); 162 | }); 163 | 164 | it('should be fully closed when client sends FIN', function (done) { 165 | this.server = net.createServer({ allowHalfOpen: true }, () => { 166 | // tunnel <-> server connection properly closed from the tunnel side 167 | sock.on('close', () => { 168 | done(); 169 | }); 170 | }); 171 | 172 | this.server.listen(serverPort, () => { 173 | net.createConnection(PORT, function (this: net.Socket) { 174 | this.write(connectStr, (err) => { 175 | if (err) { 176 | done(err); 177 | } 178 | }); 179 | this.on('data', () => { 180 | // send FIN to tunnel server 181 | this.end(); 182 | }); 183 | }); 184 | }); 185 | }); 186 | 187 | it('should be fully closed when server sends FIN', function (done) { 188 | this.server = net.createServer({ allowHalfOpen: true }, (socket) => { 189 | // tunnel <-> server connection properly closed from the tunnel side 190 | sock.on('close', done); 191 | // send FIN to tunnel server 192 | socket.end(); 193 | }); 194 | 195 | this.server.listen(serverPort, () => 196 | net.createConnection(PORT, function (this: net.Socket) { 197 | this.write(connectStr); 198 | }), 199 | ); 200 | }); 201 | }); 202 | }); 203 | -------------------------------------------------------------------------------- /tsconfig.dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "." 5 | }, 6 | "include": [ 7 | "build/**/*", 8 | "src/**/*", 9 | "test/**/*", 10 | "typings/**/*.d.ts" 11 | ] 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "Node16", 4 | "strict": true, 5 | "strictFunctionTypes": false, 6 | "noImplicitThis": false, 7 | "noUnusedParameters": true, 8 | "noUnusedLocals": true, 9 | "outDir": "build", 10 | "preserveConstEnums": true, 11 | "removeComments": true, 12 | "rootDir": "src", 13 | "sourceMap": true, 14 | "target": "es2022", 15 | "declaration": true, 16 | "skipLibCheck": true, 17 | "resolveJsonModule": true, 18 | "allowJs": true, 19 | "checkJs": true 20 | }, 21 | "include": [ 22 | "src/**/*", 23 | "typings/**/*.d.ts" 24 | ] 25 | } 26 | 27 | -------------------------------------------------------------------------------- /typings/basic-auth-parser.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Balena Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | declare module 'basic-auth-parser' { 17 | interface BasicAuth { 18 | scheme: string; 19 | username?: string; 20 | password?: string; 21 | } 22 | 23 | const parser: (auth: string) => BasicAuth; 24 | export = parser; 25 | } 26 | --------------------------------------------------------------------------------