From e4286f9172b1271f5d391d666ca6194b74a68e0f Mon Sep 17 00:00:00 2001 From: Jimmy Bradshaw Date: Thu, 16 Jan 2020 22:38:00 -0600 Subject: [PATCH] Need to increment IDs when subscribing * also added the RabbitMQ Web Stomp demos...with an added logger * for the echo demo (with uses reply-to) we end up stealing half the messages...little goofy --- examples/bunny/Dockerfile | 13 + examples/bunny/bunny.html | 144 +++++++ examples/bunny/bunny.png | Bin 0 -> 38296 bytes examples/bunny/docker-compose.yml | 30 ++ examples/bunny/echo.html | 111 ++++++ examples/bunny/index.html | 16 + examples/bunny/logger.py | 15 + examples/bunny/main.css | 40 ++ examples/bunny/pencil.cur | Bin 0 -> 2238 bytes examples/bunny/requirements.txt | 1 + examples/bunny/stomp.js | 607 ++++++++++++++++++++++++++++++ examples/bunny/temp-queue.html | 122 ++++++ examples/bunny/wait-for-it.sh | 178 +++++++++ pyproject.toml | 2 +- src/shattered/__init__.py | 2 +- src/shattered/shattered.py | 5 +- tests/test_shattered.py | 2 +- 17 files changed, 1284 insertions(+), 4 deletions(-) create mode 100644 examples/bunny/Dockerfile create mode 100644 examples/bunny/bunny.html create mode 100644 examples/bunny/bunny.png create mode 100644 examples/bunny/docker-compose.yml create mode 100644 examples/bunny/echo.html create mode 100644 examples/bunny/index.html create mode 100644 examples/bunny/logger.py create mode 100644 examples/bunny/main.css create mode 100755 examples/bunny/pencil.cur create mode 100644 examples/bunny/requirements.txt create mode 100644 examples/bunny/stomp.js create mode 100644 examples/bunny/temp-queue.html create mode 100755 examples/bunny/wait-for-it.sh diff --git a/examples/bunny/Dockerfile b/examples/bunny/Dockerfile new file mode 100644 index 0000000..39532d5 --- /dev/null +++ b/examples/bunny/Dockerfile @@ -0,0 +1,13 @@ +FROM python:slim + +ENV SHATTERED_APP=logger.py + +COPY requirements.txt /src/requirements.txt + +RUN pip install -r /src/requirements.txt + +COPY . /src + +WORKDIR /src + +CMD ["shattered", "run"] diff --git a/examples/bunny/bunny.html b/examples/bunny/bunny.html new file mode 100644 index 0000000..1a56151 --- /dev/null +++ b/examples/bunny/bunny.html @@ -0,0 +1,144 @@ + + + + + + + + RabbitMQ Web STOMP Examples: Bunny Drawing + + + +

+ RabbitMQ Web STOMP Examples > Bunny Drawing +

+ + + + diff --git a/examples/bunny/bunny.png b/examples/bunny/bunny.png new file mode 100644 index 0000000000000000000000000000000000000000..6c2284ba84fc437a45854b9a2c48e3d192fe8de5 GIT binary patch literal 38296 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 literal 0 HcmV?d00001 diff --git a/examples/bunny/docker-compose.yml b/examples/bunny/docker-compose.yml new file mode 100644 index 0000000..a89b8c9 --- /dev/null +++ b/examples/bunny/docker-compose.yml @@ -0,0 +1,30 @@ +version: "2" +services: + logger: + build: . + depends_on: + - rabbitmq + volumes: + - .:/src + command: + [ + "./wait-for-it.sh", + "-t", + "300", + "rabbitmq:61613", + "--", + "shattered", + "run", + ] + bunny: + image: python:slim + working_dir: /src + volumes: + - .:/src + ports: + - 8000:8000 + command: ["python", "-m", "http.server"] + rabbitmq: + build: ../../rabbitmq + ports: + - 15674:15674 diff --git a/examples/bunny/echo.html b/examples/bunny/echo.html new file mode 100644 index 0000000..cf81ffa --- /dev/null +++ b/examples/bunny/echo.html @@ -0,0 +1,111 @@ + + + + + + + RabbitMQ Web STOMP Examples : Echo Server + + + +

