protocols.tex 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. \section{Protocols}
  2. N2O is more that just web framework or even application server.
  3. It also has protocol specification that covers broad range of application domains.
  4. In this chapter we go deep inside network capabilities of N2O communications.
  5. N2O protocol also has an ASN.1 formal description, however here we will speak on it freely.
  6. Here is the landscape of N2O protocols stack.
  7. \includeimage{n2o-proto.png}{Protocols Stack}
  8. \paragraph{}
  9. You may find it similar to XML-based XMPP, binary COM/CORBA,
  10. JSON-based WAMP, Apache Camel or Microsoft WCF communication foundations.
  11. We took best from all and put into one protocols stack for web,
  12. social and enterprise domains providing stable and mature implementation for Erlang
  13. in a form of N2O application server.
  14. \newpage
  15. \subsection*{Cross Language Compatibility}
  16. N2O application server implemented to support N2O protocol definition
  17. in Erlang which is widely used in enterprise applications.
  18. Experimental implementation in Haskell {\bf n2o.hs} exists
  19. which supports only core {\bf heart} protocol along with {\bf bert} formatter.
  20. We will show you how N2O clients are compatible across
  21. different server implementations in different languages.
  22. \subsection*{Web Protocols: {\bf nitro}, {\bf spa}, {\bf bin}}
  23. N2O protocols stack provides definition for several unoverlapped protocol layers.
  24. N2O application server implementation of N2O protocol specification supports
  25. four protocol layers from this stack for WebSocket and IoT applications:
  26. {\bf heart}, {\bf nitro}, {\bf spa} and {\bf bin} protocols.
  27. HEART protocol is designed for reliable managed connections and stream channel initialization.
  28. The domain of NITRO protocol is HTML5 client/server interoperability, HTML events and JavaScript delivery.
  29. SPA protocol dedicated for games and static page applications that involves no HTML,
  30. such as SVG based games or non-gui IoT applications.
  31. And finally binary file transfer protocol for images and gigabyte file uploads and downloads.
  32. All these protocols transfers coexist in the same multi-channel stream.
  33. \subsection*{Social Protocols: {\bf roster}, {\bf muc}, {\bf search}}
  34. For social connectivity one may need to use {\bf synrc/roster} instant messaging server
  35. that supports {\bf roster} protocol with variation
  36. for enabling public rooms {\bf muc} or full-text {\bf search} facilities.
  37. \subsection*{Enterprise Protocols: {\bf bpe}, {\bf mq}, {\bf rest}}
  38. There is no single system shipped to support all of N2O protocols but it
  39. could exist theoretically. For other protocols implementation you may refer
  40. to other products like {\bf spawnproc/bpe}, {\bf synrc/rest} or {\bf synrc/mq}.
  41. \newpage
  42. \subsection*{Channel Termination Formatters}
  43. N2O protocol is formatter agnostic and it doesn't strict you
  44. to use a particular encoder/decoder.
  45. Application developers could choose their own formatter per protocol.
  46. \vspace{1\baselineskip}
  47. \begin{lstlisting}
  48. 1. BERT : {io,"fire();",1}
  49. 2. WAMP : [io,"fire();",1]
  50. 3. JSON : {name:io,eval:"fire();",data:1}
  51. 4. TEXT : IO \xFF fire(); \xFF 1\n
  52. 5. XML : <io><eval>fire();</eval><data>1</data></io>
  53. \end{lstlisting}
  54. \vspace{1\baselineskip}
  55. E.g. N2O uses TEXT formatting for ``PING'' and ``N2O,'' protocol messages,
  56. across versions N2O used to have IO message formatted with JSON and BERT both.
  57. All other protocol messages were BERT from origin.
  58. Make sure formatters set for client and server is compatible.
  59. \vspace{1\baselineskip}
  60. \begin{lstlisting}
  61. #cx{formatter=bert}.
  62. \end{lstlisting}
  63. \vspace{1\baselineskip}
  64. Note that you may include to support more that one protocol on the client.
  65. At server side you can change formatter on the fly without breaking
  66. the channel stream. Each message during data stream could be formatted
  67. using only one protocol at a time. If you want to pass each message
  68. through more that one formatter you should write an echo protocol.
  69. \vspace{1\baselineskip}
  70. \begin{lstlisting}
  71. <script src='/n2o/protocols/bert.js'></script>
  72. <script src='/n2o/protocols/client.js'></script>
  73. <script>protos = [ $bert, $client ]; N2O_start();</script>
  74. \end{lstlisting}
  75. \newpage
  76. \subsection*{Protocol Loop}
  77. After message arrives to endpoint and handlers chain is being initializes,
  78. message then comes to protocol stack. N2O selects appropriative protocol
  79. module and handle the message. After than message is being formatted and
  80. replied back to stream channel. Note that protocol loop is applicable
  81. only to WebSocket stream channel endpoint.
  82. \includeimage{n2o_protocols.png}{Messaging Pipeline}
  83. \paragraph{}
  84. Here is pseudocode how message travels for each protocol until some
  85. of them handle the message. Note tnat this logic is subject to change.
  86. \vspace{1\baselineskip}
  87. \begin{lstlisting}[caption=Top-level protocol loop in {n2o}\_{proto}]
  88. reply(M,R,S) -> {reply,M,R,S}.
  89. nop(R,S) -> {reply,<<>>,R,S}.
  90. push(_,R,S,[],_Acc) -> nop(R,S);
  91. push(M,R,S,[H|T],Acc) ->
  92. case H:info(M,R,S) of
  93. {unknown,_,_,_} -> push(M,R,S,T,Acc);
  94. {reply,M1,R1,S1} -> reply(M1,R1,S1);
  95. A -> push(M,R,S,T,[A|Acc]) end.
  96. \end{lstlisting}
  97. \vspace{1\baselineskip}
  98. \newpage
  99. \subsection*{Enabling Protocols}
  100. You may set up protocol from sys.config file,
  101. enabling or disabling some of them on the fly.
  102. \vspace{1\baselineskip}
  103. \begin{lstlisting}
  104. protocols() ->
  105. wf:config(n2o,protocols,[ n2o_heart,
  106. n2o_nitrogen,
  107. n2o_client,
  108. n2o_file ]).
  109. \end{lstlisting}
  110. \vspace{1\baselineskip}
  111. For example in Skyline (DSL) application you use only {\bf nitro} protocol:
  112. \vspace{1\baselineskip}
  113. \begin{lstlisting}
  114. > wf:config(n2o,protocols).
  115. [n2o_heart,n2o_nitrogen]
  116. \end{lstlisting}
  117. \vspace{1\baselineskip}
  118. And in Games (SPA) application you need only {\bf spa} protocol:
  119. \vspace{1\baselineskip}
  120. \begin{lstlisting}
  121. > wf:config(n2o,protocols).
  122. [n2o_heart,n2o_client]
  123. \end{lstlisting}
  124. \vspace{1\baselineskip}
  125. \newpage
  126. \subsection{HEART}
  127. HEART protocol is essential WebSocket application level protocol for PING and N2O initialization.
  128. It pings every 4-5 seconds from client-side to server thus allowing to
  129. determine client online presence. On reconnection or initial connect
  130. client sends N2O init marker telling to server to reinitialize the context.
  131. \paragraph{}
  132. The {\bf heart} protocol defined client originated messages N2O, PING
  133. and server originated messages PONG, IO and NOP. IO message contains EVAL
  134. that contains UTF-8 JavaScript string and DATA reply contains any
  135. binary string, including BERT encoded data. "PING" and "N2O," are
  136. defined as text 4-bytes messages and second could be followed by
  137. any text string. NOP is 0-byte acknowledging packet.
  138. This is heart essence protocol which is enough for any rpc and code
  139. transferring interface. Normally heart protocol is not for active
  140. client usage but for supporting active connection with notifications
  141. and possibly DOM updates.
  142. \subsection*{Session Initialization}
  143. After page load you should start N2O session in JavaScript with configured
  144. formatters and starting function that will start message loop on the client:
  145. \vspace{1\baselineskip}
  146. \begin{lstlisting}
  147. var transition = {pid: '', host: 'localhost', port:'8000'};
  148. protos = [ $bert, $client ];
  149. N2O_start();
  150. \end{lstlisting}
  151. \vspace{1\baselineskip}
  152. If {\bf pid} field is not set in {\bf transition} variable then you
  153. will request new session otherwise you may put here information from
  154. previously settled cookies for attaching to existing session. This {\bf pid}
  155. disregarding set or empty will be bypassed as a parameter to N2O init marker.
  156. You can manually invoke session initialization inside existing session:
  157. \vspace{1\baselineskip}
  158. \begin{lstlisting}
  159. ws.send('N2O,');
  160. \end{lstlisting}
  161. \newpage
  162. In response on successful WebSocket connection and enabled {\bf heart}
  163. protocol on the server you will receive the IO message event.
  164. IO events are containers for function and data which can be used as parameters.
  165. There is no predefined semantic to IO message. Second element of a tuple
  166. will be directly evaluated in WebBrowser. Third element can contain data or error
  167. as for SPA and BIN protocols, and can contain only error for NITRO protocol.
  168. IO events are not constructed on client. N2O request returns IO messages with
  169. evaluation string and empty data or empty evaluation string
  170. with error in data field.
  171. \vspace{1\baselineskip}
  172. \begin{lstlisting}
  173. issue TEXT N2O expect IO
  174. N2O is TEXT "N2O," ++ PID
  175. PID is TEXT "" or any
  176. IO is BERT {io,<<>>,Error}
  177. or {io,Eval,<<>>}
  178. \end{lstlisting}
  179. \vspace{1\baselineskip}
  180. \subsection*{Online Presence}
  181. \vspace{1\baselineskip}
  182. \begin{lstlisting}
  183. ws.send('PING');
  184. \end{lstlisting}
  185. \vspace{1\baselineskip}
  186. You can try manually send this messag in web console to see whats happening,
  187. also you can enable logging the heartbeat protocol by including its
  188. module in {\bf log\_modules}:
  189. \vspace{1\baselineskip}
  190. \begin{lstlisting}
  191. log_modules() -> [n2o_heart].
  192. \end{lstlisting}
  193. \vspace{1\baselineskip}
  194. Heartbeat protocol PING request returns PONG or empty NOP binary response.
  195. \vspace{1\baselineskip}
  196. \begin{lstlisting}
  197. issue TEXT PING expect PONG
  198. PONG is TEXT "PONG" or ""
  199. \end{lstlisting}
  200. \vspace{1\baselineskip}
  201. \newpage
  202. \subsection{NITRO}
  203. NITRO protocol consist of three protocol messages: {\bf pickle}, {\bf flush} and {\bf direct}.
  204. Pickled messages are used if you send messages over unencrypted
  205. channel and want to hide the content of the message,
  206. that was generated on server. You can use BASE64 pickling mechanisms
  207. with optional AES/RIPEMD160 encrypting. NITRO messages on success alway
  208. return empty data field in IO message and
  209. error otherwise. Here is definition to NITRO protocol in expect language:
  210. \vspace{1\baselineskip}
  211. \begin{lstlisting}
  212. issue BERT PICKLE expect IO
  213. issue BERT DIRECT expect IO
  214. issue BERT FLUSH expect IO
  215. PICKLE is BERT {pickle,_,_,_,_}
  216. DIRECT is BERT {direct,_}
  217. FLUSH is BERT {flush,_}
  218. \end{lstlisting}
  219. \vspace{1\baselineskip}
  220. Usually {\bf pickle} events are being sent generated from server during
  221. rendering of {\bf nitro} elements. To see how it looks like you can see
  222. inside IO messages returned from N2O initialization. There you can find
  223. something like this:
  224. \vspace{1\baselineskip}
  225. \begin{lstlisting}
  226. ws.send(enc(tuple(atom('pickle'),
  227. bin('loginButton'),
  228. bin('g2gCaAVkAAJldmQABGF1dGhkAAVsb2dpbmsAC2xvZ2lu'
  229. 'QnV0dG9uZAAFZXZlbnRoA2IAAAWiYgAA72ViAA8kIQ=='),
  230. [ tuple(tuple(utf8_toByteArray('loginButton'),
  231. bin('detail')),[]),
  232. tuple(atom('user'),querySource('user')),
  233. tuple(atom('pass'),querySource('pass'))])));
  234. \end{lstlisting}
  235. \vspace{1\baselineskip}
  236. Invocation of {\bf pickle} messages is binded to DOM elements
  237. using {\bf source} and {\bf postback} information from nitro elements.
  238. \vspace{1\baselineskip}
  239. \begin{lstlisting}
  240. #button { id=loginButton,
  241. body="Login",
  242. postback=login,
  243. source=[user,pass] } ].
  244. \end{lstlisting}
  245. \vspace{1\baselineskip}
  246. Only fields listed in {\bf source} will be included in {\bf pickle}
  247. message on invocation. Information about module and event arguments (postback)
  248. is sent encrypted or pickled. So it would be hard to know the internal
  249. structure of server codebase for potential hacker. On the server you will
  250. recieve following structure:
  251. \vspace{1\baselineskip}
  252. \begin{lstlisting}
  253. {pickle,<<"loginButton">>,
  254. <<"g2gCaAVkAAJldmQABGF1dGhkAAVsb2dpbmsAC2xvZ2lu"
  255. "QnV0dG9uZAAFZXZlbnRoA2IAAAWiYgAA72ViAA8kIQ==">>,
  256. [{{"loginButton",<<"detail">>},[]},
  257. {user,[]},
  258. {pass,"z"}]}
  259. \end{lstlisting}
  260. \vspace{1\baselineskip}
  261. You can depickle {\bf \#ev} event with {\bf wf:depickle} API:
  262. \vspace{1\baselineskip}
  263. \begin{lstlisting}
  264. > wf:depickle(<<"g1AAAAA6eJzLYMpgTWFgSi1LYWDNyU/PzIPR2Qh+"
  265. "allqXkkGcxIDA+siIHEvKomB5cBKAN+JEQ4=">>).
  266. #ev { module = Module = auth,
  267. msg = Message = login,
  268. name = event,
  269. trigger = "loginButton" }
  270. \end{lstlisting}
  271. \vspace{1\baselineskip}
  272. Information for {\bf \#ev} event is directly passed to page module
  273. as {\bf Module:event(Message) }. Information from sources {\bf user}
  274. and {\bf pass} could be retrieved with {\bf wf:q} API:
  275. \vspace{1\baselineskip}
  276. \begin{lstlisting}
  277. -module(auth).
  278. -compile(export_all).
  279. event(login) ->
  280. io:format(lists:concat([":user:",wf:q(user),
  281. ":pass:",wf:q(pass)])).
  282. \end{lstlisting}
  283. \vspace{1\baselineskip}
  284. This is Nitrogen-based messaging model. Nitrogen WebSocket processes receive also
  285. flush and delivery protocol messages, but originated from server, which is internal
  286. NITRO protocol messages. All client requests originate IO message as a response.
  287. \newpage
  288. \subsection{SPA}
  289. If you are creating SVG based game you don't need HTML5 nitro elements at all.
  290. Instead you need simple and clean JavaScript based protocol for updating DOM SVG elements
  291. but based on {\bf shen} generated or manual JavaScript code sent from server.
  292. Thus you need still IO messages as a reply but originating message shouldn't
  293. rely in {\bf nitro} at all. For that purposes in general and for {\bf synrc/games} sample
  294. in particular we created SPA protocol layer. SPA protocol consist of CLIENT originated message
  295. and SERVER message that could be originated both from client and server. All messages expects
  296. IO as a response. In IO response data field is always set with return value of the event
  297. while eval field is set with rendered actions as in NITRO protocol.
  298. \vspace{1\baselineskip}
  299. \begin{lstlisting}
  300. issue BERT CLIENT expect IO
  301. issue BERT SERVER expect IO
  302. SERVER is BERT {server,_}
  303. CLIENT is BERT {client,_}
  304. \end{lstlisting}
  305. \vspace{1\baselineskip}
  306. Client messages usually originated at client and represent the Client API Requests:
  307. \vspace{1\baselineskip}
  308. \begin{lstlisting}
  309. ws.send(enc(tuple(
  310. atom('client'),
  311. tuple(atom('join_game'),1000001))));
  312. \end{lstlisting}
  313. \vspace{1\baselineskip}
  314. Server messages are usually being sent to client originated on the
  315. server by sending {\bf info} notifications directly to Web Socket process:
  316. \begin{lstlisting}
  317. > WebSocketPid ! {server, Message}
  318. \end{lstlisting}
  319. You can obtain this Pid during page init:
  320. \begin{lstlisting}
  321. event(init) -> io:format("Pid: ~p",[self()]);
  322. \end{lstlisting}
  323. You can also send server messages from client relays and vice versa.
  324. It is up to your application and client/server handlers how to handle such messages.
  325. \newpage
  326. \subsection{BIN}
  327. When you need raw binary Blob on client-side,
  328. for images or other raw data, you can ask server like this:
  329. \vspace{1\baselineskip}
  330. \begin{lstlisting}
  331. > ws.send(enc(tuple(atom('bin'),bin('request'))));
  332. \end{lstlisting}
  333. \vspace{1\baselineskip}
  334. Ensure you have defined {\bf \#bin} handler and page you are
  335. asking is visible by router:
  336. \vspace{1\baselineskip}
  337. \begin{lstlisting}
  338. event(#bin{data=Data}) ->
  339. wf:info(?MODULE,"Binary Delivered ~p~n",[Data]),
  340. #bin{data = "SERVER v1"};
  341. \end{lstlisting}
  342. \vspace{1\baselineskip}
  343. Having enabled all loggin in module {\bf n2o\_file}, {\bf index} and {\bf wf\_convert}
  344. you will see:
  345. \vspace{1\baselineskip}
  346. \begin{lstlisting}
  347. n2o_file:BIN Message: {bin,<<"request">>}
  348. index:Binary Delivered <<"request">>
  349. wf_convert:BERT {bin,_}: "SERVER v1"
  350. \end{lstlisting}
  351. \vspace{1\baselineskip}
  352. In JavaScript when you enable `debug=true` you can see:
  353. \vspace{1\baselineskip}
  354. \begin{lstlisting}
  355. > {"t":104,"v":[{"t":100,"v":"bin"},
  356. {"t":107,"v":"SERVER v1"}]}
  357. \end{lstlisting}
  358. \vspace{1\baselineskip}
  359. Or by adding handling for BIN protocol:
  360. \begin{lstlisting}
  361. > $file.do = function (x)
  362. { console.log('BIN received: ' + x.v[1].v); }
  363. > ws.send(enc(tuple(atom('bin'),bin('request'))));
  364. > BIN received: SERVER v1
  365. \end{lstlisting}
  366. \vspace{1\baselineskip}
  367. The formal description of BIN is simple relay:
  368. \begin{lstlisting}
  369. issue BERT {bin,_} expect {bin,_}
  370. \end{lstlisting}