capricorn 2.0.8 → 2.0.9
Sign up to get free protection for your applications and to get access to all the features.
- 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 =======================================================
|