capricorn 2.0.8 → 2.0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/erlang/lib/capricorn/ebin/capricorn.app +2 -1
- data/erlang/lib/capricorn/include/capricorn.hrl +1 -0
- data/erlang/lib/capricorn/src/cap_cluster_gems.erl +6 -1
- data/erlang/lib/capricorn/src/cap_console_dispatcher.erl +214 -0
- data/erlang/lib/capricorn/src/cap_machine_apps.erl +25 -1
- data/erlang/lib/capricorn/src/cap_sup.erl +14 -0
- data/erlang/lib/ejson/Makefile +24 -0
- data/erlang/lib/ejson/ebin/ejson.app +9 -0
- data/erlang/lib/ejson/include/ejson.hrl +40 -0
- data/erlang/lib/ejson/rebar.config +3 -0
- data/erlang/lib/ejson/src/ejson.erl +22 -0
- data/erlang/lib/ejson/src/ejson_decode.erl +337 -0
- data/erlang/lib/ejson/src/ejson_encode.erl +124 -0
- data/erlang/lib/ejson/test/arrays.escript +47 -0
- data/erlang/lib/ejson/test/compound.escript +56 -0
- data/erlang/lib/ejson/test/literals.escript +30 -0
- data/erlang/lib/ejson/test/numbers.escript +70 -0
- data/erlang/lib/ejson/test/objects.escript +51 -0
- data/erlang/lib/ejson/test/strings.escript +49 -0
- data/erlang/lib/ejson/test/timing.escript +43 -0
- data/erlang/lib/ejson/test/timing.json +382 -0
- data/erlang/lib/ejson/vendor/mochijson2.erl +621 -0
- data/erlang/lib/ejson/vendor/rfc4627.erl +625 -0
- data/erlang/lib/misultin/LICENSE.txt +41 -0
- data/erlang/lib/misultin/Makefile +26 -0
- data/erlang/lib/misultin/README.txt +120 -0
- data/erlang/lib/misultin/ebin/misultin.app +9 -0
- data/erlang/lib/misultin/examples/misultin_compress.erl +43 -0
- data/erlang/lib/misultin/examples/misultin_echo.erl +58 -0
- data/erlang/lib/misultin/examples/misultin_file.erl +43 -0
- data/erlang/lib/misultin/examples/misultin_gen_server.erl +158 -0
- data/erlang/lib/misultin/examples/misultin_get_variable.erl +55 -0
- data/erlang/lib/misultin/examples/misultin_hello_world.erl +43 -0
- data/erlang/lib/misultin/examples/misultin_rest.erl +68 -0
- data/erlang/lib/misultin/examples/misultin_ssl.erl +51 -0
- data/erlang/lib/misultin/examples/misultin_stream.erl +55 -0
- data/erlang/lib/misultin/examples/misultin_websocket_event_example.erl +103 -0
- data/erlang/lib/misultin/examples/misultin_websocket_example.erl +95 -0
- data/erlang/lib/misultin/include/misultin.hrl +95 -0
- data/erlang/lib/misultin/make.bat +55 -0
- data/erlang/lib/misultin/priv/README.txt +12 -0
- data/erlang/lib/misultin/priv/test_certificate.pem +21 -0
- data/erlang/lib/misultin/priv/test_privkey.pem +18 -0
- data/erlang/lib/misultin/rebar.config +3 -0
- data/erlang/lib/misultin/src/misultin.app.src +9 -0
- data/erlang/lib/misultin/src/misultin.erl +338 -0
- data/erlang/lib/misultin/src/misultin_http.erl +488 -0
- data/erlang/lib/misultin/src/misultin_req.erl +280 -0
- data/erlang/lib/misultin/src/misultin_socket.erl +193 -0
- data/erlang/lib/misultin/src/misultin_utility.erl +357 -0
- data/erlang/lib/misultin/src/misultin_websocket.erl +252 -0
- data/erlang/lib/misultin/src/misultin_ws.erl +78 -0
- data/erlang/rebar.config +2 -0
- data/erlang/rel/overlay/etc/capricorn/app.config +4 -0
- data/erlang/rel/reltool.config +5 -0
- data/lib/capricorn/recipes/apache-debian.rb +1 -1
- data/lib/capricorn/recipes/centos-plesk.rb +1 -1
- data/lib/capricorn/recipes/debian-plesk95.rb +1 -2
- data/lib/capricorn/recipes/macports.rb +1 -1
- data/lib/capricorn/version.rb +1 -1
- metadata +51 -4
@@ -0,0 +1,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 =======================================================
|