pax_global_header00006660000000000000000000000064152001232770014511gustar00rootroot0000000000000052 comment=7706d75dd5a63b45c4fca64b84f2cdf513c62288 amqp-node-amqplib-263c8a3/000077500000000000000000000000001520012327700153505ustar00rootroot00000000000000amqp-node-amqplib-263c8a3/.claude/000077500000000000000000000000001520012327700166635ustar00rootroot00000000000000amqp-node-amqplib-263c8a3/.claude/commands/000077500000000000000000000000001520012327700204645ustar00rootroot00000000000000amqp-node-amqplib-263c8a3/.claude/commands/lint-disable.md000066400000000000000000000006641520012327700233630ustar00rootroot00000000000000Please follow the following process when I ask you to disable a lint rule using /lint-disable $rule 1. Create and change to a new branch. Ask for the branch name. 2. If the rule is off by default, delete it (See https://biomejs.dev/linter/javascript/rules/ for rule details) 3. If the rule is on by default, configure it to off in biome.json 4. Update the project changelog 5. Add the changes 6. Commit the changes 7. Push the changes amqp-node-amqplib-263c8a3/.claude/commands/lint-enable.md000066400000000000000000000012301520012327700231740ustar00rootroot00000000000000Please follow the following process when I ask you to enable a lint rule using /lint-enable $rule 1. Create and change to a new branch. Ask for the branch name. 2. Configure the rule to error in biome.json 3. Run lint (npm run lint). If there are NO errors, goto step 8 4. Use biome to SAFELY auto fix (npm run lint -- --fix). If there are errors stop and ask for instruction. 5. Run the tests (npm test). If there are errors, stop and ask for instruction. 6. If the rule defaults to error, delete it (See https://biomejs.dev/linter/javascript/rules/ for rule details) 7. Update the project changelog 8. Add the changes 9. Commit the changes 10. Push the changes amqp-node-amqplib-263c8a3/.github/000077500000000000000000000000001520012327700167105ustar00rootroot00000000000000amqp-node-amqplib-263c8a3/.github/ISSUE_TEMPLATE/000077500000000000000000000000001520012327700210735ustar00rootroot00000000000000amqp-node-amqplib-263c8a3/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000022601520012327700235650ustar00rootroot00000000000000--- name: Bug report / Support request about: Report a bug or ask for help title: '' labels: '' assignees: '' --- Before reporting a bug or requesting support please... 1. Read the troubleshooting guide - https://amqp-node.github.io/amqplib/#troubleshooting 2. Search for existing open and closed issues - https://github.com/amqp-node/amqplib/issues?q=is%3Aissue+TYPE+YOUR+KEYWORDS+HERE 3. Ensure you are familiar with the amqplib Channel API and RabbitMQ basics - https://amqp-node.github.io/amqplib/channel_api.html - https://www.cloudamqp.com/blog/part1-rabbitmq-for-beginners-what-is-rabbitmq.html - https://www.rabbitmq.com/tutorials/amqp-concepts.html If the above does not help, please provide as much of the following information as is relevant... - A clear and concise description of the problem - RabbitMQ version - amqplib version - Node.js version - The simplest possible code snippet that demonstrates the problem - A stack trace Please format code snippets and/or stack traces using GitHub Markdown - https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks). Thank you amqp-node-amqplib-263c8a3/.github/workflows/000077500000000000000000000000001520012327700207455ustar00rootroot00000000000000amqp-node-amqplib-263c8a3/.github/workflows/publish.yml000066400000000000000000000024461520012327700231440ustar00rootroot00000000000000name: Publish on: release: types: [created] jobs: build: runs-on: ubuntu-latest services: rabbitmq: image: rabbitmq:4.2-alpine ports: - 5672:5672 strategy: matrix: node-version: [18.x, 20.x, 22.x, 24.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} cache: "npm" - run: npm ci - run: | n=0 while : do sleep 5 echo 'HELO\n\n\n\n' | nc localhost 5672 | grep AMQP [[ $? = 0 ]] && break || ((n++)) (( n >= 5 )) && break done - run: echo 'HELO\n\n\n\n' | nc localhost 5672 | grep AMQP - run: make test publish: needs: build runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v4 with: node-version: '20.x' cache: "npm" registry-url: https://registry.npmjs.org/ - run: npm ci - run: npm publish --dry-run env: NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} amqp-node-amqplib-263c8a3/.github/workflows/test.yml000066400000000000000000000026161520012327700224540ustar00rootroot00000000000000# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions name: Node.js CI on: push: branches: [main] pull_request: branches: [main] jobs: build: runs-on: ubuntu-latest services: rabbitmq: image: rabbitmq:4.2-alpine ports: - 5672:5672 strategy: matrix: node-version: [18.x, 20.x, 22.x, 24.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: "npm" # Install all prerequisites - run: npm ci # Ensure RabbitMQ is available before continuing - run: | n=0 while : do sleep 5 echo 'HELO\n\n\n\n' | nc localhost 5672 | grep AMQP [[ $? = 0 ]] && break || ((n++)) (( n >= 5 )) && break done - run: echo 'HELO\n\n\n\n' | nc localhost 5672 | grep AMQP # Check TypeScript types - run: npm run typecheck # Run the tests - run: make test amqp-node-amqplib-263c8a3/.gitignore000066400000000000000000000000741520012327700173410ustar00rootroot00000000000000*~ scratch node_modules/ etc/ coverage/ /.idea/ .nyc_output/amqp-node-amqplib-263c8a3/.npmignore000066400000000000000000000002141520012327700173440ustar00rootroot00000000000000*~ scratch # do not ignore lib/defs.js, we need that # node_modules is ignored anyway .travis.yml etc/ coverage/ tsconfig.types.json types/ amqp-node-amqplib-263c8a3/CHANGELOG.md000066400000000000000000000505741520012327700171740ustar00rootroot00000000000000# Change log for amqplib ## v2.0.1 - Remove `buffer-more-ints` dependency; use Node.js built-in BigInt Buffer methods (`readBigInt64BE`, `readBigUInt64BE`, `writeBigInt64BE`, `writeBigUInt64BE`) which have been available since Node.js v10.4 ## v2.0.0 - **BREAKING**: `heartbeat: 0` now disables heartbeats, overriding any value suggested by the server. Previously, `0` was treated as "no preference" and the server's suggested value was used. If you are passing `heartbeat: 0` and want to preserve the old behaviour, omit the option or pass `null` instead (fixes #467) ## v1.2.0 - Add bundled TypeScript type definitions (fixes #843) ## v1.1.1 - Forward `handler-error` events from the underlying connection to the recovery wrapper, so listeners attached to `RecoveringPromiseModel`/`RecoveringCallbackModel` receive handler errors ## v1.1.0 - Add connection recovery via `connectWithRecoveryPromise` and `connectWithRecoveryCallback` — automatically reconnects on connection loss with configurable backoff delay, jitter, and retry limits (thanks [@ShiriNmi1520](https://github.com/ShiriNmi1520)) ## v1.0.7 - Fix `update-secret-ok` event not being forwarded by `ChannelModel` and `CallbackModel` (fixes #849) - Add `handler-error` event to connections and channels. If a user-supplied event handler (e.g. `connection.on('close', ...)`, `channel.on('error', ...)`, `channel.on('delivery', ...)` etc.) throws a synchronous error, amqplib will emit a `handler-error` event on the same emitter with the thrown error and the name of the event whose handler threw — provided a `handler-error` listener is registered. If no `handler-error` listener is registered, behaviour is unchanged from previous versions. Note: in previous versions, errors thrown in connection `close` event handlers were silently swallowed; errors thrown in channel event handlers (other than `delivery`/`return`) would kill the channel and possibly the connection (fixes #334). ## v1.0.6 - Fix channel.get() not invoking callback with error on channel close; previously only an error event was emitted (fixes #832). **Note:** if you use the callback API, ensure your channel.get() callbacks handle errors — they will now be invoked in error cases where previously they were not. If you use the promise API, the returned promise now rejects with a proper Error object (with .code, .classId and .methodId properties) rather than a raw close frame. ## v1.0.5 - Fix ConfirmChannel callbacks silently dropped on channel close when some publishes had no callback (fixes #191) ## v1.0.4 - Updated build to use RabbitMQ 4.2 - Fix memory leak in ConfirmChannel.publish when channel is already closed (fixes #842) - Close connection immediately when close() is called while the connection is blocked (fixes #744) ## v1.0.3 - Fix AssertionError crash when backpressure occurs while draining newStreams in Mux (fixes #841) ## v1.0.2 - Replace url-parse with WHATWG URL API ## v1.0.1 - Fix TLS connection failure when connecting to an AMQPS broker using an IP address (IPv4 or IPv6) — setting servername to an IP is not permitted by TLS SNI and throws ERR_INVALID_ARG_VALUE in Node.js 25+ ## v1.0.0 - **BREAKING**: Update minimum Node.js requirement to v18 - Introduce Biome formatter for consistent code formatting - Add automated formatting via pre-commit hooks using Lefthook - Format entire codebase with standardised formatting rules - Add npm format script for manual code formatting - Enable noUnusedFunctionParameters lint rule and fix all violations - Enable noUnusedVariables lint rule and remove all unused variables from codebase - Replace all var declarations with let/const for modern JavaScript standards - Ensure parseInt calls use explicit radix parameter for clarity and reliability - Fix precision loss in test data generators by using JavaScript safe integer limits - Add block scoping to switch statement cases to prevent variable declaration issues - Enforce const usage for variables that are never reassigned - Add node: protocol prefix to Node.js builtin module imports for clarity - Use modern exponentiation operator (**) instead of Math.pow() - Replace string concatenation with modern template literals - Remove redundant 'use strict' directives as modules are automatically in strict mode - Refactor assignment-in-expression patterns to improve code clarity and readability - Replace comma operators with individual statements for clearer, more readable code - Split comma-separated variable declarations into individual statements for consistency - Replace Object.prototype.hasOwnProperty() with safer Object.hasOwn() method - Enforce strict equality checks (=== and !==) instead of loose equality (== and !=) - Replace global isNaN with Number.isNaN for safer type checking - Ensure no variable redeclarations exist to prevent shadowing issues - Replace global isFinite with Number.isFinite for safer numeric validation - Enable useArrowFunction lint rule to prefer arrow functions for cleaner syntax - Enable noUselessCatch lint rule to prevent useless catch blocks that only rethrow errors - Enable noArguments rule to enforce modern rest parameters instead of legacy arguments object - Enable bracketSpacing formatting rule to add spaces inside object literals - Migrate test suite from Mocha to Node.js built-in test runner ## v0.10.9 - Add support for IPv6 urls ## v0.10.8 - Updated README ## v0.10.7 - Remove bitsyntax dependency - See https://github.com/amqp-node/amqplib/pull/785. Thanks @ikenfin - Stop checking if frame max is exceeded when parsing frames - See https://github.com/amqp-node/amqplib/pull/784. Thanks @ohroy ## v0.10.6 - Replace references to the old squaremo/amqp.name repo with ones to amqp-node/amqplib - Use a frame_max of 131072 by default for RabbitMQ 4.1.0 compatibility ## Changes in v0.10.5 git log v0.10.4..v0.10.5 - Removed readable stream - See https://github.com/amqp-node/amqplib/issues/729 - Added support for unsigned integers - See https://github.com/amqp-node/amqplib/pull/773 - Committed protocol definitions - See https://github.com/amqp-node/amqplib/commit/0a87ee480311633cff41e43350a90cb3c1221506 ## Changes in v0.10.4 - Improve stream example as per https://github.com/amqp-node/amqplib/issues/722 - Added support for RabbitMQ's connection update-secret operation. See https://github.com/amqp-node/amqplib/issues/755 ## Changes in v0.10.3 git log v0.10.2..v0.10.3 - Use @acuminous/bitsyntax fork. See https://github.com/amqp-node/amqplib/issues/453 ## Changes in v0.10.2 git log v0.10.1..v0.10.2 - Use Buffer.allocUnsafe when sending messages to improve performance ([PR 695](https://github.com/amqp-node/amqplib/pull/695), thank you @chkimes and @Uzlopak) ## Changes in v0.10.1 git log v0.10.0..v0.10.1 * Allow servername to be specified via socket options as discussed in [issue 697](https://github.com/squaremo/amqp.node/issues/697) ## Changes in v0.10.0 git log v0.9.1..v0.10.0 * Use Native promises ([PR 689](https://github.com/amqp-node/amqplib/pull/689), thank you @mohd-akram and @kibertoad) ## Changes in v0.9.1 git log v0.9.0..v0.9.1 * Assorted readme changes * Use Array.prototype.push.apply instead of concat in Mux ([PR 658](https://github.com/squaremo/amqp.node/pull/658), thank you @Uzlopak and @kibertoad) * Use Map instead of Object for BaseChannel.consumers ([PR 660](https://github.com/squaremo/amqp.node/pull/660), thank you @Uzlopak) * Delete consumer callback after cancellation to free memory ([PR 659](https://github.com/squaremo/amqp.node/pull/659), thank you @Uzlopak and @kibertoad) ## Changes in v0.9.0 git log v0.8.0..v0.9.0 * Update mocha and replace the deprecated istanbul with nyc ([PR 681](https://github.com/squaremo/amqp.node/pull/681) * Update url-parse ([PR 675](https://github.com/squaremo/amqp.node/pull/675), thank you @suhail-n and @kibertoad) * fix: done called twice on invalid options ([PR 667](https://github.com/squaremo/amqp.node/pull/667), thank you @luddd3 and @kibertoad) * Close connection to server on connect errors ([PR 647](https://github.com/squaremo/amqp.node/pull/647), thank you @luddd3 and @kibertoad) * Modernise channel_model.js ([PR 635](https://github.com/squaremo/amqp.node/pull/635), thank you @kibertoad and @jimmywarting) * Bring package-lock.json up to date ([PR 653](https://github.com/squaremo/amqp.node/pull/653) * Update url-parse ([PR 652](https://github.com/squaremo/amqp.node/pull/652), thank you @giorgioatanasov and @buffolander) * Modernise channel_model.js ([PR 651](https://github.com/squaremo/amqp.node/pull/651), thank you for the review @kibertoad) * Modernise bitset.js ([PR 634](https://github.com/squaremo/amqp.node/pull/634), thank you @kibertoad and @jimmywarting) * :warning: Drop CI for node versions below 10 ([PR 631](https://github.com/squaremo/amqp.node/pull/631), thank you for the review @kibertoad) * Replace safe-buffer dependency with native buffers ([PR 628](https://github.com/squaremo/amqp.node/pull/628), thank you @kibertoad and @jimmywarting) ## Changes in v0.8.0 git log v0.7.1..v0.8.0 * :warning: Support for NodeJS prior to v10 is dropped :warning: ([PR 615](https://github.com/squaremo/amqp.node/pull/615), thank you @xamgore and everyone who helped there) * Use hostname as TLS servername, to help with using servers behind load balancers ([PR 567](https://github.com/squaremo/amqp.node/pull/567), thanks to @carlhoerberg and commenters) ## Changes in v0.7.1 git log v0.7.0..v0.7.1 * Update url-parse (and others) ([PR 607](https://github.com/squaremo/amqp.node/pull/545), thanks @ThomasGawlitza) ## Changes in v0.7.0 git log v0.6.0..v0.7.0 * Extend support to Node.js v15 * Fix use of stream.write in tests ## Changes in v0.6.0 git log v0.5.6..v0.6.0 * Extend support to Node.js v14 ## Changes in v0.5.6 git log v0.5.5..v0.5.6 * Increase size of encoding space for message headers, to fit e.g., JWT ([PR 545](https://github.com/squaremo/amqp.node/pull/545)); thanks @twatson83 * Switch to a non-deprecated UUID module ([PR 528](https://github.com/squaremo/amqp.node/pull/528)); thanks to @StrayBird-ATSH * Fix a bug in multiplexing that caused an assertion to fail ([PR 503](https://github.com/squaremo/amqp.node/pull/503)); thanks @johanneswuerbach ## Changes in v0.5.5 git log v0.5.3..v0.5.5 **NB** this includes a minor but possibly breaking change: after [PR 498](https://github.com/squaremo/amqp.node/pull/498), all confirmation promises still unresolved will be rejected when their associated channel is closed. * Generate defs in `npm prepare` rather than `npm prepublish` so that e.g., amqplib can be installed via git ([part of PR 498](https://github.com/squaremo/amqp.node/pull/498)) * Reject all pending confirmations when the channel is closed ([PR 498](https://github.com/squaremo/amqp.node/pull/498)); thanks @johanneswuerbach * Update supported NodeJS versions in package.json ([PR 525](https://github.com/squaremo/amqp.node/pull/525)); thanks @tingwai ## (Deprecated v0.5.4) This release was mistakenly published without the generated file `./defs.js`. It has been deprecated in favour of v0.5.5. ## Changes in v0.5.3 git log v0.5.2..v0.5.3 * Bump bitsyntax to remove some `Buffer` misuse deprecation notices ([PR 480])(https://github.com/squaremo/amqp.node/pull/480) * Test on node 11.1 ([PR 473])(https://github.com/squaremo/amqp.node/pull/464); thanks @kibertoad * Updated various dependencies * Support queue mode during assertQueue ([PR 464])(https://github.com/squaremo/amqp.node/pull/464); thanks @JoeTheFkingFrypan * Expose serverProperties in the connection object ([PR 452])(https://github.com/squaremo/amqp.node/pull/452); thanks @jfromaniello * Test on Node 10 ([PR 454])(https://github.com/squaremo/amqp.node/pull/454); thanks @kibertoad * Support amqplain credentials ([PR 451])(https://github.com/squaremo/amqp.node/pull/451); thanks @jfromaniello * Decorate channel errors with methodId and classId ([PR 447])(https://github.com/squaremo/amqp.node/pull/447); thanks @MitMaro * Resolve issues caused by Node 10 `readable` changes ([PR 442])(https://github.com/squaremo/amqp.node/pull/442) * Bump uglify to 2.6.x and node to 9.1 due to nodejs/node#16781. ([PR 439])(https://github.com/squaremo/amqp.node/pull/439) * Updated README with more modern Buffer syntax ([PR 438](https://github.com/squaremo/amqp.node/pull/438); thanks @ravshansbox * Support overflow option to assert queue ([PR 436])(https://github.com/squaremo/amqp.node/pull/436); thanks to @honestserpent * Replace instances of keyword `await` ([PR 396])(https://github.com/squaremo/amqp.node/pull/396), as discussed in [issue 235](https://github.com/squaremo/amqp.node/issues/235) * Use 3rd party url for better decoding of username/password ([PR 395])(https://github.com/squaremo/amqp.node/pull/395), as discussed in [issue 385](https://github.com/squaremo/amqp.node/issues/385)) ## Changes in v0.5.2 git log v0.5.1..v0.5.2 * Increase encoding buffer to accommodate large header values ([PR 367](https://github.com/squaremo/amqp.node/pull/367)) * Bring code up to date with new Buffer interface ([PR 350](https://github.com/squaremo/amqp.node/pull/350)) * Fix dangling connection problem ([PR 340](https://github.com/squaremo/amqp.node/pull/340)) * Clear up URL credentials parsing ([PR 330](https://github.com/squaremo/amqp.node/pull/330)) * Allow connection params to be suppied in object ([PR 304](https://github.com/squaremo/amqp.node/pull/304)) * Support explicit numeric types in field tables (e.g., headers) ([PR 389](https://github.com/squaremo/amqp.node/pull/389), from a suggestion in [issue 358](https://github.com/squaremo/amqp.node/issues/358)) Thank you to all contributors, of PRs, issues and comments. ## Changes in v0.5.1 git log v0.5.0..v0.5.1 * Fix mistake in closeBecause ([PR 298](https://github.com/squaremo/amqp.node/pull/298); thanks to @lholznagel and others who reported the issue, and to @nfantone for the rapid fix) ## Changes in v0.5.0 git log v0.4.2..v0.5.0 * Port to use bluebird rather than when.js ([PR 295](https://github.com/squaremo/amqp.node/pull/295); thanks to @nfantone, and special mention to @myndzi for #158) * Fixed a problem with using `channel.get` in the callback model ([PR 283](https://github.com/squaremo/amqp.node/pull/283); good catch, @shanksauce) * Added an example that uses generators (thanks @rudijs) * Fixed a link in the comments relating to heartbeats (thanks @tapickell) ## Changes in v0.4.2 git log v0.4.1..v0.4.2 * Better documentation and examples * Replace uses of ES6 keyword 'await' ## Changes in v0.4.1 git log v0.4.0..v0.4.1 * Tested in Node.JS 0.8 through 4.2 and 5.5 * Emit an error with the 'close' event if server-initiated ## Changes in v0.4.0 git log v0.3.2..v0.4.0 * Tested on Node.JS 0.8 through 4.0 (and intervening io.js releases) * Change meaning of 'b' fields in tables to match RabbitMQ (and AMQP specification) * Can now pass an object in place of connection URL ([PR 159](https://github.com/squaremo/amqp.node/pull/159); thanks to @ben-page) * Operator-initiated connection close no longer results in 'error' event ([issue 110](https://github.com/squaremo/amqp.node/issues/110)) * Channel and Connection errors have now a `.code` field with the AMQP reply-code, which may help distinguish error cases ([PR 150](https://github.com/squaremo/amqp.node/pull/150); thanks to @hippich) * Connection.close will resolve to an error if the connection is already closed ([issue 181](https://github.com/squaremo/amqp.node/issues/181)) * Connection establishment will resolve with an error if the TCP-level connection or the handshake times out ([PR 169](https://github.com/squaremo/amqp.node/pull/169); thanks to @zweifisch and @RoCat, who both submitted fixes) * Add the `maxPriority` option as an alias for the `'x-max-priority'` queue argument ([PR 180](https://github.com/squaremo/amqp.node/pull/180); thanks to @ebardes) ## Changes in v0.3.2 (since v0.3.1) git log v0.3.1..v0.3.2 * Make the engine specification more flexible to admit io.js releases ## Changes in v0.3.1 (since v0.3.0) git log v0.3.0..v0.3.1 ### Fixes * Fail in the right way when a channel cannot be allocated [issue 129](https://github.com/squaremo/amqp.node/issues/129) * Make `waitForConfirms` work properly in callback API [PR 116](https://github.com/squaremo/amqp.node/pull/116) ### Enhancements * Two new options while connecting: [timeout](https://github.com/squaremo/amqp.node/pull/118) and [keep alive](https://github.com/squaremo/amqp.node/pull/125) (thanks to @rexxars and @jcrugzz respectively) ## Changes in v0.3.0 (since v0.2.1) git log v0.2.1..v0.3.0 ### Enhancements * Allow additional client properties to be set for a connection [Issue 98](https://github.com/squaremo/amqp.node/issues/98) and [PR 80](https://github.com/squaremo/amqp.node/pull/80) * New method in channel API to wait for all unconfirmed messages [Issue 89](https://github.com/squaremo/amqp.node/issues/89) * Now supports RabbitMQ's `EXTERNAL` authentication plugin [Issue 105](https://github.com/squaremo/amqp.node/issues/105) ## Changes in v0.2.1 (since v0.2.0) ### Fixes * Do tuning negotation properly [PR 84](https://github.com/squaremo/amqp.node/pull/84) ## Changes in v0.2.0 (since v0.1.3) git log v0.1.3..v0.2.0 ### Fixes * Correctly deal with missing fields (issue 48) ### Enhancements * Added a callback-oriented API, parallel to the existing, promise-oriented API. * The response to assertExchange now contains the exchange name, analagous to assertQueue (issue 49) * The channel method `prefetch` now has a global flag, to be [compatible with newer RabbitMQ][rabbitmq-prefetch-global]. ## Changes in v0.1.3 (since v0.1.2) git log v0.1.2..v0.1.3 ### Enhancements * Add support in the API for using Basic.Reject rather than Basic.Nack, the latter of which is a RabbitMQ extension and not in older versions of RabbitMQ. ## Changes in v0.1.2 (since v0.1.1) git log v0.1.1..v0.1.2 ### Fixes * Restore support for publishing zero-length messages ### Enhancements * Recognise [authentication failures][rabbitmq-auth-failure] * An option to set TCP_NODELAY on connection sockets ## Changes in v0.1.1 (since v0.1.0) git log v0.1.0..v0.1.1 ### Fixes * Safer frame construction, no longer relies on allocating a large, fixed-size buffer and hoping it's big enough * The ports of RabbitMQ tutorials now avoid a race between publishing and closing the connection ### Enhancements * Support for RabbitMQ's consumer priority extension * Support for RabbitMQ's connnection.blocked extension * Better write speed from batching frames for small messages * Other minor efficiency gains in method encoding and decoding * Channel and connection state errors (e.g., trying to write when closed) include a stack trace from when they moved to that state * The `arguments` table, passed as an option to some methods, can include fields in its prototype chain * Provide the more accurately named `persistent` as a near equivalent of `deliveryMode` ## Changes in v0.1.0 (since v0.0.2) git log v0.0.2..v0.1.0 ### Breaking changes * Consumer callbacks are invoked with `null` if the consumer is cancelled (see [RabbitMQ's consumer cancel notification][rabbitmq-consumer-cancel]) * In confirm channels, instead of `#publish` and `#sendToQueue` returning promises, they return a boolean as for normal channels, and take a Node.JS-style `function (err, ok)` callback for the server ack or nack ### Fixes * Overlapping channel and connection close frames are dealt with gracefully * Exceptions thrown in consumer callbacks are raised as `'error'` events * Zero-size messages are handled * Avoid monkey-patching `Buffer`, and eschew `require('util')._extend` ### Enhancements * Channels now behave like `Writable` streams with regard to `#publish` and `#sendToQueue`, returning a boolean from those methods and emitting `'drain'` * Connections now multiplex frames from channels fairly * Low-level channel machinery is now fully callback-based [rabbitmq-consumer-cancel]: http://www.rabbitmq.com/consumer-cancel.html [rabbitmq-auth-failure]: http://www.rabbitmq.com/auth-notification.html [rabbitmq-prefetch-global]: http://www.rabbitmq.com/consumer-prefetch.html amqp-node-amqplib-263c8a3/CLAUDE.md000066400000000000000000000010361520012327700166270ustar00rootroot00000000000000# Project-Specific Guidelines for amqplib ## Generated Files - DO NOT EDIT - `lib/defs.js` - This file is generated code. NEVER modify it directly. - Generated via `make lib/defs.js` from the AMQP specification - If linting issues arise, exclude this file from linting rules rather than modifying it ## Commit Requirements - NEVER commit when tests are failing - NEVER commit when there are lint errors - Always run `npm test` and `npm run lint` before committing - If tests are failing, investigate and fix the issue before proceeding amqp-node-amqplib-263c8a3/LICENSE000066400000000000000000000004231520012327700163540ustar00rootroot00000000000000amqplib copyright (c) 2013, 2014 Michael Bridgen This package, "amqplib", is licensed under the MIT License. A copy may be found in the file LICENSE-MIT in this directory, or downloaded from http://opensource.org/licenses/MIT. amqp-node-amqplib-263c8a3/LICENSE-MIT000066400000000000000000000021311520012327700170010ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2013, 2014 Michael Bridgen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. amqp-node-amqplib-263c8a3/Makefile000066400000000000000000000021621520012327700170110ustar00rootroot00000000000000RABBITMQ_SRC_VERSION=v3.12.13 JSON=amqp-rabbitmq-0.9.1.json AMQP_JSON=https://raw.githubusercontent.com/rabbitmq/rabbitmq-server/$(RABBITMQ_SRC_VERSION)/deps/rabbitmq_codegen/$(JSON) NODEJS_VERSIONS='18.20.0' '20.10.0' '22.14.0' '24.7.0' UGLIFY=./node_modules/.bin/uglifyjs .PHONY: test test-all-nodejs coverage lib/defs.js error: @echo "Please choose one of the following targets: test, test-all-nodejs, coverage, lib/defs.js" @exit 1 test: node --test --test-reporter=spec test-all-nodejs: for v in $(NODEJS_VERSIONS); \ do echo "-- Node version $$v --"; \ nave use $$v node --test --test-reporter=spec; \ done coverage: node --test --test-coverage --test-reporter=spec lib/defs.js: clean bin/generate-defs test clean: rm -f lib/defs.js bin/amqp-rabbitmq-0.9.1.json bin/generate-defs: $(UGLIFY) bin/generate-defs.js bin/amqp-rabbitmq-0.9.1.json (cd bin; node ./generate-defs.js > ../lib/defs.js) $(UGLIFY) ./lib/defs.js -o ./lib/defs.js \ -c 'sequences=false' --comments \ -b 'indent-level=2' 2>&1 | (grep -v 'WARN' || true) bin/amqp-rabbitmq-0.9.1.json: curl -L $(AMQP_JSON) > $@ $(UGLIFY): npm install amqp-node-amqplib-263c8a3/README.md000066400000000000000000000205371520012327700166360ustar00rootroot00000000000000# AMQP 0-9-1 library and client for Node.JS [![NPM version](https://img.shields.io/npm/v/amqplib.svg?style=flat-square)](https://www.npmjs.com/package/amqplib) [![NPM downloads](https://img.shields.io/npm/dm/amqplib.svg?style=flat-square)](https://www.npmjs.com/package/amqplib) [![Node.js CI](https://github.com/amqp-node/amqplib/workflows/Node.js%20CI/badge.svg)](https://github.com/amqp-node/amqplib/actions?query=workflow%3A%22Node.js+CI%22) [![amqplib](https://snyk.io/advisor/npm-package/amqplib/badge.svg)](https://snyk.io/advisor/npm-package/amqplib) A library for making AMQP 0-9-1 clients for Node.JS, and an AMQP 0-9-1 client for Node.JS v10+. This library does not implement [AMQP1.0](https://github.com/amqp-node/amqplib/issues/63) or [AMQP0-10](https://github.com/amqp-node/amqplib/issues/94). npm install amqplib ## RabbitMQ Compatibility Only `0.10.7` and later versions of this library are compatible with RabbitMQ 4.1.0 (and later releases). ## Links * [Change log][changelog] * [GitHub pages][gh-pages] * [API reference][gh-pages-apiref] * [Troubleshooting][gh-pages-trouble] * [Examples from RabbitMQ tutorials][tutes] ## Project status - Expected to work - Complete high-level and low-level APIs (i.e., all bits of the protocol) - Stable APIs - A fair few tests - Measured test coverage - Ports of the [RabbitMQ tutorials][rabbitmq-tutes] as [examples][tutes] - Used in production Still working on: - Getting to 100% (or very close to 100%) test coverage ## Callback API example ```javascript const amqplib = require('amqplib/callback_api'); const queue = 'tasks'; amqplib.connect('amqp://localhost', (err, conn) => { if (err) throw err; conn.on('error', (err) => { console.error('Connection error:', err); }); conn.on('handler-error', (err, event) => { console.error(`Uncaught exception in connection ${event} listener:`, err); }); // Listener conn.createChannel((err, ch2) => { if (err) throw err; ch2.on('error', (err) => { console.error('Channel error:', err); }); ch2.on('handler-error', (err, event) => { console.error(`Uncaught exception in channel ${event} listener:`, err); }); ch2.assertQueue(queue); ch2.consume(queue, (msg) => { if (msg !== null) { console.log(msg.content.toString()); ch2.ack(msg); } else { console.log('Consumer cancelled by server'); } }); }); // Sender conn.createChannel((err, ch1) => { if (err) throw err; ch1.on('error', (err) => { console.error('Channel error:', err); }); ch1.on('handler-error', (err, event) => { console.error(`Uncaught exception in channel ${event} listener:`, err); }); ch1.assertQueue(queue); setInterval(() => { ch1.sendToQueue(queue, Buffer.from('something to do')); }, 1000); }); }); ``` ## Promise/Async API example ```javascript const amqplib = require('amqplib'); (async () => { const queue = 'tasks'; const conn = await amqplib.connect('amqp://localhost'); conn.on('error', (err) => { console.error('Connection error:', err); }); conn.on('handler-error', (err, event) => { console.error(`Uncaught exception in connection ${event} listener:`, err); }); const ch1 = await conn.createChannel(); ch1.on('error', (err) => { console.error('Channel error:', err); }); ch1.on('handler-error', (err, event) => { console.error(`Uncaught exception in channel ${event} listener:`, err); }); await ch1.assertQueue(queue); // Listener ch1.consume(queue, (msg) => { if (msg !== null) { console.log('Received:', msg.content.toString()); ch1.ack(msg); } else { console.log('Consumer cancelled by server'); } }); // Sender const ch2 = await conn.createChannel(); ch2.on('error', (err) => { console.error('Channel error:', err); }); ch2.on('handler-error', (err, event) => { console.error(`Uncaught exception in channel ${event} listener:`, err); }); setInterval(() => { ch2.sendToQueue(queue, Buffer.from('something to do')); }, 1000); })(); ``` ## Opt-in recovery Automatic recovery is available as an opt-in feature through `connect` options: ```javascript const amqplib = require('amqplib'); const connection = await amqplib.connect('amqp://localhost', { recovery: { initialDelay: 200, // ms maxDelay: 5000, // ms factor: 2, jitter: 0.2, maxRetries: Infinity, async setup(model) { // Called after every successful (re)connect. // Recreate topology/consumers here. const ch = await model.createChannel(); await ch.assertQueue('tasks', {durable: true}); }, }, }); connection.on('connect', () => { console.log('connected'); }); connection.on('disconnect', (err) => { console.warn('disconnected', err.message); }); ``` Callback API supports the same option: ```javascript const amqplib = require('amqplib/callback_api'); amqplib.connect( 'amqp://localhost', { recovery: { initialDelay: 200, maxDelay: 5000, setup(model, done) { model.createChannel((err, ch) => { if (err) return done(err); ch.assertQueue('tasks', {durable: true}, done); }); }, }, }, (err, conn) => { if (err) throw err; conn.on('connect', () => console.log('connected')); }, ); ``` Without `recovery` options, behavior is unchanged. ## Error handling in event handlers If a user-supplied event handler throws a synchronous error, the throw will propagate into amqplib internals. Depending on where in the call stack it escapes, this can silently swallow the error, or close the channel or connection. To avoid this, register a `handler-error` listener on the connection and on each channel. If a listener is present, amqplib will catch any throw from a user event handler and deliver it there instead of letting it propagate internally. The listener receives the thrown error and the name of the event whose handler threw. Note that `handler-error` is not a replacement for the `error` event. The `error` event is emitted by amqplib itself when the connection or channel encounters a protocol-level error. The `handler-error` event is only emitted when *your own* event listener throws. ```js const connection = await amqp.connect('amqp://localhost'); connection.on('error', (err) => { /* handle protocol errors */ }); connection.on('handler-error', (err, event) => { console.error(`Uncaught exception in connection ${event} listener:`, err); }); const channel = await connection.createChannel(); channel.on('error', (err) => { /* handle protocol errors */ }); channel.on('handler-error', (err, event) => { console.error(`Uncaught exception in channel ${event} listener:`, err); }); ``` If no `handler-error` listener is registered, behaviour is unchanged from previous versions. ## Running tests npm test To run the tests RabbitMQ is required. Either install it with your package manager, or use [docker][] to run a RabbitMQ instance. docker run -d --name amqp.test -p 5672:5672 rabbitmq If prefer not to run RabbitMQ locally it is also possible to use a instance of RabbitMQ hosted elsewhere. Use the `URL` environment variable to configure a different amqp host to connect to. You may also need to do this if docker is not on localhost; e.g., if it's running in docker-machine. One public host is dev.rabbitmq.com: URL=amqp://dev.rabbitmq.com npm test **NB** You may experience test failures due to timeouts if using the dev.rabbitmq.com instance. You can run it under different versions of Node.JS using [nave][]: nave use 10 npm test or run the tests on all supported versions of Node.JS in one go: make test-all-nodejs (which also needs `nave` installed, of course). Lastly, setting the environment variable `LOG_ERRORS` will cause the tests to output error messages encountered, to the console; this is really only useful for checking the kind and formatting of the errors. LOG_ERRORS=true npm test ## Test coverage make coverage open file://`pwd`/coverage/lcov-report/index.html [gh-pages]: https://amqp-node.github.io/amqplib/ [gh-pages-apiref]: https://amqp-node.github.io/amqplib/channel_api.html [gh-pages-trouble]: https://amqp-node.github.io/amqplib/#troubleshooting [nave]: https://github.com/isaacs/nave [tutes]: https://github.com/amqp-node/amqplib/tree/main/examples/tutorials [rabbitmq-tutes]: http://www.rabbitmq.com/getstarted.html [changelog]: https://github.com/amqp-node/amqplib/blob/main/CHANGELOG.md [docker]: https://www.docker.com/ amqp-node-amqplib-263c8a3/bin/000077500000000000000000000000001520012327700161205ustar00rootroot00000000000000amqp-node-amqplib-263c8a3/bin/amqp-rabbitmq-0.9.1.json000066400000000000000000000670521520012327700222250ustar00rootroot00000000000000{ "name": "AMQP", "major-version": 0, "minor-version": 9, "revision": 1, "port": 5672, "copyright": [ "Copyright (C) 2008-2020 VMware, Inc. or its affiliates.\n", "\n", "Permission is hereby granted, free of charge, to any person\n", "obtaining a copy of this file (the \"Software\"), to deal in the\n", "Software without restriction, including without limitation the \n", "rights to use, copy, modify, merge, publish, distribute, \n", "sublicense, and/or sell copies of the Software, and to permit \n", "persons to whom the Software is furnished to do so, subject to \n", "the following conditions:\n", "\n", "The above copyright notice and this permission notice shall be\n", "included in all copies or substantial portions of the Software.\n", "\n", "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n", "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n", "OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n", "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n", "HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n", "WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n", "FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n", "OTHER DEALINGS IN THE SOFTWARE.\n", "\n", "Class information entered from amqp_xml0-8.pdf and domain types from amqp-xml-doc0-9.pdf\n", "Updated for 0-9-1 by Tony Garnock-Jones\n", "\n", "b3cb053f15e7b98808c0ccc67f23cb3e amqp_xml0-8.pdf\n", "http://twiststandards.org/?option=com_docman&task=cat_view&gid=28&Itemid=90\n", "8444db91e2949dbecfb2585e9eef6d64 amqp-xml-doc0-9.pdf\n", "https://jira.amqp.org/confluence/download/attachments/720900/amqp-xml-doc0-9.pdf?version=1\n"], "domains": [ ["bit", "bit"], ["channel-id", "longstr"], ["class-id", "short"], ["consumer-tag", "shortstr"], ["delivery-tag", "longlong"], ["destination", "shortstr"], ["duration", "longlong"], ["exchange-name", "shortstr"], ["long", "long"], ["longlong", "longlong"], ["longstr", "longstr"], ["message-count", "long"], ["method-id", "short"], ["no-ack", "bit"], ["no-local", "bit"], ["octet", "octet"], ["offset", "longlong"], ["path", "shortstr"], ["peer-properties", "table"], ["queue-name", "shortstr"], ["redelivered", "bit"], ["reference", "longstr"], ["reject-code", "short"], ["reject-text", "shortstr"], ["reply-code", "short"], ["reply-text", "shortstr"], ["security-token", "longstr"], ["short", "short"], ["shortstr", "shortstr"], ["table", "table"], ["timestamp", "timestamp"] ], "constants": [ {"name": "FRAME-METHOD", "value": 1}, {"name": "FRAME-HEADER", "value": 2}, {"name": "FRAME-BODY", "value": 3}, {"name": "FRAME-HEARTBEAT", "value": 8}, {"name": "FRAME-MIN-SIZE", "value": 4096}, {"name": "FRAME-END", "value": 206}, {"name": "REPLY-SUCCESS", "value": 200}, {"name": "CONTENT-TOO-LARGE", "value": 311, "class": "soft-error"}, {"name": "NO-ROUTE", "value": 312, "class": "soft-error"}, {"name": "NO-CONSUMERS", "value": 313, "class": "soft-error"}, {"name": "ACCESS-REFUSED", "value": 403, "class": "soft-error"}, {"name": "NOT-FOUND", "value": 404, "class": "soft-error"}, {"name": "RESOURCE-LOCKED", "value": 405, "class": "soft-error"}, {"name": "PRECONDITION-FAILED", "value": 406, "class": "soft-error"}, {"name": "CONNECTION-FORCED", "value": 320, "class": "hard-error"}, {"name": "INVALID-PATH", "value": 402, "class": "hard-error"}, {"name": "FRAME-ERROR", "value": 501, "class": "hard-error"}, {"name": "SYNTAX-ERROR", "value": 502, "class": "hard-error"}, {"name": "COMMAND-INVALID", "value": 503, "class": "hard-error"}, {"name": "CHANNEL-ERROR", "value": 504, "class": "hard-error"}, {"name": "UNEXPECTED-FRAME", "value": 505, "class": "hard-error"}, {"name": "RESOURCE-ERROR", "value": 506, "class": "hard-error"}, {"name": "NOT-ALLOWED", "value": 530, "class": "hard-error"}, {"name": "NOT-IMPLEMENTED", "value": 540, "class": "hard-error"}, {"name": "INTERNAL-ERROR", "value": 541, "class": "hard-error"} ], "classes": [ { "id": 60, "methods": [{"id": 10, "arguments": [{"type": "long", "name": "prefetch-size", "default-value": 0}, {"type": "short", "name": "prefetch-count", "default-value": 0}, {"type": "bit", "name": "global", "default-value": false}], "name": "qos", "synchronous" : true}, {"id": 11, "arguments": [], "name": "qos-ok"}, {"id": 20, "arguments": [{"domain": "short", "name": "ticket", "default-value": 0}, {"domain": "queue-name", "name": "queue", "default-value": ""}, {"type": "shortstr", "name": "consumer-tag", "default-value": ""}, {"type": "bit", "name": "no-local", "default-value": false}, {"type": "bit", "name": "no-ack", "default-value": false}, {"type": "bit", "name": "exclusive", "default-value": false}, {"type": "bit", "name": "nowait", "default-value": false}, {"type": "table", "name": "arguments", "default-value": {}}], "name": "consume", "synchronous" : true}, {"id": 21, "arguments": [{"type": "shortstr", "name": "consumer-tag"}], "name": "consume-ok"}, {"id": 30, "arguments": [{"type": "shortstr", "name": "consumer-tag"}, {"type": "bit", "name": "nowait", "default-value": false}], "name": "cancel", "synchronous" : true}, {"id": 31, "arguments": [{"type": "shortstr", "name": "consumer-tag"}], "name": "cancel-ok"}, {"content": true, "id": 40, "arguments": [{"type": "short", "name": "ticket", "default-value": 0}, {"domain": "exchange-name", "name": "exchange", "default-value": ""}, {"type": "shortstr", "name": "routing-key", "default-value": ""}, {"type": "bit", "name": "mandatory", "default-value": false}, {"type": "bit", "name": "immediate", "default-value": false}], "name": "publish"}, {"content": true, "id": 50, "arguments": [{"type": "short", "name": "reply-code"}, {"type": "shortstr", "name": "reply-text", "default-value": ""}, {"domain": "exchange-name", "name": "exchange"}, {"type": "shortstr", "name": "routing-key"}], "name": "return"}, {"content": true, "id": 60, "arguments": [{"type": "shortstr", "name": "consumer-tag"}, {"type": "longlong", "name": "delivery-tag"}, {"type": "bit", "name": "redelivered", "default-value": false}, {"domain": "exchange-name", "name": "exchange"}, {"type": "shortstr", "name": "routing-key"}], "name": "deliver"}, {"id": 70, "arguments": [{"type": "short", "name": "ticket", "default-value": 0}, {"domain": "queue-name", "name": "queue", "default-value": ""}, {"type": "bit", "name": "no-ack", "default-value": false}], "name": "get", "synchronous" : true}, {"content": true, "id": 71, "arguments": [{"type": "longlong", "name": "delivery-tag"}, {"type": "bit", "name": "redelivered", "default-value": false}, {"domain": "exchange-name", "name": "exchange"}, {"type": "shortstr", "name": "routing-key"}, {"domain": "message-count", "name": "message-count"}], "name": "get-ok"}, {"id": 72, "arguments": [{"type": "shortstr", "name": "cluster-id", "default-value": ""}], "name": "get-empty"}, {"id": 80, "arguments": [{"type": "longlong", "name": "delivery-tag", "default-value": 0}, {"type": "bit", "name": "multiple", "default-value": false}], "name": "ack"}, {"id": 90, "arguments": [{"type": "longlong", "name": "delivery-tag"}, {"type": "bit", "name": "requeue", "default-value": true}], "name": "reject"}, {"id": 100, "arguments": [{"type": "bit", "name": "requeue", "default-value": false}], "name": "recover-async"}, {"id": 110, "arguments": [{"type": "bit", "name": "requeue", "default-value": false}], "name": "recover", "synchronous" : true}, {"id": 111, "arguments": [], "name": "recover-ok"}, {"id": 120, "arguments": [{"type": "longlong", "name": "delivery-tag", "default-value": 0}, {"type": "bit", "name": "multiple", "default-value": false}, {"type": "bit", "name": "requeue", "default-value": true}], "name": "nack"}], "name": "basic", "properties": [{"type": "shortstr", "name": "content-type"}, {"type": "shortstr", "name": "content-encoding"}, {"type": "table", "name": "headers"}, {"type": "octet", "name": "delivery-mode"}, {"type": "octet", "name": "priority"}, {"type": "shortstr", "name": "correlation-id"}, {"type": "shortstr", "name": "reply-to"}, {"type": "shortstr", "name": "expiration"}, {"type": "shortstr", "name": "message-id"}, {"type": "timestamp", "name": "timestamp"}, {"type": "shortstr", "name": "type"}, {"type": "shortstr", "name": "user-id"}, {"type": "shortstr", "name": "app-id"}, {"type": "shortstr", "name": "cluster-id"}] }, { "id": 10, "methods": [{"id": 10, "arguments": [{"type": "octet", "name": "version-major", "default-value": 0}, {"type": "octet", "name": "version-minor", "default-value": 9}, {"domain": "peer-properties", "name": "server-properties"}, {"type": "longstr", "name": "mechanisms", "default-value": "PLAIN"}, {"type": "longstr", "name": "locales", "default-value": "en_US"}], "name": "start", "synchronous" : true}, {"id": 11, "arguments": [{"domain": "peer-properties", "name": "client-properties"}, {"type": "shortstr", "name": "mechanism", "default-value": "PLAIN"}, {"type": "longstr", "name": "response"}, {"type": "shortstr", "name": "locale", "default-value": "en_US"}], "name": "start-ok"}, {"id": 20, "arguments": [{"type": "longstr", "name": "challenge"}], "name": "secure", "synchronous" : true}, {"id": 21, "arguments": [{"type": "longstr", "name": "response"}], "name": "secure-ok"}, {"id": 30, "arguments": [{"type": "short", "name": "channel-max", "default-value": 0}, {"type": "long", "name": "frame-max", "default-value": 0}, {"type": "short", "name": "heartbeat", "default-value": 0}], "name": "tune", "synchronous" : true}, {"id": 31, "arguments": [{"type": "short", "name": "channel-max", "default-value": 0}, {"type": "long", "name": "frame-max", "default-value": 0}, {"type": "short", "name": "heartbeat", "default-value": 0}], "name": "tune-ok"}, {"id": 40, "arguments": [{"type": "shortstr", "name": "virtual-host", "default-value": "/"}, {"type": "shortstr", "name": "capabilities", "default-value": ""}, {"type": "bit", "name": "insist", "default-value": false}], "name": "open", "synchronous" : true}, {"id": 41, "arguments": [{"type": "shortstr", "name": "known-hosts", "default-value": ""}], "name": "open-ok"}, {"id": 50, "arguments": [{"type": "short", "name": "reply-code"}, {"type": "shortstr", "name": "reply-text", "default-value": ""}, {"type": "short", "name": "class-id"}, {"type": "short", "name": "method-id"}], "name": "close", "synchronous" : true}, {"id": 51, "arguments": [], "name": "close-ok"}, {"id": 60, "arguments": [{"type": "shortstr", "name": "reason", "default-value": ""}], "name": "blocked"}, {"id": 61, "arguments": [], "name": "unblocked"}, {"id": 70, "arguments": [{"type": "longstr", "name": "new-secret"}, {"type": "shortstr", "name": "reason"}], "name": "update-secret", "synchronous" : true}, {"id": 71, "arguments": [], "name": "update-secret-ok"} ], "name": "connection", "properties": [] }, { "id": 20, "methods": [{"id": 10, "arguments": [{"type": "shortstr", "name": "out-of-band", "default-value": ""}], "name": "open", "synchronous" : true}, {"id": 11, "arguments": [{"type": "longstr", "name": "channel-id", "default-value": ""}], "name": "open-ok"}, {"id": 20, "arguments": [{"type": "bit", "name": "active"}], "name": "flow", "synchronous" : true}, {"id": 21, "arguments": [{"type": "bit", "name": "active"}], "name": "flow-ok"}, {"id": 40, "arguments": [{"type": "short", "name": "reply-code"}, {"type": "shortstr", "name": "reply-text", "default-value": ""}, {"type": "short", "name": "class-id"}, {"type": "short", "name": "method-id"}], "name": "close", "synchronous" : true}, {"id": 41, "arguments": [], "name": "close-ok"}], "name": "channel" }, { "id": 30, "methods": [{"id": 10, "arguments": [{"type": "shortstr", "name": "realm", "default-value": "/data"}, {"type": "bit", "name": "exclusive", "default-value": false}, {"type": "bit", "name": "passive", "default-value": true}, {"type": "bit", "name": "active", "default-value": true}, {"type": "bit", "name": "write", "default-value": true}, {"type": "bit", "name": "read", "default-value": true}], "name": "request", "synchronous" : true}, {"id": 11, "arguments": [{"type": "short", "name": "ticket", "default-value": 1}], "name": "request-ok"}], "name": "access" }, { "id": 40, "methods": [{"id": 10, "arguments": [{"type": "short", "name": "ticket", "default-value": 0}, {"domain": "exchange-name", "name": "exchange"}, {"type": "shortstr", "name": "type", "default-value": "direct"}, {"type": "bit", "name": "passive", "default-value": false}, {"type": "bit", "name": "durable", "default-value": false}, {"type": "bit", "name": "auto-delete", "default-value": false}, {"type": "bit", "name": "internal", "default-value": false}, {"type": "bit", "name": "nowait", "default-value": false}, {"type": "table", "name": "arguments", "default-value": {}}], "name": "declare", "synchronous" : true}, {"id": 11, "arguments": [], "name": "declare-ok"}, {"id": 20, "arguments": [{"type": "short", "name": "ticket", "default-value": 0}, {"domain": "exchange-name", "name": "exchange"}, {"type": "bit", "name": "if-unused", "default-value": false}, {"type": "bit", "name": "nowait", "default-value": false}], "name": "delete", "synchronous" : true}, {"id": 21, "arguments": [], "name": "delete-ok"}, {"id": 30, "arguments": [{"type": "short", "name": "ticket", "default-value": 0}, {"domain": "exchange-name", "name": "destination"}, {"domain": "exchange-name", "name": "source"}, {"type": "shortstr", "name": "routing-key", "default-value": ""}, {"type": "bit", "name": "nowait", "default-value": false}, {"type": "table", "name": "arguments", "default-value": {}}], "name": "bind", "synchronous" : true}, {"id": 31, "arguments": [], "name": "bind-ok"}, {"id": 40, "arguments": [{"type": "short", "name": "ticket", "default-value": 0}, {"domain": "exchange-name", "name": "destination"}, {"domain": "exchange-name", "name": "source"}, {"type": "shortstr", "name": "routing-key", "default-value": ""}, {"type": "bit", "name": "nowait", "default-value": false}, {"type": "table", "name": "arguments", "default-value": {}}], "name": "unbind", "synchronous" : true}, {"id": 51, "arguments": [], "name": "unbind-ok"}], "name": "exchange" }, { "id": 50, "methods": [{"id": 10, "arguments": [{"type": "short", "name": "ticket", "default-value": 0}, {"domain": "queue-name", "name": "queue", "default-value": ""}, {"type": "bit", "name": "passive", "default-value": false}, {"type": "bit", "name": "durable", "default-value": false}, {"type": "bit", "name": "exclusive", "default-value": false}, {"type": "bit", "name": "auto-delete", "default-value": false}, {"type": "bit", "name": "nowait", "default-value": false}, {"type": "table", "name": "arguments", "default-value": {}}], "name": "declare", "synchronous" : true}, {"id": 11, "arguments": [{"domain": "queue-name", "name": "queue"}, {"domain": "message-count", "name": "message-count"}, {"type": "long", "name": "consumer-count"}], "name": "declare-ok"}, {"id": 20, "arguments": [{"type": "short", "name": "ticket", "default-value": 0}, {"domain": "queue-name", "name": "queue", "default-value": ""}, {"domain": "exchange-name", "name": "exchange"}, {"type": "shortstr", "name": "routing-key", "default-value": ""}, {"type": "bit", "name": "nowait", "default-value": false}, {"type": "table", "name": "arguments", "default-value": {}}], "name": "bind", "synchronous" : true}, {"id": 21, "arguments": [], "name": "bind-ok"}, {"id": 30, "arguments": [{"type": "short", "name": "ticket", "default-value": 0}, {"domain": "queue-name", "name": "queue", "default-value": ""}, {"type": "bit", "name": "nowait", "default-value": false}], "name": "purge", "synchronous" : true}, {"id": 31, "arguments": [{"domain": "message-count", "name": "message-count"}], "name": "purge-ok"}, {"id": 40, "arguments": [{"type": "short", "name": "ticket", "default-value": 0}, {"domain": "queue-name", "name": "queue", "default-value": ""}, {"type": "bit", "name": "if-unused", "default-value": false}, {"type": "bit", "name": "if-empty", "default-value": false}, {"type": "bit", "name": "nowait", "default-value": false}], "name": "delete", "synchronous" : true}, {"id": 41, "arguments": [{"domain": "message-count", "name": "message-count"}], "name": "delete-ok"}, {"id": 50, "arguments": [{"type": "short", "name": "ticket", "default-value": 0}, {"domain": "queue-name", "name": "queue", "default-value": ""}, {"domain": "exchange-name", "name": "exchange"}, {"type": "shortstr", "name": "routing-key", "default-value": ""}, {"type": "table", "name": "arguments", "default-value": {}}], "name": "unbind", "synchronous" : true}, {"id": 51, "arguments": [], "name": "unbind-ok"} ], "name": "queue" }, { "id": 90, "methods": [{"id": 10, "arguments": [], "name": "select", "synchronous" : true}, {"id": 11, "arguments": [], "name": "select-ok"}, {"id": 20, "arguments": [], "name": "commit", "synchronous" : true}, {"id": 21, "arguments": [], "name": "commit-ok"}, {"id": 30, "arguments": [], "name": "rollback", "synchronous" : true}, {"id": 31, "arguments": [], "name": "rollback-ok"}], "name": "tx" }, { "id": 85, "methods": [{"id": 10, "arguments": [ {"type": "bit", "name": "nowait", "default-value": false}], "name": "select", "synchronous": true}, {"id": 11, "arguments": [], "name": "select-ok"}], "name": "confirm" } ] } amqp-node-amqplib-263c8a3/bin/generate-defs.js000066400000000000000000000517011520012327700211730ustar00rootroot00000000000000const format = require('node:util').format; const defs = require('./amqp-rabbitmq-0.9.1.json'); const FRAME_OVERHEAD = 8; // type + channel + size + frame-end const METHOD_OVERHEAD = FRAME_OVERHEAD + 4; // F_O + classId + methodId const PROPERTIES_OVERHEAD = FRAME_OVERHEAD + 4 + 8 + 2; // F_O + classId + weight + content size + flags const out = process.stdout; function printf(...args) { out.write(format.apply(format, args), 'utf8'); } function nl() { out.write('\n'); } function println(...args) { printf.apply(printf, args); nl(); } const constants = {}; const constant_strs = {}; for (let i = 0, len = defs.constants.length; i < len; i++) { const cdef = defs.constants[i]; constants[constantName(cdef)] = cdef.value; constant_strs[cdef.value] = cdef.name; } function constantName(def) { return def.name.replace(/-/g, '_'); } function methodName(clazz, method) { return initial(clazz.name) + method.name.split('-').map(initial).join(''); } function propertyName(dashed) { const parts = dashed.split('-'); return parts[0] + parts.slice(1).map(initial).join(''); } function initial(part) { return part.charAt(0).toUpperCase() + part.substr(1); } function argument(a) { const type = a.type || domains[a.domain]; const friendlyName = propertyName(a.name); return { type: type, name: friendlyName, default: a['default-value'] }; } const domains = {}; for (let i = 0, len = defs.domains.length; i < len; i++) { const dom = defs.domains[i]; domains[dom[0]] = dom[1]; } const methods = {}; const propertieses = {}; for (let i = 0, len = defs.classes.length; i < len; i++) { const clazz = defs.classes[i]; for (let j = 0, num = clazz.methods.length; j < num; j++) { const method = clazz.methods[j]; const name = methodName(clazz, method); const info = `methodInfo${name}`; methods[name] = { id: methodId(clazz, method), name: name, methodId: method.id, clazzId: clazz.id, clazz: clazz.name, args: method['arguments'].map(argument), isReply: method.answer, encoder: `encode${name}`, decoder: `decode${name}`, info: info, }; } if (clazz.properties && clazz.properties.length > 0) { const name = propertiesName(clazz); const props = clazz.properties; propertieses[name] = { id: clazz.id, name: name, encoder: `encode${name}`, decoder: `decode${name}`, info: `propertiesInfo${name}`, args: props.map(argument), }; } } // OK let's get emitting println( '/** @preserve This file is generated by the script\n', '* ../bin/generate-defs.js, which is not in general included in a\n', '* distribution, but is available in the source repository e.g. at\n', '* https://github.com/amqp-node/amqplib/\n', '*/', ); println("'use strict';"); nl(); nl(); println('var codec = require("./codec");'); println('var encodeTable = codec.encodeTable;'); println('var decodeFields = codec.decodeFields;'); nl(); println('var SCRATCH = Buffer.alloc(65536);'); println('var EMPTY_OBJECT = Object.freeze({});'); println('module.exports.constants = %s', JSON.stringify(constants)); nl(); println('module.exports.constant_strs = %s', JSON.stringify(constant_strs)); nl(); println('module.exports.FRAME_OVERHEAD = %d;', FRAME_OVERHEAD); nl(); println('module.exports.decode = function(id, buf) {'); println('switch (id) {'); for (const m in methods) { const method = methods[m]; println('case %d: return %s(buf);', method.id, method.decoder); } for (const p in propertieses) { const props = propertieses[p]; println('case %d: return %s(buf);', props.id, props.decoder); } println('default: throw new Error("Unknown class/method ID");'); println('}}'); nl(); println('module.exports.encodeMethod =', 'function(id, channel, fields) {'); println('switch (id) {'); for (const m in methods) { const method = methods[m]; println('case %d: return %s(channel, fields);', method.id, method.encoder); } println('default: throw new Error("Unknown class/method ID");'); println('}}'); nl(); println('module.exports.encodeProperties =', 'function(id, channel, size, fields) {'); println('switch (id) {'); for (const p in propertieses) { const props = propertieses[p]; println('case %d: return %s(channel, size, fields);', props.id, props.encoder); } println('default: throw new Error("Unknown class/properties ID");'); println('}}'); nl(); println('module.exports.info = function(id) {'); println('switch(id) {'); for (const m in methods) { const method = methods[m]; println('case %d: return %s; ', method.id, method.info); } for (const p in propertieses) { const properties = propertieses[p]; println('case %d: return %s', properties.id, properties.info); } println('default: throw new Error("Unknown class/method ID");'); println('}}'); nl(); for (const m in methods) { const method = methods[m]; println('module.exports.%s = %d;', m, method.id); decoderFn(method); nl(); encoderFn(method); nl(); infoObj(method); nl(); } for (const p in propertieses) { const properties = propertieses[p]; println('module.exports.%s = %d;', p, properties.id); encodePropsFn(properties); nl(); decodePropsFn(properties); nl(); infoObj(properties); nl(); } function methodId(clazz, method) { return (clazz.id << 16) + method.id; } function propertiesName(clazz) { return `${initial(clazz.name)}Properties`; } function valTypeTest(arg) { switch (arg.type) { // everything is booleany case 'bit': return 'true'; case 'octet': case 'short': case 'long': case 'longlong': case 'timestamp': return "typeof val === 'number' && !isNaN(val)"; case 'shortstr': return "typeof val === 'string' &&" + ' Buffer.byteLength(val) < 256'; case 'longstr': return 'Buffer.isBuffer(val)'; case 'table': return "typeof val === 'object'"; } } function typeDesc(t) { switch (t) { case 'bit': return 'booleany'; case 'octet': case 'short': case 'long': case 'longlong': case 'timestamp': return 'a number (but not NaN)'; case 'shortstr': return 'a string (up to 255 chars)'; case 'longstr': return 'a Buffer'; case 'table': return 'an object'; } } function defaultValueRepr(arg) { switch (arg.type) { case 'longstr': return format('Buffer.from(%s)', JSON.stringify(arg.default)); default: // assumes no tables as defaults return JSON.stringify(arg.default); } } // Emit code to assign the arg value to `val`. function assignArg(a) { println("val = fields['%s'];", a.name); } function assignOrDefault(a) { println("val = fields['%s'];", a.name); println('if (val === undefined) val = %s;', defaultValueRepr(a)); } // Emit code for assigning an argument value to `val`, checking that // it exists (if it does not have a default) and is the correct // type. function checkAssignArg(a) { assignArg(a); println('if (val === undefined) {'); if (a.default !== undefined) { println('val = %s;', defaultValueRepr(a)); } else { println('throw new Error("Missing value for mandatory field \'%s\'");', a.name); } println('}'); // undefined test println('else if (!(%s)) {', valTypeTest(a)); println('throw new TypeError('); println('"Field \'%s\' is the wrong type; must be %s");', a.name, typeDesc(a.type)); println('}'); // type test } // Emit code for encoding `val` as a table and assign to a fresh // variable (based on the arg name). I use a scratch buffer to compose // the encoded table, otherwise I'd have to do a size calculation pass // first. I can get away with this only because 1. the encoding // procedures are not re-entrant; and, 2. I copy the result into // another buffer before returning. `scratchOffset`, `val`, `len` are // expected to have been declared. function assignTable(a) { const varname = tableVar(a); println('len = encodeTable(SCRATCH, val, scratchOffset);'); println('var %s = SCRATCH.slice(scratchOffset, scratchOffset + len);', varname); println('scratchOffset += len;'); } function tableVar(a) { return `${a.name}_encoded`; } function stringLenVar(a) { return `${a.name}_len`; } function assignStringLen(a) { const v = stringLenVar(a); // Assumes the value or default is in val println("var %s = Buffer.byteLength(val, 'utf8');", v); } function encoderFn(method) { const args = method['args']; println('function %s(channel, fields) {', method.encoder); println('var offset = 0, val = null, bits = 0, varyingSize = 0;'); println('var len, scratchOffset = 0;'); // Encoding is split into two parts. Some fields have a fixed size // (e.g., integers of a specific width), while some have a size that // depends on the datum (e.g., strings). Each field will therefore // either 1. contribute to the fixed size; or 2. emit code to // calculate the size (and possibly the encoded value, in the case // of tables). let fixedSize = METHOD_OVERHEAD; let bitsInARow = 0; for (let i = 0, len = args.length; i < len; i++) { const arg = args[i]; if (arg.type !== 'bit') bitsInARow = 0; switch (arg.type) { // varying size case 'shortstr': checkAssignArg(arg); assignStringLen(arg); println('varyingSize += %s;', stringLenVar(arg)); fixedSize += 1; break; case 'longstr': checkAssignArg(arg); println('varyingSize += val.length;'); fixedSize += 4; break; case 'table': // For a table we have to encode the table before we can see its // length. checkAssignArg(arg); assignTable(arg); println('varyingSize += %s.length;', tableVar(arg)); break; // fixed size case 'octet': fixedSize += 1; break; case 'short': fixedSize += 2; break; case 'long': fixedSize += 4; break; case 'longlong': //fall through case 'timestamp': fixedSize += 8; break; case 'bit': bitsInARow++; // open a fresh pack o' bits if (bitsInARow === 1) fixedSize += 1; // just used a pack; reset else if (bitsInARow === 8) bitsInARow = 0; break; } } println('var buffer = Buffer.alloc(%d + varyingSize);', fixedSize); println('buffer[0] = %d;', constants.FRAME_METHOD); println('buffer.writeUInt16BE(channel, 1);'); // skip size for now, we'll write it in when we know println('buffer.writeUInt32BE(%d, 7);', method.id); println('offset = 11;'); bitsInARow = 0; for (let i = 0, len = args.length; i < len; i++) { const a = args[i]; // Flush any collected bits before doing a new field if (a.type !== 'bit' && bitsInARow > 0) { bitsInARow = 0; println('buffer[offset] = bits; offset++; bits = 0;'); } switch (a.type) { case 'octet': checkAssignArg(a); println('buffer.writeUInt8(val, offset); offset++;'); break; case 'short': checkAssignArg(a); println('buffer.writeUInt16BE(val, offset); offset += 2;'); break; case 'long': checkAssignArg(a); println('buffer.writeUInt32BE(val, offset); offset += 4;'); break; case 'longlong': case 'timestamp': checkAssignArg(a); println('buffer.writeBigUInt64BE(BigInt(val), offset); offset += 8;'); break; case 'bit': checkAssignArg(a); println('if (val) bits += %d;', 1 << bitsInARow); if (bitsInARow === 7) { // I don't think this ever happens, but whatever println('buffer[offset] = bits; offset++; bits = 0;'); bitsInARow = 0; } else bitsInARow++; break; case 'shortstr': assignOrDefault(a); println('buffer[offset] = %s; offset++;', stringLenVar(a)); println('buffer.write(val, offset, "utf8"); offset += %s;', stringLenVar(a)); break; case 'longstr': assignOrDefault(a); println('len = val.length;'); println('buffer.writeUInt32BE(len, offset); offset += 4;'); println('val.copy(buffer, offset); offset += len;'); break; case 'table': println('offset += %s.copy(buffer, offset);', tableVar(a)); break; default: throw new Error(`Unexpected argument type: ${a.type}`); } } // Flush any collected bits at the end if (bitsInARow > 0) { println('buffer[offset] = bits; offset++;'); } println('buffer[offset] = %d;', constants.FRAME_END); // size does not include the frame header or frame end byte println('buffer.writeUInt32BE(offset - 7, 3);'); println('return buffer;'); println('}'); } function fieldsDecl(args) { println('var fields = {'); for (let i = 0, num = args.length; i < num; i++) { println('%s: undefined,', args[i].name); } println('};'); } function decoderFn(method) { const args = method.args; println('function %s(buffer) {', method.decoder); println('var offset = 0, val, len;'); fieldsDecl(args); let bitsInARow = 0; for (let i = 0, num = args.length; i < num; i++) { const a = args[i]; const field = `fields['${a.name}']`; // Flush any collected bits before doing a new field if (a.type !== 'bit' && bitsInARow > 0) { bitsInARow = 0; println('offset++;'); } switch (a.type) { case 'octet': println('val = buffer[offset]; offset++;'); break; case 'short': println('val = buffer.readUInt16BE(offset); offset += 2;'); break; case 'long': println('val = buffer.readUInt32BE(offset); offset += 4;'); break; case 'longlong': case 'timestamp': println('val = Number(buffer.readBigUInt64BE(offset)); offset += 8;'); break; case 'bit': { const bit = 1 << bitsInARow; println('val = !!(buffer[offset] & %d);', bit); if (bitsInARow === 7) { println('offset++;'); bitsInARow = 0; } else bitsInARow++; break; } case 'longstr': println('len = buffer.readUInt32BE(offset); offset += 4;'); println('val = buffer.subarray(offset, offset + len);'); println('offset += len;'); break; case 'shortstr': println('len = buffer.readUInt8(offset); offset++;'); println('val = buffer.toString("utf8", offset, offset + len);'); println('offset += len;'); break; case 'table': println('len = buffer.readUInt32BE(offset); offset += 4;'); println('val = decodeFields(buffer.subarray(offset, offset + len));'); println('offset += len;'); break; default: throw new TypeError(`Unexpected type in argument list: ${a.type}`); } println('%s = val;', field); } println('return fields;'); println('}'); } function infoObj(thing) { const info = JSON.stringify({ id: thing.id, classId: thing.clazzId, methodId: thing.methodId, name: thing.name, args: thing.args }); println('var %s = module.exports.%s = %s;', thing.info, thing.info, info); } // The flags are laid out in groups of fifteen in a short (high to // low bits), with a continuation bit (at 0) and another group // following if there's more than fifteen. Presence and absence // are conflated with true and false, for bit fields (i.e., if the // flag for the field is set, it's true, otherwise false). // // However, none of that is actually used in AMQP 0-9-1. The only // instance of properties -- basic properties -- has 14 fields, none // of them bits. function flagAt(index) { return 1 << (15 - index); } function encodePropsFn(props) { println('function %s(channel, size, fields) {', props.encoder); println('var offset = 0, flags = 0, val, len;'); println('var scratchOffset = 0, varyingSize = 0;'); const fixedSize = PROPERTIES_OVERHEAD; const args = props.args; function incVarying(by) { println('varyingSize += %d;', by); } for (let i = 0, num = args.length; i < num; i++) { const p = args[i]; assignArg(p); println('if (val != undefined) {'); println('if (%s) {', valTypeTest(p)); switch (p.type) { case 'shortstr': assignStringLen(p); incVarying(1); println('varyingSize += %s;', stringLenVar(p)); break; case 'longstr': incVarying(4); println('varyingSize += val.length;'); break; case 'table': assignTable(p); println('varyingSize += %s.length;', tableVar(p)); break; case 'octet': incVarying(1); break; case 'short': incVarying(2); break; case 'long': incVarying(4); break; case 'longlong': // fall through case 'timestamp': incVarying(8); break; // no case for bit, as they are accounted for in the flags } println('} else {'); println('throw new TypeError('); println('"Field \'%s\' is the wrong type; must be %s");', p.name, typeDesc(p.type)); println('}'); println('}'); } println('var buffer = Buffer.alloc(%d + varyingSize);', fixedSize); println('buffer[0] = %d', constants.FRAME_HEADER); println('buffer.writeUInt16BE(channel, 1);'); // content class ID and 'weight' (== 0) println('buffer.writeUInt32BE(%d, 7);', props.id << 16); // skip frame size for now, we'll write it in when we know. // body size println('buffer.writeBigUInt64BE(BigInt(size), 11);'); println('flags = 0;'); // we'll write the flags later too println('offset = 21;'); for (let i = 0, num = args.length; i < num; i++) { const p = args[i]; const flag = flagAt(i); assignArg(p); println('if (val != undefined) {'); if (p.type === 'bit') { // which none of them are .. println('if (val) flags += %d;', flag); } else { println('flags += %d;', flag); // %%% FIXME only slightly different to the method args encoding switch (p.type) { case 'octet': println('buffer.writeUInt8(val, offset); offset++;'); break; case 'short': println('buffer.writeUInt16BE(val, offset); offset += 2;'); break; case 'long': println('buffer.writeUInt32BE(val, offset); offset += 4;'); break; case 'longlong': case 'timestamp': println('buffer.writeBigUInt64BE(BigInt(val), offset);'); println('offset += 8;'); break; case 'shortstr': { const v = stringLenVar(p); println('buffer[offset] = %s; offset++;', v); println("buffer.write(val, offset, 'utf8');"); println('offset += %s;', v); break; } case 'longstr': println('buffer.writeUInt32BE(val.length, offset);'); println('offset += 4;'); println('offset += val.copy(buffer, offset);'); break; case 'table': println('offset += %s.copy(buffer, offset);', tableVar(p)); break; default: throw new Error(`Unexpected argument type: ${p.type}`); } } println('}'); // != undefined } println('buffer[offset] = %d;', constants.FRAME_END); // size does not include the frame header or frame end byte println('buffer.writeUInt32BE(offset - 7, 3);'); println('buffer.writeUInt16BE(flags, 19);'); println('return buffer.subarray(0, offset + 1);'); println('}'); } function decodePropsFn(props) { const args = props.args; println('function %s(buffer) {', props.decoder); println('var flags, offset = 2, val, len;'); println('flags = buffer.readUInt16BE(0);'); println('if (flags === 0) return {};'); fieldsDecl(args); for (let i = 0, num = args.length; i < num; i++) { const p = argument(args[i]); const field = `fields['${p.name}']`; println('if (flags & %d) {', flagAt(i)); if (p.type === 'bit') { println('%d = true;', field); } else { switch (p.type) { case 'octet': println('val = buffer[offset]; offset++;'); break; case 'short': println('val = buffer.readUInt16BE(offset); offset += 2;'); break; case 'long': println('val = buffer.readUInt32BE(offset); offset += 4;'); break; case 'longlong': case 'timestamp': println('val = Number(buffer.readBigUInt64BE(offset)); offset += 8;'); break; case 'longstr': println('len = buffer.readUInt32BE(offset); offset += 4;'); println('val = buffer.subarray(offset, offset + len);'); println('offset += len;'); break; case 'shortstr': println('len = buffer.readUInt8(offset); offset++;'); println('val = buffer.toString("utf8", offset, offset + len);'); println('offset += len;'); break; case 'table': println('len = buffer.readUInt32BE(offset); offset += 4;'); println('val = decodeFields(buffer.subarray(offset, offset + len));'); println('offset += len;'); break; default: throw new TypeError(`Unexpected type in argument list: ${p.type}`); } println('%s = val;', field); } println('}'); } println('return fields;'); println('}'); } amqp-node-amqplib-263c8a3/biome.json000066400000000000000000000024071520012327700173410ustar00rootroot00000000000000{ "formatter": { "enabled": true, "formatWithErrors": false, "indentStyle": "space", "indentWidth": 2, "lineWidth": 140, "lineEnding": "lf", "attributePosition": "auto", "includes": ["**/*.js", "!test"] }, "linter": { "enabled": true, "rules": { "complexity": { "noCommaOperator": "error", "noUselessCatch": "error", "useArrowFunction": "error", "useOptionalChain": "off", "noArguments": "error", "useLiteralKeys": "off" }, "suspicious": { "noRedundantUseStrict": "error", "noAsyncPromiseExecutor": "off", "noGlobalIsNan": "error", "noGlobalIsFinite": "error", "noPrototypeBuiltins": "error", "noVar": "error", "noExportsInTest": "off", "useIterableCallbackReturn": "off" }, "style": { "useSingleVarDeclarator": "error" } }, "includes": ["**/*.js", "!lib/defs.js"] }, "javascript": { "formatter": { "arrowParentheses": "always", "bracketSameLine": false, "bracketSpacing": true, "jsxQuoteStyle": "double", "quoteProperties": "asNeeded", "quoteStyle": "single", "semicolons": "always", "trailingCommas": "all" } } } amqp-node-amqplib-263c8a3/callback_api.d.ts000066400000000000000000000203251520012327700205310ustar00rootroot00000000000000import events = require('events'); import { ChannelOptions, ConsumeMessage, GetMessage, Message, Options, Replies, ServerProperties, SocketOptions, } from './lib/properties'; export * from './lib/properties'; export interface Connection extends events.EventEmitter { close(callback?: (err: Error) => void): void; createChannel(callback: (err: Error, channel: Channel) => void): Channel; createChannel(options: ChannelOptions, callback: (err: Error, channel: Channel) => void): Channel; createConfirmChannel(callback: (err: Error, confirmChannel: ConfirmChannel) => void): ConfirmChannel; createConfirmChannel(options: ChannelOptions, callback: (err: Error, confirmChannel: ConfirmChannel) => void): ConfirmChannel; readonly connection: { readonly serverProperties: ServerProperties; }; updateSecret(newSecret: Buffer, reason: string, callback?: (err: Error) => void): void; on(event: 'close', listener: (err?: Error) => void): this; on(event: 'error', listener: (err: Error) => void): this; on(event: 'blocked', listener: (reason: string) => void): this; on(event: 'unblocked', listener: () => void): this; on(event: 'update-secret-ok', listener: () => void): this; on(event: 'handler-error', listener: (err: Error, eventName: string) => void): this; on(event: string, listener: (...args: any[]) => void): this; } export interface Channel extends events.EventEmitter { readonly connection: Connection; on(event: 'close', listener: () => void): this; on(event: 'error', listener: (err: Error) => void): this; on(event: 'drain', listener: () => void): this; on(event: 'ack', listener: (fields: { deliveryTag: number; multiple: boolean }) => void): this; on(event: 'nack', listener: (fields: { deliveryTag: number; multiple: boolean; requeue: boolean }) => void): this; on(event: 'cancel', listener: (fields: { consumerTag: string }) => void): this; on(event: 'delivery', listener: (message: ConsumeMessage) => void): this; on(event: 'return', listener: (message: Message) => void): this; on(event: 'handler-error', listener: (err: Error, eventName: string) => void): this; on(event: string, listener: (...args: any[]) => void): this; close(callback?: (err: Error) => void): void; assertQueue(queue?: string, options?: Options.AssertQueue, callback?: (err: Error, ok: Replies.AssertQueue) => void): void; checkQueue(queue: string, callback?: (err: Error, ok: Replies.AssertQueue) => void): void; deleteQueue(queue: string, options?: Options.DeleteQueue, callback?: (err: Error, ok: Replies.DeleteQueue) => void): void; purgeQueue(queue: string, callback?: (err: Error, ok: Replies.PurgeQueue) => void): void; bindQueue( queue: string, source: string, pattern: string, args?: any, callback?: (err: Error, ok: Replies.Empty) => void, ): void; unbindQueue( queue: string, source: string, pattern: string, args?: any, callback?: (err: Error, ok: Replies.Empty) => void, ): void; assertExchange( exchange: string, type: 'direct' | 'topic' | 'headers' | 'fanout' | 'match' | string, options?: Options.AssertExchange, callback?: (err: Error, ok: Replies.AssertExchange) => void, ): void; checkExchange(exchange: string, callback?: (err: Error, ok: Replies.Empty) => void): void; deleteExchange(exchange: string, options?: Options.DeleteExchange, callback?: (err: Error, ok: Replies.Empty) => void): void; bindExchange( destination: string, source: string, pattern: string, args?: any, callback?: (err: Error, ok: Replies.Empty) => void, ): void; unbindExchange( destination: string, source: string, pattern: string, args?: any, callback?: (err: Error, ok: Replies.Empty) => void, ): void; publish(exchange: string, routingKey: string, content: Buffer, options?: Options.Publish): boolean; sendToQueue(queue: string, content: Buffer, options?: Options.Publish): boolean; consume( queue: string, onMessage: (msg: ConsumeMessage | null) => void, options?: Options.Consume, callback?: (err: Error, ok: Replies.Consume) => void, ): void; cancel(consumerTag: string, callback?: (err: Error, ok: Replies.Empty) => void): void; get(queue: string, options?: Options.Get, callback?: (err: Error, ok: GetMessage | false) => void): void; ack(message: Message, allUpTo?: boolean): void; ackAll(): void; nack(message: Message, allUpTo?: boolean, requeue?: boolean): void; nackAll(requeue?: boolean): void; reject(message: Message, requeue?: boolean): void; prefetch(count: number, global?: boolean, callback?: (err: Error, ok: Replies.Empty) => void): void; recover(callback?: (err: Error, ok: Replies.Empty) => void): void; } export interface ConfirmChannel extends Channel { publish( exchange: string, routingKey: string, content: Buffer, options?: Options.Publish, callback?: (err: Error, ok: Replies.Empty) => void, ): boolean; sendToQueue( queue: string, content: Buffer, options?: Options.Publish, callback?: (err: Error, ok: Replies.Empty) => void, ): boolean; waitForConfirms(callback?: (err?: Error) => void): void; } export declare class IllegalOperationError extends Error { name: 'IllegalOperationError'; stackAtStateChange: string | undefined; constructor(msg: string, stack?: string); } export declare const credentials: { plain(username: string, password: string): { mechanism: string; response(): Buffer; username: string; password: string; }; amqplain(username: string, password: string): { mechanism: string; response(): Buffer; username: string; password: string; }; external(): { mechanism: string; response(): Buffer; }; }; export interface RecoveryOptions { /** Initial reconnect delay in milliseconds. Default: 100 */ initialDelay?: number; /** Maximum reconnect delay in milliseconds. Default: 30000 */ maxDelay?: number; /** Backoff multiplier applied to delay on each attempt. Default: 2 */ factor?: number; /** Jitter factor (0–1) applied to delay to avoid thundering herd. Default: 0.2 */ jitter?: number; /** Maximum number of reconnect attempts. Default: Infinity */ maxRetries?: number; /** Optional setup function called after each successful connection */ setup?: ((model: Connection) => Promise) | ((model: Connection, done: (err?: Error) => void) => void); } export interface RecoveringConnection extends events.EventEmitter { close(callback?: (err: Error) => void): void; createChannel(callback: (err: Error, channel: Channel) => void): Channel; createChannel(options: ChannelOptions, callback: (err: Error, channel: Channel) => void): Channel; createConfirmChannel(callback: (err: Error, confirmChannel: ConfirmChannel) => void): ConfirmChannel; createConfirmChannel(options: ChannelOptions, callback: (err: Error, confirmChannel: ConfirmChannel) => void): ConfirmChannel; updateSecret(newSecret: Buffer, reason: string, callback?: (err: Error) => void): void; on(event: 'connect', listener: (model: Connection) => void): this; on(event: 'disconnect', listener: (err: Error) => void): this; on(event: 'connect-failed', listener: (err: Error) => void): this; on(event: 'reconnect-scheduled', listener: (info: { attempt: number; delay: number; error: Error }) => void): this; on(event: 'reconnect-failed', listener: (err: Error) => void): this; on(event: 'blocked', listener: (reason: string) => void): this; on(event: 'unblocked', listener: () => void): this; on(event: 'error', listener: (err: Error) => void): this; on(event: 'update-secret-ok', listener: () => void): this; on(event: string, listener: (...args: any[]) => void): this; } export declare function connect(callback: (err: Error, connection: Connection) => void): void; export declare function connect(url: string | Options.Connect, callback: (err: Error, connection: Connection) => void): void; export declare function connect( url: string | Options.Connect, socketOptions: SocketOptions, callback: (err: Error, connection: Connection) => void, ): void; export declare function connect( url: string | Options.Connect, socketOptions: SocketOptions & { recovery: RecoveryOptions | true }, callback: (err: Error, connection: RecoveringConnection) => void, ): RecoveringConnection; amqp-node-amqplib-263c8a3/callback_api.js000066400000000000000000000024121520012327700202720ustar00rootroot00000000000000const raw_connect = require('./lib/connect').connect; const CallbackModel = require('./lib/callback_model').CallbackModel; const recovery = require('./lib/recovery'); // Supports three shapes: // connect(url, options, callback) // connect(url, callback) // connect(callback) function connect(url, options, cb) { if (typeof url === 'function') { cb = url; url = false; options = false; } else if (typeof options === 'function') { cb = options; options = false; } const {connectionOptions, recovery: recoveryOptions} = recovery.splitConnectionOptions(options); if (recovery.recoveryEnabled(recoveryOptions)) { const openModel = () => { return new Promise((resolve, reject) => { raw_connect(url, connectionOptions, (err, c) => { if (err === null) resolve(new CallbackModel(c)); else reject(err); }); }); }; return recovery.connectWithRecoveryCallback(openModel, recoveryOptions, cb); } raw_connect(url, connectionOptions, (err, c) => { if (err === null) cb(null, new CallbackModel(c)); else cb(err); }); } module.exports.connect = connect; module.exports.credentials = require('./lib/credentials'); module.exports.IllegalOperationError = require('./lib/error').IllegalOperationError; amqp-node-amqplib-263c8a3/channel_api.js000066400000000000000000000016141520012327700201510ustar00rootroot00000000000000const raw_connect = require('./lib/connect').connect; const ChannelModel = require('./lib/channel_model').ChannelModel; const promisify = require('node:util').promisify; const recovery = require('./lib/recovery'); function connect(url, connOptions) { const {connectionOptions, recovery: recoveryOptions} = recovery.splitConnectionOptions(connOptions); if (recovery.recoveryEnabled(recoveryOptions)) { const openModel = () => promisify((cb) => raw_connect(url, connectionOptions, cb))().then((conn) => new ChannelModel(conn)); return recovery.connectWithRecoveryPromise(openModel, recoveryOptions); } return promisify((cb) => raw_connect(url, connectionOptions, cb))().then((conn) => new ChannelModel(conn)); } module.exports.connect = connect; module.exports.credentials = require('./lib/credentials'); module.exports.IllegalOperationError = require('./lib/error').IllegalOperationError; amqp-node-amqplib-263c8a3/examples/000077500000000000000000000000001520012327700171665ustar00rootroot00000000000000amqp-node-amqplib-263c8a3/examples/direct_reply_to_client.js000066400000000000000000000010771520012327700242560ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('../'); const queue = 'rpc_queue'; (async () => { const connection = await amqp.connect(); const channel = await connection.createChannel(); await channel.consume( 'amq.rabbitmq.reply-to', async (message) => { console.log(message.content.toString()); await channel.close(); await connection.close(); }, { noAck: true }, ); await channel.assertQueue(queue, { durable: false }); channel.sendToQueue(queue, Buffer.from(' [X] ping'), { replyTo: 'amq.rabbitmq.reply-to', }); })(); amqp-node-amqplib-263c8a3/examples/direct_reply_to_server.js000066400000000000000000000011511520012327700242770ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('../'); const queue = 'rpc_queue'; (async () => { const connection = await amqp.connect(); const channel = await connection.createChannel(); process.once('SIGINT', async () => { await channel.close(); await connection.close(); }); await channel.assertQueue(queue, { durable: false }); await channel.consume( queue, (message) => { console.log(message.content.toString()); channel.sendToQueue(message.properties.replyTo, Buffer.from(' [.] pong')); }, { noAck: true }, ); console.log(' [x] To exit press CTRL+C.'); })(); amqp-node-amqplib-263c8a3/examples/headers.js000077500000000000000000000023721520012327700211460ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('../'); (async () => { const connection = await amqp.connect(); const channel = await connection.createChannel(); process.once('SIGINT', async () => { await channel.close(); await connection.close(); }); const { exchange } = await channel.assertExchange('matching exchange', 'headers'); const { queue } = await channel.assertQueue(); // When using a headers exchange, the headers to be matched go in // the binding arguments. The routing key is ignore, so best left // empty. // 'x-match' is 'all' or 'any', meaning "all fields must match" or // "at least one field must match", respectively. The values to be // matched go in subsequent fields. await channel.bindQueue(queue, exchange, '', { 'x-match': 'any', foo: 'bar', baz: 'boo', }); await channel.consume( queue, (message) => { console.log(message.content.toString()); }, { noAck: true }, ); channel.publish(exchange, '', Buffer.from('hello'), { headers: { baz: 'boo' } }); channel.publish(exchange, '', Buffer.from('hello'), { headers: { foo: 'bar' } }); channel.publish(exchange, '', Buffer.from('lost'), { headers: { meh: 'nah' } }); console.log(' [x] To exit press CTRL+C.'); })(); amqp-node-amqplib-263c8a3/examples/receive_generator.js000066400000000000000000000016321520012327700232160ustar00rootroot00000000000000#!/usr/bin/env node const co = require('co'); const amqp = require('amqplib'); const readline = require('node:readline'); co(function* () { const myConsumer = (msg) => { if (msg !== null) { console.log('consuming message %s in generator', JSON.stringify(msg.content.toString())); } }; const conn = yield amqp.connect('amqp://localhost'); // create a message to consume const q = 'hello'; const msg = 'Hello World!'; const channel = yield conn.createChannel(); yield channel.assertQueue(q); channel.sendToQueue(q, Buffer.from(msg)); console.log(" [x] Sent '%s'", msg); // consume the message yield channel.consume(q, myConsumer, { noAck: true }); }).catch((err) => { console.warn('Error:', err); }); const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); // pend until message is consumed rl.question('newline to exit', () => process.exit()); amqp-node-amqplib-263c8a3/examples/send_generators.js000077500000000000000000000016321520012327700227130ustar00rootroot00000000000000#!/usr/bin/env node // NB this requires the module 'co': // npm install co const co = require('co'); const amqp = require('amqplib'); co(function* () { // connection errors are handled in the co .catch handler const conn = yield amqp.connect('amqp://localhost'); // try catch will throw any errors from the yielding the following promises to the co .catch handler try { const q = 'hello'; const msg = 'Hello World!'; // use a confirm channel so we can check the message is sent OK. const channel = yield conn.createConfirmChannel(); yield channel.assertQueue(q); channel.sendToQueue(q, Buffer.from(msg)); // if message has been nacked, this will result in an error (rejected promise); yield channel.waitForConfirms(); console.log(" [x] Sent '%s'", msg); channel.close(); } finally { conn.close(); } }).catch((err) => { console.warn('Error:', err); }); amqp-node-amqplib-263c8a3/examples/ssl.js000066400000000000000000000046521520012327700203340ustar00rootroot00000000000000// Example of using a TLS/SSL connection. Note that the server must be // configured to accept SSL connections; see, for example, // http://www.rabbitmq.com/ssl.html. // // When trying this out, I followed the RabbitMQ SSL guide above, // almost verbatim. I set the CN of the server certificate to // 'localhost' rather than $(hostname) (since on my MBP hostname ends // up being ".local", which is just weird). My client // certificates etc., are in `../etc/client/`. My testca certificate // is in `../etc/testca` and server certs etc., in `../etc/server`, // and I've made a `rabbitmq.config` file, with which I start // RabbitMQ: // // RABBITMQ_CONFIG_FILE=`pwd`/../etc/server/rabbitmq \ // /usr/local/sbin/rabbitmq-server & // // A way to check RabbitMQ's running with SSL OK is to use // // openssl s_client -connect localhost:5671 const amqp = require('../'); const fs = require('node:fs'); // Assemble the SSL options; for verification we need at least // * a certificate to present to the server ('cert', in PEM format) // * the private key for the certificate ('key', in PEM format) // * (possibly) a passphrase for the private key // // The first two may be replaced with a PKCS12 file ('pfx', in pkcs12 // format) // We will also want to list the CA certificates that we will trust, // since we're using a self-signed certificate. It is NOT recommended // to use `rejectUnauthorized: false`. // Options for full client and server verification: const opts = { cert: fs.readFileSync('../etc/client/cert.pem'), key: fs.readFileSync('../etc/client/key.pem'), // cert and key or // pfx: fs.readFileSync('../etc/client/keycert.p12'), passphrase: 'MySecretPassword', ca: [fs.readFileSync('../etc/testca/cacert.pem')], }; // Options for just confidentiality. This requires RabbitMQ's SSL // configuration to include the items // // {verify, verify_none}, // {fail_if_no_peer_cert,false} // // const opts = { ca: [fs.readFileSync('../etc/testca/cacert.pem')] }; // Option to use the SSL client certificate for authentication // opts.credentials = amqp.credentials.external(); (async () => { const connection = await amqp.connect('amqp://localhost', opts); const channel = await connection.createChannel(); process.on('SIGINT', async () => { await channel.close(); await connection.close(); }); channel.sendToQueue('foo', Buffer.from('Hello World!')); console.log(' [x] To exit press CTRL+C.'); })(); amqp-node-amqplib-263c8a3/examples/stream_queues/000077500000000000000000000000001520012327700220505ustar00rootroot00000000000000amqp-node-amqplib-263c8a3/examples/stream_queues/.gitignore000066400000000000000000000000221520012327700240320ustar00rootroot00000000000000package-lock.json amqp-node-amqplib-263c8a3/examples/stream_queues/README.md000066400000000000000000000035101520012327700233260ustar00rootroot00000000000000RabbitMQ Stream Examples --- The [stream queues](https://www.rabbitmq.com/streams.html) are available starting from RabbitMQ 3.9 These examples show how to use stream queues with the lib. Send a message to a stream queue ``` node send_stream.js ``` Receive all the messages from stream queue: ``` node receive_stream.js ``` Consumers can be configured to receive messages using an offset via the `x-stream-offset` argument. e.g. ```js channel.consume(queue, onMessage, { noAck: false, arguments: { 'x-stream-offset': 'first' } }); ``` RabbitMQ supports six different types of offset, however specifying them can be | Offset Type | Example Value | Notes | |-----------|----------------------------------------------------------|-------| | First | `{'x-stream-offset':'first'}` | Start from the first message in the log | | Last | `{'x-stream-offset':'last'}` | Start from the last "chunk" of messages (could be multiple messages) | | Next | `{'x-stream-offset':'next'}` | Start from the next message (the default) | | Offset | `{'x-stream-offset':5}` | a numerical value specifying an exact offset to attach to the log at | | Timestamp | `{'x-stream-offset':{'!':'timestamp',value:1686519750}}` | a timestamp value specifying the point in time to attach to the log at. The timestamp must be the number of seconds since 00:00:00 UTC, 1970-01-01. Consumers can receive messages published a bit before the specified timestamp. | | Interval | `{'x-stream-offset':'1h'}` | the time interval relative to current time to attach the log at. Valid units are Y, M, D, h, m and s | See https://www.rabbitmq.com/streams.html#consuming for more details amqp-node-amqplib-263c8a3/examples/stream_queues/package.json000066400000000000000000000004621520012327700243400ustar00rootroot00000000000000{ "name": "stream_queues", "version": "1.0.0", "description": "An example demonstrating use of stream queues", "main": "n", "dependencies": { "amqplib": "^0.10.3" }, "devDependencies": {}, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "license": "ISC" } amqp-node-amqplib-263c8a3/examples/stream_queues/receive_stream.js000077500000000000000000000034101520012327700254040ustar00rootroot00000000000000const amqp = require('amqplib'); (async () => { try { const connection = await amqp.connect('amqp://localhost'); process.once('SIGINT', connection.close); const channel = await connection.createChannel(); const queue = 'my_first_stream'; // Define the queue stream // Mandatory: exclusive: false, durable: true autoDelete: false await channel.assertQueue(queue, { exclusive: false, durable: true, autoDelete: false, arguments: { 'x-queue-type': 'stream', // Mandatory to define stream queue 'x-max-length-bytes': 2_000_000_000, // Set the queue retention to 2GB else the stream doesn't have any limit }, }); channel.qos(100); // This is mandatory channel.consume( queue, (msg) => { console.log(" [x] Received '%s'", msg.content.toString()); channel.ack(msg); // Mandatory }, { noAck: false, arguments: { /* Here you can specify the offset: : first, last, next, offset, timestamp and interval, i.e. 'x-stream-offset': 'first' 'x-stream-offset': 'last' 'x-stream-offset': 'next' 'x-stream-offset': 5 'x-stream-offset': { '!': 'timestamp', value: 1686519750 } 'x-stream-offset': '1h' The timestamp must be the desired number of seconds since 00:00:00 UTC, 1970-01-01 The interval units can be Y, M, D, h, m, s */ 'x-stream-offset': 'first', }, }, ); console.log(' [*] Waiting for messages. To exit press CTRL+C'); } catch (e) { // Catch and display any errors in the console console.log(e); } })(); amqp-node-amqplib-263c8a3/examples/stream_queues/send_stream.js000077500000000000000000000020341520012327700247140ustar00rootroot00000000000000const amqp = require('amqplib'); (async () => { try { const connection = await amqp.connect('amqp://localhost'); process.once('SIGINT', connection.close); const channel = await connection.createChannel(); const queue = 'my_first_stream'; const msg = `Hello World! ${Date.now()}`; // Define the queue stream // Mandatory: exclusive: false, durable: true autoDelete: false await channel.assertQueue(queue, { exclusive: false, durable: true, autoDelete: false, arguments: { 'x-queue-type': 'stream', // Mandatory to define stream queue 'x-max-length-bytes': 2_000_000_000, // Set the queue retention to 2GB else the stream doesn't have any limit }, }); // Send the message to the stream queue await channel.sendToQueue(queue, Buffer.from(msg)); console.log(" [x] Sent '%s'", msg); await channel.close(); // Close connection connection.close(); } catch (e) { // Catch and display any errors in the console console.log(e); } })(); amqp-node-amqplib-263c8a3/examples/tutorials/000077500000000000000000000000001520012327700212145ustar00rootroot00000000000000amqp-node-amqplib-263c8a3/examples/tutorials/README.md000066400000000000000000000055631520012327700225040ustar00rootroot00000000000000# RabbitMQ tutorials This directory contains the [RabbitMQ tutorials][rabbitmq-tutes], ported to amqplib. The sub-directory `callback_api` has translations of the tutorial programs to the callback-oriented API. ## Preparation To run the tutorial code, you need amqplib installed. Assuming you are in a clone of the amqplib repository, from the tutorials directory: npm install or to use the latest released version, npm install amqplib Then just run each file as a script, e.g., in bash ./send.js or node send.js or nave use 0.8 node send.js ## [Tutorial one: Hello World!][tute-one] A "Hello World" example, with one script sending a message to a queue, and another receiving messages from the same queue. * [send.js](send.js) * [receive.js](receive.js) ## [Tutorial two: Work queues][tute-two] Using RabbitMQ as a work queue; `new_task` creates a task, and `worker` processes tasks. Multiple `worker` process will share the tasks among them. Long-running tasks are simulated by supplying a string with dots, e.g., '...' to `new_task`. Each dot makes the worker "work" for a second. * [new_task.js](new_task.js) * [worker.js](worker.js) ## [Tutorial three: Publish/Subscribe][tute-three] Using RabbitMQ as a broadcast mechanism. `emit_log` sends a "log" message to a fanout exchange, and all `receive_logs` processes receive log messages. * [emit_log.js](emit_log.js) * [receive_logs.js](receive_logs.js) ## [Tutorial four: Routing][tute-four] Using RabbitMQ as a routing ('somecast') mechanism. `emit_log_direct` sends a log message with a severity, and all `receive_logs_direct` processes receive log messages for the severities on which they are listening. * [emit_log_direct.js](emit_log_direct.js) * [receive_logs_direct.js](receive_logs_direct.js) ## [Tutorial five: Topics][tute-five] Extends the previous tutorial to routing with wildcarded patterns. * [emit_log_topic.js](emit_log_topic.js) * [receive_logs_topic.js](receive_logs_topic.js) ## [Tutorial six: RPC][tute-six] Using RabbitMQ as an RPC intermediary, queueing requests for servers and routing replies back to clients. * [rpc_server.js](rpc_server.js) * [rpc_client.js](rpc_client.js) I depart slightly from the original tutorial code, which I think has some needless object-orientation (in the Python code; you don't get a choice about needless object-orientation in Java). [rabbitmq-tutes]: http://github.com/rabbitmq/rabbitmq-tutorials [tute-one]: http://www.rabbitmq.com/tutorials/tutorial-one-javascript.html [tute-two]: http://www.rabbitmq.com/tutorials/tutorial-two-javascript.html [tute-three]: http://www.rabbitmq.com/tutorials/tutorial-three-javascript.html [tute-four]: http://www.rabbitmq.com/tutorials/tutorial-four-javascript.html [tute-five]: http://www.rabbitmq.com/tutorials/tutorial-five-javascript.html [tute-six]: http://www.rabbitmq.com/tutorials/tutorial-six-javascript.html amqp-node-amqplib-263c8a3/examples/tutorials/callback_api/000077500000000000000000000000001520012327700236015ustar00rootroot00000000000000amqp-node-amqplib-263c8a3/examples/tutorials/callback_api/emit_log.js000077500000000000000000000013711520012327700257430ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('amqplib/callback_api'); const exchange = 'logs'; const text = process.argv.slice(2).join(' ') || 'info: Hello World!'; amqp.connect((err, connection) => { if (err) return bail(err); connection.createChannel((err, channel) => { if (err) return bail(err, connection); channel.assertExchange(exchange, 'fanout', { durable: false }, (err) => { if (err) return bail(err, connection); channel.publish(exchange, '', Buffer.from(text)); console.log(" [x] Sent '%s'", text); channel.close(() => { connection.close(); }); }); }); }); function bail(err, connection) { console.error(err); if (connection) connection.close(() => { process.exit(1); }); } amqp-node-amqplib-263c8a3/examples/tutorials/callback_api/emit_log_direct.js000077500000000000000000000015251520012327700272760ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('amqplib/callback_api'); const exchange = 'direct_logs'; const args = process.argv.slice(2); const routingKey = args.length > 0 ? args[0] : 'info'; const text = args.slice(1).join(' ') || 'Hello World!'; amqp.connect((err, connection) => { if (err) return bail(err); connection.createChannel((err, channel) => { if (err) return bail(err, connection); channel.assertExchange(exchange, 'direct', { durable: false }, (err) => { if (err) return bail(err, connection); channel.publish(exchange, routingKey, Buffer.from(text)); console.log(" [x] Sent '%s'", text); channel.close(() => { connection.close(); }); }); }); }); function bail(err, connection) { console.error(err); if (connection) connection.close(() => { process.exit(1); }); } amqp-node-amqplib-263c8a3/examples/tutorials/callback_api/emit_log_topic.js000077500000000000000000000015231520012327700271400ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('amqplib/callback_api'); const exchange = 'topic_logs'; const args = process.argv.slice(2); const routingKey = args.length > 0 ? args[0] : 'info'; const text = args.slice(1).join(' ') || 'Hello World!'; amqp.connect((err, connection) => { if (err) return bail(err); connection.createChannel((err, channel) => { if (err) return bail(err, connection); channel.assertExchange(exchange, 'topic', { durable: false }, (err) => { if (err) return bail(err, connection); channel.publish(exchange, routingKey, Buffer.from(text)); console.log(" [x] Sent '%s'", text); channel.close(() => { connection.close(); }); }); }); }); function bail(err, connection) { console.error(err); if (connection) connection.close(() => { process.exit(1); }); } amqp-node-amqplib-263c8a3/examples/tutorials/callback_api/new_task.js000077500000000000000000000013711520012327700257570ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('amqplib/callback_api'); const queue = 'task_queue'; const text = process.argv.slice(2).join(' ') || 'Hello World!'; amqp.connect((err, connection) => { if (err) return bail(err); connection.createChannel((err, channel) => { if (err) return bail(err, connection); channel.assertQueue(queue, { durable: true }, (err) => { if (err) return bails(err, connection); channel.sendToQueue(queue, Buffer.from(text), { persistent: true }); console.log(" [x] Sent '%s'", text); channel.close(() => { connection.close(); }); }); }); }); function bail(err, connection) { console.error(err); if (connection) connection.close(() => { process.exit(1); }); } amqp-node-amqplib-263c8a3/examples/tutorials/callback_api/receive.js000077500000000000000000000017711520012327700255720ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('amqplib/callback_api'); const queue = 'hello'; amqp.connect((err, connection) => { if (err) return bail(err); connection.createChannel((err, channel) => { if (err) return bail(err, connection); process.once('SIGINT', () => { channel.close(() => { connection.close(); }); }); channel.assertQueue(queue, { durable: false }, (err) => { if (err) return bail(err, connection); channel.consume( queue, (message) => { if (message) console.log(" [x] Received '%s'", message.content.toString()); else console.warn(' [x] Consumer cancelled'); }, { noAck: true }, (err) => { if (err) return bail(err, connection); console.log(' [*] Waiting for logs. To exit press CTRL+C.'); }, ); }); }); }); function bail(err, connection) { console.error(err); if (connection) connection.close(() => { process.exit(1); }); } amqp-node-amqplib-263c8a3/examples/tutorials/callback_api/receive_logs.js000077500000000000000000000025021520012327700266070ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('amqplib/callback_api'); const exchange = 'logs'; amqp.connect((err, connection) => { if (err) return bail(err); connection.createChannel((err, channel) => { if (err) return bail(err, connection); process.once('SIGINT', () => { channel.close(() => { connection.close(); }); }); channel.assertExchange(exchange, 'fanout', { durable: false }, (err, { queue: _queue }) => { if (err) return bail(err, connection); channel.assertQueue('', { exclusive: true }, (err, { queue }) => { if (err) return bail(err, connection); channel.bindQueue(queue, exchange, '', {}, (err) => { if (err) return bail(err, connection); channel.consume( queue, (message) => { if (message) console.log(" [x] '%s'", message.content.toString()); else console.warn(' [x] Consumer cancelled'); }, { noAck: true }, (err) => { if (err) return bail(err, connection); console.log(' [*] Waiting for logs. To exit press CTRL+C.'); }, ); }); }); }); }); }); function bail(err, connection) { console.error(err); if (connection) connection.close(() => { process.exit(1); }); } amqp-node-amqplib-263c8a3/examples/tutorials/callback_api/receive_logs_direct.js000077500000000000000000000035051520012327700301450ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('amqplib/callback_api'); const { basename } = require('node:path'); const exchange = 'direct_logs'; const severities = process.argv.slice(2); if (severities.length < 1) { console.log('Usage %s [info] [warning] [error]', basename(process.argv[1])); process.exit(1); } amqp.connect((err, connection) => { if (err) return bail(err); connection.createChannel((err, channel) => { if (err) return bail(err, connection); process.once('SIGINT', () => { channel.close(() => { connection.close(); }); }); channel.assertExchange(exchange, 'direct', { durable: false }, (err) => { if (err) return bail(err, connection); channel.assertQueue('', { exclusive: true }, (err, { queue }) => { if (err) return bail(err, connection); channel.consume( queue, (message) => { if (message) console.log(" [x] %s:'%s'", message.fields.routingKey, message.content.toString()); else console.warn(' [x] Consumer cancelled'); }, { noAck: true }, (err) => { if (err) return bail(err, connection); console.log(' [*] Waiting for logs. To exit press CTRL+C.'); subscribeAll(channel, queue, severities, (err) => { if (err) return bail(err, connection); }); }, ); }); }); }); }); function subscribeAll(channel, queue, bindingKeys, cb) { if (bindingKeys.length === 0) return cb(); const bindingKey = bindingKeys.shift(); channel.bindQueue(queue, exchange, bindingKey, {}, (err) => { if (err) return cb(err); subscribeAll(channel, queue, bindingKeys, cb); }); } function bail(err, connection) { console.error(err); if (connection) connection.close(() => { process.exit(1); }); } amqp-node-amqplib-263c8a3/examples/tutorials/callback_api/receive_logs_topic.js000077500000000000000000000035031520012327700300070ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('amqplib/callback_api'); const { basename } = require('node:path'); const exchange = 'topic_logs'; const severities = process.argv.slice(2); if (severities.length < 1) { console.log('Usage %s [info] [warning] [error]', basename(process.argv[1])); process.exit(1); } amqp.connect((err, connection) => { if (err) return bail(err); connection.createChannel((err, channel) => { if (err) return bail(err, connection); process.once('SIGINT', () => { channel.close(() => { connection.close(); }); }); channel.assertExchange(exchange, 'topic', { durable: false }, (err) => { if (err) return bail(err, connection); channel.assertQueue('', { exclusive: true }, (err, { queue }) => { if (err) return bail(err, connection); channel.consume( queue, (message) => { if (message) console.log(" [x] %s:'%s'", message.fields.routingKey, message.content.toString()); else console.warn(' [x] Consumer cancelled'); }, { noAck: true }, (err) => { if (err) return bail(err, connection); console.log(' [*] Waiting for logs. To exit press CTRL+C.'); subscribeAll(channel, queue, severities, (err) => { if (err) return bail(err, connection); }); }, ); }); }); }); }); function subscribeAll(channel, queue, bindingKeys, cb) { if (bindingKeys.length === 0) return cb(); const bindingKey = bindingKeys.shift(); channel.bindQueue(queue, exchange, bindingKey, {}, (err) => { if (err) return cb(err); subscribeAll(channel, queue, bindingKeys, cb); }); } function bail(err, connection) { console.error(err); if (connection) connection.close(() => { process.exit(1); }); } amqp-node-amqplib-263c8a3/examples/tutorials/callback_api/rpc_client.js000077500000000000000000000027341520012327700262720ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('amqplib/callback_api'); const { basename } = require('node:path'); const { v4: uuid } = require('uuid'); const queue = 'rpc_queue'; const n = parseInt(process.argv[2], 10); if (Number.isNaN(n)) { console.warn('Usage: %s number', basename(process.argv[1])); process.exit(1); } amqp.connect((err, connection) => { if (err) return bail(err); connection.createChannel((err, channel) => { if (err) return bail(err, connection); channel.assertQueue('', { exclusive: true }, (err, { queue: replyTo }) => { if (err) return bail(err, connection); const correlationId = uuid(); channel.consume( replyTo, (message) => { if (!message) console.warn(' [x] Consumer cancelled'); else if (message.properties.correlationId === correlationId) { console.log(' [.] Got %d', message.content.toString()); channel.close(() => { connection.close(); }); } }, { noAck: true }, ); channel.assertQueue(queue, { durable: false }, (err) => { if (err) return bail(err, connection); console.log(' [x] Requesting fib(%d)', n); channel.sendToQueue(queue, Buffer.from(n.toString()), { correlationId, replyTo, }); }); }); }); }); function bail(err, connection) { console.error(err); if (connection) connection.close(() => { process.exit(1); }); } amqp-node-amqplib-263c8a3/examples/tutorials/callback_api/rpc_server.js000077500000000000000000000027621520012327700263230ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('amqplib/callback_api'); const queue = 'rpc_queue'; amqp.connect((err, connection) => { if (err) return bail(err); connection.createChannel((err, channel) => { if (err) return bail(err, connection); process.once('SIGINT', () => { channel.close(() => { connection.close(); }); }); channel.assertQueue(queue, { durable: false }, (err) => { if (err) return bail(err, connection); channel.prefetch(1); channel.consume( queue, (message) => { const n = parseInt(message.content.toString(), 10); console.log(' [.] fib(%d)', n); const response = fib(n); channel.sendToQueue(message.properties.replyTo, Buffer.from(response.toString()), { correlationId: message.properties.correlationId, }); channel.ack(message); }, { noAck: false }, (err) => { if (err) return bail(err, conn); console.log(' [x] Awaiting RPC requests. To exit press CTRL+C.'); }, ); }); }); }); function fib(n) { // Do it the ridiculous, but not most ridiculous, way. For better, // see http://nayuki.eigenstate.org/page/fast-fibonacci-algorithms let a = 0; let b = 1; for (let i = 0; i < n; i++) { const c = a + b; a = b; b = c; } return a; } function bail(err, connection) { console.error(err); if (connection) connection.close(() => { process.exit(1); }); } amqp-node-amqplib-263c8a3/examples/tutorials/callback_api/send.js000077500000000000000000000012731520012327700250760ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('amqplib/callback_api'); const queue = 'hello'; const text = 'Hello World!'; amqp.connect((err, connection) => { if (err) return bail(err); connection.createChannel((err, channel) => { if (err) return bail(err, connection); channel.assertQueue(queue, { durable: false }, (err) => { if (err) return bail(err, connection); channel.sendToQueue(queue, Buffer.from(text)); console.log(" [x] Sent '%s'", text); channel.close(() => { connection.close(); }); }); }); }); function bail(err, connection) { console.error(err); if (connection) connection.close(() => { process.exit(1); }); } amqp-node-amqplib-263c8a3/examples/tutorials/callback_api/worker.js000077500000000000000000000021121520012327700254470ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('amqplib/callback_api'); const queue = 'task_queue'; amqp.connect((err, connection) => { if (err) return bail(err); connection.createChannel((err, channel) => { if (err) return bail(err, connection); process.once('SIGINT', () => { channel.close(() => { connection.close(); }); }); channel.assertQueue(queue, { durable: true }, (err, { queue }) => { if (err) return bail(err, connection); channel.consume( queue, (message) => { const text = message.content.toString(); console.log(" [x] Received '%s'", text); const seconds = text.split('.').length - 1; setTimeout(() => { console.log(' [x] Done'); channel.ack(message); }, seconds * 1000); }, { noAck: false }, ); console.log(' [*] Waiting for messages. To exit press CTRL+C'); }); }); }); function bail(err, connection) { console.error(err); if (connection) connection.close(() => { process.exit(1); }); } amqp-node-amqplib-263c8a3/examples/tutorials/emit_log.js000077500000000000000000000011421520012327700233520ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('amqplib'); const exchange = 'logs'; const text = process.argv.slice(2).join(' ') || 'info: Hello World!'; (async () => { let connection; try { connection = await amqp.connect('amqp://localhost'); const channel = await connection.createChannel(); await channel.assertExchange(exchange, 'fanout', { durable: false }); channel.publish(exchange, '', Buffer.from(text)); console.log(" [x] Sent '%s'", text); await channel.close(); } catch (err) { console.warn(err); } finally { if (connection) await connection.close(); } })(); amqp-node-amqplib-263c8a3/examples/tutorials/emit_log_direct.js000077500000000000000000000013151520012327700247060ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('amqplib'); const exchange = 'direct_logs'; const args = process.argv.slice(2); const routingKey = args.length > 0 ? args[0] : 'info'; const text = args.slice(1).join(' ') || 'Hello World!'; (async () => { let connection; try { connection = await amqp.connect('amqp://localhost'); const channel = await connection.createChannel(); await channel.assertExchange(exchange, 'direct', { durable: false }); channel.publish(exchange, routingKey, Buffer.from(text)); console.log(" [x] Sent %s:'%s'", routingKey, text); await channel.close(); } catch (err) { console.warn(err); } finally { if (connection) await connection.close(); } })(); amqp-node-amqplib-263c8a3/examples/tutorials/emit_log_topic.js000077500000000000000000000013161520012327700245530ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('amqplib'); const exchange = 'topic_logs'; const args = process.argv.slice(2); const routingKeys = args.length > 0 ? args[0] : 'info'; const text = args.slice(1).join(' ') || 'Hello World!'; (async () => { let connection; try { connection = await amqp.connect('amqp://localhost'); const channel = await connection.createChannel(); await channel.assertExchange(exchange, 'topic', { durable: false }); channel.publish(exchange, routingKeys, Buffer.from(text)); console.log(" [x] Sent %s:'%s'", routingKeys, text); await channel.close(); } catch (err) { console.warn(err); } finally { if (connection) await connection.close(); } })(); amqp-node-amqplib-263c8a3/examples/tutorials/new_task.js000077500000000000000000000011661520012327700233740ustar00rootroot00000000000000#!/usr/bin/env node // Post a new task to the work queue const amqp = require('amqplib'); const queue = 'task_queue'; const text = process.argv.slice(2).join(' ') || 'Hello World!'; (async () => { let connection; try { connection = await amqp.connect('amqp://localhost'); const channel = await connection.createChannel(); await channel.assertQueue(queue, { durable: true }); channel.sendToQueue(queue, Buffer.from(text), { persistent: true }); console.log(" [x] Sent '%s'", text); await channel.close(); } catch (err) { console.warn(err); } finally { await connection.close(); } })(); amqp-node-amqplib-263c8a3/examples/tutorials/package.json000066400000000000000000000005761520012327700235120ustar00rootroot00000000000000{ "name": "amqplib-tutorials", "version": "0.0.1", "description": "The RabbitMQ tutorials, ported to amqplib", "main": "send.js", "dependencies": { "amqplib": "../..", "uuid": "*" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "repository": "", "author": "Michael Bridgen ", "license": "MPL 2.0" } amqp-node-amqplib-263c8a3/examples/tutorials/receive.js000077500000000000000000000012501520012327700231750ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('amqplib'); const queue = 'hello'; (async () => { try { const connection = await amqp.connect('amqp://localhost'); const channel = await connection.createChannel(); process.once('SIGINT', async () => { await channel.close(); await connection.close(); }); await channel.assertQueue(queue, { durable: false }); await channel.consume( queue, (message) => { console.log(" [x] Received '%s'", message.content.toString()); }, { noAck: true }, ); console.log(' [*] Waiting for messages. To exit press CTRL+C'); } catch (err) { console.warn(err); } })(); amqp-node-amqplib-263c8a3/examples/tutorials/receive_logs.js000077500000000000000000000015551520012327700242310ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('amqplib'); const exchange = 'logs'; (async () => { try { const connection = await amqp.connect('amqp://localhost'); const channel = await connection.createChannel(); process.once('SIGINT', async () => { await channel.close(); await connection.close(); }); await channel.assertExchange(exchange, 'fanout', { durable: false }); const { queue } = await channel.assertQueue('', { exclusive: true }); await channel.bindQueue(queue, exchange, ''); await channel.consume( queue, (message) => { if (message) console.log(" [x] '%s'", message.content.toString()); else console.warn(' [x] Consumer cancelled'); }, { noAck: true }, ); console.log(' [*] Waiting for logs. To exit press CTRL+C'); } catch (err) { console.warn(err); } })(); amqp-node-amqplib-263c8a3/examples/tutorials/receive_logs_direct.js000077500000000000000000000023151520012327700255560ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('../..'); const { basename } = require('node:path'); const exchange = 'direct_logs'; const bindingKeys = process.argv.slice(2); if (bindingKeys.length < 1) { console.warn('Usage: %s [info] [warning] [error]', basename(process.argv[1])); process.exit(1); } (async () => { try { const connection = await amqp.connect('amqp://localhost'); const channel = await connection.createChannel(); process.once('SIGINT', async () => { await channel.close(); await connection.close(); }); await channel.assertExchange(exchange, 'direct', { durable: false }); const { queue } = await channel.assertQueue('', { exclusive: true }); await Promise.all( bindingKeys.map(async (bindingKey) => { await channel.bindQueue(queue, exchange, bindingKey); }), ); await channel.consume( queue, (message) => { if (message) console.log(" [x] %s:'%s'", message.fields.routingKey, message.content.toString()); else console.warn(' [x] Consumer cancelled'); }, { noAck: true }, ); console.log(' [*] Waiting for logs. To exit press CTRL+C.'); } catch (err) { console.warn(err); } })(); amqp-node-amqplib-263c8a3/examples/tutorials/receive_logs_topic.js000077500000000000000000000023061520012327700254220ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('../..'); const { basename } = require('node:path'); const exchange = 'topic_logs'; const bindingKeys = process.argv.slice(2); if (bindingKeys.length < 1) { console.log('Usage: %s pattern [pattern...]', basename(process.argv[1])); process.exit(1); } (async () => { try { const connection = await amqp.connect('amqp://localhost'); const channel = await connection.createChannel(); process.once('SIGINT', async () => { await channel.close(); await connection.close(); }); await channel.assertExchange(exchange, 'topic', { durable: false }); const { queue } = await channel.assertQueue('', { exclusive: true }); await Promise.all( bindingKeys.map(async (bindingKey) => { await channel.bindQueue(queue, exchange, bindingKey); }), ); await channel.consume( queue, (message) => { if (message) console.log(" [x] %s:'%s'", message.fields.routingKey, message.content.toString()); else console.warn(' [x] Consumer cancelled'); }, { noAck: true }, ); console.log(' [*] Waiting for logs. To exit press CTRL+C.'); } catch (err) { console.warn(err); } })(); amqp-node-amqplib-263c8a3/examples/tutorials/rpc_client.js000077500000000000000000000025321520012327700237010ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('amqplib'); const { basename } = require('node:path'); const { v4: uuid } = require('uuid'); const queue = 'rpc_queue'; const n = parseInt(process.argv[2], 10); if (Number.isNaN(n)) { console.warn('Usage: %s number', basename(process.argv[1])); process.exit(1); } (async () => { let connection; try { connection = await amqp.connect('amqp://localhost'); const channel = await connection.createChannel(); const correlationId = uuid(); const requestFib = new Promise(async (resolve) => { const { queue: replyTo } = await channel.assertQueue('', { exclusive: true }); await channel.consume( replyTo, (message) => { if (!message) console.warn(' [x] Consumer cancelled'); else if (message.properties.correlationId === correlationId) { resolve(message.content.toString()); } }, { noAck: true }, ); await channel.assertQueue(queue, { durable: false }); console.log(' [x] Requesting fib(%d)', n); channel.sendToQueue(queue, Buffer.from(n.toString()), { correlationId, replyTo, }); }); const fibN = await requestFib; console.log(' [.] Got %d', fibN); } catch (err) { console.warn(err); } finally { if (connection) await connection.close(); } })(); amqp-node-amqplib-263c8a3/examples/tutorials/rpc_server.js000077500000000000000000000022411520012327700237260ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('amqplib'); const queue = 'rpc_queue'; (async () => { try { const connection = await amqp.connect('amqp://localhost'); const channel = await connection.createChannel(); process.once('SIGINT', async () => { await channel.close(); await connection.close(); }); await channel.assertQueue(queue, { durable: false }); channel.prefetch(1); await channel.consume(queue, (message) => { const n = parseInt(message.content.toString(), 10); console.log(' [.] fib(%d)', n); const response = fib(n); channel.sendToQueue(message.properties.replyTo, Buffer.from(response.toString()), { correlationId: message.properties.correlationId, }); channel.ack(message); }); console.log(' [x] Awaiting RPC requests. To exit press CTRL+C.'); } catch (err) { console.warn(err); } })(); function fib(n) { // Do it the ridiculous, but not most ridiculous, way. For better, // see http://nayuki.eigenstate.org/page/fast-fibonacci-algorithms let a = 0; let b = 1; for (let i = 0; i < n; i++) { const c = a + b; a = b; b = c; } return a; } amqp-node-amqplib-263c8a3/examples/tutorials/send.js000077500000000000000000000015101520012327700225030ustar00rootroot00000000000000#!/usr/bin/env node const amqp = require('amqplib'); const queue = 'hello'; const text = 'Hello World!'; (async () => { let connection; try { connection = await amqp.connect('amqp://localhost'); const channel = await connection.createChannel(); await channel.assertQueue(queue, { durable: false }); // NB: `sentToQueue` and `publish` both return a boolean // indicating whether it's OK to send again straight away, or // (when `false`) that you should wait for the event `'drain'` // to fire before writing again. We're just doing the one write, // so we'll ignore it. channel.sendToQueue(queue, Buffer.from(text)); console.log(" [x] Sent '%s'", text); await channel.close(); } catch (err) { console.warn(err); } finally { if (connection) await connection.close(); } })(); amqp-node-amqplib-263c8a3/examples/tutorials/worker.js000077500000000000000000000016301520012327700230660ustar00rootroot00000000000000#!/usr/bin/env node // Process tasks from the work queue const amqp = require('amqplib'); const queue = 'task_queue'; (async () => { try { const connection = await amqp.connect('amqp://localhost'); process.once('SIGINT', async () => { await connection.close(); }); const channel = await connection.createChannel(); await channel.assertQueue(queue, { durable: true }); channel.prefetch(1); await channel.consume( queue, (message) => { const text = message.content.toString(); console.log(" [x] Received '%s'", text); const seconds = text.split('.').length - 1; setTimeout(() => { console.log(' [x] Done'); channel.ack(message); }, seconds * 1000); }, { noAck: false }, ); console.log(' [*] Waiting for messages. To exit press CTRL+C'); } catch (err) { console.warn(err); } })(); amqp-node-amqplib-263c8a3/examples/waitForConfirms.js000066400000000000000000000007601520012327700226430ustar00rootroot00000000000000const amqp = require('../'); (async () => { let connection; try { connection = await amqp.connect(); const channel = await connection.createConfirmChannel(); for (let i = 0; i < 20; i++) { channel.publish('amq.topic', 'whatever', Buffer.from('blah')); } await channel.waitForConfirms(); console.log('All messages done'); await channel.close(); } catch (err) { console.warn(err); } finally { if (connection) await connection.close(); } })(); amqp-node-amqplib-263c8a3/index.d.ts000066400000000000000000000151741520012327700172610ustar00rootroot00000000000000import events = require('events'); import { ChannelOptions, ConsumeMessage, GetMessage, Message, Options, Replies, ServerProperties, SocketOptions, } from './lib/properties'; export * from './lib/properties'; export interface Connection { readonly serverProperties: ServerProperties; } export interface ChannelModel extends events.EventEmitter { close(): Promise; createChannel(options?: ChannelOptions): Promise; createConfirmChannel(options?: ChannelOptions): Promise; readonly connection: Connection; updateSecret(newSecret: Buffer, reason: string): Promise; on(event: 'close', listener: (err?: Error) => void): this; on(event: 'error', listener: (err: Error) => void): this; on(event: 'blocked', listener: (reason: string) => void): this; on(event: 'unblocked', listener: () => void): this; on(event: 'update-secret-ok', listener: () => void): this; on(event: 'handler-error', listener: (err: Error, eventName: string) => void): this; on(event: string, listener: (...args: any[]) => void): this; } export interface Channel extends events.EventEmitter { readonly connection: Connection; on(event: 'close', listener: () => void): this; on(event: 'error', listener: (err: Error) => void): this; on(event: 'drain', listener: () => void): this; on(event: 'ack', listener: (fields: { deliveryTag: number; multiple: boolean }) => void): this; on(event: 'nack', listener: (fields: { deliveryTag: number; multiple: boolean; requeue: boolean }) => void): this; on(event: 'cancel', listener: (fields: { consumerTag: string }) => void): this; on(event: 'delivery', listener: (message: ConsumeMessage) => void): this; on(event: 'return', listener: (message: Message) => void): this; on(event: 'handler-error', listener: (err: Error, eventName: string) => void): this; on(event: string, listener: (...args: any[]) => void): this; close(): Promise; assertQueue(queue?: string, options?: Options.AssertQueue): Promise; checkQueue(queue: string): Promise; deleteQueue(queue: string, options?: Options.DeleteQueue): Promise; purgeQueue(queue: string): Promise; bindQueue(queue: string, source: string, pattern: string, args?: any): Promise; unbindQueue(queue: string, source: string, pattern: string, args?: any): Promise; assertExchange( exchange: string, type: 'direct' | 'topic' | 'headers' | 'fanout' | 'match' | string, options?: Options.AssertExchange, ): Promise; checkExchange(exchange: string): Promise; deleteExchange(exchange: string, options?: Options.DeleteExchange): Promise; bindExchange(destination: string, source: string, pattern: string, args?: any): Promise; unbindExchange(destination: string, source: string, pattern: string, args?: any): Promise; publish(exchange: string, routingKey: string, content: Buffer, options?: Options.Publish): boolean; sendToQueue(queue: string, content: Buffer, options?: Options.Publish): boolean; consume( queue: string, onMessage: (msg: ConsumeMessage | null) => void, options?: Options.Consume, ): Promise; cancel(consumerTag: string): Promise; get(queue: string, options?: Options.Get): Promise; ack(message: Message, allUpTo?: boolean): void; ackAll(): void; nack(message: Message, allUpTo?: boolean, requeue?: boolean): void; nackAll(requeue?: boolean): void; reject(message: Message, requeue?: boolean): void; prefetch(count: number, global?: boolean): Promise; recover(): Promise; } export interface ConfirmChannel extends Channel { publish( exchange: string, routingKey: string, content: Buffer, options?: Options.Publish, callback?: (err: any, ok: Replies.Empty) => void, ): boolean; sendToQueue( queue: string, content: Buffer, options?: Options.Publish, callback?: (err: any, ok: Replies.Empty) => void, ): boolean; waitForConfirms(): Promise; } export declare class IllegalOperationError extends Error { name: 'IllegalOperationError'; stackAtStateChange: string | undefined; constructor(msg: string, stack?: string); } export declare const credentials: { plain(username: string, password: string): { mechanism: string; response(): Buffer; username: string; password: string; }; amqplain(username: string, password: string): { mechanism: string; response(): Buffer; username: string; password: string; }; external(): { mechanism: string; response(): Buffer; }; }; export interface RecoveryOptions { /** Initial reconnect delay in milliseconds. Default: 100 */ initialDelay?: number; /** Maximum reconnect delay in milliseconds. Default: 30000 */ maxDelay?: number; /** Backoff multiplier applied to delay on each attempt. Default: 2 */ factor?: number; /** Jitter factor (0–1) applied to delay to avoid thundering herd. Default: 0.2 */ jitter?: number; /** Maximum number of reconnect attempts. Default: Infinity */ maxRetries?: number; /** Optional setup function called after each successful connection */ setup?: ((model: ChannelModel) => Promise) | ((model: ChannelModel, done: (err?: Error) => void) => void); } export interface RecoveringChannelModel extends events.EventEmitter { close(): Promise; createChannel(options?: ChannelOptions): Promise; createConfirmChannel(options?: ChannelOptions): Promise; updateSecret(newSecret: Buffer, reason: string): Promise; on(event: 'connect', listener: (model: ChannelModel) => void): this; on(event: 'disconnect', listener: (err: Error) => void): this; on(event: 'connect-failed', listener: (err: Error) => void): this; on(event: 'reconnect-scheduled', listener: (info: { attempt: number; delay: number; error: Error }) => void): this; on(event: 'reconnect-failed', listener: (err: Error) => void): this; on(event: 'blocked', listener: (reason: string) => void): this; on(event: 'unblocked', listener: () => void): this; on(event: 'error', listener: (err: Error) => void): this; on(event: 'update-secret-ok', listener: () => void): this; on(event: string, listener: (...args: any[]) => void): this; } export declare function connect(url: string | Options.Connect, socketOptions: SocketOptions & { recovery: RecoveryOptions | true }): Promise; export declare function connect(url: string | Options.Connect, socketOptions?: SocketOptions): Promise; amqp-node-amqplib-263c8a3/lefthook.yml000066400000000000000000000006021520012327700177040ustar00rootroot00000000000000pre-commit: commands: format: glob: "**/*.js" exclude: - "test/*.js" run: npx biome format {staged_files} --write stage_fixed: true lint: glob: "**/*.js" run: npx biome lint {staged_files} --write stage_fixed: true typecheck: glob: "{**/*.d.ts,types/*.ts}" run: npm run typecheck test: run: npm test amqp-node-amqplib-263c8a3/lib/000077500000000000000000000000001520012327700161165ustar00rootroot00000000000000amqp-node-amqplib-263c8a3/lib/api_args.js000066400000000000000000000205501520012327700202430ustar00rootroot00000000000000/* The channel (promise) and callback APIs have similar signatures, and in particular, both need AMQP fields prepared from the same arguments and options. The arguments marshalling is done here. Each of the procedures below takes arguments and options (the latter in an object) particular to the operation it represents, and returns an object with fields for handing to the encoder. */ // A number of AMQP methods have a table-typed field called // `arguments`, that is intended to carry extension-specific // values. RabbitMQ uses this in a number of places; e.g., to specify // an 'alternate exchange'. // // Many of the methods in this API have an `options` argument, from // which I take both values that have a default in AMQP (e.g., // autoDelete in QueueDeclare) *and* values that are specific to // RabbitMQ (e.g., 'alternate-exchange'), which would normally be // supplied in `arguments`. So that extensions I don't support yet can // be used, I include `arguments` itself among the options. // // The upshot of this is that I often need to prepare an `arguments` // value that has any values passed in `options.arguments` as well as // any I've promoted to being options themselves. Since I don't want // to mutate anything passed in, the general pattern is to create a // fresh object with the `arguments` value given as its prototype; all // fields in the supplied value will be serialised, as well as any I // set on the fresh object. What I don't want to do, however, is set a // field to undefined by copying possibly missing field values, // because that will mask a value in the prototype. // // NB the `arguments` field already has a default value of `{}`, so // there's no need to explicitly default it unless I'm setting values. function setIfDefined(obj, prop, value) { if (value !== undefined) obj[prop] = value; } const EMPTY_OPTIONS = Object.freeze({}); const Args = {}; Args.assertQueue = (queue, options) => { queue = queue || ''; options = options || EMPTY_OPTIONS; const argt = Object.create(options.arguments || null); setIfDefined(argt, 'x-expires', options.expires); setIfDefined(argt, 'x-message-ttl', options.messageTtl); setIfDefined(argt, 'x-dead-letter-exchange', options.deadLetterExchange); setIfDefined(argt, 'x-dead-letter-routing-key', options.deadLetterRoutingKey); setIfDefined(argt, 'x-max-length', options.maxLength); setIfDefined(argt, 'x-max-priority', options.maxPriority); setIfDefined(argt, 'x-overflow', options.overflow); setIfDefined(argt, 'x-queue-mode', options.queueMode); return { queue: queue, exclusive: !!options.exclusive, durable: options.durable === undefined ? true : options.durable, autoDelete: !!options.autoDelete, arguments: argt, passive: false, // deprecated but we have to include it ticket: 0, nowait: false, }; }; Args.checkQueue = (queue) => ({ queue: queue, passive: true, // switch to "completely different" mode nowait: false, durable: true, autoDelete: false, exclusive: false, // ignored ticket: 0, }); Args.deleteQueue = (queue, options) => { options = options || EMPTY_OPTIONS; return { queue: queue, ifUnused: !!options.ifUnused, ifEmpty: !!options.ifEmpty, ticket: 0, nowait: false, }; }; Args.purgeQueue = (queue) => ({ queue: queue, ticket: 0, nowait: false, }); Args.bindQueue = (queue, source, pattern, argt) => ({ queue: queue, exchange: source, routingKey: pattern, arguments: argt, ticket: 0, nowait: false, }); Args.unbindQueue = (queue, source, pattern, argt) => ({ queue: queue, exchange: source, routingKey: pattern, arguments: argt, ticket: 0, nowait: false, }); Args.assertExchange = (exchange, type, options) => { options = options || EMPTY_OPTIONS; const argt = Object.create(options.arguments || null); setIfDefined(argt, 'alternate-exchange', options.alternateExchange); return { exchange: exchange, ticket: 0, type: type, passive: false, durable: options.durable === undefined ? true : options.durable, autoDelete: !!options.autoDelete, internal: !!options.internal, nowait: false, arguments: argt, }; }; Args.checkExchange = (exchange) => ({ exchange: exchange, passive: true, // switch to 'may as well be another method' mode nowait: false, // ff are ignored durable: true, internal: false, type: '', autoDelete: false, ticket: 0, }); Args.deleteExchange = (exchange, options) => { options = options || EMPTY_OPTIONS; return { exchange: exchange, ifUnused: !!options.ifUnused, ticket: 0, nowait: false, }; }; Args.bindExchange = (dest, source, pattern, argt) => ({ source: source, destination: dest, routingKey: pattern, arguments: argt, ticket: 0, nowait: false, }); Args.unbindExchange = (dest, source, pattern, argt) => ({ source: source, destination: dest, routingKey: pattern, arguments: argt, ticket: 0, nowait: false, }); // It's convenient to construct the properties and the method fields // at the same time, since in the APIs, values for both can appear in // `options`. Since the property or mthod field names don't overlap, I // just return one big object that can be used for both purposes, and // the encoder will pick out what it wants. Args.publish = (exchange, routingKey, options) => { options = options || EMPTY_OPTIONS; // The CC and BCC fields expect an array of "longstr", which would // normally be buffer values in JavaScript; however, since a field // array (or table) cannot have shortstr values, the codec will // encode all strings as longstrs anyway. function convertCC(cc) { if (cc === undefined) { return undefined; } else if (Array.isArray(cc)) { return cc.map(String); } else return [String(cc)]; } const headers = Object.create(options.headers || null); setIfDefined(headers, 'CC', convertCC(options.CC)); setIfDefined(headers, 'BCC', convertCC(options.BCC)); let deliveryMode; // undefined will default to 1 (non-persistent) // Previously I overloaded deliveryMode be a boolean meaning // 'persistent or not'; better is to name this option for what it // is, but I need to have backwards compatibility for applications // that either supply a numeric or boolean value. if (options.persistent !== undefined) deliveryMode = options.persistent ? 2 : 1; else if (typeof options.deliveryMode === 'number') deliveryMode = options.deliveryMode; else if (options.deliveryMode) // is supplied and truthy deliveryMode = 2; let expiration = options.expiration; if (expiration !== undefined) expiration = expiration.toString(); return { // method fields exchange: exchange, routingKey: routingKey, mandatory: !!options.mandatory, immediate: false, // RabbitMQ doesn't implement this any more ticket: undefined, // properties contentType: options.contentType, contentEncoding: options.contentEncoding, headers: headers, deliveryMode: deliveryMode, priority: options.priority, correlationId: options.correlationId, replyTo: options.replyTo, expiration: expiration, messageId: options.messageId, timestamp: options.timestamp, type: options.type, userId: options.userId, appId: options.appId, clusterId: undefined, }; }; Args.consume = (queue, options) => { options = options || EMPTY_OPTIONS; const argt = Object.create(options.arguments || null); setIfDefined(argt, 'x-priority', options.priority); return { ticket: 0, queue: queue, consumerTag: options.consumerTag || '', noLocal: !!options.noLocal, noAck: !!options.noAck, exclusive: !!options.exclusive, nowait: false, arguments: argt, }; }; Args.cancel = (consumerTag) => ({ consumerTag: consumerTag, nowait: false, }); Args.get = (queue, options) => { options = options || EMPTY_OPTIONS; return { ticket: 0, queue: queue, noAck: !!options.noAck, }; }; Args.ack = (tag, allUpTo) => ({ deliveryTag: tag, multiple: !!allUpTo, }); Args.nack = (tag, allUpTo, requeue) => ({ deliveryTag: tag, multiple: !!allUpTo, requeue: requeue === undefined ? true : requeue, }); Args.reject = (tag, requeue) => ({ deliveryTag: tag, requeue: requeue === undefined ? true : requeue, }); Args.prefetch = (count, global) => ({ prefetchCount: count || 0, prefetchSize: 0, global: !!global, }); Args.recover = () => ({ requeue: true }); module.exports = Object.freeze(Args); amqp-node-amqplib-263c8a3/lib/bitset.js000066400000000000000000000057231520012327700177550ustar00rootroot00000000000000/** * A bitset implementation, after that in java.util. Yes there * already exist such things, but none implement next{Clear|Set}Bit or * equivalent, and none involved me tooling about for an evening. */ class BitSet { /** * @param {number} [size] */ constructor(size) { if (size) { const numWords = Math.ceil(size / 32); this.words = new Array(numWords); } else { this.words = []; } this.wordsInUse = 0; // = number, not index } /** * @param {number} numWords */ ensureSize(numWords) { const wordsPresent = this.words.length; if (wordsPresent < numWords) { this.words = this.words.concat(new Array(numWords - wordsPresent)); } } /** * @param {number} bitIndex */ set(bitIndex) { const w = wordIndex(bitIndex); if (w >= this.wordsInUse) { this.ensureSize(w + 1); this.wordsInUse = w + 1; } const bit = 1 << bitIndex; this.words[w] |= bit; } /** * @param {number} bitIndex */ clear(bitIndex) { const w = wordIndex(bitIndex); if (w >= this.wordsInUse) return; const mask = ~(1 << bitIndex); this.words[w] &= mask; } /** * @param {number} bitIndex */ get(bitIndex) { const w = wordIndex(bitIndex); if (w >= this.wordsInUse) return false; // >= since index vs size const bit = 1 << bitIndex; return !!(this.words[w] & bit); } /** * Give the next bit that is set on or after fromIndex, or -1 if no such bit * * @param {number} fromIndex */ nextSetBit(fromIndex) { let w = wordIndex(fromIndex); if (w >= this.wordsInUse) return -1; // the right-hand side is shifted to only test the bits of the first // word that are > fromIndex let word = this.words[w] & (0xffffffff << fromIndex); while (true) { if (word) return w * 32 + trailingZeros(word); w++; if (w === this.wordsInUse) return -1; word = this.words[w]; } } /** * @param {number} fromIndex */ nextClearBit(fromIndex) { let w = wordIndex(fromIndex); if (w >= this.wordsInUse) return fromIndex; let word = ~this.words[w] & (0xffffffff << fromIndex); while (true) { if (word) return w * 32 + trailingZeros(word); w++; if (w === this.wordsInUse) return w * 32; word = ~this.words[w]; } } } /** * @param {number} bitIndex */ function wordIndex(bitIndex) { return Math.floor(bitIndex / 32); } /** * @param {number} i */ function trailingZeros(i) { // From Hacker's Delight, via JDK. Probably far less effective here, // since bit ops are not necessarily the quick way to do things in // JS. if (i === 0) return 32; let y; let n = 31; y = i << 16; if (y !== 0) { n = n - 16; i = y; } y = i << 8; if (y !== 0) { n = n - 8; i = y; } y = i << 4; if (y !== 0) { n = n - 4; i = y; } y = i << 2; if (y !== 0) { n = n - 2; i = y; } return n - ((i << 1) >>> 31); } module.exports.BitSet = BitSet; amqp-node-amqplib-263c8a3/lib/callback_model.js000066400000000000000000000203601520012327700213710ustar00rootroot00000000000000const defs = require('./defs'); const EventEmitter = require('node:events'); const Args = require('./api_args'); const { BaseChannel, acceptMessage, convertCloseFrameToError } = require('./channel'); class CallbackModel extends EventEmitter { constructor(connection) { super(); this.connection = connection; ['error', 'close', 'blocked', 'unblocked', 'update-secret-ok'].forEach((ev) => { connection.on(ev, this.emit.bind(this, ev)); }); } close(cb) { this.connection.close(cb); } updateSecret(newSecret, reason, cb) { this.connection._updateSecret(newSecret, reason, cb); } createChannel(options, cb) { if (cb === undefined) { cb = options; options = undefined; } const ch = new Channel(this.connection); ch.setOptions(options); ch.open((err, _ok) => { if (err === null) cb && cb(null, ch); else cb && cb(err); }); return ch; } createConfirmChannel(options, cb) { if (cb === undefined) { cb = options; options = undefined; } const ch = new ConfirmChannel(this.connection); ch.setOptions(options); ch.open((err) => { if (err !== null) return cb && cb(err); else { ch.rpc(defs.ConfirmSelect, { nowait: false }, defs.ConfirmSelectOk, (err, _ok) => { if (err !== null) return cb && cb(err); else cb && cb(null, ch); }); } }); return ch; } } class Channel extends BaseChannel { constructor(connection) { super(connection); this.on('delivery', this.handleDelivery.bind(this)); this.on('cancel', this.handleCancel.bind(this)); } // This encodes straight-forward RPC: no side-effects and return the // fields from the server response. It wraps the callback given it, so // the calling method argument can be passed as-is. For anything that // needs to have side-effects, or needs to change the server response, // use `#_rpc(...)` and remember to dereference `.fields` of the // server response. rpc(method, fields, expect, cb0) { const cb = callbackWrapper(this, cb0); this._rpc(method, fields, expect, (err, ok) => { cb(err, ok && ok.fields); // in case of an error, ok will be // undefined }); return this; } // === Public API === open(cb) { try { this.allocate(); } catch (e) { return cb(e); } return this.rpc(defs.ChannelOpen, { outOfBand: '' }, defs.ChannelOpenOk, cb); } close(cb) { return this.closeBecause('Goodbye', defs.constants.REPLY_SUCCESS, () => { cb && cb(null); }); } assertQueue(queue, options, cb) { return this.rpc(defs.QueueDeclare, Args.assertQueue(queue, options), defs.QueueDeclareOk, cb); } checkQueue(queue, cb) { return this.rpc(defs.QueueDeclare, Args.checkQueue(queue), defs.QueueDeclareOk, cb); } deleteQueue(queue, options, cb) { return this.rpc(defs.QueueDelete, Args.deleteQueue(queue, options), defs.QueueDeleteOk, cb); } purgeQueue(queue, cb) { return this.rpc(defs.QueuePurge, Args.purgeQueue(queue), defs.QueuePurgeOk, cb); } bindQueue(queue, source, pattern, argt, cb) { return this.rpc(defs.QueueBind, Args.bindQueue(queue, source, pattern, argt), defs.QueueBindOk, cb); } unbindQueue(queue, source, pattern, argt, cb) { return this.rpc(defs.QueueUnbind, Args.unbindQueue(queue, source, pattern, argt), defs.QueueUnbindOk, cb); } assertExchange(ex, type, options, cb0) { const cb = callbackWrapper(this, cb0); this._rpc(defs.ExchangeDeclare, Args.assertExchange(ex, type, options), defs.ExchangeDeclareOk, (e, _) => { cb(e, { exchange: ex }); }); return this; } checkExchange(exchange, cb) { return this.rpc(defs.ExchangeDeclare, Args.checkExchange(exchange), defs.ExchangeDeclareOk, cb); } deleteExchange(exchange, options, cb) { return this.rpc(defs.ExchangeDelete, Args.deleteExchange(exchange, options), defs.ExchangeDeleteOk, cb); } bindExchange(dest, source, pattern, argt, cb) { return this.rpc(defs.ExchangeBind, Args.bindExchange(dest, source, pattern, argt), defs.ExchangeBindOk, cb); } unbindExchange(dest, source, pattern, argt, cb) { return this.rpc(defs.ExchangeUnbind, Args.unbindExchange(dest, source, pattern, argt), defs.ExchangeUnbindOk, cb); } publish(exchange, routingKey, content, options) { const fieldsAndProps = Args.publish(exchange, routingKey, options); return this.sendMessage(fieldsAndProps, fieldsAndProps, content); } sendToQueue(queue, content, options) { return this.publish('', queue, content, options); } consume(queue, callback, options, cb0) { const cb = callbackWrapper(this, cb0); const fields = Args.consume(queue, options); this._rpc(defs.BasicConsume, fields, defs.BasicConsumeOk, (err, ok) => { if (err === null) { this.registerConsumer(ok.fields.consumerTag, callback); cb(null, ok.fields); } else cb(err); }); return this; } cancel(consumerTag, cb0) { const cb = callbackWrapper(this, cb0); this._rpc(defs.BasicCancel, Args.cancel(consumerTag), defs.BasicCancelOk, (err, ok) => { if (err === null) { this.unregisterConsumer(consumerTag); cb(null, ok.fields); } else cb(err); }); return this; } get(queue, options, cb0) { const fields = Args.get(queue, options); const cb = callbackWrapper(this, cb0); this.sendOrEnqueue(defs.BasicGet, fields, (err, f) => { if (err instanceof Error) return cb(err); if (err) return cb(convertCloseFrameToError(defs.BasicGet, err)); if (f.id === defs.BasicGetEmpty) { cb(null, false); } else if (f.id === defs.BasicGetOk) { this.handleMessage = acceptMessage((m) => { m.fields = f.fields; cb(null, m); }); } else { cb(new Error(`Unexpected response to BasicGet: ${inspect(f)}`)); } }); return this; } ack(message, allUpTo) { this.sendImmediately(defs.BasicAck, Args.ack(message.fields.deliveryTag, allUpTo)); return this; } ackAll() { this.sendImmediately(defs.BasicAck, Args.ack(0, true)); return this; } nack(message, allUpTo, requeue) { this.sendImmediately(defs.BasicNack, Args.nack(message.fields.deliveryTag, allUpTo, requeue)); return this; } nackAll(requeue) { this.sendImmediately(defs.BasicNack, Args.nack(0, true, requeue)); return this; } reject(message, requeue) { this.sendImmediately(defs.BasicReject, Args.reject(message.fields.deliveryTag, requeue)); return this; } prefetch(count, global, cb) { return this.rpc(defs.BasicQos, Args.prefetch(count, global), defs.BasicQosOk, cb); } recover(cb) { return this.rpc(defs.BasicRecover, Args.recover(), defs.BasicRecoverOk, cb); } } // Wrap an RPC callback to make sure the callback is invoked with // either `(null, value)` or `(error)`, i.e., never two non-null // values. Also substitutes a stub if the callback is `undefined` or // otherwise falsey, for convenience in methods for which the callback // is optional (that is, most of them). function callbackWrapper(_ch, cb) { return cb ? (err, ok) => { if (err === null) { cb(null, ok); } else cb(err); } : () => {}; } class ConfirmChannel extends Channel { publish(exchange, routingKey, content, options, cb) { const result = Channel.prototype.publish.call(this, exchange, routingKey, content, options); this.pushConfirmCallback(cb); return result; } sendToQueue(queue, content, options, cb) { return this.publish('', queue, content, options, cb); } waitForConfirms(k) { const awaiting = []; const unconfirmed = this.unconfirmed; unconfirmed.forEach((val, index) => { if (val === null); // already confirmed else { const confirmed = new Promise((resolve, reject) => { unconfirmed[index] = (err) => { if (val) val(err); if (err === null) resolve(); else reject(err); }; }); awaiting.push(confirmed); } }); return Promise.all(awaiting).then( () => { k(); }, (err) => { k(err); }, ); } } module.exports.CallbackModel = CallbackModel; module.exports.Channel = Channel; module.exports.ConfirmChannel = ConfirmChannel; amqp-node-amqplib-263c8a3/lib/channel.js000066400000000000000000000357521520012327700201000ustar00rootroot00000000000000const defs = require('./defs'); const closeMsg = require('./format').closeMessage; const inspect = require('./format').inspect; const methodName = require('./format').methodName; const assert = require('node:assert'); const EventEmitter = require('node:events'); const fmt = require('node:util').format; const IllegalOperationError = require('./error').IllegalOperationError; const stackCapture = require('./error').stackCapture; const safeEmit = require('./safe_emit'); class Channel extends EventEmitter { constructor(connection) { super(); this.connection = connection; // for the presently outstanding RPC this.reply = null; // for the RPCs awaiting action this.pending = []; // for unconfirmed messages this.lwm = 1; // the least, unconfirmed deliveryTag this.unconfirmed = []; // rolling window of delivery callbacks this.on( 'ack', this.handleConfirm.bind(this, (cb) => { if (cb) cb(null); }), ); this.on( 'nack', this.handleConfirm.bind(this, (cb) => { if (cb) cb(new Error('message nacked')); }), ); this.on('close', function () { const err = new Error('channel closed'); for (let i = 0; i < this.unconfirmed.length; i++) { const cb = this.unconfirmed[i]; if (cb) cb(err); } this.unconfirmed = this.unconfirmed.filter((cb) => cb === false); }); // message frame state machine this.handleMessage = acceptDeliveryOrReturn; } setOptions(options) { this.options = options; } allocate() { this.ch = this.connection.freshChannel(this, this.options); return this; } // Incoming frames are either notifications of e.g., message delivery, // or replies to something we've sent. In general I deal with the // former by emitting an event, and with the latter by keeping a track // of what's expecting a reply. // // The AMQP specification implies that RPCs can't be pipelined; that // is, you can have only one outstanding RPC on a channel at a // time. Certainly that's what RabbitMQ and its clients assume. For // this reason, I buffer RPCs if the channel is already waiting for a // reply. // Just send the damn frame. sendImmediately(method, fields) { return this.connection.sendMethod(this.ch, method, fields); } // Invariant: !this.reply -> pending.length == 0. That is, whenever we // clear a reply, we must send another RPC (and thereby fill // this.reply) if there is one waiting. The invariant relevant here // and in `accept`. sendOrEnqueue(method, fields, reply) { if (!this.reply) { // if no reply waiting, we can go assert(this.pending.length === 0); this.reply = reply; this.sendImmediately(method, fields); } else { this.pending.push({ method: method, fields: fields, reply: reply, }); } } sendMessage(fields, properties, content) { return this.connection.sendMessage(this.ch, defs.BasicPublish, fields, defs.BasicProperties, properties, content); } // Internal, synchronously resolved RPC; the return value is resolved // with the whole frame. _rpc(method, fields, expect, cb) { const self = this; function reply(err, f) { if (err === null) { if (f.id === expect) { return cb(null, f); } else { // We have detected a problem, so it's up to us to close the // channel const expectedName = methodName(expect); const e = new Error(fmt('Expected %s; got %s', expectedName, inspect(f, false))); self.closeWithError(f.id, fmt('Expected %s; got %s', expectedName, methodName(f.id)), defs.constants.UNEXPECTED_FRAME, e); return cb(e); } } // An error will be given if, for example, this is waiting to be // sent and the connection closes else if (err instanceof Error) return cb(err); // A close frame will be given if this is the RPC awaiting reply // and the channel is closed by the server else return cb(convertCloseFrameToError(method, err)); } this.sendOrEnqueue(method, fields, reply); } // Move to entirely closed state. toClosed(capturedStack) { this._rejectPending(); invalidateSend(this, 'Channel closed', capturedStack); this.accept = invalidOp('Channel closed', capturedStack); this.connection.releaseChannel(this.ch); safeEmit(this, 'close'); } // Stop being able to send and receive methods and content. Used when // we close the channel. Invokes the continuation once the server has // acknowledged the close, but before the channel is moved to the // closed state. toClosing(capturedStack, k) { const send = this.sendImmediately.bind(this); invalidateSend(this, 'Channel closing', capturedStack); this.accept = function (f) { if (f.id === defs.ChannelCloseOk) { if (k) k(); const s = stackCapture('ChannelCloseOk frame received'); this.toClosed(s); } else if (f.id === defs.ChannelClose) { send(defs.ChannelCloseOk, {}); } // else ignore frame }; } _rejectPending() { function reject(r) { r(new Error('Channel ended, no reply will be forthcoming')); } if (this.reply !== null) reject(this.reply); this.reply = null; let discard = this.pending.shift(); while (discard) { if (discard) reject(discard.reply); discard = this.pending.shift(); } this.pending = null; // so pushes will break } closeBecause(reason, code, k) { this.sendImmediately(defs.ChannelClose, { replyText: reason, replyCode: code, methodId: 0, classId: 0, }); const s = stackCapture(`closeBecause called: ${reason}`); this.toClosing(s, k); } // If we close because there's been an error, we need to distinguish // between what we tell the server (`reason`) and what we report as // the cause in the client (`error`). closeWithError(id, reason, code, error) { this.closeBecause(reason, code, () => { error.code = code; // content frames and consumer errors do not provide a method a class/method ID if (id) { error.classId = defs.info(id).classId; error.methodId = defs.info(id).methodId; } safeEmit(this, 'error', error); }); } // A trampolining state machine for message frames on a channel. A // message arrives in at least two frames: first, a method announcing // the message (either a BasicDeliver or BasicGetOk); then, a message // header with the message properties; then, zero or more content // frames. // Keep the try/catch localised, in an attempt to avoid disabling // optimisation acceptMessageFrame(f) { try { this.handleMessage = this.handleMessage(f); } catch (msg) { if (typeof msg === 'string') { this.closeWithError(f.id, msg, defs.constants.UNEXPECTED_FRAME, new Error(msg)); } else if (msg instanceof Error) { this.closeWithError(f.id, 'Error while processing message', defs.constants.INTERNAL_ERROR, msg); } else { this.closeWithError(f.id, 'Internal error while processing message', defs.constants.INTERNAL_ERROR, new Error(msg.toString())); } } } handleConfirm(handle, f) { const tag = f.deliveryTag; const multi = f.multiple; if (multi) { const confirmed = this.unconfirmed.splice(0, tag - this.lwm + 1); this.lwm = tag + 1; confirmed.forEach(handle); } else { let c; if (tag === this.lwm) { c = this.unconfirmed.shift(); this.lwm++; // Advance the LWM and the window to the next non-gap, or // possibly to the end while (this.unconfirmed[0] === null) { this.unconfirmed.shift(); this.lwm++; } } else { c = this.unconfirmed[tag - this.lwm]; this.unconfirmed[tag - this.lwm] = null; } // Technically, in the single-deliveryTag case, I should report a // protocol breach if it's already been confirmed. handle(c); } } pushConfirmCallback(cb) { // `null` is used specifically for marking already confirmed slots, // so I coerce `undefined` and `null` to false; functions are never // falsey. this.unconfirmed.push(cb || false); } onBufferDrain() { safeEmit(this, 'drain'); } accept(f) { switch (f.id) { // Message frames case undefined: // content frame! case defs.BasicDeliver: case defs.BasicReturn: case defs.BasicProperties: return this.acceptMessageFrame(f); // confirmations, need to do confirm.select first case defs.BasicAck: return safeEmit(this, 'ack', f.fields); case defs.BasicNack: return safeEmit(this, 'nack', f.fields); case defs.BasicCancel: // The broker can send this if e.g., the queue is deleted. return safeEmit(this, 'cancel', f.fields); case defs.ChannelClose: { // Any remote closure is an error to us. Reject the pending reply // with the close frame, so it can see whether it was that // operation that caused it to close. if (this.reply) { const reply = this.reply; this.reply = null; reply(f); } const emsg = `Channel closed by server: ${closeMsg(f)}`; this.sendImmediately(defs.ChannelCloseOk, {}); const error = new Error(emsg); error.code = f.fields.replyCode; error.classId = f.fields.classId; error.methodId = f.fields.methodId; safeEmit(this, 'error', error); const s = stackCapture(emsg); this.toClosed(s); return; } case defs.BasicFlow: // RabbitMQ doesn't send this, it just blocks the TCP socket return this.closeWithError(f.id, 'Flow not implemented', defs.constants.NOT_IMPLEMENTED, new Error('Flow not implemented')); default: { // assume all other things are replies // Resolving the reply may lead to another RPC; to make sure we // don't hold that up, clear this.reply const reply = this.reply; this.reply = null; // however, maybe there's an RPC waiting to go? If so, that'll // fill this.reply again, restoring the invariant. This does rely // on any response being recv'ed after resolving the promise, // below; hence, I use synchronous defer. if (this.pending.length > 0) { const send = this.pending.shift(); this.reply = send.reply; this.sendImmediately(send.method, send.fields); } return reply(null, f); } } } } // Shutdown protocol. There's three scenarios: // // 1. The application decides to shut the channel // 2. The server decides to shut the channel, possibly because of // something the application did // 3. The connection is closing, so there won't be any more frames // going back and forth. // // 1 and 2 involve an exchange of method frames (Close and CloseOk), // while 3 doesn't; the connection simply says "shutdown" to the // channel, which then acts as if it's closing, without going through // the exchange. function invalidOp(msg, stack) { return () => { throw new IllegalOperationError(msg, stack); }; } function invalidateSend(ch, msg, stack) { ch.sendImmediately = ch.sendOrEnqueue = ch.sendMessage = invalidOp(msg, stack); } // Kick off a message delivery given a BasicDeliver or BasicReturn // frame (BasicGet uses the RPC mechanism) function acceptDeliveryOrReturn(f) { let event; if (f.id === defs.BasicDeliver) event = 'delivery'; else if (f.id === defs.BasicReturn) event = 'return'; else throw fmt('Expected BasicDeliver or BasicReturn; got %s', inspect(f)); const fields = f.fields; return acceptMessage((message) => { message.fields = fields; safeEmit(this, event, message); }); } // Move to the state of waiting for message frames (headers, then // one or more content frames) function acceptMessage(continuation) { let totalSize = 0; let remaining = 0; let buffers = null; const message = { fields: null, properties: null, content: null, }; return headers; // expect a headers frame function headers(f) { if (f.id === defs.BasicProperties) { message.properties = f.fields; totalSize = remaining = f.size; // for zero-length messages, content frames aren't required. if (totalSize === 0) { message.content = Buffer.alloc(0); continuation(message); return acceptDeliveryOrReturn; } else { return content; } } else { throw 'Expected headers frame after delivery'; } } // expect a content frame // %%% TODO cancelled messages (sent as zero-length content frame) function content(f) { if (f.content) { const size = f.content.length; remaining -= size; if (remaining === 0) { if (buffers !== null) { buffers.push(f.content); message.content = Buffer.concat(buffers); } else { message.content = f.content; } continuation(message); return acceptDeliveryOrReturn; } else if (remaining < 0) { throw fmt('Too much content sent! Expected %d bytes', totalSize); } else { if (buffers !== null) buffers.push(f.content); else buffers = [f.content]; return content; } } else throw 'Expected content frame after headers'; } } function convertCloseFrameToError(method, closeFrame) { const closeReason = (closeFrame.fields.classId << 16) + closeFrame.fields.methodId; const e = method === closeReason ? fmt('Operation failed: %s; %s', methodName(method), closeMsg(closeFrame)) : fmt('Channel closed by server: %s', closeMsg(closeFrame)); const error = new Error(e); error.code = closeFrame.fields.replyCode; error.classId = closeFrame.fields.classId; error.methodId = closeFrame.fields.methodId; return error; } // This adds just a bit more stuff useful for the APIs, but not // low-level machinery. class BaseChannel extends Channel { constructor(connection) { super(connection); this.consumers = new Map(); } // Not sure I like the ff, it's going to be changing hidden classes // all over the place. On the other hand, whaddya do. registerConsumer(tag, callback) { this.consumers.set(tag, callback); } unregisterConsumer(tag) { this.consumers.delete(tag); } dispatchMessage(fields, message) { const consumerTag = fields.consumerTag; const consumer = this.consumers.get(consumerTag); if (consumer) { return consumer(message); } else { // %%% Surely a race here throw new Error(`Unknown consumer: ${consumerTag}`); } } handleDelivery(message) { return this.dispatchMessage(message.fields, message); } handleCancel(fields) { const result = this.dispatchMessage(fields, null); this.unregisterConsumer(fields.consumerTag); return result; } } module.exports.acceptMessage = acceptMessage; module.exports.BaseChannel = BaseChannel; module.exports.Channel = Channel; module.exports.convertCloseFrameToError = convertCloseFrameToError; amqp-node-amqplib-263c8a3/lib/channel_model.js000066400000000000000000000205631520012327700212520ustar00rootroot00000000000000const EventEmitter = require('node:events'); const { promisify } = require('node:util'); const defs = require('./defs'); const Args = require('./api_args'); const { BaseChannel, acceptMessage, convertCloseFrameToError } = require('./channel'); const { inspect } = require('./format'); class ChannelModel extends EventEmitter { constructor(connection) { super(); this.connection = connection; ['error', 'close', 'blocked', 'unblocked', 'update-secret-ok'].forEach((ev) => { connection.on(ev, this.emit.bind(this, ev)); }); } close() { return promisify(this.connection.close.bind(this.connection))(); } updateSecret(newSecret, reason) { return promisify(this.connection._updateSecret.bind(this.connection))(newSecret, reason); } async createChannel(options) { const channel = new Channel(this.connection); channel.setOptions(options); await channel.open(); return channel; } async createConfirmChannel(options) { const channel = new ConfirmChannel(this.connection); channel.setOptions(options); await channel.open(); await channel.rpc(defs.ConfirmSelect, { nowait: false }, defs.ConfirmSelectOk); return channel; } } // Channels class Channel extends BaseChannel { constructor(connection) { super(connection); this.on('delivery', this.handleDelivery.bind(this)); this.on('cancel', this.handleCancel.bind(this)); } // An RPC that returns a 'proper' promise, which resolves to just the // response's fields; this is intended to be suitable for implementing // API procedures. async rpc(method, fields, expect) { const f = await promisify((cb) => { return this._rpc(method, fields, expect, cb); })(); return f.fields; } // Do the remarkably simple channel open handshake async open() { const ch = await this.allocate.bind(this)(); return ch.rpc(defs.ChannelOpen, { outOfBand: '' }, defs.ChannelOpenOk); } close() { return promisify((cb) => { return this.closeBecause('Goodbye', defs.constants.REPLY_SUCCESS, cb); })(); } // === Public API, declaring queues and stuff === assertQueue(queue, options) { return this.rpc(defs.QueueDeclare, Args.assertQueue(queue, options), defs.QueueDeclareOk); } checkQueue(queue) { return this.rpc(defs.QueueDeclare, Args.checkQueue(queue), defs.QueueDeclareOk); } deleteQueue(queue, options) { return this.rpc(defs.QueueDelete, Args.deleteQueue(queue, options), defs.QueueDeleteOk); } purgeQueue(queue) { return this.rpc(defs.QueuePurge, Args.purgeQueue(queue), defs.QueuePurgeOk); } bindQueue(queue, source, pattern, argt) { return this.rpc(defs.QueueBind, Args.bindQueue(queue, source, pattern, argt), defs.QueueBindOk); } unbindQueue(queue, source, pattern, argt) { return this.rpc(defs.QueueUnbind, Args.unbindQueue(queue, source, pattern, argt), defs.QueueUnbindOk); } assertExchange(exchange, type, options) { // The server reply is an empty set of fields, but it's convenient // to have the exchange name handed to the continuation. return this.rpc(defs.ExchangeDeclare, Args.assertExchange(exchange, type, options), defs.ExchangeDeclareOk).then((_ok) => { return { exchange }; }); } checkExchange(exchange) { return this.rpc(defs.ExchangeDeclare, Args.checkExchange(exchange), defs.ExchangeDeclareOk); } deleteExchange(name, options) { return this.rpc(defs.ExchangeDelete, Args.deleteExchange(name, options), defs.ExchangeDeleteOk); } bindExchange(dest, source, pattern, argt) { return this.rpc(defs.ExchangeBind, Args.bindExchange(dest, source, pattern, argt), defs.ExchangeBindOk); } unbindExchange(dest, source, pattern, argt) { return this.rpc(defs.ExchangeUnbind, Args.unbindExchange(dest, source, pattern, argt), defs.ExchangeUnbindOk); } // Working with messages publish(exchange, routingKey, content, options) { const fieldsAndProps = Args.publish(exchange, routingKey, options); return this.sendMessage(fieldsAndProps, fieldsAndProps, content); } sendToQueue(queue, content, options) { return this.publish('', queue, content, options); } consume(queue, callback, options) { // NB we want the callback to be run synchronously, so that we've // registered the consumerTag before any messages can arrive. const fields = Args.consume(queue, options); return new Promise((resolve, reject) => { this._rpc(defs.BasicConsume, fields, defs.BasicConsumeOk, (err, ok) => { if (err) return reject(err); this.registerConsumer(ok.fields.consumerTag, callback); resolve(ok.fields); }); }); } async cancel(consumerTag) { await promisify((cb) => { this._rpc(defs.BasicCancel, Args.cancel(consumerTag), defs.BasicCancelOk, cb); })().then((ok) => { this.unregisterConsumer(consumerTag); return ok.fields; }); } get(queue, options) { const fields = Args.get(queue, options); return new Promise((resolve, reject) => { this.sendOrEnqueue(defs.BasicGet, fields, (err, f) => { if (err instanceof Error) return reject(err); if (err) return reject(convertCloseFrameToError(defs.BasicGet, err)); if (f.id === defs.BasicGetEmpty) { return resolve(false); } else if (f.id === defs.BasicGetOk) { const fields = f.fields; this.handleMessage = acceptMessage((m) => { m.fields = fields; resolve(m); }); } else { reject(new Error(`Unexpected response to BasicGet: ${inspect(f)}`)); } }); }); } ack(message, allUpTo) { this.sendImmediately(defs.BasicAck, Args.ack(message.fields.deliveryTag, allUpTo)); } ackAll() { this.sendImmediately(defs.BasicAck, Args.ack(0, true)); } nack(message, allUpTo, requeue) { this.sendImmediately(defs.BasicNack, Args.nack(message.fields.deliveryTag, allUpTo, requeue)); } nackAll(requeue) { this.sendImmediately(defs.BasicNack, Args.nack(0, true, requeue)); } // `Basic.Nack` is not available in older RabbitMQ versions (or in the // AMQP specification), so you have to use the one-at-a-time // `Basic.Reject`. This is otherwise synonymous with // `#nack(message, false, requeue)`. reject(message, requeue) { this.sendImmediately(defs.BasicReject, Args.reject(message.fields.deliveryTag, requeue)); } recover() { return this.rpc(defs.BasicRecover, Args.recover(), defs.BasicRecoverOk); } qos(count, global) { return this.rpc(defs.BasicQos, Args.prefetch(count, global), defs.BasicQosOk); } } // There are more options in AMQP than exposed here; RabbitMQ only // implements prefetch based on message count, and only for individual // channels or consumers. RabbitMQ v3.3.0 and after treat prefetch // (without `global` set) as per-consumer (for consumers following), // and prefetch with `global` set as per-channel. Channel.prototype.prefetch = Channel.prototype.qos; // Confirm channel. This is a channel with confirms 'switched on', // meaning sent messages will provoke a responding 'ack' or 'nack' // from the server. The upshot of this is that `publish` and // `sendToQueue` both take a callback, which will be called either // with `null` as its argument to signify 'ack', or an exception as // its argument to signify 'nack'. class ConfirmChannel extends Channel { publish(exchange, routingKey, content, options, cb) { const result = super.publish(exchange, routingKey, content, options); this.pushConfirmCallback(cb); return result; } sendToQueue(queue, content, options, cb) { return this.publish('', queue, content, options, cb); } waitForConfirms() { const awaiting = []; const unconfirmed = this.unconfirmed; unconfirmed.forEach((cb, index) => { if (cb !== null) { const confirmed = new Promise((resolve, reject) => { unconfirmed[index] = (err) => { if (cb) cb(err); if (err === null) resolve(); else reject(err); }; }); awaiting.push(confirmed); } }); // Channel closed if (!this.pending) { const err = new Error('channel closed'); while (unconfirmed.length > 0) { const cb = unconfirmed.shift(); if (cb) cb(err); } } return Promise.all(awaiting); } } module.exports.ConfirmChannel = ConfirmChannel; module.exports.Channel = Channel; module.exports.ChannelModel = ChannelModel; amqp-node-amqplib-263c8a3/lib/codec.js000066400000000000000000000262401520012327700175350ustar00rootroot00000000000000/* The AMQP 0-9-1 is a mess when it comes to the types that can be encoded on the wire. There are four encoding schemes, and three overlapping sets of types: frames, methods, (field-)tables, and properties. Each *frame type* has a set layout in which values of given types are concatenated along with sections of "raw binary" data. In frames there are `shortstr`s, that is length-prefixed strings of UTF8 chars, 8 bit unsigned integers (called `octet`), unsigned 16 bit integers (called `short` or `short-uint`), unsigned 32 bit integers (called `long` or `long-uint`), unsigned 64 bit integers (called `longlong` or `longlong-uint`), and flags (called `bit`). Methods are encoded as a frame giving a method ID and a sequence of arguments of known types. The encoded method argument values are concatenated (with some fun complications around "packing" consecutive bit values into bytes). Along with the types given in frames, method arguments may be long byte strings (`longstr`, not required to be UTF8) or 64 bit unsigned integers to be interpreted as timestamps (yeah I don't know why either), or arbitrary sets of key-value pairs (called `field-table`). Inside a field table the keys are `shortstr` and the values are prefixed with a byte tag giving the type. The types are any of the above except for bits (which are replaced by byte-wide `bool`), along with a NULL value `void`, a special fixed-precision number encoding (`decimal`), IEEE754 `float`s and `double`s, signed integers, `field-array` (a sequence of tagged values), and nested field-tables. RabbitMQ and QPid use a subset of the field-table types, and different value tags, established before the AMQP 0-9-1 specification was published. So far as I know, no-one uses the types and tags as published. http://www.rabbitmq.com/amqp-0-9-1-errata.html gives the list of field-table types. Lastly, there are (sets of) properties, only one of which is given in AMQP 0-9-1: `BasicProperties`. These are almost the same as methods, except that they appear in content header frames, which include a content size, and they carry a set of flags indicating which properties are present. This scheme can save ones of bytes per message (messages which take a minimum of three frames each to send). */ // JavaScript uses only doubles so what I'm testing for is whether // it's *better* to encode a number as a float or double. This really // just amounts to testing whether there's a fractional part to the // number, except that see below. NB I don't use bitwise operations to // do this 'efficiently' -- it would mask the number to 32 bits. // // At 2^50, doubles don't have sufficient precision to distinguish // between floating point and integer numbers (`Math.pow(2, 50) + 0.1 // === Math.pow(2, 50)` (and, above 2^53, doubles cannot represent all // integers (`Math.pow(2, 53) + 1 === Math.pow(2, 53)`)). Hence // anything with a magnitude at or above 2^50 may as well be encoded // as a 64-bit integer. Except that only signed integers are supported // by RabbitMQ, so anything above 2^63 - 1 must be a double. function isFloatingPoint(n) { return n >= 0x8000000000000000 || (Math.abs(n) < 0x4000000000000 && Math.floor(n) !== n); } function encodeTable(buffer, val, offset) { const start = offset; offset += 4; // leave room for the table length for (const key in val) { if (val[key] !== undefined) { const len = Buffer.byteLength(key); buffer.writeUInt8(len, offset); offset++; buffer.write(key, offset, 'utf8'); offset += len; offset += encodeFieldValue(buffer, val[key], offset); } } const size = offset - start; buffer.writeUInt32BE(size - 4, start); return size; } function encodeArray(buffer, val, offset) { const start = offset; offset += 4; for (let i = 0, num = val.length; i < num; i++) { offset += encodeFieldValue(buffer, val[i], offset); } const size = offset - start; buffer.writeUInt32BE(size - 4, start); return size; } function encodeFieldValue(buffer, value, offset) { const start = offset; let type = typeof value; let val = value; // A trapdoor for specifying a type, e.g., timestamp if (value && type === 'object' && Object.hasOwn(value, '!')) { val = value.value; type = value['!']; } // If it's a JS number, we'll have to guess what type to encode it // as. if (type === 'number') { // Making assumptions about the kind of number (floating point // v integer, signed, unsigned, size) desired is dangerous in // general; however, in practice RabbitMQ uses only // longstrings and unsigned integers in its arguments, and // other clients generally conflate number types anyway. So // the only distinction we care about is floating point vs // integers, preferring integers since those can be promoted // if necessary. If floating point is required, we may as well // use double precision. if (isFloatingPoint(val)) { type = 'double'; } else { // only signed values are used in tables by // RabbitMQ. It *used* to (< v3.3.0) treat the byte 'b' // type as unsigned, but most clients (and the spec) // think it's signed, and now RabbitMQ does too. if (val < 128 && val >= -128) { type = 'byte'; } else if (val >= -0x8000 && val < 0x8000) { type = 'short'; } else if (val >= -0x80000000 && val < 0x80000000) { type = 'int'; } else { type = 'long'; } } } function tag(t) { buffer.write(t, offset); offset++; } switch (type) { case 'string': { // no shortstr in field tables const len = Buffer.byteLength(val, 'utf8'); tag('S'); buffer.writeUInt32BE(len, offset); offset += 4; buffer.write(val, offset, 'utf8'); offset += len; break; } case 'object': if (val === null) { tag('V'); } else if (Array.isArray(val)) { tag('A'); offset += encodeArray(buffer, val, offset); } else if (Buffer.isBuffer(val)) { tag('x'); buffer.writeUInt32BE(val.length, offset); offset += 4; val.copy(buffer, offset); offset += val.length; } else { tag('F'); offset += encodeTable(buffer, val, offset); } break; case 'boolean': tag('t'); buffer.writeUInt8(val ? 1 : 0, offset); offset++; break; // These are the types that are either guessed above, or // explicitly given using the {'!': type} notation. case 'double': case 'float64': tag('d'); buffer.writeDoubleBE(val, offset); offset += 8; break; case 'byte': case 'int8': tag('b'); buffer.writeInt8(val, offset); offset++; break; case 'unsignedbyte': case 'uint8': tag('B'); buffer.writeUInt8(val, offset); offset++; break; case 'short': case 'int16': tag('s'); buffer.writeInt16BE(val, offset); offset += 2; break; case 'unsignedshort': case 'uint16': tag('u'); buffer.writeUInt16BE(val, offset); offset += 2; break; case 'int': case 'int32': tag('I'); buffer.writeInt32BE(val, offset); offset += 4; break; case 'unsignedint': case 'uint32': tag('i'); buffer.writeUInt32BE(val, offset); offset += 4; break; case 'long': case 'int64': tag('l'); buffer.writeBigInt64BE(BigInt(val), offset); offset += 8; break; // Now for exotic types, those can _only_ be denoted by using // `{'!': type, value: val} case 'timestamp': tag('T'); buffer.writeBigUInt64BE(BigInt(val), offset); offset += 8; break; case 'float': tag('f'); buffer.writeFloatBE(val, offset); offset += 4; break; case 'decimal': tag('D'); if (Object.hasOwn(val, 'places') && Object.hasOwn(val, 'digits') && val.places >= 0 && val.places < 256) { buffer[offset] = val.places; offset++; buffer.writeUInt32BE(val.digits, offset); offset += 4; } else throw new TypeError(`Decimal value must be {'places': 0..255, 'digits': uint32}, got ${JSON.stringify(val)}`); break; default: throw new TypeError(`Unknown type to encode: ${type}`); } return offset - start; } // Assume we're given a slice of the buffer that contains just the // fields. function decodeFields(slice) { const fields = {}; let offset = 0; const size = slice.length; let len; let key; let val; function decodeFieldValue() { const tag = String.fromCharCode(slice[offset]); offset++; switch (tag) { case 'b': val = slice.readInt8(offset); offset++; break; case 'B': val = slice.readUInt8(offset); offset++; break; case 'S': len = slice.readUInt32BE(offset); offset += 4; val = slice.toString('utf8', offset, offset + len); offset += len; break; case 'I': val = slice.readInt32BE(offset); offset += 4; break; case 'i': val = slice.readUInt32BE(offset); offset += 4; break; case 'D': { // only positive decimals, apparently. const places = slice[offset]; offset++; const digits = slice.readUInt32BE(offset); offset += 4; val = { '!': 'decimal', value: { places: places, digits: digits } }; break; } case 'T': val = Number(slice.readBigUInt64BE(offset)); offset += 8; val = { '!': 'timestamp', value: val }; break; case 'F': len = slice.readUInt32BE(offset); offset += 4; val = decodeFields(slice.subarray(offset, offset + len)); offset += len; break; case 'A': len = slice.readUInt32BE(offset); offset += 4; decodeArray(offset + len); // NB decodeArray will itself update offset and val break; case 'd': val = slice.readDoubleBE(offset); offset += 8; break; case 'f': val = slice.readFloatBE(offset); offset += 4; break; case 'l': val = Number(slice.readBigInt64BE(offset)); offset += 8; break; case 's': val = slice.readInt16BE(offset); offset += 2; break; case 'u': val = slice.readUInt16BE(offset); offset += 2; break; case 't': val = slice[offset] !== 0; offset++; break; case 'V': val = null; break; case 'x': len = slice.readUInt32BE(offset); offset += 4; val = slice.subarray(offset, offset + len); offset += len; break; default: throw new TypeError(`Unexpected type tag "${tag}"`); } } function decodeArray(until) { const vals = []; while (offset < until) { decodeFieldValue(); vals.push(val); } val = vals; } while (offset < size) { len = slice.readUInt8(offset); offset++; key = slice.toString('utf8', offset, offset + len); offset += len; decodeFieldValue(); fields[key] = val; } return fields; } module.exports.encodeTable = encodeTable; module.exports.decodeFields = decodeFields; amqp-node-amqplib-263c8a3/lib/connect.js000066400000000000000000000133011520012327700201030ustar00rootroot00000000000000const { URL } = require('node:url'); const QS = require('node:querystring'); const net = require('node:net'); const tls = require('node:tls'); const Connection = require('./connection').Connection; const fmt = require('node:util').format; const credentials = require('./credentials'); function copyInto(obj, target) { const keys = Object.keys(obj); let i = keys.length; while (i--) { const k = keys[i]; target[k] = obj[k]; } return target; } // Adapted from util._extend, which is too fringe to use. function clone(obj) { return copyInto(obj, {}); } const CLIENT_PROPERTIES = { product: 'amqplib', version: require('../package.json').version, platform: fmt('Node.JS %s', process.version), information: 'https://amqp-node.github.io/amqplib/', capabilities: { publisher_confirms: true, exchange_exchange_bindings: true, 'basic.nack': true, consumer_cancel_notify: true, 'connection.blocked': true, authentication_failure_close: true, }, }; // Construct the main frames used in the opening handshake function openFrames(vhost, query, credentials, extraClientProperties) { if (!vhost) vhost = '/'; else vhost = QS.unescape(vhost); query = query || {}; function intOrDefault(val, def) { return val === undefined ? def : parseInt(val, 10); } const clientProperties = Object.create(CLIENT_PROPERTIES); return { // start-ok clientProperties: copyInto(extraClientProperties, clientProperties), mechanism: credentials.mechanism, response: credentials.response(), locale: query.locale || 'en_US', // tune-ok channelMax: intOrDefault(query.channelMax, 0), frameMax: intOrDefault(query.frameMax, 131072), heartbeat: query.heartbeat != null ? intOrDefault(query.heartbeat, 0) : null, // open virtualHost: vhost, capabilities: '', insist: 0, }; } // Decide on credentials based on what we're supplied. function credentialsFromUrl(parts) { let user = 'guest'; let passwd = 'guest'; if (parts.username !== '' || parts.password !== '') { user = parts.username ? unescape(parts.username) : ''; passwd = parts.password ? unescape(parts.password) : ''; } return credentials.plain(user, passwd); } function connect(url, socketOptions, openCallback) { // tls.connect uses `util._extend()` on the options given it, which // copies only properties mentioned in `Object.keys()`, when // processing the options. So I have to make copies too, rather // than using `Object.create()`. const sockopts = clone(socketOptions || {}); url = url || 'amqp://localhost'; const noDelay = !!sockopts.noDelay; const timeout = sockopts.timeout; const keepAlive = !!sockopts.keepAlive; // 0 is default for node const keepAliveDelay = sockopts.keepAliveDelay || 0; const extraClientProperties = sockopts.clientProperties || {}; let protocol; let fields; if (typeof url === 'object') { protocol = `${url.protocol || 'amqp'}:`; sockopts.host = url.hostname; if (!sockopts.servername && !net.isIP(url.hostname)) sockopts.servername = url.hostname; sockopts.port = url.port || (protocol === 'amqp:' ? 5672 : 5671); let user; let pass; // Only default if both are missing, to have the same behaviour as // the stringly URL. if (url.username === undefined && url.password === undefined) { user = 'guest'; pass = 'guest'; } else { user = url.username || ''; pass = url.password || ''; } const config = { locale: url.locale, channelMax: url.channelMax, frameMax: url.frameMax, heartbeat: url.heartbeat, }; fields = openFrames(url.vhost, config, sockopts.credentials || credentials.plain(user, pass), extraClientProperties); } else { let parts; try { parts = new URL(url); } catch (_e) { parts = {}; } const host = (parts.hostname || '').replace(/^\[|\]$/g, ''); protocol = parts.protocol || ''; sockopts.host = host; if (!sockopts.servername && !net.isIP(host)) sockopts.servername = host; sockopts.port = parseInt(parts.port, 10) || (protocol === 'amqp:' ? 5672 : 5671); const vhost = parts.pathname ? parts.pathname.substr(1) : null; const query = QS.parse(parts.search ? parts.search.slice(1) : ''); // QS.parse returns arrays for duplicate keys; match url-parse's first-value-wins behavior. for (const key in query) { if (Array.isArray(query[key])) query[key] = query[key][0]; } fields = openFrames(vhost, query, sockopts.credentials || credentialsFromUrl(parts), extraClientProperties); } let sockok = false; let sock; function onConnect() { sockok = true; sock.setNoDelay(noDelay); if (keepAlive) sock.setKeepAlive(keepAlive, keepAliveDelay); const c = new Connection(sock); c.open(fields, (err, _ok) => { // disable timeout once the connection is open, we don't want // it fouling things if (timeout) sock.setTimeout(0); if (err === null) { openCallback(null, c); } else { // The connection isn't closed by the server on e.g. wrong password sock.end(); sock.destroy(); openCallback(err); } }); } if (protocol === 'amqp:') { sock = net.connect(sockopts, onConnect); } else if (protocol === 'amqps:') { sock = tls.connect(sockopts, onConnect); } else { throw new Error(`Expected amqp: or amqps: as the protocol; got ${protocol}`); } if (timeout) { sock.setTimeout(timeout, () => { sock.end(); sock.destroy(); openCallback(new Error('connect ETIMEDOUT')); }); } sock.once('error', (err) => { if (!sockok) openCallback(err); }); } module.exports.connect = connect; module.exports.credentialsFromUrl = credentialsFromUrl; amqp-node-amqplib-263c8a3/lib/connection.js000066400000000000000000000526761520012327700206330ustar00rootroot00000000000000const defs = require('./defs'); const constants = defs.constants; const frame = require('./frame'); const HEARTBEAT = frame.HEARTBEAT; const Mux = require('./mux').Mux; const Duplex = require('node:stream').Duplex; const EventEmitter = require('node:events'); const Heart = require('./heartbeat').Heart; const methodName = require('./format').methodName; const closeMsg = require('./format').closeMessage; const inspect = require('./format').inspect; const BitSet = require('./bitset').BitSet; const fmt = require('node:util').format; const PassThrough = require('node:stream').PassThrough; const IllegalOperationError = require('./error').IllegalOperationError; const stackCapture = require('./error').stackCapture; const safeEmit = require('./safe_emit'); // High-water mark for channel write buffers, in 'objects' (which are // encoded frames as buffers). const DEFAULT_WRITE_HWM = 1024; // If all the frames of a message (method, properties, content) total // to less than this, copy them into a single buffer and write it all // at once. Note that this is less than the minimum frame size: if it // was greater, we might have to fragment the content. const SINGLE_CHUNK_THRESHOLD = 2048; class Connection extends EventEmitter { constructor(underlying) { super(); this.stream = wrapStream(underlying); this.muxer = new Mux(this.stream); // frames this.rest = Buffer.alloc(0); this.frameMax = constants.FRAME_MIN_SIZE; this.sentSinceLastCheck = false; this.recvSinceLastCheck = false; this.expectSocketClose = false; this.blocked = false; this.freeChannels = new BitSet(); this.channels = [ { channel: { accept: channel0(this) }, buffer: underlying, }, ]; } // This changed between versions, as did the codec, methods, etc. AMQP // 0-9-1 is fairly similar to 0.8, but better, and nothing implements // 0.8 that doesn't implement 0-9-1. In other words, it doesn't make // much sense to generalise here. sendProtocolHeader() { this.sendBytes(frame.PROTOCOL_HEADER); } /* The frighteningly complicated opening protocol (spec section 2.2.4): Client -> Server protocol header -> <- start start-ok -> .. next two zero or more times .. <- secure secure-ok -> <- tune tune-ok -> open -> <- open-ok If I'm only supporting SASL's PLAIN mechanism (which I am for the time being), it gets a bit easier since the server won't in general send back a `secure`, it'll just send `tune` after the `start-ok`. (SASL PLAIN: http://tools.ietf.org/html/rfc4616) */ open(allFields, openCallback0) { const self = this; const openCallback = openCallback0 || (() => {}); // This is where we'll put our negotiated values const tunedOptions = Object.create(allFields); function wait(k) { self.step((err, frame) => { if (err !== null) bail(err); else if (frame.channel !== 0) { bail(new Error(fmt('Frame on channel != 0 during handshake: %s', inspect(frame, false)))); } else k(frame); }); } function expect(Method, k) { wait((frame) => { if (frame.id === Method) k(frame); else { bail(new Error(fmt('Expected %s; got %s', methodName(Method), inspect(frame, false)))); } }); } function bail(err) { openCallback(err); } function send(Method) { // This can throw an exception if there's some problem with the // options; e.g., something is a string instead of a number. self.sendMethod(0, Method, tunedOptions); } function negotiate(server, desired) { // We get sent values for channelMax, frameMax and heartbeat, // which we may accept or lower (subject to a minimum for // frameMax, but we'll leave that to the server to enforce). In // all cases, `0` really means "no limit", or rather the highest // value in the encoding, e.g., unsigned short for channelMax. if (server === 0 || desired === 0) { // i.e., whichever places a limit, if either return Math.max(server, desired); } else { return Math.min(server, desired); } } function onStart(start) { const mechanisms = start.fields.mechanisms.toString().split(' '); if (mechanisms.indexOf(allFields.mechanism) < 0) { bail(new Error(fmt('SASL mechanism %s is not provided by the server', allFields.mechanism))); return; } self.serverProperties = start.fields.serverProperties; try { send(defs.ConnectionStartOk); } catch (err) { bail(err); return; } wait(afterStartOk); } function afterStartOk(reply) { switch (reply.id) { case defs.ConnectionSecure: bail(new Error("Wasn't expecting to have to go through secure")); break; case defs.ConnectionClose: bail(new Error(fmt('Handshake terminated by server: %s', closeMsg(reply)))); break; case defs.ConnectionTune: { const fields = reply.fields; tunedOptions.frameMax = negotiate(fields.frameMax, allFields.frameMax); tunedOptions.channelMax = negotiate(fields.channelMax, allFields.channelMax); if (allFields.heartbeat == null) { tunedOptions.heartbeat = fields.heartbeat; // no preference, accept server value } else if (allFields.heartbeat === 0) { tunedOptions.heartbeat = 0; // explicitly disable heartbeats } else { tunedOptions.heartbeat = negotiate(fields.heartbeat, allFields.heartbeat); } try { send(defs.ConnectionTuneOk); send(defs.ConnectionOpen); } catch (err) { bail(err); return; } expect(defs.ConnectionOpenOk, onOpenOk); break; } default: bail( new Error( fmt('Expected connection.secure, connection.close, ' + 'or connection.tune during handshake; got %s', inspect(reply, false)), ), ); break; } } function onOpenOk(openOk) { // Impose the maximum of the encoded value, if the negotiated // value is zero, meaning "no, no limits" self.channelMax = tunedOptions.channelMax || 0xffff; self.frameMax = tunedOptions.frameMax || 0xffffffff; // 0 means "no heartbeat", rather than "maximum period of // heartbeating" self.heartbeat = tunedOptions.heartbeat; self.heartbeater = self.startHeartbeater(); self.accept = mainAccept; succeed(openOk); } // If the server closes the connection, it's probably because of // something we did function endWhileOpening(err) { bail(err || new Error('Socket closed abruptly ' + 'during opening handshake')); } this.stream.on('end', endWhileOpening); this.stream.on('error', endWhileOpening); function succeed(ok) { self.stream.removeListener('end', endWhileOpening); self.stream.removeListener('error', endWhileOpening); self.stream.on('error', self.onSocketError.bind(self)); self.stream.on('end', self.onSocketError.bind(self, new Error('Unexpected close'))); self.on('frameError', self.onSocketError.bind(self)); self.acceptLoop(); openCallback(null, ok); } // Now kick off the handshake by prompting the server this.sendProtocolHeader(); expect(defs.ConnectionStart, onStart); } // Closing things: AMQP has a closing handshake that applies to // closing both connects and channels. As the initiating party, I send // Close, then ignore all frames until I see either CloseOK -- // which signifies that the other party has seen the Close and shut // the connection or channel down, so it's fine to free resources; or // Close, which means the other party also wanted to close the // whatever, and I should send CloseOk so it can free resources, // then go back to waiting for the CloseOk. If I receive a Close // out of the blue, I should throw away any unsent frames (they will // be ignored anyway) and send CloseOk, then clean up resources. In // general, Close out of the blue signals an error (or a forced // closure, which may as well be an error). // // RUNNING [1] --- send Close ---> Closing [2] ---> recv Close --+ // | | [3] // | +------ send CloseOk ------+ // recv Close recv CloseOk // | | // V V // Ended [4] ---- send CloseOk ---> Closed [5] // // [1] All frames accepted; getting a Close frame from the server // moves to Ended; client may initiate a close by sending Close // itself. // [2] Client has initiated a close; only CloseOk or (simulataneously // sent) Close is accepted. // [3] Simultaneous close // [4] Server won't send any more frames; accept no more frames, send // CloseOk. // [5] Fully closed, client will send no more, server will send no // more. Signal 'close' or 'error'. // // There are two signalling mechanisms used in the API. The first is // that calling `close` will return a promise, that will either // resolve once the connection or channel is cleanly shut down, or // will reject if the shutdown times out. // // The second is the 'close' and 'error' events. These are // emitted as above. The events will fire *before* promises are // resolved. // Close the connection without even giving a reason. Typical. close(closeCallback) { const k = closeCallback && (() => { closeCallback(null); }); this.closeBecause('Cheers, thanks', constants.REPLY_SUCCESS, k); } // Close with a reason and a 'code'. I'm pretty sure RabbitMQ totally // ignores these; maybe it logs them. The continuation will be invoked // when the CloseOk has been received, and before the 'close' event. closeBecause(reason, code, k) { this.sendMethod(0, defs.ConnectionClose, { replyText: reason, replyCode: code, methodId: 0, classId: 0, }); const s = stackCapture(`closeBecause called: ${reason}`); this.toClosing(s, k); } closeWithError(reason, code, error) { safeEmit(this, 'error', error); this.closeBecause(reason, code); } onSocketError(err) { if (!this.expectSocketClose) { // forestall any more calls to onSocketError, since we're signed // up for `'error'` *and* `'end'` this.expectSocketClose = true; safeEmit(this, 'error', err); const s = stackCapture('Socket error'); this.toClosed(s, err); } } // A close has been initiated. Repeat: a close has been initiated. // This means we should not send more frames, anyway they will be // ignored. We also have to shut down all the channels. toClosing(capturedStack, k) { const send = this.sendMethod.bind(this); if (this.blocked) { if (k) k(); const s = stackCapture('Connection blocked: closing immediately'); this.toClosed(s, undefined); return; } this.accept = function (f) { if (f.id === defs.ConnectionCloseOk) { if (k) k(); const s = stackCapture('ConnectionCloseOk received'); this.toClosed(s, undefined); } else if (f.id === defs.ConnectionClose) { send(0, defs.ConnectionCloseOk, {}); } // else ignore frame }; invalidateSend(this, 'Connection closing', capturedStack); } _closeChannels(capturedStack) { for (let i = 1; i < this.channels.length; i++) { const ch = this.channels[i]; if (ch !== null) { ch.channel.toClosed(capturedStack); // %%% or with an error? not clear } } } // A close has been confirmed. Cease all communication. toClosed(capturedStack, maybeErr) { this._closeChannels(capturedStack); const info = fmt('Connection closed (%s)', maybeErr ? maybeErr.toString() : 'by client'); // Tidy up, invalidate enverything, dynamite the bridges. invalidateSend(this, info, capturedStack); this.accept = invalidOp(info, capturedStack); this.close = (cb) => { cb && cb(new IllegalOperationError(info, capturedStack)); }; if (this.heartbeater) this.heartbeater.clear(); // This is certainly true now, if it wasn't before this.expectSocketClose = true; this.stream.end(); safeEmit(this, 'close', maybeErr); } _updateSecret(newSecret, reason, cb) { this.sendMethod(0, defs.ConnectionUpdateSecret, { newSecret, reason, }); this.once('update-secret-ok', cb); } // === startHeartbeater() { if (this.heartbeat === 0) return null; else { const hb = new Heart(this.heartbeat, this.checkSend.bind(this), this.checkRecv.bind(this)); hb.on('timeout', () => { const hberr = new Error('Heartbeat timeout'); safeEmit(this, 'error', hberr); const s = stackCapture('Heartbeat timeout'); this.toClosed(s, hberr); }); hb.on('beat', () => { this.sendHeartbeat(); }); return hb; } } // I use an array to keep track of the channels, rather than an // object. The channel identifiers are numbers, and allocated by the // connection. If I try to allocate low numbers when they are // available (which I do, by looking from the start of the bitset), // this ought to keep the array small, and out of 'sparse array // storage'. I also set entries to null, rather than deleting them, in // the expectation that the next channel allocation will fill the slot // again rather than growing the array. See // http://www.html5rocks.com/en/tutorials/speed/v8/ freshChannel(channel, options) { const next = this.freeChannels.nextClearBit(1); if (next < 0 || next > this.channelMax) throw new Error('No channels left to allocate'); this.freeChannels.set(next); const hwm = (options && options.highWaterMark) || DEFAULT_WRITE_HWM; const writeBuffer = new PassThrough({ objectMode: true, highWaterMark: hwm, }); this.channels[next] = { channel: channel, buffer: writeBuffer }; writeBuffer.on('drain', () => { channel.onBufferDrain(); }); this.muxer.pipeFrom(writeBuffer); return next; } releaseChannel(channel) { this.freeChannels.clear(channel); const buffer = this.channels[channel].buffer; buffer.end(); // will also cause it to be unpiped this.channels[channel] = null; } acceptLoop() { const self = this; function go() { try { let frame = self.recvFrame(); while (frame) { self.accept(frame); frame = self.recvFrame(); } } catch (e) { self.emit('frameError', e); } } self.stream.on('readable', go); go(); } step(cb) { const self = this; function recv() { let f; try { f = self.recvFrame(); } catch (e) { cb(e, null); return; } if (f) cb(null, f); else self.stream.once('readable', recv); } recv(); } checkSend() { const check = this.sentSinceLastCheck; this.sentSinceLastCheck = false; return check; } checkRecv() { const check = this.recvSinceLastCheck; this.recvSinceLastCheck = false; return check; } sendBytes(bytes) { this.sentSinceLastCheck = true; this.stream.write(bytes); } sendHeartbeat() { return this.sendBytes(frame.HEARTBEAT_BUF); } sendMethod(channel, Method, fields) { const frame = encodeMethod(Method, channel, fields); this.sentSinceLastCheck = true; const buffer = this.channels[channel].buffer; return buffer.write(frame); } sendMessage(channel, Method, fields, Properties, props, content) { if (!Buffer.isBuffer(content)) throw new TypeError('content is not a buffer'); const mframe = encodeMethod(Method, channel, fields); const pframe = encodeProperties(Properties, channel, content.length, props); const buffer = this.channels[channel].buffer; this.sentSinceLastCheck = true; const methodHeaderLen = mframe.length + pframe.length; const bodyLen = content.length > 0 ? content.length + FRAME_OVERHEAD : 0; const allLen = methodHeaderLen + bodyLen; if (allLen < SINGLE_CHUNK_THRESHOLD) { // Use `allocUnsafe` to avoid excessive allocations and CPU usage // from zeroing. The returned Buffer is not zeroed and so must be // completely filled to be used safely. // See https://github.com/amqp-node/amqplib/pull/695 const all = Buffer.allocUnsafe(allLen); let offset = mframe.copy(all, 0); offset += pframe.copy(all, offset); if (bodyLen > 0) makeBodyFrame(channel, content).copy(all, offset); return buffer.write(all); } else { if (methodHeaderLen < SINGLE_CHUNK_THRESHOLD) { // Use `allocUnsafe` to avoid excessive allocations and CPU usage // from zeroing. The returned Buffer is not zeroed and so must be // completely filled to be used safely. // See https://github.com/amqp-node/amqplib/pull/695 const both = Buffer.allocUnsafe(methodHeaderLen); const offset = mframe.copy(both, 0); pframe.copy(both, offset); buffer.write(both); } else { buffer.write(mframe); buffer.write(pframe); } return this.sendContent(channel, content); } } sendContent(channel, body) { if (!Buffer.isBuffer(body)) { throw new TypeError(fmt('Expected buffer; got %s', body)); } let writeResult = true; const buffer = this.channels[channel].buffer; const maxBody = this.frameMax - FRAME_OVERHEAD; for (let offset = 0; offset < body.length; offset += maxBody) { const end = offset + maxBody; const slice = end > body.length ? body.subarray(offset) : body.subarray(offset, end); const bodyFrame = makeBodyFrame(channel, slice); writeResult = buffer.write(bodyFrame); } this.sentSinceLastCheck = true; return writeResult; } recvFrame() { // %%% identifying invariants might help here? const frame = parseFrame(this.rest); if (!frame) { const incoming = this.stream.read(); if (incoming === null) { return false; } else { this.recvSinceLastCheck = true; this.rest = Buffer.concat([this.rest, incoming]); return this.recvFrame(); } } else { this.rest = frame.rest; return decodeFrame(frame); } } } // Usual frame accept mode function mainAccept(frame) { const rec = this.channels[frame.channel]; if (rec) { return rec.channel.accept(frame); } // NB CHANNEL_ERROR may not be right, but I don't know what is .. else this.closeWithError( fmt('Frame on unknown channel %d', frame.channel), constants.CHANNEL_ERROR, new Error(fmt('Frame on unknown channel: %s', inspect(frame, false))), ); } // Handle anything that comes through on channel 0, that's the // connection control channel. This is only used once mainAccept is // installed as the frame handler, after the opening handshake. function channel0(connection) { return (f) => { // Once we get a 'close', we know 1. we'll get no more frames, and // 2. anything we send except close, or close-ok, will be // ignored. If we already sent 'close', this won't be invoked since // we're already in closing mode; if we didn't well we're not going // to send it now are we. if (f === HEARTBEAT); // ignore; it's already counted as activity // on the socket, which is its purpose else if (f.id === defs.ConnectionClose) { // Oh. OK. I guess we're done here then. connection.sendMethod(0, defs.ConnectionCloseOk, {}); const emsg = fmt('Connection closed: %s', closeMsg(f)); const s = stackCapture(emsg); const e = new Error(emsg); e.code = f.fields.replyCode; if (isFatalError(e)) { safeEmit(connection, 'error', e); } connection.toClosed(s, e); } else if (f.id === defs.ConnectionBlocked) { connection.blocked = true; safeEmit(connection, 'blocked', f.fields.reason); } else if (f.id === defs.ConnectionUnblocked) { connection.blocked = false; safeEmit(connection, 'unblocked'); } else if (f.id === defs.ConnectionUpdateSecretOk) { safeEmit(connection, 'update-secret-ok'); } else { connection.closeWithError( fmt('Unexpected frame on channel 0'), constants.UNEXPECTED_FRAME, new Error(fmt('Unexpected frame on channel 0: %s', inspect(f, false))), ); } }; } function invalidOp(msg, stack) { return () => { throw new IllegalOperationError(msg, stack); }; } function invalidateSend(conn, msg, stack) { conn.sendMethod = conn.sendContent = conn.sendMessage = invalidOp(msg, stack); } const encodeMethod = defs.encodeMethod; const encodeProperties = defs.encodeProperties; const FRAME_OVERHEAD = defs.FRAME_OVERHEAD; const makeBodyFrame = frame.makeBodyFrame; const parseFrame = frame.parseFrame; const decodeFrame = frame.decodeFrame; function wrapStream(s) { if (s instanceof Duplex) return s; else { const ws = new Duplex(); ws.wrap(s); //wraps the readable side of things ws._write = (chunk, encoding, callback) => s.write(chunk, encoding, callback); return ws; } } function isFatalError(error) { switch (error && error.code) { case defs.constants.CONNECTION_FORCED: case defs.constants.REPLY_SUCCESS: return false; default: return true; } } module.exports.Connection = Connection; module.exports.isFatalError = isFatalError; amqp-node-amqplib-263c8a3/lib/credentials.js000066400000000000000000000017211520012327700207520ustar00rootroot00000000000000// // // // Different kind of credentials that can be supplied when opening a // connection, corresponding to SASL mechanisms There's only two // useful mechanisms that RabbitMQ implements: // * PLAIN (send username and password in the plain) // * EXTERNAL (assume the server will figure out who you are from // context, i.e., your SSL certificate) const codec = require('./codec'); module.exports.plain = (user, passwd) => ({ mechanism: 'PLAIN', response: () => Buffer.from(['', user, passwd].join(String.fromCharCode(0))), username: user, password: passwd, }); module.exports.amqplain = (user, passwd) => ({ mechanism: 'AMQPLAIN', response: () => { const buffer = Buffer.alloc(16384); const size = codec.encodeTable(buffer, { LOGIN: user, PASSWORD: passwd }, 0); return buffer.subarray(4, size); }, username: user, password: passwd, }); module.exports.external = () => ({ mechanism: 'EXTERNAL', response: () => Buffer.from(''), }); amqp-node-amqplib-263c8a3/lib/defs.js000066400000000000000000004254141520012327700174070ustar00rootroot00000000000000/** @preserve This file is generated by the script * ../bin/generate-defs.js, which is not in general included in a * distribution, but is available in the source repository e.g. at * https://github.com/amqp-node/amqplib/ */ 'use strict'; function decodeBasicQos(buffer) { var val, offset = 0, fields = { prefetchSize: void 0, prefetchCount: void 0, global: void 0, }; val = buffer.readUInt32BE(offset); offset += 4; fields.prefetchSize = val; val = buffer.readUInt16BE(offset); offset += 2; fields.prefetchCount = val; val = !!(1 & buffer[offset]); fields.global = val; return fields; } function encodeBasicQos(channel, fields) { var offset = 0, val = null, bits = 0, buffer = Buffer.alloc(19); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3932170, 7); offset = 11; val = fields.prefetchSize; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'prefetchSize' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt32BE(val, offset); offset += 4; val = fields.prefetchCount; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'prefetchCount' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; val = fields.global; void 0 === val && (val = !1); val && (bits += 1); buffer[offset] = bits; offset++; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeBasicQosOk(buffer) { return {}; } function encodeBasicQosOk(channel, fields) { var offset = 0, buffer = Buffer.alloc(12); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3932171, 7); offset = 11; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeBasicConsume(buffer) { var val, len, offset = 0, fields = { ticket: void 0, queue: void 0, consumerTag: void 0, noLocal: void 0, noAck: void 0, exclusive: void 0, nowait: void 0, arguments: void 0, }; val = buffer.readUInt16BE(offset); offset += 2; fields.ticket = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.queue = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.consumerTag = val; val = !!(1 & buffer[offset]); fields.noLocal = val; val = !!(2 & buffer[offset]); fields.noAck = val; val = !!(4 & buffer[offset]); fields.exclusive = val; val = !!(8 & buffer[offset]); fields.nowait = val; offset++; len = buffer.readUInt32BE(offset); offset += 4; val = decodeFields(buffer.subarray(offset, offset + len)); offset += len; fields.arguments = val; return fields; } function encodeBasicConsume(channel, fields) { var len, offset = 0, val = null, bits = 0, varyingSize = 0, scratchOffset = 0; val = fields.queue; if (void 0 === val) val = ''; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'queue' is the wrong type; must be a string (up to 255 chars)"); var queue_len = Buffer.byteLength(val, 'utf8'); varyingSize += queue_len; val = fields.consumerTag; if (void 0 === val) val = ''; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'consumerTag' is the wrong type; must be a string (up to 255 chars)"); var consumerTag_len = Buffer.byteLength(val, 'utf8'); varyingSize += consumerTag_len; val = fields.arguments; if (void 0 === val) val = {}; else if ('object' != typeof val) throw new TypeError("Field 'arguments' is the wrong type; must be an object"); len = encodeTable(SCRATCH, val, scratchOffset); var arguments_encoded = SCRATCH.slice(scratchOffset, scratchOffset + len); scratchOffset += len; varyingSize += arguments_encoded.length; var buffer = Buffer.alloc(17 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3932180, 7); offset = 11; val = fields.ticket; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'ticket' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; val = fields.queue; void 0 === val && (val = ''); buffer[offset] = queue_len; offset++; buffer.write(val, offset, 'utf8'); offset += queue_len; val = fields.consumerTag; void 0 === val && (val = ''); buffer[offset] = consumerTag_len; offset++; buffer.write(val, offset, 'utf8'); offset += consumerTag_len; val = fields.noLocal; void 0 === val && (val = !1); val && (bits += 1); val = fields.noAck; void 0 === val && (val = !1); val && (bits += 2); val = fields.exclusive; void 0 === val && (val = !1); val && (bits += 4); val = fields.nowait; void 0 === val && (val = !1); val && (bits += 8); buffer[offset] = bits; offset++; bits = 0; offset += arguments_encoded.copy(buffer, offset); buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeBasicConsumeOk(buffer) { var val, len, offset = 0, fields = { consumerTag: void 0, }; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.consumerTag = val; return fields; } function encodeBasicConsumeOk(channel, fields) { var offset = 0, val = null, varyingSize = 0; val = fields.consumerTag; if (void 0 === val) throw new Error("Missing value for mandatory field 'consumerTag'"); if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'consumerTag' is the wrong type; must be a string (up to 255 chars)"); var consumerTag_len = Buffer.byteLength(val, 'utf8'); varyingSize += consumerTag_len; var buffer = Buffer.alloc(13 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3932181, 7); offset = 11; val = fields.consumerTag; void 0 === val && (val = void 0); buffer[offset] = consumerTag_len; offset++; buffer.write(val, offset, 'utf8'); offset += consumerTag_len; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeBasicCancel(buffer) { var val, len, offset = 0, fields = { consumerTag: void 0, nowait: void 0, }; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.consumerTag = val; val = !!(1 & buffer[offset]); fields.nowait = val; return fields; } function encodeBasicCancel(channel, fields) { var offset = 0, val = null, bits = 0, varyingSize = 0; val = fields.consumerTag; if (void 0 === val) throw new Error("Missing value for mandatory field 'consumerTag'"); if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'consumerTag' is the wrong type; must be a string (up to 255 chars)"); var consumerTag_len = Buffer.byteLength(val, 'utf8'); varyingSize += consumerTag_len; var buffer = Buffer.alloc(14 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3932190, 7); offset = 11; val = fields.consumerTag; void 0 === val && (val = void 0); buffer[offset] = consumerTag_len; offset++; buffer.write(val, offset, 'utf8'); offset += consumerTag_len; val = fields.nowait; void 0 === val && (val = !1); val && (bits += 1); buffer[offset] = bits; offset++; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeBasicCancelOk(buffer) { var val, len, offset = 0, fields = { consumerTag: void 0, }; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.consumerTag = val; return fields; } function encodeBasicCancelOk(channel, fields) { var offset = 0, val = null, varyingSize = 0; val = fields.consumerTag; if (void 0 === val) throw new Error("Missing value for mandatory field 'consumerTag'"); if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'consumerTag' is the wrong type; must be a string (up to 255 chars)"); var consumerTag_len = Buffer.byteLength(val, 'utf8'); varyingSize += consumerTag_len; var buffer = Buffer.alloc(13 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3932191, 7); offset = 11; val = fields.consumerTag; void 0 === val && (val = void 0); buffer[offset] = consumerTag_len; offset++; buffer.write(val, offset, 'utf8'); offset += consumerTag_len; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeBasicPublish(buffer) { var val, len, offset = 0, fields = { ticket: void 0, exchange: void 0, routingKey: void 0, mandatory: void 0, immediate: void 0, }; val = buffer.readUInt16BE(offset); offset += 2; fields.ticket = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.exchange = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.routingKey = val; val = !!(1 & buffer[offset]); fields.mandatory = val; val = !!(2 & buffer[offset]); fields.immediate = val; return fields; } function encodeBasicPublish(channel, fields) { var offset = 0, val = null, bits = 0, varyingSize = 0; val = fields.exchange; if (void 0 === val) val = ''; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'exchange' is the wrong type; must be a string (up to 255 chars)"); var exchange_len = Buffer.byteLength(val, 'utf8'); varyingSize += exchange_len; val = fields.routingKey; if (void 0 === val) val = ''; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'routingKey' is the wrong type; must be a string (up to 255 chars)"); var routingKey_len = Buffer.byteLength(val, 'utf8'); varyingSize += routingKey_len; var buffer = Buffer.alloc(17 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3932200, 7); offset = 11; val = fields.ticket; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'ticket' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; val = fields.exchange; void 0 === val && (val = ''); buffer[offset] = exchange_len; offset++; buffer.write(val, offset, 'utf8'); offset += exchange_len; val = fields.routingKey; void 0 === val && (val = ''); buffer[offset] = routingKey_len; offset++; buffer.write(val, offset, 'utf8'); offset += routingKey_len; val = fields.mandatory; void 0 === val && (val = !1); val && (bits += 1); val = fields.immediate; void 0 === val && (val = !1); val && (bits += 2); buffer[offset] = bits; offset++; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeBasicReturn(buffer) { var val, len, offset = 0, fields = { replyCode: void 0, replyText: void 0, exchange: void 0, routingKey: void 0, }; val = buffer.readUInt16BE(offset); offset += 2; fields.replyCode = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.replyText = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.exchange = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.routingKey = val; return fields; } function encodeBasicReturn(channel, fields) { var offset = 0, val = null, varyingSize = 0; val = fields.replyText; if (void 0 === val) val = ''; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'replyText' is the wrong type; must be a string (up to 255 chars)"); var replyText_len = Buffer.byteLength(val, 'utf8'); varyingSize += replyText_len; val = fields.exchange; if (void 0 === val) throw new Error("Missing value for mandatory field 'exchange'"); if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'exchange' is the wrong type; must be a string (up to 255 chars)"); var exchange_len = Buffer.byteLength(val, 'utf8'); varyingSize += exchange_len; val = fields.routingKey; if (void 0 === val) throw new Error("Missing value for mandatory field 'routingKey'"); if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'routingKey' is the wrong type; must be a string (up to 255 chars)"); var routingKey_len = Buffer.byteLength(val, 'utf8'); varyingSize += routingKey_len; var buffer = Buffer.alloc(17 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3932210, 7); offset = 11; val = fields.replyCode; if (void 0 === val) throw new Error("Missing value for mandatory field 'replyCode'"); if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'replyCode' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; val = fields.replyText; void 0 === val && (val = ''); buffer[offset] = replyText_len; offset++; buffer.write(val, offset, 'utf8'); offset += replyText_len; val = fields.exchange; void 0 === val && (val = void 0); buffer[offset] = exchange_len; offset++; buffer.write(val, offset, 'utf8'); offset += exchange_len; val = fields.routingKey; void 0 === val && (val = void 0); buffer[offset] = routingKey_len; offset++; buffer.write(val, offset, 'utf8'); offset += routingKey_len; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeBasicDeliver(buffer) { var val, len, offset = 0, fields = { consumerTag: void 0, deliveryTag: void 0, redelivered: void 0, exchange: void 0, routingKey: void 0, }; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.consumerTag = val; val = Number(buffer.readBigUInt64BE(offset)); offset += 8; fields.deliveryTag = val; val = !!(1 & buffer[offset]); fields.redelivered = val; offset++; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.exchange = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.routingKey = val; return fields; } function encodeBasicDeliver(channel, fields) { var offset = 0, val = null, bits = 0, varyingSize = 0; val = fields.consumerTag; if (void 0 === val) throw new Error("Missing value for mandatory field 'consumerTag'"); if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'consumerTag' is the wrong type; must be a string (up to 255 chars)"); var consumerTag_len = Buffer.byteLength(val, 'utf8'); varyingSize += consumerTag_len; val = fields.exchange; if (void 0 === val) throw new Error("Missing value for mandatory field 'exchange'"); if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'exchange' is the wrong type; must be a string (up to 255 chars)"); var exchange_len = Buffer.byteLength(val, 'utf8'); varyingSize += exchange_len; val = fields.routingKey; if (void 0 === val) throw new Error("Missing value for mandatory field 'routingKey'"); if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'routingKey' is the wrong type; must be a string (up to 255 chars)"); var routingKey_len = Buffer.byteLength(val, 'utf8'); varyingSize += routingKey_len; var buffer = Buffer.alloc(24 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3932220, 7); offset = 11; val = fields.consumerTag; void 0 === val && (val = void 0); buffer[offset] = consumerTag_len; offset++; buffer.write(val, offset, 'utf8'); offset += consumerTag_len; val = fields.deliveryTag; if (void 0 === val) throw new Error("Missing value for mandatory field 'deliveryTag'"); if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'deliveryTag' is the wrong type; must be a number (but not NaN)"); buffer.writeBigUInt64BE(BigInt(val), offset); offset += 8; val = fields.redelivered; void 0 === val && (val = !1); val && (bits += 1); buffer[offset] = bits; offset++; bits = 0; val = fields.exchange; void 0 === val && (val = void 0); buffer[offset] = exchange_len; offset++; buffer.write(val, offset, 'utf8'); offset += exchange_len; val = fields.routingKey; void 0 === val && (val = void 0); buffer[offset] = routingKey_len; offset++; buffer.write(val, offset, 'utf8'); offset += routingKey_len; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeBasicGet(buffer) { var val, len, offset = 0, fields = { ticket: void 0, queue: void 0, noAck: void 0, }; val = buffer.readUInt16BE(offset); offset += 2; fields.ticket = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.queue = val; val = !!(1 & buffer[offset]); fields.noAck = val; return fields; } function encodeBasicGet(channel, fields) { var offset = 0, val = null, bits = 0, varyingSize = 0; val = fields.queue; if (void 0 === val) val = ''; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'queue' is the wrong type; must be a string (up to 255 chars)"); var queue_len = Buffer.byteLength(val, 'utf8'); varyingSize += queue_len; var buffer = Buffer.alloc(16 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3932230, 7); offset = 11; val = fields.ticket; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'ticket' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; val = fields.queue; void 0 === val && (val = ''); buffer[offset] = queue_len; offset++; buffer.write(val, offset, 'utf8'); offset += queue_len; val = fields.noAck; void 0 === val && (val = !1); val && (bits += 1); buffer[offset] = bits; offset++; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeBasicGetOk(buffer) { var val, len, offset = 0, fields = { deliveryTag: void 0, redelivered: void 0, exchange: void 0, routingKey: void 0, messageCount: void 0, }; val = Number(buffer.readBigUInt64BE(offset)); offset += 8; fields.deliveryTag = val; val = !!(1 & buffer[offset]); fields.redelivered = val; offset++; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.exchange = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.routingKey = val; val = buffer.readUInt32BE(offset); offset += 4; fields.messageCount = val; return fields; } function encodeBasicGetOk(channel, fields) { var offset = 0, val = null, bits = 0, varyingSize = 0; val = fields.exchange; if (void 0 === val) throw new Error("Missing value for mandatory field 'exchange'"); if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'exchange' is the wrong type; must be a string (up to 255 chars)"); var exchange_len = Buffer.byteLength(val, 'utf8'); varyingSize += exchange_len; val = fields.routingKey; if (void 0 === val) throw new Error("Missing value for mandatory field 'routingKey'"); if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'routingKey' is the wrong type; must be a string (up to 255 chars)"); var routingKey_len = Buffer.byteLength(val, 'utf8'); varyingSize += routingKey_len; var buffer = Buffer.alloc(27 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3932231, 7); offset = 11; val = fields.deliveryTag; if (void 0 === val) throw new Error("Missing value for mandatory field 'deliveryTag'"); if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'deliveryTag' is the wrong type; must be a number (but not NaN)"); buffer.writeBigUInt64BE(BigInt(val), offset); offset += 8; val = fields.redelivered; void 0 === val && (val = !1); val && (bits += 1); buffer[offset] = bits; offset++; bits = 0; val = fields.exchange; void 0 === val && (val = void 0); buffer[offset] = exchange_len; offset++; buffer.write(val, offset, 'utf8'); offset += exchange_len; val = fields.routingKey; void 0 === val && (val = void 0); buffer[offset] = routingKey_len; offset++; buffer.write(val, offset, 'utf8'); offset += routingKey_len; val = fields.messageCount; if (void 0 === val) throw new Error("Missing value for mandatory field 'messageCount'"); if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'messageCount' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt32BE(val, offset); offset += 4; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeBasicGetEmpty(buffer) { var val, len, offset = 0, fields = { clusterId: void 0, }; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.clusterId = val; return fields; } function encodeBasicGetEmpty(channel, fields) { var offset = 0, val = null, varyingSize = 0; val = fields.clusterId; if (void 0 === val) val = ''; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'clusterId' is the wrong type; must be a string (up to 255 chars)"); var clusterId_len = Buffer.byteLength(val, 'utf8'); varyingSize += clusterId_len; var buffer = Buffer.alloc(13 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3932232, 7); offset = 11; val = fields.clusterId; void 0 === val && (val = ''); buffer[offset] = clusterId_len; offset++; buffer.write(val, offset, 'utf8'); offset += clusterId_len; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeBasicAck(buffer) { var val, offset = 0, fields = { deliveryTag: void 0, multiple: void 0, }; val = Number(buffer.readBigUInt64BE(offset)); offset += 8; fields.deliveryTag = val; val = !!(1 & buffer[offset]); fields.multiple = val; return fields; } function encodeBasicAck(channel, fields) { var offset = 0, val = null, bits = 0, buffer = Buffer.alloc(21); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3932240, 7); offset = 11; val = fields.deliveryTag; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'deliveryTag' is the wrong type; must be a number (but not NaN)"); buffer.writeBigUInt64BE(BigInt(val), offset); offset += 8; val = fields.multiple; void 0 === val && (val = !1); val && (bits += 1); buffer[offset] = bits; offset++; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeBasicReject(buffer) { var val, offset = 0, fields = { deliveryTag: void 0, requeue: void 0, }; val = Number(buffer.readBigUInt64BE(offset)); offset += 8; fields.deliveryTag = val; val = !!(1 & buffer[offset]); fields.requeue = val; return fields; } function encodeBasicReject(channel, fields) { var offset = 0, val = null, bits = 0, buffer = Buffer.alloc(21); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3932250, 7); offset = 11; val = fields.deliveryTag; if (void 0 === val) throw new Error("Missing value for mandatory field 'deliveryTag'"); if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'deliveryTag' is the wrong type; must be a number (but not NaN)"); buffer.writeBigUInt64BE(BigInt(val), offset); offset += 8; val = fields.requeue; void 0 === val && (val = !0); val && (bits += 1); buffer[offset] = bits; offset++; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeBasicRecoverAsync(buffer) { var val, fields = { requeue: void 0, }; val = !!(1 & buffer[0]); fields.requeue = val; return fields; } function encodeBasicRecoverAsync(channel, fields) { var offset = 0, val = null, bits = 0, buffer = Buffer.alloc(13); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3932260, 7); offset = 11; val = fields.requeue; void 0 === val && (val = !1); val && (bits += 1); buffer[offset] = bits; offset++; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeBasicRecover(buffer) { var val, fields = { requeue: void 0, }; val = !!(1 & buffer[0]); fields.requeue = val; return fields; } function encodeBasicRecover(channel, fields) { var offset = 0, val = null, bits = 0, buffer = Buffer.alloc(13); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3932270, 7); offset = 11; val = fields.requeue; void 0 === val && (val = !1); val && (bits += 1); buffer[offset] = bits; offset++; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeBasicRecoverOk(buffer) { return {}; } function encodeBasicRecoverOk(channel, fields) { var offset = 0, buffer = Buffer.alloc(12); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3932271, 7); offset = 11; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeBasicNack(buffer) { var val, offset = 0, fields = { deliveryTag: void 0, multiple: void 0, requeue: void 0, }; val = Number(buffer.readBigUInt64BE(offset)); offset += 8; fields.deliveryTag = val; val = !!(1 & buffer[offset]); fields.multiple = val; val = !!(2 & buffer[offset]); fields.requeue = val; return fields; } function encodeBasicNack(channel, fields) { var offset = 0, val = null, bits = 0, buffer = Buffer.alloc(21); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3932280, 7); offset = 11; val = fields.deliveryTag; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'deliveryTag' is the wrong type; must be a number (but not NaN)"); buffer.writeBigUInt64BE(BigInt(val), offset); offset += 8; val = fields.multiple; void 0 === val && (val = !1); val && (bits += 1); val = fields.requeue; void 0 === val && (val = !0); val && (bits += 2); buffer[offset] = bits; offset++; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeConnectionStart(buffer) { var val, len, offset = 0, fields = { versionMajor: void 0, versionMinor: void 0, serverProperties: void 0, mechanisms: void 0, locales: void 0, }; val = buffer[offset]; offset++; fields.versionMajor = val; val = buffer[offset]; offset++; fields.versionMinor = val; len = buffer.readUInt32BE(offset); offset += 4; val = decodeFields(buffer.subarray(offset, offset + len)); offset += len; fields.serverProperties = val; len = buffer.readUInt32BE(offset); offset += 4; val = buffer.subarray(offset, offset + len); offset += len; fields.mechanisms = val; len = buffer.readUInt32BE(offset); offset += 4; val = buffer.subarray(offset, offset + len); offset += len; fields.locales = val; return fields; } function encodeConnectionStart(channel, fields) { var len, offset = 0, val = null, varyingSize = 0, scratchOffset = 0; val = fields.serverProperties; if (void 0 === val) throw new Error("Missing value for mandatory field 'serverProperties'"); if ('object' != typeof val) throw new TypeError("Field 'serverProperties' is the wrong type; must be an object"); len = encodeTable(SCRATCH, val, scratchOffset); var serverProperties_encoded = SCRATCH.slice(scratchOffset, scratchOffset + len); scratchOffset += len; varyingSize += serverProperties_encoded.length; val = fields.mechanisms; if (void 0 === val) val = Buffer.from('PLAIN'); else if (!Buffer.isBuffer(val)) throw new TypeError("Field 'mechanisms' is the wrong type; must be a Buffer"); varyingSize += val.length; val = fields.locales; if (void 0 === val) val = Buffer.from('en_US'); else if (!Buffer.isBuffer(val)) throw new TypeError("Field 'locales' is the wrong type; must be a Buffer"); varyingSize += val.length; var buffer = Buffer.alloc(22 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(655370, 7); offset = 11; val = fields.versionMajor; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'versionMajor' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt8(val, offset); offset++; val = fields.versionMinor; if (void 0 === val) val = 9; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'versionMinor' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt8(val, offset); offset++; offset += serverProperties_encoded.copy(buffer, offset); val = fields.mechanisms; void 0 === val && (val = Buffer.from('PLAIN')); len = val.length; buffer.writeUInt32BE(len, offset); offset += 4; val.copy(buffer, offset); offset += len; val = fields.locales; void 0 === val && (val = Buffer.from('en_US')); len = val.length; buffer.writeUInt32BE(len, offset); offset += 4; val.copy(buffer, offset); offset += len; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeConnectionStartOk(buffer) { var val, len, offset = 0, fields = { clientProperties: void 0, mechanism: void 0, response: void 0, locale: void 0, }; len = buffer.readUInt32BE(offset); offset += 4; val = decodeFields(buffer.subarray(offset, offset + len)); offset += len; fields.clientProperties = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.mechanism = val; len = buffer.readUInt32BE(offset); offset += 4; val = buffer.subarray(offset, offset + len); offset += len; fields.response = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.locale = val; return fields; } function encodeConnectionStartOk(channel, fields) { var len, offset = 0, val = null, varyingSize = 0, scratchOffset = 0; val = fields.clientProperties; if (void 0 === val) throw new Error("Missing value for mandatory field 'clientProperties'"); if ('object' != typeof val) throw new TypeError("Field 'clientProperties' is the wrong type; must be an object"); len = encodeTable(SCRATCH, val, scratchOffset); var clientProperties_encoded = SCRATCH.slice(scratchOffset, scratchOffset + len); scratchOffset += len; varyingSize += clientProperties_encoded.length; val = fields.mechanism; if (void 0 === val) val = 'PLAIN'; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'mechanism' is the wrong type; must be a string (up to 255 chars)"); var mechanism_len = Buffer.byteLength(val, 'utf8'); varyingSize += mechanism_len; val = fields.response; if (void 0 === val) throw new Error("Missing value for mandatory field 'response'"); if (!Buffer.isBuffer(val)) throw new TypeError("Field 'response' is the wrong type; must be a Buffer"); varyingSize += val.length; val = fields.locale; if (void 0 === val) val = 'en_US'; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'locale' is the wrong type; must be a string (up to 255 chars)"); var locale_len = Buffer.byteLength(val, 'utf8'); varyingSize += locale_len; var buffer = Buffer.alloc(18 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(655371, 7); offset = 11; offset += clientProperties_encoded.copy(buffer, offset); val = fields.mechanism; void 0 === val && (val = 'PLAIN'); buffer[offset] = mechanism_len; offset++; buffer.write(val, offset, 'utf8'); offset += mechanism_len; val = fields.response; void 0 === val && (val = Buffer.from(void 0)); len = val.length; buffer.writeUInt32BE(len, offset); offset += 4; val.copy(buffer, offset); offset += len; val = fields.locale; void 0 === val && (val = 'en_US'); buffer[offset] = locale_len; offset++; buffer.write(val, offset, 'utf8'); offset += locale_len; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeConnectionSecure(buffer) { var val, len, offset = 0, fields = { challenge: void 0, }; len = buffer.readUInt32BE(offset); offset += 4; val = buffer.subarray(offset, offset + len); offset += len; fields.challenge = val; return fields; } function encodeConnectionSecure(channel, fields) { var len, offset = 0, val = null, varyingSize = 0; val = fields.challenge; if (void 0 === val) throw new Error("Missing value for mandatory field 'challenge'"); if (!Buffer.isBuffer(val)) throw new TypeError("Field 'challenge' is the wrong type; must be a Buffer"); varyingSize += val.length; var buffer = Buffer.alloc(16 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(655380, 7); offset = 11; val = fields.challenge; void 0 === val && (val = Buffer.from(void 0)); len = val.length; buffer.writeUInt32BE(len, offset); offset += 4; val.copy(buffer, offset); offset += len; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeConnectionSecureOk(buffer) { var val, len, offset = 0, fields = { response: void 0, }; len = buffer.readUInt32BE(offset); offset += 4; val = buffer.subarray(offset, offset + len); offset += len; fields.response = val; return fields; } function encodeConnectionSecureOk(channel, fields) { var len, offset = 0, val = null, varyingSize = 0; val = fields.response; if (void 0 === val) throw new Error("Missing value for mandatory field 'response'"); if (!Buffer.isBuffer(val)) throw new TypeError("Field 'response' is the wrong type; must be a Buffer"); varyingSize += val.length; var buffer = Buffer.alloc(16 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(655381, 7); offset = 11; val = fields.response; void 0 === val && (val = Buffer.from(void 0)); len = val.length; buffer.writeUInt32BE(len, offset); offset += 4; val.copy(buffer, offset); offset += len; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeConnectionTune(buffer) { var val, offset = 0, fields = { channelMax: void 0, frameMax: void 0, heartbeat: void 0, }; val = buffer.readUInt16BE(offset); offset += 2; fields.channelMax = val; val = buffer.readUInt32BE(offset); offset += 4; fields.frameMax = val; val = buffer.readUInt16BE(offset); offset += 2; fields.heartbeat = val; return fields; } function encodeConnectionTune(channel, fields) { var offset = 0, val = null, buffer = Buffer.alloc(20); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(655390, 7); offset = 11; val = fields.channelMax; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'channelMax' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; val = fields.frameMax; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'frameMax' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt32BE(val, offset); offset += 4; val = fields.heartbeat; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'heartbeat' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeConnectionTuneOk(buffer) { var val, offset = 0, fields = { channelMax: void 0, frameMax: void 0, heartbeat: void 0, }; val = buffer.readUInt16BE(offset); offset += 2; fields.channelMax = val; val = buffer.readUInt32BE(offset); offset += 4; fields.frameMax = val; val = buffer.readUInt16BE(offset); offset += 2; fields.heartbeat = val; return fields; } function encodeConnectionTuneOk(channel, fields) { var offset = 0, val = null, buffer = Buffer.alloc(20); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(655391, 7); offset = 11; val = fields.channelMax; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'channelMax' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; val = fields.frameMax; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'frameMax' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt32BE(val, offset); offset += 4; val = fields.heartbeat; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'heartbeat' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeConnectionOpen(buffer) { var val, len, offset = 0, fields = { virtualHost: void 0, capabilities: void 0, insist: void 0, }; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.virtualHost = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.capabilities = val; val = !!(1 & buffer[offset]); fields.insist = val; return fields; } function encodeConnectionOpen(channel, fields) { var offset = 0, val = null, bits = 0, varyingSize = 0; val = fields.virtualHost; if (void 0 === val) val = '/'; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'virtualHost' is the wrong type; must be a string (up to 255 chars)"); var virtualHost_len = Buffer.byteLength(val, 'utf8'); varyingSize += virtualHost_len; val = fields.capabilities; if (void 0 === val) val = ''; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'capabilities' is the wrong type; must be a string (up to 255 chars)"); var capabilities_len = Buffer.byteLength(val, 'utf8'); varyingSize += capabilities_len; var buffer = Buffer.alloc(15 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(655400, 7); offset = 11; val = fields.virtualHost; void 0 === val && (val = '/'); buffer[offset] = virtualHost_len; offset++; buffer.write(val, offset, 'utf8'); offset += virtualHost_len; val = fields.capabilities; void 0 === val && (val = ''); buffer[offset] = capabilities_len; offset++; buffer.write(val, offset, 'utf8'); offset += capabilities_len; val = fields.insist; void 0 === val && (val = !1); val && (bits += 1); buffer[offset] = bits; offset++; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeConnectionOpenOk(buffer) { var val, len, offset = 0, fields = { knownHosts: void 0, }; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.knownHosts = val; return fields; } function encodeConnectionOpenOk(channel, fields) { var offset = 0, val = null, varyingSize = 0; val = fields.knownHosts; if (void 0 === val) val = ''; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'knownHosts' is the wrong type; must be a string (up to 255 chars)"); var knownHosts_len = Buffer.byteLength(val, 'utf8'); varyingSize += knownHosts_len; var buffer = Buffer.alloc(13 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(655401, 7); offset = 11; val = fields.knownHosts; void 0 === val && (val = ''); buffer[offset] = knownHosts_len; offset++; buffer.write(val, offset, 'utf8'); offset += knownHosts_len; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeConnectionClose(buffer) { var val, len, offset = 0, fields = { replyCode: void 0, replyText: void 0, classId: void 0, methodId: void 0, }; val = buffer.readUInt16BE(offset); offset += 2; fields.replyCode = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.replyText = val; val = buffer.readUInt16BE(offset); offset += 2; fields.classId = val; val = buffer.readUInt16BE(offset); offset += 2; fields.methodId = val; return fields; } function encodeConnectionClose(channel, fields) { var offset = 0, val = null, varyingSize = 0; val = fields.replyText; if (void 0 === val) val = ''; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'replyText' is the wrong type; must be a string (up to 255 chars)"); var replyText_len = Buffer.byteLength(val, 'utf8'); varyingSize += replyText_len; var buffer = Buffer.alloc(19 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(655410, 7); offset = 11; val = fields.replyCode; if (void 0 === val) throw new Error("Missing value for mandatory field 'replyCode'"); if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'replyCode' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; val = fields.replyText; void 0 === val && (val = ''); buffer[offset] = replyText_len; offset++; buffer.write(val, offset, 'utf8'); offset += replyText_len; val = fields.classId; if (void 0 === val) throw new Error("Missing value for mandatory field 'classId'"); if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'classId' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; val = fields.methodId; if (void 0 === val) throw new Error("Missing value for mandatory field 'methodId'"); if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'methodId' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeConnectionCloseOk(buffer) { return {}; } function encodeConnectionCloseOk(channel, fields) { var offset = 0, buffer = Buffer.alloc(12); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(655411, 7); offset = 11; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeConnectionBlocked(buffer) { var val, len, offset = 0, fields = { reason: void 0, }; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.reason = val; return fields; } function encodeConnectionBlocked(channel, fields) { var offset = 0, val = null, varyingSize = 0; val = fields.reason; if (void 0 === val) val = ''; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'reason' is the wrong type; must be a string (up to 255 chars)"); var reason_len = Buffer.byteLength(val, 'utf8'); varyingSize += reason_len; var buffer = Buffer.alloc(13 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(655420, 7); offset = 11; val = fields.reason; void 0 === val && (val = ''); buffer[offset] = reason_len; offset++; buffer.write(val, offset, 'utf8'); offset += reason_len; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeConnectionUnblocked(buffer) { return {}; } function encodeConnectionUnblocked(channel, fields) { var offset = 0, buffer = Buffer.alloc(12); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(655421, 7); offset = 11; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeConnectionUpdateSecret(buffer) { var val, len, offset = 0, fields = { newSecret: void 0, reason: void 0, }; len = buffer.readUInt32BE(offset); offset += 4; val = buffer.subarray(offset, offset + len); offset += len; fields.newSecret = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.reason = val; return fields; } function encodeConnectionUpdateSecret(channel, fields) { var len, offset = 0, val = null, varyingSize = 0; val = fields.newSecret; if (void 0 === val) throw new Error("Missing value for mandatory field 'newSecret'"); if (!Buffer.isBuffer(val)) throw new TypeError("Field 'newSecret' is the wrong type; must be a Buffer"); varyingSize += val.length; val = fields.reason; if (void 0 === val) throw new Error("Missing value for mandatory field 'reason'"); if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'reason' is the wrong type; must be a string (up to 255 chars)"); var reason_len = Buffer.byteLength(val, 'utf8'); varyingSize += reason_len; var buffer = Buffer.alloc(17 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(655430, 7); offset = 11; val = fields.newSecret; void 0 === val && (val = Buffer.from(void 0)); len = val.length; buffer.writeUInt32BE(len, offset); offset += 4; val.copy(buffer, offset); offset += len; val = fields.reason; void 0 === val && (val = void 0); buffer[offset] = reason_len; offset++; buffer.write(val, offset, 'utf8'); offset += reason_len; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeConnectionUpdateSecretOk(buffer) { return {}; } function encodeConnectionUpdateSecretOk(channel, fields) { var offset = 0, buffer = Buffer.alloc(12); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(655431, 7); offset = 11; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeChannelOpen(buffer) { var val, len, offset = 0, fields = { outOfBand: void 0, }; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.outOfBand = val; return fields; } function encodeChannelOpen(channel, fields) { var offset = 0, val = null, varyingSize = 0; val = fields.outOfBand; if (void 0 === val) val = ''; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'outOfBand' is the wrong type; must be a string (up to 255 chars)"); var outOfBand_len = Buffer.byteLength(val, 'utf8'); varyingSize += outOfBand_len; var buffer = Buffer.alloc(13 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(1310730, 7); offset = 11; val = fields.outOfBand; void 0 === val && (val = ''); buffer[offset] = outOfBand_len; offset++; buffer.write(val, offset, 'utf8'); offset += outOfBand_len; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeChannelOpenOk(buffer) { var val, len, offset = 0, fields = { channelId: void 0, }; len = buffer.readUInt32BE(offset); offset += 4; val = buffer.subarray(offset, offset + len); offset += len; fields.channelId = val; return fields; } function encodeChannelOpenOk(channel, fields) { var len, offset = 0, val = null, varyingSize = 0; val = fields.channelId; if (void 0 === val) val = Buffer.from(''); else if (!Buffer.isBuffer(val)) throw new TypeError("Field 'channelId' is the wrong type; must be a Buffer"); varyingSize += val.length; var buffer = Buffer.alloc(16 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(1310731, 7); offset = 11; val = fields.channelId; void 0 === val && (val = Buffer.from('')); len = val.length; buffer.writeUInt32BE(len, offset); offset += 4; val.copy(buffer, offset); offset += len; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeChannelFlow(buffer) { var val, fields = { active: void 0, }; val = !!(1 & buffer[0]); fields.active = val; return fields; } function encodeChannelFlow(channel, fields) { var offset = 0, val = null, bits = 0, buffer = Buffer.alloc(13); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(1310740, 7); offset = 11; val = fields.active; if (void 0 === val) throw new Error("Missing value for mandatory field 'active'"); val && (bits += 1); buffer[offset] = bits; offset++; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeChannelFlowOk(buffer) { var val, fields = { active: void 0, }; val = !!(1 & buffer[0]); fields.active = val; return fields; } function encodeChannelFlowOk(channel, fields) { var offset = 0, val = null, bits = 0, buffer = Buffer.alloc(13); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(1310741, 7); offset = 11; val = fields.active; if (void 0 === val) throw new Error("Missing value for mandatory field 'active'"); val && (bits += 1); buffer[offset] = bits; offset++; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeChannelClose(buffer) { var val, len, offset = 0, fields = { replyCode: void 0, replyText: void 0, classId: void 0, methodId: void 0, }; val = buffer.readUInt16BE(offset); offset += 2; fields.replyCode = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.replyText = val; val = buffer.readUInt16BE(offset); offset += 2; fields.classId = val; val = buffer.readUInt16BE(offset); offset += 2; fields.methodId = val; return fields; } function encodeChannelClose(channel, fields) { var offset = 0, val = null, varyingSize = 0; val = fields.replyText; if (void 0 === val) val = ''; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'replyText' is the wrong type; must be a string (up to 255 chars)"); var replyText_len = Buffer.byteLength(val, 'utf8'); varyingSize += replyText_len; var buffer = Buffer.alloc(19 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(1310760, 7); offset = 11; val = fields.replyCode; if (void 0 === val) throw new Error("Missing value for mandatory field 'replyCode'"); if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'replyCode' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; val = fields.replyText; void 0 === val && (val = ''); buffer[offset] = replyText_len; offset++; buffer.write(val, offset, 'utf8'); offset += replyText_len; val = fields.classId; if (void 0 === val) throw new Error("Missing value for mandatory field 'classId'"); if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'classId' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; val = fields.methodId; if (void 0 === val) throw new Error("Missing value for mandatory field 'methodId'"); if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'methodId' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeChannelCloseOk(buffer) { return {}; } function encodeChannelCloseOk(channel, fields) { var offset = 0, buffer = Buffer.alloc(12); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(1310761, 7); offset = 11; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeAccessRequest(buffer) { var val, len, offset = 0, fields = { realm: void 0, exclusive: void 0, passive: void 0, active: void 0, write: void 0, read: void 0, }; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.realm = val; val = !!(1 & buffer[offset]); fields.exclusive = val; val = !!(2 & buffer[offset]); fields.passive = val; val = !!(4 & buffer[offset]); fields.active = val; val = !!(8 & buffer[offset]); fields.write = val; val = !!(16 & buffer[offset]); fields.read = val; return fields; } function encodeAccessRequest(channel, fields) { var offset = 0, val = null, bits = 0, varyingSize = 0; val = fields.realm; if (void 0 === val) val = '/data'; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'realm' is the wrong type; must be a string (up to 255 chars)"); var realm_len = Buffer.byteLength(val, 'utf8'); varyingSize += realm_len; var buffer = Buffer.alloc(14 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(1966090, 7); offset = 11; val = fields.realm; void 0 === val && (val = '/data'); buffer[offset] = realm_len; offset++; buffer.write(val, offset, 'utf8'); offset += realm_len; val = fields.exclusive; void 0 === val && (val = !1); val && (bits += 1); val = fields.passive; void 0 === val && (val = !0); val && (bits += 2); val = fields.active; void 0 === val && (val = !0); val && (bits += 4); val = fields.write; void 0 === val && (val = !0); val && (bits += 8); val = fields.read; void 0 === val && (val = !0); val && (bits += 16); buffer[offset] = bits; offset++; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeAccessRequestOk(buffer) { var val, offset = 0, fields = { ticket: void 0, }; val = buffer.readUInt16BE(offset); offset += 2; fields.ticket = val; return fields; } function encodeAccessRequestOk(channel, fields) { var offset = 0, val = null, buffer = Buffer.alloc(14); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(1966091, 7); offset = 11; val = fields.ticket; if (void 0 === val) val = 1; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'ticket' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeExchangeDeclare(buffer) { var val, len, offset = 0, fields = { ticket: void 0, exchange: void 0, type: void 0, passive: void 0, durable: void 0, autoDelete: void 0, internal: void 0, nowait: void 0, arguments: void 0, }; val = buffer.readUInt16BE(offset); offset += 2; fields.ticket = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.exchange = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.type = val; val = !!(1 & buffer[offset]); fields.passive = val; val = !!(2 & buffer[offset]); fields.durable = val; val = !!(4 & buffer[offset]); fields.autoDelete = val; val = !!(8 & buffer[offset]); fields.internal = val; val = !!(16 & buffer[offset]); fields.nowait = val; offset++; len = buffer.readUInt32BE(offset); offset += 4; val = decodeFields(buffer.subarray(offset, offset + len)); offset += len; fields.arguments = val; return fields; } function encodeExchangeDeclare(channel, fields) { var len, offset = 0, val = null, bits = 0, varyingSize = 0, scratchOffset = 0; val = fields.exchange; if (void 0 === val) throw new Error("Missing value for mandatory field 'exchange'"); if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'exchange' is the wrong type; must be a string (up to 255 chars)"); var exchange_len = Buffer.byteLength(val, 'utf8'); varyingSize += exchange_len; val = fields.type; if (void 0 === val) val = 'direct'; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'type' is the wrong type; must be a string (up to 255 chars)"); var type_len = Buffer.byteLength(val, 'utf8'); varyingSize += type_len; val = fields.arguments; if (void 0 === val) val = {}; else if ('object' != typeof val) throw new TypeError("Field 'arguments' is the wrong type; must be an object"); len = encodeTable(SCRATCH, val, scratchOffset); var arguments_encoded = SCRATCH.slice(scratchOffset, scratchOffset + len); scratchOffset += len; varyingSize += arguments_encoded.length; var buffer = Buffer.alloc(17 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(2621450, 7); offset = 11; val = fields.ticket; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'ticket' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; val = fields.exchange; void 0 === val && (val = void 0); buffer[offset] = exchange_len; offset++; buffer.write(val, offset, 'utf8'); offset += exchange_len; val = fields.type; void 0 === val && (val = 'direct'); buffer[offset] = type_len; offset++; buffer.write(val, offset, 'utf8'); offset += type_len; val = fields.passive; void 0 === val && (val = !1); val && (bits += 1); val = fields.durable; void 0 === val && (val = !1); val && (bits += 2); val = fields.autoDelete; void 0 === val && (val = !1); val && (bits += 4); val = fields.internal; void 0 === val && (val = !1); val && (bits += 8); val = fields.nowait; void 0 === val && (val = !1); val && (bits += 16); buffer[offset] = bits; offset++; bits = 0; offset += arguments_encoded.copy(buffer, offset); buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeExchangeDeclareOk(buffer) { return {}; } function encodeExchangeDeclareOk(channel, fields) { var offset = 0, buffer = Buffer.alloc(12); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(2621451, 7); offset = 11; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeExchangeDelete(buffer) { var val, len, offset = 0, fields = { ticket: void 0, exchange: void 0, ifUnused: void 0, nowait: void 0, }; val = buffer.readUInt16BE(offset); offset += 2; fields.ticket = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.exchange = val; val = !!(1 & buffer[offset]); fields.ifUnused = val; val = !!(2 & buffer[offset]); fields.nowait = val; return fields; } function encodeExchangeDelete(channel, fields) { var offset = 0, val = null, bits = 0, varyingSize = 0; val = fields.exchange; if (void 0 === val) throw new Error("Missing value for mandatory field 'exchange'"); if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'exchange' is the wrong type; must be a string (up to 255 chars)"); var exchange_len = Buffer.byteLength(val, 'utf8'); varyingSize += exchange_len; var buffer = Buffer.alloc(16 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(2621460, 7); offset = 11; val = fields.ticket; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'ticket' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; val = fields.exchange; void 0 === val && (val = void 0); buffer[offset] = exchange_len; offset++; buffer.write(val, offset, 'utf8'); offset += exchange_len; val = fields.ifUnused; void 0 === val && (val = !1); val && (bits += 1); val = fields.nowait; void 0 === val && (val = !1); val && (bits += 2); buffer[offset] = bits; offset++; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeExchangeDeleteOk(buffer) { return {}; } function encodeExchangeDeleteOk(channel, fields) { var offset = 0, buffer = Buffer.alloc(12); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(2621461, 7); offset = 11; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeExchangeBind(buffer) { var val, len, offset = 0, fields = { ticket: void 0, destination: void 0, source: void 0, routingKey: void 0, nowait: void 0, arguments: void 0, }; val = buffer.readUInt16BE(offset); offset += 2; fields.ticket = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.destination = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.source = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.routingKey = val; val = !!(1 & buffer[offset]); fields.nowait = val; offset++; len = buffer.readUInt32BE(offset); offset += 4; val = decodeFields(buffer.subarray(offset, offset + len)); offset += len; fields.arguments = val; return fields; } function encodeExchangeBind(channel, fields) { var len, offset = 0, val = null, bits = 0, varyingSize = 0, scratchOffset = 0; val = fields.destination; if (void 0 === val) throw new Error("Missing value for mandatory field 'destination'"); if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'destination' is the wrong type; must be a string (up to 255 chars)"); var destination_len = Buffer.byteLength(val, 'utf8'); varyingSize += destination_len; val = fields.source; if (void 0 === val) throw new Error("Missing value for mandatory field 'source'"); if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'source' is the wrong type; must be a string (up to 255 chars)"); var source_len = Buffer.byteLength(val, 'utf8'); varyingSize += source_len; val = fields.routingKey; if (void 0 === val) val = ''; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'routingKey' is the wrong type; must be a string (up to 255 chars)"); var routingKey_len = Buffer.byteLength(val, 'utf8'); varyingSize += routingKey_len; val = fields.arguments; if (void 0 === val) val = {}; else if ('object' != typeof val) throw new TypeError("Field 'arguments' is the wrong type; must be an object"); len = encodeTable(SCRATCH, val, scratchOffset); var arguments_encoded = SCRATCH.slice(scratchOffset, scratchOffset + len); scratchOffset += len; varyingSize += arguments_encoded.length; var buffer = Buffer.alloc(18 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(2621470, 7); offset = 11; val = fields.ticket; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'ticket' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; val = fields.destination; void 0 === val && (val = void 0); buffer[offset] = destination_len; offset++; buffer.write(val, offset, 'utf8'); offset += destination_len; val = fields.source; void 0 === val && (val = void 0); buffer[offset] = source_len; offset++; buffer.write(val, offset, 'utf8'); offset += source_len; val = fields.routingKey; void 0 === val && (val = ''); buffer[offset] = routingKey_len; offset++; buffer.write(val, offset, 'utf8'); offset += routingKey_len; val = fields.nowait; void 0 === val && (val = !1); val && (bits += 1); buffer[offset] = bits; offset++; bits = 0; offset += arguments_encoded.copy(buffer, offset); buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeExchangeBindOk(buffer) { return {}; } function encodeExchangeBindOk(channel, fields) { var offset = 0, buffer = Buffer.alloc(12); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(2621471, 7); offset = 11; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeExchangeUnbind(buffer) { var val, len, offset = 0, fields = { ticket: void 0, destination: void 0, source: void 0, routingKey: void 0, nowait: void 0, arguments: void 0, }; val = buffer.readUInt16BE(offset); offset += 2; fields.ticket = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.destination = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.source = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.routingKey = val; val = !!(1 & buffer[offset]); fields.nowait = val; offset++; len = buffer.readUInt32BE(offset); offset += 4; val = decodeFields(buffer.subarray(offset, offset + len)); offset += len; fields.arguments = val; return fields; } function encodeExchangeUnbind(channel, fields) { var len, offset = 0, val = null, bits = 0, varyingSize = 0, scratchOffset = 0; val = fields.destination; if (void 0 === val) throw new Error("Missing value for mandatory field 'destination'"); if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'destination' is the wrong type; must be a string (up to 255 chars)"); var destination_len = Buffer.byteLength(val, 'utf8'); varyingSize += destination_len; val = fields.source; if (void 0 === val) throw new Error("Missing value for mandatory field 'source'"); if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'source' is the wrong type; must be a string (up to 255 chars)"); var source_len = Buffer.byteLength(val, 'utf8'); varyingSize += source_len; val = fields.routingKey; if (void 0 === val) val = ''; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'routingKey' is the wrong type; must be a string (up to 255 chars)"); var routingKey_len = Buffer.byteLength(val, 'utf8'); varyingSize += routingKey_len; val = fields.arguments; if (void 0 === val) val = {}; else if ('object' != typeof val) throw new TypeError("Field 'arguments' is the wrong type; must be an object"); len = encodeTable(SCRATCH, val, scratchOffset); var arguments_encoded = SCRATCH.slice(scratchOffset, scratchOffset + len); scratchOffset += len; varyingSize += arguments_encoded.length; var buffer = Buffer.alloc(18 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(2621480, 7); offset = 11; val = fields.ticket; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'ticket' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; val = fields.destination; void 0 === val && (val = void 0); buffer[offset] = destination_len; offset++; buffer.write(val, offset, 'utf8'); offset += destination_len; val = fields.source; void 0 === val && (val = void 0); buffer[offset] = source_len; offset++; buffer.write(val, offset, 'utf8'); offset += source_len; val = fields.routingKey; void 0 === val && (val = ''); buffer[offset] = routingKey_len; offset++; buffer.write(val, offset, 'utf8'); offset += routingKey_len; val = fields.nowait; void 0 === val && (val = !1); val && (bits += 1); buffer[offset] = bits; offset++; bits = 0; offset += arguments_encoded.copy(buffer, offset); buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeExchangeUnbindOk(buffer) { return {}; } function encodeExchangeUnbindOk(channel, fields) { var offset = 0, buffer = Buffer.alloc(12); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(2621491, 7); offset = 11; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeQueueDeclare(buffer) { var val, len, offset = 0, fields = { ticket: void 0, queue: void 0, passive: void 0, durable: void 0, exclusive: void 0, autoDelete: void 0, nowait: void 0, arguments: void 0, }; val = buffer.readUInt16BE(offset); offset += 2; fields.ticket = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.queue = val; val = !!(1 & buffer[offset]); fields.passive = val; val = !!(2 & buffer[offset]); fields.durable = val; val = !!(4 & buffer[offset]); fields.exclusive = val; val = !!(8 & buffer[offset]); fields.autoDelete = val; val = !!(16 & buffer[offset]); fields.nowait = val; offset++; len = buffer.readUInt32BE(offset); offset += 4; val = decodeFields(buffer.subarray(offset, offset + len)); offset += len; fields.arguments = val; return fields; } function encodeQueueDeclare(channel, fields) { var len, offset = 0, val = null, bits = 0, varyingSize = 0, scratchOffset = 0; val = fields.queue; if (void 0 === val) val = ''; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'queue' is the wrong type; must be a string (up to 255 chars)"); var queue_len = Buffer.byteLength(val, 'utf8'); varyingSize += queue_len; val = fields.arguments; if (void 0 === val) val = {}; else if ('object' != typeof val) throw new TypeError("Field 'arguments' is the wrong type; must be an object"); len = encodeTable(SCRATCH, val, scratchOffset); var arguments_encoded = SCRATCH.slice(scratchOffset, scratchOffset + len); scratchOffset += len; varyingSize += arguments_encoded.length; var buffer = Buffer.alloc(16 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3276810, 7); offset = 11; val = fields.ticket; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'ticket' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; val = fields.queue; void 0 === val && (val = ''); buffer[offset] = queue_len; offset++; buffer.write(val, offset, 'utf8'); offset += queue_len; val = fields.passive; void 0 === val && (val = !1); val && (bits += 1); val = fields.durable; void 0 === val && (val = !1); val && (bits += 2); val = fields.exclusive; void 0 === val && (val = !1); val && (bits += 4); val = fields.autoDelete; void 0 === val && (val = !1); val && (bits += 8); val = fields.nowait; void 0 === val && (val = !1); val && (bits += 16); buffer[offset] = bits; offset++; bits = 0; offset += arguments_encoded.copy(buffer, offset); buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeQueueDeclareOk(buffer) { var val, len, offset = 0, fields = { queue: void 0, messageCount: void 0, consumerCount: void 0, }; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.queue = val; val = buffer.readUInt32BE(offset); offset += 4; fields.messageCount = val; val = buffer.readUInt32BE(offset); offset += 4; fields.consumerCount = val; return fields; } function encodeQueueDeclareOk(channel, fields) { var offset = 0, val = null, varyingSize = 0; val = fields.queue; if (void 0 === val) throw new Error("Missing value for mandatory field 'queue'"); if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'queue' is the wrong type; must be a string (up to 255 chars)"); var queue_len = Buffer.byteLength(val, 'utf8'); varyingSize += queue_len; var buffer = Buffer.alloc(21 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3276811, 7); offset = 11; val = fields.queue; void 0 === val && (val = void 0); buffer[offset] = queue_len; offset++; buffer.write(val, offset, 'utf8'); offset += queue_len; val = fields.messageCount; if (void 0 === val) throw new Error("Missing value for mandatory field 'messageCount'"); if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'messageCount' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt32BE(val, offset); offset += 4; val = fields.consumerCount; if (void 0 === val) throw new Error("Missing value for mandatory field 'consumerCount'"); if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'consumerCount' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt32BE(val, offset); offset += 4; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeQueueBind(buffer) { var val, len, offset = 0, fields = { ticket: void 0, queue: void 0, exchange: void 0, routingKey: void 0, nowait: void 0, arguments: void 0, }; val = buffer.readUInt16BE(offset); offset += 2; fields.ticket = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.queue = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.exchange = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.routingKey = val; val = !!(1 & buffer[offset]); fields.nowait = val; offset++; len = buffer.readUInt32BE(offset); offset += 4; val = decodeFields(buffer.subarray(offset, offset + len)); offset += len; fields.arguments = val; return fields; } function encodeQueueBind(channel, fields) { var len, offset = 0, val = null, bits = 0, varyingSize = 0, scratchOffset = 0; val = fields.queue; if (void 0 === val) val = ''; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'queue' is the wrong type; must be a string (up to 255 chars)"); var queue_len = Buffer.byteLength(val, 'utf8'); varyingSize += queue_len; val = fields.exchange; if (void 0 === val) throw new Error("Missing value for mandatory field 'exchange'"); if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'exchange' is the wrong type; must be a string (up to 255 chars)"); var exchange_len = Buffer.byteLength(val, 'utf8'); varyingSize += exchange_len; val = fields.routingKey; if (void 0 === val) val = ''; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'routingKey' is the wrong type; must be a string (up to 255 chars)"); var routingKey_len = Buffer.byteLength(val, 'utf8'); varyingSize += routingKey_len; val = fields.arguments; if (void 0 === val) val = {}; else if ('object' != typeof val) throw new TypeError("Field 'arguments' is the wrong type; must be an object"); len = encodeTable(SCRATCH, val, scratchOffset); var arguments_encoded = SCRATCH.slice(scratchOffset, scratchOffset + len); scratchOffset += len; varyingSize += arguments_encoded.length; var buffer = Buffer.alloc(18 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3276820, 7); offset = 11; val = fields.ticket; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'ticket' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; val = fields.queue; void 0 === val && (val = ''); buffer[offset] = queue_len; offset++; buffer.write(val, offset, 'utf8'); offset += queue_len; val = fields.exchange; void 0 === val && (val = void 0); buffer[offset] = exchange_len; offset++; buffer.write(val, offset, 'utf8'); offset += exchange_len; val = fields.routingKey; void 0 === val && (val = ''); buffer[offset] = routingKey_len; offset++; buffer.write(val, offset, 'utf8'); offset += routingKey_len; val = fields.nowait; void 0 === val && (val = !1); val && (bits += 1); buffer[offset] = bits; offset++; bits = 0; offset += arguments_encoded.copy(buffer, offset); buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeQueueBindOk(buffer) { return {}; } function encodeQueueBindOk(channel, fields) { var offset = 0, buffer = Buffer.alloc(12); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3276821, 7); offset = 11; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeQueuePurge(buffer) { var val, len, offset = 0, fields = { ticket: void 0, queue: void 0, nowait: void 0, }; val = buffer.readUInt16BE(offset); offset += 2; fields.ticket = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.queue = val; val = !!(1 & buffer[offset]); fields.nowait = val; return fields; } function encodeQueuePurge(channel, fields) { var offset = 0, val = null, bits = 0, varyingSize = 0; val = fields.queue; if (void 0 === val) val = ''; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'queue' is the wrong type; must be a string (up to 255 chars)"); var queue_len = Buffer.byteLength(val, 'utf8'); varyingSize += queue_len; var buffer = Buffer.alloc(16 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3276830, 7); offset = 11; val = fields.ticket; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'ticket' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; val = fields.queue; void 0 === val && (val = ''); buffer[offset] = queue_len; offset++; buffer.write(val, offset, 'utf8'); offset += queue_len; val = fields.nowait; void 0 === val && (val = !1); val && (bits += 1); buffer[offset] = bits; offset++; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeQueuePurgeOk(buffer) { var val, offset = 0, fields = { messageCount: void 0, }; val = buffer.readUInt32BE(offset); offset += 4; fields.messageCount = val; return fields; } function encodeQueuePurgeOk(channel, fields) { var offset = 0, val = null, buffer = Buffer.alloc(16); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3276831, 7); offset = 11; val = fields.messageCount; if (void 0 === val) throw new Error("Missing value for mandatory field 'messageCount'"); if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'messageCount' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt32BE(val, offset); offset += 4; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeQueueDelete(buffer) { var val, len, offset = 0, fields = { ticket: void 0, queue: void 0, ifUnused: void 0, ifEmpty: void 0, nowait: void 0, }; val = buffer.readUInt16BE(offset); offset += 2; fields.ticket = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.queue = val; val = !!(1 & buffer[offset]); fields.ifUnused = val; val = !!(2 & buffer[offset]); fields.ifEmpty = val; val = !!(4 & buffer[offset]); fields.nowait = val; return fields; } function encodeQueueDelete(channel, fields) { var offset = 0, val = null, bits = 0, varyingSize = 0; val = fields.queue; if (void 0 === val) val = ''; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'queue' is the wrong type; must be a string (up to 255 chars)"); var queue_len = Buffer.byteLength(val, 'utf8'); varyingSize += queue_len; var buffer = Buffer.alloc(16 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3276840, 7); offset = 11; val = fields.ticket; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'ticket' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; val = fields.queue; void 0 === val && (val = ''); buffer[offset] = queue_len; offset++; buffer.write(val, offset, 'utf8'); offset += queue_len; val = fields.ifUnused; void 0 === val && (val = !1); val && (bits += 1); val = fields.ifEmpty; void 0 === val && (val = !1); val && (bits += 2); val = fields.nowait; void 0 === val && (val = !1); val && (bits += 4); buffer[offset] = bits; offset++; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeQueueDeleteOk(buffer) { var val, offset = 0, fields = { messageCount: void 0, }; val = buffer.readUInt32BE(offset); offset += 4; fields.messageCount = val; return fields; } function encodeQueueDeleteOk(channel, fields) { var offset = 0, val = null, buffer = Buffer.alloc(16); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3276841, 7); offset = 11; val = fields.messageCount; if (void 0 === val) throw new Error("Missing value for mandatory field 'messageCount'"); if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'messageCount' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt32BE(val, offset); offset += 4; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeQueueUnbind(buffer) { var val, len, offset = 0, fields = { ticket: void 0, queue: void 0, exchange: void 0, routingKey: void 0, arguments: void 0, }; val = buffer.readUInt16BE(offset); offset += 2; fields.ticket = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.queue = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.exchange = val; len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.routingKey = val; len = buffer.readUInt32BE(offset); offset += 4; val = decodeFields(buffer.subarray(offset, offset + len)); offset += len; fields.arguments = val; return fields; } function encodeQueueUnbind(channel, fields) { var len, offset = 0, val = null, varyingSize = 0, scratchOffset = 0; val = fields.queue; if (void 0 === val) val = ''; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'queue' is the wrong type; must be a string (up to 255 chars)"); var queue_len = Buffer.byteLength(val, 'utf8'); varyingSize += queue_len; val = fields.exchange; if (void 0 === val) throw new Error("Missing value for mandatory field 'exchange'"); if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'exchange' is the wrong type; must be a string (up to 255 chars)"); var exchange_len = Buffer.byteLength(val, 'utf8'); varyingSize += exchange_len; val = fields.routingKey; if (void 0 === val) val = ''; else if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'routingKey' is the wrong type; must be a string (up to 255 chars)"); var routingKey_len = Buffer.byteLength(val, 'utf8'); varyingSize += routingKey_len; val = fields.arguments; if (void 0 === val) val = {}; else if ('object' != typeof val) throw new TypeError("Field 'arguments' is the wrong type; must be an object"); len = encodeTable(SCRATCH, val, scratchOffset); var arguments_encoded = SCRATCH.slice(scratchOffset, scratchOffset + len); scratchOffset += len; varyingSize += arguments_encoded.length; var buffer = Buffer.alloc(17 + varyingSize); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3276850, 7); offset = 11; val = fields.ticket; if (void 0 === val) val = 0; else if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'ticket' is the wrong type; must be a number (but not NaN)"); buffer.writeUInt16BE(val, offset); offset += 2; val = fields.queue; void 0 === val && (val = ''); buffer[offset] = queue_len; offset++; buffer.write(val, offset, 'utf8'); offset += queue_len; val = fields.exchange; void 0 === val && (val = void 0); buffer[offset] = exchange_len; offset++; buffer.write(val, offset, 'utf8'); offset += exchange_len; val = fields.routingKey; void 0 === val && (val = ''); buffer[offset] = routingKey_len; offset++; buffer.write(val, offset, 'utf8'); offset += routingKey_len; offset += arguments_encoded.copy(buffer, offset); buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeQueueUnbindOk(buffer) { return {}; } function encodeQueueUnbindOk(channel, fields) { var offset = 0, buffer = Buffer.alloc(12); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3276851, 7); offset = 11; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeTxSelect(buffer) { return {}; } function encodeTxSelect(channel, fields) { var offset = 0, buffer = Buffer.alloc(12); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(5898250, 7); offset = 11; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeTxSelectOk(buffer) { return {}; } function encodeTxSelectOk(channel, fields) { var offset = 0, buffer = Buffer.alloc(12); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(5898251, 7); offset = 11; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeTxCommit(buffer) { return {}; } function encodeTxCommit(channel, fields) { var offset = 0, buffer = Buffer.alloc(12); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(5898260, 7); offset = 11; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeTxCommitOk(buffer) { return {}; } function encodeTxCommitOk(channel, fields) { var offset = 0, buffer = Buffer.alloc(12); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(5898261, 7); offset = 11; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeTxRollback(buffer) { return {}; } function encodeTxRollback(channel, fields) { var offset = 0, buffer = Buffer.alloc(12); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(5898270, 7); offset = 11; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeTxRollbackOk(buffer) { return {}; } function encodeTxRollbackOk(channel, fields) { var offset = 0, buffer = Buffer.alloc(12); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(5898271, 7); offset = 11; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeConfirmSelect(buffer) { var val, fields = { nowait: void 0, }; val = !!(1 & buffer[0]); fields.nowait = val; return fields; } function encodeConfirmSelect(channel, fields) { var offset = 0, val = null, bits = 0, buffer = Buffer.alloc(13); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(5570570, 7); offset = 11; val = fields.nowait; void 0 === val && (val = !1); val && (bits += 1); buffer[offset] = bits; offset++; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function decodeConfirmSelectOk(buffer) { return {}; } function encodeConfirmSelectOk(channel, fields) { var offset = 0, buffer = Buffer.alloc(12); buffer[0] = 1; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(5570571, 7); offset = 11; buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); return buffer; } function encodeBasicProperties(channel, size, fields) { var val, len, offset = 0, flags = 0, scratchOffset = 0, varyingSize = 0; val = fields.contentType; if (void 0 != val) { if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'contentType' is the wrong type; must be a string (up to 255 chars)"); var contentType_len = Buffer.byteLength(val, 'utf8'); varyingSize += 1; varyingSize += contentType_len; } val = fields.contentEncoding; if (void 0 != val) { if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'contentEncoding' is the wrong type; must be a string (up to 255 chars)"); var contentEncoding_len = Buffer.byteLength(val, 'utf8'); varyingSize += 1; varyingSize += contentEncoding_len; } val = fields.headers; if (void 0 != val) { if ('object' != typeof val) throw new TypeError("Field 'headers' is the wrong type; must be an object"); len = encodeTable(SCRATCH, val, scratchOffset); var headers_encoded = SCRATCH.slice(scratchOffset, scratchOffset + len); scratchOffset += len; varyingSize += headers_encoded.length; } val = fields.deliveryMode; if (void 0 != val) { if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'deliveryMode' is the wrong type; must be a number (but not NaN)"); varyingSize += 1; } val = fields.priority; if (void 0 != val) { if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'priority' is the wrong type; must be a number (but not NaN)"); varyingSize += 1; } val = fields.correlationId; if (void 0 != val) { if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'correlationId' is the wrong type; must be a string (up to 255 chars)"); var correlationId_len = Buffer.byteLength(val, 'utf8'); varyingSize += 1; varyingSize += correlationId_len; } val = fields.replyTo; if (void 0 != val) { if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'replyTo' is the wrong type; must be a string (up to 255 chars)"); var replyTo_len = Buffer.byteLength(val, 'utf8'); varyingSize += 1; varyingSize += replyTo_len; } val = fields.expiration; if (void 0 != val) { if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'expiration' is the wrong type; must be a string (up to 255 chars)"); var expiration_len = Buffer.byteLength(val, 'utf8'); varyingSize += 1; varyingSize += expiration_len; } val = fields.messageId; if (void 0 != val) { if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'messageId' is the wrong type; must be a string (up to 255 chars)"); var messageId_len = Buffer.byteLength(val, 'utf8'); varyingSize += 1; varyingSize += messageId_len; } val = fields.timestamp; if (void 0 != val) { if ('number' != typeof val || isNaN(val)) throw new TypeError("Field 'timestamp' is the wrong type; must be a number (but not NaN)"); varyingSize += 8; } val = fields.type; if (void 0 != val) { if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'type' is the wrong type; must be a string (up to 255 chars)"); var type_len = Buffer.byteLength(val, 'utf8'); varyingSize += 1; varyingSize += type_len; } val = fields.userId; if (void 0 != val) { if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'userId' is the wrong type; must be a string (up to 255 chars)"); var userId_len = Buffer.byteLength(val, 'utf8'); varyingSize += 1; varyingSize += userId_len; } val = fields.appId; if (void 0 != val) { if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'appId' is the wrong type; must be a string (up to 255 chars)"); var appId_len = Buffer.byteLength(val, 'utf8'); varyingSize += 1; varyingSize += appId_len; } val = fields.clusterId; if (void 0 != val) { if (!('string' == typeof val && Buffer.byteLength(val) < 256)) throw new TypeError("Field 'clusterId' is the wrong type; must be a string (up to 255 chars)"); var clusterId_len = Buffer.byteLength(val, 'utf8'); varyingSize += 1; varyingSize += clusterId_len; } var buffer = Buffer.alloc(22 + varyingSize); buffer[0] = 2; buffer.writeUInt16BE(channel, 1); buffer.writeUInt32BE(3932160, 7); buffer.writeBigUInt64BE(BigInt(size), 11); flags = 0; offset = 21; val = fields.contentType; if (void 0 != val) { flags += 32768; buffer[offset] = contentType_len; offset++; buffer.write(val, offset, 'utf8'); offset += contentType_len; } val = fields.contentEncoding; if (void 0 != val) { flags += 16384; buffer[offset] = contentEncoding_len; offset++; buffer.write(val, offset, 'utf8'); offset += contentEncoding_len; } val = fields.headers; if (void 0 != val) { flags += 8192; offset += headers_encoded.copy(buffer, offset); } val = fields.deliveryMode; if (void 0 != val) { flags += 4096; buffer.writeUInt8(val, offset); offset++; } val = fields.priority; if (void 0 != val) { flags += 2048; buffer.writeUInt8(val, offset); offset++; } val = fields.correlationId; if (void 0 != val) { flags += 1024; buffer[offset] = correlationId_len; offset++; buffer.write(val, offset, 'utf8'); offset += correlationId_len; } val = fields.replyTo; if (void 0 != val) { flags += 512; buffer[offset] = replyTo_len; offset++; buffer.write(val, offset, 'utf8'); offset += replyTo_len; } val = fields.expiration; if (void 0 != val) { flags += 256; buffer[offset] = expiration_len; offset++; buffer.write(val, offset, 'utf8'); offset += expiration_len; } val = fields.messageId; if (void 0 != val) { flags += 128; buffer[offset] = messageId_len; offset++; buffer.write(val, offset, 'utf8'); offset += messageId_len; } val = fields.timestamp; if (void 0 != val) { flags += 64; buffer.writeBigUInt64BE(BigInt(val), offset); offset += 8; } val = fields.type; if (void 0 != val) { flags += 32; buffer[offset] = type_len; offset++; buffer.write(val, offset, 'utf8'); offset += type_len; } val = fields.userId; if (void 0 != val) { flags += 16; buffer[offset] = userId_len; offset++; buffer.write(val, offset, 'utf8'); offset += userId_len; } val = fields.appId; if (void 0 != val) { flags += 8; buffer[offset] = appId_len; offset++; buffer.write(val, offset, 'utf8'); offset += appId_len; } val = fields.clusterId; if (void 0 != val) { flags += 4; buffer[offset] = clusterId_len; offset++; buffer.write(val, offset, 'utf8'); offset += clusterId_len; } buffer[offset] = 206; buffer.writeUInt32BE(offset - 7, 3); buffer.writeUInt16BE(flags, 19); return buffer.subarray(0, offset + 1); } function decodeBasicProperties(buffer) { var flags, val, len, offset = 2; flags = buffer.readUInt16BE(0); if (0 === flags) return {}; var fields = { contentType: void 0, contentEncoding: void 0, headers: void 0, deliveryMode: void 0, priority: void 0, correlationId: void 0, replyTo: void 0, expiration: void 0, messageId: void 0, timestamp: void 0, type: void 0, userId: void 0, appId: void 0, clusterId: void 0, }; if (32768 & flags) { len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.contentType = val; } if (16384 & flags) { len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.contentEncoding = val; } if (8192 & flags) { len = buffer.readUInt32BE(offset); offset += 4; val = decodeFields(buffer.subarray(offset, offset + len)); offset += len; fields.headers = val; } if (4096 & flags) { val = buffer[offset]; offset++; fields.deliveryMode = val; } if (2048 & flags) { val = buffer[offset]; offset++; fields.priority = val; } if (1024 & flags) { len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.correlationId = val; } if (512 & flags) { len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.replyTo = val; } if (256 & flags) { len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.expiration = val; } if (128 & flags) { len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.messageId = val; } if (64 & flags) { val = Number(buffer.readBigUInt64BE(offset)); offset += 8; fields.timestamp = val; } if (32 & flags) { len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.type = val; } if (16 & flags) { len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.userId = val; } if (8 & flags) { len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.appId = val; } if (4 & flags) { len = buffer.readUInt8(offset); offset++; val = buffer.toString('utf8', offset, offset + len); offset += len; fields.clusterId = val; } return fields; } var codec = require('./codec'), encodeTable = codec.encodeTable, decodeFields = codec.decodeFields, SCRATCH = Buffer.alloc(65536), EMPTY_OBJECT = Object.freeze({}); module.exports.constants = { FRAME_METHOD: 1, FRAME_HEADER: 2, FRAME_BODY: 3, FRAME_HEARTBEAT: 8, FRAME_MIN_SIZE: 4096, FRAME_END: 206, REPLY_SUCCESS: 200, CONTENT_TOO_LARGE: 311, NO_ROUTE: 312, NO_CONSUMERS: 313, ACCESS_REFUSED: 403, NOT_FOUND: 404, RESOURCE_LOCKED: 405, PRECONDITION_FAILED: 406, CONNECTION_FORCED: 320, INVALID_PATH: 402, FRAME_ERROR: 501, SYNTAX_ERROR: 502, COMMAND_INVALID: 503, CHANNEL_ERROR: 504, UNEXPECTED_FRAME: 505, RESOURCE_ERROR: 506, NOT_ALLOWED: 530, NOT_IMPLEMENTED: 540, INTERNAL_ERROR: 541, }; module.exports.constant_strs = { 1: 'FRAME-METHOD', 2: 'FRAME-HEADER', 3: 'FRAME-BODY', 8: 'FRAME-HEARTBEAT', 200: 'REPLY-SUCCESS', 206: 'FRAME-END', 311: 'CONTENT-TOO-LARGE', 312: 'NO-ROUTE', 313: 'NO-CONSUMERS', 320: 'CONNECTION-FORCED', 402: 'INVALID-PATH', 403: 'ACCESS-REFUSED', 404: 'NOT-FOUND', 405: 'RESOURCE-LOCKED', 406: 'PRECONDITION-FAILED', 501: 'FRAME-ERROR', 502: 'SYNTAX-ERROR', 503: 'COMMAND-INVALID', 504: 'CHANNEL-ERROR', 505: 'UNEXPECTED-FRAME', 506: 'RESOURCE-ERROR', 530: 'NOT-ALLOWED', 540: 'NOT-IMPLEMENTED', 541: 'INTERNAL-ERROR', 4096: 'FRAME-MIN-SIZE', }; module.exports.FRAME_OVERHEAD = 8; module.exports.decode = function (id, buf) { switch (id) { case 3932170: return decodeBasicQos(buf); case 3932171: return decodeBasicQosOk(buf); case 3932180: return decodeBasicConsume(buf); case 3932181: return decodeBasicConsumeOk(buf); case 3932190: return decodeBasicCancel(buf); case 3932191: return decodeBasicCancelOk(buf); case 3932200: return decodeBasicPublish(buf); case 3932210: return decodeBasicReturn(buf); case 3932220: return decodeBasicDeliver(buf); case 3932230: return decodeBasicGet(buf); case 3932231: return decodeBasicGetOk(buf); case 3932232: return decodeBasicGetEmpty(buf); case 3932240: return decodeBasicAck(buf); case 3932250: return decodeBasicReject(buf); case 3932260: return decodeBasicRecoverAsync(buf); case 3932270: return decodeBasicRecover(buf); case 3932271: return decodeBasicRecoverOk(buf); case 3932280: return decodeBasicNack(buf); case 655370: return decodeConnectionStart(buf); case 655371: return decodeConnectionStartOk(buf); case 655380: return decodeConnectionSecure(buf); case 655381: return decodeConnectionSecureOk(buf); case 655390: return decodeConnectionTune(buf); case 655391: return decodeConnectionTuneOk(buf); case 655400: return decodeConnectionOpen(buf); case 655401: return decodeConnectionOpenOk(buf); case 655410: return decodeConnectionClose(buf); case 655411: return decodeConnectionCloseOk(buf); case 655420: return decodeConnectionBlocked(buf); case 655421: return decodeConnectionUnblocked(buf); case 655430: return decodeConnectionUpdateSecret(buf); case 655431: return decodeConnectionUpdateSecretOk(buf); case 1310730: return decodeChannelOpen(buf); case 1310731: return decodeChannelOpenOk(buf); case 1310740: return decodeChannelFlow(buf); case 1310741: return decodeChannelFlowOk(buf); case 1310760: return decodeChannelClose(buf); case 1310761: return decodeChannelCloseOk(buf); case 1966090: return decodeAccessRequest(buf); case 1966091: return decodeAccessRequestOk(buf); case 2621450: return decodeExchangeDeclare(buf); case 2621451: return decodeExchangeDeclareOk(buf); case 2621460: return decodeExchangeDelete(buf); case 2621461: return decodeExchangeDeleteOk(buf); case 2621470: return decodeExchangeBind(buf); case 2621471: return decodeExchangeBindOk(buf); case 2621480: return decodeExchangeUnbind(buf); case 2621491: return decodeExchangeUnbindOk(buf); case 3276810: return decodeQueueDeclare(buf); case 3276811: return decodeQueueDeclareOk(buf); case 3276820: return decodeQueueBind(buf); case 3276821: return decodeQueueBindOk(buf); case 3276830: return decodeQueuePurge(buf); case 3276831: return decodeQueuePurgeOk(buf); case 3276840: return decodeQueueDelete(buf); case 3276841: return decodeQueueDeleteOk(buf); case 3276850: return decodeQueueUnbind(buf); case 3276851: return decodeQueueUnbindOk(buf); case 5898250: return decodeTxSelect(buf); case 5898251: return decodeTxSelectOk(buf); case 5898260: return decodeTxCommit(buf); case 5898261: return decodeTxCommitOk(buf); case 5898270: return decodeTxRollback(buf); case 5898271: return decodeTxRollbackOk(buf); case 5570570: return decodeConfirmSelect(buf); case 5570571: return decodeConfirmSelectOk(buf); case 60: return decodeBasicProperties(buf); default: throw new Error('Unknown class/method ID'); } }; module.exports.encodeMethod = function (id, channel, fields) { switch (id) { case 3932170: return encodeBasicQos(channel, fields); case 3932171: return encodeBasicQosOk(channel, fields); case 3932180: return encodeBasicConsume(channel, fields); case 3932181: return encodeBasicConsumeOk(channel, fields); case 3932190: return encodeBasicCancel(channel, fields); case 3932191: return encodeBasicCancelOk(channel, fields); case 3932200: return encodeBasicPublish(channel, fields); case 3932210: return encodeBasicReturn(channel, fields); case 3932220: return encodeBasicDeliver(channel, fields); case 3932230: return encodeBasicGet(channel, fields); case 3932231: return encodeBasicGetOk(channel, fields); case 3932232: return encodeBasicGetEmpty(channel, fields); case 3932240: return encodeBasicAck(channel, fields); case 3932250: return encodeBasicReject(channel, fields); case 3932260: return encodeBasicRecoverAsync(channel, fields); case 3932270: return encodeBasicRecover(channel, fields); case 3932271: return encodeBasicRecoverOk(channel, fields); case 3932280: return encodeBasicNack(channel, fields); case 655370: return encodeConnectionStart(channel, fields); case 655371: return encodeConnectionStartOk(channel, fields); case 655380: return encodeConnectionSecure(channel, fields); case 655381: return encodeConnectionSecureOk(channel, fields); case 655390: return encodeConnectionTune(channel, fields); case 655391: return encodeConnectionTuneOk(channel, fields); case 655400: return encodeConnectionOpen(channel, fields); case 655401: return encodeConnectionOpenOk(channel, fields); case 655410: return encodeConnectionClose(channel, fields); case 655411: return encodeConnectionCloseOk(channel, fields); case 655420: return encodeConnectionBlocked(channel, fields); case 655421: return encodeConnectionUnblocked(channel, fields); case 655430: return encodeConnectionUpdateSecret(channel, fields); case 655431: return encodeConnectionUpdateSecretOk(channel, fields); case 1310730: return encodeChannelOpen(channel, fields); case 1310731: return encodeChannelOpenOk(channel, fields); case 1310740: return encodeChannelFlow(channel, fields); case 1310741: return encodeChannelFlowOk(channel, fields); case 1310760: return encodeChannelClose(channel, fields); case 1310761: return encodeChannelCloseOk(channel, fields); case 1966090: return encodeAccessRequest(channel, fields); case 1966091: return encodeAccessRequestOk(channel, fields); case 2621450: return encodeExchangeDeclare(channel, fields); case 2621451: return encodeExchangeDeclareOk(channel, fields); case 2621460: return encodeExchangeDelete(channel, fields); case 2621461: return encodeExchangeDeleteOk(channel, fields); case 2621470: return encodeExchangeBind(channel, fields); case 2621471: return encodeExchangeBindOk(channel, fields); case 2621480: return encodeExchangeUnbind(channel, fields); case 2621491: return encodeExchangeUnbindOk(channel, fields); case 3276810: return encodeQueueDeclare(channel, fields); case 3276811: return encodeQueueDeclareOk(channel, fields); case 3276820: return encodeQueueBind(channel, fields); case 3276821: return encodeQueueBindOk(channel, fields); case 3276830: return encodeQueuePurge(channel, fields); case 3276831: return encodeQueuePurgeOk(channel, fields); case 3276840: return encodeQueueDelete(channel, fields); case 3276841: return encodeQueueDeleteOk(channel, fields); case 3276850: return encodeQueueUnbind(channel, fields); case 3276851: return encodeQueueUnbindOk(channel, fields); case 5898250: return encodeTxSelect(channel, fields); case 5898251: return encodeTxSelectOk(channel, fields); case 5898260: return encodeTxCommit(channel, fields); case 5898261: return encodeTxCommitOk(channel, fields); case 5898270: return encodeTxRollback(channel, fields); case 5898271: return encodeTxRollbackOk(channel, fields); case 5570570: return encodeConfirmSelect(channel, fields); case 5570571: return encodeConfirmSelectOk(channel, fields); default: throw new Error('Unknown class/method ID'); } }; module.exports.encodeProperties = function (id, channel, size, fields) { switch (id) { case 60: return encodeBasicProperties(channel, size, fields); default: throw new Error('Unknown class/properties ID'); } }; module.exports.info = function (id) { switch (id) { case 3932170: return methodInfoBasicQos; case 3932171: return methodInfoBasicQosOk; case 3932180: return methodInfoBasicConsume; case 3932181: return methodInfoBasicConsumeOk; case 3932190: return methodInfoBasicCancel; case 3932191: return methodInfoBasicCancelOk; case 3932200: return methodInfoBasicPublish; case 3932210: return methodInfoBasicReturn; case 3932220: return methodInfoBasicDeliver; case 3932230: return methodInfoBasicGet; case 3932231: return methodInfoBasicGetOk; case 3932232: return methodInfoBasicGetEmpty; case 3932240: return methodInfoBasicAck; case 3932250: return methodInfoBasicReject; case 3932260: return methodInfoBasicRecoverAsync; case 3932270: return methodInfoBasicRecover; case 3932271: return methodInfoBasicRecoverOk; case 3932280: return methodInfoBasicNack; case 655370: return methodInfoConnectionStart; case 655371: return methodInfoConnectionStartOk; case 655380: return methodInfoConnectionSecure; case 655381: return methodInfoConnectionSecureOk; case 655390: return methodInfoConnectionTune; case 655391: return methodInfoConnectionTuneOk; case 655400: return methodInfoConnectionOpen; case 655401: return methodInfoConnectionOpenOk; case 655410: return methodInfoConnectionClose; case 655411: return methodInfoConnectionCloseOk; case 655420: return methodInfoConnectionBlocked; case 655421: return methodInfoConnectionUnblocked; case 655430: return methodInfoConnectionUpdateSecret; case 655431: return methodInfoConnectionUpdateSecretOk; case 1310730: return methodInfoChannelOpen; case 1310731: return methodInfoChannelOpenOk; case 1310740: return methodInfoChannelFlow; case 1310741: return methodInfoChannelFlowOk; case 1310760: return methodInfoChannelClose; case 1310761: return methodInfoChannelCloseOk; case 1966090: return methodInfoAccessRequest; case 1966091: return methodInfoAccessRequestOk; case 2621450: return methodInfoExchangeDeclare; case 2621451: return methodInfoExchangeDeclareOk; case 2621460: return methodInfoExchangeDelete; case 2621461: return methodInfoExchangeDeleteOk; case 2621470: return methodInfoExchangeBind; case 2621471: return methodInfoExchangeBindOk; case 2621480: return methodInfoExchangeUnbind; case 2621491: return methodInfoExchangeUnbindOk; case 3276810: return methodInfoQueueDeclare; case 3276811: return methodInfoQueueDeclareOk; case 3276820: return methodInfoQueueBind; case 3276821: return methodInfoQueueBindOk; case 3276830: return methodInfoQueuePurge; case 3276831: return methodInfoQueuePurgeOk; case 3276840: return methodInfoQueueDelete; case 3276841: return methodInfoQueueDeleteOk; case 3276850: return methodInfoQueueUnbind; case 3276851: return methodInfoQueueUnbindOk; case 5898250: return methodInfoTxSelect; case 5898251: return methodInfoTxSelectOk; case 5898260: return methodInfoTxCommit; case 5898261: return methodInfoTxCommitOk; case 5898270: return methodInfoTxRollback; case 5898271: return methodInfoTxRollbackOk; case 5570570: return methodInfoConfirmSelect; case 5570571: return methodInfoConfirmSelectOk; case 60: return propertiesInfoBasicProperties; default: throw new Error('Unknown class/method ID'); } }; module.exports.BasicQos = 3932170; var methodInfoBasicQos = (module.exports.methodInfoBasicQos = { id: 3932170, classId: 60, methodId: 10, name: 'BasicQos', args: [ { type: 'long', name: 'prefetchSize', default: 0, }, { type: 'short', name: 'prefetchCount', default: 0, }, { type: 'bit', name: 'global', default: !1, }, ], }); module.exports.BasicQosOk = 3932171; var methodInfoBasicQosOk = (module.exports.methodInfoBasicQosOk = { id: 3932171, classId: 60, methodId: 11, name: 'BasicQosOk', args: [], }); module.exports.BasicConsume = 3932180; var methodInfoBasicConsume = (module.exports.methodInfoBasicConsume = { id: 3932180, classId: 60, methodId: 20, name: 'BasicConsume', args: [ { type: 'short', name: 'ticket', default: 0, }, { type: 'shortstr', name: 'queue', default: '', }, { type: 'shortstr', name: 'consumerTag', default: '', }, { type: 'bit', name: 'noLocal', default: !1, }, { type: 'bit', name: 'noAck', default: !1, }, { type: 'bit', name: 'exclusive', default: !1, }, { type: 'bit', name: 'nowait', default: !1, }, { type: 'table', name: 'arguments', default: {}, }, ], }); module.exports.BasicConsumeOk = 3932181; var methodInfoBasicConsumeOk = (module.exports.methodInfoBasicConsumeOk = { id: 3932181, classId: 60, methodId: 21, name: 'BasicConsumeOk', args: [ { type: 'shortstr', name: 'consumerTag', }, ], }); module.exports.BasicCancel = 3932190; var methodInfoBasicCancel = (module.exports.methodInfoBasicCancel = { id: 3932190, classId: 60, methodId: 30, name: 'BasicCancel', args: [ { type: 'shortstr', name: 'consumerTag', }, { type: 'bit', name: 'nowait', default: !1, }, ], }); module.exports.BasicCancelOk = 3932191; var methodInfoBasicCancelOk = (module.exports.methodInfoBasicCancelOk = { id: 3932191, classId: 60, methodId: 31, name: 'BasicCancelOk', args: [ { type: 'shortstr', name: 'consumerTag', }, ], }); module.exports.BasicPublish = 3932200; var methodInfoBasicPublish = (module.exports.methodInfoBasicPublish = { id: 3932200, classId: 60, methodId: 40, name: 'BasicPublish', args: [ { type: 'short', name: 'ticket', default: 0, }, { type: 'shortstr', name: 'exchange', default: '', }, { type: 'shortstr', name: 'routingKey', default: '', }, { type: 'bit', name: 'mandatory', default: !1, }, { type: 'bit', name: 'immediate', default: !1, }, ], }); module.exports.BasicReturn = 3932210; var methodInfoBasicReturn = (module.exports.methodInfoBasicReturn = { id: 3932210, classId: 60, methodId: 50, name: 'BasicReturn', args: [ { type: 'short', name: 'replyCode', }, { type: 'shortstr', name: 'replyText', default: '', }, { type: 'shortstr', name: 'exchange', }, { type: 'shortstr', name: 'routingKey', }, ], }); module.exports.BasicDeliver = 3932220; var methodInfoBasicDeliver = (module.exports.methodInfoBasicDeliver = { id: 3932220, classId: 60, methodId: 60, name: 'BasicDeliver', args: [ { type: 'shortstr', name: 'consumerTag', }, { type: 'longlong', name: 'deliveryTag', }, { type: 'bit', name: 'redelivered', default: !1, }, { type: 'shortstr', name: 'exchange', }, { type: 'shortstr', name: 'routingKey', }, ], }); module.exports.BasicGet = 3932230; var methodInfoBasicGet = (module.exports.methodInfoBasicGet = { id: 3932230, classId: 60, methodId: 70, name: 'BasicGet', args: [ { type: 'short', name: 'ticket', default: 0, }, { type: 'shortstr', name: 'queue', default: '', }, { type: 'bit', name: 'noAck', default: !1, }, ], }); module.exports.BasicGetOk = 3932231; var methodInfoBasicGetOk = (module.exports.methodInfoBasicGetOk = { id: 3932231, classId: 60, methodId: 71, name: 'BasicGetOk', args: [ { type: 'longlong', name: 'deliveryTag', }, { type: 'bit', name: 'redelivered', default: !1, }, { type: 'shortstr', name: 'exchange', }, { type: 'shortstr', name: 'routingKey', }, { type: 'long', name: 'messageCount', }, ], }); module.exports.BasicGetEmpty = 3932232; var methodInfoBasicGetEmpty = (module.exports.methodInfoBasicGetEmpty = { id: 3932232, classId: 60, methodId: 72, name: 'BasicGetEmpty', args: [ { type: 'shortstr', name: 'clusterId', default: '', }, ], }); module.exports.BasicAck = 3932240; var methodInfoBasicAck = (module.exports.methodInfoBasicAck = { id: 3932240, classId: 60, methodId: 80, name: 'BasicAck', args: [ { type: 'longlong', name: 'deliveryTag', default: 0, }, { type: 'bit', name: 'multiple', default: !1, }, ], }); module.exports.BasicReject = 3932250; var methodInfoBasicReject = (module.exports.methodInfoBasicReject = { id: 3932250, classId: 60, methodId: 90, name: 'BasicReject', args: [ { type: 'longlong', name: 'deliveryTag', }, { type: 'bit', name: 'requeue', default: !0, }, ], }); module.exports.BasicRecoverAsync = 3932260; var methodInfoBasicRecoverAsync = (module.exports.methodInfoBasicRecoverAsync = { id: 3932260, classId: 60, methodId: 100, name: 'BasicRecoverAsync', args: [ { type: 'bit', name: 'requeue', default: !1, }, ], }); module.exports.BasicRecover = 3932270; var methodInfoBasicRecover = (module.exports.methodInfoBasicRecover = { id: 3932270, classId: 60, methodId: 110, name: 'BasicRecover', args: [ { type: 'bit', name: 'requeue', default: !1, }, ], }); module.exports.BasicRecoverOk = 3932271; var methodInfoBasicRecoverOk = (module.exports.methodInfoBasicRecoverOk = { id: 3932271, classId: 60, methodId: 111, name: 'BasicRecoverOk', args: [], }); module.exports.BasicNack = 3932280; var methodInfoBasicNack = (module.exports.methodInfoBasicNack = { id: 3932280, classId: 60, methodId: 120, name: 'BasicNack', args: [ { type: 'longlong', name: 'deliveryTag', default: 0, }, { type: 'bit', name: 'multiple', default: !1, }, { type: 'bit', name: 'requeue', default: !0, }, ], }); module.exports.ConnectionStart = 655370; var methodInfoConnectionStart = (module.exports.methodInfoConnectionStart = { id: 655370, classId: 10, methodId: 10, name: 'ConnectionStart', args: [ { type: 'octet', name: 'versionMajor', default: 0, }, { type: 'octet', name: 'versionMinor', default: 9, }, { type: 'table', name: 'serverProperties', }, { type: 'longstr', name: 'mechanisms', default: 'PLAIN', }, { type: 'longstr', name: 'locales', default: 'en_US', }, ], }); module.exports.ConnectionStartOk = 655371; var methodInfoConnectionStartOk = (module.exports.methodInfoConnectionStartOk = { id: 655371, classId: 10, methodId: 11, name: 'ConnectionStartOk', args: [ { type: 'table', name: 'clientProperties', }, { type: 'shortstr', name: 'mechanism', default: 'PLAIN', }, { type: 'longstr', name: 'response', }, { type: 'shortstr', name: 'locale', default: 'en_US', }, ], }); module.exports.ConnectionSecure = 655380; var methodInfoConnectionSecure = (module.exports.methodInfoConnectionSecure = { id: 655380, classId: 10, methodId: 20, name: 'ConnectionSecure', args: [ { type: 'longstr', name: 'challenge', }, ], }); module.exports.ConnectionSecureOk = 655381; var methodInfoConnectionSecureOk = (module.exports.methodInfoConnectionSecureOk = { id: 655381, classId: 10, methodId: 21, name: 'ConnectionSecureOk', args: [ { type: 'longstr', name: 'response', }, ], }); module.exports.ConnectionTune = 655390; var methodInfoConnectionTune = (module.exports.methodInfoConnectionTune = { id: 655390, classId: 10, methodId: 30, name: 'ConnectionTune', args: [ { type: 'short', name: 'channelMax', default: 0, }, { type: 'long', name: 'frameMax', default: 0, }, { type: 'short', name: 'heartbeat', default: 0, }, ], }); module.exports.ConnectionTuneOk = 655391; var methodInfoConnectionTuneOk = (module.exports.methodInfoConnectionTuneOk = { id: 655391, classId: 10, methodId: 31, name: 'ConnectionTuneOk', args: [ { type: 'short', name: 'channelMax', default: 0, }, { type: 'long', name: 'frameMax', default: 0, }, { type: 'short', name: 'heartbeat', default: 0, }, ], }); module.exports.ConnectionOpen = 655400; var methodInfoConnectionOpen = (module.exports.methodInfoConnectionOpen = { id: 655400, classId: 10, methodId: 40, name: 'ConnectionOpen', args: [ { type: 'shortstr', name: 'virtualHost', default: '/', }, { type: 'shortstr', name: 'capabilities', default: '', }, { type: 'bit', name: 'insist', default: !1, }, ], }); module.exports.ConnectionOpenOk = 655401; var methodInfoConnectionOpenOk = (module.exports.methodInfoConnectionOpenOk = { id: 655401, classId: 10, methodId: 41, name: 'ConnectionOpenOk', args: [ { type: 'shortstr', name: 'knownHosts', default: '', }, ], }); module.exports.ConnectionClose = 655410; var methodInfoConnectionClose = (module.exports.methodInfoConnectionClose = { id: 655410, classId: 10, methodId: 50, name: 'ConnectionClose', args: [ { type: 'short', name: 'replyCode', }, { type: 'shortstr', name: 'replyText', default: '', }, { type: 'short', name: 'classId', }, { type: 'short', name: 'methodId', }, ], }); module.exports.ConnectionCloseOk = 655411; var methodInfoConnectionCloseOk = (module.exports.methodInfoConnectionCloseOk = { id: 655411, classId: 10, methodId: 51, name: 'ConnectionCloseOk', args: [], }); module.exports.ConnectionBlocked = 655420; var methodInfoConnectionBlocked = (module.exports.methodInfoConnectionBlocked = { id: 655420, classId: 10, methodId: 60, name: 'ConnectionBlocked', args: [ { type: 'shortstr', name: 'reason', default: '', }, ], }); module.exports.ConnectionUnblocked = 655421; var methodInfoConnectionUnblocked = (module.exports.methodInfoConnectionUnblocked = { id: 655421, classId: 10, methodId: 61, name: 'ConnectionUnblocked', args: [], }); module.exports.ConnectionUpdateSecret = 655430; var methodInfoConnectionUpdateSecret = (module.exports.methodInfoConnectionUpdateSecret = { id: 655430, classId: 10, methodId: 70, name: 'ConnectionUpdateSecret', args: [ { type: 'longstr', name: 'newSecret', }, { type: 'shortstr', name: 'reason', }, ], }); module.exports.ConnectionUpdateSecretOk = 655431; var methodInfoConnectionUpdateSecretOk = (module.exports.methodInfoConnectionUpdateSecretOk = { id: 655431, classId: 10, methodId: 71, name: 'ConnectionUpdateSecretOk', args: [], }); module.exports.ChannelOpen = 1310730; var methodInfoChannelOpen = (module.exports.methodInfoChannelOpen = { id: 1310730, classId: 20, methodId: 10, name: 'ChannelOpen', args: [ { type: 'shortstr', name: 'outOfBand', default: '', }, ], }); module.exports.ChannelOpenOk = 1310731; var methodInfoChannelOpenOk = (module.exports.methodInfoChannelOpenOk = { id: 1310731, classId: 20, methodId: 11, name: 'ChannelOpenOk', args: [ { type: 'longstr', name: 'channelId', default: '', }, ], }); module.exports.ChannelFlow = 1310740; var methodInfoChannelFlow = (module.exports.methodInfoChannelFlow = { id: 1310740, classId: 20, methodId: 20, name: 'ChannelFlow', args: [ { type: 'bit', name: 'active', }, ], }); module.exports.ChannelFlowOk = 1310741; var methodInfoChannelFlowOk = (module.exports.methodInfoChannelFlowOk = { id: 1310741, classId: 20, methodId: 21, name: 'ChannelFlowOk', args: [ { type: 'bit', name: 'active', }, ], }); module.exports.ChannelClose = 1310760; var methodInfoChannelClose = (module.exports.methodInfoChannelClose = { id: 1310760, classId: 20, methodId: 40, name: 'ChannelClose', args: [ { type: 'short', name: 'replyCode', }, { type: 'shortstr', name: 'replyText', default: '', }, { type: 'short', name: 'classId', }, { type: 'short', name: 'methodId', }, ], }); module.exports.ChannelCloseOk = 1310761; var methodInfoChannelCloseOk = (module.exports.methodInfoChannelCloseOk = { id: 1310761, classId: 20, methodId: 41, name: 'ChannelCloseOk', args: [], }); module.exports.AccessRequest = 1966090; var methodInfoAccessRequest = (module.exports.methodInfoAccessRequest = { id: 1966090, classId: 30, methodId: 10, name: 'AccessRequest', args: [ { type: 'shortstr', name: 'realm', default: '/data', }, { type: 'bit', name: 'exclusive', default: !1, }, { type: 'bit', name: 'passive', default: !0, }, { type: 'bit', name: 'active', default: !0, }, { type: 'bit', name: 'write', default: !0, }, { type: 'bit', name: 'read', default: !0, }, ], }); module.exports.AccessRequestOk = 1966091; var methodInfoAccessRequestOk = (module.exports.methodInfoAccessRequestOk = { id: 1966091, classId: 30, methodId: 11, name: 'AccessRequestOk', args: [ { type: 'short', name: 'ticket', default: 1, }, ], }); module.exports.ExchangeDeclare = 2621450; var methodInfoExchangeDeclare = (module.exports.methodInfoExchangeDeclare = { id: 2621450, classId: 40, methodId: 10, name: 'ExchangeDeclare', args: [ { type: 'short', name: 'ticket', default: 0, }, { type: 'shortstr', name: 'exchange', }, { type: 'shortstr', name: 'type', default: 'direct', }, { type: 'bit', name: 'passive', default: !1, }, { type: 'bit', name: 'durable', default: !1, }, { type: 'bit', name: 'autoDelete', default: !1, }, { type: 'bit', name: 'internal', default: !1, }, { type: 'bit', name: 'nowait', default: !1, }, { type: 'table', name: 'arguments', default: {}, }, ], }); module.exports.ExchangeDeclareOk = 2621451; var methodInfoExchangeDeclareOk = (module.exports.methodInfoExchangeDeclareOk = { id: 2621451, classId: 40, methodId: 11, name: 'ExchangeDeclareOk', args: [], }); module.exports.ExchangeDelete = 2621460; var methodInfoExchangeDelete = (module.exports.methodInfoExchangeDelete = { id: 2621460, classId: 40, methodId: 20, name: 'ExchangeDelete', args: [ { type: 'short', name: 'ticket', default: 0, }, { type: 'shortstr', name: 'exchange', }, { type: 'bit', name: 'ifUnused', default: !1, }, { type: 'bit', name: 'nowait', default: !1, }, ], }); module.exports.ExchangeDeleteOk = 2621461; var methodInfoExchangeDeleteOk = (module.exports.methodInfoExchangeDeleteOk = { id: 2621461, classId: 40, methodId: 21, name: 'ExchangeDeleteOk', args: [], }); module.exports.ExchangeBind = 2621470; var methodInfoExchangeBind = (module.exports.methodInfoExchangeBind = { id: 2621470, classId: 40, methodId: 30, name: 'ExchangeBind', args: [ { type: 'short', name: 'ticket', default: 0, }, { type: 'shortstr', name: 'destination', }, { type: 'shortstr', name: 'source', }, { type: 'shortstr', name: 'routingKey', default: '', }, { type: 'bit', name: 'nowait', default: !1, }, { type: 'table', name: 'arguments', default: {}, }, ], }); module.exports.ExchangeBindOk = 2621471; var methodInfoExchangeBindOk = (module.exports.methodInfoExchangeBindOk = { id: 2621471, classId: 40, methodId: 31, name: 'ExchangeBindOk', args: [], }); module.exports.ExchangeUnbind = 2621480; var methodInfoExchangeUnbind = (module.exports.methodInfoExchangeUnbind = { id: 2621480, classId: 40, methodId: 40, name: 'ExchangeUnbind', args: [ { type: 'short', name: 'ticket', default: 0, }, { type: 'shortstr', name: 'destination', }, { type: 'shortstr', name: 'source', }, { type: 'shortstr', name: 'routingKey', default: '', }, { type: 'bit', name: 'nowait', default: !1, }, { type: 'table', name: 'arguments', default: {}, }, ], }); module.exports.ExchangeUnbindOk = 2621491; var methodInfoExchangeUnbindOk = (module.exports.methodInfoExchangeUnbindOk = { id: 2621491, classId: 40, methodId: 51, name: 'ExchangeUnbindOk', args: [], }); module.exports.QueueDeclare = 3276810; var methodInfoQueueDeclare = (module.exports.methodInfoQueueDeclare = { id: 3276810, classId: 50, methodId: 10, name: 'QueueDeclare', args: [ { type: 'short', name: 'ticket', default: 0, }, { type: 'shortstr', name: 'queue', default: '', }, { type: 'bit', name: 'passive', default: !1, }, { type: 'bit', name: 'durable', default: !1, }, { type: 'bit', name: 'exclusive', default: !1, }, { type: 'bit', name: 'autoDelete', default: !1, }, { type: 'bit', name: 'nowait', default: !1, }, { type: 'table', name: 'arguments', default: {}, }, ], }); module.exports.QueueDeclareOk = 3276811; var methodInfoQueueDeclareOk = (module.exports.methodInfoQueueDeclareOk = { id: 3276811, classId: 50, methodId: 11, name: 'QueueDeclareOk', args: [ { type: 'shortstr', name: 'queue', }, { type: 'long', name: 'messageCount', }, { type: 'long', name: 'consumerCount', }, ], }); module.exports.QueueBind = 3276820; var methodInfoQueueBind = (module.exports.methodInfoQueueBind = { id: 3276820, classId: 50, methodId: 20, name: 'QueueBind', args: [ { type: 'short', name: 'ticket', default: 0, }, { type: 'shortstr', name: 'queue', default: '', }, { type: 'shortstr', name: 'exchange', }, { type: 'shortstr', name: 'routingKey', default: '', }, { type: 'bit', name: 'nowait', default: !1, }, { type: 'table', name: 'arguments', default: {}, }, ], }); module.exports.QueueBindOk = 3276821; var methodInfoQueueBindOk = (module.exports.methodInfoQueueBindOk = { id: 3276821, classId: 50, methodId: 21, name: 'QueueBindOk', args: [], }); module.exports.QueuePurge = 3276830; var methodInfoQueuePurge = (module.exports.methodInfoQueuePurge = { id: 3276830, classId: 50, methodId: 30, name: 'QueuePurge', args: [ { type: 'short', name: 'ticket', default: 0, }, { type: 'shortstr', name: 'queue', default: '', }, { type: 'bit', name: 'nowait', default: !1, }, ], }); module.exports.QueuePurgeOk = 3276831; var methodInfoQueuePurgeOk = (module.exports.methodInfoQueuePurgeOk = { id: 3276831, classId: 50, methodId: 31, name: 'QueuePurgeOk', args: [ { type: 'long', name: 'messageCount', }, ], }); module.exports.QueueDelete = 3276840; var methodInfoQueueDelete = (module.exports.methodInfoQueueDelete = { id: 3276840, classId: 50, methodId: 40, name: 'QueueDelete', args: [ { type: 'short', name: 'ticket', default: 0, }, { type: 'shortstr', name: 'queue', default: '', }, { type: 'bit', name: 'ifUnused', default: !1, }, { type: 'bit', name: 'ifEmpty', default: !1, }, { type: 'bit', name: 'nowait', default: !1, }, ], }); module.exports.QueueDeleteOk = 3276841; var methodInfoQueueDeleteOk = (module.exports.methodInfoQueueDeleteOk = { id: 3276841, classId: 50, methodId: 41, name: 'QueueDeleteOk', args: [ { type: 'long', name: 'messageCount', }, ], }); module.exports.QueueUnbind = 3276850; var methodInfoQueueUnbind = (module.exports.methodInfoQueueUnbind = { id: 3276850, classId: 50, methodId: 50, name: 'QueueUnbind', args: [ { type: 'short', name: 'ticket', default: 0, }, { type: 'shortstr', name: 'queue', default: '', }, { type: 'shortstr', name: 'exchange', }, { type: 'shortstr', name: 'routingKey', default: '', }, { type: 'table', name: 'arguments', default: {}, }, ], }); module.exports.QueueUnbindOk = 3276851; var methodInfoQueueUnbindOk = (module.exports.methodInfoQueueUnbindOk = { id: 3276851, classId: 50, methodId: 51, name: 'QueueUnbindOk', args: [], }); module.exports.TxSelect = 5898250; var methodInfoTxSelect = (module.exports.methodInfoTxSelect = { id: 5898250, classId: 90, methodId: 10, name: 'TxSelect', args: [], }); module.exports.TxSelectOk = 5898251; var methodInfoTxSelectOk = (module.exports.methodInfoTxSelectOk = { id: 5898251, classId: 90, methodId: 11, name: 'TxSelectOk', args: [], }); module.exports.TxCommit = 5898260; var methodInfoTxCommit = (module.exports.methodInfoTxCommit = { id: 5898260, classId: 90, methodId: 20, name: 'TxCommit', args: [], }); module.exports.TxCommitOk = 5898261; var methodInfoTxCommitOk = (module.exports.methodInfoTxCommitOk = { id: 5898261, classId: 90, methodId: 21, name: 'TxCommitOk', args: [], }); module.exports.TxRollback = 5898270; var methodInfoTxRollback = (module.exports.methodInfoTxRollback = { id: 5898270, classId: 90, methodId: 30, name: 'TxRollback', args: [], }); module.exports.TxRollbackOk = 5898271; var methodInfoTxRollbackOk = (module.exports.methodInfoTxRollbackOk = { id: 5898271, classId: 90, methodId: 31, name: 'TxRollbackOk', args: [], }); module.exports.ConfirmSelect = 5570570; var methodInfoConfirmSelect = (module.exports.methodInfoConfirmSelect = { id: 5570570, classId: 85, methodId: 10, name: 'ConfirmSelect', args: [ { type: 'bit', name: 'nowait', default: !1, }, ], }); module.exports.ConfirmSelectOk = 5570571; var methodInfoConfirmSelectOk = (module.exports.methodInfoConfirmSelectOk = { id: 5570571, classId: 85, methodId: 11, name: 'ConfirmSelectOk', args: [], }); module.exports.BasicProperties = 60; var propertiesInfoBasicProperties = (module.exports.propertiesInfoBasicProperties = { id: 60, name: 'BasicProperties', args: [ { type: 'shortstr', name: 'contentType', }, { type: 'shortstr', name: 'contentEncoding', }, { type: 'table', name: 'headers', }, { type: 'octet', name: 'deliveryMode', }, { type: 'octet', name: 'priority', }, { type: 'shortstr', name: 'correlationId', }, { type: 'shortstr', name: 'replyTo', }, { type: 'shortstr', name: 'expiration', }, { type: 'shortstr', name: 'messageId', }, { type: 'timestamp', name: 'timestamp', }, { type: 'shortstr', name: 'type', }, { type: 'shortstr', name: 'userId', }, { type: 'shortstr', name: 'appId', }, { type: 'shortstr', name: 'clusterId', }, ], }); amqp-node-amqplib-263c8a3/lib/error.js000066400000000000000000000012431520012327700176050ustar00rootroot00000000000000const inherits = require('node:util').inherits; function trimStack(stack, num) { return stack && stack.split('\n').slice(num).join('\n'); } function IllegalOperationError(msg, stack) { const tmp = new Error(); this.message = msg; this.stack = `${this.toString()}\n${trimStack(tmp.stack, 2)}`; this.stackAtStateChange = stack; } inherits(IllegalOperationError, Error); IllegalOperationError.prototype.name = 'IllegalOperationError'; function stackCapture(reason) { const e = new Error(); return `Stack capture: ${reason}\n${trimStack(e.stack, 2)}`; } module.exports.IllegalOperationError = IllegalOperationError; module.exports.stackCapture = stackCapture; amqp-node-amqplib-263c8a3/lib/format.js000066400000000000000000000014011520012327700177400ustar00rootroot00000000000000const defs = require('./defs'); const format = require('node:util').format; const HEARTBEAT = require('./frame').HEARTBEAT; module.exports.closeMessage = (close) => { const code = close.fields.replyCode; return format('%d (%s) with message "%s"', code, defs.constant_strs[code], close.fields.replyText); }; module.exports.methodName = (id) => defs.info(id).name; module.exports.inspect = (frame, showFields) => { if (frame === HEARTBEAT) { return ''; } else if (!frame.id) { return format('', frame.channel, frame.size); } else { const info = defs.info(frame.id); return format('<%s channel:%d%s>', info.name, frame.channel, showFields ? ` ${JSON.stringify(frame.fields, undefined, 2)}` : ''); } }; amqp-node-amqplib-263c8a3/lib/frame.js000066400000000000000000000075021520012327700175520ustar00rootroot00000000000000const defs = require('./defs'); const constants = defs.constants; const decode = defs.decode; module.exports.PROTOCOL_HEADER = `AMQP${String.fromCharCode(0, 0, 9, 1)}`; /* Frame format: 0 1 3 7 size+7 size+8 +------+---------+-------------+ +------------+ +-----------+ | type | channel | size | | payload | | frame-end | +------+---------+-------------+ +------------+ +-----------+ octet short long size octets octet In general I want to know those first three things straight away, so I can discard frames early. */ // framing constants const FRAME_METHOD = constants.FRAME_METHOD; const FRAME_HEARTBEAT = constants.FRAME_HEARTBEAT; const FRAME_HEADER = constants.FRAME_HEADER; const FRAME_BODY = constants.FRAME_BODY; const FRAME_END = constants.FRAME_END; // expected byte sizes for frame parts const TYPE_BYTES = 1; const CHANNEL_BYTES = 2; const SIZE_BYTES = 4; const FRAME_HEADER_BYTES = TYPE_BYTES + CHANNEL_BYTES + SIZE_BYTES; const FRAME_END_BYTES = 1; /** * @typedef {{ * type: number, * channel: number, * size: number, * payload: Buffer, * rest: Buffer * }} FrameStructure */ function readInt64BE(buffer, offset) { return Number(buffer.readBigInt64BE(offset)); } // %%% TESTME possibly better to cons the first bit and write the // second directly, in the absence of IO lists /** * Make a frame header * @arg { number } channel * @arg { Buffer } payload */ module.exports.makeBodyFrame = (channel, payload) => { const frameSize = FRAME_HEADER_BYTES + payload.length + FRAME_END_BYTES; const frame = Buffer.alloc(frameSize); let offset = 0; offset = frame.writeUInt8(FRAME_BODY, offset); offset = frame.writeUInt16BE(channel, offset); offset = frame.writeInt32BE(payload.length, offset); payload.copy(frame, offset); offset += payload.length; frame.writeUInt8(FRAME_END, offset); return frame; }; /** * Parse an AMQP frame * @arg { Buffer } bin * @arg { number } max * @returns { FrameStructure | boolean } */ function parseFrame(bin) { if (bin.length < FRAME_HEADER_BYTES) { return false; } const type = bin.readUInt8(0); const channel = bin.readUInt16BE(1); const size = bin.readUInt32BE(3); const totalSize = FRAME_HEADER_BYTES + size + FRAME_END_BYTES; if (bin.length < totalSize) { return false; } const frameEnd = bin.readUInt8(FRAME_HEADER_BYTES + size); if (frameEnd !== FRAME_END) { throw new Error('Invalid frame'); } return { type, channel, size, payload: bin.subarray(FRAME_HEADER_BYTES, FRAME_HEADER_BYTES + size), rest: bin.subarray(totalSize), }; } module.exports.parseFrame = parseFrame; const HEARTBEAT = { channel: 0 }; /** * Decode AMQP frame into JS object * @param { FrameStructure } frame * @returns */ module.exports.decodeFrame = (frame) => { const payload = frame.payload; const channel = frame.channel; switch (frame.type) { case FRAME_METHOD: { const id = payload.readUInt32BE(0); const args = payload.subarray(4); const fields = decode(id, args); return { id, channel, fields }; } case FRAME_HEADER: { const id = payload.readUInt16BE(0); // const weight = payload.readUInt16BE(2) const size = readInt64BE(payload, 4); const flagsAndfields = payload.subarray(12); const fields = decode(id, flagsAndfields); return { id, channel, size, fields }; } case FRAME_BODY: return { channel, content: payload }; case FRAME_HEARTBEAT: return HEARTBEAT; default: throw new Error(`Unknown frame type ${frame.type}`); } }; // encoded heartbeat module.exports.HEARTBEAT_BUF = Buffer.from([ constants.FRAME_HEARTBEAT, 0, 0, 0, 0, // size = 0 0, 0, // channel = 0 constants.FRAME_END, ]); module.exports.HEARTBEAT = HEARTBEAT; amqp-node-amqplib-263c8a3/lib/heartbeat.js000066400000000000000000000065131520012327700204200ustar00rootroot00000000000000// Heartbeats. In AMQP both clients and servers may expect a heartbeat // frame if there is no activity on the connection for a negotiated // period of time. If there's no activity for two such intervals, the // server or client is allowed to close the connection on the // presumption that the other party is dead. // // The client has two jobs here: the first is to send a heartbeat // frame if it's not sent any frames for a while, so that the server // doesn't think it's dead; the second is to check periodically that // it's seen activity from the server, and to advise if there doesn't // appear to have been any for over two intervals. // // Node.JS timers are a bit unreliable, in that they endeavour only to // fire at some indeterminate point *after* the given time (rather // gives the lie to 'realtime', dunnit). Because the scheduler is just // an event loop, it's quite easy to delay timers indefinitely by // reacting to some I/O with a lot of computation. // // To mitigate this I need a bit of creative interpretation: // // - I'll schedule a server activity check for every `interval`, and // check just how much time has passed. It will overshoot by at // least a small margin; modulo missing timer deadlines, it'll // notice between two and three intervals after activity actually // stops (otherwise, at some point after two intervals). // // - Every `interval / 2` I'll check that we've sent something since // the last check, and if not, send a heartbeat frame. If we're // really too busy to even run the check for two whole heartbeat // intervals, there must be a lot of I (but not O, at least not on // the connection), or computation, in which case perhaps it's best // the server cuts us off anyway. Why `interval / 2`? Because the // edge case is that the client sent a frame just after a // heartbeat, which would mean I only send one after almost two // intervals. (NB a heartbeat counts as a send, so it'll be checked // at least twice before sending another) // // This design is based largely on RabbitMQ's heartbeating: // https://github.com/rabbitmq/rabbitmq-common/blob/master/src/rabbit_heartbeat.erl // %% Yes, I could apply the same 'actually passage of time' thing to // %% send as well as to recv. const EventEmitter = require('node:events'); // Exported so that we can mess with it in tests module.exports.UNITS_TO_MS = 1000; class Heart extends EventEmitter { constructor(interval, checkSend, checkRecv) { super(); this.interval = interval; const intervalMs = interval * module.exports.UNITS_TO_MS; // Function#bind is my new best friend const beat = this.emit.bind(this, 'beat'); const timeout = this.emit.bind(this, 'timeout'); this.sendTimer = setInterval(this.runHeartbeat.bind(this, checkSend, beat), intervalMs / 2); // A timeout occurs if I see nothing for *two consecutive* intervals let recvMissed = 0; function missedTwo() { if (!checkRecv()) return ++recvMissed < 2; else { recvMissed = 0; return true; } } this.recvTimer = setInterval(this.runHeartbeat.bind(this, missedTwo, timeout), intervalMs); } clear() { clearInterval(this.sendTimer); clearInterval(this.recvTimer); } runHeartbeat(check, fail) { // Have we seen activity? if (!check()) fail(); } } module.exports.Heart = Heart; amqp-node-amqplib-263c8a3/lib/mux.js000066400000000000000000000070061520012327700172700ustar00rootroot00000000000000// A Mux is an object into which other readable streams may be piped; // it then writes 'packets' from the upstreams to the given // downstream. const assert = require('node:assert'); const schedule = typeof setImmediate === 'function' ? setImmediate : process.nextTick; class Mux { constructor(downstream) { this.newStreams = []; this.oldStreams = []; this.blocked = false; this.scheduledRead = false; this.out = downstream; downstream.on('drain', () => { this.blocked = false; this._readIncoming(); }); } // There are 2 states we can be in: // - waiting for outbound capacity, which will be signalled by a // - 'drain' event on the downstream; or, // - no packets to send, waiting for an inbound buffer to have // packets, which will be signalled by a 'readable' event // If we write all packets available whenever there is outbound // capacity, we will either run out of outbound capacity (`#write` // returns false), or run out of packets (all calls to an // `inbound.read()` have returned null). _readIncoming() { // We may be sent here speculatively, if an incoming stream has // become readable if (this.blocked) return; let accepting = true; const out = this.out; // Try to read a chunk from each stream in turn, until all streams // are empty, or we exhaust our ability to accept chunks. function roundrobin(streams) { while (accepting && streams.length > 0) { const stream = streams.shift(); const chunk = stream.read(); if (chunk !== null) { accepting = out.write(chunk); streams.push(stream); } } } roundrobin(this.newStreams); // Either we exhausted the new queues, or we ran out of capacity. If // we ran out of capacity, all the remaining new streams (i.e., // those with packets left) become old streams. This effectively // prioritises streams that keep their buffers close to empty over // those that are constantly near full. if (accepting) { // all new queues are exhausted, write as many as // we can from the old streams assert.equal(0, this.newStreams.length); roundrobin(this.oldStreams); } else { // ran out of room assert(this.newStreams.length > 0, 'Expect some new streams to remain'); Array.prototype.push.apply(this.oldStreams, this.newStreams); this.newStreams = []; } // We may have exhausted all the old queues, or run out of room; // either way, all we need to do is record whether we have capacity // or not, so any speculative reads will know this.blocked = !accepting; } _scheduleRead() { if (!this.scheduledRead) { schedule(() => { this.scheduledRead = false; this._readIncoming(); }); this.scheduledRead = true; } } pipeFrom(readable) { const self = this; function enqueue() { self.newStreams.push(readable); self._scheduleRead(); } function cleanup() { readable.removeListener('readable', enqueue); readable.removeListener('error', cleanup); readable.removeListener('end', cleanup); readable.removeListener('unpipeFrom', cleanupIfMe); } function cleanupIfMe(dest) { if (dest === self) cleanup(); } readable.on('unpipeFrom', cleanupIfMe); readable.on('end', cleanup); readable.on('error', cleanup); readable.on('readable', enqueue); } unpipeFrom(readable) { readable.emit('unpipeFrom', this); } } module.exports.Mux = Mux; amqp-node-amqplib-263c8a3/lib/properties.d.ts000066400000000000000000000103211520012327700211010ustar00rootroot00000000000000import * as tls from 'tls'; export namespace Replies { interface Empty {} interface AssertQueue { queue: string; messageCount: number; consumerCount: number; } interface PurgeQueue { messageCount: number; } interface DeleteQueue { messageCount: number; } interface AssertExchange { exchange: string; } interface Consume { consumerTag: string; } } export namespace Options { interface Connect { protocol?: string; hostname?: string; port?: number; username?: string; password?: string; locale?: string; frameMax?: number; heartbeat?: number; vhost?: string; channelMax?: number; credentials?: { mechanism: string; response(): Buffer; username?: string; password?: string; }; } interface AssertQueue { exclusive?: boolean; durable?: boolean; autoDelete?: boolean; arguments?: any; messageTtl?: number; expires?: number; deadLetterExchange?: string; deadLetterRoutingKey?: string; maxLength?: number; maxPriority?: number; overflow?: string; queueMode?: string; } interface DeleteQueue { ifUnused?: boolean; ifEmpty?: boolean; } interface AssertExchange { durable?: boolean; internal?: boolean; autoDelete?: boolean; alternateExchange?: string; arguments?: any; } interface DeleteExchange { ifUnused?: boolean; } interface Publish { expiration?: string | number; userId?: string; CC?: string | string[]; mandatory?: boolean; persistent?: boolean; deliveryMode?: boolean | number; BCC?: string | string[]; contentType?: string; contentEncoding?: string; headers?: any; priority?: number; correlationId?: string; replyTo?: string; messageId?: string; timestamp?: number; type?: string; appId?: string; } interface Consume { consumerTag?: string; noLocal?: boolean; noAck?: boolean; exclusive?: boolean; priority?: number; arguments?: any; } interface Get { noAck?: boolean; } } export interface SocketOptions extends tls.ConnectionOptions { noDelay?: boolean; timeout?: number; keepAlive?: boolean; keepAliveDelay?: number; clientProperties?: Record; credentials?: { mechanism: string; response(): Buffer; username?: string; password?: string; }; } export interface ChannelOptions { highWaterMark?: number; } export interface Message { content: Buffer; fields: MessageFields; properties: MessageProperties; } export interface GetMessage extends Message { fields: GetMessageFields; } export interface ConsumeMessage extends Message { fields: ConsumeMessageFields; } export interface CommonMessageFields { deliveryTag: number; redelivered: boolean; exchange: string; routingKey: string; } export interface MessageFields extends CommonMessageFields { messageCount?: number; consumerTag?: string; } export interface GetMessageFields extends CommonMessageFields { messageCount: number; } export interface ConsumeMessageFields extends CommonMessageFields { consumerTag: string; } export interface MessageProperties { contentType: any | undefined; contentEncoding: any | undefined; headers: MessagePropertyHeaders | undefined; deliveryMode: any | undefined; priority: any | undefined; correlationId: any | undefined; replyTo: any | undefined; expiration: any | undefined; messageId: any | undefined; timestamp: any | undefined; type: any | undefined; userId: any | undefined; appId: any | undefined; clusterId: any | undefined; } export interface MessagePropertyHeaders { 'x-first-death-exchange'?: string; 'x-first-death-queue'?: string; 'x-first-death-reason'?: string; 'x-death'?: XDeath[]; [key: string]: any; } export interface XDeath { count: number; reason: 'rejected' | 'expired' | 'maxlen'; queue: string; time: { '!': 'timestamp'; value: number; }; exchange: string; 'original-expiration'?: any; 'routing-keys': string[]; } export interface ServerProperties { host: string; product: string; version: string; platform: string; copyright?: string; information: string; [key: string]: string | undefined; } amqp-node-amqplib-263c8a3/lib/recovery.js000066400000000000000000000302211520012327700203100ustar00rootroot00000000000000const EventEmitter = require('node:events'); const DEFAULT_RECOVERY = { initialDelay: 100, maxDelay: 30000, factor: 2, jitter: 0.2, maxRetries: Infinity, setup: null, }; function splitConnectionOptions(connOptions) { if (!connOptions || typeof connOptions !== 'object') { return { connectionOptions: connOptions, recovery: null, }; } if (!Object.hasOwn(connOptions, 'recovery')) { return { connectionOptions: connOptions, recovery: null, }; } const connectionOptions = Object.assign({}, connOptions); const recovery = connectionOptions.recovery; delete connectionOptions.recovery; return { connectionOptions, recovery }; } function recoveryEnabled(recovery) { if (!recovery) return false; if (recovery === true) return true; return recovery.enabled !== false; } function toFiniteNumber(value, fallback) { const n = Number(value); return Number.isFinite(n) ? n : fallback; } function normaliseRecoveryOptions(recovery) { const source = recovery === true ? {} : recovery || {}; const initialDelay = Math.max(0, toFiniteNumber(source.initialDelay, DEFAULT_RECOVERY.initialDelay)); const maxDelay = Math.max(initialDelay, toFiniteNumber(source.maxDelay, DEFAULT_RECOVERY.maxDelay)); const factor = Math.max(1, toFiniteNumber(source.factor, DEFAULT_RECOVERY.factor)); const jitter = Math.min(1, Math.max(0, toFiniteNumber(source.jitter, DEFAULT_RECOVERY.jitter))); const parsedMaxRetries = source.maxRetries == null ? Infinity : toFiniteNumber(source.maxRetries, Infinity); return { initialDelay, maxDelay, factor, jitter, maxRetries: parsedMaxRetries < 0 ? 0 : parsedMaxRetries, setup: typeof source.setup === 'function' ? source.setup : null, }; } function toError(err, fallbackMessage) { if (err instanceof Error) return err; return new Error(err ? String(err) : fallbackMessage); } function calculateDelay(recovery, attempt) { const base = Math.min(recovery.maxDelay, recovery.initialDelay * recovery.factor ** (attempt - 1)); const jitterPart = base * recovery.jitter; const offset = jitterPart > 0 ? Math.random() * jitterPart * 2 - jitterPart : 0; return Math.max(0, Math.round(base + offset)); } function runSetup(setup, model) { if (!setup) return Promise.resolve(); return new Promise((resolve, reject) => { let settled = false; function done(err) { if (settled) return; settled = true; if (err) reject(toError(err, 'Recovery setup failed')); else resolve(); } try { if (setup.length >= 2) { setup(model, done); } else { Promise.resolve(setup(model)).then(() => done(), done); } } catch (err) { done(err); } }); } function makeCallback(promise, cb) { const callback = typeof cb === 'function' ? cb : () => {}; promise.then( (value) => callback(null, value), (err) => callback(toError(err, 'Operation failed')), ); } class RecoveringCore extends EventEmitter { constructor(openModel, recovery, adapters) { super(); this._openModel = openModel; this._recovery = normaliseRecoveryOptions(recovery); this._adapters = adapters; this._model = null; this._modelListeners = null; this._waiters = []; this._connecting = false; this._stopped = false; this._timer = null; this._attempt = 0; this._initialReady = false; this._initialWait = new Promise((resolve, reject) => { this._resolveInitial = resolve; this._rejectInitial = reject; }); this._connect(); } waitForConnect() { return this._initialWait; } async close() { if (this._stopped) return; this._stopped = true; if (this._timer) { clearTimeout(this._timer); this._timer = null; } const closedError = new Error('Connection closed'); this._rejectPendingWaiters(closedError); this._rejectInitialConnection(closedError); if (this._model) { const model = this._model; this._model = null; this._unbindModel(); await this._closeModelNoThrow(model); } } async createChannel(options) { const model = await this._waitForConnection(); return this._adapters.createChannel(model, options, false); } async createConfirmChannel(options) { const model = await this._waitForConnection(); return this._adapters.createChannel(model, options, true); } async updateSecret(newSecret, reason) { const model = await this._waitForConnection(); return this._adapters.updateSecret(model, newSecret, reason); } _resolveInitialConnection() { if (this._initialReady) return; this._initialReady = true; this._resolveInitial(); } _rejectInitialConnection(err) { if (this._initialReady) return; this._initialReady = true; this._rejectInitial(err); } _resolvePendingWaiters(model) { const waiters = this._waiters.splice(0); waiters.forEach((waiter) => { waiter.resolve(model); }); } _rejectPendingWaiters(err) { const waiters = this._waiters.splice(0); waiters.forEach((waiter) => { waiter.reject(err); }); } _waitForConnection() { if (this._model) return Promise.resolve(this._model); if (this._stopped) return Promise.reject(new Error('Connection closed')); return new Promise((resolve, reject) => { this._waiters.push({ resolve, reject }); }); } _bindModel(model) { const onClose = (maybeErr) => { if (this._model !== model) return; this._model = null; this._unbindModel(); if (this._stopped) return; const err = toError(maybeErr, 'Connection closed'); this.emit('disconnect', err); this._scheduleReconnect(err); }; const onBlocked = (reason) => this.emit('blocked', reason); const onUnblocked = () => this.emit('unblocked'); const onConnectionError = (err) => this.emit('error', err); const onUpdateSecretOk = () => this.emit('update-secret-ok'); const onHandlerError = (err, event) => this.emit('handler-error', err, event); model.on('close', onClose); model.on('blocked', onBlocked); model.on('unblocked', onUnblocked); model.on('error', onConnectionError); model.on('update-secret-ok', onUpdateSecretOk); model.on('handler-error', onHandlerError); this._modelListeners = { model, onClose, onBlocked, onUnblocked, onConnectionError, onUpdateSecretOk, onHandlerError, }; } _unbindModel() { if (!this._modelListeners) return; const { model, onClose, onBlocked, onUnblocked, onConnectionError, onUpdateSecretOk, onHandlerError } = this._modelListeners; model.removeListener('close', onClose); model.removeListener('blocked', onBlocked); model.removeListener('unblocked', onUnblocked); model.removeListener('error', onConnectionError); model.removeListener('update-secret-ok', onUpdateSecretOk); model.removeListener('handler-error', onHandlerError); model.on('error', () => {}); this._modelListeners = null; } async _connect() { if (this._stopped || this._connecting) return; this._connecting = true; let model; try { model = await this._openModel(); await runSetup(this._recovery.setup, model); if (this._stopped) { await this._closeModelNoThrow(model); return; } this._model = model; this._attempt = 0; this._bindModel(model); this._resolveInitialConnection(); this._resolvePendingWaiters(model); this.emit('connect', model); } catch (err) { const error = toError(err, 'Connection recovery failed'); if (model) await this._closeModelNoThrow(model); this.emit('connect-failed', error); this._scheduleReconnect(error); } finally { this._connecting = false; } } _scheduleReconnect(err) { if (this._stopped) return; if (this._timer) return; if (this._attempt >= this._recovery.maxRetries) { this._rejectInitialConnection(err); this._rejectPendingWaiters(err); this.emit('reconnect-failed', err); return; } const attempt = this._attempt + 1; const delay = calculateDelay(this._recovery, attempt); this._attempt = attempt; this.emit('reconnect-scheduled', { attempt, delay, error: err }); this._timer = setTimeout(() => { this._timer = null; this._connect(); }, delay); } _closeModelNoThrow(model) { return this._adapters.closeModel(model).catch(() => {}); } } const PROMISE_ADAPTERS = { closeModel(model) { return model.close(); }, createChannel(model, options, confirm) { return confirm ? model.createConfirmChannel(options) : model.createChannel(options); }, updateSecret(model, newSecret, reason) { return model.updateSecret(newSecret, reason); }, }; function callbackClose(model) { return new Promise((resolve, reject) => { model.close((err) => { if (err) reject(err); else resolve(); }); }); } function callbackCreateChannel(model, options, confirm) { return new Promise((resolve, reject) => { const method = confirm ? 'createConfirmChannel' : 'createChannel'; const cb = (err, channel) => { if (err) reject(err); else resolve(channel); }; if (options === undefined) model[method](cb); else model[method](options, cb); }); } function callbackUpdateSecret(model, newSecret, reason) { return new Promise((resolve, reject) => { model.updateSecret(newSecret, reason, (err, ok) => { if (err) reject(err); else resolve(ok); }); }); } const CALLBACK_ADAPTERS = { closeModel: callbackClose, createChannel: callbackCreateChannel, updateSecret: callbackUpdateSecret, }; function wireCommonEvents(source, target) { [ 'connect', 'disconnect', 'connect-failed', 'reconnect-scheduled', 'reconnect-failed', 'blocked', 'unblocked', 'error', 'update-secret-ok', 'handler-error', ].forEach((eventName) => { source.on(eventName, (...args) => target.emit(eventName, ...args)); }); } class RecoveringPromiseModel extends EventEmitter { constructor(openModel, recovery) { super(); this._core = new RecoveringCore(openModel, recovery, PROMISE_ADAPTERS); wireCommonEvents(this._core, this); } async waitForConnect() { await this._core.waitForConnect(); return this; } close() { return this._core.close(); } createChannel(options) { return this._core.createChannel(options); } createConfirmChannel(options) { return this._core.createConfirmChannel(options); } updateSecret(newSecret, reason) { return this._core.updateSecret(newSecret, reason); } } class RecoveringCallbackModel extends EventEmitter { constructor(openModel, recovery) { super(); this._core = new RecoveringCore(openModel, recovery, CALLBACK_ADAPTERS); wireCommonEvents(this._core, this); } waitForConnect(cb) { const callback = typeof cb === 'function' ? cb : () => {}; this._core.waitForConnect().then( () => callback(null, this), (err) => callback(toError(err, 'Operation failed')), ); return this; } close(cb) { makeCallback(this._core.close(), cb); return this; } updateSecret(newSecret, reason, cb) { makeCallback(this._core.updateSecret(newSecret, reason), cb); return this; } createChannel(options, cb) { if (typeof options === 'function') { cb = options; options = undefined; } makeCallback(this._core.createChannel(options), cb); return this; } createConfirmChannel(options, cb) { if (typeof options === 'function') { cb = options; options = undefined; } makeCallback(this._core.createConfirmChannel(options), cb); return this; } } function connectWithRecoveryPromise(openModel, recovery) { const recovering = new RecoveringPromiseModel(openModel, recovery); return recovering.waitForConnect(); } function connectWithRecoveryCallback(openModel, recovery, cb) { const recovering = new RecoveringCallbackModel(openModel, recovery); recovering.waitForConnect(cb); return recovering; } module.exports = { splitConnectionOptions, recoveryEnabled, connectWithRecoveryPromise, connectWithRecoveryCallback, RecoveringPromiseModel, RecoveringCallbackModel, }; amqp-node-amqplib-263c8a3/lib/safe_emit.js000066400000000000000000000004441520012327700204120ustar00rootroot00000000000000function safeEmit(emitter, event, ...args) { try { emitter.emit(event, ...args); } catch (e) { if (emitter.listenerCount('handler-error') > 0) { setImmediate(() => emitter.emit('handler-error', e, event)); } else { throw e; } } } module.exports = safeEmit; amqp-node-amqplib-263c8a3/package-lock.json000066400000000000000000000764601520012327700206010ustar00rootroot00000000000000{ "name": "amqplib", "version": "2.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "amqplib", "version": "2.0.1", "license": "MIT", "devDependencies": { "@biomejs/biome": "^2.2.2", "@types/node": "^25.6.0", "claire": "0.4.1", "lefthook": "^1.12.3", "typescript": "^5.9.3", "uglify-js": "2.8.x" }, "engines": { "node": ">=18" } }, "node_modules/@biomejs/biome": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.2.2.tgz", "integrity": "sha512-j1omAiQWCkhuLgwpMKisNKnsM6W8Xtt1l0WZmqY/dFj8QPNkIoTvk4tSsi40FaAAkBE1PU0AFG2RWFBWenAn+w==", "dev": true, "license": "MIT OR Apache-2.0", "bin": { "biome": "bin/biome" }, "engines": { "node": ">=14.21.3" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/biome" }, "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.2.2", "@biomejs/cli-darwin-x64": "2.2.2", "@biomejs/cli-linux-arm64": "2.2.2", "@biomejs/cli-linux-arm64-musl": "2.2.2", "@biomejs/cli-linux-x64": "2.2.2", "@biomejs/cli-linux-x64-musl": "2.2.2", "@biomejs/cli-win32-arm64": "2.2.2", "@biomejs/cli-win32-x64": "2.2.2" } }, "node_modules/@biomejs/cli-darwin-arm64": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.2.2.tgz", "integrity": "sha512-6ePfbCeCPryWu0CXlzsWNZgVz/kBEvHiPyNpmViSt6A2eoDf4kXs3YnwQPzGjy8oBgQulrHcLnJL0nkCh80mlQ==", "cpu": [ "arm64" ], "dev": true, "license": "MIT OR Apache-2.0", "optional": true, "os": [ "darwin" ], "engines": { "node": ">=14.21.3" } }, "node_modules/@biomejs/cli-darwin-x64": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.2.2.tgz", "integrity": "sha512-Tn4JmVO+rXsbRslml7FvKaNrlgUeJot++FkvYIhl1OkslVCofAtS35MPlBMhXgKWF9RNr9cwHanrPTUUXcYGag==", "cpu": [ "x64" ], "dev": true, "license": "MIT OR Apache-2.0", "optional": true, "os": [ "darwin" ], "engines": { "node": ">=14.21.3" } }, "node_modules/@biomejs/cli-linux-arm64": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.2.2.tgz", "integrity": "sha512-JfrK3gdmWWTh2J5tq/rcWCOsImVyzUnOS2fkjhiYKCQ+v8PqM+du5cfB7G1kXas+7KQeKSWALv18iQqdtIMvzw==", "cpu": [ "arm64" ], "dev": true, "license": "MIT OR Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">=14.21.3" } }, "node_modules/@biomejs/cli-linux-arm64-musl": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.2.2.tgz", "integrity": "sha512-/MhYg+Bd6renn6i1ylGFL5snYUn/Ct7zoGVKhxnro3bwekiZYE8Kl39BSb0MeuqM+72sThkQv4TnNubU9njQRw==", "cpu": [ "arm64" ], "dev": true, "license": "MIT OR Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">=14.21.3" } }, "node_modules/@biomejs/cli-linux-x64": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.2.2.tgz", "integrity": "sha512-Ogb+77edO5LEP/xbNicACOWVLt8mgC+E1wmpUakr+O4nKwLt9vXe74YNuT3T1dUBxC/SnrVmlzZFC7kQJEfquQ==", "cpu": [ "x64" ], "dev": true, "license": "MIT OR Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">=14.21.3" } }, "node_modules/@biomejs/cli-linux-x64-musl": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.2.2.tgz", "integrity": "sha512-ZCLXcZvjZKSiRY/cFANKg+z6Fhsf9MHOzj+NrDQcM+LbqYRT97LyCLWy2AS+W2vP+i89RyRM+kbGpUzbRTYWig==", "cpu": [ "x64" ], "dev": true, "license": "MIT OR Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">=14.21.3" } }, "node_modules/@biomejs/cli-win32-arm64": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.2.2.tgz", "integrity": "sha512-wBe2wItayw1zvtXysmHJQoQqXlTzHSpQRyPpJKiNIR21HzH/CrZRDFic1C1jDdp+zAPtqhNExa0owKMbNwW9cQ==", "cpu": [ "arm64" ], "dev": true, "license": "MIT OR Apache-2.0", "optional": true, "os": [ "win32" ], "engines": { "node": ">=14.21.3" } }, "node_modules/@biomejs/cli-win32-x64": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.2.2.tgz", "integrity": "sha512-DAuHhHekGfiGb6lCcsT4UyxQmVwQiBCBUMwVra/dcOSs9q8OhfaZgey51MlekT3p8UwRqtXQfFuEJBhJNdLZwg==", "cpu": [ "x64" ], "dev": true, "license": "MIT OR Apache-2.0", "optional": true, "os": [ "win32" ], "engines": { "node": ">=14.21.3" } }, "node_modules/@types/node": { "version": "25.6.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", "dev": true, "license": "MIT", "dependencies": { "undici-types": "~7.19.0" } }, "node_modules/align-text": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, "dependencies": { "kind-of": "^3.0.2", "longest": "^1.0.1", "repeat-string": "^1.5.2" }, "engines": { "node": ">=0.10.0" } }, "node_modules/boo": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/boo/-/boo-1.2.4.tgz", "integrity": "sha1-szMxw2xK552C9P0ORJBgTLfFE7U=", "dev": true }, "node_modules/camelcase": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/center-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", "dev": true, "dependencies": { "align-text": "^0.1.3", "lazy-cache": "^1.0.3" }, "engines": { "node": ">=0.10.0" } }, "node_modules/claire": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/claire/-/claire-0.4.1.tgz", "integrity": "sha1-ynDcEmHd2PJaGvq604BcOfziY7w=", "dev": true, "dependencies": { "boo": "~1.2.4", "flaw": "~0.1.0", "prelude-ls": "~0.6.0" } }, "node_modules/cliui": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", "dev": true, "dependencies": { "center-align": "^0.1.1", "right-align": "^0.1.1", "wordwrap": "0.0.2" } }, "node_modules/cliui/node_modules/wordwrap": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", "dev": true, "engines": { "node": ">=0.4.0" } }, "node_modules/decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/flaw": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/flaw/-/flaw-0.1.0.tgz", "integrity": "sha1-wMPo+BYWdTh+2tofbOhNZ5sCUn8=", "dev": true }, "node_modules/is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, "node_modules/kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "dependencies": { "is-buffer": "^1.1.5" }, "engines": { "node": ">=0.10.0" } }, "node_modules/lazy-cache": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/lefthook": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/lefthook/-/lefthook-1.12.3.tgz", "integrity": "sha512-huMg+mGp6wHPjkaLdchuOvxVRMzvz6OVdhivatiH2Qn47O5Zm46jwzbVPYIanX6N/8ZTjGLBxv8tZ0KYmKt/Jg==", "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { "lefthook": "bin/index.js" }, "optionalDependencies": { "lefthook-darwin-arm64": "1.12.3", "lefthook-darwin-x64": "1.12.3", "lefthook-freebsd-arm64": "1.12.3", "lefthook-freebsd-x64": "1.12.3", "lefthook-linux-arm64": "1.12.3", "lefthook-linux-x64": "1.12.3", "lefthook-openbsd-arm64": "1.12.3", "lefthook-openbsd-x64": "1.12.3", "lefthook-windows-arm64": "1.12.3", "lefthook-windows-x64": "1.12.3" } }, "node_modules/lefthook-darwin-arm64": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/lefthook-darwin-arm64/-/lefthook-darwin-arm64-1.12.3.tgz", "integrity": "sha512-j1lwaosWRy3vhz8oQgCS1M6EUFN95aIYeNuqkczsBoAA6BDNAmVP1ctYEIYUK4bYaIgENbqbA9prYMAhyzh6Og==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/lefthook-darwin-x64": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/lefthook-darwin-x64/-/lefthook-darwin-x64-1.12.3.tgz", "integrity": "sha512-x6aWFfLQX4m5zQ4X9zh5+hHOE5XTvNjz2zB9DI+xbIBLs2RRg0xJNT3OfgSrBU1QtEBneJ5dRQP5nl47td9GDQ==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/lefthook-freebsd-arm64": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/lefthook-freebsd-arm64/-/lefthook-freebsd-arm64-1.12.3.tgz", "integrity": "sha512-41OmulLqVZ0EOHmmHouJrpL59SwDD7FLoso4RsQVIBPaf8fHacdLo07Ye28VWQ5XolZQvnWcr1YXKo4JhqQMyw==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/lefthook-freebsd-x64": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/lefthook-freebsd-x64/-/lefthook-freebsd-x64-1.12.3.tgz", "integrity": "sha512-741/JRCJIS++hgYEH2uefN4FsH872V7gy2zDhcfQofiZnWP7+qhl4Wmwi8IpjIu4X7hLOC4cT18LOVU5L8KV9Q==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/lefthook-linux-arm64": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/lefthook-linux-arm64/-/lefthook-linux-arm64-1.12.3.tgz", "integrity": "sha512-BXIy1aDFZmFgmebJliNrEqZfX1lSOD4b/USvANv1UirFrNgTq5SRssd1CKfflT2PwKX6LsJTD4WabLLWZOxp9A==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/lefthook-linux-x64": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/lefthook-linux-x64/-/lefthook-linux-x64-1.12.3.tgz", "integrity": "sha512-FRdwdj5jsQAP2eVrtkVUqMqYNCbQ2Ix84izy29/BvLlu/hVypAGbDfUkgFnsmAd6ZsCBeYCEtPuqyg3E3SO0Rg==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/lefthook-openbsd-arm64": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/lefthook-openbsd-arm64/-/lefthook-openbsd-arm64-1.12.3.tgz", "integrity": "sha512-tch5wXY4GOjKAYohH7OFoxNdVHuUSYt2Pulo2VTkMYEG8IrvJnRO5MkvgHtKDHzU5mfABQYv5+ccJykDx5hQWA==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "openbsd" ] }, "node_modules/lefthook-openbsd-x64": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/lefthook-openbsd-x64/-/lefthook-openbsd-x64-1.12.3.tgz", "integrity": "sha512-IHbHg/rUFXrAN7LnjcQEtutCHBaD49CZge96Hpk0GZ2eEG5GTCNRnUyEf+Kf3+RTqHFgwtADdpeDa/ZaGZTM4g==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "openbsd" ] }, "node_modules/lefthook-windows-arm64": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/lefthook-windows-arm64/-/lefthook-windows-arm64-1.12.3.tgz", "integrity": "sha512-wghcE5TSpb+mbtemUV6uAo9hEK09kxRzhf2nPdeDX+fw42cL2TGZsbaCnDyzaY144C+L2/wEWrLIHJMnZYkuqA==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/lefthook-windows-x64": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/lefthook-windows-x64/-/lefthook-windows-x64-1.12.3.tgz", "integrity": "sha512-7Co/L8e2x2hGC1L33jDJ4ZlTkO3PJm25GOGpLfN1kqwhGB/uzMLeTI/PBczjlIN8isUv26ouNd9rVR7Bibrwyg==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/prelude-ls": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-0.6.0.tgz", "integrity": "sha1-z4JLS0fMc8vZb56YhQc7Q6rqqzs=", "dev": true, "engines": { "node": ">= 0.8.0" } }, "node_modules/repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true, "engines": { "node": ">=0.10" } }, "node_modules/right-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", "dev": true, "dependencies": { "align-text": "^0.1.1" }, "engines": { "node": ">=0.10.0" } }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { "node": ">=14.17" } }, "node_modules/uglify-js": { "version": "2.8.29", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", "dev": true, "dependencies": { "source-map": "~0.5.1", "yargs": "~3.10.0" }, "bin": { "uglifyjs": "bin/uglifyjs" }, "engines": { "node": ">=0.8.0" }, "optionalDependencies": { "uglify-to-browserify": "~1.0.0" } }, "node_modules/uglify-js/node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/uglify-to-browserify": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", "dev": true, "optional": true }, "node_modules/undici-types": { "version": "7.19.2", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", "dev": true, "license": "MIT" }, "node_modules/window-size": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", "dev": true, "engines": { "node": ">= 0.8.0" } }, "node_modules/yargs": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "dev": true, "dependencies": { "camelcase": "^1.0.2", "cliui": "^2.1.0", "decamelize": "^1.0.0", "window-size": "0.1.0" } } }, "dependencies": { "@biomejs/biome": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.2.2.tgz", "integrity": "sha512-j1omAiQWCkhuLgwpMKisNKnsM6W8Xtt1l0WZmqY/dFj8QPNkIoTvk4tSsi40FaAAkBE1PU0AFG2RWFBWenAn+w==", "dev": true, "requires": { "@biomejs/cli-darwin-arm64": "2.2.2", "@biomejs/cli-darwin-x64": "2.2.2", "@biomejs/cli-linux-arm64": "2.2.2", "@biomejs/cli-linux-arm64-musl": "2.2.2", "@biomejs/cli-linux-x64": "2.2.2", "@biomejs/cli-linux-x64-musl": "2.2.2", "@biomejs/cli-win32-arm64": "2.2.2", "@biomejs/cli-win32-x64": "2.2.2" } }, "@biomejs/cli-darwin-arm64": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.2.2.tgz", "integrity": "sha512-6ePfbCeCPryWu0CXlzsWNZgVz/kBEvHiPyNpmViSt6A2eoDf4kXs3YnwQPzGjy8oBgQulrHcLnJL0nkCh80mlQ==", "dev": true, "optional": true }, "@biomejs/cli-darwin-x64": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.2.2.tgz", "integrity": "sha512-Tn4JmVO+rXsbRslml7FvKaNrlgUeJot++FkvYIhl1OkslVCofAtS35MPlBMhXgKWF9RNr9cwHanrPTUUXcYGag==", "dev": true, "optional": true }, "@biomejs/cli-linux-arm64": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.2.2.tgz", "integrity": "sha512-JfrK3gdmWWTh2J5tq/rcWCOsImVyzUnOS2fkjhiYKCQ+v8PqM+du5cfB7G1kXas+7KQeKSWALv18iQqdtIMvzw==", "dev": true, "optional": true }, "@biomejs/cli-linux-arm64-musl": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.2.2.tgz", "integrity": "sha512-/MhYg+Bd6renn6i1ylGFL5snYUn/Ct7zoGVKhxnro3bwekiZYE8Kl39BSb0MeuqM+72sThkQv4TnNubU9njQRw==", "dev": true, "optional": true }, "@biomejs/cli-linux-x64": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.2.2.tgz", "integrity": "sha512-Ogb+77edO5LEP/xbNicACOWVLt8mgC+E1wmpUakr+O4nKwLt9vXe74YNuT3T1dUBxC/SnrVmlzZFC7kQJEfquQ==", "dev": true, "optional": true }, "@biomejs/cli-linux-x64-musl": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.2.2.tgz", "integrity": "sha512-ZCLXcZvjZKSiRY/cFANKg+z6Fhsf9MHOzj+NrDQcM+LbqYRT97LyCLWy2AS+W2vP+i89RyRM+kbGpUzbRTYWig==", "dev": true, "optional": true }, "@biomejs/cli-win32-arm64": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.2.2.tgz", "integrity": "sha512-wBe2wItayw1zvtXysmHJQoQqXlTzHSpQRyPpJKiNIR21HzH/CrZRDFic1C1jDdp+zAPtqhNExa0owKMbNwW9cQ==", "dev": true, "optional": true }, "@biomejs/cli-win32-x64": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.2.2.tgz", "integrity": "sha512-DAuHhHekGfiGb6lCcsT4UyxQmVwQiBCBUMwVra/dcOSs9q8OhfaZgey51MlekT3p8UwRqtXQfFuEJBhJNdLZwg==", "dev": true, "optional": true }, "@types/node": { "version": "25.6.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", "dev": true, "requires": { "undici-types": "~7.19.0" } }, "align-text": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, "requires": { "kind-of": "^3.0.2", "longest": "^1.0.1", "repeat-string": "^1.5.2" } }, "boo": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/boo/-/boo-1.2.4.tgz", "integrity": "sha1-szMxw2xK552C9P0ORJBgTLfFE7U=", "dev": true }, "camelcase": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", "dev": true }, "center-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", "dev": true, "requires": { "align-text": "^0.1.3", "lazy-cache": "^1.0.3" } }, "claire": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/claire/-/claire-0.4.1.tgz", "integrity": "sha1-ynDcEmHd2PJaGvq604BcOfziY7w=", "dev": true, "requires": { "boo": "~1.2.4", "flaw": "~0.1.0", "prelude-ls": "~0.6.0" } }, "cliui": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", "dev": true, "requires": { "center-align": "^0.1.1", "right-align": "^0.1.1", "wordwrap": "0.0.2" }, "dependencies": { "wordwrap": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", "dev": true } } }, "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, "flaw": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/flaw/-/flaw-0.1.0.tgz", "integrity": "sha1-wMPo+BYWdTh+2tofbOhNZ5sCUn8=", "dev": true }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { "is-buffer": "^1.1.5" } }, "lazy-cache": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", "dev": true }, "lefthook": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/lefthook/-/lefthook-1.12.3.tgz", "integrity": "sha512-huMg+mGp6wHPjkaLdchuOvxVRMzvz6OVdhivatiH2Qn47O5Zm46jwzbVPYIanX6N/8ZTjGLBxv8tZ0KYmKt/Jg==", "dev": true, "requires": { "lefthook-darwin-arm64": "1.12.3", "lefthook-darwin-x64": "1.12.3", "lefthook-freebsd-arm64": "1.12.3", "lefthook-freebsd-x64": "1.12.3", "lefthook-linux-arm64": "1.12.3", "lefthook-linux-x64": "1.12.3", "lefthook-openbsd-arm64": "1.12.3", "lefthook-openbsd-x64": "1.12.3", "lefthook-windows-arm64": "1.12.3", "lefthook-windows-x64": "1.12.3" } }, "lefthook-darwin-arm64": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/lefthook-darwin-arm64/-/lefthook-darwin-arm64-1.12.3.tgz", "integrity": "sha512-j1lwaosWRy3vhz8oQgCS1M6EUFN95aIYeNuqkczsBoAA6BDNAmVP1ctYEIYUK4bYaIgENbqbA9prYMAhyzh6Og==", "dev": true, "optional": true }, "lefthook-darwin-x64": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/lefthook-darwin-x64/-/lefthook-darwin-x64-1.12.3.tgz", "integrity": "sha512-x6aWFfLQX4m5zQ4X9zh5+hHOE5XTvNjz2zB9DI+xbIBLs2RRg0xJNT3OfgSrBU1QtEBneJ5dRQP5nl47td9GDQ==", "dev": true, "optional": true }, "lefthook-freebsd-arm64": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/lefthook-freebsd-arm64/-/lefthook-freebsd-arm64-1.12.3.tgz", "integrity": "sha512-41OmulLqVZ0EOHmmHouJrpL59SwDD7FLoso4RsQVIBPaf8fHacdLo07Ye28VWQ5XolZQvnWcr1YXKo4JhqQMyw==", "dev": true, "optional": true }, "lefthook-freebsd-x64": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/lefthook-freebsd-x64/-/lefthook-freebsd-x64-1.12.3.tgz", "integrity": "sha512-741/JRCJIS++hgYEH2uefN4FsH872V7gy2zDhcfQofiZnWP7+qhl4Wmwi8IpjIu4X7hLOC4cT18LOVU5L8KV9Q==", "dev": true, "optional": true }, "lefthook-linux-arm64": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/lefthook-linux-arm64/-/lefthook-linux-arm64-1.12.3.tgz", "integrity": "sha512-BXIy1aDFZmFgmebJliNrEqZfX1lSOD4b/USvANv1UirFrNgTq5SRssd1CKfflT2PwKX6LsJTD4WabLLWZOxp9A==", "dev": true, "optional": true }, "lefthook-linux-x64": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/lefthook-linux-x64/-/lefthook-linux-x64-1.12.3.tgz", "integrity": "sha512-FRdwdj5jsQAP2eVrtkVUqMqYNCbQ2Ix84izy29/BvLlu/hVypAGbDfUkgFnsmAd6ZsCBeYCEtPuqyg3E3SO0Rg==", "dev": true, "optional": true }, "lefthook-openbsd-arm64": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/lefthook-openbsd-arm64/-/lefthook-openbsd-arm64-1.12.3.tgz", "integrity": "sha512-tch5wXY4GOjKAYohH7OFoxNdVHuUSYt2Pulo2VTkMYEG8IrvJnRO5MkvgHtKDHzU5mfABQYv5+ccJykDx5hQWA==", "dev": true, "optional": true }, "lefthook-openbsd-x64": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/lefthook-openbsd-x64/-/lefthook-openbsd-x64-1.12.3.tgz", "integrity": "sha512-IHbHg/rUFXrAN7LnjcQEtutCHBaD49CZge96Hpk0GZ2eEG5GTCNRnUyEf+Kf3+RTqHFgwtADdpeDa/ZaGZTM4g==", "dev": true, "optional": true }, "lefthook-windows-arm64": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/lefthook-windows-arm64/-/lefthook-windows-arm64-1.12.3.tgz", "integrity": "sha512-wghcE5TSpb+mbtemUV6uAo9hEK09kxRzhf2nPdeDX+fw42cL2TGZsbaCnDyzaY144C+L2/wEWrLIHJMnZYkuqA==", "dev": true, "optional": true }, "lefthook-windows-x64": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/lefthook-windows-x64/-/lefthook-windows-x64-1.12.3.tgz", "integrity": "sha512-7Co/L8e2x2hGC1L33jDJ4ZlTkO3PJm25GOGpLfN1kqwhGB/uzMLeTI/PBczjlIN8isUv26ouNd9rVR7Bibrwyg==", "dev": true, "optional": true }, "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", "dev": true }, "prelude-ls": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-0.6.0.tgz", "integrity": "sha1-z4JLS0fMc8vZb56YhQc7Q6rqqzs=", "dev": true }, "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, "right-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", "dev": true, "requires": { "align-text": "^0.1.1" } }, "typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true }, "uglify-js": { "version": "2.8.29", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", "dev": true, "requires": { "source-map": "~0.5.1", "uglify-to-browserify": "~1.0.0", "yargs": "~3.10.0" }, "dependencies": { "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true } } }, "uglify-to-browserify": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", "dev": true, "optional": true }, "undici-types": { "version": "7.19.2", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", "dev": true }, "window-size": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", "dev": true }, "yargs": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "dev": true, "requires": { "camelcase": "^1.0.2", "cliui": "^2.1.0", "decamelize": "^1.0.0", "window-size": "0.1.0" } } } } amqp-node-amqplib-263c8a3/package.json000066400000000000000000000025551520012327700176450ustar00rootroot00000000000000{ "name": "amqplib", "homepage": "http://amqp-node.github.io/amqplib/", "main": "./channel_api.js", "version": "2.0.1", "types": "./index.d.ts", "exports": { ".": { "types": "./index.d.ts", "default": "./channel_api.js" }, "./callback_api": { "types": "./callback_api.d.ts", "default": "./callback_api.js" } }, "description": "An AMQP 0-9-1 (e.g., RabbitMQ) library and client.", "repository": { "type": "git", "url": "git+https://github.com/amqp-node/amqplib.git" }, "engines": { "node": ">=18" }, "dependencies": {}, "devDependencies": { "@biomejs/biome": "^2.2.2", "@types/node": "^25.6.0", "claire": "0.4.1", "lefthook": "^1.12.3", "typescript": "^5.9.3", "uglify-js": "2.8.x" }, "scripts": { "test": "node --test --test-timeout=1000", "test:only": "node --test --test-only --test-timeout=1000", "rabbitmq-3": "docker run -d --rm --name rabbitmq -p 5672:5672 rabbitmq:3.12-alpine", "rabbitmq-4": "docker run -d --rm --name rabbitmq -p 5672:5672 rabbitmq:4.2-alpine", "format": "biome format . --write", "lint": "biome lint --max-diagnostics=1000 .", "typecheck": "tsc --noEmit -p tsconfig.types.json" }, "keywords": [ "AMQP", "AMQP 0-9-1", "RabbitMQ" ], "author": "Michael Bridgen ", "license": "MIT" } amqp-node-amqplib-263c8a3/test/000077500000000000000000000000001520012327700163275ustar00rootroot00000000000000amqp-node-amqplib-263c8a3/test/bitset.test.js000066400000000000000000000031331520012327700211350ustar00rootroot00000000000000const { describe, it } = require('node:test'); const claire = require('claire'); const { BitSet } = require('../lib/bitset'); const { forAll, data: arb, label, choice, transform } = claire; const PosInt = transform(Math.floor, arb.Positive); const EmptyBitSet = label('bitset', transform((size) => new BitSet(size), choice(arb.Nothing, PosInt))); describe('BitSet', () => { it('get bit', forAll(EmptyBitSet, PosInt) .satisfy((b, bit) => { b.set(bit); return b.get(bit); }) .asTest()); it('clear bit', forAll(EmptyBitSet, PosInt) .satisfy((b, bit) => { b.set(bit); b.clear(bit); return !b.get(bit); }) .asTest()); it('next set of empty', forAll(EmptyBitSet) .satisfy((b) => { return b.nextSetBit(0) === -1; }) .asTest()); it('next set of one bit', forAll(EmptyBitSet, PosInt) .satisfy((b, bit) => { b.set(bit); return b.nextSetBit(0) === bit; }) .asTest()); it('next set same bit', forAll(EmptyBitSet, PosInt) .satisfy((b, bit) => { b.set(bit); return b.nextSetBit(bit) === bit; }) .asTest()); it('next set following bit', forAll(EmptyBitSet, PosInt) .satisfy((b, bit) => { b.set(bit); return b.nextSetBit(bit + 1) === -1; }) .asTest()); it('next clear of empty', forAll(EmptyBitSet, PosInt) .satisfy((b, bit) => { return b.nextClearBit(bit) === bit; }) .asTest()); it('next clear of one set', forAll(EmptyBitSet, PosInt) .satisfy((b, bit) => { b.set(bit); return b.nextClearBit(bit) === bit + 1; }) .asTest()); }); amqp-node-amqplib-263c8a3/test/callback_api.test.js000066400000000000000000000307641520012327700222420ustar00rootroot00000000000000const { describe, it, afterEach } = require('node:test'); const assert = require('node:assert'); const domain = require('node:domain'); const { connect } = require('../callback_api'); const { schedule, randomString, latch } = require('./lib/util'); function waitForMessages(ch, q, cb) { ch.checkQueue(q, (e, ok) => { if (e != null) return cb(e); else if (ok.messageCount > 0) return cb(null, ok); else schedule(waitForMessages.bind(null, ch, q, cb)); }); } describe('Callback API', () => { describe('connect', () => { it('at all', (_t, done) => { connect((err, c) => { assert.ifError(err); c.close(done); }); }); }); describe('updateSecret', () => { let c; afterEach((_t, done) => { if (c) c.close(() => done()) else done(); }); it('updateSecret', (_t, done) => { connect((err, _c) => { assert.ifError(err); c = _c; c.updateSecret(Buffer.from('new secret'), 'no reason', (err) => { assert.ifError(err); done(); }); }); }); it('emits update-secret-ok event', (_t, done) => { connect((err, _c) => { assert.ifError(err); c = _c; c.on('update-secret-ok', () => done()); c.updateSecret(Buffer.from('new secret'), 'no reason', (err) => { assert.ifError(err); }); }); }); }); const channel_test_fn = (method) => { return (name, options, chfun) => { if (typeof options === 'function') { chfun = options; options = {}; } it(name, (_t, done) => { connect((err, c) => { assert.ifError(err); c[method](options, ((err, ch) => { assert.ifError(err); chfun(ch, done); })); }); }); }; }; const channel_test = channel_test_fn('createChannel'); const confirm_channel_test = channel_test_fn('createConfirmChannel'); describe('channel open', () => { channel_test('at all', (ch, done) => { ch.connection.close(done); }); channel_test('open and close', (ch, done) => { ch.close((err) => { assert.ifError(err); ch.connection.close(done); }); }); }); describe('assert, check, delete', () => { let ch; afterEach((_t, done) => { ch?.connection?.close(done); }); channel_test('assert, check, delete queue', (_ch, done) => { ch = _ch; ch.assertQueue('test.cb.queue', {}, (err, { queue }) => { assert.ifError(err); assert.strictEqual(queue, 'test.cb.queue'); ch.checkQueue('test.cb.queue', (err, ok) => { assert.ifError(err); assert.ok(ok); ch.deleteQueue('test.cb.queue', {}, done); }) }); }); channel_test('assert, check, delete exchange', (_ch, done) => { ch = _ch; ch.assertExchange('test.cb.exchange', 'topic', {}, (err, { exchange }) => { assert.ifError(err); assert.strictEqual(exchange, 'test.cb.exchange'); ch.checkExchange('test.cb.exchange', (err, _ok) => { assert.ifError(err); ch.deleteExchange('test.cb.exchange', {}, done); }); }); }); channel_test('fail on check non-queue', (_ch, done) => { ch = _ch; const decrementLatch = latch(2, done); ch.on('error', (err) => { assert.match(err.message, /Channel closed by server/) decrementLatch() }); ch.checkQueue('test.cb.nothere', (err) => { assert.match(err.message, /QueueDeclare; 404 \(NOT-FOUND\)/); decrementLatch(); }); }); channel_test('fail on check non-exchange', (_ch, done) => { ch = _ch; const decrementLatch = latch(2, done); ch.on('error', (err) => { assert.match(err.message, /Channel closed by server/) decrementLatch() }); ch.checkExchange('test.cb.nothere', (err) => { assert.match(err.message, /ExchangeDeclare; 404 \(NOT-FOUND\)/); decrementLatch(); }); }); }); describe('bindings', () => { let ch; afterEach((_t, done) => { ch?.connection?.close(done); }); channel_test('bind queue', (_ch, done) => { ch = _ch; ch.assertQueue('test.cb.bindq', {}, (err, q) => { assert.ifError(err); ch.assertExchange('test.cb.bindex', 'fanout', {}, (err, ex) => { assert.ifError(err); ch.bindQueue(q.queue, ex.exchange, '', {}, done); }); }); }); channel_test('bind exchange', (_ch, done) => { ch = _ch; ch.assertExchange('test.cb.bindex1', 'fanout', {}, (err, ex1) => { assert.ifError(err); ch.assertExchange('test.cb.bindex2', 'fanout', {}, (err, ex2) => { assert.ifError(err); ch.bindExchange(ex1.exchange, ex2.exchange, '', {}, done); }); }); }); }); describe('sending messages', () => { let ch; afterEach((_t, done) => { ch?.connection?.close(done); }); channel_test('send to queue and consume noAck', (_ch, done) => { ch = _ch; const msg = randomString(); ch.assertQueue('', { exclusive: true }, (err, { queue }) => { assert.ifError(err); ch.consume(queue, (m) => { assert.strictEqual(m.content.toString(), msg); done(); }, { noAck: true, exclusive: true }); ch.sendToQueue(queue, Buffer.from(msg)); }); }); channel_test('send to queue and consume ack', (_ch, done) => { ch = _ch; const msg = randomString(); ch.assertQueue('', { exclusive: true }, (err, { queue }) => { assert.ifError(err); ch.consume(queue, (m) => { assert.strictEqual(m.content.toString(), msg); ch.ack(m); done(); }, { noAck: false, exclusive: true }); ch.sendToQueue(queue, Buffer.from(msg)); }); }); channel_test('send to and get from queue', (_ch, done) => { ch = _ch; ch.assertQueue('', { exclusive: true }, (err, { queue }) => { assert.ifError(err); const msg = randomString(); ch.sendToQueue(queue, Buffer.from(msg)); waitForMessages(ch, queue, (err, _) => { assert.ifError(err); ch.get(queue, { noAck: true }, (err, m) => { assert.ifError(err); assert.ok(m, 'Expected message, got empty/false'); assert.strictEqual(m.content.toString(), msg); done(); }); }); }); }); const channelOptions = {}; channel_test('find high watermark', (_ch, done) => { ch = _ch; const msg = randomString(); let baseline = 0; ch.assertQueue('', { exclusive: true }, (err, { queue }) => { assert.ifError(err); while (ch.sendToQueue(queue, Buffer.from(msg))) { baseline++; } channelOptions.highWaterMark = baseline * 2; done(); }); }); channel_test('set high watermark', channelOptions, (_ch, done) => { ch = _ch; const msg = randomString(); ch.assertQueue('', { exclusive: true }, (err, { queue }) => { assert.ifError(err); let ok; for (let i = 0; i < channelOptions.highWaterMark; i++) { ok = ch.sendToQueue(queue, Buffer.from(msg)); assert.equal(ok, true); } done(); }); }); }); describe('ConfirmChannel', () => { let ch; afterEach((_t, done) => { ch?.connection?.close(done); }); confirm_channel_test('Receive confirmation', (_ch, done) => { ch = _ch; // An unroutable message, on the basis that you're not allowed a // queue with an empty name, and you can't make bindings to the // default exchange. Tricky eh? ch.publish('', '', Buffer.from('foo'), {}, done); }); confirm_channel_test('Wait for confirms', (_ch, done) => { ch = _ch; for (let i = 0; i < 1000; i++) { ch.publish('', '', Buffer.from('foo'), {}); } ch.waitForConfirms(done); }); const channelOptions = {}; confirm_channel_test('find high watermark', (_ch, done) => { ch = _ch; const msg = randomString(); let baseline = 0; ch.assertQueue('', { exclusive: true }, (err, { queue }) => { assert.ifError(err); while (ch.sendToQueue(queue, Buffer.from(msg))) { baseline++; } channelOptions.highWaterMark = baseline * 2; done(); }); }); confirm_channel_test('set high watermark', channelOptions, (_ch, done) => { ch = _ch; const msg = randomString(); ch.assertQueue('', { exclusive: true }, (err, { queue }) => { assert.ifError(err); let ok; for (let i = 0; i < channelOptions.highWaterMark; i++) { ok = ch.sendToQueue(queue, Buffer.from(msg)); assert.equal(ok, true); } done(); }); }); confirm_channel_test('publish on closed channel does not leak callbacks', (_ch, done) => { ch = _ch; ch.close(() => { for (let i = 0; i < 10; i++) { try { ch.publish('', '', Buffer.from('x'), {}, () => {}); } catch (_) {} } assert.strictEqual(ch.unconfirmed.length, 0); done(); }); }); }); describe('Error handling', () => { let c; afterEach((_t, done) => { c?.close(() => done()); }) it('Throw error in connection open callback', (_t, done) => { const dom = domain.createDomain(); dom.on('error', (err) => { assert.match(err.message, /Spurious connection open callback error/); done(); }); connect(dom.bind((_err, _c) => { c = _c; throw new Error('Spurious connection open callback error'); })); }); function error_test(name, fun) { it(name, (_t, done) => { const dom = domain.createDomain(); dom.run(() => { connect((err, _c) => { assert.ifError(err); c = _c; // Seems like there were some unironed wrinkles in 0.8's // implementation of domains; explicitly adding the connection // to the domain makes sure any exception thrown in the course // of processing frames is handled by the domain. For other // versions of Node.JS, this ends up being belt-and-braces. dom.add(c); c.createChannel((_err, ch) => { fun(ch, done, dom); }); }); }); }); } error_test('Channel open callback throws an error', (_ch, done, dom) => { dom.on('error', (err) => { assert.match(err.message, /Error in open callback/) done(); }); throw new Error('Error in open callback'); }); error_test('RPC callback throws error', (ch, done, dom) => { dom.on('error', (err) => { assert.match(err.message, /Spurious callback error/) done(); }); ch.prefetch(0, false, (_err, _ok) => { throw new Error('Spurious callback error'); }); }); error_test('Get callback throws error', (ch, done, dom) => { dom.on('error', (err) => { assert.match(err.message, /Spurious callback error/) done(); }); ch.assertQueue('test.cb.get-with-error', {}, (_err, _ok) => { ch.get('test.cb.get-with-error', { noAck: true }, () => { throw new Error('Spurious callback error'); }); }); }); error_test('Consume callback throws error', (ch, done, dom) => { dom.on('error', (err) => { assert.match(err.message, /Spurious callback error/) done(); }); ch.assertQueue('test.cb.consume-with-error', {}, (_err, _ok) => { ch.consume('test.cb.consume-with-error', () => { }, { noAck: true }, () => { throw new Error('Spurious callback error'); }); }); }); error_test('Get from non-queue invokes error', (ch, done, dom) => { const decrementLatch = latch(2, () => done()); dom.on('error', (err) => { assert.match(err.message, /404 \(NOT-FOUND\)/); decrementLatch(); }); ch.get('', {}, (err) => { assert.match(err.message, /404 \(NOT-FOUND\)/) decrementLatch() }); }); error_test('Consume from non-queue invokes error', (ch, done, dom) => { const decrementLatch = latch(2, done); dom.on('error', (err) => { assert.match(err.message, /404 \(NOT-FOUND\)/); decrementLatch(); }); ch.consume('', () => { }, {}, (err) => { assert.match(err.message, /404 \(NOT-FOUND\)/) decrementLatch() }); }); }); }); amqp-node-amqplib-263c8a3/test/channel.test.js000066400000000000000000000742631520012327700212670ustar00rootroot00000000000000const { describe, it, beforeEach, afterEach } = require('node:test'); const assert = require('node:assert'); const promisify = require('node:util').promisify; const Channel = require('../lib/channel').Channel; const Connection = require('../lib/connection').Connection; const util = require('./lib/util'); const latch = util.latch; const completes = util.completes; const handshake = util.handshake; const defs = require('../lib/defs'); const OPEN_OPTS = require('./lib/data').OPEN_OPTS; const LOG_ERRORS = process.env.LOG_ERRORS; function baseChannelTest(client, server) { return (_t, done) => { const decrementLatch = latch(2, done); const pair = util.socketPair(); const c = new Connection(pair.client); if (LOG_ERRORS) c.on('error', console.warn); c.open(OPEN_OPTS, (err, _ok) => { if (err === null) client(c, decrementLatch); else decrementLatch(err); }); pair.server.read(8); // discard the protocol header util.runServer(pair.server, (send, wait) => { handshake(send, wait).then(() => { server(send, wait, decrementLatch); }, decrementLatch); }); }; } function channelTest(client, server) { return baseChannelTest( (conn, done) => { const ch = new Channel(conn); if (LOG_ERRORS) ch.on('error', console.warn); client(ch, done, conn); }, (send, wait, done) => { channel_handshake(send, wait) .then((ch) => server(send, wait, done, ch)) .then(null, done); // so you can return a promise to let // errors bubble out }, ); } function channel_handshake(send, wait) { return wait(defs.ChannelOpen)().then((open) => { assert.notEqual(0, open.channel); send(defs.ChannelOpenOk, { channelId: Buffer.from('') }, open.channel); return open.channel; }); } // fields for deliver and publish and get-ok const DELIVER_FIELDS = { consumerTag: 'fake', deliveryTag: 1, redelivered: false, exchange: 'foo', routingKey: 'bar', replyCode: defs.constants.NO_ROUTE, replyText: 'derp', }; function open(ch) { ch.allocate(); return promisify((cb) => { ch._rpc(defs.ChannelOpen, { outOfBand: '' }, defs.ChannelOpenOk, cb); })(); } describe('Channel', () => { describe('channel open and close', () => { it('open', channelTest( (ch, cb) => open(ch).then((ok) => { assert.equal(ok.id, defs.ChannelOpenOk); cb(); }), (_send, _wait, cb) => cb() )); it('bad server', baseChannelTest( (c, cb) => { const ch = new Channel(c); assert.rejects(() => open(ch), (err) => { assert.match(err.message, /Expected ChannelOpenOk; got /); return true; }).then(() => cb()); }, (send, wait, cb) => wait(defs.ChannelOpen)() .then((open) => send(defs.ChannelCloseOk, {}, open.channel)) .then(cb) ) ); it('open, close', channelTest( (ch, cb) => { open(ch) .then(() => new Promise((resolve) => { ch.closeBecause('Bye', defs.constants.REPLY_SUCCESS, resolve); })) .then(cb); }, (send, wait, cb, ch) => wait(defs.ChannelClose)() .then((_close) => send(defs.ChannelCloseOk, {}, ch)) .then(cb) ) ); it('server close', channelTest( (ch, cb) => { ch.once('error', (err) => { assert.match(err.message, /Channel closed by server: 504 \(CHANNEL-ERROR\)/); assert.strictEqual(504, err.code); assert.strictEqual(0, err.classId); assert.strictEqual(0, err.methodId); cb(); }); open(ch); }, (send, wait, cb, ch) => { send(defs.ChannelClose, { replyText: 'Forced close', replyCode: defs.constants.CHANNEL_ERROR, classId: 0, methodId: 0, }, ch); wait(defs.ChannelCloseOk)().then(cb); })); it('overlapping channel/server close', channelTest( (ch, cb, conn) => { const decrementLatch = latch(2, cb); conn.once('error', (err) => { assert.match(err.message, /Connection closed: 541 \(INTERNAL-ERROR\)/); decrementLatch(); }); ch.on('close', (err) => { assert.ifError(err); decrementLatch(); }); open(ch) .then(() => ch.closeBecause('Bye', defs.constants.REPLY_SUCCESS)); }, (send, wait, cb, _ch) => { wait(defs.ChannelClose)() .then(() => send(defs.ConnectionClose, { replyText: 'Got there first', replyCode: defs.constants.INTERNAL_ERROR, classId: 0, methodId: 0, }, 0) ) .then(wait(defs.ConnectionCloseOk)) .then(cb); })); it('double close', channelTest((ch, cb) => { open(ch) .then(() => { ch.closeBecause('First close', defs.constants.REPLY_SUCCESS); // NB no synchronisation, we do this straight away assert.throws(() => { ch.closeBecause('Second close', defs.constants.REPLY_SUCCESS); }, (err) => { assert.match(err.message, /Channel closing/); return true; }); }) .then(cb); }, (send, wait, cb, ch) => { wait(defs.ChannelClose)() .then(() => send(defs.ChannelCloseOk, {}, ch)) .then(cb); })); }); describe('channel machinery', () => { it('RPC', channelTest((ch, cb) => { const decrementLatch = latch(3, cb); open(ch) .then(() => { const fields = { prefetchCount: 10, prefetchSize: 0, global: false }; ch._rpc(defs.BasicQos, fields, defs.BasicQosOk, decrementLatch); ch._rpc(defs.BasicQos, fields, defs.BasicQosOk, decrementLatch); ch._rpc(defs.BasicQos, fields, defs.BasicQosOk, decrementLatch); }) }, (send, wait, cb, ch) => { function sendOk() { send(defs.BasicQosOk, {}, ch); } return wait(defs.BasicQos)() .then(sendOk) .then(wait(defs.BasicQos)) .then(sendOk) .then(wait(defs.BasicQos)) .then(sendOk) .then(cb); })); it('Bad RPC', channelTest( (ch, cb) => { // We want to see the RPC rejected and the channel closed (with an error) const decrementLatch = latch(2, cb); ch.once('error', (err) => { assert.match(err.message, /Expected BasicRecoverOk; got /); assert.strictEqual(505, err.code); assert.strictEqual(60, err.classId); assert.strictEqual(72, err.methodId); decrementLatch(); }); open(ch).then(() => { ch._rpc(defs.BasicRecover, { requeue: true }, defs.BasicRecoverOk, (err) => { assert.match(err.message, /Expected BasicRecoverOk; got /); decrementLatch(); }); }, decrementLatch); }, (send, wait, cb, ch) => wait()() .then(() => send(defs.BasicGetEmpty, { clusterId: '' }, ch)) // oh wait! that was wrong! expect a channel close .then(wait(defs.ChannelClose)) .then(() => send(defs.ChannelCloseOk, {}, ch)) .then(cb), )); it('RPC on closed channel', channelTest( (ch, cb) => { open(ch); const close = new Promise((resolve) => { ch.once('error', (err) => { assert.match(err.message, /Channel closed by server: 504 \(CHANNEL-ERROR\)/); assert.strictEqual(504, err.code); assert.strictEqual(0, err.classId); assert.strictEqual(0, err.methodId); resolve(); }); }); function failureCb(resolve, reject) { return (err) => { if (err !== null) resolve(); else reject(); }; } const fail1 = new Promise((resolve, reject) => ch._rpc(defs.BasicRecover, { requeue: true }, defs.BasicRecoverOk, failureCb(resolve, reject)), ); const fail2 = new Promise((resolve, reject) => ch._rpc(defs.BasicRecover, { requeue: true }, defs.BasicRecoverOk, failureCb(resolve, reject)), ); Promise.all([close, fail1, fail2]).then(cb); }, (send, wait, cb, ch) => { wait(defs.BasicRecover)() .then(() => { send(defs.ChannelClose, { replyText: 'Nuh-uh!', replyCode: defs.constants.CHANNEL_ERROR, methodId: 0, classId: 0, }, ch); return wait(defs.ChannelCloseOk); }) .then(cb); }, )); it('publish all < single chunk threshold', channelTest( (ch, cb) => { open(ch) .then(() => { ch.sendMessage({ exchange: 'foo', routingKey: 'bar', mandatory: false, immediate: false, ticket: 0, }, {}, Buffer.from('foobar')); }) .then(cb); }, (_send, wait, cb, _ch) => { wait(defs.BasicPublish)() .then(wait(defs.BasicProperties)) .then(wait()) // content frame .then((f) => { assert.equal('foobar', f.content.toString()); }) .then(cb); }, )); it('publish content > single chunk threshold', channelTest( (ch, cb) => { open(ch); completes(() => { ch.sendMessage( { exchange: 'foo', routingKey: 'bar', mandatory: false, immediate: false, ticket: 0, }, {}, Buffer.alloc(3000), ); }, cb); }, (_send, wait, cb, _ch) => { wait(defs.BasicPublish)() .then(wait(defs.BasicProperties)) .then(wait()) // content frame .then((f) => { assert.equal(3000, f.content.length); }) .then(cb); } )); it('publish method & headers > threshold', channelTest( (ch, cb) => { open(ch); completes(() => { ch.sendMessage({ exchange: 'foo', routingKey: 'bar', mandatory: false, immediate: false, ticket: 0, }, { headers: { foo: Buffer.alloc(3000) }, }, Buffer.from('foobar')); }, cb); }, (_send, wait, cb, _ch) => { wait(defs.BasicPublish)() .then(wait(defs.BasicProperties)) .then(wait()) // content frame .then((f) => { assert.equal('foobar', f.content.toString()); }) .then(cb); } )); it('publish zero-length message', channelTest((ch, cb) => { open(ch); completes(() => { ch.sendMessage({ exchange: 'foo', routingKey: 'bar', mandatory: false, immediate: false, ticket: 0, }, {}, Buffer.alloc(0)); ch.sendMessage({ exchange: 'foo', routingKey: 'bar', mandatory: false, immediate: false, ticket: 0, }, {}, Buffer.alloc(0)); }, cb); }, (_send, wait, cb, _ch) => { wait(defs.BasicPublish)() .then(wait(defs.BasicProperties)) // no content frame for a zero-length message .then(wait(defs.BasicPublish)) .then(cb); })); it('delivery', channelTest((ch, cb) => { open(ch); ch.on('delivery', (m) => { completes(() => { assert.equal('barfoo', m.content.toString()); }, cb); }); }, (send, _wait, cb, ch) => { completes(() => { send(defs.BasicDeliver, DELIVER_FIELDS, ch, Buffer.from('barfoo')); }, cb); })); it('zero byte msg', channelTest( (ch, cb) => { open(ch); ch.on('delivery', (m) => { completes(() => { assert.deepEqual(Buffer.alloc(0), m.content); }, cb); }); }, (send, _wait, cb, ch) => { completes(() => { send(defs.BasicDeliver, DELIVER_FIELDS, ch, Buffer.from('')); }, cb); } )); it('bad delivery', channelTest((ch, cb) => { const decrementLatch = latch(2, cb); ch.on('error', (err) => { assert.match(err.message, /Expected headers frame after delivery/); assert.strictEqual(505, err.code); assert.strictEqual(60, err.classId); assert.strictEqual(60, err.methodId); decrementLatch(); }); ch.on('close', decrementLatch); open(ch); }, (send, wait, cb, ch) => { send(defs.BasicDeliver, DELIVER_FIELDS, ch); // now send another deliver without having sent the content send(defs.BasicDeliver, DELIVER_FIELDS, ch); return wait(defs.ChannelClose)() .then(() => send(defs.ChannelCloseOk, {}, ch)) .then(cb); })); it('bad content send', channelTest((ch, cb) => { completes(() => { open(ch); assert.throws(() => { ch.sendMessage({ routingKey: 'foo', exchange: 'amq.direct' }, {}, null); }, (err) => { assert.match(err.message, /content is not a buffer/); return true; }); }, cb); }, (_send, _wait, cb, _ch) => cb())); it('bad properties send', channelTest((ch, cb) => { completes(() => { open(ch); assert.throws(() => { ch.sendMessage({ routingKey: 'foo', exchange: 'amq.direct' }, { contentEncoding: 7 }, Buffer.from('foobar')); }, (err) => { assert.match(err.message, /Field 'contentEncoding' is the wrong type/); return true; }); }, cb); }, (_send, _wait, cb, _ch) => cb())); it('bad consumer', channelTest((ch, cb) => { const decrementLatch = latch(2, cb); ch.on('delivery', () => { throw new Error('I am a bad consumer'); }); ch.on('error', (err) => { assert.match(err.message, /I am a bad consumer/); assert.strictEqual(541, err.code); assert.strictEqual(undefined, err.classId); assert.strictEqual(undefined, err.methodId); decrementLatch(); }); ch.on('close', decrementLatch); open(ch); }, (send, wait, cb, ch) => { send(defs.BasicDeliver, DELIVER_FIELDS, ch, Buffer.from('barfoo')); return wait(defs.ChannelClose)() .then(() => send(defs.ChannelCloseOk, {}, ch)) .then(cb); })); it('bad send in consumer', channelTest((ch, cb) => { const decrementLatch = latch(2, cb); ch.on('delivery', () => { ch.sendMessage({ routingKey: 'foo', exchange: 'amq.direct' }, {}, null); // can't send null }); ch.on('error', (err) => { assert.match(err.message, /content is not a buffer/); assert.strictEqual(541, err.code); assert.strictEqual(undefined, err.classId); assert.strictEqual(undefined, err.methodId); decrementLatch(); }); ch.on('close', decrementLatch); open(ch); }, (send, wait, cb, ch) => { const decrementLatch = latch(2, cb); completes(() => { send(defs.BasicDeliver, DELIVER_FIELDS, ch, Buffer.from('barfoo')); }, decrementLatch); return wait(defs.ChannelClose)() .then(() => send(defs.ChannelCloseOk, {}, ch)) .then(decrementLatch); })); it('return', channelTest((ch, cb) => { ch.on('return', (m) => { completes(() => { assert.equal('barfoo', m.content.toString()); }, cb); }); open(ch); }, (send, _wait, cb, ch) => { completes(() => { send(defs.BasicReturn, DELIVER_FIELDS, ch, Buffer.from('barfoo')); }, cb); })); it('cancel', channelTest((ch, cb) => { ch.on('cancel', (f) => { completes(() => { assert.equal('product of society', f.consumerTag); }, cb); }); open(ch); }, (send, _wait, cb, ch) => { completes(() => { send(defs.BasicCancel, { consumerTag: 'product of society', nowait: false, }, ch); }, cb); })); function confirmTest(variety, method) { return it(`confirm ${variety}`, channelTest((ch, done) => { ch.on(variety, (f) => { completes(() => { assert.equal(1, f.deliveryTag); }, done); }); open(ch); }, (send, _wait, done, ch) => { completes(() => { send(method, { deliveryTag: 1, multiple: false, }, ch); }, done); })); } confirmTest('ack', defs.BasicAck); confirmTest('nack', defs.BasicNack); it('out-of-order acks', channelTest((ch, cb) => { const decrementLatch = latch(3, () => { completes(() => { assert.equal(0, ch.unconfirmed.length); assert.equal(4, ch.lwm); }, cb); }); ch.pushConfirmCallback(decrementLatch); ch.pushConfirmCallback(decrementLatch); ch.pushConfirmCallback(decrementLatch); open(ch); }, (send, _wait, cb, ch) => { completes(() => { send(defs.BasicAck, { deliveryTag: 2, multiple: false }, ch); send(defs.BasicAck, { deliveryTag: 3, multiple: false }, ch); send(defs.BasicAck, { deliveryTag: 1, multiple: false }, ch); }, cb); })); it('not all out-of-order acks', channelTest((ch, cb) => { const decrementLatch = latch(2, () => { completes(() => { assert.equal(1, ch.unconfirmed.length); assert.equal(3, ch.lwm); }, cb); }); ch.pushConfirmCallback(decrementLatch); // tag = 1 ch.pushConfirmCallback(decrementLatch); // tag = 2 ch.pushConfirmCallback(() => { assert.fail('Confirm callback should not be called'); }); open(ch); }, (send, _wait, cb, ch) => { completes(() => { send(defs.BasicAck, { deliveryTag: 2, multiple: false }, ch); send(defs.BasicAck, { deliveryTag: 1, multiple: false }, ch); }, cb); })); // Regression test for https://github.com/amqp-node/amqplib/issues/191 // pushConfirmCallback stores `false` for publishes with no callback. // The close drain used `while (cb)` which stops at the first `false`, // silently dropping all callbacks that follow it. it('invokes all pending callbacks on close even when some publishes had no callback', channelTest((ch, cb) => { let called = 0; const countErr = (err) => { if (err) called++; }; ch.pushConfirmCallback(countErr); // tag = 1 — has callback ch.pushConfirmCallback(null); // tag = 2 — no callback, stored as `false` ch.pushConfirmCallback(countErr); // tag = 3 — has callback, dropped by while(cb) bug open(ch).then(() => { ch.toClosed('test close'); completes(() => { assert.equal(2, called); }, cb); }, cb); }, (_send, _wait, cb) => { cb(); })); }); describe('Event handler errors - without handler-error listener', () => { let prevUncaughtExceptionListeners; beforeEach(() => { prevUncaughtExceptionListeners = process.rawListeners('uncaughtException').slice(); process.removeAllListeners('uncaughtException'); }); afterEach(() => { prevUncaughtExceptionListeners.forEach((h) => process.on('uncaughtException', h)); }); it('throw in close handler kills the connection without handler-error listener', channelTest((ch, cb, conn) => { const expectedErr = new Error('user close handler explodes'); let connErrorSeen = false; // The throw propagates to acceptLoop → frameError → onSocketError → connection.emit('error') conn.on('error', (err) => { assert.strictEqual(err, expectedErr); connErrorSeen = true; }); // The ChannelCloseOk buffered before the kill is flushed by the mux after the // stream is ended, producing ERR_STREAM_PUSH_AFTER_EOF. Absorb it and use it // as the signal to end the test, since it fires after the conn error. process.once('uncaughtException', (err) => { assert.strictEqual(err.code, 'ERR_STREAM_PUSH_AFTER_EOF'); assert.ok(connErrorSeen); cb(); }); ch.once('error', (err) => { assert.match(err.message, /Channel closed by server/); }); ch.on('close', () => { throw expectedErr; }); open(ch); }, (send, _wait, cb, ch) => { completes(() => { send(defs.ChannelClose, { replyText: 'Forced close', replyCode: defs.constants.CHANNEL_ERROR, classId: 0, methodId: 0, }, ch); }, cb); })); it('throw in error handler becomes uncaught exception', channelTest((ch, cb) => { const expectedErr = new Error('user error handler explodes'); process.once('uncaughtException', (err) => { assert.strictEqual(err, expectedErr); cb(); }); ch.on('error', () => { throw expectedErr; }); open(ch); }, (send, wait, cb, ch) => { send(defs.ChannelClose, { replyText: 'Forced close', replyCode: defs.constants.CHANNEL_ERROR, classId: 0, methodId: 0, }, ch); wait(defs.ChannelCloseOk)().then(cb, cb); })); it('throw in drain handler becomes uncaught exception', channelTest((ch, cb) => { const expectedErr = new Error('user drain handler explodes'); process.once('uncaughtException', (err) => { assert.strictEqual(err, expectedErr); cb(); }); ch.on('drain', () => { throw expectedErr; }); // Call outside a Promise chain so the throw becomes an uncaughtException open(ch).then(() => setImmediate(() => ch.onBufferDrain())); }, (_send, _wait, cb) => { cb(); })); it('throw in ack handler becomes uncaught exception', channelTest((ch, cb) => { const expectedErr = new Error('user ack handler explodes'); process.once('uncaughtException', (err) => { assert.strictEqual(err, expectedErr); cb(); }); ch.on('ack', () => { throw expectedErr; }); open(ch); }, (send, _wait, cb, ch) => { completes(() => { send(defs.BasicAck, { deliveryTag: 1, multiple: false }, ch); }, cb); })); it('throw in nack handler becomes uncaught exception', channelTest((ch, cb) => { const expectedErr = new Error('user nack handler explodes'); process.once('uncaughtException', (err) => { assert.strictEqual(err, expectedErr); cb(); }); ch.on('nack', () => { throw expectedErr; }); open(ch); }, (send, _wait, cb, ch) => { completes(() => { send(defs.BasicNack, { deliveryTag: 1, multiple: false, requeue: false }, ch); }, cb); })); it('throw in cancel handler becomes uncaught exception', channelTest((ch, cb) => { const expectedErr = new Error('user cancel handler explodes'); process.once('uncaughtException', (err) => { assert.strictEqual(err, expectedErr); cb(); }); ch.on('cancel', () => { throw expectedErr; }); open(ch); }, (send, _wait, cb, ch) => { completes(() => { send(defs.BasicCancel, { consumerTag: 'tag', nowait: false }, ch); }, cb); })); it('throw in delivery handler closes channel with error', channelTest((ch, cb) => { // acceptMessageFrame catches the throw and routes it to closeWithError, // which fires channel 'error' then 'close' — it does NOT become an uncaughtException. const expectedErr = new Error('user delivery handler explodes'); const decrementLatch = latch(2, cb); ch.on('error', (err) => { assert.strictEqual(err, expectedErr); decrementLatch(); }); ch.on('close', decrementLatch); ch.on('delivery', () => { throw expectedErr; }); open(ch); }, (send, wait, cb, ch) => { send(defs.BasicDeliver, DELIVER_FIELDS, ch, Buffer.from('hello')); wait(defs.ChannelClose)() .then(() => send(defs.ChannelCloseOk, {}, ch)) .then(cb, cb); })); it('throw in return handler closes channel with error', channelTest((ch, cb) => { // acceptMessageFrame catches the throw and routes it to closeWithError. const expectedErr = new Error('user return handler explodes'); const decrementLatch = latch(2, cb); ch.on('error', (err) => { assert.strictEqual(err, expectedErr); decrementLatch(); }); ch.on('close', decrementLatch); ch.on('return', () => { throw expectedErr; }); open(ch); }, (send, wait, cb, ch) => { send(defs.BasicReturn, DELIVER_FIELDS, ch, Buffer.from('hello')); wait(defs.ChannelClose)() .then(() => send(defs.ChannelCloseOk, {}, ch)) .then(cb, cb); })); }); describe('Event handler errors - with handler-error listener', () => { let prevUncaughtExceptionListeners; beforeEach(() => { prevUncaughtExceptionListeners = process.rawListeners('uncaughtException').slice(); process.removeAllListeners('uncaughtException'); }); afterEach(() => { prevUncaughtExceptionListeners.forEach((h) => process.on('uncaughtException', h)); }); it('throw in close handler is delivered via handler-error event', channelTest((ch, cb) => { const expectedErr = new Error('user close handler explodes'); ch.on('handler-error', (err, event) => { assert.strictEqual(err, expectedErr); assert.strictEqual(event, 'close'); cb(); }); // A server-initiated ChannelClose always fires 'error' before 'close' (line 291 in channel.js). // Handle it so only the throw from the close handler reaches handler-error. ch.once('error', (err) => { assert.match(err.message, /Channel closed by server/); }); ch.on('close', () => { throw expectedErr; }); open(ch); }, (send, wait, cb, ch) => { send(defs.ChannelClose, { replyText: 'Forced close', replyCode: defs.constants.CHANNEL_ERROR, classId: 0, methodId: 0, }, ch); wait(defs.ChannelCloseOk)().then(cb, cb); })); it('throw in error handler is delivered via handler-error event', channelTest((ch, cb) => { const expectedErr = new Error('user error handler explodes'); ch.on('handler-error', (err, event) => { assert.strictEqual(err, expectedErr); assert.strictEqual(event, 'error'); cb(); }); ch.on('error', () => { throw expectedErr; }); open(ch); }, (send, wait, cb, ch) => { send(defs.ChannelClose, { replyText: 'Forced close', replyCode: defs.constants.CHANNEL_ERROR, classId: 0, methodId: 0, }, ch); wait(defs.ChannelCloseOk)().then(cb, cb); })); it('throw in drain handler is delivered via handler-error event', channelTest((ch, cb) => { const expectedErr = new Error('user drain handler explodes'); ch.on('handler-error', (err, event) => { assert.strictEqual(err, expectedErr); assert.strictEqual(event, 'drain'); cb(); }); ch.on('drain', () => { throw expectedErr; }); open(ch).then(() => setImmediate(() => ch.onBufferDrain())); }, (_send, _wait, cb) => { cb(); })); it('throw in ack handler is delivered via handler-error event', channelTest((ch, cb) => { const expectedErr = new Error('user ack handler explodes'); ch.on('handler-error', (err, event) => { assert.strictEqual(err, expectedErr); assert.strictEqual(event, 'ack'); cb(); }); ch.on('ack', () => { throw expectedErr; }); open(ch); }, (send, _wait, cb, ch) => { completes(() => { send(defs.BasicAck, { deliveryTag: 1, multiple: false }, ch); }, cb); })); it('throw in nack handler is delivered via handler-error event', channelTest((ch, cb) => { const expectedErr = new Error('user nack handler explodes'); ch.on('handler-error', (err, event) => { assert.strictEqual(err, expectedErr); assert.strictEqual(event, 'nack'); cb(); }); ch.on('nack', () => { throw expectedErr; }); open(ch); }, (send, _wait, cb, ch) => { completes(() => { send(defs.BasicNack, { deliveryTag: 1, multiple: false, requeue: false }, ch); }, cb); })); it('throw in cancel handler is delivered via handler-error event', channelTest((ch, cb) => { const expectedErr = new Error('user cancel handler explodes'); ch.on('handler-error', (err, event) => { assert.strictEqual(err, expectedErr); assert.strictEqual(event, 'cancel'); cb(); }); ch.on('cancel', () => { throw expectedErr; }); open(ch); }, (send, _wait, cb, ch) => { completes(() => { send(defs.BasicCancel, { consumerTag: 'tag', nowait: false }, ch); }, cb); })); it('throw in delivery handler is delivered via handler-error event', channelTest((ch, cb) => { const expectedErr = new Error('user delivery handler explodes'); ch.on('handler-error', (err, event) => { assert.strictEqual(err, expectedErr); assert.strictEqual(event, 'delivery'); cb(); }); ch.on('delivery', () => { throw expectedErr; }); open(ch); }, (send, _wait, cb, ch) => { completes(() => { send(defs.BasicDeliver, DELIVER_FIELDS, ch, Buffer.from('hello')); }, cb); })); it('throw in return handler is delivered via handler-error event', channelTest((ch, cb) => { const expectedErr = new Error('user return handler explodes'); ch.on('handler-error', (err, event) => { assert.strictEqual(err, expectedErr); assert.strictEqual(event, 'return'); cb(); }); ch.on('return', () => { throw expectedErr; }); open(ch); }, (send, _wait, cb, ch) => { completes(() => { send(defs.BasicReturn, DELIVER_FIELDS, ch, Buffer.from('hello')); }, cb); })); it('throw in handler-error handler becomes uncaught exception', channelTest((ch, cb) => { const expectedErr = new Error('handler-error handler explodes'); process.once('uncaughtException', (err) => { assert.strictEqual(err, expectedErr); cb(); }); ch.on('handler-error', () => { throw expectedErr; }); ch.on('ack', () => { throw new Error('user ack handler explodes'); }); open(ch); }, (send, _wait, cb, ch) => { completes(() => { send(defs.BasicAck, { deliveryTag: 1, multiple: false }, ch); }, cb); })); }); }); amqp-node-amqplib-263c8a3/test/channel_api.test.js000066400000000000000000000511211520012327700221040ustar00rootroot00000000000000const { describe, it } = require('node:test'); const assert = require('node:assert'); const api = require('../channel_api'); const util = require('./lib/util'); const schedule = util.schedule; const randomString = util.randomString; const promisify = require('node:util').promisify; const URL = process.env.URL || 'amqp://localhost'; const QUEUE_OPTS = { durable: false }; const EX_OPTS = { durable: false }; function ignore() {} describe('connect', () => { it('at all', () => connect().then((c) => c.close())); it('create channel', () => withChannel(ignore)); }); describe('updateSecret', () => { it('updateSecret', () => { return connect().then((c) => c.updateSecret(Buffer.from('new secret'), 'no reason') .finally(() => c.close()) ); }); it('emits update-secret-ok event', () => { return connect().then((c) => new Promise((resolve, reject) => { c.on('update-secret-ok', resolve); c.updateSecret(Buffer.from('new secret'), 'no reason').catch(reject); }).finally(() => c.close()) ); }); }); describe('assert, check, delete', () => { it('assert and check queue', () => { return withChannel((ch) => { return ch.assertQueue('test.check-queue', QUEUE_OPTS) .then((qok) => assert.strictEqual(qok.queue, 'test.check-queue')) .then(() => ch.checkQueue('test.check-queue')) .then((qok) => assert.strictEqual(qok.queue, 'test.check-queue')) }); }); it('assert and check exchange', () => { return withChannel((ch) => { return ch.assertExchange('test.check-exchange', 'direct', EX_OPTS) .then((eok) => assert.strictEqual(eok.exchange, 'test.check-exchange')) .then(() => ch.checkExchange('test.check-exchange')) .then((eok) => assert.ok(eok)); }); }); it('fail on reasserting queue with different options', () => { return withChannel((ch) => { ch.once('error', ignore); return ch.assertQueue('test.reassert-queue', { durable: false, autoDelete: true }) .then(() => assert.rejects(() => ch.assertQueue('test.reassert-queue', { durable: false, autoDelete: false }), (err) => { assert.match(err.message, /PRECONDITION_FAILED/); assert.strictEqual(406, err.code); assert.strictEqual(50, err.classId); assert.strictEqual(10, err.methodId); return true; })); }); }); it("fail on checking a queue that's not there", () => { return withChannel((ch) => { ch.once('error', ignore); return assert.rejects(() => ch.checkQueue(`test.random-${randomString()}`), (err) => { assert.match(err.message, /NOT_FOUND/); assert.strictEqual(404, err.code); assert.strictEqual(50, err.classId); assert.strictEqual(10, err.methodId); return true; }); }); }); it("fail on checking an exchange that's not there", () => { return withChannel((ch) => { ch.once('error', ignore); return assert.rejects(() => ch.checkExchange(`test.random-${randomString()}`), (err) => { assert.match(err.message, /NOT_FOUND/); assert.strictEqual(404, err.code); return true; }); }); }); it('fail on reasserting exchange with different type', () => { return withChannel((ch) => { ch.once('error', ignore); const ex = 'test.reassert-ex'; return ch.assertExchange(ex, 'fanout', EX_OPTS) .then(() => assert.rejects(() => ch.assertExchange(ex, 'direct', EX_OPTS), (err) => { assert.match(err.message, /PRECONDITION_FAILED/); assert.strictEqual(406, err.code); return true; })); }); }); it('channel break on publishing to non-exchange', () => { return withChannel((ch) => new Promise((resolve) => { ch.once('error', (err) => { assert.match(err.message, /NOT_FOUND/); assert.strictEqual(404, err.code); resolve(); }); ch.publish(randomString(), '', Buffer.from('foobar')); }) ); }); it('delete queue', () => { return withChannel((ch) => { return ch.assertQueue('test.delete-queue', QUEUE_OPTS) .then(() => ch.deleteQueue('test.delete-queue')) .then(() => { ch.once('error', ignore); return assert.rejects(() => ch.checkQueue('test.delete-queue'), (err) => { assert.match(err.message, /NOT_FOUND/); assert.strictEqual(404, err.code); return true; }); }); }); }); it('delete exchange', () => { return withChannel((ch) => { return ch.assertExchange('test.delete-exchange', 'fanout', EX_OPTS) .then(() => ch.deleteExchange('test.delete-exchange')) .then(() => { ch.once('error', ignore); return assert.rejects(() => ch.checkExchange('test.delete-exchange'), (err) => { assert.match(err.message, /NOT_FOUND/); assert.strictEqual(404, err.code); return true; }); }); }); }); }); describe('sendMessage', () => { it('send to queue and get from queue', () => { const msg = randomString(); return withChannel((ch) => ch.assertQueue('test.send-to-q', QUEUE_OPTS) .then(() => ch.purgeQueue('test.send-to-q')) .then(() => { ch.sendToQueue('test.send-to-q', Buffer.from(msg)); return waitForMessages('test.send-to-q'); }) .then(() => ch.get('test.send-to-q', { noAck: true })) .then((m) => { assert(m); assert.equal(msg, m.content.toString()); }) ); }); it('send (and get) zero content to queue', () => { return withChannel((ch) => ch.assertQueue('test.send-to-q', QUEUE_OPTS) .then(() => ch.purgeQueue('test.send-to-q')) .then(() => { ch.sendToQueue('test.send-to-q', Buffer.alloc(0)); return waitForMessages('test.send-to-q'); }) .then(() => ch.get('test.send-to-q', { noAck: true })) .then((m) => { assert(m); assert.deepEqual(Buffer.alloc(0), m.content); }) ); }); }); describe('get', () => { it('returns false when queue is empty', () => { return withChannel((ch) => ch.assertQueue('test.get-empty', QUEUE_OPTS) .then(() => ch.purgeQueue('test.get-empty')) .then(() => ch.get('test.get-empty', { noAck: true })) .then((m) => assert.strictEqual(false, m)) ); }); it('returns a message when queue has messages', () => { const msg = randomString(); return withChannel((ch) => ch.assertQueue('test.get-message', QUEUE_OPTS) .then(() => ch.purgeQueue('test.get-message')) .then(() => { ch.sendToQueue('test.get-message', Buffer.from(msg)); return waitForMessages('test.get-message'); }) .then(() => ch.get('test.get-message', { noAck: true })) .then((m) => { assert(m); assert.equal(msg, m.content.toString()); }) ); }); it('rejects with a proper error when queue does not exist', () => { return withChannel((ch) => { ch.once('error', ignore); return assert.rejects(() => ch.get('', {}), (err) => { assert(err instanceof Error); assert.match(err.message, /NOT_FOUND/); assert.strictEqual(404, err.code); assert.strictEqual(60, err.classId); assert.strictEqual(70, err.methodId); return true; }); }); }); }); describe('binding, consuming', () => { // bind, publish, get it('route message', () => { const msg = randomString(); return withChannel((ch) => ch.assertExchange('test.route-message', 'fanout', EX_OPTS) .then(() => ch.assertQueue('test.route-message-q', QUEUE_OPTS)) .then(() => ch.purgeQueue('test.route-message-q')) .then(() => ch.bindQueue('test.route-message-q', 'test.route-message', '', {})) .then(() => { ch.publish('test.route-message', '', Buffer.from(msg)); return waitForMessages('test.route-message-q'); }) .then(() => ch.get('test.route-message-q', { noAck: true })) .then((m) => { assert(m); assert.equal(msg, m.content.toString()); }) ); }); // send to queue, purge, get-empty it('purge queue', () => { return withChannel((ch) => ch.assertQueue('test.purge-queue', QUEUE_OPTS) .then(() => { ch.sendToQueue('test.purge-queue', Buffer.from('foobar')); return waitForMessages('test.purge-queue'); }) .then(() => ch.purgeQueue('test.purge-queue')) .then(() => ch.get('test.purge-queue', { noAck: true })) .then((m) => { assert(!m); // get-empty }) ); }); // bind again, unbind, publish, get-empty it('unbind queue', () => { const viabinding = randomString(); const direct = randomString(); return withChannel((ch) => ch.assertExchange('test.unbind-queue-ex', 'fanout', EX_OPTS) .then(() => ch.assertQueue('test.unbind-queue', QUEUE_OPTS)) .then(() => ch.purgeQueue('test.unbind-queue')) .then(() => ch.bindQueue('test.unbind-queue', 'test.unbind-queue-ex', '', {})) .then(() => { ch.publish('test.unbind-queue-ex', '', Buffer.from('foobar')); return waitForMessages('test.unbind-queue'); }) .then(() => ch.get('test.unbind-queue', { noAck: true })) .then((m) => assert(m)) // message got through! .then(() => ch.unbindQueue('test.unbind-queue', 'test.unbind-queue-ex', '', {})) .then(() => { // via the no-longer-existing binding ch.publish('test.unbind-queue-ex', '', Buffer.from(viabinding)); // direct to the queue ch.sendToQueue('test.unbind-queue', Buffer.from(direct)); return waitForMessages('test.unbind-queue'); }) .then(() => ch.get('test.unbind-queue')) .then((m) => { // the direct to queue message got through, the via-binding message (sent first) did not assert.equal(direct, m.content.toString()); }) ); }); // To some extent this is now just testing semantics of the server, // but we can at least try out a few settings, and consume. it('consume via exchange-exchange binding', () => { const msg = randomString(); return withChannel((ch) => ch.assertExchange('test.ex-ex-binding1', 'direct', EX_OPTS) .then(() => ch.assertExchange('test.ex-ex-binding2', 'fanout', { durable: false, internal: true })) .then(() => ch.assertQueue('test.ex-ex-binding-q', QUEUE_OPTS)) .then(() => ch.purgeQueue('test.ex-ex-binding-q')) .then(() => ch.bindExchange('test.ex-ex-binding2', 'test.ex-ex-binding1', 'test.routing.key', {})) .then(() => ch.bindQueue('test.ex-ex-binding-q', 'test.ex-ex-binding2', '', {})) .then(() => new Promise((resolve, reject) => { ch.consume('test.ex-ex-binding-q', (m) => { if (m.content.toString() === msg) resolve(); else reject(new Error('Wrong message')); }, { noAck: true }).then(() => { ch.publish('test.ex-ex-binding1', 'test.routing.key', Buffer.from(msg)); }); })) ); }); // bind again, unbind, publish, get-empty it('unbind exchange', () => { const viabinding = randomString(); const direct = randomString(); return withChannel((ch) => ch.assertExchange('test.unbind-ex-source', 'fanout', EX_OPTS) .then(() => ch.assertExchange('test.unbind-ex-dest', 'fanout', EX_OPTS)) .then(() => ch.assertQueue('test.unbind-ex-queue', QUEUE_OPTS)) .then(() => ch.purgeQueue('test.unbind-ex-queue')) .then(() => ch.bindExchange('test.unbind-ex-dest', 'test.unbind-ex-source', '', {})) .then(() => ch.bindQueue('test.unbind-ex-queue', 'test.unbind-ex-dest', '', {})) .then(() => { ch.publish('test.unbind-ex-source', '', Buffer.from('foobar')); return waitForMessages('test.unbind-ex-queue'); }) .then(() => ch.get('test.unbind-ex-queue', { noAck: true })) .then((m) => assert(m)) // message got through! .then(() => ch.unbindExchange('test.unbind-ex-dest', 'test.unbind-ex-source', '', {})) .then(() => { // via the no-longer-existing binding ch.publish('test.unbind-ex-source', '', Buffer.from(viabinding)); // direct to the queue ch.sendToQueue('test.unbind-ex-queue', Buffer.from(direct)); return waitForMessages('test.unbind-ex-queue'); }) .then(() => ch.get('test.unbind-ex-queue')) .then((m) => { // the direct to queue message got through, the via-binding message (sent first) did not assert.equal(direct, m.content.toString()); }) ); }); it('cancel consumer', () => { let ctag; return withChannel((ch) => ch.assertQueue('test.consumer-cancel', QUEUE_OPTS) .then(() => ch.purgeQueue('test.consumer-cancel')) .then(() => new Promise((resolve) => { ch.consume('test.consumer-cancel', resolve, { noAck: true }) .then((ok) => { ctag = ok.consumerTag; ch.sendToQueue('test.consumer-cancel', Buffer.from('foo')); }); })) // first message arrived via consumer .then(() => ch.cancel(ctag)) .then(() => { ch.sendToQueue('test.consumer-cancel', Buffer.from('bar')); return waitForMessages('test.consumer-cancel'); }) .then(() => ch.get('test.consumer-cancel', { noAck: true })) .then((m) => { // message arrived in queue but NOT via the cancelled consumer assert.equal('bar', m.content.toString()); }) ); }); it('cancelled consumer', () => { return withChannel((ch) => ch.assertQueue('test.cancelled-consumer') .then(() => ch.purgeQueue('test.cancelled-consumer')) .then(() => new Promise((resolve, reject) => { ch.consume('test.cancelled-consumer', (msg) => { if (msg === null) resolve(); else reject(new Error('Message not expected')); }).then(() => ch.deleteQueue('test.cancelled-consumer')); })) ); }); // ack, by default, removes a single message from the queue it('ack', () => { const msg1 = randomString(); const msg2 = randomString(); return withChannel((ch) => ch.assertQueue('test.ack', QUEUE_OPTS) .then(() => ch.purgeQueue('test.ack')) .then(() => { ch.sendToQueue('test.ack', Buffer.from(msg1)); ch.sendToQueue('test.ack', Buffer.from(msg2)); return waitForMessages('test.ack', 2); }) .then(() => ch.get('test.ack', { noAck: false })) .then((m) => { assert.equal(msg1, m.content.toString()); ch.ack(m); // %%% is there a race here? may depend on rabbitmq-specific semantics return ch.get('test.ack'); }) .then((m) => { assert(m); assert.equal(msg2, m.content.toString()); }) ); }); // Nack, by default, puts a message back on the queue (where in the queue is up to the server) it('nack', () => { const msg1 = randomString(); return withChannel((ch) => ch.assertQueue('test.nack', QUEUE_OPTS) .then(() => ch.purgeQueue('test.nack')) .then(() => { ch.sendToQueue('test.nack', Buffer.from(msg1)); return waitForMessages('test.nack'); }) .then(() => ch.get('test.nack', { noAck: false })) .then((m) => { assert.equal(msg1, m.content.toString()); ch.nack(m); return waitForMessages('test.nack'); }) .then(() => ch.get('test.nack')) .then((m) => { assert(m); assert.equal(msg1, m.content.toString()); }) ); }); // reject is a near-synonym for nack, the latter of which is not available in earlier RabbitMQ (or in AMQP proper). it('reject', () => { const msg1 = randomString(); return withChannel((ch) => ch.assertQueue('test.reject', QUEUE_OPTS) .then(() => ch.purgeQueue('test.reject')) .then(() => { ch.sendToQueue('test.reject', Buffer.from(msg1)); return waitForMessages('test.reject'); }) .then(() => ch.get('test.reject', { noAck: false })) .then((m) => { assert.equal(msg1, m.content.toString()); ch.reject(m); return waitForMessages('test.reject'); }) .then(() => ch.get('test.reject')) .then((m) => { assert(m); assert.equal(msg1, m.content.toString()); }) ); }); it('prefetch', () => { return withChannel((ch) => ch.assertQueue('test.prefetch', QUEUE_OPTS) .then(() => ch.purgeQueue('test.prefetch')) .then(() => ch.prefetch(1)) .then(() => { ch.sendToQueue('test.prefetch', Buffer.from('foobar')); ch.sendToQueue('test.prefetch', Buffer.from('foobar')); return waitForMessages('test.prefetch', 2); }) .then(() => new Promise((resolve) => { let messageCount = 0; ch.consume('test.prefetch', (msg) => { ch.ack(msg); if (++messageCount > 1) resolve(messageCount); }, { noAck: false }); })) .then((c) => assert.equal(2, c)) ); }); it('close', () => { return withChannel((ch) => ch.close()); }); }); describe('confirms', () => { it('message is confirmed', () => { return withConfirmChannel((ch) => ch.assertQueue('test.confirm-message', QUEUE_OPTS) .then(() => ch.purgeQueue('test.confirm-message')) .then(() => ch.sendToQueue('test.confirm-message', Buffer.from('bleep'))) ); }); // Usually one can provoke the server into confirming more than one // message in an ack by simply sending a few messages in quick // succession; a bit unscientific I know. Luckily we can eavesdrop on // the acknowledgements coming through to see if we really did get a // multi-ack. it('multiple confirms', () => { return withConfirmChannel((ch) => ch.assertQueue('test.multiple-confirms', QUEUE_OPTS) .then(() => ch.purgeQueue('test.multiple-confirms')) .then(() => { let multipleRainbows = false; ch.on('ack', (a) => { if (a.multiple) multipleRainbows = true; }); function prod(num) { const cs = []; for (let i = 0; i < num; i++) { cs.push(promisify((cb) => ch.sendToQueue('test.multiple-confirms', Buffer.from('bleep'), {}, cb))()); } return Promise.all(cs).then(() => { if (multipleRainbows) return true; else if (num > 500) throw new Error(`Couldn't provoke the server into multi-acking with ${num} messages; giving up`); else return prod(num * 2); }); } return prod(5); }) ); }); it('wait for confirms', () => { return withConfirmChannel((ch) => { for (let i = 0; i < 1000; i++) { ch.publish('', '', Buffer.from('foobar'), {}); } return ch.waitForConfirms(); }); }); it('works when channel is closed', () => { return withConfirmChannel((ch) => { for (let i = 0; i < 1000; i++) { ch.publish('', '', Buffer.from('foobar'), {}); } return ch.close() .then(() => assert.rejects(() => ch.waitForConfirms(), (e) => { assert.strictEqual(e.message, 'channel closed'); return true; })); }); }); it('publish on closed channel does not leak callbacks', () => { return withConfirmChannel((ch) => { return ch.close().then(() => { for (let i = 0; i < 10; i++) { try { ch.publish('', '', Buffer.from('x'), {}, () => {}); } catch (_) {} } assert.strictEqual(ch.unconfirmed.length, 0); }); }); }); }); function connect() { return api.connect(URL); } function withChannel(cb) { return connect().then((c) => c.createChannel().then(cb).finally(() => c.close())); } function withConfirmChannel(cb) { return connect().then((c) => c.createConfirmChannel().then(cb).finally(() => c.close())); } function waitForQueue(q, condition) { return connect(URL).then((c) => c.createChannel().then((ch) => ch.checkQueue(q).then((_qok) => { function check() { return ch.checkQueue(q).then((qok) => { if (condition(qok)) { c.close(); return qok; } else schedule(check); }); } return check(); }), ), ); } function waitForMessages(q, num) { const min = num === undefined ? 1 : num; return waitForQueue(q, (qok) => qok.messageCount >= min); } amqp-node-amqplib-263c8a3/test/codec.test.js000066400000000000000000000142461520012327700207270ustar00rootroot00000000000000const { describe, it } = require('node:test'); const assert = require('node:assert'); const { forAll } = require('claire'); const codec = require('../lib/codec'); const defs = require('../lib/defs'); const amqp = require('./lib/data'); const { removeExplicitTypes, assertEqualModuloDefaults } = require('./lib/util'); // These just test known encodings; to generate the answers I used RabbitMQ's binary generator module. const testCases = [ // integers ['byte', { byte: 112 }, [4, 98, 121, 116, 101, 98, 112]], ['byte max value', { byte: 127 }, [4, 98, 121, 116, 101, 98, 127]], ['byte min value', { byte: -128 }, [4, 98, 121, 116, 101, 98, 128]], ['< -128 promoted to signed short', { short: -129 }, [5, 115, 104, 111, 114, 116, 115, 255, 127]], ['> 127 promoted to short', { short: 128 }, [5, 115, 104, 111, 114, 116, 115, 0, 128]], ['< 2^15 still a short', { short: 0x7fff }, [5, 115, 104, 111, 114, 116, 115, 127, 255]], ['-2^15 still a short', { short: -0x8000 }, [5, 115, 104, 111, 114, 116, 115, 128, 0]], ['>= 2^15 promoted to int', { int: 0x8000 }, [3, 105, 110, 116, 73, 0, 0, 128, 0]], ['< -2^15 promoted to int', { int: -0x8001 }, [3, 105, 110, 116, 73, 255, 255, 127, 255]], ['< 2^31 still an int', { int: 0x7fffffff }, [3, 105, 110, 116, 73, 127, 255, 255, 255]], ['>= -2^31 still an int', { int: -0x80000000 }, [3, 105, 110, 116, 73, 128, 0, 0, 0]], ['>= 2^31 promoted to long', { long: 0x80000000 }, [4, 108, 111, 110, 103, 108, 0, 0, 0, 0, 128, 0, 0, 0]], ['< -2^31 promoted to long', { long: -0x80000001 }, [4, 108, 111, 110, 103, 108, 255, 255, 255, 255, 127, 255, 255, 255]], // floating point ['float value', { double: 0.5 }, [6, 100, 111, 117, 98, 108, 101, 100, 63, 224, 0, 0, 0, 0, 0, 0]], ['negative float value', { double: -0.5 }, [6, 100, 111, 117, 98, 108, 101, 100, 191, 224, 0, 0, 0, 0, 0, 0]], // %% test some boundaries of precision? // string ['string', { string: 'boop' }, [6, 115, 116, 114, 105, 110, 103, 83, 0, 0, 0, 4, 98, 111, 111, 112]], // buffer -> byte array ['byte array from buffer', { bytes: Buffer.from([1, 2, 3, 4]) }, [5, 98, 121, 116, 101, 115, 120, 0, 0, 0, 4, 1, 2, 3, 4]], // boolean, void ['true', { bool: true }, [4, 98, 111, 111, 108, 116, 1]], ['false', { bool: false }, [4, 98, 111, 111, 108, 116, 0]], ['null', { void: null }, [4, 118, 111, 105, 100, 86]], // array, object ['array', { array: [6, true, 'foo'] }, [5, 97, 114, 114, 97, 121, 65, 0, 0, 0, 12, 98, 6, 116, 1, 83, 0, 0, 0, 3, 102, 111, 111]], [ 'object', { object: { foo: 'bar', baz: 12 } }, [6, 111, 98, 106, 101, 99, 116, 70, 0, 0, 0, 18, 3, 102, 111, 111, 83, 0, 0, 0, 3, 98, 97, 114, 3, 98, 97, 122, 98, 12], ], // exotic types [ 'timestamp', { timestamp: { '!': 'timestamp', value: 1357212277527 } }, [9, 116, 105, 109, 101, 115, 116, 97, 109, 112, 84, 0, 0, 1, 60, 0, 39, 219, 23], ], [ 'decimal', { decimal: { '!': 'decimal', value: { digits: 2345, places: 2 } } }, [7, 100, 101, 99, 105, 109, 97, 108, 68, 2, 0, 0, 9, 41], ], ['float', { float: { '!': 'float', value: 0.1 } }, [5, 102, 108, 111, 97, 116, 102, 61, 204, 204, 205]], [ 'unsignedbyte', { unsignedbyte: { '!': 'unsignedbyte', value: 255 } }, [12, 117, 110, 115, 105, 103, 110, 101, 100, 98, 121, 116, 101, 66, 255], ], [ 'unsignedshort', { unsignedshort: { '!': 'unsignedshort', value: 65535 } }, [13, 117, 110, 115, 105, 103, 110, 101, 100, 115, 104, 111, 114, 116, 117, 255, 255], ], [ 'unsignedint', { unsignedint: { '!': 'unsignedint', value: 4294967295 } }, [11, 117, 110, 115, 105, 103, 110, 101, 100, 105, 110, 116, 105, 255, 255, 255, 255], ], ]; function bufferToArray(b) { return Array.prototype.slice.call(b); } describe('Codec', () => { describe('Implicit encodings', () => { testCases.forEach(([name, val, expect]) => { it(name, () => { const buffer = Buffer.alloc(1000); const size = codec.encodeTable(buffer, val, 0); const result = buffer.subarray(4, size); assert.deepEqual(expect, bufferToArray(result)); }); }); }); // Whole frames function roundtrip_table(t) { const buf = Buffer.alloc(4096); const size = codec.encodeTable(buf, t, 0); const decoded = codec.decodeFields(buf.subarray(4, size)); // ignore the length-prefix try { assert.deepEqual(removeExplicitTypes(t), decoded); } catch { return false; } return true; } function roundtrips(T) { return forAll(T).satisfy((v) => roundtrip_table({ value: v })); } describe('Roundtrip values', () => { [ amqp.Octet, amqp.ShortStr, amqp.LongStr, amqp.UShort, amqp.ULong, amqp.ULongLong, amqp.UShort, amqp.Short, amqp.Long, amqp.Bit, amqp.Decimal, amqp.Timestamp, amqp.UnsignedByte, amqp.UnsignedShort, amqp.UnsignedInt, amqp.Double, amqp.Float, amqp.FieldArray, amqp.FieldTable, ].forEach((T) => { it(`${T.toString()} roundtrip`, roundtrips(T).asTest()); }); }); function roundtripMethod(Method) { return forAll(Method).satisfy((method) => { const buf = defs.encodeMethod(method.id, 0, method.fields); // FIXME depends on framing, ugh const fs1 = defs.decode(method.id, buf.subarray(11, buf.length)); assertEqualModuloDefaults(method, fs1); return true; }); } function roundtripProperties(Properties) { return forAll(Properties).satisfy((properties) => { const buf = defs.encodeProperties(properties.id, 0, properties.size, properties.fields); // FIXME depends on framing, ugh const fs1 = defs.decode(properties.id, buf.subarray(19, buf.length)); assert.equal(properties.size, Number(buf.readBigUInt64BE(11))); assertEqualModuloDefaults(properties, fs1); return true; }); } describe('Roundtrip methods', () => { amqp.methods.forEach((Method) => { it(`${Method.toString()} roundtrip`, roundtripMethod(Method).asTest()); }); }); describe('Roundtrip properties', () => { amqp.properties.forEach((Properties) => { it(`${Properties.toString()} roundtrip`, roundtripProperties(Properties).asTest()); }); }); }); amqp-node-amqplib-263c8a3/test/connect.test.js000066400000000000000000000114071520012327700212770ustar00rootroot00000000000000const { describe, it, afterEach } = require('node:test'); const assert = require('node:assert'); const net = require('node:net'); const connect = require('../lib/connect').connect; const defs = require('../lib/defs'); const { plain, amqplain } = require('../lib/credentials') const { latch, runServer } = require('./lib/util'); describe('Connect', () => { describe('Connect API', () => { let connection; afterEach((_t, done) => { if (connection) connection.close(() => done()) else done(); }); it('Connection refused', (_t, done) => { connect('amqp://localhost:23450', {}, (err) => { assert.strictEqual(err.code, 'ECONNREFUSED'); done(); }); }); // %% this ought to fail the promise, rather than throwing an error it('bad URL', (_t, done) => { assert.throws(() => connect('blurble'), (err) => { assert.match(err.message, /Expected amqp: or amqps: as the protocol/); return true; }); done(); }); it('wrongly typed open option', (_t, done) => { connect('amqp://localhost?frameMax=NOT_A_NUMBER', {}, (err) => { assert.match(err.message, /Field 'frameMax' is the wrong type/); done(); }); }); it('serverProperties', (_t, done) => { connect({ protocol: 'amqp', hostname: 'localhost' }, {}, (err, _connection) => { connection = _connection; assert.ifError(err); assert.equal(connection.serverProperties.product, 'RabbitMQ'); done(); }); }); it('using custom heartbeat option', (_t, done) => { connect({ heartbeat: 20 }, {}, (err, _connection) => { assert.ifError(err); connection = _connection; assert.strictEqual(connection.heartbeat, 20); done(); }); }); it('wrongly typed heartbeat option', (_t, done) => { connect('amqp://localhost?heartbeat=NOT_A_NUMBER', {}, (err) => { assert.match(err.message, /Field 'heartbeat' is the wrong type/); done(); }); }); it('using plain credentials', (_t, done) => { connect('amqp://localhost', { credentials: plain('guest', 'guest') }, (err, _connection) => { assert.ifError(err); connection = _connection; done(); }); }); it('using amqplain credentials', (_t, done) => { connect('amqp://localhost', { credentials: amqplain('guest', 'guest') }, (err, _connection) => { assert.ifError(err); connection = _connection; done(); }); }); it('ipv6', (_t, done) => { connect('amqp://[::1]', {}, (err, _connection) => { assert.ifError(err); connection = _connection; done(); }); }); it('using unsupported mechanism', (_t, done) => { const credentials = { mechanism: 'UNSUPPORTED', response: () => Buffer.from('') }; connect('amqp://localhost', { credentials }, (err) => { assert.match(err.message, /SASL mechanism UNSUPPORTED is not provided by the server/); done(); }); }); it('with a given connection timeout', (_t, done) => { const timeoutServer = net.createServer(() => { }).listen(31991); connect('amqp://localhost:31991', { timeout: 50 }, (err) => { timeoutServer.close(); assert.match(err.message, /ETIMEDOUT/); done(); }); }); }); describe('Errors on connect', () => { let server; afterEach(() => { if (server) server.close(); }); it('closes underlying connection on authentication error', (_t, done) => { const decrementLatch = latch(2, done); server = net.createServer((socket) => { socket.once('data', (protocolHeader) => { assert.deepStrictEqual(protocolHeader, Buffer.from(`AMQP${String.fromCharCode(0, 0, 9, 1)}`)); runServer(socket, (send, wait) => { send(defs.ConnectionStart, { versionMajor: 0, versionMinor: 9, serverProperties: {}, mechanisms: Buffer.from('PLAIN'), locales: Buffer.from('en_US'), }); wait(defs.ConnectionStartOk)().then(() => { send(defs.ConnectionClose, { replyCode: 403, replyText: 'ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN', classId: 0, methodId: 0, }); }); }); }); // Wait for the connection to be closed after the authentication error socket.once('end', () => decrementLatch()); }); server.listen(0); connect(`amqp://localhost:${server.address().port}`, {}, (err) => { assert.match(err.message, /Handshake terminated by server: 403 \(ACCESS-REFUSED\)/); decrementLatch(); }); }); }); }); amqp-node-amqplib-263c8a3/test/connection.test.js000066400000000000000000000635721520012327700220170ustar00rootroot00000000000000const { describe, it, beforeEach, afterEach } = require('node:test'); const assert = require('node:assert'); const defs = require('../lib/defs'); const Connection = require('../lib/connection').Connection; const HEARTBEAT = require('../lib/frame').HEARTBEAT; const HB_BUF = require('../lib/frame').HEARTBEAT_BUF; const OPEN_OPTS = require('./lib/data').OPEN_OPTS; const heartbeat = require('../lib/heartbeat'); const { latch, handshake, runServer, socketPair } = require('./lib/util'); const LOG_ERRORS = process.env.LOG_ERRORS; function connectionTest(client, server) { return (_t, done) => { const decrementLatch = latch(2, done); const pair = socketPair(); const c = new Connection(pair.client); if (LOG_ERRORS) c.on('error', console.warn); client(c, decrementLatch); // NB only not a race here because the writes are synchronous const protocolHeader = pair.server.read(8); assert.deepEqual(Buffer.from(`AMQP${String.fromCharCode(0, 0, 9, 1)}`), protocolHeader); runServer(pair.server, (send, wait) => { server(send, wait, decrementLatch, pair.server); }); }; } describe('Connection', () => { describe('Connection errors', () => { it('socket close during open', (_t, done) => { // RabbitMQ itself will take at least 3 seconds to close the socket // in the event of a handshake problem. Instead of using a live // connection, I'm just going to pretend. const pair = socketPair(); const conn = new Connection(pair.client); pair.server.on('readable', () => pair.server.end()); conn.open({}, (err) => { assert.ok(err); assert.match(err.message, /Socket closed abruptly during opening handshake/); done(); }); }); it('bad frame during open', (_t, done) => { const ss = socketPair(); const conn = new (require('../lib/connection').Connection)(ss.client); ss.server.on('readable', () => { ss.server.write(Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])); }); conn.open({}, (err) => { assert.ok(err); assert.match(err.message, /Invalid frame/); done(); }); }); }); describe('Connection open', () => { it('happy', connectionTest((c, cb) => { c.open(OPEN_OPTS, (err) => { assert.ifError(err); cb(); }) }, (send, wait, cb) => { handshake(send, wait) .then(cb, cb); })); it('wrong first frame', connectionTest((c, cb) => { c.open(OPEN_OPTS, (err) => { assert.ok(err); assert.match(err.message, /Expected ConnectionStart; got { // bad server! bad! whatever were you thinking? send(defs.ConnectionTune, { channelMax: 0, heartbeat: 0, frameMax: 0 }) cb(); })); it('unexpected socket close', connectionTest((c, cb) => { c.open(OPEN_OPTS, (err) => { assert.ok(err); assert.match(err.message, /Socket closed abruptly during opening handshake/); cb(); }); }, (send, wait, cb, socket) => { send(defs.ConnectionStart, { versionMajor: 0, versionMinor: 9, serverProperties: {}, mechanisms: Buffer.from('PLAIN'), locales: Buffer.from('en_US'), }); return wait(defs.ConnectionStartOk)() .then(() => socket.end()) .then(cb, cb); })); }); describe('Connection running', () => { it('wrong frame on channel 0', connectionTest((c, cb) => { c.once('error', (err) => { assert.match(err.message, /Unexpected frame on channel 0/); cb() }); c.open(OPEN_OPTS); }, (send, wait, cb) => { handshake(send, wait) // there's actually nothing that would plausibly be sent to a // just opened connection, so this is violating more than one // rule. Nonetheless. .then(() => send(defs.ChannelOpenOk, { channelId: Buffer.from('') }, 0)) .then(wait(defs.ConnectionClose)) .then((_close) => send(defs.ConnectionCloseOk, {}, 0)) .then(cb, cb); })); it('unopened channel', connectionTest((c, cb) => { c.once('error', (err) => { assert.match(err.message, /Frame on unknown channel/); cb(); }); c.open(OPEN_OPTS); }, (send, wait, cb) => { handshake(send, wait) // there's actually nothing that would plausibly be sent to a // just opened connection, so this is violating more than one // rule. Nonetheless. .then(() => send(defs.ChannelOpenOk, { channelId: Buffer.from('') }, 3)) .then(wait(defs.ConnectionClose)) .then((_close) => send(defs.ConnectionCloseOk, {}, 0)) .then(cb, cb); })); it('unexpected socket close', connectionTest((c, cb) => { const decrementLatch = latch(2, cb); c.on('error', (err) => { assert.match(err.message, /Unexpected close/); decrementLatch(); }); c.on('close', (err) => { assert.match(err.message, /Unexpected close/); decrementLatch(); }); c.open(OPEN_OPTS, (err) => { assert.ifError(err); c.sendHeartbeat(); }); }, (send, wait, cb, socket) => { handshake(send, wait) .then(wait()) .then(() => socket.end()) .then(cb, cb); })); it('connection.blocked', connectionTest((c, cb) => { c.on('blocked', (reason) => { assert.strictEqual(reason, 'felt like it'); cb(); }); c.open(OPEN_OPTS); }, (send, wait, cb, _socket) => { handshake(send, wait) .then(() => send(defs.ConnectionBlocked, { reason: 'felt like it' }, 0)) .then(cb, cb); })); it('connection.unblocked', connectionTest((c, cb) => { c.on('unblocked', () => cb()); c.open(OPEN_OPTS); }, (send, wait, cb, _socket) => { handshake(send, wait) .then(() => send(defs.ConnectionUnblocked, {}, 0)) .then(cb, cb); })); }); describe('Connection close', () => { it('happy', connectionTest((c, cb) => { const decrementLatch = latch(2, cb); c.on('close', () => decrementLatch()); c.open(OPEN_OPTS, (err) => { assert.ifError(err); c.close((err) => { assert.ifError(err); decrementLatch(); }); }); }, (send, wait, cb) => { handshake(send, wait) .then(wait(defs.ConnectionClose)) .then((_close) => send(defs.ConnectionCloseOk, {})) .then(cb, cb); })); it('interleaved close frames', connectionTest((c, cb) => { const decrementLatch = latch(2, cb); c.on('close', () => decrementLatch()); c.open(OPEN_OPTS, (err) => { assert.ifError(err); c.close((err) => { assert.ifError(err); decrementLatch(); }); }); }, (send, wait, cb) => { handshake(send, wait) .then(wait(defs.ConnectionClose)) .then(() => send(defs.ConnectionClose, { replyText: 'Ha!', replyCode: defs.constants.REPLY_SUCCESS, methodId: 0, classId: 0, })) .then(wait(defs.ConnectionCloseOk)) .then(() => send(defs.ConnectionCloseOk, {})) .then(cb, cb); })); it('server error close', connectionTest((c, cb) => { const decrementLatch = latch(2, cb); c.once('close', (err) => { assert.match(err.message, /Connection closed: 541 \(INTERNAL-ERROR\) with message "Begone"/); decrementLatch(); }); c.once('error', (err) => { assert.match(err.message, /Connection closed: 541 \(INTERNAL-ERROR\) with message "Begone"/); decrementLatch(); }); c.open(OPEN_OPTS); }, (send, wait, cb) => { handshake(send, wait) .then(() => send(defs.ConnectionClose, { replyText: 'Begone', replyCode: defs.constants.INTERNAL_ERROR, methodId: 0, classId: 0, })) .then(wait(defs.ConnectionCloseOk)) .then(cb, cb); })); it('operator-intiated close', connectionTest((c, cb) => { c.once('close', (err) => { assert.match(err.message, /Connection closed: 320 \(CONNECTION-FORCED\) with message "Begone"/); cb(); }); c.once('error', (err) => assert.fail(`Unexepcted error: ${err.message}`)); c.open(OPEN_OPTS); }, (send, wait, cb) => { handshake(send, wait) .then(() => send(defs.ConnectionClose, { replyText: 'Begone', replyCode: defs.constants.CONNECTION_FORCED, methodId: 0, classId: 0, })) .then(wait(defs.ConnectionCloseOk)) .then(cb, cb); })); it('double close', connectionTest((c, cb) => { c.open(OPEN_OPTS, (err) => { assert.ifError(err); c.close(); // NB no synchronisation, we do this straight away assert.throws(() => c.close()); cb(); }); }, (send, wait, cb) => { handshake(send, wait) .then(wait(defs.ConnectionClose)) .then(() => send(defs.ConnectionCloseOk, {})) .then(cb, cb); })); it('close while blocked closes immediately', connectionTest((c, cb) => { const decrementLatch = latch(2, cb); c.on('close', () => decrementLatch()); c.open(OPEN_OPTS, (err) => { assert.ifError(err); c.once('blocked', () => { c.close((err) => { assert.ifError(err); decrementLatch(); }); }); }); }, (send, wait, cb) => { handshake(send, wait) .then(() => send(defs.ConnectionBlocked, { reason: 'memory' }, 0)) .then(wait(defs.ConnectionClose)) // deliberately do NOT send ConnectionCloseOk — client must close anyway .then(cb, cb); })); }); describe('Event handler errors - without handler-event listener', () => { let prevUncaughtExceptionListeners; beforeEach(() => { prevUncaughtExceptionListeners = process.rawListeners('uncaughtException').slice(); process.removeAllListeners('uncaughtException'); }); afterEach(() => { prevUncaughtExceptionListeners.forEach((h) => process.on('uncaughtException', h)); heartbeat.UNITS_TO_MS = 1000; }); it('throw in close handler from server-initiated close is swallowed without handler-error listener', connectionTest((c, cb) => { c.on('close', () => { throw new Error('user handler explodes'); }); c.open(OPEN_OPTS); cb(); }, (send, wait, cb) => { handshake(send, wait) .then(() => send(defs.ConnectionClose, { replyText: 'Begone', replyCode: defs.constants.CONNECTION_FORCED, methodId: 0, classId: 0, })) .then(wait(defs.ConnectionCloseOk)) .then(cb, cb); })); it('throw in close handler from client-initiated close is swallowed without handler-error listener', connectionTest((c, cb) => { c.open(OPEN_OPTS, (err) => { assert.ifError(err); c.on('close', () => { throw new Error('user handler explodes on client close'); }); c.close(); cb(); }); }, (send, wait, cb) => { handshake(send, wait) .then(wait(defs.ConnectionClose)) .then(() => send(defs.ConnectionCloseOk, {})) .then(cb, cb); })); it('throw in error handler becomes uncaught exception', connectionTest((c, cb) => { const expectedErr = new Error('user error handler explodes'); process.once('uncaughtException', (err) => { assert.strictEqual(err, expectedErr); cb(); }); c.on('error', () => { throw expectedErr; }); c.open(OPEN_OPTS); }, (send, wait, cb) => { handshake(send, wait) .then(() => send(defs.ConnectionClose, { replyText: 'Begone', replyCode: defs.constants.INTERNAL_ERROR, methodId: 0, classId: 0, })) .then(wait(defs.ConnectionCloseOk)) .then(cb, cb); })); it('throw in blocked handler becomes uncaught exception', connectionTest((c, cb) => { const expectedErr = new Error('user blocked handler explodes'); process.once('uncaughtException', (err) => { assert.strictEqual(err, expectedErr); cb(); }); c.on('blocked', () => { throw expectedErr; }); c.open(OPEN_OPTS); }, (send, wait, cb) => { handshake(send, wait) .then(() => send(defs.ConnectionBlocked, { reason: 'memory' }, 0)) .then(cb, cb); })); it('throw in unblocked handler becomes uncaught exception', connectionTest((c, cb) => { const expectedErr = new Error('user unblocked handler explodes'); process.once('uncaughtException', (err) => { assert.strictEqual(err, expectedErr); cb(); }); c.on('unblocked', () => { throw expectedErr; }); c.open(OPEN_OPTS); }, (send, wait, cb) => { handshake(send, wait) .then(() => send(defs.ConnectionUnblocked, {}, 0)) .then(cb, cb); })); it('throw in update-secret-ok handler becomes uncaught exception', connectionTest((c, cb) => { const expectedErr = new Error('user update-secret-ok handler explodes'); process.once('uncaughtException', (err) => { assert.strictEqual(err, expectedErr); cb(); }); c.open(OPEN_OPTS, (err) => { assert.ifError(err); c.on('update-secret-ok', () => { throw expectedErr; }); c._updateSecret(Buffer.from('new secret'), 'reason', () => {}); }); }, (send, wait, cb) => { handshake(send, wait) .then(wait(defs.ConnectionUpdateSecret)) .then(() => send(defs.ConnectionUpdateSecretOk, {}, 0)) .then(cb, cb); })); it('throw in error handler from closeWithError becomes uncaught exception', connectionTest((c, cb) => { const expectedErr = new Error('user error handler explodes on closeWithError'); process.once('uncaughtException', (err) => { assert.strictEqual(err, expectedErr); cb(); }); c.once('error', (err) => { assert.match(err.message, /Unexpected frame on channel 0/); throw expectedErr; }); c.open(OPEN_OPTS); }, (send, wait, cb) => { handshake(send, wait) .then(() => send(defs.ChannelOpenOk, { channelId: Buffer.from('') }, 0)) .then(cb, cb); })); it('throw in error handler from onSocketError', connectionTest((c, cb) => { const expectedErr = new Error('user error handler explodes on socket error'); process.once('uncaughtException', (err) => { assert.strictEqual(err, expectedErr); cb(); }); c.once('error', (err) => { assert.match(err.message, /Unexpected close/); throw expectedErr; }); c.open(OPEN_OPTS, (err) => { assert.ifError(err); c.sendHeartbeat(); }); }, (send, wait, cb, socket) => { handshake(send, wait) .then(wait()) .then(() => socket.end()) .then(cb, cb); })); it('throw in error handler from heartbeat timeout', connectionTest((c, cb) => { heartbeat.UNITS_TO_MS = 20; const expectedErr = new Error('user error handler explodes on heartbeat timeout'); const opts = Object.create(OPEN_OPTS); opts.heartbeat = 1; process.once('uncaughtException', (err) => { assert.strictEqual(err, expectedErr); c.heartbeater.clear(); cb(); }); c.on('error', (err) => { assert.match(err.message, /Heartbeat timeout/); throw expectedErr; }); c.open(opts); }, (send, wait, cb) => { handshake(send, wait) .then(cb, cb); // conspicuously not sending anything ... })); }); describe('Event handler errors - with handler-error listener', () => { let prevUncaughtExceptionListeners; beforeEach(() => { prevUncaughtExceptionListeners = process.rawListeners('uncaughtException').slice(); process.removeAllListeners('uncaughtException'); }); afterEach(() => { prevUncaughtExceptionListeners.forEach((h) => process.on('uncaughtException', h)); heartbeat.UNITS_TO_MS = 1000; }); it('throw in close handler is delivered via handler-error event', connectionTest((c, cb) => { const expectedErr = new Error('user close handler explodes'); c.on('handler-error', (err, event) => { assert.strictEqual(err, expectedErr); assert.strictEqual(event, 'close'); cb(); }); c.on('close', () => { throw expectedErr; }); c.open(OPEN_OPTS); }, (send, wait, cb) => { handshake(send, wait) .then(() => send(defs.ConnectionClose, { replyText: 'Begone', replyCode: defs.constants.CONNECTION_FORCED, methodId: 0, classId: 0, })) .then(wait(defs.ConnectionCloseOk)) .then(cb, cb); })); it('throw in error handler is delivered via handler-error event', connectionTest((c, cb) => { const expectedErr = new Error('user error handler explodes'); c.on('handler-error', (err, event) => { assert.strictEqual(err, expectedErr); assert.strictEqual(event, 'error'); cb(); }); c.on('error', () => { throw expectedErr; }); c.open(OPEN_OPTS); }, (send, wait, cb) => { handshake(send, wait) .then(() => send(defs.ConnectionClose, { replyText: 'Begone', replyCode: defs.constants.INTERNAL_ERROR, methodId: 0, classId: 0, })) .then(wait(defs.ConnectionCloseOk)) .then(cb, cb); })); it('throw in blocked handler is delivered via handler-error event', connectionTest((c, cb) => { const expectedErr = new Error('user blocked handler explodes'); c.on('handler-error', (err, event) => { assert.strictEqual(err, expectedErr); assert.strictEqual(event, 'blocked'); cb(); }); c.on('blocked', () => { throw expectedErr; }); c.open(OPEN_OPTS); }, (send, wait, cb) => { handshake(send, wait) .then(() => send(defs.ConnectionBlocked, { reason: 'memory' }, 0)) .then(cb, cb); })); it('throw in unblocked handler is delivered via handler-error event', connectionTest((c, cb) => { const expectedErr = new Error('user unblocked handler explodes'); c.on('handler-error', (err, event) => { assert.strictEqual(err, expectedErr); assert.strictEqual(event, 'unblocked'); cb(); }); c.on('unblocked', () => { throw expectedErr; }); c.open(OPEN_OPTS); }, (send, wait, cb) => { handshake(send, wait) .then(() => send(defs.ConnectionUnblocked, {}, 0)) .then(cb, cb); })); it('throw in update-secret-ok handler is delivered via handler-error event', connectionTest((c, cb) => { const expectedErr = new Error('user update-secret-ok handler explodes'); c.on('handler-error', (err, event) => { assert.strictEqual(err, expectedErr); assert.strictEqual(event, 'update-secret-ok'); cb(); }); c.open(OPEN_OPTS, (err) => { assert.ifError(err); c.on('update-secret-ok', () => { throw expectedErr; }); c._updateSecret(Buffer.from('new secret'), 'reason', () => {}); }); }, (send, wait, cb) => { handshake(send, wait) .then(wait(defs.ConnectionUpdateSecret)) .then(() => send(defs.ConnectionUpdateSecretOk, {}, 0)) .then(cb, cb); })); it('throw in error handler from heartbeat timeout is delivered via handler-error event', connectionTest((c, cb) => { heartbeat.UNITS_TO_MS = 20; const expectedErr = new Error('user error handler explodes on heartbeat timeout'); const opts = Object.create(OPEN_OPTS); opts.heartbeat = 1; c.on('handler-error', (err, event) => { assert.strictEqual(err, expectedErr); assert.strictEqual(event, 'error'); c.heartbeater.clear(); cb(); }); c.on('error', (err) => { assert.match(err.message, /Heartbeat timeout/); throw expectedErr; }); c.open(opts); }, (send, wait, cb) => { handshake(send, wait) .then(cb, cb); // conspicuously not sending anything ... })); it('throw in error handler from closeWithError is delivered via handler-error event', connectionTest((c, cb) => { const expectedErr = new Error('user error handler explodes on closeWithError'); c.on('handler-error', (err, event) => { assert.strictEqual(err, expectedErr); assert.strictEqual(event, 'error'); cb(); }); c.once('error', (err) => { assert.match(err.message, /Unexpected frame on channel 0/); throw expectedErr; }); c.open(OPEN_OPTS); }, (send, wait, cb) => { handshake(send, wait) .then(() => send(defs.ChannelOpenOk, { channelId: Buffer.from('') }, 0)) .then(cb, cb); })); it('throw in error handler from onSocketError is delivered via handler-error event', connectionTest((c, cb) => { const expectedErr = new Error('user error handler explodes on socket error'); c.on('handler-error', (err, event) => { assert.strictEqual(err, expectedErr); assert.strictEqual(event, 'error'); cb(); }); c.once('error', (err) => { assert.match(err.message, /Unexpected close/); throw expectedErr; }); c.open(OPEN_OPTS, (err) => { assert.ifError(err); c.sendHeartbeat(); }); }, (send, wait, cb, socket) => { handshake(send, wait) .then(wait()) .then(() => socket.end()) .then(cb, cb); })); it('throw in handler-error handler becomes uncaught exception', connectionTest((c, cb) => { const expectedErr = new Error('handler-error handler explodes'); process.once('uncaughtException', (err) => { assert.strictEqual(err, expectedErr); cb(); }); c.on('handler-error', () => { throw expectedErr; }); c.on('blocked', () => { throw new Error('user blocked handler explodes'); }); c.open(OPEN_OPTS); }, (send, wait, cb) => { handshake(send, wait) .then(() => send(defs.ConnectionBlocked, { reason: 'memory' }, 0)) .then(cb, cb); })); }); describe('heartbeats', () => { beforeEach(() => { heartbeat.UNITS_TO_MS = 20; }); afterEach(() => { heartbeat.UNITS_TO_MS = 1000; }); it('send heartbeat after open', connectionTest((c, cb) => { const opts = Object.create(OPEN_OPTS); opts.heartbeat = 1; // Don't leave the error waiting to happen for the next test c.on('error', (err) => { assert.match(err.message, /Heartbeat timeout/) }); c.open(opts); cb() }, (send, wait, cb, socket) => { let timer; handshake(send, wait) .then(() => { timer = setInterval(() => socket.write(HB_BUF), heartbeat.UNITS_TO_MS); }) .then(wait()) .then((hb) => { assert.strictEqual(hb, HEARTBEAT); cb(); }).finally(() => clearInterval(timer)); })); it('detect lack of heartbeats', connectionTest((c, cb) => { const opts = Object.create(OPEN_OPTS); opts.heartbeat = 1; c.once('error', (err) => { assert.match(err.message, /Heartbeat timeout/) cb(); }); c.open(opts); }, (send, wait, cb, _socket) => { handshake(send, wait) .then(cb, cb); // conspicuously not sending anything ... })); it('without a heartbeat option, server suggested value is used', connectionTest((c, cb) => { // OPEN_OPTS.heartbeat is null — no preference set by client c.once('error', (err) => { assert.match(err.message, /Heartbeat timeout/); cb(); }); c.open(OPEN_OPTS, (err) => { assert.ifError(err); assert.strictEqual(c.heartbeat, 1); }); }, (send, wait, cb) => { send(defs.ConnectionStart, { versionMajor: 0, versionMinor: 9, serverProperties: {}, mechanisms: Buffer.from('PLAIN'), locales: Buffer.from('en_US'), }); wait(defs.ConnectionStartOk)() .then(() => send(defs.ConnectionTune, { channelMax: 0, heartbeat: 1, frameMax: 0 })) .then(wait(defs.ConnectionTuneOk)) .then(wait(defs.ConnectionOpen)) .then(() => send(defs.ConnectionOpenOk, { knownHosts: '' })) .then(cb, cb); })); it('heartbeat:0 means no heartbeater is started', connectionTest((c, cb) => { const opts = Object.create(OPEN_OPTS); opts.heartbeat = 0; c.open(opts, (err) => { assert.ifError(err); assert.strictEqual(c.heartbeat, 0); assert.strictEqual(c.heartbeater, null); cb(); }); }, (send, wait, cb) => { send(defs.ConnectionStart, { versionMajor: 0, versionMinor: 9, serverProperties: {}, mechanisms: Buffer.from('PLAIN'), locales: Buffer.from('en_US'), }); wait(defs.ConnectionStartOk)() .then(() => send(defs.ConnectionTune, { channelMax: 0, heartbeat: 60, frameMax: 0 })) .then(wait(defs.ConnectionTuneOk)) .then(wait(defs.ConnectionOpen)) .then(() => send(defs.ConnectionOpenOk, { knownHosts: '' })) .then(cb, cb); })); it('heartbeat:0 disables heartbeats even if server suggests one', connectionTest((c, cb) => { const opts = Object.create(OPEN_OPTS); opts.heartbeat = 0; c.open(opts, cb); }, (send, wait, cb) => { send(defs.ConnectionStart, { versionMajor: 0, versionMinor: 9, serverProperties: {}, mechanisms: Buffer.from('PLAIN'), locales: Buffer.from('en_US'), }); wait(defs.ConnectionStartOk)() .then(() => send(defs.ConnectionTune, { channelMax: 0, heartbeat: 60, frameMax: 0 })) .then(wait(defs.ConnectionTuneOk)) .then((tuneOk) => { assert.strictEqual(tuneOk.fields.heartbeat, 0); }) .then(wait(defs.ConnectionOpen)) .then(() => send(defs.ConnectionOpenOk, { knownHosts: '' })) .then(cb, cb); })); }); }); amqp-node-amqplib-263c8a3/test/data.test.js000066400000000000000000000026151520012327700205600ustar00rootroot00000000000000const { forAll } = require('claire'); const { Octet, ShortStr, LongStr, UShort, ULong, ULongLong, Short, Long, LongLong, Bit, Double, Float, Decimal, Timestamp, FieldTable, FieldArray } = require('./lib/data'); const domainProps = [ [Octet, (n) => n >= 0 && n < 256], [ShortStr, (s) => typeof s === 'string' && s.length < 256], [LongStr, (s) => Buffer.isBuffer(s)], [UShort, (n) => n >= 0 && n <= 0xffff], [ULong, (n) => n >= 0 && n <= 0xffffffff], [ULongLong, (n) => n >= 0 && n <= 0xffffffffffffffff], [Short, (n) => n >= -0x8000 && n <= 0x8000], [Long, (n) => n >= -0x80000000 && n < 0x80000000], [LongLong, (n) => n >= Number.MIN_SAFE_INTEGER && n <= Number.MAX_SAFE_INTEGER], [Bit, (b) => typeof b === 'boolean'], [Double, (f) => !Number.isNaN(f) && Number.isFinite(f)], [Float, (f) => !Number.isNaN(f) && Number.isFinite(f) && Math.log(Math.abs(f)) * Math.LOG10E < 309], [Decimal, (d) => d['!'] === 'decimal' && d.value['places'] <= 255 && d.value['digits'] <= 0xffffffff], [Timestamp, (t) => t['!'] === 'timestamp'], [FieldTable, (t) => typeof t === 'object'], [FieldArray, (a) => Array.isArray(a)], ]; if (global.describe) { describe('Domains', () => { domainProps.forEach((p) => { console.log(p[0].toString()) it(`${p[0]} domain`, forAll(p[0]).satisfy((v) => { console.log({ v }); return p[1](v) }).asTest({ times: 1 })); }); }); } amqp-node-amqplib-263c8a3/test/frame.test.js000066400000000000000000000124701520012327700207410ustar00rootroot00000000000000const { describe, it } = require('node:test'); const assert = require('node:assert'); const connection = require('../lib/connection'); const Frames = connection.Connection; const HEARTBEAT = require('../lib/frame').HEARTBEAT; const Stream = require('node:stream'); const PassThrough = Stream.PassThrough; const { choice, forAll, repeat, label, sequence, transform, sized } = require('claire'); const amqp = require('./lib/data'); const assertEqualModuloDefaults = require('./lib/util').assertEqualModuloDefaults; const defs = require('../lib/defs'); // We'll need to supply a stream which we manipulate ourselves function inputs() { // don't coalesce buffers, since that could mess up properties (e.g., encoded frame size) return new PassThrough({ objectMode: true }); } const HB = Buffer.from([ defs.constants.FRAME_HEARTBEAT, 0, 0, // channel 0 0, 0, 0, 0, // zero size defs.constants.FRAME_END, ]); describe('Frame', () => { describe('Explicit parsing', () => { it('Parse heartbeat', () => { const input = inputs(); const frames = new Frames(input); input.write(HB); assert.ok(frames.recvFrame() === HEARTBEAT); assert.ok(!frames.recvFrame()); }); it('Parse partitioned', () => { const input = inputs(); const frames = new Frames(input); input.write(HB.subarray(0, 3)); assert.ok(!frames.recvFrame()); input.write(HB.subarray(3)); assert.ok(frames.recvFrame() === HEARTBEAT); assert.ok(!frames.recvFrame()); }); function testBogusFrame(name, bytes) { it(name, (_t, done) => { const input = inputs(); const frames = new Frames(input); frames.frameMax = 5; //for the max frame test input.write(Buffer.from(bytes)); frames.step((err, _frame) => { if (err != null) done(); else assert.fail('Was a bogus frame!'); }); }); } testBogusFrame('Wrong sized frame', [ defs.constants.FRAME_BODY, 0, 0, 0, 0, 0, 0, // zero length 65, // but a byte! defs.constants.FRAME_END, ]); testBogusFrame('Unknown method frame', [ defs.constants.FRAME_METHOD, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, // garbage ID defs.constants.FRAME_END, ]); }); const Trace = label('frame trace', repeat(choice.apply(choice, amqp.methods))); describe('Parsing', () => { function testPartitioning(partition) { return forAll(Trace) .satisfy((t) => { const bufs = []; const input = inputs(); const frames = new Frames(input); let i = 0; let ex; frames.accept = (f) => { // A minor hack to make sure we get the assertion exception; // otherwise, it's just a test that we reached the line // incrementing `i` for each frame. try { assertEqualModuloDefaults(t[i], f.fields); } catch (e) { ex = e; } i++; }; t.forEach((f) => { f.channel = 0; bufs.push(defs.encodeMethod(f.id, 0, f.fields)); }); partition(bufs).forEach((chunk) => input.write(chunk)); frames.acceptLoop(); if (ex) throw ex; return i === t.length; }) .asTest({ times: 20 }); } it('Parse trace of methods', testPartitioning((bufs) => bufs)); it("Parse concat'd methods", testPartitioning((bufs) => [Buffer.concat(bufs)])); it('Parse partitioned methods', testPartitioning((bufs) => { const full = Buffer.concat(bufs); const onethird = Math.floor(full.length / 3); const twothirds = 2 * onethird; return [full.subarray(0, onethird), full.subarray(onethird, twothirds), full.subarray(twothirds)]; })); }); const FRAME_MAX_MAX = 4096 * 4; const FRAME_MAX_MIN = 4096; const FrameMax = amqp.rangeInt('frame max', FRAME_MAX_MIN, FRAME_MAX_MAX); const Body = sized((_n) => Math.floor(Math.random() * FRAME_MAX_MAX), repeat(amqp.Octet)); const Content = transform( (args) => ({ method: args[0].fields, header: args[1].fields, body: Buffer.from(args[2]), }), sequence(amqp.methods['BasicDeliver'], amqp.properties['BasicProperties'], Body), ); describe('Content framing', () => { it('Adhere to frame max', forAll(Content, FrameMax) .satisfy((content, max) => { const input = inputs(); const frames = new Frames(input); frames.frameMax = max; frames.sendMessage(0, defs.BasicDeliver, content.method, defs.BasicProperties, content.header, content.body); let _i = 0; let largest = 0; let frame = input.read(); while (frame) { _i++; if (frame.length > largest) largest = frame.length; if (frame.length > max) { return false; } frame = input.read(); } // The ratio of frames to 'contents' should always be >= 2 // (one properties frame and at least one content frame); > 2 // indicates fragmentation. The largest is always, of course <= frame max //console.log('Frames: %d; frames per message: %d; largest frame %d', _i, _i / t.length, largest); return true; }) .asTest()); }); }); amqp-node-amqplib-263c8a3/test/lib/000077500000000000000000000000001520012327700170755ustar00rootroot00000000000000amqp-node-amqplib-263c8a3/test/lib/data.js000066400000000000000000000136501520012327700203510ustar00rootroot00000000000000const { data: arb, transform, repeat, label, sequence, asGenerator, sized, recursive, choice, Undefined } = require('claire'); const defs = require('../../lib/defs'); // These aren't exported in claire/index. so I could have to reproduce them I guess. function choose(a, b) { return Math.random() * (b - a) + a; } function chooseInt(a, b) { return Math.floor(choose(a, b)); } function rangeInt(name, a, b) { return label(name, asGenerator((_) => chooseInt(a, b))); } function toFloat32(i) { const b = Buffer.alloc(4); b.writeFloatBE(i, 0); return b.readFloatBE(0); } function floatChooser(maxExp) { return () => { let n = Number.NaN; while (Number.isNaN(n)) { const mantissa = Math.random() * 2 - 1; const exponent = chooseInt(0, maxExp); n = mantissa ** exponent; } return n; }; } function explicitType(t, underlying) { return label( t, transform((n) => ({ '!': t, value: n }), underlying), ); } // FIXME null, byte array, others? const Octet = rangeInt('octet', 0, 255); const ShortStr = label('shortstr', transform((s) => s.substr(0, 255), arb.Str)); const LongStr = label('longstr', transform((bytes) => Buffer.from(bytes), repeat(Octet))); const UShort = rangeInt('short-uint', 0, 0xffff); const ULong = rangeInt('long-uint', 0, 0xffffffff); const ULongLong = rangeInt('longlong-uint', 0, Number.MAX_SAFE_INTEGER); const Short = rangeInt('short-int', -0x8000, 0x7fff); const Long = rangeInt('long-int', -0x80000000, 0x7fffffff); const LongLong = rangeInt('longlong-int', Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER); const Bit = label('bit', arb.Bool); const Double = label('double', asGenerator(floatChooser(308))); const Float = label('float', transform(toFloat32, floatChooser(38))); const Timestamp = label('timestamp', transform((n) => ({ '!': 'timestamp', value: n }), ULongLong)); const Decimal = label('decimal', transform((args) => ({ '!': 'decimal', value: { places: args[1], digits: args[0] } }), sequence(arb.UInt, Octet))); const UnsignedByte = label('unsignedbyte', transform((n) => ({ '!': 'unsignedbyte', value: n }), Octet)); const UnsignedShort = label('unsignedshort', transform((n) => ({ '!': 'unsignedshort', value: n }), UShort)); const UnsignedInt = label('unsignedint', transform((n) => ({ '!': 'unsignedint', value: n }), ULong)); const Byte = rangeInt('byte', -128, 127); // Explicitly typed values const ExByte = explicitType('byte', Byte); const ExInt8 = explicitType('int8', Byte); const ExShort = explicitType('short', Short); const ExInt16 = explicitType('int16', Short); const ExInt = explicitType('int', Long); const ExInt32 = explicitType('int32', Long); const ExLong = explicitType('long', LongLong); const ExInt64 = explicitType('int64', LongLong); const FieldArray = label('field-array', recursive(() => arb.Array( arb.Null, LongStr, ShortStr, Octet, UShort, ULong, ULongLong, Byte, Short, Long, LongLong, ExByte, ExInt8, ExShort, ExInt16, ExInt, ExInt32, ExLong, ExInt64, Bit, Float, Double, FieldTable, FieldArray, ), ), ); const FieldTable = label('field-table', recursive(() => sized( () => 5, arb.Object( arb.Null, LongStr, ShortStr, Octet, UShort, ULong, ULongLong, Byte, Short, Long, LongLong, ExByte, ExInt8, ExShort, ExInt16, ExInt, ExInt32, ExLong, ExInt64, Bit, Float, Double, FieldArray, FieldTable, ), ), ), ); // For methods and properties (as opposed to field table values) it's // easier just to accept and produce numbers for timestamps. const ArgTimestamp = label('timestamp', ULongLong); // These are the domains used in method arguments const ARG_TYPES = { octet: Octet, shortstr: ShortStr, longstr: LongStr, short: UShort, long: ULong, longlong: ULongLong, bit: Bit, table: FieldTable, timestamp: ArgTimestamp, }; function argtype(thing) { if (thing.default === undefined) { return ARG_TYPES[thing.type]; } else { return choice(ARG_TYPES[thing.type], Undefined); } } function zipObject(vals, names) { const obj = {}; vals.forEach((v, i) => { obj[names[i]] = v; }); return obj; } function name(arg) { return arg.name; } function method(info) { const domain = sequence.apply(null, info.args.map(argtype)); const names = info.args.map(name); return label( info.name, transform((fieldVals) => ({ id: info.id, fields: zipObject(fieldVals, names) }), domain), ); } function properties(info) { const types = info.args.map(argtype); types.unshift(ULongLong); // size const domain = sequence.apply(null, types); const names = info.args.map(name); return label(info.name, transform((fieldVals) => ({ id: info.id, size: fieldVals[0], fields: zipObject(fieldVals.slice(1), names) }), domain), ); } const methods = []; const propertieses = []; for (const k in defs) { if (k.substr(0, 10) === 'methodInfo') { methods.push(method(defs[k])); methods[defs[k].name] = method(defs[k]); } else if (k.substr(0, 14) === 'propertiesInfo') { propertieses.push(properties(defs[k])); propertieses[defs[k].name] = properties(defs[k]); } } const OPEN_OPTS = { // start-ok clientProperties: {}, mechanism: 'PLAIN', response: Buffer.from(['', 'guest', 'guest'].join(String.fromCharCode(0))), locale: 'en_US', // tune-ok channelMax: 0, frameMax: 0, heartbeat: null, // open virtualHost: '/', capabilities: '', insist: 0, }; module.exports = { Octet, ShortStr, LongStr, UShort, ULong, ULongLong, Short, Long, LongLong, Bit, Double, Float, Timestamp, Decimal, UnsignedByte, UnsignedShort, UnsignedInt, FieldArray, FieldTable, methods, properties: propertieses, rangeInt, OPEN_OPTS, }; amqp-node-amqplib-263c8a3/test/lib/util.js000066400000000000000000000145421520012327700204160ustar00rootroot00000000000000const crypto = require('node:crypto'); const PassThrough = require('node:stream').PassThrough; const Connection = require('../../lib/connection').Connection; const defs = require('../../lib/defs'); const assert = require('node:assert'); const schedule = typeof setImmediate === 'function' ? setImmediate : process.nextTick; function randomString() { const hash = crypto.createHash('sha1'); hash.update(crypto.randomBytes(64)); return hash.digest('base64'); } // Set up a socket pair {client, server}, such that writes to the // client are readable from the server, and writes to the server are // readable at the client. // // +---+ +---+ // | C | | S | // --write->| l |----->| e |--read--> // | i | | r | // <--read--| e |<-----| v |<-write-- // | n | | e | // | t | | r | // +---+ +---+ // // I also need to make sure that end called on either socket affects // the other. function socketPair() { const server = new PassThrough(); const client = new PassThrough(); server.write = client.push.bind(client); client.write = server.push.bind(server); function end(chunk, encoding) { if (chunk) this.push(chunk, encoding); this.push(null); } server.end = end.bind(client); client.end = end.bind(server); return { client: client, server: server }; } function runServer(socket, run) { const frames = new Connection(socket); // We will be closing the socket without doing a closing handshake, // so cheat frames.expectSocketClose = true; // We also need to create some channel buffers, again a cheat frames.freshChannel(null); frames.freshChannel(null); frames.freshChannel(null); function send(id, fields, channel, content) { channel = channel || 0; if (content) { schedule(() => { frames.sendMessage(channel, id, fields, defs.BasicProperties, fields, content); }); } else { schedule(() => { frames.sendMethod(channel, id, fields); }); } } function wait(method) { return () => new Promise((resolve, reject) => { if (method) { frames.step((e, f) => { if (e !== null) return reject(e); if (f.id === method) resolve(f); else reject(new Error(`Expected method: ${method}, got ${f.id}`)); }); } else { frames.step((e, f) => { if (e !== null) return reject(e); else resolve(f); }); } }); } run(send, wait); return frames; } // Create a function that will call done once it's been called itself // `count` times. If it's called with an error value, it will // immediately call done with that error value. function latch(count, done) { let awaiting = count; let alive = true; return (err) => { if (err instanceof Error && alive) { alive = false; done(err); } else { awaiting--; if (awaiting === 0 && alive) { alive = false; done(); } } }; } // Call a thunk with a continuation that will be called with an error // if the thunk throws one, or nothing if it runs to completion. function completes(thunk, done) { try { thunk(); done(); } catch (e) { done(e); } } // When encoding, you can supply explicitly-typed fields like `{'!': // int32, 50}`. Most of these do not appear in the decoded values, so // to compare like-to-like we have to remove them from the input. function removeExplicitTypes(input) { switch (typeof input) { case 'object': if (input == null) { return null; } if (Array.isArray(input)) { const newArr = []; for (let i = 0; i < input.length; i++) { newArr[i] = removeExplicitTypes(input[i]); } return newArr; } if (Buffer.isBuffer(input)) { return input; } switch (input['!']) { case 'timestamp': case 'decimal': case 'float': return input; case undefined: { const newObj = {}; for (const k in input) { newObj[k] = removeExplicitTypes(input[k]); } return newObj; } default: return input.value; } default: return input; } } // Asserts that the decoded fields are equal to the original fields, // or equal to a default where absent in the original. The defaults // depend on the type of method or properties. // // This works slightly different for methods and properties: for // methods, each field must have a value, so the default is // substituted for undefined values when encoding; for properties, // fields may be absent in the encoded value, so a default is // substituted for missing fields when decoding. The effect is the // same so far as these tests are concerned. function assertEqualModuloDefaults(original, decodedFields) { const args = defs.info(original.id).args; for (let i = 0; i < args.length; i++) { const arg = args[i]; const originalValue = original.fields[arg.name]; const decodedValue = decodedFields[arg.name]; try { if (originalValue === undefined) { // longstr gets special treatment here, since the defaults are // given as strings rather than buffers, but the decoded values // will be buffers. assert.deepEqual(arg.type === 'longstr' ? Buffer.from(arg.default) : arg.default, decodedValue); } else { assert.deepEqual(removeExplicitTypes(originalValue), decodedValue); } } catch (assertionErr) { const methodOrProps = defs.info(original.id).name; assertionErr.message += ` (frame ${methodOrProps} field ${arg.name})`; throw assertionErr; } } // %%% TODO make sure there's no surplus fields return true; } function handshake(send, wait) { // kick it off send(defs.ConnectionStart, { versionMajor: 0, versionMinor: 9, serverProperties: {}, mechanisms: Buffer.from('PLAIN'), locales: Buffer.from('en_US'), }); return wait(defs.ConnectionStartOk)() .then(() => send(defs.ConnectionTune, { channelMax: 0, heartbeat: 0, frameMax: 0 })) .then(wait(defs.ConnectionTuneOk)) .then(wait(defs.ConnectionOpen)) .then(() => send(defs.ConnectionOpenOk, { knownHosts: '' })); } module.exports = { socketPair, runServer, latch, completes, schedule, randomString, removeExplicitTypes, assertEqualModuloDefaults, handshake, }; amqp-node-amqplib-263c8a3/test/mux.test.js000066400000000000000000000152251520012327700204610ustar00rootroot00000000000000const { describe, it } = require('node:test'); const assert = require('node:assert'); const Mux = require('../lib/mux').Mux; const PassThrough = require('node:stream').PassThrough; const latch = require('./lib/util').latch; const schedule = require('./lib/util').schedule; function stream() { return new PassThrough({ objectMode: true }); } function readAllObjects(s, cb) { const objs = []; function read() { let v = s.read(); while (v !== null) { objs.push(v); v = s.read(); } } s.on('end', () => { cb(objs); }); s.on('readable', read); read(); } describe('mux', () => { it('single input', (_t, done) => { const input = stream(); const output = stream(); input.on('end', () => output.end()); const mux = new Mux(output); mux.pipeFrom(input); const data = [1, 2, 3, 4, 5, 6, 7, 8, 9]; // not 0, it's treated specially by PassThrough for some reason. By // 'specially' I mean it breaks the stream. See e.g., // https://github.com/isaacs/readable-stream/pull/55 data.forEach((chunk) => input.write(chunk)); readAllObjects(output, (vals) => { assert.deepEqual(data, vals); done(); }); input.end(); }); it('single input, resuming stream', (_t, done) => { const input = stream(); const output = stream(); input.on('end', () => output.end()); const mux = new Mux(output); mux.pipeFrom(input); // Streams might be blocked and become readable again, simulate this // using a special read function and a marker const data = [1, 2, 3, 4, 'skip', 6, 7, 8, 9]; const oldRead = input.read; input.read = (size) => { const val = oldRead.call(input, size); if (val === 'skip') { input.emit('readable'); return null; } return val; }; data.forEach((chunk) => input.write(chunk)); readAllObjects(output, (vals) => { assert.deepEqual([1, 2, 3, 4, 6, 7, 8, 9], vals); done(); }); input.end(); }); it('two sequential inputs', (_t, done) => { const input1 = stream(); const input2 = stream(); const output = stream(); const mux = new Mux(output); mux.pipeFrom(input1); mux.pipeFrom(input2); const data = [1, 2, 3, 4, 5, 6, 7, 8, 9]; data.forEach((v) => { input1.write(v); }); input1.on('end', () => { data.forEach((v) => { input2.write(v); }); input2.end(); }); input2.on('end', () => output.end()); input1.end(); readAllObjects(output, (vs) => { assert.equal(2 * data.length, vs.length); done(); }); }); it('two interleaved inputs', (_t, done) => { const input1 = stream(); const input2 = stream(); const output = stream(); const mux = new Mux(output); mux.pipeFrom(input1); mux.pipeFrom(input2); const decrementLatch = latch(2, () => output.end()); input1.on('end', decrementLatch); input2.on('end', decrementLatch); const data = [1, 2, 3, 4, 5, 6, 7, 8, 9]; data.forEach((v) => { input1.write(v); }); input1.end(); data.forEach((v) => input2.write(v)); input2.end(); readAllObjects(output, (vs) => { assert.equal(2 * data.length, vs.length); done(); }); }); it('unpipe', (_t, done) => { const input = stream(); const output = stream(); const mux = new Mux(output); const pipedData = [1, 2, 3, 4, 5]; const unpipedData = [6, 7, 8, 9]; mux.pipeFrom(input); schedule(() => { pipedData.forEach((chunk) => input.write(chunk)); schedule(() => { mux.unpipeFrom(input); schedule(() => { unpipedData.forEach((chunk) => input.write(chunk)); input.end(); schedule(() => { // exhaust so that 'end' fires let v; do { v = input.read(); } while (v); }); }); }); }); input.on('end', () => output.end()); readAllObjects(output, (vals) => { try { assert.deepEqual(pipedData, vals); done(); } catch (e) { done(e); } }); }); it('backpressure does not crash when newStreams is drained', () => { // Regression test: out.write() returns false (backpressure) while // draining newStreams. The buggy code does an unconditional shift at the // bottom of the loop, so if the next stream reads null (empty) it gets // discarded without being pushed back, leaving newStreams empty and // triggering assert(this.newStreams.length > 0) in the else-branch. // // Scenario: newStreams = [input1, input2], input1 has data, input2 empty. // - shift input1, read 1, write → backpressure (false), push input1 back // - (buggy) unconditional shift takes input2, loop condition false → exit // - newStreams = [input1], length > 0, assert passes ← not the bug path // // The crash path requires backpressure AND the shifted stream reading null // with no other streams remaining. Use a single stream that has data, then // engineer a second read() on the same stream returning null after the // write so the stream is not pushed back, emptying newStreams entirely. const input1 = stream(); const output = stream(); const mux = new Mux(output); mux.newStreams.push(input1); input1.write(1); // After the first write (which triggers backpressure), wrap read() so // the stream appears empty on the next call — simulating a stream that // had one item buffered and is now drained within the same roundrobin pass. const originalRead = input1.read.bind(input1); let readCount = 0; input1.read = (size) => { readCount++; if (readCount > 1) return null; // appear empty after first read return originalRead(size); }; const originalWrite = output.write.bind(output); output.write = (...args) => { originalWrite(...args); return false; // signal backpressure immediately }; assert.doesNotThrow(() => mux._readIncoming()); }); it('roundrobin', (_t, done) => { const input1 = stream(); const input2 = stream(); const output = stream(); const mux = new Mux(output); mux.pipeFrom(input1); mux.pipeFrom(input2); const decrementLatch = latch(2, () => output.end()); input1.on('end', decrementLatch); input2.on('end', decrementLatch); const ones = [1, 1, 1, 1, 1]; ones.forEach((v) => { input1.write(v); }); input1.end(); const twos = [2, 2, 2, 2, 2]; twos.forEach((v) => input2.write(v)); input2.end(); readAllObjects(output, (vs) => { assert.deepEqual([1, 2, 1, 2, 1, 2, 1, 2, 1, 2], vs); done(); }); }); }); amqp-node-amqplib-263c8a3/test/recovery.test.js000066400000000000000000000120651520012327700215050ustar00rootroot00000000000000 const { describe, it } = require('node:test'); const assert = require('node:assert'); const EventEmitter = require('node:events'); const recovery = require('../lib/recovery'); class FakePromiseModel extends EventEmitter { constructor(id) { super(); this.id = id; } close() { return Promise.resolve(); } createChannel(options) { return Promise.resolve({kind: 'channel', id: this.id, options}); } createConfirmChannel(options) { return Promise.resolve({kind: 'confirm', id: this.id, options}); } updateSecret(_newSecret, _reason) { return Promise.resolve(); } } class FakeCallbackModel extends EventEmitter { constructor(id) { super(); this.id = id; } close(cb) { cb && cb(null); } createChannel(options, cb) { if (typeof options === 'function') { cb = options; options = undefined; } cb && cb(null, {kind: 'channel', id: this.id, options}); } createConfirmChannel(options, cb) { if (typeof options === 'function') { cb = options; options = undefined; } cb && cb(null, {kind: 'confirm', id: this.id, options}); } updateSecret(_newSecret, _reason, cb) { cb && cb(null); } } describe('recovery', () => { it('promise recovery reconnects after close', async () => { const models = []; let opened = 0; function openModel() { const model = new FakePromiseModel(++opened); models.push(model); return Promise.resolve(model); } const client = await recovery.connectWithRecoveryPromise(openModel, { initialDelay: 1, maxDelay: 1, jitter: 0, maxRetries: 3, }); assert.equal(1, opened); await new Promise((resolve) => { client.once('connect', () => { assert.equal(2, opened); resolve(); }); models[0].emit('close', new Error('socket closed')); }); await client.close(); }); it('promise recovery runs setup on every connect', async () => { const models = []; let setupCalls = 0; let opened = 0; function openModel() { const model = new FakePromiseModel(++opened); models.push(model); return Promise.resolve(model); } const client = await recovery.connectWithRecoveryPromise(openModel, { initialDelay: 1, maxDelay: 1, jitter: 0, maxRetries: 3, setup() { setupCalls++; }, }); assert.equal(1, setupCalls); await new Promise((resolve) => { client.once('connect', () => { assert.equal(2, setupCalls); resolve(); }); models[0].emit('close', new Error('socket closed')); }); await client.close(); }); it('callback recovery reconnects and creates channels after reconnect', (done) => { const models = []; let opened = 0; function openModel() { const model = new FakeCallbackModel(++opened); models.push(model); return Promise.resolve(model); } const client = recovery.connectWithRecoveryCallback( openModel, { initialDelay: 1, maxDelay: 1, jitter: 0, maxRetries: 3, }, (err, c) => { if (err) return done(err); c.createChannel((createErr, ch) => { if (createErr) return done(createErr); assert.equal(1, ch.id); c.once('connect', () => { c.createChannel((reconnectErr, reconnectedChannel) => { if (reconnectErr) return done(reconnectErr); assert.equal(2, reconnectedChannel.id); c.close(done); }); }); models[0].emit('close', new Error('socket closed')); }); }, ); assert(client); }); it('handler-error from underlying model is forwarded to recovery wrapper', async () => { const models = []; let opened = 0; function openModel() { const model = new FakePromiseModel(++opened); models.push(model); return Promise.resolve(model); } const client = await recovery.connectWithRecoveryPromise(openModel, { initialDelay: 1, maxDelay: 1, jitter: 0, }); const expectedErr = new Error('user close handler explodes'); await new Promise((resolve) => { client.on('handler-error', (err, event) => { assert.strictEqual(err, expectedErr); assert.strictEqual(event, 'close'); resolve(); }); // Simulate a handler throwing in a 'close' listener on the underlying model models[0].emit('handler-error', expectedErr, 'close'); }); await client.close(); }); it('promise recovery fails after max retries', async () => { let attempts = 0; function openModel() { attempts++; return Promise.reject(new Error('connect failed')); } let failed = false; try { await recovery.connectWithRecoveryPromise(openModel, { initialDelay: 1, maxDelay: 1, jitter: 0, maxRetries: 1, }); } catch (err) { failed = true; assert.equal('connect failed', err.message); } assert(failed); assert.equal(2, attempts); }); }); amqp-node-amqplib-263c8a3/test/uri_spec.test.js000066400000000000000000000153731520012327700214650ustar00rootroot00000000000000const { describe, it } = require('node:test'); const assert = require('node:assert'); const net = require('node:net'); const connect = require('../lib/connect').connect; const defs = require('../lib/defs'); const { runServer } = require('./lib/util'); function captureHandshake(url, callback) { let server; const captured = {}; server = net.createServer((socket) => { socket.once('data', (_header) => { runServer(socket, (send, wait) => { send(defs.ConnectionStart, { versionMajor: 0, versionMinor: 9, serverProperties: {}, mechanisms: Buffer.from('PLAIN'), locales: Buffer.from('en_US'), }); wait(defs.ConnectionStartOk)().then((startOk) => { const parts = startOk.fields.response.toString().split('\0'); captured.username = parts[1]; captured.password = parts[2]; send(defs.ConnectionTune, { channelMax: 0, heartbeat: 0, frameMax: 0 }); return wait(defs.ConnectionTuneOk)(); }).then(() => { return wait(defs.ConnectionOpen)(); }).then((open) => { captured.vhost = open.fields.virtualHost; send(defs.ConnectionClose, { replyCode: 200, replyText: 'ok', classId: 0, methodId: 0, }); }).catch((err) => { server.close(); callback(err); }); }); socket.once('end', () => { server.close(() => callback(null, captured)); }); }); }); server.listen(0, () => { const port = server.address().port; const urlWithPort = url.replace('TESTPORT', String(port)); connect(urlWithPort, {}, (_err) => {}); }); } describe('AMQP URI spec', () => { describe('credentials', () => { it('no userinfo defaults to guest/guest', (_t, done) => { captureHandshake('amqp://localhost:TESTPORT', (err, result) => { assert.ifError(err); assert.strictEqual(result.username, 'guest'); assert.strictEqual(result.password, 'guest'); done(); }); }); it('plain user:pass', (_t, done) => { captureHandshake('amqp://user:pass@localhost:TESTPORT', (err, result) => { assert.ifError(err); assert.strictEqual(result.username, 'user'); assert.strictEqual(result.password, 'pass'); done(); }); }); it('empty username', (_t, done) => { captureHandshake('amqp://:password@localhost:TESTPORT', (err, result) => { assert.ifError(err); assert.strictEqual(result.username, ''); assert.strictEqual(result.password, 'password'); done(); }); }); it('empty password', (_t, done) => { captureHandshake('amqp://username:@localhost:TESTPORT', (err, result) => { assert.ifError(err); assert.strictEqual(result.username, 'username'); assert.strictEqual(result.password, ''); done(); }); }); // Issue #385: colon in username or password must be percent-encoded it('percent-encoded colon (%3A) in username and password', (_t, done) => { captureHandshake('amqp://user%3Aname:pass%3Aword@localhost:TESTPORT', (err, result) => { assert.ifError(err); assert.strictEqual(result.username, 'user:name'); assert.strictEqual(result.password, 'pass:word'); done(); }); }); // Issue #483: @ in password must be percent-encoded as %40 it('percent-encoded @ (%40) in password', (_t, done) => { captureHandshake('amqp://user:p%40ssword@localhost:TESTPORT', (err, result) => { assert.ifError(err); assert.strictEqual(result.username, 'user'); assert.strictEqual(result.password, 'p@ssword'); done(); }); }); // RabbitMQ URI spec example: amqp://user:passw%23rd@host:10000/vhost it('percent-encoded # (%23) in password', (_t, done) => { captureHandshake('amqp://user:passw%23rd@localhost:TESTPORT', (err, result) => { assert.ifError(err); assert.strictEqual(result.username, 'user'); assert.strictEqual(result.password, 'passw#rd'); done(); }); }); it('percent-encoded % (%25) in password', (_t, done) => { captureHandshake('amqp://user:pass%25word@localhost:TESTPORT', (err, result) => { assert.ifError(err); assert.strictEqual(result.username, 'user'); assert.strictEqual(result.password, 'pass%word'); done(); }); }); // RabbitMQ URI spec example: amqp://user%61:%61pass@ho%61st:10000/v%2fhost it('percent-encoded letters (%61 = "a") in username and password', (_t, done) => { captureHandshake('amqp://user%61:%61pass@localhost:TESTPORT', (err, result) => { assert.ifError(err); assert.strictEqual(result.username, 'usera'); assert.strictEqual(result.password, 'apass'); done(); }); }); }); describe('vhost', () => { it('no path defaults to vhost /', (_t, done) => { captureHandshake('amqp://localhost:TESTPORT', (err, result) => { assert.ifError(err); assert.strictEqual(result.vhost, '/'); done(); }); }); it('explicit / path gives vhost /', (_t, done) => { captureHandshake('amqp://localhost:TESTPORT/', (err, result) => { assert.ifError(err); assert.strictEqual(result.vhost, '/'); done(); }); }); it('named vhost', (_t, done) => { captureHandshake('amqp://localhost:TESTPORT/myvhost', (err, result) => { assert.ifError(err); assert.strictEqual(result.vhost, 'myvhost'); done(); }); }); // The RabbitMQ spec requires %2F to represent a literal / in a vhost it('percent-encoded slash (%2F) in vhost decodes to /', (_t, done) => { captureHandshake('amqp://localhost:TESTPORT/%2F', (err, result) => { assert.ifError(err); assert.strictEqual(result.vhost, '/'); done(); }); }); it('percent-encoded slash lowercase (%2f) in vhost decodes to /', (_t, done) => { captureHandshake('amqp://localhost:TESTPORT/%2f', (err, result) => { assert.ifError(err); assert.strictEqual(result.vhost, '/'); done(); }); }); // RabbitMQ URI spec example: /v%2fhost => "v/host" it('mixed percent-encoding in vhost (v%2Fhost decodes to "v/host")', (_t, done) => { captureHandshake('amqp://localhost:TESTPORT/v%2Fhost', (err, result) => { assert.ifError(err); assert.strictEqual(result.vhost, 'v/host'); done(); }); }); it('percent-encoded space (%20) in vhost', (_t, done) => { captureHandshake('amqp://localhost:TESTPORT/my%20vhost', (err, result) => { assert.ifError(err); assert.strictEqual(result.vhost, 'my vhost'); done(); }); }); }); }); amqp-node-amqplib-263c8a3/tsconfig.types.json000066400000000000000000000005341520012327700212240ustar00rootroot00000000000000{ "compilerOptions": { "strict": true, "noEmit": true, "moduleResolution": "node", "module": "commonjs", "target": "ES2020", "skipLibCheck": false, "noUnusedLocals": false, "noUnusedParameters": false }, "include": [ "index.d.ts", "callback_api.d.ts", "lib/properties.d.ts", "types/*.ts" ] } amqp-node-amqplib-263c8a3/types/000077500000000000000000000000001520012327700165145ustar00rootroot00000000000000amqp-node-amqplib-263c8a3/types/callback_api.ts000066400000000000000000000202121520012327700214460ustar00rootroot00000000000000// Type-level tests for the callback API. // These are checked by `tsc --noEmit` but never executed at runtime. import * as amqp from '../callback_api'; import { Options, SocketOptions } from '../lib/properties'; function testConnectOverloads() { // Overload 1: connect(callback) amqp.connect((err, conn) => { if (err) return; const _conn: amqp.Connection = conn; }); // Overload 2: connect(url, callback) amqp.connect('amqp://localhost', (err, conn) => { if (err) return; const _conn: amqp.Connection = conn; }); // Overload 2 with Options.Connect object const opts: Options.Connect = { hostname: 'localhost', port: 5672 }; amqp.connect(opts, (err, conn) => { if (err) return; const _conn: amqp.Connection = conn; }); // Overload 3: connect(url, socketOptions, callback) const sockOpts: SocketOptions = { noDelay: true, rejectUnauthorized: false, }; amqp.connect('amqps://localhost', sockOpts, (err, conn) => { if (err) return; const _conn: amqp.Connection = conn; }); } function testConnection(conn: amqp.Connection) { // serverProperties lives on conn.connection.serverProperties const _serverProps: amqp.ServerProperties = conn.connection.serverProperties; // createChannel — callback only conn.createChannel((err, ch) => { if (err) return; const _ch: amqp.Channel = ch; }); // createChannel — with options conn.createChannel({ highWaterMark: 1024 }, (err, ch) => { if (err) return; const _ch: amqp.Channel = ch; }); // createConfirmChannel — callback only conn.createConfirmChannel((err, ch) => { if (err) return; const _ch: amqp.ConfirmChannel = ch; }); // createConfirmChannel — with options conn.createConfirmChannel({ highWaterMark: 512 }, (err, ch) => { if (err) return; const _ch: amqp.ConfirmChannel = ch; }); conn.updateSecret(Buffer.from('newsecret'), 'rotation'); conn.close(); conn.close((err) => { void err; }); } function testChannel(ch: amqp.Channel) { // assertQueue — queue name is optional (server-named queues) ch.assertQueue(); ch.assertQueue(''); ch.assertQueue('myqueue', { durable: true }); ch.assertQueue('myqueue', { durable: true }, (err, ok) => { if (err) return; const _name: string = ok.queue; const _count: number = ok.messageCount; }); // overflow and queueMode options ch.assertQueue('q', { overflow: 'reject-publish', queueMode: 'lazy' }); ch.checkQueue('q', (err, ok) => { if (err) return; const _name: string = ok.queue; }); ch.deleteQueue('q', {}, (err, ok) => { if (err) return; const _count: number = ok.messageCount; }); ch.purgeQueue('q', (err, ok) => { if (err) return; const _count: number = ok.messageCount; }); ch.bindQueue('q', 'ex', 'rk'); ch.unbindQueue('q', 'ex', 'rk'); ch.assertExchange('ex', 'direct', { durable: true }, (err, ok) => { if (err) return; const _name: string = ok.exchange; }); ch.assertExchange('ex', 'topic'); ch.assertExchange('ex', 'x-custom'); // still valid via `string` ch.checkExchange('ex'); ch.deleteExchange('ex', { ifUnused: false }); ch.bindExchange('dest', 'src', 'rk'); ch.unbindExchange('dest', 'src', 'rk'); // publish / sendToQueue const _pub: boolean = ch.publish('ex', 'rk', Buffer.from('hello'), { persistent: true }); const _sent: boolean = ch.sendToQueue('q', Buffer.from('hi')); // consume ch.consume('q', (msg) => { if (msg !== null) { const _content: Buffer = msg.content; const _tag: string = msg.fields.consumerTag; ch.ack(msg); } }, { noAck: false }, (err, ok) => { if (err) return; const _tag: string = ok.consumerTag; }); ch.cancel('tag'); ch.cancel('tag', (err, ok) => { void [err, ok]; }); // get ch.get('q', { noAck: false }, (err, msg) => { if (err) return; if (msg !== false) { const _content: Buffer = msg.content; const _count: number = msg.fields.messageCount; } }); const fakeMsg = {} as amqp.Message; ch.ack(fakeMsg); ch.ackAll(); ch.nack(fakeMsg, false, true); ch.nackAll(true); ch.reject(fakeMsg, false); ch.prefetch(10); ch.prefetch(10, false, (err, ok) => { void [err, ok]; }); ch.recover(); ch.recover((err, ok) => { void [err, ok]; }); ch.close(); } function testConfirmChannel(ch: amqp.ConfirmChannel) { ch.publish('ex', 'rk', Buffer.from('hello'), {}, (err, _ok) => { if (err) console.error(err); }); ch.sendToQueue('q', Buffer.from('hello'), {}, (err, _ok) => { if (err) console.error(err); }); ch.waitForConfirms(); ch.waitForConfirms((err) => { void err; }); } function testCredentials() { const plain = amqp.credentials.plain('user', 'pass'); const _mech: string = plain.mechanism; const _resp: Buffer = plain.response(); const external = amqp.credentials.external(); const _mech2: string = external.mechanism; } function testIllegalOperationError() { const err = new amqp.IllegalOperationError('channel closed'); const _name: 'IllegalOperationError' = err.name; const _stack: string | undefined = err.stackAtStateChange; } function testConnectionEvents(conn: amqp.Connection) { conn.on('close', () => {}); conn.on('close', (err) => { const _e: Error | undefined = err; }); conn.on('error', (err) => { const _e: Error = err; }); conn.on('blocked', (reason) => { const _r: string = reason; }); conn.on('unblocked', () => {}); conn.on('update-secret-ok', () => {}); conn.on('handler-error', (err, eventName) => { const _e: Error = err; const _n: string = eventName; }); } function testChannelEvents(ch: amqp.Channel) { ch.on('close', () => {}); ch.on('error', (err) => { const _e: Error = err; }); ch.on('drain', () => {}); ch.on('ack', (fields) => { const _tag: number = fields.deliveryTag; const _multiple: boolean = fields.multiple; }); ch.on('nack', (fields) => { const _tag: number = fields.deliveryTag; const _multiple: boolean = fields.multiple; const _requeue: boolean = fields.requeue; }); ch.on('cancel', (fields) => { const _tag: string = fields.consumerTag; }); ch.on('delivery', (msg) => { const _content: Buffer = msg.content; }); ch.on('return', (msg) => { const _content: Buffer = msg.content; }); ch.on('handler-error', (err, eventName) => { const _e: Error = err; const _n: string = eventName; }); } function testRecovery() { const recoveryOpts: amqp.RecoveryOptions = { initialDelay: 100, maxDelay: 30000, factor: 2, jitter: 0.2, maxRetries: 10, setup: (model, done) => { model.createChannel((err, _ch) => done(err ?? undefined)); }, }; const sockOpts: SocketOptions = { noDelay: true }; // connect with recovery enabled via boolean const conn1: amqp.RecoveringConnection = amqp.connect('amqp://localhost', { ...sockOpts, recovery: true, }, (err, conn) => { if (err) return; const _conn: amqp.RecoveringConnection = conn; }); // connect with recovery options const conn2: amqp.RecoveringConnection = amqp.connect('amqp://localhost', { ...sockOpts, recovery: recoveryOpts, }, (err, conn) => { if (err) return; const _conn: amqp.RecoveringConnection = conn; }); // RecoveringConnection supports same channel operations conn1.createChannel((err, ch) => { if (err) return; const _ch: amqp.Channel = ch; }); conn1.createConfirmChannel((err, ch) => { if (err) return; const _ch: amqp.ConfirmChannel = ch; }); conn1.updateSecret(Buffer.from('secret'), 'rotation'); conn1.close(); // Recovery-specific events conn2.on('connect', (model) => { const _m: amqp.Connection = model; }); conn2.on('disconnect', (err) => { const _e: Error = err; }); conn2.on('connect-failed', (err) => { const _e: Error = err; }); conn2.on('reconnect-scheduled', (info) => { const _attempt: number = info.attempt; const _delay: number = info.delay; const _err: Error = info.error; }); conn2.on('reconnect-failed', (err) => { const _e: Error = err; }); conn2.on('blocked', (reason) => { const _r: string = reason; }); conn2.on('unblocked', () => {}); conn2.on('error', (err) => { const _e: Error = err; }); conn2.on('update-secret-ok', () => {}); void [conn1, conn2]; } amqp-node-amqplib-263c8a3/types/channel_api.ts000066400000000000000000000206541520012327700213340ustar00rootroot00000000000000// Type-level tests for the promise (channel) API. // These are checked by `tsc --noEmit` but never executed at runtime. import * as amqp from '../index'; import { Options, SocketOptions } from '../lib/properties'; async function testConnect() { // connect with string URL const conn1: amqp.ChannelModel = await amqp.connect('amqp://localhost'); // connect with Options.Connect object const opts: Options.Connect = { protocol: 'amqp', hostname: 'localhost', port: 5672, username: 'guest', password: 'guest', vhost: '/', heartbeat: 60, frameMax: 131072, channelMax: 0, }; const conn2: amqp.ChannelModel = await amqp.connect(opts); // connect with SocketOptions including TLS fields const sockOpts: SocketOptions = { noDelay: true, timeout: 5000, keepAlive: true, keepAliveDelay: 1000, clientProperties: { appName: 'test' }, rejectUnauthorized: false, // tls.ConnectionOptions field }; const conn3: amqp.ChannelModel = await amqp.connect('amqps://localhost', sockOpts); return [conn1, conn2, conn3]; } async function testChannelModel(conn: amqp.ChannelModel) { // ChannelModel properties const _connection: amqp.Connection = conn.connection; const _serverProps: amqp.ServerProperties = conn.connection.serverProperties; // create channels const ch: amqp.Channel = await conn.createChannel(); const chWithOpts: amqp.Channel = await conn.createChannel({ highWaterMark: 1024 }); const confirmCh: amqp.ConfirmChannel = await conn.createConfirmChannel(); await conn.updateSecret(Buffer.from('newsecret'), 'rotation'); await conn.close(); return [ch, chWithOpts, confirmCh]; } async function testChannel(ch: amqp.Channel) { // assertQueue — queue name is optional (server-named queues) const q1: amqp.Replies.AssertQueue = await ch.assertQueue(); const q2: amqp.Replies.AssertQueue = await ch.assertQueue(''); const q3: amqp.Replies.AssertQueue = await ch.assertQueue('myqueue', { durable: true, exclusive: false, autoDelete: false, messageTtl: 60000, expires: 300000, deadLetterExchange: 'dlx', deadLetterRoutingKey: 'dlrk', maxLength: 1000, maxPriority: 10, overflow: 'drop-head', queueMode: 'lazy', }); void [q1, q2, q3]; await ch.checkQueue('myqueue'); await ch.deleteQueue('myqueue', { ifUnused: true, ifEmpty: true }); await ch.purgeQueue('myqueue'); await ch.bindQueue('myqueue', 'myexchange', 'routing.key'); await ch.unbindQueue('myqueue', 'myexchange', 'routing.key'); // assertExchange — type literal union await ch.assertExchange('myexchange', 'direct', { durable: true }); await ch.assertExchange('myexchange', 'topic'); await ch.assertExchange('myexchange', 'fanout'); await ch.assertExchange('myexchange', 'headers'); await ch.assertExchange('myexchange', 'x-custom-type'); // still valid via `string` await ch.checkExchange('myexchange'); await ch.deleteExchange('myexchange', { ifUnused: false }); await ch.bindExchange('dest', 'src', 'pattern'); await ch.unbindExchange('dest', 'src', 'pattern'); // publish / sendToQueue const published: boolean = ch.publish('ex', 'rk', Buffer.from('hello'), { persistent: true, contentType: 'application/json', headers: { 'x-custom': 'value' }, correlationId: 'abc', replyTo: 'reply-queue', messageId: 'msg-1', timestamp: Date.now(), type: 'event', appId: 'myapp', }); const sent: boolean = ch.sendToQueue('q', Buffer.from('hello')); void [published, sent]; // consume const consumeReply: amqp.Replies.Consume = await ch.consume('q', (msg) => { if (msg !== null) { const _content: Buffer = msg.content; const _tag: string = msg.fields.consumerTag; ch.ack(msg); } }); void consumeReply; await ch.cancel('consumer-tag'); // get const msg = await ch.get('q', { noAck: false }); if (msg !== false) { const _content: Buffer = msg.content; const _count: number = msg.fields.messageCount; ch.ack(msg); } // ack / nack / reject const fakeMsg = {} as amqp.Message; ch.ack(fakeMsg, false); ch.ackAll(); ch.nack(fakeMsg, false, true); ch.nackAll(true); ch.reject(fakeMsg, false); await ch.prefetch(10); await ch.prefetch(10, true); await ch.recover(); await ch.close(); } async function testConfirmChannel(ch: amqp.ConfirmChannel) { ch.publish('ex', 'rk', Buffer.from('hello'), {}, (err, _ok) => { if (err) console.error(err); }); ch.sendToQueue('q', Buffer.from('hello'), {}, (err, _ok) => { if (err) console.error(err); }); await ch.waitForConfirms(); } function testCredentials() { const plain = amqp.credentials.plain('user', 'pass'); const _mech1: string = plain.mechanism; const _resp1: Buffer = plain.response(); const _user: string = plain.username; const _pass: string = plain.password; const amqplain = amqp.credentials.amqplain('user', 'pass'); const _mech2: string = amqplain.mechanism; const external = amqp.credentials.external(); const _mech3: string = external.mechanism; const _resp3: Buffer = external.response(); } function testIllegalOperationError() { const err = new amqp.IllegalOperationError('channel closed'); const _name: 'IllegalOperationError' = err.name; const _stack: string | undefined = err.stackAtStateChange; const _msg: string = err.message; } // Exercise re-exports from lib/properties function testReExports() { const _opts: Options.Publish = { persistent: true }; const _getOpts: Options.Get = { noAck: true }; } function testChannelModelEvents(conn: amqp.ChannelModel) { conn.on('close', () => {}); conn.on('close', (err) => { const _e: Error | undefined = err; }); conn.on('error', (err) => { const _e: Error = err; }); conn.on('blocked', (reason) => { const _r: string = reason; }); conn.on('unblocked', () => {}); conn.on('update-secret-ok', () => {}); conn.on('handler-error', (err, eventName) => { const _e: Error = err; const _n: string = eventName; }); } function testChannelEvents(ch: amqp.Channel) { ch.on('close', () => {}); ch.on('error', (err) => { const _e: Error = err; }); ch.on('drain', () => {}); ch.on('ack', (fields) => { const _tag: number = fields.deliveryTag; const _multiple: boolean = fields.multiple; }); ch.on('nack', (fields) => { const _tag: number = fields.deliveryTag; const _multiple: boolean = fields.multiple; const _requeue: boolean = fields.requeue; }); ch.on('cancel', (fields) => { const _tag: string = fields.consumerTag; }); ch.on('delivery', (msg) => { const _content: Buffer = msg.content; }); ch.on('return', (msg) => { const _content: Buffer = msg.content; }); ch.on('handler-error', (err, eventName) => { const _e: Error = err; const _n: string = eventName; }); } async function testRecovery() { const recoveryOpts: amqp.RecoveryOptions = { initialDelay: 100, maxDelay: 30000, factor: 2, jitter: 0.2, maxRetries: 10, setup: async (model: amqp.ChannelModel) => { const _ch: amqp.Channel = await model.createChannel(); }, }; const sockOpts: SocketOptions = { noDelay: true }; // connect with recovery enabled via boolean (direct object literal) const conn1: amqp.RecoveringChannelModel = await amqp.connect('amqp://localhost', { noDelay: true, recovery: true, }); // connect with recovery options (spread from typed variable) const conn2: amqp.RecoveringChannelModel = await amqp.connect('amqp://localhost', { ...sockOpts, recovery: recoveryOpts, }); // RecoveringChannelModel supports same channel operations const ch: amqp.Channel = await conn1.createChannel(); const confirmCh: amqp.ConfirmChannel = await conn1.createConfirmChannel(); await conn1.updateSecret(Buffer.from('secret'), 'rotation'); void [ch, confirmCh]; // Recovery-specific events conn2.on('connect', (model) => { const _m: amqp.ChannelModel = model; }); conn2.on('disconnect', (err) => { const _e: Error = err; }); conn2.on('connect-failed', (err) => { const _e: Error = err; }); conn2.on('reconnect-scheduled', (info) => { const _attempt: number = info.attempt; const _delay: number = info.delay; const _err: Error = info.error; }); conn2.on('reconnect-failed', (err) => { const _e: Error = err; }); conn2.on('blocked', (reason) => { const _r: string = reason; }); conn2.on('unblocked', () => {}); conn2.on('error', (err) => { const _e: Error = err; }); conn2.on('update-secret-ok', () => {}); await conn1.close(); await conn2.close(); }