capricorn 2.0.8 → 2.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/erlang/lib/capricorn/ebin/capricorn.app +2 -1
  2. data/erlang/lib/capricorn/include/capricorn.hrl +1 -0
  3. data/erlang/lib/capricorn/src/cap_cluster_gems.erl +6 -1
  4. data/erlang/lib/capricorn/src/cap_console_dispatcher.erl +214 -0
  5. data/erlang/lib/capricorn/src/cap_machine_apps.erl +25 -1
  6. data/erlang/lib/capricorn/src/cap_sup.erl +14 -0
  7. data/erlang/lib/ejson/Makefile +24 -0
  8. data/erlang/lib/ejson/ebin/ejson.app +9 -0
  9. data/erlang/lib/ejson/include/ejson.hrl +40 -0
  10. data/erlang/lib/ejson/rebar.config +3 -0
  11. data/erlang/lib/ejson/src/ejson.erl +22 -0
  12. data/erlang/lib/ejson/src/ejson_decode.erl +337 -0
  13. data/erlang/lib/ejson/src/ejson_encode.erl +124 -0
  14. data/erlang/lib/ejson/test/arrays.escript +47 -0
  15. data/erlang/lib/ejson/test/compound.escript +56 -0
  16. data/erlang/lib/ejson/test/literals.escript +30 -0
  17. data/erlang/lib/ejson/test/numbers.escript +70 -0
  18. data/erlang/lib/ejson/test/objects.escript +51 -0
  19. data/erlang/lib/ejson/test/strings.escript +49 -0
  20. data/erlang/lib/ejson/test/timing.escript +43 -0
  21. data/erlang/lib/ejson/test/timing.json +382 -0
  22. data/erlang/lib/ejson/vendor/mochijson2.erl +621 -0
  23. data/erlang/lib/ejson/vendor/rfc4627.erl +625 -0
  24. data/erlang/lib/misultin/LICENSE.txt +41 -0
  25. data/erlang/lib/misultin/Makefile +26 -0
  26. data/erlang/lib/misultin/README.txt +120 -0
  27. data/erlang/lib/misultin/ebin/misultin.app +9 -0
  28. data/erlang/lib/misultin/examples/misultin_compress.erl +43 -0
  29. data/erlang/lib/misultin/examples/misultin_echo.erl +58 -0
  30. data/erlang/lib/misultin/examples/misultin_file.erl +43 -0
  31. data/erlang/lib/misultin/examples/misultin_gen_server.erl +158 -0
  32. data/erlang/lib/misultin/examples/misultin_get_variable.erl +55 -0
  33. data/erlang/lib/misultin/examples/misultin_hello_world.erl +43 -0
  34. data/erlang/lib/misultin/examples/misultin_rest.erl +68 -0
  35. data/erlang/lib/misultin/examples/misultin_ssl.erl +51 -0
  36. data/erlang/lib/misultin/examples/misultin_stream.erl +55 -0
  37. data/erlang/lib/misultin/examples/misultin_websocket_event_example.erl +103 -0
  38. data/erlang/lib/misultin/examples/misultin_websocket_example.erl +95 -0
  39. data/erlang/lib/misultin/include/misultin.hrl +95 -0
  40. data/erlang/lib/misultin/make.bat +55 -0
  41. data/erlang/lib/misultin/priv/README.txt +12 -0
  42. data/erlang/lib/misultin/priv/test_certificate.pem +21 -0
  43. data/erlang/lib/misultin/priv/test_privkey.pem +18 -0
  44. data/erlang/lib/misultin/rebar.config +3 -0
  45. data/erlang/lib/misultin/src/misultin.app.src +9 -0
  46. data/erlang/lib/misultin/src/misultin.erl +338 -0
  47. data/erlang/lib/misultin/src/misultin_http.erl +488 -0
  48. data/erlang/lib/misultin/src/misultin_req.erl +280 -0
  49. data/erlang/lib/misultin/src/misultin_socket.erl +193 -0
  50. data/erlang/lib/misultin/src/misultin_utility.erl +357 -0
  51. data/erlang/lib/misultin/src/misultin_websocket.erl +252 -0
  52. data/erlang/lib/misultin/src/misultin_ws.erl +78 -0
  53. data/erlang/rebar.config +2 -0
  54. data/erlang/rel/overlay/etc/capricorn/app.config +4 -0
  55. data/erlang/rel/reltool.config +5 -0
  56. data/lib/capricorn/recipes/apache-debian.rb +1 -1
  57. data/lib/capricorn/recipes/centos-plesk.rb +1 -1
  58. data/lib/capricorn/recipes/debian-plesk95.rb +1 -2
  59. data/lib/capricorn/recipes/macports.rb +1 -1
  60. data/lib/capricorn/version.rb +1 -1
  61. metadata +51 -4
