# HG changeset patch # User Mahlon E. Smith # Date 1458944289 25200 # Node ID 48806dfcbba548800a8d748a34e8ba47ab497742 # Parent 3c678f7bf1a011fe852d8e9c93dca9dff63c3275 Update README. diff -r 3c678f7bf1a0 -r 48806dfcbba5 README.md --- a/README.md Fri Mar 25 21:42:44 2016 +0000 +++ b/README.md Fri Mar 25 15:18:09 2016 -0700 @@ -1,29 +1,213 @@ # README # -This README would normally document whatever steps are necessary to get your application up and running. +## Overview ## + +This is a pure-nim client library for interacting with Stomp +compliant messaging brokers. + +https://stomp.github.io/ + +Stomp is a simple protocol for message passing between clients, using a central +broker. It is a subset of other more elaborate protocols (like AMQP), supporting +only the most used features of common brokers. + +Because this library is pure-nim, there are no external dependencies. If you +can compile a nim binary, you can participate in advanced messaging between processes. + +A list of broker support for Stomp can be found here: +https://stomp.github.io/implementations.html. + +This library has been tested with recent versions of RabbitMQ. If it +works for you with another broker, please let the author know. + +### Installation ### -### What is this repository for? ### +The easiest way to install this module is via the nimble package manager, +by simply running 'nimble install stomp'. + +Alternatively, you can fetch the 'stomp.nim' file yourself, and put it +in a place of your choosing. + + +### Protocol support ### + +This library supports (almost) the entirety of the Stomp 1.2 spec, +with the exception of client to server heartbeat. Server to client +heartbeat is fully supported, which should normally be sufficient to +keep firewall state tables open and sockets alive. + + +### Callbacks ### + +Because a client can receive frames at any time, most of the behavior +of this module is implemented via callback procs. + +By default, most every event is a no-op. You can override various +behaviors with the following callbacks: + +* **connected_callback**: Called when the Stomp library makes a successful + connection to the broker. -* Quick summary -* Version -* [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo) +* **error_callback**: Called if there was a **ERROR** frame in the stream. By default, + this raises a **StompError** exception with the error message. + +* **heartbeat_callback**: Called when a server heartbeat is received. + +* **message_callback**: Called when a **MESSAGE** frame is received. + +* **missed_heartbeat_callback**: Called when the Stomp socket is idle longer than + the specified heartbeat time -- usually an indication of a problem. The default behavior + raises a **StompError** exception. + +* **receipt_callback**: Called when a **RECEIPT** frame is received. + + +### Custom headers ### + +Depending on the broker, you may be able to add addtional features to outgoing messages +by adding specific headers. You can also add "x-headers" that are carried between messages. + +Another use is to issue "receipts" on sends or subscriptions, to ensure the broker has +processed your request. Here's an example of how to perform receipt processing: -### How do I get set up? ### +``` +#!nimrod +proc accept_receipt( c: StompClient, r: StompResponse ) = + var receipt = r[ "receipt-id" ] + # ... match this receipt up to the request that generated it + +var client = newStompClient( socket, "..." ) + +client.receipt_callback = accept_receipt +client.connect + +var headers = seq[ tuple[name:string, value:string] ] +headers.add( "x-breakfast", "tequila" ) +headers.add( "receipt", "special-identifier" ) + +client.send( "/destination", "message!", "text/plain", headers ) +``` + + +### Transactions ### + +This library has full support for transactions. Once entering a +transaction, any messages or acknowledgments attached to it must be +committed before the broker will release them. + +With one open transaction, messages are automatically attached to it. +If you have multiple open transactions, you'll need to add which one +you want a message to be part of via the custom headers. -* Summary of set up -* Configuration -* Dependencies -* Database configuration -* How to run tests -* Deployment instructions +``` +#!nimrod +# Single transaction +# +client.begin( "trans-1" ) +client.send( "/destination", "hi" ) # Part of "trans-1" +client.send( "/destination", "yo" ) # Part of "trans-1" +client.send( "/destination", "whaddup" ) # Part of "trans-1" +client.commit # or client.abort + +# Multiple simultaneous transactions +# +client.begin( "trans-1" ) +client.begin( "trans-2" ) +client.begin( "trans-3" ) + +var headers = seq[ tuple[name:string, value:string] ] +headers.add( "transaction", "trans-1" ) +client.send( "/destination", "hi", nil, headers ) # Part of "trans-1" + +headers = @[] +headers.add( "transaction", "trans-2" ) +client.send( "/destination", "hi", nil, headers ) # Part of "trans-2" +client.ack( "some-ack-id", "trans-2" ) # Part of "trans-2" -### Contribution guidelines ### +headers = @[] +headers.add( "transaction", "trans-3" ) +client.send( "/destination", "hi", nil, headers ) # Part of "trans-3" +client.ack( "some-ack-id", "trans-3" ) # Part of "trans-3" + +client.abort( "trans-1" ) # anything "trans-1" never happened +client.commit( "trans-2" ) # "trans-2" messages and acks are released +client.abort( "trans-3" ) # anything "trans-3" never happened +``` + +### Example ### + +This is a complete client that does the following: + +* Connect to an AMQP server at **mq.example.com** via SSL as the **test** user, + in the **/example** vhost. +* Request heartbeats every **5** seconds. +* Subscribe to a topic exchange **events** with the key of **user.create**, requiring message + acknowledgement. +* Accept incoming messages, parsing the JSON payloads. +* If parsing was successful, ACK the message and emit a new message to the exchange + with JSON results to the **user.created** key -- presumably to be picked up by another + process elsewhere. + +``` +#!nimrod +# (This should be compiled with -d:ssl) -* Writing tests -* Code review -* Other guidelines +import + json, + net, + stomp + +let + socket = newSocket() + sslContext = newContext( verifyMode = CVerifyNone ) + +sslContext.wrapSocket( socket ) +var client = newStompClient( socket, "stomp+ssl://test:test@mq.example.com/%2Fexample?heartbeat=5" ) + +# Announce when we're connected. +proc connected( c: StompClient, r: StompResponse ) = + echo "Connected to a ", c["server"], " server." + +# Echo to screen when we see a heartbeat. +proc heartbeat( c: StompClient, r: StompResponse ) = + echo "Heartbeat at: ", c.last_msgtime + +# Parse JSON, perform work, send success message. +proc message( c: StompClient, r: StompResponse ) = + let id = r[ "ack" ] + + if r[ "content-type" ] != "application/json": + echo "I expect JSON payloads." + c.nack( id ) + return -### Who do I talk to? ### + try: + var json = r.payload.parse_json + + # ... do the heavy lifting with the parsed data. + # ... and assuming is was successful, ack and emit! + + c.ack( id ) + + var message = %*{ "user": json["user"].getStr, "otherstuff": true } + c.send( "/exchange/events/user.created", $message, "application/json" ) + + except JsonParsingError: + echo "Couldn't parse JSON! ", r.payload + c.nack( id ) -* Repo owner or admin -* Other community or team contact \ No newline at end of file +# Attach callbacks +client.connected_callback = connected +client.message_callback = message +client.heartbeat_callback = heartbeat + +# Open a session with the broker +client.connect + +# Subscribe to a topic key, requiring acknowledgements. +client.subscribe( "/exchange/events/user.create", "client-individual" ) + +# Enter message loop. +client.wait_for_messages +``` +