Packages

Pure Erlang NATS Protocol Message Encoder/Decoder

Current section

3 Versions

Jump to

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