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.
Files changed (61) hide show
  1. data/erlang/lib/capricorn/ebin/capricorn.app +2 -1
  2. data/erlang/lib/capricorn/include/capricorn.hrl +1 -0
  3. data/erlang/lib/capricorn/src/cap_cluster_gems.erl +6 -1
  4. data/erlang/lib/capricorn/src/cap_console_dispatcher.erl +214 -0
  5. data/erlang/lib/capricorn/src/cap_machine_apps.erl +25 -1
  6. data/erlang/lib/capricorn/src/cap_sup.erl +14 -0
  7. data/erlang/lib/ejson/Makefile +24 -0
  8. data/erlang/lib/ejson/ebin/ejson.app +9 -0
  9. data/erlang/lib/ejson/include/ejson.hrl +40 -0
  10. data/erlang/lib/ejson/rebar.config +3 -0
  11. data/erlang/lib/ejson/src/ejson.erl +22 -0
  12. data/erlang/lib/ejson/src/ejson_decode.erl +337 -0
  13. data/erlang/lib/ejson/src/ejson_encode.erl +124 -0
  14. data/erlang/lib/ejson/test/arrays.escript +47 -0
  15. data/erlang/lib/ejson/test/compound.escript +56 -0
  16. data/erlang/lib/ejson/test/literals.escript +30 -0
  17. data/erlang/lib/ejson/test/numbers.escript +70 -0
  18. data/erlang/lib/ejson/test/objects.escript +51 -0
  19. data/erlang/lib/ejson/test/strings.escript +49 -0
  20. data/erlang/lib/ejson/test/timing.escript +43 -0
  21. data/erlang/lib/ejson/test/timing.json +382 -0
  22. data/erlang/lib/ejson/vendor/mochijson2.erl +621 -0
  23. data/erlang/lib/ejson/vendor/rfc4627.erl +625 -0
  24. data/erlang/lib/misultin/LICENSE.txt +41 -0
  25. data/erlang/lib/misultin/Makefile +26 -0
  26. data/erlang/lib/misultin/README.txt +120 -0
  27. data/erlang/lib/misultin/ebin/misultin.app +9 -0
  28. data/erlang/lib/misultin/examples/misultin_compress.erl +43 -0
  29. data/erlang/lib/misultin/examples/misultin_echo.erl +58 -0
  30. data/erlang/lib/misultin/examples/misultin_file.erl +43 -0
  31. data/erlang/lib/misultin/examples/misultin_gen_server.erl +158 -0
  32. data/erlang/lib/misultin/examples/misultin_get_variable.erl +55 -0
  33. data/erlang/lib/misultin/examples/misultin_hello_world.erl +43 -0
  34. data/erlang/lib/misultin/examples/misultin_rest.erl +68 -0
  35. data/erlang/lib/misultin/examples/misultin_ssl.erl +51 -0
  36. data/erlang/lib/misultin/examples/misultin_stream.erl +55 -0
  37. data/erlang/lib/misultin/examples/misultin_websocket_event_example.erl +103 -0
  38. data/erlang/lib/misultin/examples/misultin_websocket_example.erl +95 -0
  39. data/erlang/lib/misultin/include/misultin.hrl +95 -0
  40. data/erlang/lib/misultin/make.bat +55 -0
  41. data/erlang/lib/misultin/priv/README.txt +12 -0
  42. data/erlang/lib/misultin/priv/test_certificate.pem +21 -0
  43. data/erlang/lib/misultin/priv/test_privkey.pem +18 -0
  44. data/erlang/lib/misultin/rebar.config +3 -0
  45. data/erlang/lib/misultin/src/misultin.app.src +9 -0
  46. data/erlang/lib/misultin/src/misultin.erl +338 -0
  47. data/erlang/lib/misultin/src/misultin_http.erl +488 -0
  48. data/erlang/lib/misultin/src/misultin_req.erl +280 -0
  49. data/erlang/lib/misultin/src/misultin_socket.erl +193 -0
  50. data/erlang/lib/misultin/src/misultin_utility.erl +357 -0
  51. data/erlang/lib/misultin/src/misultin_websocket.erl +252 -0
  52. data/erlang/lib/misultin/src/misultin_ws.erl +78 -0
  53. data/erlang/rebar.config +2 -0
  54. data/erlang/rel/overlay/etc/capricorn/app.config +4 -0
  55. data/erlang/rel/reltool.config +5 -0
  56. data/lib/capricorn/recipes/apache-debian.rb +1 -1
  57. data/lib/capricorn/recipes/centos-plesk.rb +1 -1
  58. data/lib/capricorn/recipes/debian-plesk95.rb +1 -2
  59. data/lib/capricorn/recipes/macports.rb +1 -1
  60. data/lib/capricorn/version.rb +1 -1
  61. metadata +51 -4
@@ -0,0 +1,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,3 @@
1
+ {cover_enabled, true}.
2
+ {erl_opts, [debug_info, fail_on_warning, {i, ".."}]}.
3
+ {lib_dirs, [".."]}.
@@ -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 =======================================================