capricorn 2.0.8 → 2.0.9

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