RabbitMQ Web STOMP Examples > Echo Server

+ +
+

Received

+
+
+
+ +
+

Logs

+
+
+ + + + diff --git a/examples/bunny/index.html b/examples/bunny/index.html new file mode 100644 index 0000000..76e1279 --- /dev/null +++ b/examples/bunny/index.html @@ -0,0 +1,16 @@ + + + + + RabbitMQ Web STOMP Examples + + + +

RabbitMQ Web STOMP Examples

+ + + diff --git a/examples/bunny/logger.py b/examples/bunny/logger.py new file mode 100644 index 0000000..def5462 --- /dev/null +++ b/examples/bunny/logger.py @@ -0,0 +1,15 @@ +import logging + +from shattered import Shattered + +logger = logging.getLogger(__name__) +logging.basicConfig(level=logging.INFO) + +shattered_app = Shattered(host="rabbitmq") + + +@shattered_app.subscribe("/queue/test") +@shattered_app.subscribe("/topic/test") +@shattered_app.subscribe("/topic/bunny") +def echo(headers, body, conn): + logger.info(body) diff --git a/examples/bunny/main.css b/examples/bunny/main.css new file mode 100644 index 0000000..f2d1bb5 --- /dev/null +++ b/examples/bunny/main.css @@ -0,0 +1,40 @@ +body { + font-family: "Arial"; + color: #444; +} + +h1, +h2 { + color: #f60; + font-weight: normal; +} + +h1 { + font-size: 1.5em; +} + +h2 { + font-size: 1.2em; + margin: 0; +} + +a { + color: #f60; + border: 1px solid #fda; + background: #fff0e0; + border-radius: 3px; + -moz-border-radius: 3px; + padding: 2px; + text-decoration: none; + /* font-weight: bold; */ +} + +ul.menu { + list-style-type: none; + padding: 0; + margin: 0; +} + +ul.menu li { + padding: 5px 0; +} diff --git a/examples/bunny/pencil.cur b/examples/bunny/pencil.cur new file mode 100755 index 0000000000000000000000000000000000000000..a3e3598bf0e5128c6a24beb18e2a333b66e30a69 GIT binary patch literal 2238 zcmd6nc~nka6vlrkDj}4N384_m98t-X6e1-GDf67MXf_W;AwwBLAwyBd2D2h%o~ew< zkf}nv?tR}fEpOji-#_1X?t0EWXFt!```KsRbwNRR>eWM-RftjnEkIv@5W-A=7?%zu z&`^^>2#SPsC@3gUrc4Y`SPf#sZpUq1u9mo zNTo`ZP*+!{a^=cYsZxciRjZ<*p@F8RCe^A{LrY7G>eZ`LqecyC)~rdbTD7QMyEb*| z)InQYo4R%DqNAfD_^wC&`t@nhpaHtNx-@LqkVcIf(YSGA^z`)5*Vm^>lO{B6+LUI^ znqgpIK=bC!Y0;tuEnBw4(9jSgBO_Y1YDMeTt!dMy4Q<=DrCqyrv~S;@4jnpRY-~)& zjveXLsS_q9CUoxHnJ!(rU}|cLnVA{p=H_(m+7$~63%YgdM)&UB>CvMHJ$v?~SFc`J zT3XV(cWB-!=bD1}99$sEvczb)}k&%%^MMbf5=T3I*+QshOyNQmDX3w5I?A^PUef##YfB$}B zVq%Dmjpe|B0~|bfkhr)w;^X5vbm$O=4aS{>|NK8y5DJhBM zg(u5k70RjysT#`WvhxpCtLH*enL)~#FIzI~fJckXca?p^NPyT|?e z_j&N(0S_NOBr`LUtgI}uv$J{h=n;<}Kjz7kCp>-nlxNSL@%;I7a&mHb@!|!!xw*W2 z`I1+!Uh(?%Yu>zhLtb7UZ{NP<-Me?ZfB&8jA3pH$<45xI^C>7Ops=uzPoF;V`SWMK zeEC9AQ4wFie&yS@Z%8B(*$?1 _ref1; + i = start <= _ref1 ? ++_j : --_j + ) { + chr = data.charAt(i); + if (chr === Byte.NULL) { + break; + } + body += chr; + } + } + return new Frame(command, headers, body); + }; + + Frame.unmarshall = function(datas) { + var frame, frames, last_frame, r; + frames = datas.split(RegExp("" + Byte.NULL + Byte.LF + "*")); + r = { + frames: [], + partial: "" + }; + r.frames = (function() { + var _i, _len, _ref, _results; + _ref = frames.slice(0, -1); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + frame = _ref[_i]; + _results.push(unmarshallSingle(frame)); + } + return _results; + })(); + last_frame = frames.slice(-1)[0]; + if ( + last_frame === Byte.LF || + last_frame.search(RegExp("" + Byte.NULL + Byte.LF + "*$")) !== -1 + ) { + r.frames.push(unmarshallSingle(last_frame)); + } else { + r.partial = last_frame; + } + return r; + }; + + Frame.marshall = function(command, headers, body) { + var frame; + frame = new Frame(command, headers, body); + return frame.toString() + Byte.NULL; + }; + + return Frame; + })(); + + Client = (function() { + var now; + + function Client(ws) { + this.ws = ws; + this.ws.binaryType = "arraybuffer"; + this.counter = 0; + this.connected = false; + this.heartbeat = { + outgoing: 10000, + incoming: 10000 + }; + this.maxWebSocketFrameSize = 16 * 1024; + this.subscriptions = {}; + this.partialData = ""; + } + + Client.prototype.debug = function(message) { + var _ref; + return typeof window !== "undefined" && window !== null + ? (_ref = window.console) != null + ? _ref.log(message) + : void 0 + : void 0; + }; + + now = function() { + if (Date.now) { + return Date.now(); + } else { + return new Date().valueOf; + } + }; + + Client.prototype._transmit = function(command, headers, body) { + var out; + out = Frame.marshall(command, headers, body); + if (typeof this.debug === "function") { + this.debug(">>> " + out); + } + while (true) { + if (out.length > this.maxWebSocketFrameSize) { + this.ws.send(out.substring(0, this.maxWebSocketFrameSize)); + out = out.substring(this.maxWebSocketFrameSize); + if (typeof this.debug === "function") { + this.debug("remaining = " + out.length); + } + } else { + return this.ws.send(out); + } + } + }; + + Client.prototype._setupHeartbeat = function(headers) { + var serverIncoming, serverOutgoing, ttl, v, _ref, _ref1; + if ( + (_ref = headers.version) !== Stomp.VERSIONS.V1_1 && + _ref !== Stomp.VERSIONS.V1_2 + ) { + return; + } + (_ref1 = (function() { + var _i, _len, _ref1, _results; + _ref1 = headers["heart-beat"].split(","); + _results = []; + for (_i = 0, _len = _ref1.length; _i < _len; _i++) { + v = _ref1[_i]; + _results.push(parseInt(v)); + } + return _results; + })()), + (serverOutgoing = _ref1[0]), + (serverIncoming = _ref1[1]); + if (!(this.heartbeat.outgoing === 0 || serverIncoming === 0)) { + ttl = Math.max(this.heartbeat.outgoing, serverIncoming); + if (typeof this.debug === "function") { + this.debug("send PING every " + ttl + "ms"); + } + this.pinger = Stomp.setInterval( + ttl, + (function(_this) { + return function() { + _this.ws.send(Byte.LF); + return typeof _this.debug === "function" + ? _this.debug(">>> PING") + : void 0; + }; + })(this) + ); + } + if (!(this.heartbeat.incoming === 0 || serverOutgoing === 0)) { + ttl = Math.max(this.heartbeat.incoming, serverOutgoing); + if (typeof this.debug === "function") { + this.debug("check PONG every " + ttl + "ms"); + } + return (this.ponger = Stomp.setInterval( + ttl, + (function(_this) { + return function() { + var delta; + delta = now() - _this.serverActivity; + if (delta > ttl * 2) { + if (typeof _this.debug === "function") { + _this.debug( + "did not receive server activity for the last " + + delta + + "ms" + ); + } + return _this.ws.close(); + } + }; + })(this) + )); + } + }; + + Client.prototype._parseConnect = function() { + var args, connectCallback, errorCallback, headers; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + headers = {}; + switch (args.length) { + case 2: + (headers = args[0]), (connectCallback = args[1]); + break; + case 3: + if (args[1] instanceof Function) { + (headers = args[0]), + (connectCallback = args[1]), + (errorCallback = args[2]); + } else { + (headers.login = args[0]), + (headers.passcode = args[1]), + (connectCallback = args[2]); + } + break; + case 4: + (headers.login = args[0]), + (headers.passcode = args[1]), + (connectCallback = args[2]), + (errorCallback = args[3]); + break; + default: + (headers.login = args[0]), + (headers.passcode = args[1]), + (connectCallback = args[2]), + (errorCallback = args[3]), + (headers.host = args[4]); + } + return [headers, connectCallback, errorCallback]; + }; + + Client.prototype.connect = function() { + var args, errorCallback, headers, out; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + out = this._parseConnect.apply(this, args); + (headers = out[0]), + (this.connectCallback = out[1]), + (errorCallback = out[2]); + if (typeof this.debug === "function") { + this.debug("Opening Web Socket..."); + } + this.ws.onmessage = (function(_this) { + return function(evt) { + var arr, + c, + client, + data, + frame, + messageID, + onreceive, + subscription, + unmarshalledData, + _i, + _len, + _ref, + _results; + data = + typeof ArrayBuffer !== "undefined" && + evt.data instanceof ArrayBuffer + ? ((arr = new Uint8Array(evt.data)), + typeof _this.debug === "function" + ? _this.debug("--- got data length: " + arr.length) + : void 0, + (function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = arr.length; _i < _len; _i++) { + c = arr[_i]; + _results.push(String.fromCharCode(c)); + } + return _results; + })().join("")) + : evt.data; + _this.serverActivity = now(); + if (data === Byte.LF) { + if (typeof _this.debug === "function") { + _this.debug("<<< PONG"); + } + return; + } + if (typeof _this.debug === "function") { + _this.debug("<<< " + data); + } + unmarshalledData = Frame.unmarshall(_this.partialData + data); + _this.partialData = unmarshalledData.partial; + _ref = unmarshalledData.frames; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + frame = _ref[_i]; + switch (frame.command) { + case "CONNECTED": + if (typeof _this.debug === "function") { + _this.debug("connected to server " + frame.headers.server); + } + _this.connected = true; + _this._setupHeartbeat(frame.headers); + _results.push( + typeof _this.connectCallback === "function" + ? _this.connectCallback(frame) + : void 0 + ); + break; + case "MESSAGE": + subscription = frame.headers.subscription; + onreceive = + _this.subscriptions[subscription] || _this.onreceive; + if (onreceive) { + client = _this; + messageID = frame.headers["message-id"]; + frame.ack = function(headers) { + if (headers == null) { + headers = {}; + } + return client.ack(messageID, subscription, headers); + }; + frame.nack = function(headers) { + if (headers == null) { + headers = {}; + } + return client.nack(messageID, subscription, headers); + }; + _results.push(onreceive(frame)); + } else { + _results.push( + typeof _this.debug === "function" + ? _this.debug("Unhandled received MESSAGE: " + frame) + : void 0 + ); + } + break; + case "RECEIPT": + _results.push( + typeof _this.onreceipt === "function" + ? _this.onreceipt(frame) + : void 0 + ); + break; + case "ERROR": + _results.push( + typeof errorCallback === "function" + ? errorCallback(frame) + : void 0 + ); + break; + default: + _results.push( + typeof _this.debug === "function" + ? _this.debug("Unhandled frame: " + frame) + : void 0 + ); + } + } + return _results; + }; + })(this); + this.ws.onclose = (function(_this) { + return function() { + var msg; + msg = "Whoops! Lost connection to " + _this.ws.url; + if (typeof _this.debug === "function") { + _this.debug(msg); + } + _this._cleanUp(); + return typeof errorCallback === "function" + ? errorCallback(msg) + : void 0; + }; + })(this); + return (this.ws.onopen = (function(_this) { + return function() { + if (typeof _this.debug === "function") { + _this.debug("Web Socket Opened..."); + } + headers["accept-version"] = Stomp.VERSIONS.supportedVersions(); + headers["heart-beat"] = [ + _this.heartbeat.outgoing, + _this.heartbeat.incoming + ].join(","); + return _this._transmit("CONNECT", headers); + }; + })(this)); + }; + + Client.prototype.disconnect = function(disconnectCallback, headers) { + if (headers == null) { + headers = {}; + } + this._transmit("DISCONNECT", headers); + this.ws.onclose = null; + this.ws.close(); + this._cleanUp(); + return typeof disconnectCallback === "function" + ? disconnectCallback() + : void 0; + }; + + Client.prototype._cleanUp = function() { + this.connected = false; + if (this.pinger) { + Stomp.clearInterval(this.pinger); + } + if (this.ponger) { + return Stomp.clearInterval(this.ponger); + } + }; + + Client.prototype.send = function(destination, headers, body) { + if (headers == null) { + headers = {}; + } + if (body == null) { + body = ""; + } + headers.destination = destination; + return this._transmit("SEND", headers, body); + }; + + Client.prototype.subscribe = function(destination, callback, headers) { + var client; + if (headers == null) { + headers = {}; + } + if (!headers.id) { + headers.id = "sub-" + this.counter++; + } + headers.destination = destination; + this.subscriptions[headers.id] = callback; + this._transmit("SUBSCRIBE", headers); + client = this; + return { + id: headers.id, + unsubscribe: function() { + return client.unsubscribe(headers.id); + } + }; + }; + + Client.prototype.unsubscribe = function(id) { + delete this.subscriptions[id]; + return this._transmit("UNSUBSCRIBE", { + id: id + }); + }; + + Client.prototype.begin = function(transaction) { + var client, txid; + txid = transaction || "tx-" + this.counter++; + this._transmit("BEGIN", { + transaction: txid + }); + client = this; + return { + id: txid, + commit: function() { + return client.commit(txid); + }, + abort: function() { + return client.abort(txid); + } + }; + }; + + Client.prototype.commit = function(transaction) { + return this._transmit("COMMIT", { + transaction: transaction + }); + }; + + Client.prototype.abort = function(transaction) { + return this._transmit("ABORT", { + transaction: transaction + }); + }; + + Client.prototype.ack = function(messageID, subscription, headers) { + if (headers == null) { + headers = {}; + } + headers["message-id"] = messageID; + headers.subscription = subscription; + return this._transmit("ACK", headers); + }; + + Client.prototype.nack = function(messageID, subscription, headers) { + if (headers == null) { + headers = {}; + } + headers["message-id"] = messageID; + headers.subscription = subscription; + return this._transmit("NACK", headers); + }; + + return Client; + })(); + + Stomp = { + VERSIONS: { + V1_0: "1.0", + V1_1: "1.1", + V1_2: "1.2", + supportedVersions: function() { + return "1.1,1.0"; + } + }, + client: function(url, protocols) { + var klass, ws; + if (protocols == null) { + protocols = ["v10.stomp", "v11.stomp"]; + } + klass = Stomp.WebSocketClass || WebSocket; + ws = new klass(url, protocols); + return new Client(ws); + }, + over: function(ws) { + return new Client(ws); + }, + Frame: Frame + }; + + if (typeof exports !== "undefined" && exports !== null) { + exports.Stomp = Stomp; + } + + if (typeof window !== "undefined" && window !== null) { + Stomp.setInterval = function(interval, f) { + return window.setInterval(f, interval); + }; + Stomp.clearInterval = function(id) { + return window.clearInterval(id); + }; + window.Stomp = Stomp; + } else if (!exports) { + self.Stomp = Stomp; + } +}.call(this)); diff --git a/examples/bunny/temp-queue.html b/examples/bunny/temp-queue.html new file mode 100644 index 0000000..244eb15 --- /dev/null +++ b/examples/bunny/temp-queue.html @@ -0,0 +1,122 @@ + + + + + + + RabbitMQ Web STOMP Examples : Temporary Queue + + + +

+ RabbitMQ Web STOMP Examples > Temporary Queue +

+ +

+ When you type text in the form's input, the application will send a + message to the /queue/test destination with the + reply-to header set to /temp-queue/foo. +

+

+ The STOMP client sets a default onreceive callback to receive + messages from this temporary queue and display the message's text. +

+

+ Finally, the client subscribes to the + /queue/test destination. When it receives message from this + destination, it reverses the message's text and reply by sending the + reversed text to the destination defined by the message's + reply-to header. +

+ +
+

Received

+
+
+
+ +
+

Logs

+
+
+ + + + diff --git a/examples/bunny/wait-for-it.sh b/examples/bunny/wait-for-it.sh new file mode 100755 index 0000000..071c2be --- /dev/null +++ b/examples/bunny/wait-for-it.sh @@ -0,0 +1,178 @@ +#!/usr/bin/env bash +# Use this script to test if a given TCP host/port are available + +WAITFORIT_cmdname=${0##*/} + +echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi } + +usage() +{ + cat << USAGE >&2 +Usage: + $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args] + -h HOST | --host=HOST Host or IP under test + -p PORT | --port=PORT TCP port under test + Alternatively, you specify the host and port as host:port + -s | --strict Only execute subcommand if the test succeeds + -q | --quiet Don't output any status messages + -t TIMEOUT | --timeout=TIMEOUT + Timeout in seconds, zero for no timeout + -- COMMAND ARGS Execute command with args after the test finishes +USAGE + exit 1 +} + +wait_for() +{ + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + else + echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout" + fi + WAITFORIT_start_ts=$(date +%s) + while : + do + if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then + nc -z $WAITFORIT_HOST $WAITFORIT_PORT + WAITFORIT_result=$? + else + (echo > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1 + WAITFORIT_result=$? + fi + if [[ $WAITFORIT_result -eq 0 ]]; then + WAITFORIT_end_ts=$(date +%s) + echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds" + break + fi + sleep 1 + done + return $WAITFORIT_result +} + +wait_for_wrapper() +{ + # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 + if [[ $WAITFORIT_QUIET -eq 1 ]]; then + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + else + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + fi + WAITFORIT_PID=$! + trap "kill -INT -$WAITFORIT_PID" INT + wait $WAITFORIT_PID + WAITFORIT_RESULT=$? + if [[ $WAITFORIT_RESULT -ne 0 ]]; then + echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + fi + return $WAITFORIT_RESULT +} + +# process arguments +while [[ $# -gt 0 ]] +do + case "$1" in + *:* ) + WAITFORIT_hostport=(${1//:/ }) + WAITFORIT_HOST=${WAITFORIT_hostport[0]} + WAITFORIT_PORT=${WAITFORIT_hostport[1]} + shift 1 + ;; + --child) + WAITFORIT_CHILD=1 + shift 1 + ;; + -q | --quiet) + WAITFORIT_QUIET=1 + shift 1 + ;; + -s | --strict) + WAITFORIT_STRICT=1 + shift 1 + ;; + -h) + WAITFORIT_HOST="$2" + if [[ $WAITFORIT_HOST == "" ]]; then break; fi + shift 2 + ;; + --host=*) + WAITFORIT_HOST="${1#*=}" + shift 1 + ;; + -p) + WAITFORIT_PORT="$2" + if [[ $WAITFORIT_PORT == "" ]]; then break; fi + shift 2 + ;; + --port=*) + WAITFORIT_PORT="${1#*=}" + shift 1 + ;; + -t) + WAITFORIT_TIMEOUT="$2" + if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi + shift 2 + ;; + --timeout=*) + WAITFORIT_TIMEOUT="${1#*=}" + shift 1 + ;; + --) + shift + WAITFORIT_CLI=("$@") + break + ;; + --help) + usage + ;; + *) + echoerr "Unknown argument: $1" + usage + ;; + esac +done + +if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then + echoerr "Error: you need to provide a host and port to test." + usage +fi + +WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15} +WAITFORIT_STRICT=${WAITFORIT_STRICT:-0} +WAITFORIT_CHILD=${WAITFORIT_CHILD:-0} +WAITFORIT_QUIET=${WAITFORIT_QUIET:-0} + +# check to see if timeout is from busybox? +WAITFORIT_TIMEOUT_PATH=$(type -p timeout) +WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH) +if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then + WAITFORIT_ISBUSY=1 + WAITFORIT_BUSYTIMEFLAG="-t" + +else + WAITFORIT_ISBUSY=0 + WAITFORIT_BUSYTIMEFLAG="" +fi + +if [[ $WAITFORIT_CHILD -gt 0 ]]; then + wait_for + WAITFORIT_RESULT=$? + exit $WAITFORIT_RESULT +else + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + wait_for_wrapper + WAITFORIT_RESULT=$? + else + wait_for + WAITFORIT_RESULT=$? + fi +fi + +if [[ $WAITFORIT_CLI != "" ]]; then + if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then + echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess" + exit $WAITFORIT_RESULT + fi + exec "${WAITFORIT_CLI[@]}" +else + exit $WAITFORIT_RESULT +fi diff --git a/pyproject.toml b/pyproject.toml index 6b705a5..2a49a4b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "shattered" -version = "0.3.0" +version = "0.4.0" description = "STOMP meets bottle.py" authors = ["Jimmy Bradshaw "] repository = "https://github.com/bradshjg/shattered" diff --git a/src/shattered/__init__.py b/src/shattered/__init__.py index 831105d..e7b791f 100644 --- a/src/shattered/__init__.py +++ b/src/shattered/__init__.py @@ -1,3 +1,3 @@ -__version__ = "0.3.0" +__version__ = "0.4.0" from shattered.shattered import Shattered diff --git a/src/shattered/shattered.py b/src/shattered/shattered.py index c5884b3..c4cc9e6 100644 --- a/src/shattered/shattered.py +++ b/src/shattered/shattered.py @@ -9,13 +9,16 @@ class ShatteredListener(stomp.ConnectionListener): + sub_id = 1 + def __init__(self, app): self.app = app def on_connected(self, headers, body): logger.info("STOMP connection established") for destination in self.app.subscriptions: - self.app.conn.subscribe(destination, 1) + self.app.conn.subscribe(destination, self.sub_id) + self.sub_id += 1 def on_message(self, headers, body): logger.info("STOMP message received") diff --git a/tests/test_shattered.py b/tests/test_shattered.py index 100303d..79181c0 100644 --- a/tests/test_shattered.py +++ b/tests/test_shattered.py @@ -68,7 +68,7 @@ def test_listener_on_connected_subscribes(app, mocker): app._run() assert app.conn.subscribe.mock_calls == [] app.listener.on_connected({}, "") - assert app.conn.subscribe.mock_calls == [call("foo", 1), call("bar", 1)] + assert app.conn.subscribe.mock_calls == [call("foo", 1), call("bar", 2)] def test_listener_on_message(app, mocker):