README.md
changeset 2 48806dfcbba5
parent 1 3c678f7bf1a0
child 3 d0bc42746346
equal deleted inserted replaced
1:3c678f7bf1a0 2:48806dfcbba5
     1 # README #
     1 # README #
     2 
     2 
     3 This README would normally document whatever steps are necessary to get your application up and running.
     3 ## Overview ##
     4 
     4 
     5 ### What is this repository for? ###
     5 This is a pure-nim client library for interacting with Stomp
     6 
     6 compliant messaging brokers.
     7 * Quick summary
     7 
     8 * Version
     8 https://stomp.github.io/
     9 * [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
     9 
    10 
    10 Stomp is a simple protocol for message passing between clients, using a central
    11 ### How do I get set up? ###
    11 broker.  It is a subset of other more elaborate protocols (like AMQP), supporting
    12 
    12 only the most used features of common brokers.
    13 * Summary of set up
    13 
    14 * Configuration
    14 Because this library is pure-nim, there are no external dependencies.  If you
    15 * Dependencies
    15 can compile a nim binary, you can participate in advanced messaging between processes.
    16 * Database configuration
    16 
    17 * How to run tests
    17 A list of broker support for Stomp can be found here:
    18 * Deployment instructions
    18 https://stomp.github.io/implementations.html.
    19 
    19 
    20 ### Contribution guidelines ###
    20 This library has been tested with recent versions of RabbitMQ.  If it
    21 
    21 works for you with another broker, please let the author know.
    22 * Writing tests
    22 
    23 * Code review
    23 ### Installation ###
    24 * Other guidelines
    24 
    25 
    25 The easiest way to install this module is via the nimble package manager, 
    26 ### Who do I talk to? ###
    26 by simply running 'nimble install stomp'.
    27 
    27 
    28 * Repo owner or admin
    28 Alternatively, you can fetch the 'stomp.nim' file yourself, and put it
    29 * Other community or team contact
    29 in a place of your choosing.
       
    30 
       
    31 
       
    32 ### Protocol support ###
       
    33 
       
    34 This library supports (almost) the entirety of the Stomp 1.2 spec,
       
    35 with the exception of client to server heartbeat.  Server to client
       
    36 heartbeat is fully supported, which should normally be sufficient to
       
    37 keep firewall state tables open and sockets alive.
       
    38 
       
    39 
       
    40 ### Callbacks ###
       
    41 
       
    42 Because a client can receive frames at any time, most of the behavior
       
    43 of this module is implemented via callback procs.
       
    44 
       
    45 By default, most every event is a no-op.  You can override various
       
    46 behaviors with the following callbacks:
       
    47 
       
    48 * **connected_callback**:  Called when the Stomp library makes a successful
       
    49  connection to the broker.
       
    50 
       
    51 * **error_callback**: Called if there was a **ERROR** frame in the stream. By default,
       
    52  this raises a **StompError** exception with the error message.
       
    53 
       
    54 * **heartbeat_callback**: Called when a server heartbeat is received.
       
    55 
       
    56 * **message_callback**: Called when a **MESSAGE** frame is received.
       
    57 
       
    58 * **missed_heartbeat_callback**: Called when the Stomp socket is idle longer than
       
    59  the specified heartbeat time -- usually an indication of a problem.  The default behavior
       
    60  raises a **StompError** exception.
       
    61 
       
    62 * **receipt_callback**: Called when a **RECEIPT** frame is received.
       
    63 
       
    64 
       
    65 ### Custom headers ###
       
    66 
       
    67 Depending on the broker, you may be able to add addtional features to outgoing messages
       
    68 by adding specific headers.  You can also add "x-headers" that are carried between messages.
       
    69 
       
    70 Another use is to issue "receipts" on sends or subscriptions, to ensure the broker has
       
    71 processed your request.  Here's an example of how to perform receipt processing:
       
    72 
       
    73 ```
       
    74 #!nimrod
       
    75 proc accept_receipt( c: StompClient, r: StompResponse ) =
       
    76    var receipt = r[ "receipt-id" ]
       
    77    # ... match this receipt up to the request that generated it
       
    78 
       
    79 var client = newStompClient( socket, "..." )
       
    80 
       
    81 client.receipt_callback = accept_receipt
       
    82 client.connect
       
    83 
       
    84 var headers = seq[ tuple[name:string, value:string] ]
       
    85 headers.add( "x-breakfast", "tequila" )
       
    86 headers.add( "receipt", "special-identifier" )
       
    87 
       
    88 client.send( "/destination", "message!", "text/plain", headers )
       
    89 ```
       
    90 
       
    91 
       
    92 ### Transactions ###
       
    93 
       
    94 This library has full support for transactions.  Once entering a
       
    95 transaction, any messages or acknowledgments attached to it must be
       
    96 committed before the broker will release them.
       
    97 
       
    98 With one open transaction, messages are automatically attached to it.
       
    99 If you have multiple open transactions, you'll need to add which one
       
   100 you want a message to be part of via the custom headers.
       
   101 
       
   102 ```
       
   103 #!nimrod
       
   104 # Single transaction
       
   105 #
       
   106 client.begin( "trans-1" )
       
   107 client.send( "/destination", "hi" ) # Part of "trans-1"
       
   108 client.send( "/destination", "yo" ) # Part of "trans-1"
       
   109 client.send( "/destination", "whaddup" ) # Part of "trans-1"
       
   110 client.commit # or client.abort
       
   111 
       
   112 # Multiple simultaneous transactions
       
   113 #
       
   114 client.begin( "trans-1" )
       
   115 client.begin( "trans-2" )
       
   116 client.begin( "trans-3" )
       
   117 
       
   118 var headers = seq[ tuple[name:string, value:string] ]
       
   119 headers.add( "transaction", "trans-1" )
       
   120 client.send( "/destination", "hi", nil, headers ) # Part of "trans-1"
       
   121 
       
   122 headers = @[]
       
   123 headers.add( "transaction", "trans-2" )
       
   124 client.send( "/destination", "hi", nil, headers ) # Part of "trans-2"
       
   125 client.ack( "some-ack-id", "trans-2" ) # Part of "trans-2"
       
   126 
       
   127 headers = @[]
       
   128 headers.add( "transaction", "trans-3" )
       
   129 client.send( "/destination", "hi", nil, headers ) # Part of "trans-3"
       
   130 client.ack( "some-ack-id", "trans-3" ) # Part of "trans-3"
       
   131 
       
   132 client.abort( "trans-1" )  # anything "trans-1" never happened
       
   133 client.commit( "trans-2" ) # "trans-2" messages and acks are released
       
   134 client.abort( "trans-3" )  # anything "trans-3" never happened
       
   135 ```
       
   136 
       
   137 ### Example ###
       
   138 
       
   139 This is a complete client that does the following:
       
   140 
       
   141 * Connect to an AMQP server at **mq.example.com** via SSL as the **test** user,
       
   142   in the **/example** vhost.
       
   143 * Request heartbeats every **5** seconds.
       
   144 * Subscribe to a topic exchange **events** with the key of **user.create**, requiring message
       
   145   acknowledgement.
       
   146 * Accept incoming messages, parsing the JSON payloads.
       
   147 * If parsing was successful, ACK the message and emit a new message to the exchange
       
   148   with JSON results to the **user.created** key -- presumably to be picked up by another
       
   149   process elsewhere.
       
   150 
       
   151 ```
       
   152 #!nimrod
       
   153 # (This should be compiled with -d:ssl)
       
   154 
       
   155 import
       
   156    json,
       
   157    net,
       
   158    stomp
       
   159 
       
   160 let
       
   161    socket = newSocket()
       
   162    sslContext = newContext( verifyMode = CVerifyNone )
       
   163 
       
   164 sslContext.wrapSocket( socket )
       
   165 var client = newStompClient( socket, "stomp+ssl://test:test@mq.example.com/%2Fexample?heartbeat=5" )
       
   166 
       
   167 # Announce when we're connected.
       
   168 proc connected( c: StompClient, r: StompResponse ) =
       
   169    echo "Connected to a ", c["server"], " server."
       
   170 
       
   171 # Echo to screen when we see a heartbeat.
       
   172 proc heartbeat( c: StompClient, r: StompResponse ) =
       
   173    echo "Heartbeat at: ",  c.last_msgtime
       
   174 
       
   175 # Parse JSON, perform work, send success message.
       
   176 proc message( c: StompClient, r: StompResponse ) =
       
   177    let id = r[ "ack" ]
       
   178 
       
   179    if r[ "content-type" ] != "application/json":
       
   180 	   echo "I expect JSON payloads."
       
   181 	   c.nack( id )
       
   182 	   return
       
   183 
       
   184    try:
       
   185 	   var json = r.payload.parse_json
       
   186 
       
   187 	   # ... do the heavy lifting with the parsed data.
       
   188 	   # ... and assuming is was successful, ack and emit!
       
   189 	   
       
   190 	   c.ack( id )
       
   191 
       
   192 	   var message = %*{ "user": json["user"].getStr, "otherstuff": true }
       
   193 	   c.send( "/exchange/events/user.created", $message, "application/json" )
       
   194 
       
   195    except JsonParsingError:
       
   196 	   echo "Couldn't parse JSON! ", r.payload
       
   197 	   c.nack( id )
       
   198 
       
   199 # Attach callbacks
       
   200 client.connected_callback = connected
       
   201 client.message_callback = message
       
   202 client.heartbeat_callback = heartbeat
       
   203 
       
   204 # Open a session with the broker
       
   205 client.connect
       
   206 
       
   207 # Subscribe to a topic key, requiring acknowledgements.
       
   208 client.subscribe( "/exchange/events/user.create", "client-individual" )
       
   209 
       
   210 # Enter message loop.
       
   211 client.wait_for_messages
       
   212 ```
       
   213