primer cambio
This commit is contained in:
41
node_modules/amqplib/.github/ISSUE_TEMPLATE/bug_report.md
generated
vendored
Normal file
41
node_modules/amqplib/.github/ISSUE_TEMPLATE/bug_report.md
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
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
|
||||
|
||||
|
||||
59
node_modules/amqplib/.github/workflows/publish.yml
generated
vendored
Normal file
59
node_modules/amqplib/.github/workflows/publish.yml
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
name: Publish
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
rabbitmq:
|
||||
image: rabbitmq:3-alpine
|
||||
ports:
|
||||
- 5672:5672
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [10.x, 12.x, 14.x, 16.x, 18.x, 20.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 }}
|
||||
51
node_modules/amqplib/.github/workflows/test.yml
generated
vendored
Normal file
51
node_modules/amqplib/.github/workflows/test.yml
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
# 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:3.12-alpine
|
||||
ports:
|
||||
- 5672:5672
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [10.x, 12.x, 14.x, 16.x, 18.x, 20.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
|
||||
|
||||
# Run the tests
|
||||
- run: make test
|
||||
454
node_modules/amqplib/CHANGELOG.md
generated
vendored
Normal file
454
node_modules/amqplib/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,454 @@
|
||||
# Change log for amqplib
|
||||
|
||||
## 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
|
||||
6
node_modules/amqplib/LICENSE
generated
vendored
Normal file
6
node_modules/amqplib/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
amqplib copyright (c) 2013, 2014
|
||||
Michael Bridgen <mikeb@squaremobius.net>
|
||||
|
||||
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.
|
||||
21
node_modules/amqplib/LICENSE-MIT
generated
vendored
Normal file
21
node_modules/amqplib/LICENSE-MIT
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013, 2014 Michael Bridgen <mikeb@squaremobius.net>
|
||||
|
||||
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.
|
||||
49
node_modules/amqplib/Makefile
generated
vendored
Normal file
49
node_modules/amqplib/Makefile
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
RABBITMQ_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='10.21' '11.15' '12.18' '13.14' '14.5' '15.8' '16.3.0' '18.1.0' '20.10.0' '22.14.0'
|
||||
|
||||
MOCHA=./node_modules/.bin/mocha
|
||||
_MOCHA=./node_modules/.bin/_mocha
|
||||
UGLIFY=./node_modules/.bin/uglifyjs
|
||||
NYC=./node_modules/.bin/nyc
|
||||
|
||||
.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:
|
||||
$(MOCHA) --check-leaks -u tdd --exit test/
|
||||
|
||||
test-all-nodejs:
|
||||
for v in $(NODEJS_VERSIONS); \
|
||||
do echo "-- Node version $$v --"; \
|
||||
nave use $$v $(MOCHA) -u tdd --exit -R progress test; \
|
||||
done
|
||||
|
||||
coverage: $(NYC)
|
||||
$(NYC) --clean --reporter=lcov --reporter=text $(_MOCHA) -u tdd --exit -R progress test/
|
||||
@echo "HTML report at file://$$(pwd)/coverage/lcov-report/index.html"
|
||||
|
||||
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) > $@
|
||||
|
||||
$(ISTANBUL):
|
||||
npm install
|
||||
|
||||
$(UGLIFY):
|
||||
npm install
|
||||
162
node_modules/amqplib/README.md
generated
vendored
Normal file
162
node_modules/amqplib/README.md
generated
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
# AMQP 0-9-1 library and client for Node.JS
|
||||
|
||||
[](https://www.npmjs.com/package/amqplib)
|
||||
[](https://www.npmjs.com/package/amqplib)
|
||||
[](https://github.com/amqp-node/amqplib/actions?query=workflow%3A%22Node.js+CI%22)
|
||||
[](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
|
||||
|
||||
## ❤️ Help Support Jack
|
||||
One of my close friends, Jack, was recently diagnosed with terminal brain cancer (grade 4 astrocytoma). He’s a young father facing an unimaginably tough road with remarkable courage. Thanks to chemotherapy, radiotherapy, and your support, two of his tumours have stopped showing activity. Donations help Jack continue accessing promising complementary therapies, attend hospital appointments, and spend meaningful time with his children.
|
||||
|
||||
If you’ve benefited from amqplib, please consider supporting Jack’s journey through his [J Crushing Cancer](https://www.gofundme.com/f/j-crushing-cancer) gofundme page. Thank you - @cressie176
|
||||
|
||||
## 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;
|
||||
|
||||
// Listener
|
||||
conn.createChannel((err, ch2) => {
|
||||
if (err) throw 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.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');
|
||||
|
||||
const ch1 = await conn.createChannel();
|
||||
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();
|
||||
|
||||
setInterval(() => {
|
||||
ch2.sendToQueue(queue, Buffer.from('something to do'));
|
||||
}, 1000);
|
||||
})();
|
||||
|
||||
```
|
||||
|
||||
## 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/
|
||||
483
node_modules/amqplib/bin/amqp-rabbitmq-0.9.1.json
generated
vendored
Normal file
483
node_modules/amqplib/bin/amqp-rabbitmq-0.9.1.json
generated
vendored
Normal file
@@ -0,0 +1,483 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
722
node_modules/amqplib/bin/generate-defs.js
generated
vendored
Normal file
722
node_modules/amqplib/bin/generate-defs.js
generated
vendored
Normal file
@@ -0,0 +1,722 @@
|
||||
var FS = require('fs');
|
||||
var format = require('util').format;
|
||||
|
||||
var defs = require('./amqp-rabbitmq-0.9.1.json');
|
||||
|
||||
var FRAME_OVERHEAD = 8; // type + channel + size + frame-end
|
||||
|
||||
var METHOD_OVERHEAD = FRAME_OVERHEAD + 4;
|
||||
// F_O + classId + methodId
|
||||
|
||||
var PROPERTIES_OVERHEAD = FRAME_OVERHEAD + 4 + 8 + 2;
|
||||
// F_O + classId + weight + content size + flags
|
||||
|
||||
|
||||
var out = process.stdout;
|
||||
|
||||
function printf() {
|
||||
out.write(format.apply(format, arguments), 'utf8');
|
||||
}
|
||||
|
||||
function nl() { out.write('\n'); }
|
||||
function println() { printf.apply(printf, arguments); nl(); }
|
||||
|
||||
function isEmptyObject(val) {
|
||||
return (val != null && typeof val === 'object' &&
|
||||
Object.keys(val).length === 0);
|
||||
}
|
||||
|
||||
function stringifyValue(val) {
|
||||
return (isEmptyObject(val)) ? 'EMPTY_OBJECT' :
|
||||
JSON.stringify(val);
|
||||
}
|
||||
|
||||
var constants = {};
|
||||
var constant_strs = {};
|
||||
|
||||
for (var i = 0, len = defs.constants.length; i < len; i++) {
|
||||
var 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) {
|
||||
var 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) {
|
||||
var type = a.type || domains[a.domain];
|
||||
var friendlyName = propertyName(a.name);
|
||||
return {type: type, name: friendlyName, default: a['default-value']};
|
||||
}
|
||||
|
||||
var domains = {};
|
||||
for (var i=0, len = defs.domains.length; i < len; i++) {
|
||||
var dom = defs.domains[i];
|
||||
domains[dom[0]] = dom[1];
|
||||
}
|
||||
|
||||
var methods = {};
|
||||
var propertieses = {};
|
||||
|
||||
for (var i = 0, len = defs.classes.length; i < len; i++) {
|
||||
var clazz = defs.classes[i];
|
||||
for (var j = 0, num = clazz.methods.length; j < num; j++) {
|
||||
var method = clazz.methods[j];
|
||||
var name = methodName(clazz, method);
|
||||
var 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) {
|
||||
var name = propertiesName(clazz);
|
||||
var 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 ints = require("buffer-more-ints");');
|
||||
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 (var m in methods) {
|
||||
var method = methods[m];
|
||||
println('case %d: return %s(buf);', method.id, method.decoder);
|
||||
}
|
||||
for (var p in propertieses) {
|
||||
var 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 (var m in methods) {
|
||||
var 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 (var p in propertieses) {
|
||||
var 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 (var m in methods) {
|
||||
var method = methods[m];
|
||||
println('case %d: return %s; ', method.id, method.info);
|
||||
}
|
||||
for (var p in propertieses) {
|
||||
var properties = propertieses[p];
|
||||
println('case %d: return %s', properties.id, properties.info);
|
||||
}
|
||||
println('default: throw new Error("Unknown class/method ID");');
|
||||
println('}}'); nl();
|
||||
|
||||
for (var m in methods) {
|
||||
var method = methods[m];
|
||||
println('module.exports.%s = %d;', m, method.id);
|
||||
decoderFn(method); nl();
|
||||
encoderFn(method); nl();
|
||||
infoObj(method); nl();
|
||||
}
|
||||
|
||||
for (var p in propertieses) {
|
||||
var 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) {
|
||||
var 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) {
|
||||
var v = stringLenVar(a);
|
||||
// Assumes the value or default is in val
|
||||
println("var %s = Buffer.byteLength(val, 'utf8');", v);
|
||||
}
|
||||
|
||||
|
||||
function encoderFn(method) {
|
||||
var 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).
|
||||
var fixedSize = METHOD_OVERHEAD;
|
||||
|
||||
var bitsInARow = 0;
|
||||
|
||||
for (var i=0, len = args.length; i < len; i++) {
|
||||
var 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 (var i = 0, len = args.length; i < len; i++) {
|
||||
var 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('ints.writeUInt64BE(buffer, 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 (var i=0, num=args.length; i < num; i++) {
|
||||
println('%s: undefined,', args[i].name);
|
||||
}
|
||||
println('};');
|
||||
}
|
||||
|
||||
function decoderFn(method) {
|
||||
var args = method.args;
|
||||
println('function %s(buffer) {', method.decoder);
|
||||
println('var offset = 0, val, len;');
|
||||
fieldsDecl(args);
|
||||
|
||||
var bitsInARow = 0;
|
||||
|
||||
for (var i=0, num=args.length; i < num; i++) {
|
||||
var a = args[i];
|
||||
var 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 = ints.readUInt64BE(buffer, offset); offset += 8;');
|
||||
break;
|
||||
case 'bit':
|
||||
var 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) {
|
||||
var 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;');
|
||||
|
||||
var fixedSize = PROPERTIES_OVERHEAD;
|
||||
|
||||
var args = props.args;
|
||||
|
||||
function incVarying(by) {
|
||||
println("varyingSize += %d;", by);
|
||||
}
|
||||
|
||||
for (var i=0, num=args.length; i < num; i++) {
|
||||
var 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('ints.writeUInt64BE(buffer, size, 11);');
|
||||
|
||||
println('flags = 0;');
|
||||
// we'll write the flags later too
|
||||
println('offset = 21;');
|
||||
|
||||
for (var i=0, num=args.length; i < num; i++) {
|
||||
var p = args[i];
|
||||
var 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('ints.writeUInt64BE(buffer, val, offset);');
|
||||
println('offset += 8;');
|
||||
break;
|
||||
case 'shortstr':
|
||||
var 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) {
|
||||
var 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 (var i=0, num=args.length; i < num; i++) {
|
||||
var p = argument(args[i]);
|
||||
var 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 = ints.readUInt64BE(buffer, 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('}');
|
||||
}
|
||||
22
node_modules/amqplib/callback_api.js
generated
vendored
Normal file
22
node_modules/amqplib/callback_api.js
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
var raw_connect = require('./lib/connect').connect;
|
||||
var CallbackModel = require('./lib/callback_model').CallbackModel;
|
||||
|
||||
// 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;
|
||||
|
||||
raw_connect(url, options, function(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;
|
||||
16
node_modules/amqplib/channel_api.js
generated
vendored
Normal file
16
node_modules/amqplib/channel_api.js
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
var raw_connect = require('./lib/connect').connect;
|
||||
var ChannelModel = require('./lib/channel_model').ChannelModel;
|
||||
var promisify = require('util').promisify;
|
||||
|
||||
function connect(url, connOptions) {
|
||||
return promisify(function(cb) {
|
||||
return raw_connect(url, connOptions, cb);
|
||||
})()
|
||||
.then(function(conn) {
|
||||
return new ChannelModel(conn);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.connect = connect;
|
||||
module.exports.credentials = require('./lib/credentials');
|
||||
module.exports.IllegalOperationError = require('./lib/error').IllegalOperationError;
|
||||
22
node_modules/amqplib/examples/direct_reply_to_client.js
generated
vendored
Normal file
22
node_modules/amqplib/examples/direct_reply_to_client.js
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/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',
|
||||
});
|
||||
})();
|
||||
25
node_modules/amqplib/examples/direct_reply_to_server.js
generated
vendored
Normal file
25
node_modules/amqplib/examples/direct_reply_to_server.js
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const amqp = require('../');
|
||||
const { v4: uuid } = require('uuid');
|
||||
|
||||
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.');
|
||||
|
||||
})();
|
||||
40
node_modules/amqplib/examples/headers.js
generated
vendored
Normal file
40
node_modules/amqplib/examples/headers.js
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/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.');
|
||||
})();
|
||||
39
node_modules/amqplib/examples/receive_generator.js
generated
vendored
Normal file
39
node_modules/amqplib/examples/receive_generator.js
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
const co = require('co');
|
||||
const amqp = require('amqplib');
|
||||
const readline = require('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');
|
||||
try {
|
||||
// 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 (e) {
|
||||
throw e;
|
||||
}
|
||||
}).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());
|
||||
42
node_modules/amqplib/examples/send_generators.js
generated
vendored
Normal file
42
node_modules/amqplib/examples/send_generators.js
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
// 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();
|
||||
}
|
||||
catch (e) {
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
conn.close();
|
||||
}
|
||||
|
||||
}).catch(err => {
|
||||
console.warn('Error:', err);
|
||||
});
|
||||
70
node_modules/amqplib/examples/ssl.js
generated
vendored
Normal file
70
node_modules/amqplib/examples/ssl.js
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
// 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 "<blah>.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('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.');
|
||||
})();
|
||||
|
||||
40
node_modules/amqplib/examples/stream_queues/README.md
generated
vendored
Normal file
40
node_modules/amqplib/examples/stream_queues/README.md
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
RabbitMQ 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
|
||||
14
node_modules/amqplib/examples/stream_queues/package.json
generated
vendored
Normal file
14
node_modules/amqplib/examples/stream_queues/package.json
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
55
node_modules/amqplib/examples/stream_queues/receive_stream.js
generated
vendored
Normal file
55
node_modules/amqplib/examples/stream_queues/receive_stream.js
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
const 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 and display any errors in the console
|
||||
catch(e) {
|
||||
console.log(e)
|
||||
}
|
||||
})();
|
||||
38
node_modules/amqplib/examples/stream_queues/send_stream.js
generated
vendored
Normal file
38
node_modules/amqplib/examples/stream_queues/send_stream.js
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
const 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 and display any errors in the console
|
||||
catch(e) {
|
||||
console.log(e)
|
||||
}
|
||||
})();
|
||||
|
||||
93
node_modules/amqplib/examples/tutorials/README.md
generated
vendored
Normal file
93
node_modules/amqplib/examples/tutorials/README.md
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
# 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
|
||||
28
node_modules/amqplib/examples/tutorials/callback_api/emit_log.js
generated
vendored
Normal file
28
node_modules/amqplib/examples/tutorials/callback_api/emit_log.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/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);
|
||||
});
|
||||
}
|
||||
30
node_modules/amqplib/examples/tutorials/callback_api/emit_log_direct.js
generated
vendored
Normal file
30
node_modules/amqplib/examples/tutorials/callback_api/emit_log_direct.js
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
#!/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);
|
||||
});
|
||||
}
|
||||
30
node_modules/amqplib/examples/tutorials/callback_api/emit_log_topic.js
generated
vendored
Normal file
30
node_modules/amqplib/examples/tutorials/callback_api/emit_log_topic.js
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
#!/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);
|
||||
});
|
||||
}
|
||||
28
node_modules/amqplib/examples/tutorials/callback_api/new_task.js
generated
vendored
Normal file
28
node_modules/amqplib/examples/tutorials/callback_api/new_task.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/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);
|
||||
});
|
||||
}
|
||||
36
node_modules/amqplib/examples/tutorials/callback_api/receive.js
generated
vendored
Normal file
36
node_modules/amqplib/examples/tutorials/callback_api/receive.js
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
#!/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);
|
||||
});
|
||||
}
|
||||
42
node_modules/amqplib/examples/tutorials/callback_api/receive_logs.js
generated
vendored
Normal file
42
node_modules/amqplib/examples/tutorials/callback_api/receive_logs.js
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
#!/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 }) => {
|
||||
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);
|
||||
});
|
||||
}
|
||||
57
node_modules/amqplib/examples/tutorials/callback_api/receive_logs_direct.js
generated
vendored
Normal file
57
node_modules/amqplib/examples/tutorials/callback_api/receive_logs_direct.js
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const amqp = require('amqplib/callback_api');
|
||||
const { basename } = require('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}, function(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);
|
||||
});
|
||||
}
|
||||
58
node_modules/amqplib/examples/tutorials/callback_api/receive_logs_topic.js
generated
vendored
Normal file
58
node_modules/amqplib/examples/tutorials/callback_api/receive_logs_topic.js
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const amqp = require('amqplib/callback_api');
|
||||
const { basename } = require('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}, function(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);
|
||||
});
|
||||
}
|
||||
|
||||
51
node_modules/amqplib/examples/tutorials/callback_api/rpc_client.js
generated
vendored
Normal file
51
node_modules/amqplib/examples/tutorials/callback_api/rpc_client.js
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const amqp = require('amqplib/callback_api');
|
||||
const { basename } = require('path');
|
||||
const { v4: uuid } = require('uuid');
|
||||
|
||||
const queue = 'rpc_queue';
|
||||
|
||||
const n = parseInt(process.argv[2], 10);
|
||||
if (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);
|
||||
});
|
||||
}
|
||||
|
||||
55
node_modules/amqplib/examples/tutorials/callback_api/rpc_server.js
generated
vendored
Normal file
55
node_modules/amqplib/examples/tutorials/callback_api/rpc_server.js
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
#!/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 }, function(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, b = 1;
|
||||
for (let i=0; i < n; i++) {
|
||||
let c = a + b;
|
||||
a = b; b = c;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
function bail(err, connection) {
|
||||
console.error(err);
|
||||
if (connection) connection.close(() => {
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
28
node_modules/amqplib/examples/tutorials/callback_api/send.js
generated
vendored
Normal file
28
node_modules/amqplib/examples/tutorials/callback_api/send.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/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);
|
||||
});
|
||||
}
|
||||
39
node_modules/amqplib/examples/tutorials/callback_api/worker.js
generated
vendored
Normal file
39
node_modules/amqplib/examples/tutorials/callback_api/worker.js
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
#!/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);
|
||||
});
|
||||
}
|
||||
24
node_modules/amqplib/examples/tutorials/emit_log.js
generated
vendored
Normal file
24
node_modules/amqplib/examples/tutorials/emit_log.js
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/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();
|
||||
};
|
||||
})();
|
||||
26
node_modules/amqplib/examples/tutorials/emit_log_direct.js
generated
vendored
Normal file
26
node_modules/amqplib/examples/tutorials/emit_log_direct.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
#!/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();
|
||||
};
|
||||
})();
|
||||
26
node_modules/amqplib/examples/tutorials/emit_log_topic.js
generated
vendored
Normal file
26
node_modules/amqplib/examples/tutorials/emit_log_topic.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
#!/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();
|
||||
};
|
||||
})();
|
||||
25
node_modules/amqplib/examples/tutorials/new_task.js
generated
vendored
Normal file
25
node_modules/amqplib/examples/tutorials/new_task.js
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/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();
|
||||
};
|
||||
})();
|
||||
16
node_modules/amqplib/examples/tutorials/package.json
generated
vendored
Normal file
16
node_modules/amqplib/examples/tutorials/package.json
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"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 <mikeb@squaremobius.net>",
|
||||
"license": "MPL 2.0"
|
||||
}
|
||||
26
node_modules/amqplib/examples/tutorials/receive.js
generated
vendored
Normal file
26
node_modules/amqplib/examples/tutorials/receive.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
#!/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);
|
||||
}
|
||||
})();
|
||||
30
node_modules/amqplib/examples/tutorials/receive_logs.js
generated
vendored
Normal file
30
node_modules/amqplib/examples/tutorials/receive_logs.js
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
#!/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);
|
||||
}
|
||||
})();
|
||||
38
node_modules/amqplib/examples/tutorials/receive_logs_direct.js
generated
vendored
Normal file
38
node_modules/amqplib/examples/tutorials/receive_logs_direct.js
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const amqp = require('../..');
|
||||
const { basename } = require('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);
|
||||
}
|
||||
})();
|
||||
39
node_modules/amqplib/examples/tutorials/receive_logs_topic.js
generated
vendored
Normal file
39
node_modules/amqplib/examples/tutorials/receive_logs_topic.js
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const amqp = require('../..');
|
||||
const { basename } = require('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);
|
||||
}
|
||||
})();
|
||||
49
node_modules/amqplib/examples/tutorials/rpc_client.js
generated
vendored
Normal file
49
node_modules/amqplib/examples/tutorials/rpc_client.js
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const amqp = require('amqplib');
|
||||
const { basename } = require('path');
|
||||
const { v4: uuid } = require('uuid');
|
||||
|
||||
const queue = 'rpc_queue';
|
||||
|
||||
const n = parseInt(process.argv[2], 10);
|
||||
if (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();
|
||||
};
|
||||
})();
|
||||
46
node_modules/amqplib/examples/tutorials/rpc_server.js
generated
vendored
Normal file
46
node_modules/amqplib/examples/tutorials/rpc_server.js
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
#!/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, b = 1;
|
||||
for (let i=0; i < n; i++) {
|
||||
let c = a + b;
|
||||
a = b; b = c;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
31
node_modules/amqplib/examples/tutorials/send.js
generated
vendored
Normal file
31
node_modules/amqplib/examples/tutorials/send.js
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
#!/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();
|
||||
};
|
||||
})();
|
||||
34
node_modules/amqplib/examples/tutorials/worker.js
generated
vendored
Normal file
34
node_modules/amqplib/examples/tutorials/worker.js
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
#!/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);
|
||||
}
|
||||
})();
|
||||
21
node_modules/amqplib/examples/waitForConfirms.js
generated
vendored
Normal file
21
node_modules/amqplib/examples/waitForConfirms.js
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
const amqp = require('../');
|
||||
|
||||
(async () => {
|
||||
let connection;
|
||||
try {
|
||||
connection = await amqp.connect();
|
||||
const channel = await connection.createConfirmChannel();
|
||||
|
||||
for (var 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();
|
||||
}
|
||||
})();
|
||||
314
node_modules/amqplib/lib/api_args.js
generated
vendored
Normal file
314
node_modules/amqplib/lib/api_args.js
generated
vendored
Normal file
@@ -0,0 +1,314 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
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;
|
||||
}
|
||||
|
||||
var EMPTY_OPTIONS = Object.freeze({});
|
||||
|
||||
var Args = {};
|
||||
|
||||
Args.assertQueue = function(queue, options) {
|
||||
queue = queue || '';
|
||||
options = options || EMPTY_OPTIONS;
|
||||
|
||||
var 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 = function(queue) {
|
||||
return {
|
||||
queue: queue,
|
||||
passive: true, // switch to "completely different" mode
|
||||
nowait: false,
|
||||
durable: true, autoDelete: false, exclusive: false, // ignored
|
||||
ticket: 0,
|
||||
};
|
||||
};
|
||||
|
||||
Args.deleteQueue = function(queue, options) {
|
||||
options = options || EMPTY_OPTIONS;
|
||||
return {
|
||||
queue: queue,
|
||||
ifUnused: !!options.ifUnused,
|
||||
ifEmpty: !!options.ifEmpty,
|
||||
ticket: 0, nowait: false
|
||||
};
|
||||
};
|
||||
|
||||
Args.purgeQueue = function(queue) {
|
||||
return {
|
||||
queue: queue,
|
||||
ticket: 0, nowait: false
|
||||
};
|
||||
};
|
||||
|
||||
Args.bindQueue = function(queue, source, pattern, argt) {
|
||||
return {
|
||||
queue: queue,
|
||||
exchange: source,
|
||||
routingKey: pattern,
|
||||
arguments: argt,
|
||||
ticket: 0, nowait: false
|
||||
};
|
||||
};
|
||||
|
||||
Args.unbindQueue = function(queue, source, pattern, argt) {
|
||||
return {
|
||||
queue: queue,
|
||||
exchange: source,
|
||||
routingKey: pattern,
|
||||
arguments: argt,
|
||||
ticket: 0, nowait: false
|
||||
};
|
||||
};
|
||||
|
||||
Args.assertExchange = function(exchange, type, options) {
|
||||
options = options || EMPTY_OPTIONS;
|
||||
var 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 = function(exchange) {
|
||||
return {
|
||||
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 = function(exchange, options) {
|
||||
options = options || EMPTY_OPTIONS;
|
||||
return {
|
||||
exchange: exchange,
|
||||
ifUnused: !!options.ifUnused,
|
||||
ticket: 0, nowait: false
|
||||
};
|
||||
};
|
||||
|
||||
Args.bindExchange = function(dest, source, pattern, argt) {
|
||||
return {
|
||||
source: source,
|
||||
destination: dest,
|
||||
routingKey: pattern,
|
||||
arguments: argt,
|
||||
ticket: 0, nowait: false
|
||||
};
|
||||
};
|
||||
|
||||
Args.unbindExchange = function(dest, source, pattern, argt) {
|
||||
return {
|
||||
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 = function(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)];
|
||||
}
|
||||
|
||||
var headers = Object.create(options.headers || null);
|
||||
setIfDefined(headers, 'CC', convertCC(options.CC));
|
||||
setIfDefined(headers, 'BCC', convertCC(options.BCC));
|
||||
|
||||
var 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;
|
||||
|
||||
var 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 = function(queue, options) {
|
||||
options = options || EMPTY_OPTIONS;
|
||||
var 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 = function(consumerTag) {
|
||||
return {
|
||||
consumerTag: consumerTag,
|
||||
nowait: false
|
||||
};
|
||||
};
|
||||
|
||||
Args.get = function(queue, options) {
|
||||
options = options || EMPTY_OPTIONS;
|
||||
return {
|
||||
ticket: 0,
|
||||
queue: queue,
|
||||
noAck: !!options.noAck
|
||||
};
|
||||
};
|
||||
|
||||
Args.ack = function(tag, allUpTo) {
|
||||
return {
|
||||
deliveryTag: tag,
|
||||
multiple: !!allUpTo
|
||||
};
|
||||
};
|
||||
|
||||
Args.nack = function(tag, allUpTo, requeue) {
|
||||
return {
|
||||
deliveryTag: tag,
|
||||
multiple: !!allUpTo,
|
||||
requeue: (requeue === undefined) ? true : requeue
|
||||
};
|
||||
};
|
||||
|
||||
Args.reject = function(tag, requeue) {
|
||||
return {
|
||||
deliveryTag: tag,
|
||||
requeue: (requeue === undefined) ? true : requeue
|
||||
};
|
||||
};
|
||||
|
||||
Args.prefetch = function(count, global) {
|
||||
return {
|
||||
prefetchCount: count || 0,
|
||||
prefetchSize: 0,
|
||||
global: !!global
|
||||
};
|
||||
};
|
||||
|
||||
Args.recover = function() {
|
||||
return {requeue: true};
|
||||
};
|
||||
|
||||
module.exports = Object.freeze(Args);
|
||||
130
node_modules/amqplib/lib/bitset.js
generated
vendored
Normal file
130
node_modules/amqplib/lib/bitset.js
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* 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, 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;
|
||||
342
node_modules/amqplib/lib/callback_model.js
generated
vendored
Normal file
342
node_modules/amqplib/lib/callback_model.js
generated
vendored
Normal file
@@ -0,0 +1,342 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
'use strict';
|
||||
|
||||
var defs = require('./defs');
|
||||
var EventEmitter = require('events');
|
||||
var BaseChannel = require('./channel').BaseChannel;
|
||||
var acceptMessage = require('./channel').acceptMessage;
|
||||
var Args = require('./api_args');
|
||||
|
||||
class CallbackModel extends EventEmitter {
|
||||
constructor (connection) {
|
||||
super();
|
||||
this.connection = connection;
|
||||
var self = this;
|
||||
['error', 'close', 'blocked', 'unblocked'].forEach(function (ev) {
|
||||
connection.on(ev, self.emit.bind(self, ev));
|
||||
});
|
||||
}
|
||||
|
||||
close (cb) {
|
||||
this.connection.close(cb);
|
||||
}
|
||||
|
||||
updateSecret(newSecret, reason, cb) {
|
||||
this.connection._updateSecret(newSecret, reason, cb);
|
||||
}
|
||||
|
||||
createChannel (options, cb) {
|
||||
if (arguments.length === 1) {
|
||||
cb = options;
|
||||
options = undefined;
|
||||
}
|
||||
var ch = new Channel(this.connection);
|
||||
ch.setOptions(options);
|
||||
ch.open(function (err, ok) {
|
||||
if (err === null)
|
||||
cb && cb(null, ch);
|
||||
else
|
||||
cb && cb(err);
|
||||
});
|
||||
return ch;
|
||||
}
|
||||
|
||||
createConfirmChannel (options, cb) {
|
||||
if (arguments.length === 1) {
|
||||
cb = options;
|
||||
options = undefined;
|
||||
}
|
||||
var ch = new ConfirmChannel(this.connection);
|
||||
ch.setOptions(options);
|
||||
ch.open(function (err) {
|
||||
if (err !== null)
|
||||
return cb && cb(err);
|
||||
else {
|
||||
ch.rpc(defs.ConfirmSelect, { nowait: false },
|
||||
defs.ConfirmSelectOk, function (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) {
|
||||
var cb = callbackWrapper(this, cb0);
|
||||
this._rpc(method, fields, expect, function (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,
|
||||
function () { 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) {
|
||||
var cb = callbackWrapper(this, cb0);
|
||||
this._rpc(defs.ExchangeDeclare,
|
||||
Args.assertExchange(ex, type, options),
|
||||
defs.ExchangeDeclareOk,
|
||||
function (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) {
|
||||
var 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) {
|
||||
var cb = callbackWrapper(this, cb0);
|
||||
var fields = Args.consume(queue, options);
|
||||
var self = this;
|
||||
this._rpc(
|
||||
defs.BasicConsume, fields, defs.BasicConsumeOk,
|
||||
function (err, ok) {
|
||||
if (err === null) {
|
||||
self.registerConsumer(ok.fields.consumerTag, callback);
|
||||
cb(null, ok.fields);
|
||||
}
|
||||
else
|
||||
cb(err);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
cancel (consumerTag, cb0) {
|
||||
var cb = callbackWrapper(this, cb0);
|
||||
var self = this;
|
||||
this._rpc(
|
||||
defs.BasicCancel, Args.cancel(consumerTag), defs.BasicCancelOk,
|
||||
function (err, ok) {
|
||||
if (err === null) {
|
||||
self.unregisterConsumer(consumerTag);
|
||||
cb(null, ok.fields);
|
||||
}
|
||||
else
|
||||
cb(err);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
get (queue, options, cb0) {
|
||||
var self = this;
|
||||
var fields = Args.get(queue, options);
|
||||
var cb = callbackWrapper(this, cb0);
|
||||
this.sendOrEnqueue(defs.BasicGet, fields, function (err, f) {
|
||||
if (err === null) {
|
||||
if (f.id === defs.BasicGetEmpty) {
|
||||
cb(null, false);
|
||||
}
|
||||
else if (f.id === defs.BasicGetOk) {
|
||||
self.handleMessage = acceptMessage(function (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) ? function(err, ok) {
|
||||
if (err === null) {
|
||||
cb(null, ok);
|
||||
}
|
||||
else cb(err);
|
||||
} : function() {};
|
||||
}
|
||||
|
||||
class ConfirmChannel extends Channel {
|
||||
publish (exchange, routingKey,
|
||||
content, options, cb) {
|
||||
this.pushConfirmCallback(cb);
|
||||
return Channel.prototype.publish.call(
|
||||
this, exchange, routingKey, content, options);
|
||||
}
|
||||
|
||||
sendToQueue (queue, content,
|
||||
options, cb) {
|
||||
return this.publish('', queue, content, options, cb);
|
||||
}
|
||||
|
||||
waitForConfirms (k) {
|
||||
var awaiting = [];
|
||||
var unconfirmed = this.unconfirmed;
|
||||
unconfirmed.forEach(function (val, index) {
|
||||
if (val === null)
|
||||
; // already confirmed
|
||||
else {
|
||||
var confirmed = new Promise(function (resolve, reject) {
|
||||
unconfirmed[index] = function (err) {
|
||||
if (val)
|
||||
val(err);
|
||||
if (err === null)
|
||||
resolve();
|
||||
else
|
||||
reject(err);
|
||||
};
|
||||
});
|
||||
awaiting.push(confirmed);
|
||||
}
|
||||
});
|
||||
return Promise.all(awaiting).then(function () { k(); },
|
||||
function (err) { k(err); });
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.CallbackModel = CallbackModel;
|
||||
module.exports.Channel = Channel;
|
||||
module.exports.ConfirmChannel = ConfirmChannel;
|
||||
510
node_modules/amqplib/lib/channel.js
generated
vendored
Normal file
510
node_modules/amqplib/lib/channel.js
generated
vendored
Normal file
@@ -0,0 +1,510 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
// Channel machinery.
|
||||
|
||||
'use strict';
|
||||
|
||||
var defs = require('./defs');
|
||||
var closeMsg = require('./format').closeMessage;
|
||||
var inspect = require('./format').inspect;
|
||||
var methodName = require('./format').methodName;
|
||||
var assert = require('assert');
|
||||
var EventEmitter = require('events');
|
||||
var fmt = require('util').format;
|
||||
var IllegalOperationError = require('./error').IllegalOperationError;
|
||||
var stackCapture = require('./error').stackCapture;
|
||||
|
||||
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, function (cb) {
|
||||
if (cb)
|
||||
cb(null);
|
||||
}));
|
||||
this.on('nack', this.handleConfirm.bind(this, function (cb) {
|
||||
if (cb)
|
||||
cb(new Error('message nacked'));
|
||||
}));
|
||||
this.on('close', function () {
|
||||
var cb;
|
||||
while (cb = this.unconfirmed.shift()) {
|
||||
if (cb)
|
||||
cb(new Error('channel closed'));
|
||||
}
|
||||
});
|
||||
// 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) {
|
||||
var 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
|
||||
var expectedName = methodName(expect);
|
||||
|
||||
var 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 {
|
||||
// otherwise, it's a close frame
|
||||
var closeReason = (err.fields.classId << 16) + err.fields.methodId;
|
||||
var e = (method === closeReason)
|
||||
? fmt("Operation failed: %s; %s",
|
||||
methodName(method), closeMsg(err))
|
||||
: fmt("Channel closed by server: %s", closeMsg(err));
|
||||
var closeFrameError = new Error(e);
|
||||
closeFrameError.code = err.fields.replyCode;
|
||||
closeFrameError.classId = err.fields.classId;
|
||||
closeFrameError.methodId = err.fields.methodId;
|
||||
return cb(closeFrameError);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
this.emit('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) {
|
||||
var send = this.sendImmediately.bind(this);
|
||||
invalidateSend(this, 'Channel closing', capturedStack);
|
||||
|
||||
this.accept = function (f) {
|
||||
if (f.id === defs.ChannelCloseOk) {
|
||||
if (k)
|
||||
k();
|
||||
var s = stackCapture('ChannelCloseOk frame received');
|
||||
this.toClosed(s);
|
||||
}
|
||||
else if (f.id === defs.ChannelClose) {
|
||||
send(defs.ChannelCloseOk, {});
|
||||
}
|
||||
// else ignore frame
|
||||
};
|
||||
}
|
||||
|
||||
_rejectPending () {
|
||||
function rej (r) {
|
||||
r(new Error("Channel ended, no reply will be forthcoming"));
|
||||
}
|
||||
if (this.reply !== null)
|
||||
rej(this.reply);
|
||||
this.reply = null;
|
||||
|
||||
var discard;
|
||||
while (discard = this.pending.shift())
|
||||
rej(discard.reply);
|
||||
this.pending = null; // so pushes will break
|
||||
}
|
||||
|
||||
closeBecause (reason, code, k) {
|
||||
this.sendImmediately(defs.ChannelClose, {
|
||||
replyText: reason,
|
||||
replyCode: code,
|
||||
methodId: 0, classId: 0
|
||||
});
|
||||
var 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) {
|
||||
var self = this;
|
||||
this.closeBecause(reason, code, function () {
|
||||
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;
|
||||
}
|
||||
self.emit('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) {
|
||||
var tag = f.deliveryTag;
|
||||
var multi = f.multiple;
|
||||
|
||||
if (multi) {
|
||||
var confirmed = this.unconfirmed.splice(0, tag - this.lwm + 1);
|
||||
this.lwm = tag + 1;
|
||||
confirmed.forEach(handle);
|
||||
}
|
||||
else {
|
||||
var 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 () {
|
||||
this.emit('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 this.emit('ack', f.fields);
|
||||
case defs.BasicNack:
|
||||
return this.emit('nack', f.fields);
|
||||
case defs.BasicCancel:
|
||||
// The broker can send this if e.g., the queue is deleted.
|
||||
return this.emit('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) {
|
||||
var reply = this.reply; this.reply = null;
|
||||
reply(f);
|
||||
}
|
||||
var emsg = "Channel closed by server: " + closeMsg(f);
|
||||
this.sendImmediately(defs.ChannelCloseOk, {});
|
||||
|
||||
var error = new Error(emsg);
|
||||
error.code = f.fields.replyCode;
|
||||
error.classId = f.fields.classId;
|
||||
error.methodId = f.fields.methodId;
|
||||
this.emit('error', error);
|
||||
|
||||
var 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
|
||||
var 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) {
|
||||
var 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 function() {
|
||||
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) {
|
||||
var 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));
|
||||
|
||||
var self = this;
|
||||
var fields = f.fields;
|
||||
return acceptMessage(function(message) {
|
||||
message.fields = fields;
|
||||
self.emit(event, message);
|
||||
});
|
||||
}
|
||||
|
||||
// Move to the state of waiting for message frames (headers, then
|
||||
// one or more content frames)
|
||||
function acceptMessage(continuation) {
|
||||
var totalSize = 0, remaining = 0;
|
||||
var buffers = null;
|
||||
|
||||
var 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) {
|
||||
var 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"
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
var consumerTag = fields.consumerTag;
|
||||
var 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) {
|
||||
var result = this.dispatchMessage(fields, null);
|
||||
this.unregisterConsumer(fields.consumerTag);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.acceptMessage = acceptMessage;
|
||||
module.exports.BaseChannel = BaseChannel;
|
||||
module.exports.Channel = Channel;
|
||||
308
node_modules/amqplib/lib/channel_model.js
generated
vendored
Normal file
308
node_modules/amqplib/lib/channel_model.js
generated
vendored
Normal file
@@ -0,0 +1,308 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
'use strict';
|
||||
|
||||
const EventEmitter = require('events');
|
||||
const promisify = require('util').promisify;
|
||||
const defs = require('./defs');
|
||||
const {BaseChannel} = require('./channel');
|
||||
const {acceptMessage} = require('./channel');
|
||||
const Args = require('./api_args');
|
||||
const {inspect} = require('./format');
|
||||
|
||||
class ChannelModel extends EventEmitter {
|
||||
constructor(connection) {
|
||||
super();
|
||||
this.connection = connection;
|
||||
|
||||
['error', 'close', 'blocked', 'unblocked'].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) {
|
||||
const ok = 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) return reject(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) {
|
||||
this.pushConfirmCallback(cb);
|
||||
return super.publish(exchange, routingKey, content, options);
|
||||
}
|
||||
|
||||
sendToQueue(queue, content, options, cb) {
|
||||
return this.publish('', queue, content, options, cb);
|
||||
}
|
||||
|
||||
waitForConfirms() {
|
||||
const awaiting = [];
|
||||
const unconfirmed = this.unconfirmed;
|
||||
unconfirmed.forEach((val, index) => {
|
||||
if (val !== null) {
|
||||
const confirmed = new Promise((resolve, reject) => {
|
||||
unconfirmed[index] = err => {
|
||||
if (val) val(err);
|
||||
if (err === null) resolve();
|
||||
else reject(err);
|
||||
};
|
||||
});
|
||||
awaiting.push(confirmed);
|
||||
}
|
||||
});
|
||||
// Channel closed
|
||||
if (!this.pending) {
|
||||
var cb;
|
||||
while (cb = this.unconfirmed.shift()) {
|
||||
if (cb) cb(new Error('channel closed'));
|
||||
}
|
||||
}
|
||||
return Promise.all(awaiting);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.ConfirmChannel = ConfirmChannel;
|
||||
module.exports.Channel = Channel;
|
||||
module.exports.ChannelModel = ChannelModel;
|
||||
345
node_modules/amqplib/lib/codec.js
generated
vendored
Normal file
345
node_modules/amqplib/lib/codec.js
generated
vendored
Normal file
@@ -0,0 +1,345 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
/*
|
||||
|
||||
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).
|
||||
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var ints = require('buffer-more-ints');
|
||||
|
||||
// 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) {
|
||||
var start = offset;
|
||||
offset += 4; // leave room for the table length
|
||||
for (var key in val) {
|
||||
if (val[key] !== undefined) {
|
||||
var len = Buffer.byteLength(key);
|
||||
buffer.writeUInt8(len, offset); offset++;
|
||||
buffer.write(key, offset, 'utf8'); offset += len;
|
||||
offset += encodeFieldValue(buffer, val[key], offset);
|
||||
}
|
||||
}
|
||||
var size = offset - start;
|
||||
buffer.writeUInt32BE(size - 4, start);
|
||||
return size;
|
||||
}
|
||||
|
||||
function encodeArray(buffer, val, offset) {
|
||||
var start = offset;
|
||||
offset += 4;
|
||||
for (var i=0, num=val.length; i < num; i++) {
|
||||
offset += encodeFieldValue(buffer, val[i], offset);
|
||||
}
|
||||
var size = offset - start;
|
||||
buffer.writeUInt32BE(size - 4, start);
|
||||
return size;
|
||||
}
|
||||
|
||||
function encodeFieldValue(buffer, value, offset) {
|
||||
var start = offset;
|
||||
var type = typeof value, val = value;
|
||||
// A trapdoor for specifying a type, e.g., timestamp
|
||||
if (value && type === 'object' && value.hasOwnProperty('!')) {
|
||||
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
|
||||
var 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');
|
||||
ints.writeInt64BE(buffer, val, offset); offset += 8;
|
||||
break;
|
||||
|
||||
// Now for exotic types, those can _only_ be denoted by using
|
||||
// `{'!': type, value: val}
|
||||
case 'timestamp':
|
||||
tag('T');
|
||||
ints.writeUInt64BE(buffer, val, offset); offset += 8;
|
||||
break;
|
||||
case 'float':
|
||||
tag('f');
|
||||
buffer.writeFloatBE(val, offset); offset += 4;
|
||||
break;
|
||||
case 'decimal':
|
||||
tag('D');
|
||||
if (val.hasOwnProperty('places') && val.hasOwnProperty('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) {
|
||||
var fields = {}, offset = 0, size = slice.length;
|
||||
var len, key, val;
|
||||
|
||||
function decodeFieldValue() {
|
||||
var 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.
|
||||
var places = slice[offset]; offset++;
|
||||
var digits = slice.readUInt32BE(offset); offset += 4;
|
||||
val = {'!': 'decimal', value: {places: places, digits: digits}};
|
||||
break;
|
||||
case 'T':
|
||||
val = ints.readUInt64BE(slice, 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 = ints.readInt64BE(slice, 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) {
|
||||
var 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;
|
||||
189
node_modules/amqplib/lib/connect.js
generated
vendored
Normal file
189
node_modules/amqplib/lib/connect.js
generated
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
// General-purpose API for glueing everything together.
|
||||
|
||||
'use strict';
|
||||
|
||||
var URL = require('url-parse');
|
||||
var QS = require('querystring');
|
||||
var Connection = require('./connection').Connection;
|
||||
var fmt = require('util').format;
|
||||
var credentials = require('./credentials');
|
||||
|
||||
function copyInto(obj, target) {
|
||||
var keys = Object.keys(obj);
|
||||
var i = keys.length;
|
||||
while (i--) {
|
||||
var 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, {});
|
||||
}
|
||||
|
||||
var 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);
|
||||
|
||||
var query = query || {};
|
||||
|
||||
function intOrDefault(val, def) {
|
||||
return (val === undefined) ? def : parseInt(val);
|
||||
}
|
||||
|
||||
var 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': intOrDefault(query.heartbeat, 0),
|
||||
|
||||
// open
|
||||
'virtualHost': vhost,
|
||||
'capabilities': '',
|
||||
'insist': 0
|
||||
};
|
||||
}
|
||||
|
||||
// Decide on credentials based on what we're supplied.
|
||||
function credentialsFromUrl(parts) {
|
||||
var user = 'guest', 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()`.
|
||||
var sockopts = clone(socketOptions || {});
|
||||
url = url || 'amqp://localhost';
|
||||
|
||||
var noDelay = !!sockopts.noDelay;
|
||||
var timeout = sockopts.timeout;
|
||||
var keepAlive = !!sockopts.keepAlive;
|
||||
// 0 is default for node
|
||||
var keepAliveDelay = sockopts.keepAliveDelay || 0;
|
||||
|
||||
var extraClientProperties = sockopts.clientProperties || {};
|
||||
|
||||
var protocol, fields;
|
||||
if (typeof url === 'object') {
|
||||
protocol = (url.protocol || 'amqp') + ':';
|
||||
sockopts.host = url.hostname;
|
||||
sockopts.servername = sockopts.servername || url.hostname;
|
||||
sockopts.port = url.port || ((protocol === 'amqp:') ? 5672 : 5671);
|
||||
|
||||
var user, 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 || '';
|
||||
}
|
||||
|
||||
var 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 {
|
||||
var parts = URL(url, true); // yes, parse the query string
|
||||
protocol = parts.protocol;
|
||||
sockopts.host = parts.hostname;
|
||||
sockopts.servername = sockopts.servername || parts.hostname;
|
||||
sockopts.port = parseInt(parts.port) || ((protocol === 'amqp:') ? 5672 : 5671);
|
||||
var vhost = parts.pathname ? parts.pathname.substr(1) : null;
|
||||
fields = openFrames(vhost, parts.query, sockopts.credentials || credentialsFromUrl(parts), extraClientProperties);
|
||||
}
|
||||
|
||||
var sockok = false;
|
||||
var sock;
|
||||
|
||||
function onConnect() {
|
||||
sockok = true;
|
||||
sock.setNoDelay(noDelay);
|
||||
if (keepAlive) sock.setKeepAlive(keepAlive, keepAliveDelay);
|
||||
|
||||
var c = new Connection(sock);
|
||||
c.open(fields, function(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 = require('net').connect(sockopts, onConnect);
|
||||
}
|
||||
else if (protocol === 'amqps:') {
|
||||
sock = require('tls').connect(sockopts, onConnect);
|
||||
}
|
||||
else {
|
||||
throw new Error("Expected amqp: or amqps: as the protocol; got " + protocol);
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
sock.setTimeout(timeout, function() {
|
||||
sock.end();
|
||||
sock.destroy();
|
||||
openCallback(new Error('connect ETIMEDOUT'));
|
||||
});
|
||||
}
|
||||
|
||||
sock.once('error', function(err) {
|
||||
if (!sockok) openCallback(err);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
module.exports.connect = connect;
|
||||
module.exports.credentialsFromUrl = credentialsFromUrl;
|
||||
675
node_modules/amqplib/lib/connection.js
generated
vendored
Normal file
675
node_modules/amqplib/lib/connection.js
generated
vendored
Normal file
@@ -0,0 +1,675 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
'use strict';
|
||||
|
||||
var defs = require('./defs');
|
||||
var constants = defs.constants;
|
||||
var frame = require('./frame');
|
||||
var HEARTBEAT = frame.HEARTBEAT;
|
||||
var Mux = require('./mux').Mux;
|
||||
|
||||
var Duplex = require('stream').Duplex;
|
||||
var EventEmitter = require('events');
|
||||
var Heart = require('./heartbeat').Heart;
|
||||
|
||||
var methodName = require('./format').methodName;
|
||||
var closeMsg = require('./format').closeMessage;
|
||||
var inspect = require('./format').inspect;
|
||||
|
||||
var BitSet = require('./bitset').BitSet;
|
||||
var fmt = require('util').format;
|
||||
var PassThrough = require('stream').PassThrough;
|
||||
var IllegalOperationError = require('./error').IllegalOperationError;
|
||||
var stackCapture = require('./error').stackCapture;
|
||||
|
||||
// High-water mark for channel write buffers, in 'objects' (which are
|
||||
// encoded frames as buffers).
|
||||
var 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.
|
||||
var SINGLE_CHUNK_THRESHOLD = 2048;
|
||||
|
||||
class Connection extends EventEmitter {
|
||||
constructor (underlying) {
|
||||
super();
|
||||
|
||||
var stream = this.stream = wrapStream(underlying);
|
||||
this.muxer = new Mux(stream);
|
||||
|
||||
// frames
|
||||
this.rest = Buffer.alloc(0);
|
||||
this.frameMax = constants.FRAME_MIN_SIZE;
|
||||
this.sentSinceLastCheck = false;
|
||||
this.recvSinceLastCheck = false;
|
||||
|
||||
this.expectSocketClose = 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) {
|
||||
var self = this;
|
||||
var openCallback = openCallback0 || function () { };
|
||||
|
||||
// This is where we'll put our negotiated values
|
||||
var tunedOptions = Object.create(allFields);
|
||||
|
||||
function wait (k) {
|
||||
self.step(function (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(function (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) {
|
||||
var 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:
|
||||
var fields = reply.fields;
|
||||
tunedOptions.frameMax =
|
||||
negotiate(fields.frameMax, allFields.frameMax);
|
||||
tunedOptions.channelMax =
|
||||
negotiate(fields.channelMax, allFields.channelMax);
|
||||
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) {
|
||||
var k = closeCallback && function () { 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
|
||||
});
|
||||
var s = stackCapture('closeBecause called: ' + reason);
|
||||
this.toClosing(s, k);
|
||||
}
|
||||
|
||||
closeWithError (reason, code, error) {
|
||||
this.emit('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;
|
||||
this.emit('error', err);
|
||||
var 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) {
|
||||
var send = this.sendMethod.bind(this);
|
||||
|
||||
this.accept = function (f) {
|
||||
if (f.id === defs.ConnectionCloseOk) {
|
||||
if (k)
|
||||
k();
|
||||
var 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 (var i = 1; i < this.channels.length; i++) {
|
||||
var 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);
|
||||
var 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 = function (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();
|
||||
this.emit('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 {
|
||||
var self = this;
|
||||
var hb = new Heart(this.heartbeat,
|
||||
this.checkSend.bind(this),
|
||||
this.checkRecv.bind(this));
|
||||
hb.on('timeout', function () {
|
||||
var hberr = new Error("Heartbeat timeout");
|
||||
self.emit('error', hberr);
|
||||
var s = stackCapture('Heartbeat timeout');
|
||||
self.toClosed(s, hberr);
|
||||
});
|
||||
hb.on('beat', function () {
|
||||
self.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) {
|
||||
var next = this.freeChannels.nextClearBit(1);
|
||||
if (next < 0 || next > this.channelMax)
|
||||
throw new Error("No channels left to allocate");
|
||||
this.freeChannels.set(next);
|
||||
|
||||
var hwm = (options && options.highWaterMark) || DEFAULT_WRITE_HWM;
|
||||
var writeBuffer = new PassThrough({
|
||||
objectMode: true, highWaterMark: hwm
|
||||
});
|
||||
this.channels[next] = { channel: channel, buffer: writeBuffer };
|
||||
writeBuffer.on('drain', function () {
|
||||
channel.onBufferDrain();
|
||||
});
|
||||
this.muxer.pipeFrom(writeBuffer);
|
||||
return next;
|
||||
}
|
||||
|
||||
releaseChannel (channel) {
|
||||
this.freeChannels.clear(channel);
|
||||
var buffer = this.channels[channel].buffer;
|
||||
buffer.end(); // will also cause it to be unpiped
|
||||
this.channels[channel] = null;
|
||||
}
|
||||
|
||||
acceptLoop () {
|
||||
var self = this;
|
||||
|
||||
function go () {
|
||||
try {
|
||||
var f; while (f = self.recvFrame())
|
||||
self.accept(f);
|
||||
}
|
||||
catch (e) {
|
||||
self.emit('frameError', e);
|
||||
}
|
||||
}
|
||||
self.stream.on('readable', go);
|
||||
go();
|
||||
}
|
||||
|
||||
step (cb) {
|
||||
var self = this;
|
||||
function recv () {
|
||||
var f;
|
||||
try {
|
||||
f = self.recvFrame();
|
||||
}
|
||||
catch (e) {
|
||||
cb(e, null);
|
||||
return;
|
||||
}
|
||||
if (f)
|
||||
cb(null, f);
|
||||
else
|
||||
self.stream.once('readable', recv);
|
||||
}
|
||||
recv();
|
||||
}
|
||||
|
||||
checkSend () {
|
||||
var check = this.sentSinceLastCheck;
|
||||
this.sentSinceLastCheck = false;
|
||||
return check;
|
||||
}
|
||||
|
||||
checkRecv () {
|
||||
var 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) {
|
||||
var frame = encodeMethod(Method, channel, fields);
|
||||
this.sentSinceLastCheck = true;
|
||||
var 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');
|
||||
|
||||
var mframe = encodeMethod(Method, channel, fields);
|
||||
var pframe = encodeProperties(Properties, channel,
|
||||
content.length, props);
|
||||
var buffer = this.channels[channel].buffer;
|
||||
this.sentSinceLastCheck = true;
|
||||
|
||||
var methodHeaderLen = mframe.length + pframe.length;
|
||||
var bodyLen = (content.length > 0) ?
|
||||
content.length + FRAME_OVERHEAD : 0;
|
||||
var 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
|
||||
var all = Buffer.allocUnsafe(allLen);
|
||||
var 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
|
||||
var both = Buffer.allocUnsafe(methodHeaderLen);
|
||||
var 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));
|
||||
}
|
||||
var writeResult = true;
|
||||
var buffer = this.channels[channel].buffer;
|
||||
|
||||
var maxBody = this.frameMax - FRAME_OVERHEAD;
|
||||
|
||||
for (var offset = 0; offset < body.length; offset += maxBody) {
|
||||
var end = offset + maxBody;
|
||||
var slice = (end > body.length) ? body.subarray(offset) : body.subarray(offset, end);
|
||||
var bodyFrame = makeBodyFrame(channel, slice);
|
||||
writeResult = buffer.write(bodyFrame);
|
||||
}
|
||||
this.sentSinceLastCheck = true;
|
||||
return writeResult;
|
||||
}
|
||||
|
||||
recvFrame () {
|
||||
// %%% identifying invariants might help here?
|
||||
var frame = parseFrame(this.rest);
|
||||
|
||||
if (!frame) {
|
||||
var 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) {
|
||||
var 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 function(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, {});
|
||||
var emsg = fmt('Connection closed: %s', closeMsg(f));
|
||||
var s = stackCapture(emsg);
|
||||
var e = new Error(emsg);
|
||||
e.code = f.fields.replyCode;
|
||||
if (isFatalError(e)) {
|
||||
connection.emit('error', e);
|
||||
}
|
||||
connection.toClosed(s, e);
|
||||
}
|
||||
else if (f.id === defs.ConnectionBlocked) {
|
||||
connection.emit('blocked', f.fields.reason);
|
||||
}
|
||||
else if (f.id === defs.ConnectionUnblocked) {
|
||||
connection.emit('unblocked');
|
||||
}
|
||||
else if (f.id === defs.ConnectionUpdateSecretOk) {
|
||||
connection.emit('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 function() {
|
||||
throw new IllegalOperationError(msg, stack);
|
||||
};
|
||||
}
|
||||
|
||||
function invalidateSend(conn, msg, stack) {
|
||||
conn.sendMethod = conn.sendContent = conn.sendMessage =
|
||||
invalidOp(msg, stack);
|
||||
}
|
||||
|
||||
var encodeMethod = defs.encodeMethod;
|
||||
var encodeProperties = defs.encodeProperties;
|
||||
|
||||
var FRAME_OVERHEAD = defs.FRAME_OVERHEAD;
|
||||
var makeBodyFrame = frame.makeBodyFrame;
|
||||
|
||||
var parseFrame = frame.parseFrame;
|
||||
var decodeFrame = frame.decodeFrame;
|
||||
|
||||
function wrapStream(s) {
|
||||
if (s instanceof Duplex) return s;
|
||||
else {
|
||||
var ws = new Duplex();
|
||||
ws.wrap(s); //wraps the readable side of things
|
||||
ws._write = function(chunk, encoding, callback) {
|
||||
return 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;
|
||||
42
node_modules/amqplib/lib/credentials.js
generated
vendored
Normal file
42
node_modules/amqplib/lib/credentials.js
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
// 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)
|
||||
var codec = require('./codec')
|
||||
|
||||
module.exports.plain = function(user, passwd) {
|
||||
return {
|
||||
mechanism: 'PLAIN',
|
||||
response: function() {
|
||||
return Buffer.from(['', user, passwd].join(String.fromCharCode(0)))
|
||||
},
|
||||
username: user,
|
||||
password: passwd
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.amqplain = function(user, passwd) {
|
||||
return {
|
||||
mechanism: 'AMQPLAIN',
|
||||
response: function() {
|
||||
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 = function() {
|
||||
return {
|
||||
mechanism: 'EXTERNAL',
|
||||
response: function() { return Buffer.from(''); }
|
||||
}
|
||||
}
|
||||
5077
node_modules/amqplib/lib/defs.js
generated
vendored
Normal file
5077
node_modules/amqplib/lib/defs.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
24
node_modules/amqplib/lib/error.js
generated
vendored
Normal file
24
node_modules/amqplib/lib/error.js
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
var inherits = require('util').inherits;
|
||||
|
||||
function trimStack(stack, num) {
|
||||
return stack && stack.split('\n').slice(num).join('\n');
|
||||
}
|
||||
|
||||
function IllegalOperationError(msg, stack) {
|
||||
var 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) {
|
||||
var e = new Error();
|
||||
return 'Stack capture: ' + reason + '\n' +
|
||||
trimStack(e.stack, 2);
|
||||
}
|
||||
|
||||
module.exports.IllegalOperationError = IllegalOperationError;
|
||||
module.exports.stackCapture = stackCapture;
|
||||
39
node_modules/amqplib/lib/format.js
generated
vendored
Normal file
39
node_modules/amqplib/lib/format.js
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
// Stringifying various things
|
||||
|
||||
'use strict';
|
||||
|
||||
var defs = require('./defs');
|
||||
var format = require('util').format;
|
||||
var HEARTBEAT = require('./frame').HEARTBEAT;
|
||||
|
||||
module.exports.closeMessage = function(close) {
|
||||
var code = close.fields.replyCode;
|
||||
return format('%d (%s) with message "%s"',
|
||||
code, defs.constant_strs[code],
|
||||
close.fields.replyText);
|
||||
}
|
||||
|
||||
module.exports.methodName = function(id) {
|
||||
return defs.info(id).name;
|
||||
};
|
||||
|
||||
module.exports.inspect = function(frame, showFields) {
|
||||
if (frame === HEARTBEAT) {
|
||||
return '<Heartbeat>';
|
||||
}
|
||||
else if (!frame.id) {
|
||||
return format('<Content channel:%d size:%d>',
|
||||
frame.channel, frame.size);
|
||||
}
|
||||
else {
|
||||
var info = defs.info(frame.id);
|
||||
return format('<%s channel:%d%s>', info.name, frame.channel,
|
||||
(showFields)
|
||||
? ' ' + JSON.stringify(frame.fields, undefined, 2)
|
||||
: '');
|
||||
}
|
||||
}
|
||||
175
node_modules/amqplib/lib/frame.js
generated
vendored
Normal file
175
node_modules/amqplib/lib/frame.js
generated
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
// The river sweeps through
|
||||
// Silt and twigs, gravel and leaves
|
||||
// Driving the wheel on
|
||||
|
||||
'use strict';
|
||||
|
||||
const ints = require('buffer-more-ints')
|
||||
var defs = require('./defs');
|
||||
var constants = defs.constants;
|
||||
var 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
|
||||
var FRAME_METHOD = constants.FRAME_METHOD,
|
||||
FRAME_HEARTBEAT = constants.FRAME_HEARTBEAT,
|
||||
FRAME_HEADER = constants.FRAME_HEADER,
|
||||
FRAME_BODY = constants.FRAME_BODY,
|
||||
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
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is a polyfill which will read a big int 64 bit as a number.
|
||||
* @arg { Buffer } buffer
|
||||
* @arg { number } offset
|
||||
* @returns { number }
|
||||
*/
|
||||
function readInt64BE(buffer, offset) {
|
||||
/**
|
||||
* We try to use native implementation if available here because
|
||||
* buffer-more-ints does not
|
||||
*/
|
||||
if (typeof Buffer.prototype.readBigInt64BE === 'function') {
|
||||
return Number(buffer.readBigInt64BE(offset))
|
||||
}
|
||||
|
||||
return ints.readInt64BE(buffer, 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 = function (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;
|
||||
|
||||
var 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;
|
||||
92
node_modules/amqplib/lib/heartbeat.js
generated
vendored
Normal file
92
node_modules/amqplib/lib/heartbeat.js
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
// 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.
|
||||
|
||||
'use strict';
|
||||
|
||||
var EventEmitter = require('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;
|
||||
|
||||
var intervalMs = interval * module.exports.UNITS_TO_MS;
|
||||
// Function#bind is my new best friend
|
||||
var beat = this.emit.bind(this, 'beat');
|
||||
var 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
|
||||
var 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;
|
||||
126
node_modules/amqplib/lib/mux.js
generated
vendored
Normal file
126
node_modules/amqplib/lib/mux.js
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
'use strict';
|
||||
|
||||
// A Mux is an object into which other readable streams may be piped;
|
||||
// it then writes 'packets' from the upstreams to the given
|
||||
// downstream.
|
||||
|
||||
var assert = require('assert');
|
||||
|
||||
var schedule = (typeof setImmediate === 'function') ?
|
||||
setImmediate : process.nextTick;
|
||||
|
||||
class Mux {
|
||||
constructor (downstream) {
|
||||
this.newStreams = [];
|
||||
this.oldStreams = [];
|
||||
this.blocked = false;
|
||||
this.scheduledRead = false;
|
||||
|
||||
this.out = downstream;
|
||||
var self = this;
|
||||
downstream.on('drain', function () {
|
||||
self.blocked = false;
|
||||
self._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;
|
||||
|
||||
var accepting = true;
|
||||
var 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) {
|
||||
var s;
|
||||
while (accepting && (s = streams.shift())) {
|
||||
var chunk = s.read();
|
||||
if (chunk !== null) {
|
||||
accepting = out.write(chunk);
|
||||
streams.push(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 () {
|
||||
var self = this;
|
||||
|
||||
if (!self.scheduledRead) {
|
||||
schedule(function () {
|
||||
self.scheduledRead = false;
|
||||
self._readIncoming();
|
||||
});
|
||||
self.scheduledRead = true;
|
||||
}
|
||||
}
|
||||
|
||||
pipeFrom (readable) {
|
||||
var 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;
|
||||
34
node_modules/amqplib/package.json
generated
vendored
Normal file
34
node_modules/amqplib/package.json
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "amqplib",
|
||||
"homepage": "http://amqp-node.github.io/amqplib/",
|
||||
"main": "./channel_api.js",
|
||||
"version": "0.10.8",
|
||||
"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": ">=10"
|
||||
},
|
||||
"dependencies": {
|
||||
"buffer-more-ints": "~1.0.0",
|
||||
"url-parse": "~1.5.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"claire": "0.4.1",
|
||||
"mocha": "^9.2.2",
|
||||
"nyc": "^15.1.0",
|
||||
"uglify-js": "2.8.x"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "make test"
|
||||
},
|
||||
"keywords": [
|
||||
"AMQP",
|
||||
"AMQP 0-9-1",
|
||||
"RabbitMQ"
|
||||
],
|
||||
"author": "Michael Bridgen <mikeb@squaremobius.net>",
|
||||
"license": "MIT"
|
||||
}
|
||||
69
node_modules/amqplib/test/bitset.js
generated
vendored
Normal file
69
node_modules/amqplib/test/bitset.js
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
'use strict';
|
||||
|
||||
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 => {
|
||||
return new BitSet(size);
|
||||
},
|
||||
choice(arb.Nothing, PosInt)));
|
||||
|
||||
suite('BitSet', () => {
|
||||
|
||||
test('get bit', forAll(EmptyBitSet, PosInt)
|
||||
.satisfy((b, bit) => {
|
||||
b.set(bit);
|
||||
return b.get(bit);
|
||||
}).asTest());
|
||||
|
||||
test('clear bit', forAll(EmptyBitSet, PosInt)
|
||||
.satisfy((b, bit) => {
|
||||
b.set(bit);
|
||||
b.clear(bit);
|
||||
return !b.get(bit);
|
||||
}).asTest());
|
||||
|
||||
test('next set of empty', forAll(EmptyBitSet)
|
||||
.satisfy(b => {
|
||||
return b.nextSetBit(0) === -1;
|
||||
}).asTest());
|
||||
|
||||
test('next set of one bit', forAll(EmptyBitSet, PosInt)
|
||||
.satisfy((b, bit) => {
|
||||
b.set(bit);
|
||||
return b.nextSetBit(0) === bit;
|
||||
}).asTest());
|
||||
|
||||
test('next set same bit', forAll(EmptyBitSet, PosInt)
|
||||
.satisfy((b, bit) => {
|
||||
b.set(bit);
|
||||
return b.nextSetBit(bit) === bit;
|
||||
}).asTest());
|
||||
|
||||
test('next set following bit', forAll(EmptyBitSet, PosInt)
|
||||
.satisfy((b, bit) => {
|
||||
b.set(bit);
|
||||
return b.nextSetBit(bit+1) === -1;
|
||||
}).asTest());
|
||||
|
||||
test('next clear of empty', forAll(EmptyBitSet, PosInt)
|
||||
.satisfy((b, bit) => { return b.nextClearBit(bit) === bit; })
|
||||
.asTest());
|
||||
|
||||
test('next clear of one set', forAll(EmptyBitSet, PosInt)
|
||||
.satisfy((b, bit) => {
|
||||
b.set(bit);
|
||||
return b.nextClearBit(bit) === bit + 1;
|
||||
}).asTest());
|
||||
});
|
||||
379
node_modules/amqplib/test/callback_api.js
generated
vendored
Normal file
379
node_modules/amqplib/test/callback_api.js
generated
vendored
Normal file
@@ -0,0 +1,379 @@
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var crypto = require('crypto');
|
||||
var api = require('../callback_api');
|
||||
var util = require('./util');
|
||||
var schedule = util.schedule;
|
||||
var randomString = util.randomString;
|
||||
var kCallback = util.kCallback;
|
||||
var domain = require('domain');
|
||||
|
||||
var URL = process.env.URL || 'amqp://localhost';
|
||||
|
||||
function connect(cb) {
|
||||
api.connect(URL, {}, cb);
|
||||
}
|
||||
|
||||
// Construct a node-style callback from a `done` function
|
||||
function doneCallback(done) {
|
||||
return function(err, _) {
|
||||
if (err == null) done();
|
||||
else done(err);
|
||||
};
|
||||
}
|
||||
|
||||
function ignore() {}
|
||||
|
||||
function twice(done) {
|
||||
var first = function(err) {
|
||||
if (err == undefined) second = done;
|
||||
else second = ignore, done(err);
|
||||
};
|
||||
var second = function(err) {
|
||||
if (err == undefined) first = done;
|
||||
else first = ignore, done(err);
|
||||
};
|
||||
return {first: function(err) { first(err); },
|
||||
second: function(err) { second(err); }};
|
||||
}
|
||||
|
||||
// Adapt 'done' to a callback that's expected to fail
|
||||
function failCallback(done) {
|
||||
return function(err, _) {
|
||||
if (err == null) done(new Error('Expected failure, got ' + val));
|
||||
else done();
|
||||
};
|
||||
}
|
||||
|
||||
function waitForMessages(ch, q, k) {
|
||||
ch.checkQueue(q, function(e, ok) {
|
||||
if (e != null) return k(e);
|
||||
else if (ok.messageCount > 0) return k(null, ok);
|
||||
else schedule(waitForMessages.bind(null, ch, q, k));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
suite('connect', function() {
|
||||
|
||||
test('at all', function(done) {
|
||||
connect(doneCallback(done));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('updateSecret', function() {
|
||||
test('updateSecret', function(done) {
|
||||
connect(kCallback(function(c) {
|
||||
c.updateSecret(Buffer.from('new secret'), 'no reason', doneCallback(done));
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
function channel_test_fn(method) {
|
||||
return function(name, options, chfun) {
|
||||
if (arguments.length === 2) {
|
||||
chfun = options;
|
||||
options = {};
|
||||
}
|
||||
test(name, function(done) {
|
||||
connect(kCallback(function(c) {
|
||||
c[method](options, kCallback(function(ch) {
|
||||
chfun(ch, done);
|
||||
}, done));
|
||||
}, done));
|
||||
});
|
||||
};
|
||||
}
|
||||
var channel_test = channel_test_fn('createChannel');
|
||||
var confirm_channel_test = channel_test_fn('createConfirmChannel');
|
||||
|
||||
suite('channel open', function() {
|
||||
|
||||
channel_test('at all', function(ch, done) {
|
||||
done();
|
||||
});
|
||||
|
||||
channel_test('open and close', function(ch, done) {
|
||||
ch.close(doneCallback(done));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('assert, check, delete', function() {
|
||||
|
||||
channel_test('assert, check, delete queue', function(ch, done) {
|
||||
ch.assertQueue('test.cb.queue', {}, kCallback(function(q) {
|
||||
ch.checkQueue('test.cb.queue', kCallback(function(ok) {
|
||||
ch.deleteQueue('test.cb.queue', {}, doneCallback(done));
|
||||
}, done));
|
||||
}, done));
|
||||
});
|
||||
|
||||
channel_test('assert, check, delete exchange', function(ch, done) {
|
||||
ch.assertExchange(
|
||||
'test.cb.exchange', 'topic', {}, kCallback(function(ex) {
|
||||
ch.checkExchange('test.cb.exchange', kCallback(function(ok) {
|
||||
ch.deleteExchange('test.cb.exchange', {}, doneCallback(done));
|
||||
}, done));
|
||||
}, done));
|
||||
});
|
||||
|
||||
channel_test('fail on check non-queue', function(ch, done) {
|
||||
var both = twice(done);
|
||||
ch.on('error', failCallback(both.first));
|
||||
ch.checkQueue('test.cb.nothere', failCallback(both.second));
|
||||
});
|
||||
|
||||
channel_test('fail on check non-exchange', function(ch, done) {
|
||||
var both = twice(done);
|
||||
ch.on('error', failCallback(both.first));
|
||||
ch.checkExchange('test.cb.nothere', failCallback(both.second));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('bindings', function() {
|
||||
|
||||
channel_test('bind queue', function(ch, done) {
|
||||
ch.assertQueue('test.cb.bindq', {}, kCallback(function(q) {
|
||||
ch.assertExchange(
|
||||
'test.cb.bindex', 'fanout', {}, kCallback(function(ex) {
|
||||
ch.bindQueue(q.queue, ex.exchange, '', {},
|
||||
doneCallback(done));
|
||||
}, done));
|
||||
}, done));
|
||||
});
|
||||
|
||||
channel_test('bind exchange', function(ch, done) {
|
||||
ch.assertExchange(
|
||||
'test.cb.bindex1', 'fanout', {}, kCallback(function(ex1) {
|
||||
ch.assertExchange(
|
||||
'test.cb.bindex2', 'fanout', {}, kCallback(function(ex2) {
|
||||
ch.bindExchange(ex1.exchange,
|
||||
ex2.exchange, '', {},
|
||||
doneCallback(done));
|
||||
}, done));
|
||||
}, done));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('sending messages', function() {
|
||||
|
||||
channel_test('send to queue and consume noAck', function(ch, done) {
|
||||
var msg = randomString();
|
||||
ch.assertQueue('', {exclusive: true}, function(e, q) {
|
||||
if (e !== null) return done(e);
|
||||
ch.consume(q.queue, function(m) {
|
||||
if (m.content.toString() == msg) done();
|
||||
else done(new Error("message content doesn't match:" +
|
||||
msg + " =/= " + m.content.toString()));
|
||||
}, {noAck: true, exclusive: true});
|
||||
ch.sendToQueue(q.queue, Buffer.from(msg));
|
||||
});
|
||||
});
|
||||
|
||||
channel_test('send to queue and consume ack', function(ch, done) {
|
||||
var msg = randomString();
|
||||
ch.assertQueue('', {exclusive: true}, function(e, q) {
|
||||
if (e !== null) return done(e);
|
||||
ch.consume(q.queue, function(m) {
|
||||
if (m.content.toString() == msg) {
|
||||
ch.ack(m);
|
||||
done();
|
||||
}
|
||||
else done(new Error("message content doesn't match:" +
|
||||
msg + " =/= " + m.content.toString()));
|
||||
}, {noAck: false, exclusive: true});
|
||||
ch.sendToQueue(q.queue, Buffer.from(msg));
|
||||
});
|
||||
});
|
||||
|
||||
channel_test('send to and get from queue', function(ch, done) {
|
||||
ch.assertQueue('', {exclusive: true}, function(e, q) {
|
||||
if (e != null) return done(e);
|
||||
var msg = randomString();
|
||||
ch.sendToQueue(q.queue, Buffer.from(msg));
|
||||
waitForMessages(ch, q.queue, function(e, _) {
|
||||
if (e != null) return done(e);
|
||||
ch.get(q.queue, {noAck: true}, function(e, m) {
|
||||
if (e != null)
|
||||
return done(e);
|
||||
else if (!m)
|
||||
return done(new Error('Empty (false) not expected'));
|
||||
else if (m.content.toString() == msg)
|
||||
return done();
|
||||
else
|
||||
return done(
|
||||
new Error('Messages do not match: ' +
|
||||
msg + ' =/= ' + m.content.toString()));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var channelOptions = {};
|
||||
|
||||
channel_test('find high watermark', function(ch, done) {
|
||||
var msg = randomString();
|
||||
var baseline = 0;
|
||||
ch.assertQueue('', {exclusive: true}, function(e, q) {
|
||||
if (e !== null) return done(e);
|
||||
while (ch.sendToQueue(q.queue, Buffer.from(msg))) {
|
||||
baseline++;
|
||||
};
|
||||
channelOptions.highWaterMark = baseline * 2;
|
||||
done();
|
||||
})
|
||||
});
|
||||
|
||||
channel_test('set high watermark', channelOptions, function(ch, done) {
|
||||
var msg = randomString();
|
||||
ch.assertQueue('', {exclusive: true}, function(e, q) {
|
||||
if (e !== null) return done(e);
|
||||
var ok;
|
||||
for (var i = 0; i < channelOptions.highWaterMark; i++) {
|
||||
ok = ch.sendToQueue(q.queue, Buffer.from(msg));
|
||||
assert.equal(ok, true);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('ConfirmChannel', function() {
|
||||
|
||||
confirm_channel_test('Receive confirmation', function(ch, done) {
|
||||
// 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', function(ch, done) {
|
||||
for (var i=0; i < 1000; i++) {
|
||||
ch.publish('', '', Buffer.from('foo'), {});
|
||||
}
|
||||
ch.waitForConfirms(done);
|
||||
});
|
||||
|
||||
var channelOptions = {};
|
||||
|
||||
confirm_channel_test('find high watermark', function(ch, done) {
|
||||
var msg = randomString();
|
||||
var baseline = 0;
|
||||
ch.assertQueue('', {exclusive: true}, function(e, q) {
|
||||
if (e !== null) return done(e);
|
||||
while (ch.sendToQueue(q.queue, Buffer.from(msg))) {
|
||||
baseline++;
|
||||
};
|
||||
channelOptions.highWaterMark = baseline * 2;
|
||||
done();
|
||||
})
|
||||
});
|
||||
|
||||
confirm_channel_test('set high watermark', channelOptions, function(ch, done) {
|
||||
var msg = randomString();
|
||||
ch.assertQueue('', {exclusive: true}, function(e, q) {
|
||||
if (e !== null) return done(e);
|
||||
var ok;
|
||||
for (var i = 0; i < channelOptions.highWaterMark; i++) {
|
||||
ok = ch.sendToQueue(q.queue, Buffer.from(msg));
|
||||
assert.equal(ok, true);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite("Error handling", function() {
|
||||
|
||||
/*
|
||||
I don't like having to do this, but there appears to be something
|
||||
broken about domains in Node.JS v0.8 and mocha. Apparently it has to
|
||||
do with how mocha and domains hook into error propogation:
|
||||
https://github.com/visionmedia/mocha/issues/513 (summary: domains in
|
||||
Node.JS v0.8 don't prevent uncaughtException from firing, and that's
|
||||
what mocha uses to detect .. an uncaught exception).
|
||||
|
||||
Using domains with amqplib *does* work in practice in Node.JS v0.8:
|
||||
that is, it's possible to throw an exception in a callback and deal
|
||||
with it in the active domain, and thereby avoid it crashing the
|
||||
program.
|
||||
*/
|
||||
if (util.versionGreaterThan(process.versions.node, '0.8')) {
|
||||
test('Throw error in connection open callback', function(done) {
|
||||
var dom = domain.createDomain();
|
||||
dom.on('error', failCallback(done));
|
||||
connect(dom.bind(function(err, conn) {
|
||||
throw new Error('Spurious connection open callback error');
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: refactor {error_test, channel_test}
|
||||
function error_test(name, fun) {
|
||||
test(name, function(done) {
|
||||
var dom = domain.createDomain();
|
||||
dom.run(function() {
|
||||
connect(kCallback(function(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(kCallback(function(ch) {
|
||||
fun(ch, done, dom);
|
||||
}, done));
|
||||
}, done));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
error_test('Channel open callback throws an error', function(ch, done, dom) {
|
||||
dom.on('error', failCallback(done));
|
||||
throw new Error('Error in open callback');
|
||||
});
|
||||
|
||||
error_test('RPC callback throws error', function(ch, done, dom) {
|
||||
dom.on('error', failCallback(done));
|
||||
ch.prefetch(0, false, function(err, ok) {
|
||||
throw new Error('Spurious callback error');
|
||||
});
|
||||
});
|
||||
|
||||
error_test('Get callback throws error', function(ch, done, dom) {
|
||||
dom.on('error', failCallback(done));
|
||||
ch.assertQueue('test.cb.get-with-error', {}, function(err, ok) {
|
||||
ch.get('test.cb.get-with-error', {noAck: true}, function() {
|
||||
throw new Error('Spurious callback error');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
error_test('Consume callback throws error', function(ch, done, dom) {
|
||||
dom.on('error', failCallback(done));
|
||||
ch.assertQueue('test.cb.consume-with-error', {}, function(err, ok) {
|
||||
ch.consume('test.cb.consume-with-error', ignore, {noAck: true}, function() {
|
||||
throw new Error('Spurious callback error');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
error_test('Get from non-queue invokes error k', function(ch, done, dom) {
|
||||
var both = twice(failCallback(done));
|
||||
dom.on('error', both.first);
|
||||
ch.get('', {}, both.second);
|
||||
});
|
||||
|
||||
error_test('Consume from non-queue invokes error k', function(ch, done, dom) {
|
||||
var both = twice(failCallback(done));
|
||||
dom.on('error', both.first);
|
||||
ch.consume('', both.second);
|
||||
});
|
||||
|
||||
});
|
||||
621
node_modules/amqplib/test/channel.js
generated
vendored
Normal file
621
node_modules/amqplib/test/channel.js
generated
vendored
Normal file
@@ -0,0 +1,621 @@
|
||||
// Test the channel machinery
|
||||
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var promisify = require('util').promisify;
|
||||
var Channel = require('../lib/channel').Channel;
|
||||
var Connection = require('../lib/connection').Connection;
|
||||
var util = require('./util');
|
||||
var succeed = util.succeed, fail = util.fail, latch = util.latch;
|
||||
var completes = util.completes;
|
||||
var defs = require('../lib/defs');
|
||||
var conn_handshake = require('./connection').connection_handshake;
|
||||
var OPEN_OPTS = require('./connection').OPEN_OPTS;
|
||||
|
||||
var LOG_ERRORS = process.env.LOG_ERRORS;
|
||||
|
||||
function baseChannelTest(client, server) {
|
||||
return function(done) {
|
||||
var bothDone = latch(2, done);
|
||||
var pair = util.socketPair();
|
||||
var c = new Connection(pair.client);
|
||||
|
||||
if (LOG_ERRORS) c.on('error', console.warn);
|
||||
|
||||
c.open(OPEN_OPTS, function(err, ok) {
|
||||
if (err === null) client(c, bothDone);
|
||||
else fail(bothDone);
|
||||
});
|
||||
|
||||
pair.server.read(8); // discard the protocol header
|
||||
var s = util.runServer(pair.server, function(send, wait) {
|
||||
conn_handshake(send, wait)
|
||||
.then(function() {
|
||||
server(send, wait, bothDone);
|
||||
}, fail(bothDone));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function channelTest(client, server) {
|
||||
return baseChannelTest(
|
||||
function(conn, done) {
|
||||
var ch = new Channel(conn);
|
||||
if (LOG_ERRORS) ch.on('error', console.warn);
|
||||
client(ch, done, conn);
|
||||
},
|
||||
function(send, wait, done) {
|
||||
channel_handshake(send, wait)
|
||||
.then(function(ch) {
|
||||
return server(send, wait, done, ch);
|
||||
}).then(null, fail(done)); // so you can return a promise to let
|
||||
// errors bubble out
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
function channel_handshake(send, wait) {
|
||||
return wait(defs.ChannelOpen)()
|
||||
.then(function(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
|
||||
var 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(function(cb) {
|
||||
ch._rpc(defs.ChannelOpen, {outOfBand: ''}, defs.ChannelOpenOk, cb);
|
||||
})();
|
||||
}
|
||||
|
||||
suite("channel open and close", function() {
|
||||
|
||||
test("open", channelTest(
|
||||
function(ch, done) {
|
||||
open(ch).then(succeed(done), fail(done));
|
||||
},
|
||||
function(send, wait, done) {
|
||||
done();
|
||||
}));
|
||||
|
||||
test("bad server", baseChannelTest(
|
||||
function(c, done) {
|
||||
var ch = new Channel(c);
|
||||
open(ch).then(fail(done), succeed(done));
|
||||
},
|
||||
function(send, wait, done) {
|
||||
return wait(defs.ChannelOpen)()
|
||||
.then(function(open) {
|
||||
send(defs.ChannelCloseOk, {}, open.channel);
|
||||
}).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("open, close", channelTest(
|
||||
function(ch, done) {
|
||||
open(ch)
|
||||
.then(function() {
|
||||
return new Promise(function(resolve) {
|
||||
ch.closeBecause("Bye", defs.constants.REPLY_SUCCESS, resolve);
|
||||
});
|
||||
})
|
||||
.then(succeed(done), fail(done));
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
return wait(defs.ChannelClose)()
|
||||
.then(function(close) {
|
||||
send(defs.ChannelCloseOk, {}, ch);
|
||||
}).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("server close", channelTest(
|
||||
function(ch, done) {
|
||||
ch.on('error', function(error) {
|
||||
assert.strictEqual(504, error.code);
|
||||
assert.strictEqual(0, error.classId);
|
||||
assert.strictEqual(0, error.methodId);
|
||||
succeed(done)();
|
||||
});
|
||||
open(ch);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
send(defs.ChannelClose, {
|
||||
replyText: 'Forced close',
|
||||
replyCode: defs.constants.CHANNEL_ERROR,
|
||||
classId: 0, methodId: 0
|
||||
}, ch);
|
||||
wait(defs.ChannelCloseOk)()
|
||||
.then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("overlapping channel/server close", channelTest(
|
||||
function(ch, done, conn) {
|
||||
var both = latch(2, done);
|
||||
conn.on('error', succeed(both));
|
||||
ch.on('close', succeed(both));
|
||||
open(ch).then(function() {
|
||||
ch.closeBecause("Bye", defs.constants.REPLY_SUCCESS);
|
||||
}, fail(both));
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
wait(defs.ChannelClose)()
|
||||
.then(function() {
|
||||
send(defs.ConnectionClose, {
|
||||
replyText: 'Got there first',
|
||||
replyCode: defs.constants.INTERNAL_ERROR,
|
||||
classId: 0, methodId: 0
|
||||
}, 0);
|
||||
})
|
||||
.then(wait(defs.ConnectionCloseOk))
|
||||
.then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("double close", channelTest(
|
||||
function(ch, done) {
|
||||
open(ch).then(function() {
|
||||
ch.closeBecause("First close", defs.constants.REPLY_SUCCESS);
|
||||
// NB no synchronisation, we do this straight away
|
||||
assert.throws(function() {
|
||||
ch.closeBecause("Second close", defs.constants.REPLY_SUCCESS);
|
||||
});
|
||||
}).then(succeed(done), fail(done));
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
wait(defs.ChannelClose)()
|
||||
.then(function() {
|
||||
send(defs.ChannelCloseOk, {
|
||||
}, ch);
|
||||
})
|
||||
.then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
}); //suite
|
||||
|
||||
suite("channel machinery", function() {
|
||||
|
||||
test("RPC", channelTest(
|
||||
function(ch, done) {
|
||||
var rpcLatch = latch(3, done);
|
||||
open(ch).then(function() {
|
||||
|
||||
function wheeboom(err, f) {
|
||||
if (err !== null) rpcLatch(err);
|
||||
else rpcLatch();
|
||||
}
|
||||
|
||||
var fields = {
|
||||
prefetchCount: 10,
|
||||
prefetchSize: 0,
|
||||
global: false
|
||||
};
|
||||
|
||||
ch._rpc(defs.BasicQos, fields, defs.BasicQosOk, wheeboom);
|
||||
ch._rpc(defs.BasicQos, fields, defs.BasicQosOk, wheeboom);
|
||||
ch._rpc(defs.BasicQos, fields, defs.BasicQosOk, wheeboom);
|
||||
}).then(null, fail(rpcLatch));
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
function sendOk(f) {
|
||||
send(defs.BasicQosOk, {}, ch);
|
||||
}
|
||||
|
||||
return wait(defs.BasicQos)()
|
||||
.then(sendOk)
|
||||
.then(wait(defs.BasicQos))
|
||||
.then(sendOk)
|
||||
.then(wait(defs.BasicQos))
|
||||
.then(sendOk)
|
||||
.then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("Bad RPC", channelTest(
|
||||
function(ch, done) {
|
||||
// We want to see the RPC rejected and the channel closed (with an
|
||||
// error)
|
||||
var errLatch = latch(2, done);
|
||||
ch.on('error', function(error) {
|
||||
assert.strictEqual(505, error.code);
|
||||
assert.strictEqual(60, error.classId);
|
||||
assert.strictEqual(72, error.methodId);
|
||||
succeed(errLatch)();
|
||||
});
|
||||
|
||||
open(ch)
|
||||
.then(function() {
|
||||
ch._rpc(defs.BasicRecover, {requeue: true}, defs.BasicRecoverOk,
|
||||
function(err) {
|
||||
if (err !== null) errLatch();
|
||||
else errLatch(new Error('Expected RPC failure'));
|
||||
});
|
||||
}, fail(errLatch));
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
return wait()()
|
||||
.then(function() {
|
||||
send(defs.BasicGetEmpty, {clusterId: ''}, ch);
|
||||
}) // oh wait! that was wrong! expect a channel close
|
||||
.then(wait(defs.ChannelClose))
|
||||
.then(function() {
|
||||
send(defs.ChannelCloseOk, {}, ch);
|
||||
}).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("RPC on closed channel", channelTest(
|
||||
function(ch, done) {
|
||||
open(ch);
|
||||
|
||||
var close = new Promise(function(resolve) {
|
||||
ch.on('error', function(error) {
|
||||
assert.strictEqual(504, error.code);
|
||||
assert.strictEqual(0, error.classId);
|
||||
assert.strictEqual(0, error.methodId);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
function failureCb(resolve, reject) {
|
||||
return function(err) {
|
||||
if (err !== null) resolve();
|
||||
else reject();
|
||||
}
|
||||
}
|
||||
|
||||
var fail1 = new Promise(function(resolve, reject) {
|
||||
return ch._rpc(defs.BasicRecover, {requeue:true}, defs.BasicRecoverOk,
|
||||
failureCb(resolve, reject));
|
||||
});
|
||||
|
||||
var fail2 = new Promise(function(resolve, reject) {
|
||||
return ch._rpc(defs.BasicRecover, {requeue:true}, defs.BasicRecoverOk,
|
||||
failureCb(resolve, reject));
|
||||
});
|
||||
|
||||
Promise.all([close, fail1, fail2])
|
||||
.then(succeed(done))
|
||||
.catch(fail(done));
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
wait(defs.BasicRecover)()
|
||||
.then(function() {
|
||||
send(defs.ChannelClose, {
|
||||
replyText: 'Nuh-uh!',
|
||||
replyCode: defs.constants.CHANNEL_ERROR,
|
||||
methodId: 0, classId: 0
|
||||
}, ch);
|
||||
return wait(defs.ChannelCloseOk);
|
||||
})
|
||||
.then(succeed(done))
|
||||
.catch(fail(done));
|
||||
}));
|
||||
|
||||
test("publish all < single chunk threshold", channelTest(
|
||||
function(ch, done) {
|
||||
open(ch)
|
||||
.then(function() {
|
||||
ch.sendMessage({
|
||||
exchange: 'foo', routingKey: 'bar',
|
||||
mandatory: false, immediate: false, ticket: 0
|
||||
}, {}, Buffer.from('foobar'));
|
||||
})
|
||||
.then(succeed(done), fail(done));
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
wait(defs.BasicPublish)()
|
||||
.then(wait(defs.BasicProperties))
|
||||
.then(wait(undefined)) // content frame
|
||||
.then(function(f) {
|
||||
assert.equal('foobar', f.content.toString());
|
||||
}).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("publish content > single chunk threshold", channelTest(
|
||||
function(ch, done) {
|
||||
open(ch);
|
||||
completes(function() {
|
||||
ch.sendMessage({
|
||||
exchange: 'foo', routingKey: 'bar',
|
||||
mandatory: false, immediate: false, ticket: 0
|
||||
}, {}, Buffer.alloc(3000));
|
||||
}, done);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
wait(defs.BasicPublish)()
|
||||
.then(wait(defs.BasicProperties))
|
||||
.then(wait(undefined)) // content frame
|
||||
.then(function(f) {
|
||||
assert.equal(3000, f.content.length);
|
||||
}).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("publish method & headers > threshold", channelTest(
|
||||
function(ch, done) {
|
||||
open(ch);
|
||||
completes(function() {
|
||||
ch.sendMessage({
|
||||
exchange: 'foo', routingKey: 'bar',
|
||||
mandatory: false, immediate: false, ticket: 0
|
||||
}, {
|
||||
headers: {foo: Buffer.alloc(3000)}
|
||||
}, Buffer.from('foobar'));
|
||||
}, done);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
wait(defs.BasicPublish)()
|
||||
.then(wait(defs.BasicProperties))
|
||||
.then(wait(undefined)) // content frame
|
||||
.then(function(f) {
|
||||
assert.equal('foobar', f.content.toString());
|
||||
}).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("publish zero-length message", channelTest(
|
||||
function(ch, done) {
|
||||
open(ch);
|
||||
completes(function() {
|
||||
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));
|
||||
}, done);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
wait(defs.BasicPublish)()
|
||||
.then(wait(defs.BasicProperties))
|
||||
// no content frame for a zero-length message
|
||||
.then(wait(defs.BasicPublish))
|
||||
.then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("delivery", channelTest(
|
||||
function(ch, done) {
|
||||
open(ch);
|
||||
ch.on('delivery', function(m) {
|
||||
completes(function() {
|
||||
assert.equal('barfoo', m.content.toString());
|
||||
}, done);
|
||||
});
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
completes(function() {
|
||||
send(defs.BasicDeliver, DELIVER_FIELDS, ch, Buffer.from('barfoo'));
|
||||
}, done);
|
||||
}));
|
||||
|
||||
test("zero byte msg", channelTest(
|
||||
function(ch, done) {
|
||||
open(ch);
|
||||
ch.on('delivery', function(m) {
|
||||
completes(function() {
|
||||
assert.deepEqual(Buffer.alloc(0), m.content);
|
||||
}, done);
|
||||
});
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
completes(function() {
|
||||
send(defs.BasicDeliver, DELIVER_FIELDS, ch, Buffer.from(''));
|
||||
}, done);
|
||||
}));
|
||||
|
||||
test("bad delivery", channelTest(
|
||||
function(ch, done) {
|
||||
var errorAndClose = latch(2, done);
|
||||
ch.on('error', function(error) {
|
||||
assert.strictEqual(505, error.code);
|
||||
assert.strictEqual(60, error.classId);
|
||||
assert.strictEqual(60, error.methodId);
|
||||
succeed(errorAndClose)();
|
||||
});
|
||||
ch.on('close', succeed(errorAndClose));
|
||||
open(ch);
|
||||
},
|
||||
function(send, wait, done, 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(function() {
|
||||
send(defs.ChannelCloseOk, {}, ch);
|
||||
}).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("bad content send", channelTest(
|
||||
function(ch, done) {
|
||||
completes(function() {
|
||||
open(ch);
|
||||
assert.throws(function() {
|
||||
ch.sendMessage({routingKey: 'foo',
|
||||
exchange: 'amq.direct'},
|
||||
{}, null);
|
||||
});
|
||||
}, done);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
done();
|
||||
}));
|
||||
|
||||
test("bad properties send", channelTest(
|
||||
function(ch, done) {
|
||||
completes(function() {
|
||||
open(ch);
|
||||
assert.throws(function() {
|
||||
ch.sendMessage({routingKey: 'foo',
|
||||
exchange: 'amq.direct'},
|
||||
{contentEncoding: 7},
|
||||
Buffer.from('foobar'));
|
||||
});
|
||||
}, done);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
done();
|
||||
}));
|
||||
|
||||
test("bad consumer", channelTest(
|
||||
function(ch, done) {
|
||||
var errorAndClose = latch(2, done);
|
||||
ch.on('delivery', function() {
|
||||
throw new Error("I am a bad consumer");
|
||||
});
|
||||
ch.on('error', function(error) {
|
||||
assert.strictEqual(541, error.code);
|
||||
assert.strictEqual(undefined, error.classId);
|
||||
assert.strictEqual(undefined, error.methodId);
|
||||
succeed(errorAndClose)();
|
||||
});
|
||||
ch.on('close', succeed(errorAndClose));
|
||||
open(ch);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
send(defs.BasicDeliver, DELIVER_FIELDS, ch, Buffer.from('barfoo'));
|
||||
return wait(defs.ChannelClose)()
|
||||
.then(function() {
|
||||
send(defs.ChannelCloseOk, {}, ch);
|
||||
}).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("bad send in consumer", channelTest(
|
||||
function(ch, done) {
|
||||
var errorAndClose = latch(2, done);
|
||||
ch.on('close', succeed(errorAndClose));
|
||||
ch.on('error', function(error) {
|
||||
assert.strictEqual(541, error.code);
|
||||
assert.strictEqual(undefined, error.classId);
|
||||
assert.strictEqual(undefined, error.methodId);
|
||||
succeed(errorAndClose)();
|
||||
});
|
||||
|
||||
ch.on('delivery', function() {
|
||||
ch.sendMessage({routingKey: 'foo',
|
||||
exchange: 'amq.direct'},
|
||||
{}, null); // can't send null
|
||||
});
|
||||
|
||||
open(ch);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
completes(function() {
|
||||
send(defs.BasicDeliver, DELIVER_FIELDS, ch,
|
||||
Buffer.from('barfoo'));
|
||||
}, done);
|
||||
return wait(defs.ChannelClose)()
|
||||
.then(function() {
|
||||
send(defs.ChannelCloseOk, {}, ch);
|
||||
}).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("return", channelTest(
|
||||
function(ch, done) {
|
||||
ch.on('return', function(m) {
|
||||
completes(function() {
|
||||
assert.equal('barfoo', m.content.toString());
|
||||
}, done);
|
||||
});
|
||||
open(ch);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
completes(function() {
|
||||
send(defs.BasicReturn, DELIVER_FIELDS, ch, Buffer.from('barfoo'));
|
||||
}, done);
|
||||
}));
|
||||
|
||||
test("cancel", channelTest(
|
||||
function(ch, done) {
|
||||
ch.on('cancel', function(f) {
|
||||
completes(function() {
|
||||
assert.equal('product of society', f.consumerTag);
|
||||
}, done);
|
||||
});
|
||||
open(ch);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
completes(function() {
|
||||
send(defs.BasicCancel, {
|
||||
consumerTag: 'product of society',
|
||||
nowait: false
|
||||
}, ch);
|
||||
}, done);
|
||||
}));
|
||||
|
||||
function confirmTest(variety, Method) {
|
||||
return test('confirm ' + variety, channelTest(
|
||||
function(ch, done) {
|
||||
ch.on(variety, function(f) {
|
||||
completes(function() {
|
||||
assert.equal(1, f.deliveryTag);
|
||||
}, done);
|
||||
});
|
||||
open(ch);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
completes(function() {
|
||||
send(Method, {
|
||||
deliveryTag: 1,
|
||||
multiple: false
|
||||
}, ch);
|
||||
}, done);
|
||||
}));
|
||||
}
|
||||
|
||||
confirmTest("ack", defs.BasicAck);
|
||||
confirmTest("nack", defs.BasicNack);
|
||||
|
||||
test("out-of-order acks", channelTest(
|
||||
function(ch, done) {
|
||||
var allConfirms = latch(3, function() {
|
||||
completes(function() {
|
||||
assert.equal(0, ch.unconfirmed.length);
|
||||
assert.equal(4, ch.lwm);
|
||||
}, done);
|
||||
});
|
||||
ch.pushConfirmCallback(allConfirms);
|
||||
ch.pushConfirmCallback(allConfirms);
|
||||
ch.pushConfirmCallback(allConfirms);
|
||||
open(ch);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
completes(function() {
|
||||
send(defs.BasicAck, {deliveryTag: 2, multiple: false}, ch);
|
||||
send(defs.BasicAck, {deliveryTag: 3, multiple: false}, ch);
|
||||
send(defs.BasicAck, {deliveryTag: 1, multiple: false}, ch);
|
||||
}, done);
|
||||
}));
|
||||
|
||||
test("not all out-of-order acks", channelTest(
|
||||
function(ch, done) {
|
||||
var allConfirms = latch(2, function() {
|
||||
completes(function() {
|
||||
assert.equal(1, ch.unconfirmed.length);
|
||||
assert.equal(3, ch.lwm);
|
||||
}, done);
|
||||
});
|
||||
ch.pushConfirmCallback(allConfirms); // tag = 1
|
||||
ch.pushConfirmCallback(allConfirms); // tag = 2
|
||||
ch.pushConfirmCallback(function() {
|
||||
done(new Error('Confirm callback should not be called'));
|
||||
});
|
||||
open(ch);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
completes(function() {
|
||||
send(defs.BasicAck, {deliveryTag: 2, multiple: false}, ch);
|
||||
send(defs.BasicAck, {deliveryTag: 1, multiple: false}, ch);
|
||||
}, done);
|
||||
}));
|
||||
|
||||
});
|
||||
606
node_modules/amqplib/test/channel_api.js
generated
vendored
Normal file
606
node_modules/amqplib/test/channel_api.js
generated
vendored
Normal file
@@ -0,0 +1,606 @@
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var api = require('../channel_api');
|
||||
var util = require('./util');
|
||||
var succeed = util.succeed, fail = util.fail;
|
||||
var schedule = util.schedule;
|
||||
var randomString = util.randomString;
|
||||
var promisify = require('util').promisify;
|
||||
|
||||
var URL = process.env.URL || 'amqp://localhost';
|
||||
|
||||
function connect() {
|
||||
return api.connect(URL);
|
||||
}
|
||||
|
||||
// Expect this promise to fail, and flip the results accordingly.
|
||||
function expectFail(promise) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
return promise.then(reject).catch(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
// I'll rely on operations being rejected, rather than the channel
|
||||
// close error, to detect failure.
|
||||
function ignore () {}
|
||||
function ignoreErrors(c) {
|
||||
c.on('error', ignore); return c;
|
||||
}
|
||||
function logErrors(c) {
|
||||
c.on('error', console.warn); return c;
|
||||
}
|
||||
|
||||
// Run a test with `name`, given a function that takes an open
|
||||
// channel, and returns a promise that is resolved on test success or
|
||||
// rejected on test failure.
|
||||
function channel_test(chmethod, name, chfun) {
|
||||
test(name, function(done) {
|
||||
connect(URL).then(logErrors).then(function(c) {
|
||||
c[chmethod]().then(ignoreErrors).then(chfun)
|
||||
.then(succeed(done), fail(done))
|
||||
// close the connection regardless of what happens with the test
|
||||
.finally(function() {c.close();});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var chtest = channel_test.bind(null, 'createChannel');
|
||||
|
||||
suite("connect", function() {
|
||||
|
||||
test("at all", function(done) {
|
||||
connect(URL).then(function(c) {
|
||||
return c.close()
|
||||
;}).then(succeed(done), fail(done));
|
||||
});
|
||||
|
||||
chtest("create channel", ignore); // i.e., just don't bork
|
||||
|
||||
});
|
||||
|
||||
suite("updateSecret", function() {
|
||||
test("updateSecret", function(done) {
|
||||
connect().then(function(c) {
|
||||
c.updateSecret(Buffer.from("new secret"), "no reason")
|
||||
.then(succeed(done), fail(done))
|
||||
.finally(function() { c.close(); });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var QUEUE_OPTS = {durable: false};
|
||||
var EX_OPTS = {durable: false};
|
||||
|
||||
suite("assert, check, delete", function() {
|
||||
|
||||
chtest("assert and check queue", function(ch) {
|
||||
return ch.assertQueue('test.check-queue', QUEUE_OPTS)
|
||||
.then(function(qok) {
|
||||
return ch.checkQueue('test.check-queue');
|
||||
});
|
||||
});
|
||||
|
||||
chtest("assert and check exchange", function(ch) {
|
||||
return ch.assertExchange('test.check-exchange', 'direct', EX_OPTS)
|
||||
.then(function(eok) {
|
||||
assert.equal('test.check-exchange', eok.exchange);
|
||||
return ch.checkExchange('test.check-exchange');
|
||||
});
|
||||
});
|
||||
|
||||
chtest("fail on reasserting queue with different options",
|
||||
function(ch) {
|
||||
var q = 'test.reassert-queue';
|
||||
return ch.assertQueue(
|
||||
q, {durable: false, autoDelete: true})
|
||||
.then(function() {
|
||||
return expectFail(
|
||||
ch.assertQueue(q, {durable: false,
|
||||
autoDelete: false}));
|
||||
});
|
||||
});
|
||||
|
||||
chtest("fail on checking a queue that's not there", function(ch) {
|
||||
return expectFail(ch.checkQueue('test.random-' + randomString()));
|
||||
});
|
||||
|
||||
chtest("fail on checking an exchange that's not there", function(ch) {
|
||||
return expectFail(ch.checkExchange('test.random-' + randomString()));
|
||||
});
|
||||
|
||||
chtest("fail on reasserting exchange with different type",
|
||||
function(ch) {
|
||||
var ex = 'test.reassert-ex';
|
||||
return ch.assertExchange(ex, 'fanout', EX_OPTS)
|
||||
.then(function() {
|
||||
return expectFail(
|
||||
ch.assertExchange(ex, 'direct', EX_OPTS));
|
||||
});
|
||||
});
|
||||
|
||||
chtest("channel break on publishing to non-exchange", function(ch) {
|
||||
return new Promise(function(resolve) {
|
||||
ch.on('error', resolve);
|
||||
ch.publish(randomString(), '', Buffer.from('foobar'));
|
||||
});
|
||||
});
|
||||
|
||||
chtest("delete queue", function(ch) {
|
||||
var q = 'test.delete-queue';
|
||||
return Promise.all([
|
||||
ch.assertQueue(q, QUEUE_OPTS),
|
||||
ch.checkQueue(q)])
|
||||
.then(function() {
|
||||
return ch.deleteQueue(q);})
|
||||
.then(function() {
|
||||
return expectFail(ch.checkQueue(q));});
|
||||
});
|
||||
|
||||
chtest("delete exchange", function(ch) {
|
||||
var ex = 'test.delete-exchange';
|
||||
return Promise.all([
|
||||
ch.assertExchange(ex, 'fanout', EX_OPTS),
|
||||
ch.checkExchange(ex)])
|
||||
.then(function() {
|
||||
return ch.deleteExchange(ex);})
|
||||
.then(function() {
|
||||
return expectFail(ch.checkExchange(ex));});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// Wait for the queue to meet the condition; useful for waiting for
|
||||
// messages to arrive, for example.
|
||||
function waitForQueue(q, condition) {
|
||||
return connect(URL).then(function(c) {
|
||||
return c.createChannel()
|
||||
.then(function(ch) {
|
||||
return ch.checkQueue(q).then(function(qok) {
|
||||
function check() {
|
||||
return ch.checkQueue(q).then(function(qok) {
|
||||
if (condition(qok)) {
|
||||
c.close();
|
||||
return qok;
|
||||
}
|
||||
else schedule(check);
|
||||
});
|
||||
}
|
||||
return check();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Return a promise that resolves when the queue has at least `num`
|
||||
// messages. If num is not supplied its assumed to be 1.
|
||||
function waitForMessages(q, num) {
|
||||
var min = (num === undefined) ? 1 : num;
|
||||
return waitForQueue(q, function(qok) {
|
||||
return qok.messageCount >= min;
|
||||
});
|
||||
}
|
||||
|
||||
suite("sendMessage", function() {
|
||||
|
||||
// publish different size messages
|
||||
chtest("send to queue and get from queue", function(ch) {
|
||||
var q = 'test.send-to-q';
|
||||
var msg = randomString();
|
||||
return Promise.all([ch.assertQueue(q, QUEUE_OPTS), ch.purgeQueue(q)])
|
||||
.then(function() {
|
||||
ch.sendToQueue(q, Buffer.from(msg));
|
||||
return waitForMessages(q);
|
||||
})
|
||||
.then(function() {
|
||||
return ch.get(q, {noAck: true});
|
||||
})
|
||||
.then(function(m) {
|
||||
assert(m);
|
||||
assert.equal(msg, m.content.toString());
|
||||
});
|
||||
});
|
||||
|
||||
chtest("send (and get) zero content to queue", function(ch) {
|
||||
var q = 'test.send-to-q';
|
||||
var msg = Buffer.alloc(0);
|
||||
return Promise.all([
|
||||
ch.assertQueue(q, QUEUE_OPTS),
|
||||
ch.purgeQueue(q)])
|
||||
.then(function() {
|
||||
ch.sendToQueue(q, msg);
|
||||
return waitForMessages(q);})
|
||||
.then(function() {
|
||||
return ch.get(q, {noAck: true});})
|
||||
.then(function(m) {
|
||||
assert(m);
|
||||
assert.deepEqual(msg, m.content);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite("binding, consuming", function() {
|
||||
|
||||
// bind, publish, get
|
||||
chtest("route message", function(ch) {
|
||||
var ex = 'test.route-message';
|
||||
var q = 'test.route-message-q';
|
||||
var msg = randomString();
|
||||
|
||||
return Promise.all([
|
||||
ch.assertExchange(ex, 'fanout', EX_OPTS),
|
||||
ch.assertQueue(q, QUEUE_OPTS),
|
||||
ch.purgeQueue(q),
|
||||
ch.bindQueue(q, ex, '', {})])
|
||||
.then(function() {
|
||||
ch.publish(ex, '', Buffer.from(msg));
|
||||
return waitForMessages(q);})
|
||||
.then(function() {
|
||||
return ch.get(q, {noAck: true});})
|
||||
.then(function(m) {
|
||||
assert(m);
|
||||
assert.equal(msg, m.content.toString());
|
||||
});
|
||||
});
|
||||
|
||||
// send to queue, purge, get-empty
|
||||
chtest("purge queue", function(ch) {
|
||||
var q = 'test.purge-queue';
|
||||
return ch.assertQueue(q, {durable: false})
|
||||
.then(function() {
|
||||
ch.sendToQueue(q, Buffer.from('foobar'));
|
||||
return waitForMessages(q);})
|
||||
.then(function() {
|
||||
ch.purgeQueue(q);
|
||||
return ch.get(q, {noAck: true});})
|
||||
.then(function(m) {
|
||||
assert(!m); // get-empty
|
||||
});
|
||||
});
|
||||
|
||||
// bind again, unbind, publish, get-empty
|
||||
chtest("unbind queue", function(ch) {
|
||||
var ex = 'test.unbind-queue-ex';
|
||||
var q = 'test.unbind-queue';
|
||||
var viabinding = randomString();
|
||||
var direct = randomString();
|
||||
|
||||
return Promise.all([
|
||||
ch.assertExchange(ex, 'fanout', EX_OPTS),
|
||||
ch.assertQueue(q, QUEUE_OPTS),
|
||||
ch.purgeQueue(q),
|
||||
ch.bindQueue(q, ex, '', {})])
|
||||
.then(function() {
|
||||
ch.publish(ex, '', Buffer.from('foobar'));
|
||||
return waitForMessages(q);})
|
||||
.then(function() { // message got through!
|
||||
return ch.get(q, {noAck:true})
|
||||
.then(function(m) {assert(m);});})
|
||||
.then(function() {
|
||||
return ch.unbindQueue(q, ex, '', {});})
|
||||
.then(function() {
|
||||
// via the no-longer-existing binding
|
||||
ch.publish(ex, '', Buffer.from(viabinding));
|
||||
// direct to the queue
|
||||
ch.sendToQueue(q, Buffer.from(direct));
|
||||
return waitForMessages(q);})
|
||||
.then(function() {return ch.get(q)})
|
||||
.then(function(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.
|
||||
chtest("consume via exchange-exchange binding", function(ch) {
|
||||
var ex1 = 'test.ex-ex-binding1', ex2 = 'test.ex-ex-binding2';
|
||||
var q = 'test.ex-ex-binding-q';
|
||||
var rk = 'test.routing.key', msg = randomString();
|
||||
return Promise.all([
|
||||
ch.assertExchange(ex1, 'direct', EX_OPTS),
|
||||
ch.assertExchange(ex2, 'fanout',
|
||||
{durable: false, internal: true}),
|
||||
ch.assertQueue(q, QUEUE_OPTS),
|
||||
ch.purgeQueue(q),
|
||||
ch.bindExchange(ex2, ex1, rk, {}),
|
||||
ch.bindQueue(q, ex2, '', {})])
|
||||
.then(function() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
function delivery(m) {
|
||||
if (m.content.toString() === msg) resolve();
|
||||
else reject(new Error("Wrong message"));
|
||||
}
|
||||
ch.consume(q, delivery, {noAck: true})
|
||||
.then(function() {
|
||||
ch.publish(ex1, rk, Buffer.from(msg));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// bind again, unbind, publish, get-empty
|
||||
chtest("unbind exchange", function(ch) {
|
||||
var source = 'test.unbind-ex-source';
|
||||
var dest = 'test.unbind-ex-dest';
|
||||
var q = 'test.unbind-ex-queue';
|
||||
var viabinding = randomString();
|
||||
var direct = randomString();
|
||||
|
||||
return Promise.all([
|
||||
ch.assertExchange(source, 'fanout', EX_OPTS),
|
||||
ch.assertExchange(dest, 'fanout', EX_OPTS),
|
||||
ch.assertQueue(q, QUEUE_OPTS),
|
||||
ch.purgeQueue(q),
|
||||
ch.bindExchange(dest, source, '', {}),
|
||||
ch.bindQueue(q, dest, '', {})])
|
||||
.then(function() {
|
||||
ch.publish(source, '', Buffer.from('foobar'));
|
||||
return waitForMessages(q);})
|
||||
.then(function() { // message got through!
|
||||
return ch.get(q, {noAck:true})
|
||||
.then(function(m) {assert(m);});})
|
||||
.then(function() {
|
||||
return ch.unbindExchange(dest, source, '', {});})
|
||||
.then(function() {
|
||||
// via the no-longer-existing binding
|
||||
ch.publish(source, '', Buffer.from(viabinding));
|
||||
// direct to the queue
|
||||
ch.sendToQueue(q, Buffer.from(direct));
|
||||
return waitForMessages(q);})
|
||||
.then(function() {return ch.get(q)})
|
||||
.then(function(m) {
|
||||
// the direct to queue message got through, the via-binding
|
||||
// message (sent first) did not
|
||||
assert.equal(direct, m.content.toString());
|
||||
});
|
||||
});
|
||||
|
||||
// This is a bit convoluted. Sorry.
|
||||
chtest("cancel consumer", function(ch) {
|
||||
var q = 'test.consumer-cancel';
|
||||
var ctag;
|
||||
var recv1 = new Promise(function (resolve, reject) {
|
||||
Promise.all([
|
||||
ch.assertQueue(q, QUEUE_OPTS),
|
||||
ch.purgeQueue(q),
|
||||
// My callback is 'resolve the promise in `arrived`'
|
||||
ch.consume(q, resolve, {noAck:true})
|
||||
.then(function(ok) {
|
||||
ctag = ok.consumerTag;
|
||||
ch.sendToQueue(q, Buffer.from('foo'));
|
||||
})]);
|
||||
});
|
||||
|
||||
// A message should arrive because of the consume
|
||||
return recv1.then(function() {
|
||||
var recv2 = Promise.all([
|
||||
ch.cancel(ctag).then(function() {
|
||||
return ch.sendToQueue(q, Buffer.from('bar'));
|
||||
}),
|
||||
// but check a message did arrive in the queue
|
||||
waitForMessages(q)])
|
||||
.then(function() {
|
||||
return ch.get(q, {noAck:true});
|
||||
})
|
||||
.then(function(m) {
|
||||
// I'm going to reject it, because I flip succeed/fail
|
||||
// just below
|
||||
if (m.content.toString() === 'bar') {
|
||||
throw new Error();
|
||||
}
|
||||
});
|
||||
|
||||
return expectFail(recv2);
|
||||
});
|
||||
});
|
||||
|
||||
chtest("cancelled consumer", function(ch) {
|
||||
var q = 'test.cancelled-consumer';
|
||||
return new Promise(function(resolve, reject) {
|
||||
return Promise.all([
|
||||
ch.assertQueue(q),
|
||||
ch.purgeQueue(q),
|
||||
ch.consume(q, function(msg) {
|
||||
if (msg === null) resolve();
|
||||
else reject(new Error('Message not expected'));
|
||||
})])
|
||||
.then(function() {
|
||||
return ch.deleteQueue(q);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ack, by default, removes a single message from the queue
|
||||
chtest("ack", function(ch) {
|
||||
var q = 'test.ack';
|
||||
var msg1 = randomString(), msg2 = randomString();
|
||||
|
||||
return Promise.all([
|
||||
ch.assertQueue(q, QUEUE_OPTS),
|
||||
ch.purgeQueue(q)])
|
||||
.then(function() {
|
||||
ch.sendToQueue(q, Buffer.from(msg1));
|
||||
ch.sendToQueue(q, Buffer.from(msg2));
|
||||
return waitForMessages(q, 2);
|
||||
})
|
||||
.then(function() {
|
||||
return ch.get(q, {noAck: false})
|
||||
})
|
||||
.then(function(m) {
|
||||
assert.equal(msg1, m.content.toString());
|
||||
ch.ack(m);
|
||||
// %%% is there a race here? may depend on
|
||||
// rabbitmq-sepcific semantics
|
||||
return ch.get(q);
|
||||
})
|
||||
.then(function(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)
|
||||
chtest("nack", function(ch) {
|
||||
var q = 'test.nack';
|
||||
var msg1 = randomString();
|
||||
|
||||
return Promise.all([
|
||||
ch.assertQueue(q, QUEUE_OPTS), ch.purgeQueue(q)])
|
||||
.then(function() {
|
||||
ch.sendToQueue(q, Buffer.from(msg1));
|
||||
return waitForMessages(q);})
|
||||
.then(function() {
|
||||
return ch.get(q, {noAck: false})})
|
||||
.then(function(m) {
|
||||
assert.equal(msg1, m.content.toString());
|
||||
ch.nack(m);
|
||||
return waitForMessages(q);})
|
||||
.then(function() {
|
||||
return ch.get(q);})
|
||||
.then(function(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).
|
||||
chtest("reject", function(ch) {
|
||||
var q = 'test.reject';
|
||||
var msg1 = randomString();
|
||||
|
||||
return Promise.all([
|
||||
ch.assertQueue(q, QUEUE_OPTS), ch.purgeQueue(q)])
|
||||
.then(function() {
|
||||
ch.sendToQueue(q, Buffer.from(msg1));
|
||||
return waitForMessages(q);})
|
||||
.then(function() {
|
||||
return ch.get(q, {noAck: false})})
|
||||
.then(function(m) {
|
||||
assert.equal(msg1, m.content.toString());
|
||||
ch.reject(m);
|
||||
return waitForMessages(q);})
|
||||
.then(function() {
|
||||
return ch.get(q);})
|
||||
.then(function(m) {
|
||||
assert(m);
|
||||
assert.equal(msg1, m.content.toString());
|
||||
});
|
||||
});
|
||||
|
||||
chtest("prefetch", function(ch) {
|
||||
var q = 'test.prefetch';
|
||||
return Promise.all([
|
||||
ch.assertQueue(q, QUEUE_OPTS), ch.purgeQueue(q),
|
||||
ch.prefetch(1)])
|
||||
.then(function() {
|
||||
ch.sendToQueue(q, Buffer.from('foobar'));
|
||||
ch.sendToQueue(q, Buffer.from('foobar'));
|
||||
return waitForMessages(q, 2);
|
||||
})
|
||||
.then(function() {
|
||||
return new Promise(function(resolve) {
|
||||
var messageCount = 0;
|
||||
function receive(msg) {
|
||||
ch.ack(msg);
|
||||
if (++messageCount > 1) {
|
||||
resolve(messageCount);
|
||||
}
|
||||
}
|
||||
return ch.consume(q, receive, {noAck: false})
|
||||
});
|
||||
})
|
||||
.then(function(c) {
|
||||
return assert.equal(2, c);
|
||||
});
|
||||
});
|
||||
|
||||
chtest('close', function(ch) {
|
||||
// Resolving promise guarantees
|
||||
// channel is closed
|
||||
return ch.close();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
var confirmtest = channel_test.bind(null, 'createConfirmChannel');
|
||||
|
||||
suite("confirms", function() {
|
||||
|
||||
confirmtest('message is confirmed', function(ch) {
|
||||
var q = 'test.confirm-message';
|
||||
return Promise.all([
|
||||
ch.assertQueue(q, QUEUE_OPTS), ch.purgeQueue(q)])
|
||||
.then(function() {
|
||||
return ch.sendToQueue(q, 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.
|
||||
confirmtest('multiple confirms', function(ch) {
|
||||
var q = 'test.multiple-confirms';
|
||||
return Promise.all([
|
||||
ch.assertQueue(q, QUEUE_OPTS), ch.purgeQueue(q)])
|
||||
.then(function() {
|
||||
var multipleRainbows = false;
|
||||
ch.on('ack', function(a) {
|
||||
if (a.multiple) multipleRainbows = true;
|
||||
});
|
||||
|
||||
function prod(num) {
|
||||
var cs = [];
|
||||
|
||||
function sendAndPushPromise() {
|
||||
var conf = promisify(function(cb) {
|
||||
return ch.sendToQueue(q, Buffer.from('bleep'), {}, cb);
|
||||
})();
|
||||
cs.push(conf);
|
||||
}
|
||||
|
||||
for (var i=0; i < num; i++) sendAndPushPromise();
|
||||
|
||||
return Promise.all(cs).then(function() {
|
||||
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 {
|
||||
//console.warn("Failed with " + num + "; trying " + num * 2);
|
||||
return prod(num * 2);
|
||||
}
|
||||
});
|
||||
}
|
||||
return prod(5);
|
||||
});
|
||||
});
|
||||
|
||||
confirmtest('wait for confirms', function(ch) {
|
||||
for (var i=0; i < 1000; i++) {
|
||||
ch.publish('', '', Buffer.from('foobar'), {});
|
||||
}
|
||||
return ch.waitForConfirms();
|
||||
})
|
||||
|
||||
confirmtest('works when channel is closed', function(ch) {
|
||||
for (var i=0; i < 1000; i++) {
|
||||
ch.publish('', '', Buffer.from('foobar'), {});
|
||||
}
|
||||
return ch.close().then(function () {
|
||||
return ch.waitForConfirms()
|
||||
}).then(function () {
|
||||
assert.strictEqual(true, false, 'Wait should have failed.')
|
||||
}, function (e) {
|
||||
assert.strictEqual(e.message, 'channel closed')
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
237
node_modules/amqplib/test/codec.js
generated
vendored
Normal file
237
node_modules/amqplib/test/codec.js
generated
vendored
Normal file
@@ -0,0 +1,237 @@
|
||||
'use strict';
|
||||
|
||||
var codec = require('../lib/codec');
|
||||
var defs = require('../lib/defs');
|
||||
var assert = require('assert');
|
||||
var ints = require('buffer-more-ints');
|
||||
var C = require('claire');
|
||||
var forAll = C.forAll;
|
||||
|
||||
// These just test known encodings; to generate the answers I used
|
||||
// RabbitMQ's binary generator module.
|
||||
|
||||
var 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);
|
||||
}
|
||||
|
||||
suite("Implicit encodings", function() {
|
||||
|
||||
testCases.forEach(function(tc) {
|
||||
var name = tc[0], val = tc[1], expect = tc[2];
|
||||
test(name, function() {
|
||||
var buffer = Buffer.alloc(1000);
|
||||
var size = codec.encodeTable(buffer, val, 0);
|
||||
var result = buffer.subarray(4, size);
|
||||
assert.deepEqual(expect, bufferToArray(result));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Whole frames
|
||||
|
||||
var amqp = require('./data');
|
||||
|
||||
function roundtrip_table(t) {
|
||||
var buf = Buffer.alloc(4096);
|
||||
var size = codec.encodeTable(buf, t, 0);
|
||||
var decoded = codec.decodeFields(buf.subarray(4, size)); // ignore the length-prefix
|
||||
try {
|
||||
assert.deepEqual(removeExplicitTypes(t), decoded);
|
||||
}
|
||||
catch (e) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
function roundtrips(T) {
|
||||
return forAll(T).satisfy(function(v) { return roundtrip_table({value: v}); });
|
||||
}
|
||||
|
||||
suite("Roundtrip values", function() {
|
||||
[
|
||||
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(function(T) {
|
||||
test(T.toString() + ' roundtrip', roundtrips(T).asTest());
|
||||
});
|
||||
});
|
||||
|
||||
// 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)) {
|
||||
var newArr = [];
|
||||
for (var 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:
|
||||
var newObj = {}
|
||||
for (var 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) {
|
||||
var args = defs.info(original.id).args;
|
||||
for (var i=0; i < args.length; i++) {
|
||||
var arg = args[i];
|
||||
var originalValue = original.fields[arg.name];
|
||||
var 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) {
|
||||
var 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;
|
||||
}
|
||||
|
||||
// This is handy for elsewhere
|
||||
module.exports.assertEqualModuloDefaults = assertEqualModuloDefaults;
|
||||
|
||||
function roundtripMethod(Method) {
|
||||
return forAll(Method).satisfy(function(method) {
|
||||
var buf = defs.encodeMethod(method.id, 0, method.fields);
|
||||
// FIXME depends on framing, ugh
|
||||
var fs1 = defs.decode(method.id, buf.subarray(11, buf.length));
|
||||
assertEqualModuloDefaults(method, fs1);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function roundtripProperties(Properties) {
|
||||
return forAll(Properties).satisfy(function(properties) {
|
||||
var buf = defs.encodeProperties(properties.id, 0, properties.size,
|
||||
properties.fields);
|
||||
// FIXME depends on framing, ugh
|
||||
var fs1 = defs.decode(properties.id, buf.subarray(19, buf.length));
|
||||
assert.equal(properties.size, ints.readUInt64BE(buf, 11));
|
||||
assertEqualModuloDefaults(properties, fs1);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
suite("Roundtrip methods", function() {
|
||||
amqp.methods.forEach(function(Method) {
|
||||
test(Method.toString() + ' roundtrip',
|
||||
roundtripMethod(Method).asTest());
|
||||
});
|
||||
});
|
||||
|
||||
suite("Roundtrip properties", function() {
|
||||
amqp.properties.forEach(function(Properties) {
|
||||
test(Properties.toString() + ' roundtrip',
|
||||
roundtripProperties(Properties).asTest());
|
||||
});
|
||||
});
|
||||
197
node_modules/amqplib/test/connect.js
generated
vendored
Normal file
197
node_modules/amqplib/test/connect.js
generated
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
'use strict';
|
||||
|
||||
var connect = require('../lib/connect').connect;
|
||||
var credentialsFromUrl = require('../lib/connect').credentialsFromUrl;
|
||||
var defs = require('../lib/defs');
|
||||
var assert = require('assert');
|
||||
var util = require('./util');
|
||||
var net = require('net');
|
||||
var fail = util.fail, succeed = util.succeed, latch = util.latch,
|
||||
kCallback = util.kCallback,
|
||||
succeedIfAttributeEquals = util.succeedIfAttributeEquals;
|
||||
var format = require('util').format;
|
||||
|
||||
var URL = process.env.URL || 'amqp://localhost';
|
||||
|
||||
var urlparse = require('url-parse');
|
||||
|
||||
suite("Credentials", function() {
|
||||
|
||||
function checkCreds(creds, user, pass, done) {
|
||||
if (creds.mechanism != 'PLAIN') {
|
||||
return done('expected mechanism PLAIN');
|
||||
}
|
||||
if (creds.username != user || creds.password != pass) {
|
||||
return done(format("expected '%s', '%s'; got '%s', '%s'",
|
||||
user, pass, creds.username, creds.password));
|
||||
}
|
||||
done();
|
||||
}
|
||||
|
||||
test("no creds", function(done) {
|
||||
var parts = urlparse('amqp://localhost');
|
||||
var creds = credentialsFromUrl(parts);
|
||||
checkCreds(creds, 'guest', 'guest', done);
|
||||
});
|
||||
test("usual user:pass", function(done) {
|
||||
var parts = urlparse('amqp://user:pass@localhost')
|
||||
var creds = credentialsFromUrl(parts);
|
||||
checkCreds(creds, 'user', 'pass', done);
|
||||
});
|
||||
test("missing user", function(done) {
|
||||
var parts = urlparse('amqps://:password@localhost');
|
||||
var creds = credentialsFromUrl(parts);
|
||||
checkCreds(creds, '', 'password', done);
|
||||
});
|
||||
test("missing password", function(done) {
|
||||
var parts = urlparse('amqps://username:@localhost');
|
||||
var creds = credentialsFromUrl(parts);
|
||||
checkCreds(creds, 'username', '', done);
|
||||
});
|
||||
test("escaped colons", function(done) {
|
||||
var parts = urlparse('amqp://user%3Aname:pass%3Aword@localhost')
|
||||
var creds = credentialsFromUrl(parts);
|
||||
checkCreds(creds, 'user:name', 'pass:word', done);
|
||||
});
|
||||
});
|
||||
|
||||
suite("Connect API", function() {
|
||||
|
||||
test("Connection refused", function(done) {
|
||||
connect('amqp://localhost:23450', {},
|
||||
kCallback(fail(done), succeed(done)));
|
||||
});
|
||||
|
||||
// %% this ought to fail the promise, rather than throwing an error
|
||||
test("bad URL", function() {
|
||||
assert.throws(function() {
|
||||
connect('blurble');
|
||||
});
|
||||
});
|
||||
|
||||
test("wrongly typed open option", function(done) {
|
||||
var url = require('url');
|
||||
var parts = url.parse(URL, true);
|
||||
var q = parts.query || {};
|
||||
q.frameMax = 'NOT A NUMBER';
|
||||
parts.query = q;
|
||||
var u = url.format(parts);
|
||||
connect(u, {}, kCallback(fail(done), succeed(done)));
|
||||
});
|
||||
|
||||
test("serverProperties", function(done) {
|
||||
var url = require('url');
|
||||
var parts = url.parse(URL, true);
|
||||
var config = parts.query || {};
|
||||
connect(config, {}, function(err, connection) {
|
||||
if (err) { return done(err); }
|
||||
assert.equal(connection.serverProperties.product, 'RabbitMQ');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test("using custom heartbeat option", function(done) {
|
||||
var url = require('url');
|
||||
var parts = url.parse(URL, true);
|
||||
var config = parts.query || {};
|
||||
config.heartbeat = 20;
|
||||
connect(config, {}, kCallback(succeedIfAttributeEquals('heartbeat', 20, done), fail(done)));
|
||||
});
|
||||
|
||||
test("wrongly typed heartbeat option", function(done) {
|
||||
var url = require('url');
|
||||
var parts = url.parse(URL, true);
|
||||
var config = parts.query || {};
|
||||
config.heartbeat = 'NOT A NUMBER';
|
||||
connect(config, {}, kCallback(fail(done), succeed(done)));
|
||||
});
|
||||
|
||||
test("using plain credentials", function(done) {
|
||||
var url = require('url');
|
||||
var parts = url.parse(URL, true);
|
||||
var u = 'guest', p = 'guest';
|
||||
if (parts.auth) {
|
||||
var auth = parts.auth.split(":");
|
||||
u = auth[0], p = auth[1];
|
||||
}
|
||||
connect(URL, {credentials: require('../lib/credentials').plain(u, p)},
|
||||
kCallback(succeed(done), fail(done)));
|
||||
});
|
||||
|
||||
test("using amqplain credentials", function(done) {
|
||||
var url = require('url');
|
||||
var parts = url.parse(URL, true);
|
||||
var u = 'guest', p = 'guest';
|
||||
if (parts.auth) {
|
||||
var auth = parts.auth.split(":");
|
||||
u = auth[0], p = auth[1];
|
||||
}
|
||||
connect(URL, {credentials: require('../lib/credentials').amqplain(u, p)},
|
||||
kCallback(succeed(done), fail(done)));
|
||||
});
|
||||
|
||||
test("using unsupported mechanism", function(done) {
|
||||
var creds = {
|
||||
mechanism: 'UNSUPPORTED',
|
||||
response: function() { return Buffer.from(''); }
|
||||
};
|
||||
connect(URL, {credentials: creds},
|
||||
kCallback(fail(done), succeed(done)));
|
||||
});
|
||||
|
||||
test("with a given connection timeout", function(done) {
|
||||
var timeoutServer = net.createServer(function() {}).listen(31991);
|
||||
|
||||
connect('amqp://localhost:31991', {timeout: 50}, function(err, val) {
|
||||
timeoutServer.close();
|
||||
if (val) done(new Error('Expected connection timeout, did not'));
|
||||
else done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('Errors on connect', function() {
|
||||
var server
|
||||
teardown(function() {
|
||||
if (server) {
|
||||
server.close();
|
||||
}
|
||||
})
|
||||
|
||||
test("closes underlying connection on authentication error", function(done) {
|
||||
var bothDone = latch(2, done);
|
||||
server = net.createServer(function(socket) {
|
||||
socket.once('data', function(protocolHeader) {
|
||||
assert.deepStrictEqual(
|
||||
protocolHeader,
|
||||
Buffer.from("AMQP" + String.fromCharCode(0,0,9,1))
|
||||
);
|
||||
util.runServer(socket, function(send, wait) {
|
||||
send(defs.ConnectionStart,
|
||||
{versionMajor: 0,
|
||||
versionMinor: 9,
|
||||
serverProperties: {},
|
||||
mechanisms: Buffer.from('PLAIN'),
|
||||
locales: Buffer.from('en_US')});
|
||||
wait(defs.ConnectionStartOk)().then(function() {
|
||||
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', function() {
|
||||
bothDone();
|
||||
});
|
||||
}).listen(0);
|
||||
|
||||
connect('amqp://localhost:' + server.address().port, {}, function(err) {
|
||||
if (!err) bothDone(new Error('Expected authentication error'));
|
||||
bothDone();
|
||||
});
|
||||
});
|
||||
});
|
||||
390
node_modules/amqplib/test/connection.js
generated
vendored
Normal file
390
node_modules/amqplib/test/connection.js
generated
vendored
Normal file
@@ -0,0 +1,390 @@
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var defs = require('../lib/defs');
|
||||
var Connection = require('../lib/connection').Connection;
|
||||
var HEARTBEAT = require('../lib/frame').HEARTBEAT;
|
||||
var HB_BUF = require('../lib/frame').HEARTBEAT_BUF;
|
||||
var util = require('./util');
|
||||
var succeed = util.succeed, fail = util.fail, latch = util.latch;
|
||||
var completes = util.completes;
|
||||
var kCallback = util.kCallback;
|
||||
|
||||
var LOG_ERRORS = process.env.LOG_ERRORS;
|
||||
|
||||
var 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': 0,
|
||||
|
||||
// open
|
||||
'virtualHost': '/',
|
||||
'capabilities': '',
|
||||
'insist': 0
|
||||
};
|
||||
module.exports.OPEN_OPTS = OPEN_OPTS;
|
||||
|
||||
function happy_open(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(function(f) {
|
||||
send(defs.ConnectionTune,
|
||||
{channelMax: 0,
|
||||
heartbeat: 0,
|
||||
frameMax: 0});
|
||||
})
|
||||
.then(wait(defs.ConnectionTuneOk))
|
||||
.then(wait(defs.ConnectionOpen))
|
||||
.then(function(f) {
|
||||
send(defs.ConnectionOpenOk,
|
||||
{knownHosts: ''});
|
||||
});
|
||||
}
|
||||
module.exports.connection_handshake = happy_open;
|
||||
|
||||
function connectionTest(client, server) {
|
||||
return function(done) {
|
||||
var bothDone = latch(2, done);
|
||||
var pair = util.socketPair();
|
||||
var c = new Connection(pair.client);
|
||||
if (LOG_ERRORS) c.on('error', console.warn);
|
||||
client(c, bothDone);
|
||||
|
||||
// NB only not a race here because the writes are synchronous
|
||||
var protocolHeader = pair.server.read(8);
|
||||
assert.deepEqual(Buffer.from("AMQP" + String.fromCharCode(0,0,9,1)),
|
||||
protocolHeader);
|
||||
|
||||
var s = util.runServer(pair.server, function(send, wait) {
|
||||
server(send, wait, bothDone, pair.server);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
suite("Connection errors", function() {
|
||||
|
||||
test("socket close during open", function(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.
|
||||
var pair = util.socketPair();
|
||||
var conn = new Connection(pair.client);
|
||||
pair.server.on('readable', function() {
|
||||
pair.server.end();
|
||||
});
|
||||
conn.open({}, kCallback(fail(done), succeed(done)));
|
||||
});
|
||||
|
||||
test("bad frame during open", function(done) {
|
||||
var ss = util.socketPair();
|
||||
var conn = new (require('../lib/connection').Connection)(ss.client);
|
||||
ss.server.on('readable', function() {
|
||||
ss.server.write(Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]));
|
||||
});
|
||||
conn.open({}, kCallback(fail(done), succeed(done)));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite("Connection open", function() {
|
||||
|
||||
test("happy", connectionTest(
|
||||
function(c, done) {
|
||||
c.open(OPEN_OPTS, kCallback(succeed(done), fail(done)));
|
||||
},
|
||||
function(send, wait, done) {
|
||||
happy_open(send, wait).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("wrong first frame", connectionTest(
|
||||
function(c, done) {
|
||||
c.open(OPEN_OPTS, kCallback(fail(done), succeed(done)));
|
||||
},
|
||||
function(send, wait, done) {
|
||||
// bad server! bad! whatever were you thinking?
|
||||
completes(function() {
|
||||
send(defs.ConnectionTune,
|
||||
{channelMax: 0,
|
||||
heartbeat: 0,
|
||||
frameMax: 0});
|
||||
}, done);
|
||||
}));
|
||||
|
||||
test("unexpected socket close", connectionTest(
|
||||
function(c, done) {
|
||||
c.open(OPEN_OPTS, kCallback(fail(done), succeed(done)));
|
||||
},
|
||||
function(send, wait, done, socket) {
|
||||
send(defs.ConnectionStart,
|
||||
{versionMajor: 0,
|
||||
versionMinor: 9,
|
||||
serverProperties: {},
|
||||
mechanisms: Buffer.from('PLAIN'),
|
||||
locales: Buffer.from('en_US')});
|
||||
return wait(defs.ConnectionStartOk)()
|
||||
.then(function() {
|
||||
socket.end();
|
||||
})
|
||||
.then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
suite("Connection running", function() {
|
||||
|
||||
test("wrong frame on channel 0", connectionTest(
|
||||
function(c, done) {
|
||||
c.on('error', succeed(done));
|
||||
c.open(OPEN_OPTS);
|
||||
},
|
||||
function(send, wait, done) {
|
||||
happy_open(send, wait)
|
||||
.then(function() {
|
||||
// there's actually nothing that would plausibly be sent to a
|
||||
// just opened connection, so this is violating more than one
|
||||
// rule. Nonetheless.
|
||||
send(defs.ChannelOpenOk, {channelId: Buffer.from('')}, 0);
|
||||
})
|
||||
.then(wait(defs.ConnectionClose))
|
||||
.then(function(close) {
|
||||
send(defs.ConnectionCloseOk, {}, 0);
|
||||
}).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("unopened channel", connectionTest(
|
||||
function(c, done) {
|
||||
c.on('error', succeed(done));
|
||||
c.open(OPEN_OPTS);
|
||||
},
|
||||
function(send, wait, done) {
|
||||
happy_open(send, wait)
|
||||
.then(function() {
|
||||
// there's actually nothing that would plausibly be sent to a
|
||||
// just opened connection, so this is violating more than one
|
||||
// rule. Nonetheless.
|
||||
send(defs.ChannelOpenOk, {channelId: Buffer.from('')}, 3);
|
||||
})
|
||||
.then(wait(defs.ConnectionClose))
|
||||
.then(function(close) {
|
||||
send(defs.ConnectionCloseOk, {}, 0);
|
||||
}).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("unexpected socket close", connectionTest(
|
||||
function(c, done) {
|
||||
var errorAndClosed = latch(2, done);
|
||||
c.on('error', succeed(errorAndClosed));
|
||||
c.on('close', succeed(errorAndClosed));
|
||||
c.open(OPEN_OPTS, kCallback(function() {
|
||||
c.sendHeartbeat();
|
||||
}, fail(errorAndClosed)));
|
||||
},
|
||||
function(send, wait, done, socket) {
|
||||
happy_open(send, wait)
|
||||
.then(wait())
|
||||
.then(function() {
|
||||
socket.end();
|
||||
}).then(succeed(done));
|
||||
}));
|
||||
|
||||
test("connection.blocked", connectionTest(
|
||||
function(c, done) {
|
||||
c.on('blocked', succeed(done));
|
||||
c.open(OPEN_OPTS);
|
||||
},
|
||||
function(send, wait, done, socket) {
|
||||
happy_open(send, wait)
|
||||
.then(function() {
|
||||
send(defs.ConnectionBlocked, {reason: 'felt like it'}, 0);
|
||||
})
|
||||
.then(succeed(done));
|
||||
}));
|
||||
|
||||
test("connection.unblocked", connectionTest(
|
||||
function(c, done) {
|
||||
c.on('unblocked', succeed(done));
|
||||
c.open(OPEN_OPTS);
|
||||
},
|
||||
function(send, wait, done, socket) {
|
||||
happy_open(send, wait)
|
||||
.then(function() {
|
||||
send(defs.ConnectionUnblocked, {}, 0);
|
||||
})
|
||||
.then(succeed(done));
|
||||
}));
|
||||
|
||||
|
||||
});
|
||||
|
||||
suite("Connection close", function() {
|
||||
|
||||
test("happy", connectionTest(
|
||||
function(c, done0) {
|
||||
var done = latch(2, done0);
|
||||
c.on('close', done);
|
||||
c.open(OPEN_OPTS, kCallback(function(_ok) {
|
||||
c.close(kCallback(succeed(done), fail(done)));
|
||||
}, function() {}));
|
||||
},
|
||||
function(send, wait, done) {
|
||||
happy_open(send, wait)
|
||||
.then(wait(defs.ConnectionClose))
|
||||
.then(function(close) {
|
||||
send(defs.ConnectionCloseOk, {});
|
||||
})
|
||||
.then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("interleaved close frames", connectionTest(
|
||||
function(c, done0) {
|
||||
var done = latch(2, done0);
|
||||
c.on('close', done);
|
||||
c.open(OPEN_OPTS, kCallback(function(_ok) {
|
||||
c.close(kCallback(succeed(done), fail(done)));
|
||||
}, done));
|
||||
},
|
||||
function(send, wait, done) {
|
||||
happy_open(send, wait)
|
||||
.then(wait(defs.ConnectionClose))
|
||||
.then(function(f) {
|
||||
send(defs.ConnectionClose, {
|
||||
replyText: "Ha!",
|
||||
replyCode: defs.constants.REPLY_SUCCESS,
|
||||
methodId: 0, classId: 0
|
||||
});
|
||||
})
|
||||
.then(wait(defs.ConnectionCloseOk))
|
||||
.then(function(f) {
|
||||
send(defs.ConnectionCloseOk, {});
|
||||
})
|
||||
.then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("server error close", connectionTest(
|
||||
function(c, done0) {
|
||||
var done = latch(2, done0);
|
||||
c.on('close', succeed(done));
|
||||
c.on('error', succeed(done));
|
||||
c.open(OPEN_OPTS);
|
||||
},
|
||||
function(send, wait, done) {
|
||||
happy_open(send, wait)
|
||||
.then(function(f) {
|
||||
send(defs.ConnectionClose, {
|
||||
replyText: "Begone",
|
||||
replyCode: defs.constants.INTERNAL_ERROR,
|
||||
methodId: 0, classId: 0
|
||||
});
|
||||
})
|
||||
.then(wait(defs.ConnectionCloseOk))
|
||||
.then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("operator-intiated close", connectionTest(
|
||||
function(c, done) {
|
||||
c.on('close', succeed(done));
|
||||
c.on('error', fail(done));
|
||||
c.open(OPEN_OPTS);
|
||||
},
|
||||
function(send, wait, done) {
|
||||
happy_open(send, wait)
|
||||
.then(function(f) {
|
||||
send(defs.ConnectionClose, {
|
||||
replyText: "Begone",
|
||||
replyCode: defs.constants.CONNECTION_FORCED,
|
||||
methodId: 0, classId: 0
|
||||
});
|
||||
})
|
||||
.then(wait(defs.ConnectionCloseOk))
|
||||
.then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
|
||||
test("double close", connectionTest(
|
||||
function(c, done) {
|
||||
c.open(OPEN_OPTS, kCallback(function() {
|
||||
c.close();
|
||||
// NB no synchronisation, we do this straight away
|
||||
assert.throws(function() {
|
||||
c.close();
|
||||
});
|
||||
done();
|
||||
}, done));
|
||||
},
|
||||
function(send, wait, done) {
|
||||
happy_open(send, wait)
|
||||
.then(wait(defs.ConnectionClose))
|
||||
.then(function() {
|
||||
send(defs.ConnectionCloseOk, {});
|
||||
})
|
||||
.then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
suite("heartbeats", function() {
|
||||
|
||||
var heartbeat = require('../lib/heartbeat');
|
||||
|
||||
setup(function() {
|
||||
heartbeat.UNITS_TO_MS = 20;
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
heartbeat.UNITS_TO_MS = 1000;
|
||||
});
|
||||
|
||||
test("send heartbeat after open", connectionTest(
|
||||
function(c, done) {
|
||||
completes(function() {
|
||||
var opts = Object.create(OPEN_OPTS);
|
||||
opts.heartbeat = 1;
|
||||
// Don't leave the error waiting to happen for the next test, this
|
||||
// confuses mocha awfully
|
||||
c.on('error', function() {});
|
||||
c.open(opts);
|
||||
}, done);
|
||||
},
|
||||
function(send, wait, done, socket) {
|
||||
var timer;
|
||||
happy_open(send, wait)
|
||||
.then(function() {
|
||||
timer = setInterval(function() {
|
||||
socket.write(HB_BUF);
|
||||
}, heartbeat.UNITS_TO_MS);
|
||||
})
|
||||
.then(wait())
|
||||
.then(function(hb) {
|
||||
if (hb === HEARTBEAT) done();
|
||||
else done("Next frame after silence not a heartbeat");
|
||||
clearInterval(timer);
|
||||
});
|
||||
}));
|
||||
|
||||
test("detect lack of heartbeats", connectionTest(
|
||||
function(c, done) {
|
||||
var opts = Object.create(OPEN_OPTS);
|
||||
opts.heartbeat = 1;
|
||||
c.on('error', succeed(done));
|
||||
c.open(opts);
|
||||
},
|
||||
function(send, wait, done, socket) {
|
||||
happy_open(send, wait)
|
||||
.then(succeed(done), fail(done));
|
||||
// conspicuously not sending anything ...
|
||||
}));
|
||||
|
||||
});
|
||||
269
node_modules/amqplib/test/data.js
generated
vendored
Normal file
269
node_modules/amqplib/test/data.js
generated
vendored
Normal file
@@ -0,0 +1,269 @@
|
||||
// Property-based testing representations of various things in AMQP
|
||||
|
||||
'use strict';
|
||||
|
||||
var C = require('claire');
|
||||
var forAll = C.forAll;
|
||||
var arb = C.data;
|
||||
var transform = C.transform;
|
||||
var repeat = C.repeat;
|
||||
var label = C.label;
|
||||
var sequence = C.sequence;
|
||||
var asGenerator = C.asGenerator;
|
||||
var sized = C.sized;
|
||||
var recursive = C.recursive;
|
||||
var choice = C.choice;
|
||||
var Undefined = C.Undefined;
|
||||
|
||||
// Stub these out so we can use outside tests
|
||||
// if (!suite) var suite = function() {}
|
||||
// if (!test) var test = function() {}
|
||||
|
||||
// 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(function(_) { return chooseInt(a, b); }));
|
||||
}
|
||||
|
||||
function toFloat32(i) {
|
||||
var b = Buffer.alloc(4);
|
||||
b.writeFloatBE(i, 0);
|
||||
return b.readFloatBE(0);
|
||||
}
|
||||
|
||||
function floatChooser(maxExp) {
|
||||
return function() {
|
||||
var n = Number.NaN;
|
||||
while (isNaN(n)) {
|
||||
var mantissa = Math.random() * 2 - 1;
|
||||
var exponent = chooseInt(0, maxExp);
|
||||
n = Math.pow(mantissa, exponent);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
function explicitType(t, underlying) {
|
||||
return label(t, transform(function(n) {
|
||||
return {'!': t, value: n};
|
||||
}, underlying));
|
||||
}
|
||||
|
||||
// FIXME null, byte array, others?
|
||||
|
||||
var Octet = rangeInt('octet', 0, 255);
|
||||
var ShortStr = label('shortstr',
|
||||
transform(function(s) {
|
||||
return s.substr(0, 255);
|
||||
}, arb.Str));
|
||||
|
||||
var LongStr = label('longstr',
|
||||
transform(
|
||||
function(bytes) { return Buffer.from(bytes); },
|
||||
repeat(Octet)));
|
||||
|
||||
var UShort = rangeInt('short-uint', 0, 0xffff);
|
||||
var ULong = rangeInt('long-uint', 0, 0xffffffff);
|
||||
var ULongLong = rangeInt('longlong-uint', 0, 0xffffffffffffffff);
|
||||
var Short = rangeInt('short-int', -0x8000, 0x7fff);
|
||||
var Long = rangeInt('long-int', -0x80000000, 0x7fffffff);
|
||||
var LongLong = rangeInt('longlong-int', -0x8000000000000000,
|
||||
0x7fffffffffffffff);
|
||||
var Bit = label('bit', arb.Bool);
|
||||
var Double = label('double', asGenerator(floatChooser(308)));
|
||||
var Float = label('float', transform(toFloat32, floatChooser(38)));
|
||||
var Timestamp = label('timestamp', transform(
|
||||
function(n) {
|
||||
return {'!': 'timestamp', value: n};
|
||||
}, ULongLong));
|
||||
var Decimal = label('decimal', transform(
|
||||
function(args) {
|
||||
return {'!': 'decimal', value: {places: args[1], digits: args[0]}};
|
||||
}, sequence(arb.UInt, Octet)));
|
||||
var UnsignedByte = label('unsignedbyte', transform(
|
||||
function(n) {
|
||||
return {'!': 'unsignedbyte', value: n};
|
||||
}, Octet));
|
||||
var UnsignedShort = label('unsignedshort', transform(
|
||||
function(n) {
|
||||
return {'!': 'unsignedshort', value: n};
|
||||
}, UShort));
|
||||
var UnsignedInt = label('unsignedint', transform(
|
||||
function(n) {
|
||||
return {'!': 'unsignedint', value: n};
|
||||
}, ULong));
|
||||
|
||||
// Signed 8 bit int
|
||||
var Byte = rangeInt('byte', -128, 127);
|
||||
|
||||
// Explicitly typed values
|
||||
var ExByte = explicitType('byte', Byte);
|
||||
var ExInt8 = explicitType('int8', Byte);
|
||||
var ExShort = explicitType('short', Short);
|
||||
var ExInt16 = explicitType('int16', Short);
|
||||
var ExInt = explicitType('int', Long);
|
||||
var ExInt32 = explicitType('int32', Long);
|
||||
var ExLong = explicitType('long', LongLong);
|
||||
var ExInt64 = explicitType('int64', LongLong);
|
||||
|
||||
var FieldArray = label('field-array', recursive(function() {
|
||||
return 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)
|
||||
}));
|
||||
|
||||
var FieldTable = label('table', recursive(function() {
|
||||
return sized(function() { return 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))
|
||||
}));
|
||||
|
||||
// Internal tests of our properties
|
||||
var domainProps = [
|
||||
[Octet, function(n) { return n >= 0 && n < 256; }],
|
||||
[ShortStr, function(s) { return typeof s === 'string' && s.length < 256; }],
|
||||
[LongStr, function(s) { return Buffer.isBuffer(s); }],
|
||||
[UShort, function(n) { return n >= 0 && n <= 0xffff; }],
|
||||
[ULong, function(n) { return n >= 0 && n <= 0xffffffff; }],
|
||||
[ULongLong, function(n) {
|
||||
return n >= 0 && n <= 0xffffffffffffffff; }],
|
||||
[Short, function(n) { return n >= -0x8000 && n <= 0x8000; }],
|
||||
[Long, function(n) { return n >= -0x80000000 && n < 0x80000000; }],
|
||||
[LongLong, function(n) { return n >= -0x8000000000000000 && n < 0x8000000000000000; }],
|
||||
[Bit, function(b) { return typeof b === 'boolean'; }],
|
||||
[Double, function(f) { return !isNaN(f) && isFinite(f); }],
|
||||
[Float, function(f) { return !isNaN(f) && isFinite(f) && (Math.log(Math.abs(f)) * Math.LOG10E) < 309; }],
|
||||
[Decimal, function(d) {
|
||||
return d['!'] === 'decimal' &&
|
||||
d.value['places'] <= 255 &&
|
||||
d.value['digits'] <= 0xffffffff;
|
||||
}],
|
||||
[Timestamp, function(t) { return t['!'] === 'timestamp'; }],
|
||||
[FieldTable, function(t) { return typeof t === 'object'; }],
|
||||
[FieldArray, function(a) { return Array.isArray(a); }]
|
||||
];
|
||||
|
||||
suite("Domains", function() {
|
||||
domainProps.forEach(function(p) {
|
||||
test(p[0] + ' domain',
|
||||
forAll(p[0]).satisfy(p[1]).asTest({times: 500}));
|
||||
});
|
||||
});
|
||||
|
||||
// For methods and properties (as opposed to field table values) it's
|
||||
// easier just to accept and produce numbers for timestamps.
|
||||
var ArgTimestamp = label('timestamp', ULongLong);
|
||||
|
||||
// These are the domains used in method arguments
|
||||
var 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) {
|
||||
var obj = {};
|
||||
vals.forEach(function(v, i) { obj[names[i]] = v; });
|
||||
return obj;
|
||||
}
|
||||
|
||||
function name(arg) { return arg.name; }
|
||||
|
||||
var defs = require('../lib/defs');
|
||||
|
||||
function method(info) {
|
||||
var domain = sequence.apply(null, info.args.map(argtype));
|
||||
var names = info.args.map(name);
|
||||
return label(info.name, transform(function(fieldVals) {
|
||||
return {id: info.id,
|
||||
fields: zipObject(fieldVals, names)};
|
||||
}, domain));
|
||||
}
|
||||
|
||||
function properties(info) {
|
||||
var types = info.args.map(argtype);
|
||||
types.unshift(ULongLong); // size
|
||||
var domain = sequence.apply(null, types);
|
||||
var names = info.args.map(name);
|
||||
return label(info.name, transform(function(fieldVals) {
|
||||
return {id: info.id,
|
||||
size: fieldVals[0],
|
||||
fields: zipObject(fieldVals.slice(1), names)};
|
||||
}, domain));
|
||||
}
|
||||
|
||||
var methods = [];
|
||||
var propertieses = [];
|
||||
|
||||
for (var 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]);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
Octet: Octet,
|
||||
ShortStr: ShortStr,
|
||||
LongStr: LongStr,
|
||||
UShort: UShort,
|
||||
ULong: ULong,
|
||||
ULongLong: ULongLong,
|
||||
Short: Short,
|
||||
Long: Long,
|
||||
LongLong: LongLong,
|
||||
Bit: Bit,
|
||||
Double: Double,
|
||||
Float: Float,
|
||||
Timestamp: Timestamp,
|
||||
Decimal: Decimal,
|
||||
UnsignedByte: UnsignedByte,
|
||||
UnsignedShort: UnsignedShort,
|
||||
UnsignedInt: UnsignedInt,
|
||||
FieldArray: FieldArray,
|
||||
FieldTable: FieldTable,
|
||||
|
||||
methods: methods,
|
||||
properties: propertieses
|
||||
};
|
||||
|
||||
module.exports.rangeInt = rangeInt;
|
||||
191
node_modules/amqplib/test/frame.js
generated
vendored
Normal file
191
node_modules/amqplib/test/frame.js
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var succeed = require('./util').succeed;
|
||||
var fail = require('./util').fail;
|
||||
var connection = require('../lib/connection');
|
||||
var Frames = connection.Connection;
|
||||
var HEARTBEAT = require('../lib/frame').HEARTBEAT;
|
||||
var Stream = require('stream');
|
||||
var PassThrough = Stream.PassThrough;
|
||||
|
||||
var 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});
|
||||
}
|
||||
|
||||
var HB = Buffer.from([defs.constants.FRAME_HEARTBEAT,
|
||||
0, 0, // channel 0
|
||||
0, 0, 0, 0, // zero size
|
||||
defs.constants.FRAME_END]);
|
||||
|
||||
suite("Explicit parsing", function() {
|
||||
|
||||
test('Parse heartbeat', function() {
|
||||
var input = inputs();
|
||||
var frames = new Frames(input);
|
||||
input.write(HB);
|
||||
assert(frames.recvFrame() === HEARTBEAT);
|
||||
assert(!frames.recvFrame());
|
||||
});
|
||||
|
||||
test('Parse partitioned', function() {
|
||||
var input = inputs();
|
||||
var frames = new Frames(input);
|
||||
input.write(HB.subarray(0, 3));
|
||||
assert(!frames.recvFrame());
|
||||
input.write(HB.subarray(3));
|
||||
assert(frames.recvFrame() === HEARTBEAT);
|
||||
assert(!frames.recvFrame());
|
||||
});
|
||||
|
||||
function testBogusFrame(name, bytes) {
|
||||
test(name, function(done) {
|
||||
var input = inputs();
|
||||
var frames = new Frames(input);
|
||||
frames.frameMax = 5; //for the max frame test
|
||||
input.write(Buffer.from(bytes));
|
||||
frames.step(function(err, frame) {
|
||||
if (err != null) done();
|
||||
else done(new Error('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]);
|
||||
|
||||
});
|
||||
|
||||
// Now for a bit more fun.
|
||||
|
||||
var amqp = require('./data');
|
||||
var claire = require('claire');
|
||||
var choice = claire.choice;
|
||||
var forAll = claire.forAll;
|
||||
var repeat = claire.repeat;
|
||||
var label = claire.label;
|
||||
var sequence = claire.sequence;
|
||||
var transform = claire.transform;
|
||||
var sized = claire.sized;
|
||||
|
||||
var assertEqualModuloDefaults =
|
||||
require('./codec').assertEqualModuloDefaults;
|
||||
|
||||
var Trace = label('frame trace',
|
||||
repeat(choice.apply(choice, amqp.methods)));
|
||||
|
||||
suite("Parsing", function() {
|
||||
|
||||
function testPartitioning(partition) {
|
||||
return forAll(Trace).satisfy(function(t) {
|
||||
var bufs = [];
|
||||
var input = inputs();
|
||||
var frames = new Frames(input);
|
||||
var i = 0, ex;
|
||||
frames.accept = function(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(function(f) {
|
||||
f.channel = 0;
|
||||
bufs.push(defs.encodeMethod(f.id, 0, f.fields));
|
||||
});
|
||||
|
||||
partition(bufs).forEach(function (chunk) { input.write(chunk); });
|
||||
frames.acceptLoop();
|
||||
if (ex) throw ex;
|
||||
return i === t.length;
|
||||
}).asTest({times: 20})
|
||||
};
|
||||
|
||||
test("Parse trace of methods",
|
||||
testPartitioning(function(bufs) { return bufs; }));
|
||||
|
||||
test("Parse concat'd methods",
|
||||
testPartitioning(function(bufs) {
|
||||
return [Buffer.concat(bufs)];
|
||||
}));
|
||||
|
||||
test("Parse partitioned methods",
|
||||
testPartitioning(function(bufs) {
|
||||
var full = Buffer.concat(bufs);
|
||||
var onethird = Math.floor(full.length / 3);
|
||||
var twothirds = 2 * onethird;
|
||||
return [
|
||||
full.subarray(0, onethird),
|
||||
full.subarray(onethird, twothirds),
|
||||
full.subarray(twothirds)
|
||||
];
|
||||
}));
|
||||
});
|
||||
|
||||
var FRAME_MAX_MAX = 4096 * 4;
|
||||
var FRAME_MAX_MIN = 4096;
|
||||
|
||||
var FrameMax = amqp.rangeInt('frame max',
|
||||
FRAME_MAX_MIN,
|
||||
FRAME_MAX_MAX);
|
||||
|
||||
var Body = sized(function(_n) {
|
||||
return Math.floor(Math.random() * FRAME_MAX_MAX);
|
||||
}, repeat(amqp.Octet));
|
||||
|
||||
var Content = transform(function(args) {
|
||||
return {
|
||||
method: args[0].fields,
|
||||
header: args[1].fields,
|
||||
body: Buffer.from(args[2])
|
||||
}
|
||||
}, sequence(amqp.methods['BasicDeliver'],
|
||||
amqp.properties['BasicProperties'], Body));
|
||||
|
||||
suite("Content framing", function() {
|
||||
test("Adhere to frame max",
|
||||
forAll(Content, FrameMax).satisfy(function(content, max) {
|
||||
var input = inputs();
|
||||
var frames = new Frames(input);
|
||||
frames.frameMax = max;
|
||||
frames.sendMessage(
|
||||
0,
|
||||
defs.BasicDeliver, content.method,
|
||||
defs.BasicProperties, content.header,
|
||||
content.body);
|
||||
var f, i = 0, largest = 0;
|
||||
while (f = input.read()) {
|
||||
i++;
|
||||
if (f.length > largest) largest = f.length;
|
||||
if (f.length > max) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 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());
|
||||
});
|
||||
203
node_modules/amqplib/test/mux.js
generated
vendored
Normal file
203
node_modules/amqplib/test/mux.js
generated
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var Mux = require('../lib/mux').Mux;
|
||||
var PassThrough = require('stream').PassThrough;
|
||||
|
||||
var latch = require('./util').latch;
|
||||
var schedule = require('./util').schedule;
|
||||
|
||||
function stream() {
|
||||
return new PassThrough({objectMode: true});
|
||||
}
|
||||
|
||||
function readAllObjects(s, cb) {
|
||||
var objs = [];
|
||||
|
||||
function read() {
|
||||
var v = s.read();
|
||||
while (v !== null) {
|
||||
objs.push(v);
|
||||
v = s.read();
|
||||
}
|
||||
}
|
||||
|
||||
s.on('end', function() { cb(objs); });
|
||||
s.on('readable', read);
|
||||
|
||||
read();
|
||||
}
|
||||
|
||||
test("single input", function(done) {
|
||||
var input = stream();
|
||||
var output = stream();
|
||||
input.on('end', function() { output.end() });
|
||||
|
||||
var mux = new Mux(output);
|
||||
mux.pipeFrom(input);
|
||||
|
||||
var 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(function (chunk) { input.write(chunk); });
|
||||
|
||||
readAllObjects(output, function(vals) {
|
||||
assert.deepEqual(data, vals);
|
||||
done();
|
||||
});
|
||||
|
||||
input.end();
|
||||
});
|
||||
|
||||
test("single input, resuming stream", function(done) {
|
||||
var input = stream();
|
||||
var output = stream();
|
||||
input.on('end', function() { output.end() });
|
||||
|
||||
var 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
|
||||
var data = [1,2,3,4,'skip',6,7,8,9];
|
||||
|
||||
var oldRead = input.read;
|
||||
input.read = function(size) {
|
||||
var val = oldRead.call(input, size)
|
||||
|
||||
if (val === 'skip') {
|
||||
input.emit('readable');
|
||||
return null
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
data.forEach(function (chunk) { input.write(chunk); });
|
||||
|
||||
readAllObjects(output, function(vals) {
|
||||
assert.deepEqual([1,2,3,4,6,7,8,9], vals);
|
||||
done();
|
||||
});
|
||||
|
||||
input.end();
|
||||
});
|
||||
|
||||
test("two sequential inputs", function(done) {
|
||||
var input1 = stream();
|
||||
var input2 = stream();
|
||||
var output = stream();
|
||||
var mux = new Mux(output);
|
||||
mux.pipeFrom(input1);
|
||||
mux.pipeFrom(input2);
|
||||
|
||||
var data = [1,2,3,4,5,6,7,8,9];
|
||||
data.forEach(function(v) { input1.write(v); });
|
||||
|
||||
input1.on('end', function() {
|
||||
data.forEach(function (v) { input2.write(v); });
|
||||
input2.end();
|
||||
});
|
||||
input2.on('end', function() { output.end(); });
|
||||
|
||||
input1.end();
|
||||
readAllObjects(output, function(vs) {
|
||||
assert.equal(2 * data.length, vs.length);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test("two interleaved inputs", function(done) {
|
||||
var input1 = stream();
|
||||
var input2 = stream();
|
||||
var output = stream();
|
||||
var mux = new Mux(output);
|
||||
mux.pipeFrom(input1);
|
||||
mux.pipeFrom(input2);
|
||||
|
||||
var endLatch = latch(2, function() { output.end(); });
|
||||
input1.on('end', endLatch);
|
||||
input2.on('end', endLatch);
|
||||
|
||||
var data = [1,2,3,4,5,6,7,8,9];
|
||||
data.forEach(function(v) { input1.write(v); });
|
||||
input1.end();
|
||||
|
||||
data.forEach(function(v) { input2.write(v); });
|
||||
input2.end();
|
||||
|
||||
readAllObjects(output, function(vs) {
|
||||
assert.equal(2 * data.length, vs.length);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test("unpipe", function(done) {
|
||||
var input = stream();
|
||||
var output = stream();
|
||||
var mux = new Mux(output);
|
||||
|
||||
var pipedData = [1,2,3,4,5];
|
||||
var unpipedData = [6,7,8,9];
|
||||
|
||||
mux.pipeFrom(input);
|
||||
|
||||
schedule(function() {
|
||||
pipedData.forEach(function (chunk) { input.write(chunk); });
|
||||
|
||||
schedule(function() {
|
||||
mux.unpipeFrom(input);
|
||||
|
||||
schedule(function() {
|
||||
unpipedData.forEach(function(chunk) { input.write(chunk); });
|
||||
input.end();
|
||||
schedule(function() {
|
||||
// exhaust so that 'end' fires
|
||||
var v; while (v = input.read());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
input.on('end', function() {
|
||||
output.end();
|
||||
});
|
||||
|
||||
readAllObjects(output, function(vals) {
|
||||
try {
|
||||
assert.deepEqual(pipedData, vals);
|
||||
done();
|
||||
}
|
||||
catch (e) { done(e); }
|
||||
});
|
||||
});
|
||||
|
||||
test("roundrobin", function(done) {
|
||||
var input1 = stream();
|
||||
var input2 = stream();
|
||||
var output = stream();
|
||||
var mux = new Mux(output);
|
||||
|
||||
mux.pipeFrom(input1);
|
||||
mux.pipeFrom(input2);
|
||||
|
||||
var endLatch = latch(2, function() { output.end(); });
|
||||
input1.on('end', endLatch);
|
||||
input2.on('end', endLatch);
|
||||
|
||||
var ones = [1,1,1,1,1];
|
||||
ones.forEach(function(v) { input1.write(v); });
|
||||
input1.end();
|
||||
|
||||
var twos = [2,2,2,2,2];
|
||||
twos.forEach(function(v) { input2.write(v); });
|
||||
input2.end();
|
||||
|
||||
readAllObjects(output, function(vs) {
|
||||
assert.deepEqual([1,2,1,2,1,2,1,2,1,2], vs);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
217
node_modules/amqplib/test/util.js
generated
vendored
Normal file
217
node_modules/amqplib/test/util.js
generated
vendored
Normal file
@@ -0,0 +1,217 @@
|
||||
'use strict';
|
||||
|
||||
var crypto = require('crypto');
|
||||
var Connection = require('../lib/connection').Connection;
|
||||
var PassThrough = require('stream').PassThrough;
|
||||
var defs = require('../lib/defs');
|
||||
var assert = require('assert');
|
||||
|
||||
var schedule = (typeof setImmediate === 'function') ?
|
||||
setImmediate : process.nextTick;
|
||||
|
||||
function randomString() {
|
||||
var 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() {
|
||||
var server = new PassThrough();
|
||||
var 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) {
|
||||
var 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(function() {
|
||||
frames.sendMessage(channel, id, fields,
|
||||
defs.BasicProperties, fields,
|
||||
content);
|
||||
});
|
||||
}
|
||||
else {
|
||||
schedule(function() {
|
||||
frames.sendMethod(channel, id, fields);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function wait(method) {
|
||||
return function() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (method) {
|
||||
frames.step(function(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(function(e, f) {
|
||||
if (e !== null) return reject(e);
|
||||
else resolve(f);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
run(send, wait);
|
||||
return frames;
|
||||
}
|
||||
|
||||
// Produce a callback that will complete the test successfully
|
||||
function succeed(done) {
|
||||
return function() { done(); }
|
||||
}
|
||||
|
||||
// Produce a callback that will complete the test successfully
|
||||
// only if the value is an object, it has the specified
|
||||
// attribute, and its value is equals to the expected value
|
||||
function succeedIfAttributeEquals(attribute, value, done) {
|
||||
return function(object) {
|
||||
if (object && !(object instanceof Error) && value === object[attribute]) {
|
||||
return done();
|
||||
}
|
||||
|
||||
done(new Error(attribute + " is not equal to " + value));
|
||||
};
|
||||
}
|
||||
|
||||
// Produce a callback that will fail the test, given either an error
|
||||
// (to be used as a failure continuation) or any other value (to be
|
||||
// used as a success continuation when failure is expected)
|
||||
function fail(done) {
|
||||
return function(err) {
|
||||
if (err instanceof Error) done(err);
|
||||
else done(new Error("Expected to fail, instead got " + err.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
var awaiting = count;
|
||||
var alive = true;
|
||||
return function(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); }
|
||||
}
|
||||
|
||||
// Construct a Node.JS-style callback from a success continuation and
|
||||
// an error continuation
|
||||
function kCallback(k, ek) {
|
||||
return function(err, val) {
|
||||
if (err === null) k && k(val);
|
||||
else ek && ek(err);
|
||||
};
|
||||
}
|
||||
|
||||
// A noddy way to make tests depend on the node version.
|
||||
function versionGreaterThan(actual, spec) {
|
||||
|
||||
function int(e) { return parseInt(e); }
|
||||
|
||||
var version = actual.split('.').map(int);
|
||||
var desired = spec.split('.').map(int);
|
||||
for (var i=0; i < desired.length; i++) {
|
||||
var a = version[i], b = desired[i];
|
||||
if (a != b) return a > b;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
suite('versionGreaterThan', function() {
|
||||
|
||||
test('full spec', function() {
|
||||
assert(versionGreaterThan('0.8.26', '0.6.12'));
|
||||
assert(versionGreaterThan('0.8.26', '0.8.21'));
|
||||
});
|
||||
|
||||
test('partial spec', function() {
|
||||
assert(versionGreaterThan('0.9.12', '0.8'));
|
||||
});
|
||||
|
||||
test('not greater', function() {
|
||||
assert(!versionGreaterThan('0.8.12', '0.8.26'));
|
||||
assert(!versionGreaterThan('0.6.2', '0.6.12'));
|
||||
assert(!versionGreaterThan('0.8.29', '0.8'));
|
||||
});
|
||||
|
||||
test
|
||||
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
socketPair: socketPair,
|
||||
runServer: runServer,
|
||||
succeed: succeed,
|
||||
succeedIfAttributeEquals: succeedIfAttributeEquals,
|
||||
fail: fail,
|
||||
latch: latch,
|
||||
completes: completes,
|
||||
kCallback: kCallback,
|
||||
schedule: schedule,
|
||||
randomString: randomString,
|
||||
versionGreaterThan: versionGreaterThan
|
||||
};
|
||||
Reference in New Issue
Block a user