Current section
3 Versions
Jump to
Current section
3 Versions
Compare versions
4
files changed
+373
additions
-79
deletions
| @@ -5,7 +5,8 @@ | |
| 5 5 | [NATS](http://nats.io/) high performance messaging platform. |
| 6 6 | For details about NATS protocol, see: |
| 7 7 | [NATS Protocol](http://nats.io/documentation/internals/nats-protocol). It doesn't |
| 8 | - have any dependency other than Erlang/OTP install (16+ *should* be OK) and [rebar3](http://www.rebar3.org/). |
| 8 | + have any dependency other than Erlang/OTP (16+ *should* be OK) and optionally |
| 9 | + [rebar3](http://www.rebar3.org/). |
| 9 10 | |
| 10 11 | ## Install |
| 11 12 | |
| @@ -14,12 +15,16 @@ have any dependency other than Erlang/OTP install (16+ *should* be OK) and [reba | |
| 14 15 | it is available on [hex.pm](https://hex.pm/). Just include the following |
| 15 16 | in your `rebar.config`: |
| 16 17 | |
| 17 | - {deps[nats_msg]}. |
| 18 | + ```erlang |
| 19 | + {deps[nats_msg]}. |
| 20 | + ``` |
| 18 21 | |
| 19 22 | Alternatively (*for whatever reason you don't like to use hex.pm*): |
| 20 23 | |
| 21 | - {deps, [ |
| 22 | - {nat_msg, {git, "https://github.com/yuce/nats_msg.git", {branch, "master"}}. |
| 24 | + ```erlang |
| 25 | + {deps, [ |
| 26 | + {nat_msg, {git, "https://github.com/yuce/nats_msg.git", {branch, "master"}}. |
| 27 | + ``` |
| 23 28 | |
| 24 29 | Or, you can just copy `src/nats_msg.erl` to your project to use it. |
| 25 30 | |
| @@ -35,66 +40,118 @@ Run the tests using: | |
| 35 40 | |
| 36 41 | ## Usage |
| 37 42 | |
| 38 | - Binaries are used exclusively throught the library. Encoding a message produces a binary. Decoding a binary produces a `{Messages, RemainingBinary}` tuple. |
| 39 | - `Messages` is a list of messages and |
| 40 | - `RemainingBinary` is the part of the input which wasn't decoded and returned. The latter |
| 41 | - is very useful when dealing with streams, where the input is chunked and requires appending |
| 42 | - chunks to be able to decode messages. In those situations, just prepend `RemaningBinary` to |
| 43 | - the next binary chunk. |
| 43 | + Binaries are used exclusively throughout the library. |
| 44 44 | |
| 45 | - Currently, no error handling is performed. |
| 45 | + Currently, no error handling is performed during encoding/decoding. You can protect |
| 46 | + against crashes by wrapping library functions between `try...catch`. |
| 46 47 | |
| 47 | - ### INFO |
| 48 | + `INFO` and `CONNECT` messages have a JSON object as their parameter; but in order to |
| 49 | + not introduce a dependency, **nats_msg** does not encode/decode JSON objects. These parameters |
| 50 | + are kept or returned as binaries. You can use [jsx](https://github.com/talentdeficit/jsx) or [jiffy](https://github.com/davisp/jiffy) |
| 51 | + to deal with JSON. See the **INFO** and **CONNECT** sections in this document for examples. |
| 52 | + |
| 53 | + ### Encoding |
| 54 | + |
| 55 | + Encoding a message produces a binary. `nats_msg:encode/1` takes an atom as the name of the |
| 56 | + message or a tuple which contains the name, parameters and payload of the message. |
| 57 | + |
| 58 | + The general form of `nats_msg:encode/1` parameters is: |
| 59 | + |
| 60 | + * `Name :: atom()`: For messages taking no parameters, |
| 61 | + * `{Name :: atom(), Parameters :: {binary() | int(), ...}}` for messages taking parameters but |
| 62 | + not a payload, |
| 63 | + * `{Name :: atom(), Parameters :: {binary() | int(), ...}, Payload :: binary()}` for messages |
| 64 | + taking parameters and a payload. |
| 65 | + |
| 66 | + Messages of the same type always have the same structure, even if some of the values are |
| 67 | + `undefined`. Some examples: |
| 68 | + |
| 69 | + * `nats_msg:encode(ping)` produces a `PING` message, |
| 70 | + * `nats_msg:encode({sub, {<<"INBOX">>, undefined, <<"2">>}}` produces a `SUB` message with |
| 71 | + subject `<<"INBOX">>` and SID `<<"2">>`. This particular message has no *queue group*, so |
| 72 | + that field is set to `undefined`. |
| 73 | + * `nats_msg:encode({pub, {<<"FOO">>, undefined, 11}, <<"Hello NATS!">>}` produces a `PUB` message |
| 74 | + with subject `<<"FOO">>` and payload `<<"Hello NATS!">>` of size `11` and no *reply to* subject. |
| 75 | + |
| 76 | + The library has convenience functions for all messages, like `nats_msg:ping/0`, which are |
| 77 | + discussed later in this document. |
| 78 | + |
| 79 | + ### Decoding |
| 80 | + |
| 81 | + Decoding a binary produces a `{Messages, RemainingBinary}` tuple. |
| 82 | + `Messages` is a list of messages and `RemainingBinary` is the part of the input which |
| 83 | + wasn't decoded and returned. The latter is very useful when dealing with streams, where |
| 84 | + the input is chunked and appending chunks is required to be able to decode messages. |
| 85 | + In those situations, just prepend `RemainingBinary` to the next binary chunk before attempting |
| 86 | + to decode it. |
| 87 | + |
| 88 | + Messages in the `Messages` list can be used as inputs to `nats_msg:encode`, like: |
| 89 | + |
| 90 | + ```erlang |
| 91 | + SomeBinary = ... |
| 92 | + {[Msg], Remaining} = nats_msg:decode(SomeBinary), |
| 93 | + ReEncodedBinary = nats_msg:encode(Msg), |
| 94 | + % ReEncodedBinary = SomeBinary |
| 95 | + ``` |
| 96 | + |
| 97 | + Note that in this document, the message extraction code is written like the following for |
| 98 | + convenience: |
| 99 | + |
| 100 | + ```erlang |
| 101 | + {[Msg], Remaining} = nats_msg:decode(SomeBinary), |
| 102 | + ``` |
| 103 | + |
| 104 | + That will work if there is only 1 decodable message in the input, and will cause a crash |
| 105 | + if there are more. The correct way of handling messages is: |
| 106 | + |
| 107 | + ```erlang |
| 108 | + {Messages, Remaining} = nats_msg:decode(SomeBinary), |
| 109 | + % Operate on Messages |
| 110 | + ``` |
| 111 | + |
| 112 | + ### INFO Message |
| 48 113 | |
| 49 114 | [NATS Spec](http://nats.io/documentation/internals/nats-protocol/#INFO) |
| 50 115 | |
| 51 116 | #### Encode |
| 52 117 | |
| 53 | - This message requires a JSON object but in order to not introduce a dependency, `nats_msg:info/1` |
| 54 | - takes a binary. You can use [jsx](https://github.com/talentdeficit/jsx) or [jiffy](https://github.com/davisp/jiffy) |
| 55 | - to convert an Erlang map to binary: |
| 56 | - |
| 57 | - ServerInfo = #{<<"auth_required">> => true, <<"server_id">> => <<"0001-SERVER">>}, |
| 58 | - BinaryInfo = jsx:encode(ServerInfo), |
| 59 | - BinaryMsg = nats_msg:info(BinaryInfo). |
| 118 | + ```erlang |
| 119 | + ServerInfo = #{<<"auth_required">> => true, <<"server_id">> => <<"0001-SERVER">>}, |
| 120 | + BinaryInfo = jsx:encode(ServerInfo), |
| 121 | + BinaryMsg = nats_msg:info(BinaryInfo). |
| 122 | + ``` |
| 60 123 | |
| 61 124 | #### Decode |
| 62 125 | |
| 63 | - This message requires a JSON object but in order to not introduce a dependency, `nats_msg:decode/1` |
| 64 | - returns `{info, BinaryInfo}` messages. You can use [jsx](https://github.com/talentdeficit/jsx) or [jiffy](https://github.com/davisp/jiffy) |
| 65 | - to convert `BinaryInfo` to a map: |
| 126 | + ```erlang |
| 127 | + Chunk = <<"INFO {\"auth_required\":true,\"server_id\":\"0001-SERVER\"}\r\n">>, |
| 128 | + {[Msg], _} = nats_msg:decode(Chunk), |
| 129 | + {info, BinaryInfo} = Msg, |
| 130 | + ServerInfo = jsx:decode(BinaryInfo, [return_maps]). |
| 131 | + ``` |
| 66 132 | |
| 67 | - Chunk = <<"INFO {\"auth_required\":true,\"server_id\":\"0001-SERVER\"}\r\n">>, |
| 68 | - {[Msg], _} = nats_msg:decode(Chunk), |
| 69 | - {info, BinaryInfo} = Msg, |
| 70 | - ServerInfo = jsx:decode(BinaryInfo, [return_maps]). |
| 71 | - |
| 72 | - ### CONNECT |
| 133 | + ### CONNECT Message |
| 73 134 | |
| 74 135 | [NATS Spec](http://nats.io/documentation/internals/nats-protocol/#CONNECT) |
| 75 136 | |
| 76 137 | #### Encode |
| 77 138 | |
| 78 | - This message requires a JSON object but in order to not introduce a dependency, `nats_msg:connect/1` |
| 79 | - takes a binary. You can use [jsx](https://github.com/talentdeficit/jsx) or [jiffy](https://github.com/davisp/jiffy) |
| 80 | - to convert an Erlang map to binary: |
| 81 | - |
| 82 | - ConnectInfo = #{<<"auth_required">> => true, <<"server_id">> => <<"0001-SERVER">>}, |
| 83 | - BinaryInfo = jsx:encode(ServerInfo), |
| 84 | - BinaryMsg = nats_msg:connect(BinaryInfo). |
| 139 | + ```erlang |
| 140 | + ConnectInfo = #{<<"auth_required">> => true, <<"server_id">> => <<"0001-SERVER">>}, |
| 141 | + BinaryInfo = jsx:encode(ServerInfo), |
| 142 | + BinaryMsg = nats_msg:connect(BinaryInfo). |
| 143 | + ``` |
| 85 144 | |
| 86 145 | #### Decode |
| 87 146 | |
| 88 | - This message requires a JSON object but in order to not introduce a dependency, `nats_msg:decode/1` |
| 89 | - returns `{connect, BinaryInfo}` messages. You can use [jsx](https://github.com/talentdeficit/jsx) or [jiffy](https://github.com/davisp/jiffy) |
| 90 | - to convert `BinaryInfo` to a map: |
| 147 | + ```erlang |
| 148 | + Chunk = <<"CONNECT {\"verbose\":true,\"name\":\"the_client\"}\r\n">>, |
| 149 | + {[Msg], _} = nats_msg:decode(Chunk), |
| 150 | + {connect, BinaryInfo} = Msg, |
| 151 | + ClientInfo = jsx:decode(BinaryInfo, [return_maps]). |
| 152 | + ``` |
| 91 153 | |
| 92 | - Chunk = <<"CONNECT {\"verbose\":true,\"name\":\"the_client\"}\r\n">>, |
| 93 | - {[Msg], _} = nats_msg:decode(Chunk), |
| 94 | - {connect, BinaryInfo} = Msg, |
| 95 | - ClientInfo = jsx:decode(BinaryInfo, [return_maps]). |
| 96 | - |
| 97 | - ### PUB |
| 154 | + ### PUB Message |
| 98 155 | |
| 99 156 | [NATS Spec](http://nats.io/documentation/internals/nats-protocol/#PUB) |
| 100 157 | |
| @@ -102,62 +159,254 @@ to convert `BinaryInfo` to a map: | |
| 102 159 | |
| 103 160 | Notify subscribers of a subject: |
| 104 161 | |
| 105 | - BinaryMsg = nats_msg:pub(<<"NOTIFY.INBOX">>). |
| 162 | + ```erlang |
| 163 | + BinaryMsg = nats_msg:pub(<<"NOTIFY.INBOX">>). |
| 164 | + ``` |
| 106 165 | |
| 107 166 | Send some data (*payload*) to subscribers, providing a *reply* subject: |
| 108 167 | |
| 109 | - BinaryMsg = nats_msg:pub(<<"FOOBAR">>, <<"REPRAP">>, <<"Hello, World!">>). |
| 168 | + ```erlang |
| 169 | + BinaryMsg = nats_msg:pub(<<"FOOBAR">>, <<"REPRAP">>, <<"Hello, World!">>). |
| 170 | + ``` |
| 110 171 | |
| 111 172 | Send some data (*payload*) to subscribers (*without a reply subject*): |
| 112 173 | |
| 113 | - BinaryMsg = nats_msg:pub(<<"FOOBAR">>, undefined, <<"Hello, World!">>). |
| 174 | + ```erlang |
| 175 | + BinaryMsg = nats_msg:pub(<<"FOOBAR">>, undefined, <<"Hello, World!">>). |
| 176 | + ``` |
| 114 177 | |
| 115 178 | ### Decode |
| 116 179 | |
| 117 | - Notification: |
| 180 | + Publish notification: |
| 118 181 | |
| 119 | - Chunk = <<"PUB NOTIFY 0\r\n\r\n">>, |
| 120 | - {[Msg], _} = nats_msg:decode(Chunk), |
| 121 | - {pub, {Subject, ReplyTo, PayloadSize}, Payload} = Msg, |
| 122 | - % Subject = <<"NOTIFY">>, |
| 123 | - % ReplyTo = undefined, |
| 124 | - % PayloadSize = 0, |
| 125 | - % Payload = <<>>. |
| 182 | + ```erlang |
| 183 | + Chunk = <<"PUB NOTIFY 0\r\n\r\n">>, |
| 184 | + {[Msg], _} = nats_msg:decode(Chunk), |
| 185 | + {pub, {Subject, ReplyTo, PayloadSize}, Payload} = Msg, |
| 186 | + % Subject = <<"NOTIFY">>, |
| 187 | + % ReplyTo = undefined, |
| 188 | + % PayloadSize = 0, |
| 189 | + % Payload = <<>>. |
| 190 | + ``` |
| 126 191 | |
| 127 | - Publish message with replier and payload: |
| 192 | + Publish message with subject, replier and payload: |
| 128 193 | |
| 129 | - Chunk = <<"PUB FRONT.DOOR INBOX.22 11\r\nKnock Knock\r\n">>, |
| 130 | - {[Msg], _} = nats_msg:decode(Chunk), |
| 131 | - {pub, {Subject, ReplyTo, PayloadSize}, Payload} = Msg, |
| 132 | - % Subject = <<"FRONT.DOOR">>, |
| 133 | - % ReplyTo = <<"INBOX.22">>, |
| 134 | - % PayloadSize = 11, |
| 135 | - % Payload = <<"Knock Knock">>. |
| 194 | + ```erlang |
| 195 | + Chunk = <<"PUB FRONT.DOOR INBOX.22 11\r\nKnock Knock\r\n">>, |
| 196 | + {[Msg], _} = nats_msg:decode(Chunk), |
| 197 | + {pub, {Subject, ReplyTo, PayloadSize}, Payload} = Msg, |
| 198 | + % Subject = <<"FRONT.DOOR">>, |
| 199 | + % ReplyTo = <<"INBOX.22">>, |
| 200 | + % PayloadSize = 11, |
| 201 | + % Payload = <<"Knock Knock">>. |
| 202 | + ``` |
| 136 203 | |
| 137 | - ### SUB |
| 204 | + ### SUB Message |
| 138 205 | |
| 139 206 | [NATS Spec](http://nats.io/documentation/internals/nats-protocol/#SUB) |
| 140 207 | |
| 141 | - ### UNSUB |
| 208 | + #### Encode |
| 209 | + |
| 210 | + Subscribe message with subject and SID: |
| 211 | + |
| 212 | + ```erlang |
| 213 | + BinaryMsg = nats_msg:sub(<<"FOO">>, <<"1">>). |
| 214 | + ``` |
| 215 | + |
| 216 | + Subscribe message with subject, group queue and SID: |
| 217 | + |
| 218 | + ```erlang |
| 219 | + BinaryMsg = nats_msg:sub(<<"BAR">>, <<"G1">>, <<"44">>) |
| 220 | + ``` |
| 221 | + |
| 222 | + #### Decode |
| 223 | + |
| 224 | + ```erlang |
| 225 | + Chunk = <<"SUB FOO 1\r\n">>, |
| 226 | + {[Msg], _} = nats_msg:decode(Chunk), |
| 227 | + {sub, {Subject, GroupQueue, Sid}} = Msg, |
| 228 | + % Subject = <<"FOO">>, |
| 229 | + % GroupQueue = undefined, |
| 230 | + % Sid = <<"1">>. |
| 231 | + ``` |
| 232 | + |
| 233 | + ### UNSUB Message |
| 142 234 | |
| 143 235 | [NATS Spec](http://nats.io/documentation/internals/nats-protocol/#UNSUB) |
| 144 236 | |
| 145 | - ### MSG |
| 237 | + #### Encode |
| 238 | + |
| 239 | + Unsubscribe message with SID: |
| 240 | + |
| 241 | + ```erlang |
| 242 | + BinaryMsg = nats_msg:unsub(<<"1">>). |
| 243 | + ``` |
| 244 | + |
| 245 | + Unsubscribe message with SID and *max messages*: |
| 246 | + |
| 247 | + ```erlang |
| 248 | + BinaryMsg = nats_msg:unsub(<<"1">>, 10). |
| 249 | + ``` |
| 250 | + |
| 251 | + #### Decode |
| 252 | + |
| 253 | + ```erlang |
| 254 | + Chunk = <<"UNSUB 1 10\r\n">>, |
| 255 | + {[Msg], _} = nats_msg:decode(Chunk), |
| 256 | + {unsub, {Sid, MaxMessages}} = Msg, |
| 257 | + % Sid = <<"1">>, |
| 258 | + % MaxMessages = 10 |
| 259 | + ``` |
| 260 | + |
| 261 | + ### MSG Message |
| 146 262 | |
| 147 263 | [NATS Spec](http://nats.io/documentation/internals/nats-protocol/#MSG) |
| 148 264 | |
| 149 | - ### PING |
| 265 | + #### Encode |
| 266 | + |
| 267 | + Message with subject and SID: |
| 268 | + |
| 269 | + ```erlang |
| 270 | + BinaryMsg = nats_msg:msg(<<"FOO">>, <<"5">>). |
| 271 | + ``` |
| 272 | + |
| 273 | + Message with subject, sid, *reply to subject* and payload: |
| 274 | + |
| 275 | + ```erlang |
| 276 | + BinaryMsg = nats_msg:msg(<<"FOO">>, <<"5">>, <<"INBOX">>, <<"Hello!">>). |
| 277 | + ``` |
| 278 | + |
| 279 | + Message with subject, sid and payload: |
| 280 | + |
| 281 | + ```erlang |
| 282 | + BinaryMsg = nats_msg:msg(<<"FOO">>, <<"5">>, undefined, <<"Hello!">>). |
| 283 | + ``` |
| 284 | + |
| 285 | + #### Decode |
| 286 | + |
| 287 | + Message with subject, sid and payload: |
| 288 | + |
| 289 | + ```erlang |
| 290 | + Chunk = <<"MSG FOO.BAR 9 13\r\nHello, World!\r\n">>, |
| 291 | + {[Msg], _} = nats_msg:decode(Chunk), |
| 292 | + {msg, {Subject, Sid, ReplyTo, 13}, Payload} = Msg, |
| 293 | + % Subject = <<"FOO.BAR">>, |
| 294 | + % Sid = <<"9">>, |
| 295 | + % ReplyTo = undefined, |
| 296 | + % Payload = <<"Hello, World!">>. |
| 297 | + ``` |
| 298 | + |
| 299 | + ### PING Message |
| 150 300 | |
| 151 301 | [NATS Spec](http://nats.io/documentation/internals/nats-protocol/#PING) |
| 152 302 | |
| 153 | - ### PONG |
| 303 | + #### Encode |
| 304 | + |
| 305 | + ```erlang |
| 306 | + BinaryMsg = nats_msg:ping(). |
| 307 | + ``` |
| 308 | + |
| 309 | + #### Decode |
| 310 | + |
| 311 | + ```erlang |
| 312 | + {[Msg], _} = nats_msg:decode(<<"PING\r\n">>), |
| 313 | + % Msg = ping |
| 314 | + ``` |
| 315 | + |
| 316 | + ### PONG Message |
| 154 317 | |
| 155 318 | [NATS Spec](http://nats.io/documentation/internals/nats-protocol/#PONG) |
| 156 319 | |
| 157 | - ### +OK |
| 320 | + #### Encode |
| 321 | + |
| 322 | + ```erlang |
| 323 | + BinaryMsg = nats_msg:pong(). |
| 324 | + ``` |
| 325 | + |
| 326 | + #### Decode |
| 327 | + |
| 328 | + ```erlang |
| 329 | + {[Msg], _} = nats_msg:decode(<<"PONG\r\n">>), |
| 330 | + % Msg = pong |
| 331 | + ``` |
| 332 | + |
| 333 | + ### +OK Message |
| 158 334 | |
| 159 335 | [NATS Spec](http://nats.io/documentation/internals/nats-protocol/#OKERR) |
| 160 336 | |
| 161 | - ### -ERR |
| 337 | + #### Encode |
| 162 338 | |
| 163 | - [NATS Spec](http://nats.io/documentation/internals/nats-protocol/#OKERR) |
| \ No newline at end of file | |
| 339 | + ```erlang |
| 340 | + BinaryMsg = nats_msg:ok(). |
| 341 | + ``` |
| 342 | + |
| 343 | + #### Decode |
| 344 | + |
| 345 | + ```erlang |
| 346 | + {[Msg], _} = nats_msg:decode(<<"+OK\r\n">>), |
| 347 | + % Msg = ok |
| 348 | + ``` |
| 349 | + |
| 350 | + ### -ERR Message |
| 351 | + |
| 352 | + [NATS Spec](http://nats.io/documentation/internals/nats-protocol/#OKERR) |
| 353 | + |
| 354 | + The spec defines a predefined set of error messages, so **nats_msg** encodes/decodes these |
| 355 | + to/from atoms as: |
| 356 | + |
| 357 | + * `'Unknown Protocol Operation'` => `unknown_operation` |
| 358 | + * `'Authorization Violation'` => `auth_violation` |
| 359 | + * `'Authorization Timeout'` => `auth_timeout` |
| 360 | + * `'Parser Error'` => `parser_error` |
| 361 | + * `'Stale Connection'` => `stale_connection` |
| 362 | + * `'Slow Consumer'` => `slow_consumer` |
| 363 | + * `'Maximum Payload Exceeded'` => `max_payload` |
| 364 | + * `'Invalid Subject'` => `invalid_subject` |
| 365 | + * Other errors are converted to `unknown_error` during decoding and kept as is during encoding. |
| 366 | + |
| 367 | + #### Encode |
| 368 | + |
| 369 | + ```erlang |
| 370 | + BinaryMsg = nats_msg:err(auth_violation). |
| 371 | + ``` |
| 372 | + |
| 373 | + #### Decode |
| 374 | + |
| 375 | + ```erlang |
| 376 | + Chunk = <<"-ERR 'Authorization Timeout'\r\n">>, |
| 377 | + {[Msg], _} = nats_msg:decode(Chunk), |
| 378 | + {err, Error} = Msg, |
| 379 | + % Error = auth_timeout |
| 380 | + ``` |
| 381 | + |
| 382 | + ## License |
| 383 | + |
| 384 | + Copyright (c) 2016, Yuce Tekol <yucetekol@gmail.com>. |
| 385 | + All rights reserved. |
| 386 | + |
| 387 | + Redistribution and use in source and binary forms, with or without |
| 388 | + modification, are permitted provided that the following conditions are |
| 389 | + met: |
| 390 | + |
| 391 | + * Redistributions of source code must retain the above copyright |
| 392 | + notice, this list of conditions and the following disclaimer. |
| 393 | + |
| 394 | + * Redistributions in binary form must reproduce the above copyright |
| 395 | + notice, this list of conditions and the following disclaimer in the |
| 396 | + documentation and/or other materials provided with the distribution. |
| 397 | + |
| 398 | + * The names of its contributors may not be used to endorse or promote |
| 399 | + products derived from this software without specific prior written |
| 400 | + permission. |
| 401 | + |
| 402 | + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 403 | + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 404 | + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 405 | + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 406 | + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 407 | + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 408 | + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 409 | + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 410 | + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 411 | + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 412 | + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| @@ -1,12 +1,12 @@ | |
| 1 1 | {<<"name">>,<<"nats_msg">>}. |
| 2 | - {<<"version">>,<<"0.3.1">>}. |
| 2 | + {<<"version">>,<<"0.3.3">>}. |
| 3 3 | {<<"app">>,<<"nats_msg">>}. |
| 4 4 | {<<"maintainers">>,[<<"Yuce Tekol">>]}. |
| 5 5 | {<<"precompiled">>,false}. |
| 6 6 | {<<"description">>,<<"Pure Erlang NATS Protocol Message Encoder/Decoder">>}. |
| 7 7 | {<<"files">>, |
| 8 8 | [{<<"src/nats_msg.app.src">>, |
| 9 | - <<"{application,nats_msg,\n [{description,\"Pure Erlang NATS Protocol Message Encoder/Decoder\"},\n {vsn,\"0.3.1\"},\n {registered,[]},\n {applications,[kernel,stdlib]},\n {env,[]},\n {modules,[]},\n {maintainers,[\"Yuce Tekol\"]},\n {licenses,[\"MIT\"]},\n {links,[{\"Github\",\"https://github.com/yuce/nats_msg\"}]}]}.\n">>}, |
| 9 | + <<"{application,nats_msg,\n [{description,\"Pure Erlang NATS Protocol Message Encoder/Decoder\"},\n {vsn,\"0.3.3\"},\n {registered,[]},\n {applications,[kernel,stdlib]},\n {env,[]},\n {modules,[]},\n {maintainers,[\"Yuce Tekol\"]},\n {licenses,[\"MIT\"]},\n {links,[{\"Github\",\"https://github.com/yuce/nats_msg\"}]}]}.\n">>}, |
| 10 10 | {<<"src/nats_msg.app.src">>, |
| 11 11 | <<"/Users/yuce/Projects/nats_msg/src/nats_msg.app.src">>}, |
| 12 12 | {<<"src/nats_msg.erl">>, |
| @@ -1,6 +1,6 @@ | |
| 1 1 | {application, 'nats_msg', |
| 2 2 | [{description, "Pure Erlang NATS Protocol Message Encoder/Decoder"}, |
| 3 | - {vsn, "0.3.1"}, |
| 3 | + {vsn, "0.3.3"}, |
| 4 4 | {registered, []}, |
| 5 5 | {applications, |
| 6 6 | [kernel, |
| @@ -1,3 +1,35 @@ | |
| 1 | + % |
| 2 | + % Copyright (c) 2016, Yuce Tekol <yucetekol@gmail.com>. |
| 3 | + % All rights reserved. |
| 4 | + |
| 5 | + % Redistribution and use in source and binary forms, with or without |
| 6 | + % modification, are permitted provided that the following conditions are |
| 7 | + % met: |
| 8 | + |
| 9 | + % * Redistributions of source code must retain the above copyright |
| 10 | + % notice, this list of conditions and the following disclaimer. |
| 11 | + |
| 12 | + % * Redistributions in binary form must reproduce the above copyright |
| 13 | + % notice, this list of conditions and the following disclaimer in the |
| 14 | + % documentation and/or other materials provided with the distribution. |
| 15 | + |
| 16 | + % * The names of its contributors may not be used to endorse or promote |
| 17 | + % products derived from this software without specific prior written |
| 18 | + % permission. |
| 19 | + |
| 20 | + % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 21 | + % "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 22 | + % LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 23 | + % A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 24 | + % OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 25 | + % SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 26 | + % LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 27 | + % DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 28 | + % THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 29 | + % (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 30 | + % OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 31 | + % |
| 32 | + |
| 1 33 | -module(nats_msg). |
| 2 34 | -author("Yuce Tekol"). |
| 3 35 | |
| @@ -52,7 +84,7 @@ encode(ping) -> encode(ping, [], undefined); | |
| 52 84 | encode(pong) -> encode(pong, [], undefined); |
| 53 85 | encode(ok) -> encode(ok, [], undefined); |
| 54 86 | |
| 55 | - encode({err, unknown_protocol}) -> |
| 87 | + encode({err, unknown_operation}) -> |
| 56 88 | encode(err, [<<"'Unknown Protocol Operation'">>], undefined); |
| 57 89 | |
| 58 90 | encode({err, auth_violation}) -> |
| @@ -210,9 +242,9 @@ extract_msg(Bin, MsgAcc) -> | |
| 210 242 | split_msg(Bin) -> |
| 211 243 | Ret = case binary:match(Bin, ?SEPLIST) of |
| 212 244 | nomatch -> |
| 213 | - make_msg(bin_to_name(Bin), <<>>); |
| 245 | + make_msg(bin_to_name(upper_case(Bin)), <<>>); |
| 214 246 | {Pos, Len} -> |
| 215 | - Name = bin_to_name(binary:part(Bin, {0, Pos})), |
| 247 | + Name = bin_to_name(upper_case(binary:part(Bin, {0, Pos}))), |
| 216 248 | Rest = binary:part(Bin, {Pos + Len, byte_size(Bin) - (Pos + Len)}), |
| 217 249 | make_msg(Name, Rest) |
| 218 250 | end, |
| @@ -289,7 +321,7 @@ bin_to_name(<<"PONG">>) -> pong; | |
| 289 321 | bin_to_name(<<"+OK">>) -> ok; |
| 290 322 | bin_to_name(<<"-ERR">>) -> err. |
| 291 323 | |
| 292 | - err_to_atom(<<"'Unknown Protocol Operation'">>) -> unknown_protocol; |
| 324 | + err_to_atom(<<"'Unknown Protocol Operation'">>) -> unknown_operation; |
| 293 325 | err_to_atom(<<"'Authorization Violation'">>) -> auth_violation; |
| 294 326 | err_to_atom(<<"'Authorization Timeout'">>) -> auth_timeout; |
| 295 327 | err_to_atom(<<"'Parser Error'">>) -> parser_error; |
| @@ -299,6 +331,9 @@ err_to_atom(<<"'Maximum Payload Exceeded'">>) -> max_payload; | |
| 299 331 | err_to_atom(<<"'Invalid Subject'">>) -> invalid_subject; |
| 300 332 | err_to_atom(_) -> unknown_error. |
| 301 333 | |
| 334 | + upper_case(Bin) -> |
| 335 | + list_to_binary(string:to_upper(binary_to_list(Bin))). |
| 336 | + |
| 302 337 | %% == Tests |
| 303 338 | |
| 304 339 | -ifdef(TEST). |
| @@ -469,7 +504,7 @@ dec_nl_in_payload_test() -> | |
| 469 504 | ?assertEqual(E, M), |
| 470 505 | ?assertEqual(Rem, <<>>). |
| 471 506 | |
| 472 | - decl_incomplete_payload_test() -> |
| 507 | + dec_incomplete_payload_test() -> |
| 473 508 | B = <<"SUB BAR 3\r\nPUB FOO 12\r\nHello\r\nNATS!">>, |
| 474 509 | {Msgs, Rem} = decode(B), |
| 475 510 | ?assertEqual(1, length(Msgs)), |
| @@ -478,7 +513,17 @@ decl_incomplete_payload_test() -> | |
| 478 513 | E2 = <<"PUB FOO 12\r\nHello\r\nNATS!">>, |
| 479 514 | ?assertEqual(E2, Rem). |
| 480 515 | |
| 516 | + dec_case_insensitive_op_name_1_test() -> |
| 517 | + {[R1], _} = decode(<<"PING\r\n">>), |
| 518 | + {[R2], _} = decode(<<"PinG\r\n">>), |
| 519 | + ?assertEqual(ping, R1), |
| 520 | + ?assertEqual(ping, R2). |
| 481 521 | |
| 522 | + dec_case_insensitive_op_name_2_test() -> |
| 523 | + {[{R1, _}], _} = decode(<<"SUB FOO 1\r\n">>), |
| 524 | + {[{R2, _}], _} = decode(<<"sub FOO 1\r\n">>), |
| 525 | + ?assertEqual(sub, R1), |
| 526 | + ?assertEqual(sub, R2). |
| 482 527 | |
| 483 528 | % == Other Tests |