@@ -0,0 +1,357 @@
1
+ % ==========================================================================================================
2
+ % MISULTIN - Various Utilities
3
+ %
4
+ % >-|-|-(°>
5
+ %
6
+ % Copyright (C) 2010, Roberto Ostinelli <roberto@ostinelli.net>.
7
+ % All rights reserved.
8
+ %
9
+ % BSD License
10
+ %
11
+ % Redistribution and use in source and binary forms, with or without modification, are permitted provided
12
+ % that the following conditions are met:
13
+ %
14
+ % * Redistributions of source code must retain the above copyright notice, this list of conditions and the
15
+ % following disclaimer.
16
+ % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
17
+ % the following disclaimer in the documentation and/or other materials provided with the distribution.
18
+ % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote
19
+ % products derived from this software without specific prior written permission.
20
+ %
21
+ % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
22
+ % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23
+ % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
24
+ % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
25
+ % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
+ % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27
+ % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
+ % POSSIBILITY OF SUCH DAMAGE.
29
+ % ==========================================================================================================
30
+ -module(misultin_utility).
31
+ -vsn("0.6.1").
32
+
33
+ % API
34
+ -export([get_http_status_code/1, get_content_type/1, get_key_value/2, header_get_value/2]).
35
+
36
+
37
+ % ============================ \/ API ======================================================================
38
+
39
+ % Function: HttpStatus
40
+ % Description: Returns a complete HTTP header
41
+ % most common first
42
+ get_http_status_code(200) ->
43
+ "HTTP/1.1 200 OK\r\n";
44
+ get_http_status_code(100) ->
45
+ "HTTP/1.1 100 Continue\r\n";
46
+ get_http_status_code(101) ->
47
+ "HTTP/1.1 101 Switching Protocols\r\n";
48
+ get_http_status_code(301) ->
49
+ "HTTP/1.1 301 Moved Permanently\r\n";
50
+ get_http_status_code(400) ->
51
+ "HTTP/1.1 400 Bad Request\r\n";
52
+ get_http_status_code(401) ->
53
+ "HTTP/1.1 401 Unauthorized\r\n";
54
+ get_http_status_code(403) ->
55
+ "HTTP/1.1 403 Forbidden\r\n";
56
+ get_http_status_code(404) ->
57
+ "HTTP/1.1 404 Not Found\r\n";
58
+ get_http_status_code(408) ->
59
+ "HTTP/1.1 408 Request Timeout\r\n";
60
+ get_http_status_code(500) ->
61
+ "HTTP/1.1 500 Internal Server Error\r\n";
62
+ get_http_status_code(501) ->
63
+ "HTTP/1.1 501 Not Implemented\r\n";
64
+ % less common last
65
+ get_http_status_code(201) ->
66
+ "HTTP/1.1 201 Created\r\n";
67
+ get_http_status_code(202) ->
68
+ "HTTP/1.1 202 Accepted\r\n";
69
+ get_http_status_code(203) ->
70
+ "HTTP/1.1 203 Non-Authoritative Information\r\n";
71
+ get_http_status_code(204) ->
72
+ "HTTP/1.1 204 No Content\r\n";
73
+ get_http_status_code(205) ->
74
+ "HTTP/1.1 205 Reset Content\r\n";
75
+ get_http_status_code(206) ->
76
+ "HTTP/1.1 206 Partial Content\r\n";
77
+ get_http_status_code(300) ->
78
+ "HTTP/1.1 300 Multiple Choices\r\n";
79
+ get_http_status_code(302) ->
80
+ "HTTP/1.1 302 Found\r\n";
81
+ get_http_status_code(303) ->
82
+ "HTTP/1.1 303 See Other\r\n";
83
+ get_http_status_code(304) ->
84
+ "HTTP/1.1 304 Not Modified\r\n";
85
+ get_http_status_code(305) ->
86
+ "HTTP/1.1 305 Use Proxy\r\n";
87
+ get_http_status_code(307) ->
88
+ "HTTP/1.1 307 Temporary Redirect\r\n";
89
+ get_http_status_code(402) ->
90
+ "HTTP/1.1 402 Payment Required\r\n";
91
+ get_http_status_code(405) ->
92
+ "HTTP/1.1 405 Method Not Allowed\r\n";
93
+ get_http_status_code(406) ->
94
+ "HTTP/1.1 406 Not Acceptable\r\n";
95
+ get_http_status_code(407) ->
96
+ "HTTP/1.1 407 Proxy Authentication Required\r\n";
97
+ get_http_status_code(409) ->
98
+ "HTTP/1.1 409 Conflict\r\n";
99
+ get_http_status_code(410) ->
100
+ "HTTP/1.1 410 Gone\r\n";
101
+ get_http_status_code(411) ->
102
+ "HTTP/1.1 411 Length Required\r\n";
103
+ get_http_status_code(412) ->
104
+ "HTTP/1.1 412 Precondition Failed\r\n";
105
+ get_http_status_code(413) ->
106
+ "HTTP/1.1 413 Request Entity Too Large\r\n";
107
+ get_http_status_code(414) ->
108
+ "HTTP/1.1 414 Request-URI Too Long\r\n";
109
+ get_http_status_code(415) ->
110
+ "HTTP/1.1 415 Unsupported Media Type\r\n";
111
+ get_http_status_code(416) ->
112
+ "HTTP/1.1 416 Requested Range Not Satisfiable\r\n";
113
+ get_http_status_code(417) ->
114
+ "HTTP/1.1 417 Expectation Failed\r\n";
115
+ get_http_status_code(502) ->
116
+ "HTTP/1.1 502 Bad Gateway\r\n";
117
+ get_http_status_code(503) ->
118
+ "HTTP/1.1 503 Service Unavailable\r\n";
119
+ get_http_status_code(504) ->
120
+ "HTTP/1.1 504 Gateway Timeout\r\n";
121
+ get_http_status_code(505) ->
122
+ "HTTP/1.1 505 HTTP Version Not Supported\r\n";
123
+ get_http_status_code(Other) ->
124
+ lists:flatten(io_lib:format("HTTP/1.1 ~p \r\n", [Other])).
125
+
126
+ % get content type
127
+ get_content_type(FileName) ->
128
+ case filename:extension(FileName) of
129
+ % most common first
130
+ ".doc" -> "application/msword";
131
+ ".exe" -> "application/octet-stream";
132
+ ".pdf" -> "application/pdf";
133
+ ".rtf" -> "application/rtf";
134
+ ".ppt" -> "application/vnd.ms-powerpoint";
135
+ ".tgz" -> "application/x-compressed";
136
+ ".tar" -> "application/x-tar";
137
+ ".zip" -> "application/zip";
138
+ ".mp3" -> "audio/mpeg";
139
+ ".wav" -> "audio/x-wav";
140
+ ".bmp" -> "image/bmp";
141
+ ".ram" -> "audio/x-pn-realaudio";
142
+ ".gif" -> "image/gif";
143
+ ".jpe" -> "image/jpeg";
144
+ ".jpeg" -> "image/jpeg";
145
+ ".jpg" -> "image/jpeg";
146
+ ".tif" -> "image/tiff";
147
+ ".tiff" -> "image/tiff";
148
+ ".htm" -> "text/html";
149
+ ".html" -> "text/html";
150
+ ".txt" -> "text/plain";
151
+ ".mp2" -> "video/mpeg";
152
+ ".mpa" -> "video/mpeg";
153
+ ".mpe" -> "video/mpeg";
154
+ ".mpeg" -> "video/mpeg";
155
+ ".mpg" -> "video/mpeg";
156
+ ".mov" -> "video/quicktime";
157
+ ".avi" -> "video/x-msvideo";
158
+ % less common last
159
+ ".evy" -> "application/envoy";
160
+ ".fif" -> "application/fractals";
161
+ ".spl" -> "application/futuresplash";
162
+ ".hta" -> "application/hta";
163
+ ".acx" -> "application/internet-property-stream";
164
+ ".hqx" -> "application/mac-binhex40";
165
+ ".dot" -> "application/msword";
166
+ ".bin" -> "application/octet-stream";
167
+ ".class" -> "application/octet-stream";
168
+ ".dms" -> "application/octet-stream";
169
+ ".lha" -> "application/octet-stream";
170
+ ".lzh" -> "application/octet-stream";
171
+ ".oda" -> "application/oda";
172
+ ".axs" -> "application/olescript";
173
+ ".prf" -> "application/pics-rules";
174
+ ".p10" -> "application/pkcs10";
175
+ ".crl" -> "application/pkix-crl";
176
+ ".ai" -> "application/postscript";
177
+ ".eps" -> "application/postscript";
178
+ ".ps" -> "application/postscript";
179
+ ".setpay" -> "application/set-payment-initiation";
180
+ ".setreg" -> "application/set-registration-initiation";
181
+ ".xla" -> "application/vnd.ms-excel";
182
+ ".xlc" -> "application/vnd.ms-excel";
183
+ ".xlm" -> "application/vnd.ms-excel";
184
+ ".xls" -> "application/vnd.ms-excel";
185
+ ".xlt" -> "application/vnd.ms-excel";
186
+ ".xlw" -> "application/vnd.ms-excel";
187
+ ".msg" -> "application/vnd.ms-outlook";
188
+ ".sst" -> "application/vnd.ms-pkicertstore";
189
+ ".cat" -> "application/vnd.ms-pkiseccat";
190
+ ".stl" -> "application/vnd.ms-pkistl";
191
+ ".pot" -> "application/vnd.ms-powerpoint";
192
+ ".pps" -> "application/vnd.ms-powerpoint";
193
+ ".mpp" -> "application/vnd.ms-project";
194
+ ".wcm" -> "application/vnd.ms-works";
195
+ ".wdb" -> "application/vnd.ms-works";
196
+ ".wks" -> "application/vnd.ms-works";
197
+ ".wps" -> "application/vnd.ms-works";
198
+ ".hlp" -> "application/winhlp";
199
+ ".bcpio" -> "application/x-bcpio";
200
+ ".cdf" -> "application/x-cdf";
201
+ ".z" -> "application/x-compress";
202
+ ".cpio" -> "application/x-cpio";
203
+ ".csh" -> "application/x-csh";
204
+ ".dcr" -> "application/x-director";
205
+ ".dir" -> "application/x-director";
206
+ ".dxr" -> "application/x-director";
207
+ ".dvi" -> "application/x-dvi";
208
+ ".gtar" -> "application/x-gtar";
209
+ ".gz" -> "application/x-gzip";
210
+ ".hdf" -> "application/x-hdf";
211
+ ".ins" -> "application/x-internet-signup";
212
+ ".isp" -> "application/x-internet-signup";
213
+ ".iii" -> "application/x-iphone";
214
+ ".js" -> "application/x-javascript";
215
+ ".latex" -> "application/x-latex";
216
+ ".mdb" -> "application/x-msaccess";
217
+ ".crd" -> "application/x-mscardfile";
218
+ ".clp" -> "application/x-msclip";
219
+ ".dll" -> "application/x-msdownload";
220
+ ".m13" -> "application/x-msmediaview";
221
+ ".m14" -> "application/x-msmediaview";
222
+ ".mvb" -> "application/x-msmediaview";
223
+ ".wmf" -> "application/x-msmetafile";
224
+ ".mny" -> "application/x-msmoney";
225
+ ".pub" -> "application/x-mspublisher";
226
+ ".scd" -> "application/x-msschedule";
227
+ ".trm" -> "application/x-msterminal";
228
+ ".wri" -> "application/x-mswrite";
229
+ ".nc" -> "application/x-netcdf";
230
+ ".pma" -> "application/x-perfmon";
231
+ ".pmc" -> "application/x-perfmon";
232
+ ".pml" -> "application/x-perfmon";
233
+ ".pmr" -> "application/x-perfmon";
234
+ ".pmw" -> "application/x-perfmon";
235
+ ".p12" -> "application/x-pkcs12";
236
+ ".pfx" -> "application/x-pkcs12";
237
+ ".p7b" -> "application/x-pkcs7-certificates";
238
+ ".spc" -> "application/x-pkcs7-certificates";
239
+ ".p7r" -> "application/x-pkcs7-certreqresp";
240
+ ".p7c" -> "application/x-pkcs7-mime";
241
+ ".p7m" -> "application/x-pkcs7-mime";
242
+ ".p7s" -> "application/x-pkcs7-signature";
243
+ ".sh" -> "application/x-sh";
244
+ ".shar" -> "application/x-shar";
245
+ ".swf" -> "application/x-shockwave-flash";
246
+ ".sit" -> "application/x-stuffit";
247
+ ".sv4cpio" -> "application/x-sv4cpio";
248
+ ".sv4crc" -> "application/x-sv4crc";
249
+ ".tcl" -> "application/x-tcl";
250
+ ".tex" -> "application/x-tex";
251
+ ".texi" -> "application/x-texinfo";
252
+ ".texinfo" -> "application/x-texinfo";
253
+ ".roff" -> "application/x-troff";
254
+ ".t" -> "application/x-troff";
255
+ ".tr" -> "application/x-troff";
256
+ ".man" -> "application/x-troff-man";
257
+ ".me" -> "application/x-troff-me";
258
+ ".ms" -> "application/x-troff-ms";
259
+ ".ustar" -> "application/x-ustar";
260
+ ".src" -> "application/x-wais-source";
261
+ ".cer" -> "application/x-x509-ca-cert";
262
+ ".crt" -> "application/x-x509-ca-cert";
263
+ ".der" -> "application/x-x509-ca-cert";
264
+ ".pko" -> "application/ynd.ms-pkipko";
265
+ ".au" -> "audio/basic";
266
+ ".snd" -> "audio/basic";
267
+ ".mid" -> "audio/mid";
268
+ ".rmi" -> "audio/mid";
269
+ ".aif" -> "audio/x-aiff";
270
+ ".aifc" -> "audio/x-aiff";
271
+ ".aiff" -> "audio/x-aiff";
272
+ ".m3u" -> "audio/x-mpegurl";
273
+ ".ra" -> "audio/x-pn-realaudio";
274
+ ".cod" -> "image/cis-cod";
275
+ ".ief" -> "image/ief";
276
+ ".jfif" -> "image/pipeg";
277
+ ".svg" -> "image/svg+xml";
278
+ ".ras" -> "image/x-cmu-raster";
279
+ ".cmx" -> "image/x-cmx";
280
+ ".ico" -> "image/x-icon";
281
+ ".pnm" -> "image/x-portable-anymap";
282
+ ".pbm" -> "image/x-portable-bitmap";
283
+ ".pgm" -> "image/x-portable-graymap";
284
+ ".ppm" -> "image/x-portable-pixmap";
285
+ ".rgb" -> "image/x-rgb";
286
+ ".xbm" -> "image/x-xbitmap";
287
+ ".xpm" -> "image/x-xpixmap";
288
+ ".xwd" -> "image/x-xwindowdump";
289
+ ".mht" -> "message/rfc822";
290
+ ".mhtml" -> "message/rfc822";
291
+ ".nws" -> "message/rfc822";
292
+ ".css" -> "text/css";
293
+ ".323" -> "text/h323";
294
+ ".stm" -> "text/html";
295
+ ".uls" -> "text/iuls";
296
+ ".bas" -> "text/plain";
297
+ ".c" -> "text/plain";
298
+ ".h" -> "text/plain";
299
+ ".rtx" -> "text/richtext";
300
+ ".sct" -> "text/scriptlet";
301
+ ".tsv" -> "text/tab-separated-values";
302
+ ".htt" -> "text/webviewhtml";
303
+ ".htc" -> "text/x-component";
304
+ ".etx" -> "text/x-setext";
305
+ ".vcf" -> "text/x-vcard";
306
+ ".mpv2" -> "video/mpeg";
307
+ ".qt" -> "video/quicktime";
308
+ ".lsf" -> "video/x-la-asf";
309
+ ".lsx" -> "video/x-la-asf";
310
+ ".asf" -> "video/x-ms-asf";
311
+ ".asr" -> "video/x-ms-asf";
312
+ ".asx" -> "video/x-ms-asf";
313
+ ".movie" -> "video/x-sgi-movie";
314
+ ".flr" -> "x-world/x-vrml";
315
+ ".vrml" -> "x-world/x-vrml";
316
+ ".wrl" -> "x-world/x-vrml";
317
+ ".wrz" -> "x-world/x-vrml";
318
+ ".xaf" -> "x-world/x-vrml";
319
+ ".xof" -> "x-world/x-vrml";
320
+ _ -> "application/octet-stream"
321
+ end.
322
+
323
+ % faster than proplists:get_value
324
+ get_key_value(Key, List)->
325
+ case lists:keyfind(Key, 1, List) of
326
+ false-> undefined;
327
+ {_K, Value}-> Value
328
+ end.
329
+
330
+ % Function: Value | false
331
+ % Description: Find atom Tag in Headers, Headers being both atoms [for known headers] and strings. Comparison on string Header Tags is case insensitive.
332
+ header_get_value(Tag, Headers) when is_atom(Tag) ->
333
+ case lists:keyfind(Tag, 1, Headers) of
334
+ false ->
335
+ % header not found, test also conversion to string -> convert all string tags to lowercase (HTTP tags are case insensitive)
336
+ F = fun({HTag, HValue}) ->
337
+ case is_atom(HTag) of
338
+ true -> {HTag, HValue};
339
+ false -> {string:to_lower(HTag), HValue}
340
+ end
341
+ end,
342
+ HeadersStr = lists:map(F, Headers),
343
+ % test
344
+ case lists:keyfind(string:to_lower(atom_to_list(Tag)), 1, HeadersStr) of
345
+ false -> false;
346
+ {_, Value} -> Value
347
+ end;
348
+ {_, Value} -> Value
349
+ end.
350
+
351
+ % ============================ /\ API ======================================================================
352
+
353
+
354
+
355
+ % ============================ \/ INTERNAL FUNCTIONS =======================================================
356
+
357
+ % ============================ /\ INTERNAL FUNCTIONS =======================================================
@@ -0,0 +1,252 @@
1
+ % ==========================================================================================================
2
+ % MISULTIN - WebSocket
3
+ %
4
+ % >-|-|-(°>
5
+ %
6
+ % Copyright (C) 2010, Roberto Ostinelli <roberto@ostinelli.net>, Joe Armstrong.
7
+ % All rights reserved.
8
+ %
9
+ % Code portions from Joe Armstrong have been originally taken under MIT license at the address:
10
+ % <http://armstrongonsoftware.blogspot.com/2009/12/comet-is-dead-long-live-websockets.html>
11
+ %
12
+ % BSD License
13
+ %
14
+ % Redistribution and use in source and binary forms, with or without modification, are permitted provided
15
+ % that the following conditions are met:
16
+ %
17
+ % * Redistributions of source code must retain the above copyright notice, this list of conditions and the
18
+ % following disclaimer.
19
+ % * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
20
+ % the following disclaimer in the documentation and/or other materials provided with the distribution.
21
+ % * Neither the name of the authors nor the names of its contributors may be used to endorse or promote
22
+ % products derived from this software without specific prior written permission.
23
+ %
24
+ % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
25
+ % WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
26
+ % PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
27
+ % ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
28
+ % TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29
+ % HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30
+ % NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31
+ % POSSIBILITY OF SUCH DAMAGE.
32
+ % ==========================================================================================================
33
+ -module(misultin_websocket).
34
+ -vsn("0.6.1").
35
+
36
+ % API
37
+ -export([check/2, connect/3]).
38
+
39
+
40
+ % includes
41
+ -include("../include/misultin.hrl").
42
+
43
+
44
+ % ============================ \/ API ======================================================================
45
+
46
+ % Function: {true, Origin, Host, Path} | false
47
+ % Description: Check if the incoming request is a websocket handshake.
48
+ check(_Path, Headers) ->
49
+ ?LOG_DEBUG("testing for a websocket request path: ~p headers: ~p", [_Path, Headers]),
50
+ % set supported websocket protocols, order does matter
51
+ VsnSupported = [{'draft-hixie', 76}, {'draft-hixie', 68}],
52
+ % checks
53
+ check_websockets(VsnSupported, Headers).
54
+
55
+ % Connect and handshake with Websocket.
56
+ connect(Req, #ws{vsn = Vsn, socket = Socket, socket_mode = SocketMode, path = Path, headers = Headers, ws_autoexit = WsAutoExit} = Ws, WsLoop) ->
57
+ ?LOG_DEBUG("building handshake response", []),
58
+ % get data
59
+ Origin = misultin_utility:header_get_value('Origin', Headers),
60
+ Host = misultin_utility:header_get_value('Host', Headers),
61
+ % build handshake
62
+ HandshakeServer = handshake(Vsn, Req, Headers, {Path, Origin, Host}),
63
+ % send handshake back
64
+ misultin_socket:send(Socket, HandshakeServer, SocketMode),
65
+ % add data to ws record and spawn controlling process
66
+ Ws0 = misultin_ws:new(Ws#ws{origin = Origin, host = Host}, self()),
67
+ WsHandleLoopPid = spawn(fun() -> WsLoop(Ws0) end),
68
+ erlang:monitor(process, WsHandleLoopPid),
69
+ % set opts
70
+ misultin_socket:setopts(Socket, [{packet, 0}, {active, true}], SocketMode),
71
+ % add main websocket pid to misultin server reference
72
+ misultin:persistent_socket_pid_add(self()),
73
+ % start listening for incoming data
74
+ ws_loop(Socket, none, WsHandleLoopPid, SocketMode, WsAutoExit).
75
+
76
+ % ============================ /\ API ======================================================================
77
+
78
+
79
+ % ============================ \/ INTERNAL FUNCTIONS =======================================================
80
+
81
+ % Description: Loop to check for all available supported websocket protocols.
82
+ check_websockets([], _Headers) -> false;
83
+ check_websockets([Vsn|T], Headers) ->
84
+ case check_websocket(Vsn, Headers) of
85
+ false -> check_websockets(T, Headers);
86
+ {true, Vsn} -> {true, Vsn}
87
+ end.
88
+
89
+ % Function: {true, Vsn} | false
90
+ % Description: Check if the incoming request is a websocket request.
91
+ check_websocket({'draft-hixie', 76} = Vsn, Headers) ->
92
+ ?LOG_DEBUG("testing for websocket protocol ~p", [Vsn]),
93
+ % set required headers
94
+ RequiredHeaders = [
95
+ {'Upgrade', "WebSocket"}, {'Connection', "Upgrade"}, {'Host', ignore}, {'Origin', ignore},
96
+ {'Sec-WebSocket-Key1', ignore}, {'Sec-WebSocket-Key2', ignore}
97
+ ],
98
+ % check for headers existance
99
+ case check_headers(Headers, RequiredHeaders) of
100
+ true ->
101
+ % return
102
+ {true, Vsn};
103
+ _RemainingHeaders ->
104
+ ?LOG_DEBUG("not protocol ~p, remaining headers: ~p", [Vsn, _RemainingHeaders]),
105
+ false
106
+ end;
107
+ check_websocket({'draft-hixie', 68} = Vsn, Headers) ->
108
+ ?LOG_DEBUG("testing for websocket protocol ~p", [Vsn]),
109
+ % set required headers
110
+ RequiredHeaders = [
111
+ {'Upgrade', "WebSocket"}, {'Connection', "Upgrade"}, {'Host', ignore}, {'Origin', ignore}
112
+ ],
113
+ % check for headers existance
114
+ case check_headers(Headers, RequiredHeaders) of
115
+ true -> {true, Vsn};
116
+ _RemainingHeaders ->
117
+ ?LOG_DEBUG("not protocol ~p, remaining headers: ~p", [Vsn, _RemainingHeaders]),
118
+ false
119
+ end;
120
+ check_websocket(_Vsn, _Headers) -> false. % not implemented
121
+
122
+ % Function: true | [{RequiredTag, RequiredVal}, ..]
123
+ % Description: Check if headers correspond to headers requirements.
124
+ check_headers(Headers, RequiredHeaders) ->
125
+ F = fun({Tag, Val}) ->
126
+ % see if the required Tag is in the Headers
127
+ case misultin_utility:header_get_value(Tag, Headers) of
128
+ false -> true; % header not found, keep in list
129
+ HVal ->
130
+ case Val of
131
+ ignore -> false; % ignore value -> ok, remove from list
132
+ HVal -> false; % expected val -> ok, remove from list
133
+ _ -> true % val is different, keep in list
134
+ end
135
+ end
136
+ end,
137
+ case lists:filter(F, RequiredHeaders) of
138
+ [] -> true;
139
+ MissingHeaders -> MissingHeaders
140
+ end.
141
+
142
+ % Function: List
143
+ % Description: Builds the server handshake response.
144
+ handshake({'draft-hixie', 76}, #req{socket = Sock, socket_mode = SocketMode}, Headers, {Path, Origin, Host}) ->
145
+ % build data
146
+ Key1 = misultin_utility:header_get_value('Sec-WebSocket-Key1', Headers),
147
+ Key2 = misultin_utility:header_get_value('Sec-WebSocket-Key2', Headers),
148
+ % handshake needs body of the request, still need to read it [TODO: default recv timeout hard set, will be exported when WS protocol is final]
149
+ misultin_socket:setopts(Sock, [{packet, raw}, {active, false}], SocketMode),
150
+ Body = case misultin_socket:recv(Sock, 8, 30*1000, SocketMode) of
151
+ {ok, Bin} -> Bin;
152
+ {error, timeout} ->
153
+ ?LOG_WARNING("timeout in reading websocket body", []),
154
+ <<>>;
155
+ _Other ->
156
+ ?LOG_ERROR("tcp error treating data: ~p", [_Other]),
157
+ <<>>
158
+ end,
159
+ ?LOG_DEBUG("got content in body of websocket request: ~p", [Body]),
160
+ % prepare handhsake response
161
+ ["HTTP/1.1 101 WebSocket Protocol Handshake\r\n",
162
+ "Upgrade: WebSocket\r\n",
163
+ "Connection: Upgrade\r\n",
164
+ "Sec-WebSocket-Origin: ", Origin, "\r\n",
165
+ "Sec-WebSocket-Location: ws://", lists:concat([Host, Path]), "\r\n\r\n",
166
+ build_challenge({'draft-hixie', 76}, {Key1, Key2, Body})
167
+ ];
168
+ handshake({'draft-hixie', 68}, _Req, _Headers, {Path, Origin, Host}) ->
169
+ % prepare handhsake response
170
+ ["HTTP/1.1 101 Web Socket Protocol Handshake\r\n",
171
+ "Upgrade: WebSocket\r\n",
172
+ "Connection: Upgrade\r\n",
173
+ "WebSocket-Origin: ", Origin , "\r\n",
174
+ "WebSocket-Location: ws://", lists:concat([Host, Path]), "\r\n\r\n"
175
+ ].
176
+
177
+ % Function: List
178
+ % Description: Builds the challenge for a handshake response.
179
+ % Code portions from Sergio Veiga <http://sergioveiga.com/index.php/2010/06/17/websocket-handshake-76-in-erlang/>
180
+ build_challenge({'draft-hixie', 76}, {Key1, Key2, Key3}) ->
181
+ Ikey1 = [D || D <- Key1, $0 =< D, D =< $9],
182
+ Ikey2 = [D || D <- Key2, $0 =< D, D =< $9],
183
+ Blank1 = length([D || D <- Key1, D =:= 32]),
184
+ Blank2 = length([D || D <- Key2, D =:= 32]),
185
+ Part1 = list_to_integer(Ikey1) div Blank1,
186
+ Part2 = list_to_integer(Ikey2) div Blank2,
187
+ Ckey = <<Part1:4/big-unsigned-integer-unit:8, Part2:4/big-unsigned-integer-unit:8, Key3/binary>>,
188
+ erlang:md5(Ckey).
189
+
190
+ % Main Websocket loop
191
+ ws_loop(Socket, Buffer, WsHandleLoopPid, SocketMode, WsAutoExit) ->
192
+ ?LOG_DEBUG("websocket loop", []),
193
+ receive
194
+ {tcp, Socket, Data} ->
195
+ handle_data(Buffer, binary_to_list(Data), Socket, WsHandleLoopPid, SocketMode, WsAutoExit);
196
+ {tcp_closed, Socket} ->
197
+ ?LOG_DEBUG("tcp connection was closed, exit", []),
198
+ % close websocket and custom controlling loop
199
+ websocket_close(Socket, WsHandleLoopPid, SocketMode, WsAutoExit);
200
+ {'DOWN', Ref, process, WsHandleLoopPid, Reason} ->
201
+ case Reason of
202
+ normal ->
203
+ ?LOG_DEBUG("linked websocket controlling loop stopped.", []);
204
+ _ ->
205
+ ?LOG_ERROR("linked websocket controlling loop crashed with reason: ~p", [Reason])
206
+ end,
207
+ % demonitor
208
+ erlang:demonitor(Ref),
209
+ % close websocket and custom controlling loop
210
+ websocket_close(Socket, WsHandleLoopPid, SocketMode, WsAutoExit);
211
+ {send, Data} ->
212
+ ?LOG_DEBUG("sending data to websocket: ~p", [Data]),
213
+ misultin_socket:send(Socket, [0, Data, 255], SocketMode),
214
+ ws_loop(Socket, Buffer, WsHandleLoopPid, SocketMode, WsAutoExit);
215
+ shutdown ->
216
+ ?LOG_DEBUG("shutdown request received, closing websocket with pid ~p", [self()]),
217
+ % close websocket and custom controlling loop
218
+ websocket_close(Socket, WsHandleLoopPid, SocketMode, WsAutoExit);
219
+ _Ignored ->
220
+ ?LOG_WARNING("received unexpected message, ignoring: ~p", [_Ignored]),
221
+ ws_loop(Socket, Buffer, WsHandleLoopPid, SocketMode, WsAutoExit)
222
+ end.
223
+
224
+ % Buffering and data handling
225
+ handle_data(none, [0|T], Socket, WsHandleLoopPid, SocketMode, WsAutoExit) ->
226
+ handle_data([], T, Socket, WsHandleLoopPid, SocketMode, WsAutoExit);
227
+ handle_data(none, [], Socket, WsHandleLoopPid, SocketMode, WsAutoExit) ->
228
+ ws_loop(Socket, none, WsHandleLoopPid, SocketMode, WsAutoExit);
229
+ handle_data(L, [255|T], Socket, WsHandleLoopPid, SocketMode, WsAutoExit) ->
230
+ WsHandleLoopPid ! {browser, lists:reverse(L)},
231
+ handle_data(none, T, Socket, WsHandleLoopPid, SocketMode, WsAutoExit);
232
+ handle_data(L, [H|T], Socket, WsHandleLoopPid, SocketMode, WsAutoExit) ->
233
+ handle_data([H|L], T, Socket, WsHandleLoopPid, SocketMode, WsAutoExit);
234
+ handle_data([], L, Socket, WsHandleLoopPid, SocketMode, WsAutoExit) ->
235
+ ws_loop(Socket, L, WsHandleLoopPid, SocketMode, WsAutoExit).
236
+
237
+ % Close socket and custom handling loop dependency
238
+ websocket_close(Socket, WsHandleLoopPid, SocketMode, WsAutoExit) ->
239
+ % remove main websocket pid from misultin server reference
240
+ misultin:persistent_socket_pid_remove(self()),
241
+ case WsAutoExit of
242
+ true ->
243
+ % kill custom handling loop process
244
+ exit(WsHandleLoopPid, kill);
245
+ false ->
246
+ % the killing of the custom handling loop process is handled in the loop itself -> send event
247
+ WsHandleLoopPid ! closed
248
+ end,
249
+ % close main socket
250
+ misultin_socket:close(Socket, SocketMode).
251
+
252
+ % ============================ /\ INTERNAL FUNCTIONS =======================================================