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,280 @@
|
|
1
|
+
% ==========================================================================================================
|
2
|
+
% MISULTIN - Request
|
3
|
+
%
|
4
|
+
% >-|-|-(°>
|
5
|
+
%
|
6
|
+
% Copyright (C) 2010, Roberto Ostinelli <roberto@ostinelli.net>,
|
7
|
+
% Bob Ippolito <bob@mochimedia.com> for Mochi Media, Inc.
|
8
|
+
% All rights reserved.
|
9
|
+
%
|
10
|
+
% Code portions from Bob Ippolito have been originally taken under MIT license from MOCHIWEB:
|
11
|
+
% <http://code.google.com/p/mochiweb/>
|
12
|
+
%
|
13
|
+
% BSD License
|
14
|
+
%
|
15
|
+
% Redistribution and use in source and binary forms, with or without modification, are permitted provided
|
16
|
+
% that the following conditions are met:
|
17
|
+
%
|
18
|
+
% * Redistributions of source code must retain the above copyright notice, this list of conditions and the
|
19
|
+
% following disclaimer.
|
20
|
+
% * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
|
21
|
+
% the following disclaimer in the documentation and/or other materials provided with the distribution.
|
22
|
+
% * Neither the name of the authors nor the names of its contributors may be used to endorse or promote
|
23
|
+
% products derived from this software without specific prior written permission.
|
24
|
+
%
|
25
|
+
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
|
26
|
+
% WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
27
|
+
% PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
28
|
+
% ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
29
|
+
% TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
30
|
+
% HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
31
|
+
% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
32
|
+
% POSSIBILITY OF SUCH DAMAGE.
|
33
|
+
% ==========================================================================================================
|
34
|
+
-module(misultin_req, [Req, SocketPid]).
|
35
|
+
-vsn("0.6.1").
|
36
|
+
|
37
|
+
% macros
|
38
|
+
-define(PERCENT, 37). % $\%
|
39
|
+
-define(FULLSTOP, 46). % $\.
|
40
|
+
-define(IS_HEX(C), ((C >= $0 andalso C =< $9) orelse
|
41
|
+
(C >= $a andalso C =< $f) orelse
|
42
|
+
(C >= $A andalso C =< $F))).
|
43
|
+
-define(FILE_READ_BUFFER, 64*1012).
|
44
|
+
|
45
|
+
% API
|
46
|
+
-export([raw/0]).
|
47
|
+
-export([ok/1, ok/2, ok/3, respond/2, respond/3, respond/4, stream/1, stream/2, stream/3]).
|
48
|
+
-export([get/1, parse_qs/0, parse_post/0, file/1, file/2, resource/1]).
|
49
|
+
|
50
|
+
% includes
|
51
|
+
-include("../include/misultin.hrl").
|
52
|
+
-include_lib("kernel/include/file.hrl").
|
53
|
+
|
54
|
+
|
55
|
+
% ============================ \/ API ======================================================================
|
56
|
+
|
57
|
+
% Description: Returns raw request content.
|
58
|
+
raw() ->
|
59
|
+
Req.
|
60
|
+
|
61
|
+
% Description: Get request info.
|
62
|
+
get(socket) ->
|
63
|
+
Req#req.socket;
|
64
|
+
get(socket_mode) ->
|
65
|
+
Req#req.socket_mode;
|
66
|
+
get(peer_addr) ->
|
67
|
+
Req#req.peer_addr;
|
68
|
+
get(peer_port) ->
|
69
|
+
Req#req.peer_port;
|
70
|
+
get(peer_cert) ->
|
71
|
+
Req#req.peer_cert;
|
72
|
+
get(connection) ->
|
73
|
+
Req#req.connection;
|
74
|
+
get(content_length) ->
|
75
|
+
Req#req.content_length;
|
76
|
+
get(vsn) ->
|
77
|
+
Req#req.vsn;
|
78
|
+
get(method) ->
|
79
|
+
Req#req.method;
|
80
|
+
get(uri) ->
|
81
|
+
Req#req.uri;
|
82
|
+
get(args) ->
|
83
|
+
Req#req.args;
|
84
|
+
get(headers) ->
|
85
|
+
Req#req.headers;
|
86
|
+
get(body) ->
|
87
|
+
Req#req.body.
|
88
|
+
|
89
|
+
% Description: Formats a 200 response.
|
90
|
+
ok(Template) ->
|
91
|
+
ok([], Template).
|
92
|
+
ok(Headers, Template) ->
|
93
|
+
respond(200, Headers, Template).
|
94
|
+
ok(Headers, Template, Vars) ->
|
95
|
+
respond(200, Headers, Template, Vars).
|
96
|
+
|
97
|
+
% Description: Formats a response.
|
98
|
+
respond(HttpCode, Template) ->
|
99
|
+
respond(HttpCode, [], Template).
|
100
|
+
respond(HttpCode, Headers, Template) ->
|
101
|
+
{HttpCode, Headers, Template}.
|
102
|
+
respond(HttpCode, Headers, Template, Vars) when is_list(Template) =:= true ->
|
103
|
+
{HttpCode, Headers, io_lib:format(Template, Vars)}.
|
104
|
+
|
105
|
+
% Description: Start stream.
|
106
|
+
stream(close) ->
|
107
|
+
catch SocketPid ! stream_close;
|
108
|
+
stream(head) ->
|
109
|
+
stream(head, 200, []);
|
110
|
+
stream(Template) ->
|
111
|
+
catch SocketPid ! {stream_data, Template}.
|
112
|
+
stream(head, Headers) ->
|
113
|
+
stream(head, 200, Headers);
|
114
|
+
stream(Template, Vars) when is_list(Template) =:= true ->
|
115
|
+
catch SocketPid ! {stream_data, io_lib:format(Template, Vars)}.
|
116
|
+
stream(head, HttpCode, Headers) ->
|
117
|
+
catch SocketPid ! {stream_head, HttpCode, Headers}.
|
118
|
+
|
119
|
+
% Description: Sends a file to the browser.
|
120
|
+
file(FilePath) ->
|
121
|
+
file_send(FilePath, []).
|
122
|
+
% Description: Sends a file for download.
|
123
|
+
file(attachment, FilePath) ->
|
124
|
+
% get filename
|
125
|
+
FileName = filename:basename(FilePath),
|
126
|
+
file_send(FilePath, [{'Content-Disposition', lists:flatten(io_lib:format("attachment; filename=~s", [FileName]))}]).
|
127
|
+
|
128
|
+
% Description: Parse QueryString
|
129
|
+
parse_qs() ->
|
130
|
+
parse_qs(Req#req.args).
|
131
|
+
|
132
|
+
% Description: Parse Post
|
133
|
+
parse_post() ->
|
134
|
+
% get header confirmation
|
135
|
+
case misultin_utility:get_key_value('Content-Type', Req#req.headers) of
|
136
|
+
undefined ->
|
137
|
+
[];
|
138
|
+
ContentType ->
|
139
|
+
[Type|_CharSet] = string:tokens(ContentType, ";"),
|
140
|
+
case Type of
|
141
|
+
"application/x-www-form-urlencoded" ->
|
142
|
+
parse_qs(Req#req.body);
|
143
|
+
_Other ->
|
144
|
+
[]
|
145
|
+
end
|
146
|
+
end.
|
147
|
+
|
148
|
+
% Description: Sets resource elements for restful services.
|
149
|
+
resource(Options) when is_list(Options) ->
|
150
|
+
% clean uri
|
151
|
+
{_UriType, RawUri} = Req#req.uri,
|
152
|
+
Uri = lists:foldl(fun(Option, Acc) -> clean_uri(Option, Acc) end, RawUri, Options),
|
153
|
+
% split
|
154
|
+
string:tokens(Uri, "/").
|
155
|
+
|
156
|
+
% ============================ /\ API ======================================================================
|
157
|
+
|
158
|
+
|
159
|
+
|
160
|
+
% ============================ \/ INTERNAL FUNCTIONS =======================================================
|
161
|
+
|
162
|
+
% parse querystring & post
|
163
|
+
parse_qs(Binary) when is_binary(Binary) ->
|
164
|
+
parse_qs(binary_to_list(Binary));
|
165
|
+
parse_qs(String) ->
|
166
|
+
parse_qs(String, []).
|
167
|
+
parse_qs([], Acc) ->
|
168
|
+
lists:reverse(Acc);
|
169
|
+
parse_qs(String, Acc) ->
|
170
|
+
{Key, Rest} = parse_qs_key(String),
|
171
|
+
{Value, Rest1} = parse_qs_value(Rest),
|
172
|
+
parse_qs(Rest1, [{Key, Value} | Acc]).
|
173
|
+
parse_qs_key(String) ->
|
174
|
+
parse_qs_key(String, []).
|
175
|
+
parse_qs_key([], Acc) ->
|
176
|
+
{qs_revdecode(Acc), ""};
|
177
|
+
parse_qs_key([$= | Rest], Acc) ->
|
178
|
+
{qs_revdecode(Acc), Rest};
|
179
|
+
parse_qs_key(Rest=[$; | _], Acc) ->
|
180
|
+
{qs_revdecode(Acc), Rest};
|
181
|
+
parse_qs_key(Rest=[$& | _], Acc) ->
|
182
|
+
{qs_revdecode(Acc), Rest};
|
183
|
+
parse_qs_key([C | Rest], Acc) ->
|
184
|
+
parse_qs_key(Rest, [C | Acc]).
|
185
|
+
parse_qs_value(String) ->
|
186
|
+
parse_qs_value(String, []).
|
187
|
+
parse_qs_value([], Acc) ->
|
188
|
+
{qs_revdecode(Acc), ""};
|
189
|
+
parse_qs_value([$; | Rest], Acc) ->
|
190
|
+
{qs_revdecode(Acc), Rest};
|
191
|
+
parse_qs_value([$& | Rest], Acc) ->
|
192
|
+
{qs_revdecode(Acc), Rest};
|
193
|
+
parse_qs_value([C | Rest], Acc) ->
|
194
|
+
parse_qs_value(Rest, [C | Acc]).
|
195
|
+
|
196
|
+
% revdecode
|
197
|
+
qs_revdecode(S) ->
|
198
|
+
qs_revdecode(S, []).
|
199
|
+
qs_revdecode([], Acc) ->
|
200
|
+
Acc;
|
201
|
+
qs_revdecode([$+ | Rest], Acc) ->
|
202
|
+
qs_revdecode(Rest, [$\s | Acc]);
|
203
|
+
qs_revdecode([Lo, Hi, ?PERCENT | Rest], Acc) when ?IS_HEX(Lo), ?IS_HEX(Hi) ->
|
204
|
+
qs_revdecode(Rest, [(unhexdigit(Lo) bor (unhexdigit(Hi) bsl 4)) | Acc]);
|
205
|
+
qs_revdecode([C | Rest], Acc) ->
|
206
|
+
qs_revdecode(Rest, [C | Acc]).
|
207
|
+
|
208
|
+
% unexdigit
|
209
|
+
unhexdigit(C) when C >= $0, C =< $9 -> C - $0;
|
210
|
+
unhexdigit(C) when C >= $a, C =< $f -> C - $a + 10;
|
211
|
+
unhexdigit(C) when C >= $A, C =< $F -> C - $A + 10.
|
212
|
+
|
213
|
+
% unquote
|
214
|
+
unquote(Binary) when is_binary(Binary) ->
|
215
|
+
unquote(binary_to_list(Binary));
|
216
|
+
unquote(String) ->
|
217
|
+
qs_revdecode(lists:reverse(String)).
|
218
|
+
|
219
|
+
% Description: Clean URI.
|
220
|
+
clean_uri(lowercase, Uri) ->
|
221
|
+
string:to_lower(Uri);
|
222
|
+
clean_uri(urldecode, Uri) ->
|
223
|
+
unquote(Uri);
|
224
|
+
% ignore unexisting option
|
225
|
+
clean_uri(_Unavailable, Uri) ->
|
226
|
+
Uri.
|
227
|
+
|
228
|
+
% sending of a file
|
229
|
+
file_send(FilePath, Headers) ->
|
230
|
+
% get file size
|
231
|
+
case file:read_file_info(FilePath) of
|
232
|
+
{ok, FileInfo} ->
|
233
|
+
% get filesize
|
234
|
+
FileSize = FileInfo#file_info.size,
|
235
|
+
% send headers
|
236
|
+
HeadersFull = [{'Content-Type', misultin_utility:get_content_type(FilePath)}, {'Content-Size', FileSize} | Headers],
|
237
|
+
stream(head, HeadersFull),
|
238
|
+
% do the gradual sending
|
239
|
+
case file_open_and_send(FilePath) of
|
240
|
+
{error, _Reason} ->
|
241
|
+
{raw, misultin_utility:get_http_status_code(500)};
|
242
|
+
ok ->
|
243
|
+
% sending successful
|
244
|
+
ok
|
245
|
+
end;
|
246
|
+
{error, _Reason} ->
|
247
|
+
{raw, misultin_utility:get_http_status_code(500)}
|
248
|
+
end.
|
249
|
+
file_open_and_send(FilePath) ->
|
250
|
+
case file:open(FilePath, [read, binary]) of
|
251
|
+
{error, Reason} ->
|
252
|
+
{error, Reason};
|
253
|
+
{ok, IoDevice} ->
|
254
|
+
% read portions
|
255
|
+
case file_read_and_send(IoDevice, 0) of
|
256
|
+
{error, Reason} ->
|
257
|
+
file:close(IoDevice),
|
258
|
+
{error, Reason};
|
259
|
+
ok ->
|
260
|
+
file:close(IoDevice),
|
261
|
+
ok
|
262
|
+
end
|
263
|
+
end.
|
264
|
+
file_read_and_send(IoDevice, Position) ->
|
265
|
+
% read buffer
|
266
|
+
case file:pread(IoDevice, Position, ?FILE_READ_BUFFER) of
|
267
|
+
{ok, Data} ->
|
268
|
+
% file read, send
|
269
|
+
stream(Data),
|
270
|
+
% loop
|
271
|
+
file_read_and_send(IoDevice, Position + ?FILE_READ_BUFFER);
|
272
|
+
eof ->
|
273
|
+
% finished, close
|
274
|
+
stream(close),
|
275
|
+
ok;
|
276
|
+
{error, Reason} ->
|
277
|
+
{error, Reason}
|
278
|
+
end.
|
279
|
+
|
280
|
+
% ============================ /\ INTERNAL FUNCTIONS =======================================================
|
@@ -0,0 +1,193 @@
|
|
1
|
+
% ==========================================================================================================
|
2
|
+
% MISULTIN - Socket
|
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_socket).
|
34
|
+
-vsn("0.6.1").
|
35
|
+
|
36
|
+
% API
|
37
|
+
-export([start_link/5]).
|
38
|
+
|
39
|
+
% callbacks
|
40
|
+
-export([listener/5]).
|
41
|
+
|
42
|
+
% internal
|
43
|
+
-export([listen/3, setopts/3, recv/4, send/3, close/2]).
|
44
|
+
|
45
|
+
% includes
|
46
|
+
-include("../include/misultin.hrl").
|
47
|
+
|
48
|
+
|
49
|
+
% ============================ \/ API ======================================================================
|
50
|
+
|
51
|
+
% Function: {ok,Pid} | ignore | {error, Error}
|
52
|
+
% Description: Starts the socket.
|
53
|
+
start_link(ListenSocket, ListenPort, RecvTimeout, SocketMode, CustomOpts) ->
|
54
|
+
proc_lib:spawn_link(?MODULE, listener, [ListenSocket, ListenPort, RecvTimeout, SocketMode, CustomOpts]).
|
55
|
+
|
56
|
+
% Function: {ok,Pid} | ignore | {error, Error}
|
57
|
+
% Description: Starts the socket.
|
58
|
+
listener(ListenSocket, ListenPort, RecvTimeout, SocketMode, CustomOpts) ->
|
59
|
+
case catch accept(ListenSocket, SocketMode) of
|
60
|
+
{ok, {sslsocket, _, _} = Sock} ->
|
61
|
+
% received a SSL socket -> spawn a ssl_accept process to avoid locking the main listener
|
62
|
+
spawn(fun() ->
|
63
|
+
case ssl:ssl_accept(Sock, 60000) of
|
64
|
+
ok ->
|
65
|
+
create_socket_pid(Sock, ListenPort, RecvTimeout, SocketMode, CustomOpts);
|
66
|
+
{error, _Reason} ->
|
67
|
+
% could not negotiate a SSL transaction, leave process
|
68
|
+
?LOG_WARNING("could not negotiate a SSL transaction: ~p", [_Reason]),
|
69
|
+
catch close(Sock, SocketMode)
|
70
|
+
end
|
71
|
+
end),
|
72
|
+
% get back to accept loop
|
73
|
+
listener(ListenSocket, ListenPort, RecvTimeout, SocketMode, CustomOpts);
|
74
|
+
{ok, Sock} ->
|
75
|
+
% received a HTTP socket
|
76
|
+
create_socket_pid(Sock, ListenPort, RecvTimeout, SocketMode, CustomOpts),
|
77
|
+
% get back to accept loop
|
78
|
+
listener(ListenSocket, ListenPort, RecvTimeout, SocketMode, CustomOpts);
|
79
|
+
{error, _Error} ->
|
80
|
+
?LOG_WARNING("accept failed with error: ~p", [_Error]),
|
81
|
+
% get back to accept loop
|
82
|
+
listener(ListenSocket, ListenPort, RecvTimeout, SocketMode, CustomOpts);
|
83
|
+
{'EXIT', Error} ->
|
84
|
+
?LOG_ERROR("accept exited with error: ~p, quitting process", [Error]),
|
85
|
+
exit({error, {accept_failed, Error}})
|
86
|
+
end.
|
87
|
+
|
88
|
+
% ============================ /\ API ======================================================================
|
89
|
+
|
90
|
+
|
91
|
+
% ============================ \/ INTERNAL FUNCTIONS =======================================================
|
92
|
+
|
93
|
+
% start socket Pid
|
94
|
+
create_socket_pid(Sock, ListenPort, RecvTimeout, SocketMode, CustomOpts) ->
|
95
|
+
?LOG_DEBUG("accepted an incoming TCP connection in ~p mode on socket ~p, spawning controlling process", [SocketMode, Sock]),
|
96
|
+
Pid = spawn(fun() ->
|
97
|
+
receive
|
98
|
+
set ->
|
99
|
+
?LOG_DEBUG("activated controlling process ~p", [self()]),
|
100
|
+
% get peer address and port
|
101
|
+
{PeerAddr, PeerPort} = peername(Sock, SocketMode),
|
102
|
+
% get peer certificate, if any
|
103
|
+
PeerCert = peercert(Sock, SocketMode),
|
104
|
+
% jump to external callback
|
105
|
+
?LOG_DEBUG("jump to connection logic", []),
|
106
|
+
misultin_http:handle_data(Sock, SocketMode, ListenPort, PeerAddr, PeerPort, PeerCert, RecvTimeout, CustomOpts)
|
107
|
+
after 60000 ->
|
108
|
+
?LOG_ERROR("timeout waiting for set in controlling process, closing socket", []),
|
109
|
+
catch close(Sock, SocketMode)
|
110
|
+
end
|
111
|
+
end),
|
112
|
+
% set controlling process
|
113
|
+
case controlling_process(Sock, Pid, SocketMode) of
|
114
|
+
ok ->
|
115
|
+
Pid ! set;
|
116
|
+
{error, _Reason} ->
|
117
|
+
?LOG_ERROR("could not set controlling process: ~p, closing socket", [_Reason]),
|
118
|
+
catch close(Sock, SocketMode)
|
119
|
+
end.
|
120
|
+
|
121
|
+
% socket listen
|
122
|
+
listen(Port, Options, http) -> gen_tcp:listen(Port, Options);
|
123
|
+
listen(Port, Options, ssl) -> ssl:listen(Port, Options).
|
124
|
+
|
125
|
+
% socket accept
|
126
|
+
accept(ListenSocket, http) -> gen_tcp:accept(ListenSocket);
|
127
|
+
accept(ListenSocket, ssl) ->
|
128
|
+
try ssl:transport_accept(ListenSocket)
|
129
|
+
catch
|
130
|
+
error:{badmatch, {error, Reason}} ->
|
131
|
+
{error, Reason}
|
132
|
+
end.
|
133
|
+
|
134
|
+
% socket controlling process
|
135
|
+
controlling_process(Sock, Pid, http) -> gen_tcp:controlling_process(Sock, Pid);
|
136
|
+
controlling_process(Sock, Pid, ssl) -> ssl:controlling_process(Sock, Pid).
|
137
|
+
|
138
|
+
% Function: -> {PeerAddr, PeerPort} | PeerAddr = list() | undefined | PeerPort = integer() | undefined
|
139
|
+
% Description: Get socket peername
|
140
|
+
peername(Sock, http) -> peername(Sock, fun inet:peername/1);
|
141
|
+
peername(Sock, ssl) -> peername(Sock, fun ssl:peername/1);
|
142
|
+
peername(Sock, F) ->
|
143
|
+
case F(Sock) of
|
144
|
+
{ok, {Addr, Port}} ->
|
145
|
+
{Addr, Port};
|
146
|
+
{error, _Reason} ->
|
147
|
+
{undefined, undefined}
|
148
|
+
end.
|
149
|
+
|
150
|
+
% Function: -> Certificate | undefined
|
151
|
+
% Description: Get socket certificate
|
152
|
+
peercert(_Sock, http) -> undefined;
|
153
|
+
peercert(Sock, ssl) ->
|
154
|
+
case ssl:peercert(Sock) of
|
155
|
+
{ok, Cert} -> Cert;
|
156
|
+
{error, _Reason} -> undefined
|
157
|
+
end.
|
158
|
+
|
159
|
+
% socket set options
|
160
|
+
setopts(Sock, Options, http) -> inet:setopts(Sock, Options);
|
161
|
+
setopts(Sock, Options, ssl) -> ssl:setopts(Sock, Options).
|
162
|
+
|
163
|
+
% socket receive
|
164
|
+
recv(Sock, Len, RecvTimeout, http) -> gen_tcp:recv(Sock, Len, RecvTimeout);
|
165
|
+
recv(Sock, Len, RecvTimeout, ssl) -> ssl:recv(Sock, Len, RecvTimeout).
|
166
|
+
|
167
|
+
% socket send
|
168
|
+
send(Sock, Data, http) -> send(Sock, Data, fun gen_tcp:send/2);
|
169
|
+
send(Sock, Data, ssl) -> send(Sock, Data, fun ssl:send/2);
|
170
|
+
send(Sock, Data, F) ->
|
171
|
+
?LOG_DEBUG("sending data: ~p", [Data]),
|
172
|
+
case F(Sock, Data) of
|
173
|
+
ok ->
|
174
|
+
ok;
|
175
|
+
{error, _Reason} ->
|
176
|
+
?LOG_ERROR("worker crash: ~p", [_Reason]),
|
177
|
+
exit(normal)
|
178
|
+
end.
|
179
|
+
|
180
|
+
% TCP close
|
181
|
+
close(Sock, http) -> close(Sock, fun gen_tcp:close/1);
|
182
|
+
close(Sock, ssl) -> close(Sock, fun ssl:close/1);
|
183
|
+
close(Sock, F) ->
|
184
|
+
?LOG_DEBUG("closing socket", []),
|
185
|
+
case catch F(Sock) of
|
186
|
+
ok ->
|
187
|
+
ok;
|
188
|
+
_Else ->
|
189
|
+
?LOG_WARNING("could not close socket: ~p", [_Else]),
|
190
|
+
exit(normal)
|
191
|
+
end.
|
192
|
+
|
193
|
+
% ============================ /\ INTERNAL FUNCTIONS =======================================================
|