capricorn 2.0.8 → 2.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/erlang/lib/capricorn/ebin/capricorn.app +2 -1
- data/erlang/lib/capricorn/include/capricorn.hrl +1 -0
- data/erlang/lib/capricorn/src/cap_cluster_gems.erl +6 -1
- data/erlang/lib/capricorn/src/cap_console_dispatcher.erl +214 -0
- data/erlang/lib/capricorn/src/cap_machine_apps.erl +25 -1
- data/erlang/lib/capricorn/src/cap_sup.erl +14 -0
- data/erlang/lib/ejson/Makefile +24 -0
- data/erlang/lib/ejson/ebin/ejson.app +9 -0
- data/erlang/lib/ejson/include/ejson.hrl +40 -0
- data/erlang/lib/ejson/rebar.config +3 -0
- data/erlang/lib/ejson/src/ejson.erl +22 -0
- data/erlang/lib/ejson/src/ejson_decode.erl +337 -0
- data/erlang/lib/ejson/src/ejson_encode.erl +124 -0
- data/erlang/lib/ejson/test/arrays.escript +47 -0
- data/erlang/lib/ejson/test/compound.escript +56 -0
- data/erlang/lib/ejson/test/literals.escript +30 -0
- data/erlang/lib/ejson/test/numbers.escript +70 -0
- data/erlang/lib/ejson/test/objects.escript +51 -0
- data/erlang/lib/ejson/test/strings.escript +49 -0
- data/erlang/lib/ejson/test/timing.escript +43 -0
- data/erlang/lib/ejson/test/timing.json +382 -0
- data/erlang/lib/ejson/vendor/mochijson2.erl +621 -0
- data/erlang/lib/ejson/vendor/rfc4627.erl +625 -0
- data/erlang/lib/misultin/LICENSE.txt +41 -0
- data/erlang/lib/misultin/Makefile +26 -0
- data/erlang/lib/misultin/README.txt +120 -0
- data/erlang/lib/misultin/ebin/misultin.app +9 -0
- data/erlang/lib/misultin/examples/misultin_compress.erl +43 -0
- data/erlang/lib/misultin/examples/misultin_echo.erl +58 -0
- data/erlang/lib/misultin/examples/misultin_file.erl +43 -0
- data/erlang/lib/misultin/examples/misultin_gen_server.erl +158 -0
- data/erlang/lib/misultin/examples/misultin_get_variable.erl +55 -0
- data/erlang/lib/misultin/examples/misultin_hello_world.erl +43 -0
- data/erlang/lib/misultin/examples/misultin_rest.erl +68 -0
- data/erlang/lib/misultin/examples/misultin_ssl.erl +51 -0
- data/erlang/lib/misultin/examples/misultin_stream.erl +55 -0
- data/erlang/lib/misultin/examples/misultin_websocket_event_example.erl +103 -0
- data/erlang/lib/misultin/examples/misultin_websocket_example.erl +95 -0
- data/erlang/lib/misultin/include/misultin.hrl +95 -0
- data/erlang/lib/misultin/make.bat +55 -0
- data/erlang/lib/misultin/priv/README.txt +12 -0
- data/erlang/lib/misultin/priv/test_certificate.pem +21 -0
- data/erlang/lib/misultin/priv/test_privkey.pem +18 -0
- data/erlang/lib/misultin/rebar.config +3 -0
- data/erlang/lib/misultin/src/misultin.app.src +9 -0
- data/erlang/lib/misultin/src/misultin.erl +338 -0
- data/erlang/lib/misultin/src/misultin_http.erl +488 -0
- data/erlang/lib/misultin/src/misultin_req.erl +280 -0
- data/erlang/lib/misultin/src/misultin_socket.erl +193 -0
- data/erlang/lib/misultin/src/misultin_utility.erl +357 -0
- data/erlang/lib/misultin/src/misultin_websocket.erl +252 -0
- data/erlang/lib/misultin/src/misultin_ws.erl +78 -0
- data/erlang/rebar.config +2 -0
- data/erlang/rel/overlay/etc/capricorn/app.config +4 -0
- data/erlang/rel/reltool.config +5 -0
- data/lib/capricorn/recipes/apache-debian.rb +1 -1
- data/lib/capricorn/recipes/centos-plesk.rb +1 -1
- data/lib/capricorn/recipes/debian-plesk95.rb +1 -2
- data/lib/capricorn/recipes/macports.rb +1 -1
- data/lib/capricorn/version.rb +1 -1
- 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 =======================================================
|