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,95 @@
|
|
1
|
+
% ==========================================================================================================
|
2
|
+
% MISULTIN - Include file
|
3
|
+
%
|
4
|
+
% Copyright (C) 2009, Sean Hinde, Roberto Ostinelli <roberto@ostinelli.net>
|
5
|
+
% All rights reserved.
|
6
|
+
%
|
7
|
+
% BSD License
|
8
|
+
%
|
9
|
+
% Redistribution and use in source and binary forms, with or without modification, are permitted provided
|
10
|
+
% that the following conditions are met:
|
11
|
+
%
|
12
|
+
% * Redistributions of source code must retain the above copyright notice, this list of conditions and the
|
13
|
+
% following disclaimer.
|
14
|
+
% * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
|
15
|
+
% the following disclaimer in the documentation and/or other materials provided with the distribution.
|
16
|
+
% * Neither the name of the authors nor the names of its contributors may be used to endorse or promote
|
17
|
+
% products derived from this software without specific prior written permission.
|
18
|
+
%
|
19
|
+
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
|
20
|
+
% WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
21
|
+
% PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
22
|
+
% ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
23
|
+
% TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
24
|
+
% HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
25
|
+
% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
26
|
+
% POSSIBILITY OF SUCH DAMAGE.
|
27
|
+
% ==========================================================================================================
|
28
|
+
|
29
|
+
% define debug
|
30
|
+
-ifdef(log_debug).
|
31
|
+
-define(LOG_DEBUG(Str, Args), erlang:apply(error_logger, info_msg, [lists:concat(["[DEBUG] pid: ", pid_to_list(self()), "~n module: ", ?MODULE, "~n line: ", ?LINE, "~n", Str, "~n"]), Args])).
|
32
|
+
-define(LOG_INFO(Str, Args), erlang:apply(error_logger, info_msg, [lists:concat([" module: ", ?MODULE, "~n line: ", ?LINE, "~n", Str, "~n"]), Args])).
|
33
|
+
-define(LOG_WARNING(Str, Args), erlang:apply(error_logger, warning_msg, [lists:concat([" module: ", ?MODULE, "~n line: ", ?LINE, "~n", Str, "~n"]), Args])).
|
34
|
+
-define(LOG_ERROR(Str, Args), erlang:apply(error_logger, error_msg, [lists:concat([" module: ", ?MODULE, "~n line: ", ?LINE, "~n", Str, "~n"]), Args])).
|
35
|
+
-else.
|
36
|
+
-ifdef(log_info).
|
37
|
+
-define(LOG_DEBUG(Str, Args), ok).
|
38
|
+
-define(LOG_INFO(Str, Args), erlang:apply(error_logger, info_msg, [lists:concat([" module: ", ?MODULE, "~n line: ", ?LINE, "~n", Str, "~n"]), Args])).
|
39
|
+
-define(LOG_WARNING(Str, Args), erlang:apply(error_logger, warning_msg, [lists:concat([" module: ", ?MODULE, "~n line: ", ?LINE, "~n", Str, "~n"]), Args])).
|
40
|
+
-define(LOG_ERROR(Str, Args), erlang:apply(error_logger, error_msg, [lists:concat([" module: ", ?MODULE, "~n line: ", ?LINE, "~n", Str, "~n"]), Args])).
|
41
|
+
-else.
|
42
|
+
-ifdef(log_warning).
|
43
|
+
-define(LOG_DEBUG(Str, Args), ok).
|
44
|
+
-define(LOG_INFO(Str, Args), ok).
|
45
|
+
-define(LOG_WARNING(Str, Args), erlang:apply(error_logger, warning_msg, [lists:concat([" module: ", ?MODULE, "~n line: ", ?LINE, "~n", Str, "~n"]), Args])).
|
46
|
+
-define(LOG_ERROR(Str, Args), erlang:apply(error_logger, error_msg, [lists:concat([" module: ", ?MODULE, "~n line: ", ?LINE, "~n", Str, "~n"]), Args])).
|
47
|
+
-else.
|
48
|
+
-define(LOG_DEBUG(Str, Args), ok).
|
49
|
+
-define(LOG_INFO(Str, Args), ok).
|
50
|
+
-define(LOG_WARNING(Str, Args), ok).
|
51
|
+
-define(LOG_ERROR(Str, Args), erlang:apply(error_logger, error_msg, [lists:concat([" module: ", ?MODULE, "~n line: ", ?LINE, "~n", Str, "~n"]), Args])).
|
52
|
+
-endif.
|
53
|
+
-endif.
|
54
|
+
-endif.
|
55
|
+
|
56
|
+
% misultin server Options
|
57
|
+
-record(custom_opts, {
|
58
|
+
compress, % send compressed output if supported by browser
|
59
|
+
stream_support, % stream support option
|
60
|
+
loop, % the fun handling requests
|
61
|
+
ws_loop, % the loop handling websockets
|
62
|
+
ws_autoexit % true | false
|
63
|
+
}).
|
64
|
+
|
65
|
+
% Request
|
66
|
+
-record(req, {
|
67
|
+
socket, % the socket handling the request
|
68
|
+
socket_mode, % http | ssl
|
69
|
+
peer_addr, % peer IP | undefined
|
70
|
+
peer_port, % peer port | undefined
|
71
|
+
peer_cert, % undefined | the DER encoded peer certificate that can be decoded with public_key:pkix_decode_cert/2
|
72
|
+
connection = keep_alive, % keep_alive | close
|
73
|
+
content_length, % Integer
|
74
|
+
vsn, % {Maj,Min}
|
75
|
+
method, % 'GET'|'POST'
|
76
|
+
uri, % Truncated URI /index.html
|
77
|
+
args = "", % Part of URI after ?
|
78
|
+
headers, % [{Tag, Val}]
|
79
|
+
body = <<>> % Content Body
|
80
|
+
}).
|
81
|
+
|
82
|
+
% Websocket Request
|
83
|
+
-record(ws, {
|
84
|
+
socket, % the socket handling the request
|
85
|
+
socket_mode, % http | ssl
|
86
|
+
ws_autoexit, % websocket process is automatically killed: true | false
|
87
|
+
peer_addr, % peer IP | undefined
|
88
|
+
peer_port, % peer port | undefined
|
89
|
+
peer_cert, % undefined | the DER encoded peer certificate that can be decoded with public_key:pkix_decode_cert/2
|
90
|
+
vsn, % {Maj,Min} | {'draft-hixie', Ver}
|
91
|
+
origin, % the originator
|
92
|
+
host, % the host
|
93
|
+
path, % the websocket GET request path
|
94
|
+
headers % [{Tag, Val}]
|
95
|
+
}).
|
@@ -0,0 +1,55 @@
|
|
1
|
+
@echo off
|
2
|
+
REM ==========================================================================================================
|
3
|
+
REM MISULTIN - compile
|
4
|
+
REM
|
5
|
+
REM >-|-|-<°>
|
6
|
+
REM
|
7
|
+
REM Copyright (C) 2009, Roberto Ostinelli <roberto@ostinelli.net>
|
8
|
+
REM All rights reserved.
|
9
|
+
REM
|
10
|
+
REM BSD License
|
11
|
+
REM
|
12
|
+
REM Redistribution and use in source and binary forms, with or without modification, are permitted provided
|
13
|
+
REM that the following conditions are met:
|
14
|
+
REM
|
15
|
+
REM * Redistributions of source code must retain the above copyright notice, this list of conditions and the
|
16
|
+
REM following disclaimer.
|
17
|
+
REM * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
|
18
|
+
REM the following disclaimer in the documentation and/or other materials provided with the distribution.
|
19
|
+
REM * Neither the name of the authors nor the names of its contributors may be used to endorse or promote
|
20
|
+
REM products derived from this software without specific prior written permission.
|
21
|
+
REM
|
22
|
+
REM THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
|
23
|
+
REM WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
24
|
+
REM PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
25
|
+
REM ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
26
|
+
REM TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
27
|
+
REM HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
28
|
+
REM NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
29
|
+
REM POSSIBILITY OF SUCH DAMAGE.
|
30
|
+
REM ==========================================================================================================
|
31
|
+
|
32
|
+
:BEGIN
|
33
|
+
IF "%1"=="debug" GOTO SETDEBUG
|
34
|
+
IF "%1"=="example" GOTO EXAMPLES
|
35
|
+
IF "%1"=="clean" GOTO CLEAN
|
36
|
+
GOTO COMPILE
|
37
|
+
|
38
|
+
:SETDEBUG
|
39
|
+
SET command=-D log_debug
|
40
|
+
GOTO COMPILE
|
41
|
+
|
42
|
+
:EXAMPLES
|
43
|
+
mkdir ebin
|
44
|
+
FOR %%f in (examples\*.erl) DO erlc -W %command% -o ebin "%%f"
|
45
|
+
|
46
|
+
:COMPILE
|
47
|
+
mkdir ebin
|
48
|
+
FOR %%f in (src\*.erl) DO erlc -W %command% -o ebin "%%f"
|
49
|
+
copy src\misultin.app.src ebin\misultin.app /Y
|
50
|
+
GOTO END
|
51
|
+
|
52
|
+
:CLEAN
|
53
|
+
FOR %%f in (ebin\*) DO del "%%f"
|
54
|
+
|
55
|
+
:END
|
@@ -0,0 +1,12 @@
|
|
1
|
+
==========================================================================================================
|
2
|
+
DISCLAIMER
|
3
|
+
==========================================================================================================
|
4
|
+
|
5
|
+
Please note that the included certificate 'test_certificate.pem' and private key 'test_privkey.pem' are
|
6
|
+
publicly available via the Misultin repositories, and should NOT be used for any secure application. These
|
7
|
+
have been provided here for your testing comfort only.
|
8
|
+
|
9
|
+
You may consider getting your copy of OpenSSL <http://www.openssl.org> and generate your own certificate
|
10
|
+
and private key by issuing a command similar to:
|
11
|
+
|
12
|
+
openssl req -new -x509 -newkey rsa:1024 -days 365 -keyout test_privkey.pem -out test_certificate.pem
|
@@ -0,0 +1,21 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDhzCCAvCgAwIBAgIJAPFFl046vv7vMA0GCSqGSIb3DQEBBQUAMIGKMQswCQYD
|
3
|
+
VQQGEwJJVDENMAsGA1UECBMEQ29tbzENMAsGA1UEBxMEQ29tbzERMA8GA1UEChMI
|
4
|
+
TWlzdWx0aW4xETAPBgNVBAsTCE1pc3VsdGluMREwDwYDVQQDEwhNaXN1bHRpbjEk
|
5
|
+
MCIGCSqGSIb3DQEJARYVcm9iZXJ0b0Bvc3RpbmVsbGkubmV0MB4XDTEwMDQyMDE3
|
6
|
+
NDczOFoXDTIwMDQxNzE3NDczOFowgYoxCzAJBgNVBAYTAklUMQ0wCwYDVQQIEwRD
|
7
|
+
b21vMQ0wCwYDVQQHEwRDb21vMREwDwYDVQQKEwhNaXN1bHRpbjERMA8GA1UECxMI
|
8
|
+
TWlzdWx0aW4xETAPBgNVBAMTCE1pc3VsdGluMSQwIgYJKoZIhvcNAQkBFhVyb2Jl
|
9
|
+
cnRvQG9zdGluZWxsaS5uZXQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM8A
|
10
|
+
BT4OzNCcgrfVnzmEp8VdyR+O0ubsSBunX8J/BTKbWgZVrrGrY9fO8AkVmD1VFm8n
|
11
|
+
w/yLlz/Ow24j40UCY82Y9gMDgADa3BqcDPn1lPdGpOHhaMXMRFKnrVOfwMPE0wfx
|
12
|
+
kpr9/I5rAPAnkX1WtvOWMK0V5yGsuIMBd2S4VzmrAgMBAAGjgfIwge8wHQYDVR0O
|
13
|
+
BBYEFItpRD/8fT21N/pLeSWKexZHWP/3MIG/BgNVHSMEgbcwgbSAFItpRD/8fT21
|
14
|
+
N/pLeSWKexZHWP/3oYGQpIGNMIGKMQswCQYDVQQGEwJJVDENMAsGA1UECBMEQ29t
|
15
|
+
bzENMAsGA1UEBxMEQ29tbzERMA8GA1UEChMITWlzdWx0aW4xETAPBgNVBAsTCE1p
|
16
|
+
c3VsdGluMREwDwYDVQQDEwhNaXN1bHRpbjEkMCIGCSqGSIb3DQEJARYVcm9iZXJ0
|
17
|
+
b0Bvc3RpbmVsbGkubmV0ggkA8UWXTjq+/u8wDAYDVR0TBAUwAwEB/zANBgkqhkiG
|
18
|
+
9w0BAQUFAAOBgQA5ePVuJo6LcHegSKfD1GlTu2Ffkom547e9PYmKBaDyuNhCfP4F
|
19
|
+
YB4GMi1SZeCsaYzJpEOCY9JEJD8hJO7xPnyKnwc3FhT7KfYHWO7FNZdmdMxE99mi
|
20
|
+
lolCiDfglJkQCPihtxK5TKBDK+lzMub+1Xmc3VOvIuonzjh+VkSz1rCciQ==
|
21
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,18 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
Proc-Type: 4,ENCRYPTED
|
3
|
+
DEK-Info: DES-EDE3-CBC,E6F891BC96EC9B16
|
4
|
+
|
5
|
+
kBpeVQGiACcY96k5ix5H+/Htt6XIGB2cyUhBMZlgBPQSAttU9B94YV+nXZmIVi8f
|
6
|
+
r0cBDqGZ7uv+YjQom1HWw/NilWJr5x67VOhBGvg3kb2wYe3aMeGrmqRpPqNi6K2U
|
7
|
+
t9QuragXj7tyFu8+sYnW0SeI7GN9aghF2S9bVKasOFDBHGDg1igb7PzBsctrQh1Q
|
8
|
+
Hhkl2/Ql3+j18yUNZ2vCZgvGE2UfX5TJ79irpUCFiSgbf31EU7WCePZfSuNZfJB7
|
9
|
+
hjQM5q/vfEmgqVCdVR1W8wFxdalPJOA819gKwpKBBgpfWPn2Gvyw/yAhm3FPLasG
|
10
|
+
OgrY9PgsAcfozXgCOJP6NEP8IHzvb7kWoTussgBCd8P2Vv+YTp8WkBQDtpAlQExf
|
11
|
+
03sKUE9+pDXnzLnq4Pore6xBlzcZ4hu35WUo854UApRT/OQB5+Kmth9pqax43bbp
|
12
|
+
9Lfg6Zg9NXDuGHbRdK3U48uXLa7lDi5TMJ3LHuJ+DxZq4WNCTbMA3YZTFnjGiD/N
|
13
|
+
NvTy54oQThjn67N7BQe3PeWI2ryGEWJAXShnc0ZTaxQaQn+18zVAe2tQOFUPhbbA
|
14
|
+
Bq7zx49gea1tlJC1DHLktmw72v0g5W3HZ2fP1m+9socH9n4iORGEpicwuMgf9Tlb
|
15
|
+
T0mFP3hL0y1wEdBoohF6Euk1Y33P44tbXsYn6bP2/mVmWphVA3wWOocMYw/UgSxM
|
16
|
+
pzpC+z6y19dhqYNJyywgsMv6GQdrovW1DF2udmjx1Mv7qbwdJlH0GyLdyBL8aZFb
|
17
|
+
WBXI2PjWWtZS/F1U7QsELzV3mM1U8n+K5hZuBPtvLzohpq2W59tPkg==
|
18
|
+
-----END RSA PRIVATE KEY-----
|
@@ -0,0 +1,9 @@
|
|
1
|
+
{application, misultin,
|
2
|
+
[
|
3
|
+
{description, "Lightweight HTTP(s) and Websockets Server Library"},
|
4
|
+
{vsn, "0.6.1"},
|
5
|
+
{modules, [misultin, misultin_req, misultin_socket, misultin_http, misultin_utility, misultin_websocket, misultin_ws]},
|
6
|
+
{registered, [misultin]},
|
7
|
+
{env, []},
|
8
|
+
{applications, [kernel, stdlib]}
|
9
|
+
]}.
|
@@ -0,0 +1,338 @@
|
|
1
|
+
% ==========================================================================================================
|
2
|
+
% MISULTIN - Main
|
3
|
+
%
|
4
|
+
% >-|-|-(°>
|
5
|
+
%
|
6
|
+
% Copyright (C) 2010, Roberto Ostinelli <roberto@ostinelli.net>, Sean Hinde.
|
7
|
+
% All rights reserved.
|
8
|
+
%
|
9
|
+
% Code portions from Sean Hinde have been originally taken under BSD license from Trapexit at the address:
|
10
|
+
% <http://www.trapexit.org/A_fast_web_server_demonstrating_some_undocumented_Erlang_features>
|
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).
|
34
|
+
-behaviour(gen_server).
|
35
|
+
-vsn("0.6.1").
|
36
|
+
|
37
|
+
% gen_server callbacks
|
38
|
+
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
|
39
|
+
|
40
|
+
% API
|
41
|
+
-export([start_link/1, stop/0, persistent_socket_pid_add/1, persistent_socket_pid_remove/1]).
|
42
|
+
|
43
|
+
% macros
|
44
|
+
-define(SERVER, ?MODULE).
|
45
|
+
|
46
|
+
% records
|
47
|
+
-record(state, {
|
48
|
+
% tcp
|
49
|
+
listen_socket,
|
50
|
+
socket_mode,
|
51
|
+
port,
|
52
|
+
options,
|
53
|
+
acceptor,
|
54
|
+
recv_timeout,
|
55
|
+
persistent_sock_references = [],
|
56
|
+
% misultin
|
57
|
+
custom_opts
|
58
|
+
}).
|
59
|
+
|
60
|
+
% includes
|
61
|
+
-include("../include/misultin.hrl").
|
62
|
+
|
63
|
+
|
64
|
+
% ============================ \/ API ======================================================================
|
65
|
+
|
66
|
+
% Function: {ok,Pid} | ignore | {error, Error}
|
67
|
+
% Description: Starts the server.
|
68
|
+
start_link(Options) when is_list(Options) ->
|
69
|
+
gen_server:start_link({local, ?SERVER}, ?MODULE, [Options], []).
|
70
|
+
|
71
|
+
% Function: -> ok
|
72
|
+
% Description: Manually stops the server.
|
73
|
+
stop() ->
|
74
|
+
gen_server:cast(?SERVER, stop).
|
75
|
+
|
76
|
+
% Function -> ok
|
77
|
+
% Description: Adds a new persistent socket pid reference to status
|
78
|
+
persistent_socket_pid_add(WsPid) ->
|
79
|
+
gen_server:cast(?SERVER, {add_ws_pid, WsPid}).
|
80
|
+
|
81
|
+
% Function -> ok
|
82
|
+
% Description: Remove a persistent socket pid reference from status
|
83
|
+
persistent_socket_pid_remove(WsPid) ->
|
84
|
+
gen_server:cast(?SERVER, {remove_ws_pid, WsPid}).
|
85
|
+
|
86
|
+
% ============================ /\ API ======================================================================
|
87
|
+
|
88
|
+
|
89
|
+
% ============================ \/ GEN_SERVER CALLBACKS =====================================================
|
90
|
+
|
91
|
+
% ----------------------------------------------------------------------------------------------------------
|
92
|
+
% Function: -> {ok, State} | {ok, State, Timeout} | ignore | {stop, Reason}
|
93
|
+
% Description: Initiates the server.
|
94
|
+
% ----------------------------------------------------------------------------------------------------------
|
95
|
+
init([Options]) ->
|
96
|
+
process_flag(trap_exit, true),
|
97
|
+
?LOG_INFO("starting with Pid: ~p", [self()]),
|
98
|
+
% test and get options
|
99
|
+
OptionProps = [
|
100
|
+
% socket
|
101
|
+
{ip, {0, 0, 0, 0}, fun check_and_convert_string_to_ip/1, invalid_ip},
|
102
|
+
{port, 80, fun is_integer/1, port_not_integer},
|
103
|
+
{backlog, 128, fun is_integer/1, backlog_not_integer},
|
104
|
+
{recv_timeout, 30*1000, fun is_integer/1, recv_timeout_not_integer},
|
105
|
+
{ssl, false, fun check_ssl_options/1, invalid_ssl_options},
|
106
|
+
% misultin
|
107
|
+
{compress, false, fun is_boolean/1, invalid_compress_option},
|
108
|
+
{stream_support, true, fun is_boolean/1, invalid_stream_support_option},
|
109
|
+
{loop, {error, undefined_loop}, fun is_function/1, loop_not_function},
|
110
|
+
{ws_loop, none, fun is_function/1, ws_loop_not_function},
|
111
|
+
{ws_autoexit, true, fun is_boolean/1, invalid_ws_autoexit_option}
|
112
|
+
],
|
113
|
+
OptionsVerified = lists:foldl(fun(OptionName, Acc) -> [get_option(OptionName, Options)|Acc] end, [], OptionProps),
|
114
|
+
case proplists:get_value(error, OptionsVerified) of
|
115
|
+
undefined ->
|
116
|
+
% ok, no error found in options
|
117
|
+
% tcp options
|
118
|
+
Ip = proplists:get_value(ip, OptionsVerified),
|
119
|
+
Port = proplists:get_value(port, OptionsVerified),
|
120
|
+
Backlog = proplists:get_value(backlog, OptionsVerified),
|
121
|
+
RecvTimeout = proplists:get_value(recv_timeout, OptionsVerified),
|
122
|
+
SslOptions0 = proplists:get_value(ssl, OptionsVerified),
|
123
|
+
% misultin options
|
124
|
+
Compress = proplists:get_value(compress, OptionsVerified),
|
125
|
+
StreamSupport = proplists:get_value(stream_support, OptionsVerified),
|
126
|
+
Loop = proplists:get_value(loop, OptionsVerified),
|
127
|
+
WsLoop = proplists:get_value(ws_loop, OptionsVerified),
|
128
|
+
WsAutoExit = proplists:get_value(ws_autoexit, OptionsVerified),
|
129
|
+
% ipv6 support
|
130
|
+
?LOG_DEBUG("ip address is: ~p", [Ip]),
|
131
|
+
% set additional options according to socket mode if necessary
|
132
|
+
Continue = case SslOptions0 of
|
133
|
+
false ->
|
134
|
+
% without SSL
|
135
|
+
SocketMode = http,
|
136
|
+
InetOpt = case Ip of
|
137
|
+
{_, _, _, _} ->
|
138
|
+
% IPv4
|
139
|
+
inet;
|
140
|
+
{_, _, _, _, _, _, _, _} ->
|
141
|
+
% IPv6
|
142
|
+
inet6
|
143
|
+
end,
|
144
|
+
AdditionalOptions = [InetOpt],
|
145
|
+
true;
|
146
|
+
_ ->
|
147
|
+
% with SSL
|
148
|
+
SocketMode = ssl,
|
149
|
+
% the only current way to use {active, once} in Ssl is to start the crypto module
|
150
|
+
% and set {ssl_imp, new} as SSL option, see
|
151
|
+
% <http://www.erlang.org/cgi-bin/ezmlm-cgi?4:mss:50633:201004:fpopocbfkpppecdembbe>
|
152
|
+
AdditionalOptions = [{ssl_imp, new}|SslOptions0],
|
153
|
+
% start Ssl and crypto applications if necessary, and get outcomes
|
154
|
+
AppStartResults = lists:keyfind(error, 1, [start_application(ssl), start_application(crypto)]),
|
155
|
+
case AppStartResults of
|
156
|
+
false ->
|
157
|
+
% all applications started succesfully
|
158
|
+
true;
|
159
|
+
_ ->
|
160
|
+
% error starting application
|
161
|
+
{error, AppStartResults}
|
162
|
+
end
|
163
|
+
end,
|
164
|
+
% proceed?
|
165
|
+
case Continue of
|
166
|
+
true ->
|
167
|
+
% set options
|
168
|
+
OptionsTcp = [binary, {packet, raw}, {ip, Ip}, {reuseaddr, true}, {active, false}, {backlog, Backlog}|AdditionalOptions],
|
169
|
+
% build custom_opts
|
170
|
+
CustomOpts = #custom_opts{compress = Compress, stream_support = StreamSupport, loop = Loop, ws_loop = WsLoop, ws_autoexit = WsAutoExit},
|
171
|
+
% create listening socket and acceptor
|
172
|
+
case create_listener_and_acceptor(Port, OptionsTcp, RecvTimeout, SocketMode, CustomOpts) of
|
173
|
+
{ok, ListenSocket, AcceptorPid} ->
|
174
|
+
?LOG_DEBUG("listening socket and acceptor succesfully started",[]),
|
175
|
+
{ok, #state{listen_socket = ListenSocket, socket_mode = SocketMode, port = Port, options = OptionsTcp, acceptor = AcceptorPid, recv_timeout = RecvTimeout, custom_opts = CustomOpts}};
|
176
|
+
{error, Reason} ->
|
177
|
+
?LOG_ERROR("error starting listener socket: ~p", [Reason]),
|
178
|
+
{stop, Reason}
|
179
|
+
end;
|
180
|
+
Error ->
|
181
|
+
{stop, Error}
|
182
|
+
end;
|
183
|
+
Reason ->
|
184
|
+
% error found in options
|
185
|
+
{stop, Reason}
|
186
|
+
end.
|
187
|
+
|
188
|
+
% ----------------------------------------------------------------------------------------------------------
|
189
|
+
% Function: handle_call(Request, From, State) -> {reply, Reply, State} | {reply, Reply, State, Timeout} |
|
190
|
+
% {noreply, State} | {noreply, State, Timeout} |
|
191
|
+
% {stop, Reason, Reply, State} | {stop, Reason, State}
|
192
|
+
% Description: Handling call messages.
|
193
|
+
% ----------------------------------------------------------------------------------------------------------
|
194
|
+
|
195
|
+
% handle_call generic fallback
|
196
|
+
handle_call(_Request, _From, State) ->
|
197
|
+
{reply, undefined, State}.
|
198
|
+
|
199
|
+
% ----------------------------------------------------------------------------------------------------------
|
200
|
+
% Function: handle_cast(Msg, State) -> {noreply, State} | {noreply, State, Timeout} | {stop, Reason, State}
|
201
|
+
% Description: Handling cast messages.
|
202
|
+
% ----------------------------------------------------------------------------------------------------------
|
203
|
+
|
204
|
+
% manual shutdown
|
205
|
+
handle_cast(stop, State) ->
|
206
|
+
?LOG_INFO("manual shutdown..", []),
|
207
|
+
{stop, normal, State};
|
208
|
+
|
209
|
+
% add persistent socket reference to server
|
210
|
+
handle_cast({add_ws_pid, PsPid}, #state{persistent_sock_references = PersistentSockReferences} = State) ->
|
211
|
+
{noreply, State#state{persistent_sock_references = [PsPid|PersistentSockReferences]}};
|
212
|
+
|
213
|
+
% remove persistent socket reference from server
|
214
|
+
handle_cast({remove_ws_pid, PsPid}, #state{persistent_sock_references = PersistentSockReferences} = State) ->
|
215
|
+
{noreply, State#state{persistent_sock_references = lists:delete(PsPid, PersistentSockReferences)}};
|
216
|
+
|
217
|
+
% handle_cast generic fallback (ignore)
|
218
|
+
handle_cast(_Msg, State) ->
|
219
|
+
?LOG_WARNING("received unknown cast message: ~p", [_Msg]),
|
220
|
+
{noreply, State}.
|
221
|
+
|
222
|
+
% ----------------------------------------------------------------------------------------------------------
|
223
|
+
% Function: handle_info(Info, State) -> {noreply, State} | {noreply, State, Timeout} | {stop, Reason, State}
|
224
|
+
% Description: Handling all non call/cast messages.
|
225
|
+
% ----------------------------------------------------------------------------------------------------------
|
226
|
+
|
227
|
+
% Acceptor died
|
228
|
+
% -> shutdown in progress, ignore
|
229
|
+
handle_info({'EXIT', Pid, {error, {{accept_failed, {shutdown, _}}}}}, #state{acceptor = Pid} = State) -> {noreply, State};
|
230
|
+
% -> respawn listening socket and acceptor
|
231
|
+
handle_info({'EXIT', Pid, _Reason}, #state{listen_socket = ListenSocket, socket_mode = SocketMode, port = Port, acceptor = Pid, recv_timeout = RecvTimeout, custom_opts = CustomOpts} = State) ->
|
232
|
+
?LOG_WARNING("acceptor has died with reason: ~p, respawning", [_Reason]),
|
233
|
+
AcceptorPid = misultin_socket:start_link(ListenSocket, Port, RecvTimeout, SocketMode, CustomOpts),
|
234
|
+
{noreply, State#state{acceptor = AcceptorPid}};
|
235
|
+
|
236
|
+
% handle_info generic fallback (ignore)
|
237
|
+
handle_info(_Info, State) ->
|
238
|
+
?LOG_WARNING("received unknown info message: ~p", [_Info]),
|
239
|
+
{noreply, State}.
|
240
|
+
|
241
|
+
% ----------------------------------------------------------------------------------------------------------
|
242
|
+
% Function: terminate(Reason, State) -> void()
|
243
|
+
% Description: This function is called by a gen_server when it is about to terminate. When it returns,
|
244
|
+
% the gen_server terminates with Reason. The return value is ignored.
|
245
|
+
% ----------------------------------------------------------------------------------------------------------
|
246
|
+
terminate(_Reason, #state{listen_socket = ListenSocket, socket_mode = SocketMode, acceptor = AcceptorPid, persistent_sock_references = PersistentSockReferences}) ->
|
247
|
+
?LOG_INFO("shutting down server with Pid ~p", [self()]),
|
248
|
+
% kill acceptor
|
249
|
+
exit(AcceptorPid, kill),
|
250
|
+
% send a shutdown message to all persistent sockets, if any
|
251
|
+
?LOG_DEBUG("sending shutdown message to persistent sockets, if any", []),
|
252
|
+
lists:foreach(fun(PsPid) -> catch PsPid ! shutdown end, PersistentSockReferences),
|
253
|
+
% stop tcp socket
|
254
|
+
misultin_socket:close(ListenSocket, SocketMode),
|
255
|
+
terminated.
|
256
|
+
|
257
|
+
% ----------------------------------------------------------------------------------------------------------
|
258
|
+
% Function: code_change(OldVsn, State, Extra) -> {ok, NewState}
|
259
|
+
% Description: Convert process state when code is changed.
|
260
|
+
% ----------------------------------------------------------------------------------------------------------
|
261
|
+
code_change(_OldVsn, State, _Extra) ->
|
262
|
+
{ok, State}.
|
263
|
+
|
264
|
+
% ============================ /\ GEN_SERVER CALLBACKS =====================================================
|
265
|
+
|
266
|
+
|
267
|
+
% ============================ \/ INTERNAL FUNCTIONS =======================================================
|
268
|
+
|
269
|
+
% Function: -> false | IpTuple
|
270
|
+
% Description: Checks and converts a string Ip to inet repr.
|
271
|
+
check_and_convert_string_to_ip(Ip) ->
|
272
|
+
case inet_parse:address(Ip) of
|
273
|
+
{error, _Reason} ->
|
274
|
+
false;
|
275
|
+
{ok, IpTuple} ->
|
276
|
+
IpTuple
|
277
|
+
end.
|
278
|
+
|
279
|
+
% Function: -> true | false
|
280
|
+
% Description: Checks if all necessary Ssl Options have been specified
|
281
|
+
check_ssl_options(SslOptions) ->
|
282
|
+
Opts = [verify, fail_if_no_peer_cert, verify_fun, depth, certfile, keyfile, password, cacertfile, ciphers, reuse_sessions, reuse_session],
|
283
|
+
F = fun({Name, _Value}) ->
|
284
|
+
?LOG_DEBUG("testing ~p", [Name]),
|
285
|
+
case lists:member(Name, Opts) of
|
286
|
+
false -> ?LOG_DEBUG("NOT found ~p", [Name]),false;
|
287
|
+
_ -> ?LOG_DEBUG("found ~p", [Name]),true
|
288
|
+
end
|
289
|
+
end,
|
290
|
+
lists:all(F, SslOptions).
|
291
|
+
|
292
|
+
% Description: Validate and get misultin options.
|
293
|
+
get_option({OptionName, DefaultValue, CheckAndConvertFun, FailTypeError}, Options) ->
|
294
|
+
case proplists:get_value(OptionName, Options) of
|
295
|
+
undefined ->
|
296
|
+
case DefaultValue of
|
297
|
+
{error, Reason} ->
|
298
|
+
{error, Reason};
|
299
|
+
Value ->
|
300
|
+
{OptionName, Value}
|
301
|
+
end;
|
302
|
+
Value ->
|
303
|
+
case CheckAndConvertFun(Value) of
|
304
|
+
false ->
|
305
|
+
{error, {FailTypeError, Value}};
|
306
|
+
true ->
|
307
|
+
{OptionName, Value};
|
308
|
+
OutValue ->
|
309
|
+
{OptionName, OutValue}
|
310
|
+
end
|
311
|
+
end.
|
312
|
+
|
313
|
+
% Function: -> ok | {error, Reason}
|
314
|
+
% Description: Start an application.
|
315
|
+
start_application(Application) ->
|
316
|
+
case lists:keyfind(Application, 1, application:which_applications()) of
|
317
|
+
false ->
|
318
|
+
application:start(Application);
|
319
|
+
_ ->
|
320
|
+
ok
|
321
|
+
end.
|
322
|
+
|
323
|
+
% Function: -> {ok, ListenSocket, AcceptorPid} | {error, Error}
|
324
|
+
% Description: Create listening socket
|
325
|
+
create_listener_and_acceptor(Port, Options, RecvTimeout, SocketMode, CustomOpts) ->
|
326
|
+
?LOG_DEBUG("starting listening ~p socket with options ~p on port ~p", [SocketMode, Options, Port]),
|
327
|
+
case misultin_socket:listen(Port, Options, SocketMode) of
|
328
|
+
{ok, ListenSocket} ->
|
329
|
+
?LOG_DEBUG("starting acceptor",[]),
|
330
|
+
% create acceptor
|
331
|
+
AcceptorPid = misultin_socket:start_link(ListenSocket, Port, RecvTimeout, SocketMode, CustomOpts),
|
332
|
+
{ok, ListenSocket, AcceptorPid};
|
333
|
+
{error, Reason} ->
|
334
|
+
% error
|
335
|
+
{error, Reason}
|
336
|
+
end.
|
337
|
+
|
338
|
+
% ============================ /\ INTERNAL FUNCTIONS =======================================================
|