ejabberd-contrib/ejabberd_auth_http/deps/fusco/test/webserver.erl

148 lines
4.1 KiB
Erlang
Raw Normal View History

%%%-----------------------------------------------------------------------------
%%% @copyright (C) 1999-2013, Erlang Solutions Ltd
%%% @author Oscar Hellström <oscar@hellstrom.st>
%%% @author Magnus Henoch <magnus@erlang-consulting.com>
%%% @author Diana Parra Corbacho <diana.corbacho@erlang-solutions.com>
%%% @doc Simple web server for testing purposes
%%% @end
%%%-----------------------------------------------------------------------------
-module(webserver).
-copyright("2013, Erlang Solutions Ltd.").
-export([start/2, start/3, stop/2, stop/3]).
-export([acceptor/3]).
start(Module, Responders) ->
start(Module, Responders, inet).
start(Module, Responders, Family) ->
case get_addr("localhost", Family) of
{ok, Addr} ->
LS = listen(Module, Addr, Family),
Pid = spawn(?MODULE, acceptor, [Module, LS, Responders]),
{ok, Pid, LS, port(Module, LS)};
Error ->
Error
end.
stop(Listener, LS) ->
stop(gen_tcp, Listener, LS).
stop(Module, Listener, LS) ->
(catch exit(kill, Listener)),
Module:close(LS).
acceptor(Module, ListenSocket, Responders) ->
case accept(Module, ListenSocket) of
error ->
ok;
Socket ->
spawn_link(fun() ->
acceptor(Module, ListenSocket, Responders)
end),
server_loop(Module, Socket, nil, [], Responders)
end.
server_loop(Module, Socket, _, _, []) ->
Module:close(Socket);
server_loop(Module, Socket, Request, Headers, [H | T] = Responders) ->
receive
stop ->
Module:close(Socket)
after 0 ->
case Module:recv(Socket, 0, 500) of
{ok, {http_request, _, _, _} = NewRequest} ->
server_loop(Module, Socket, NewRequest, Headers, Responders);
{ok, {http_header, _, Field, _, Value}} when is_atom(Field) ->
NewHeaders = [{atom_to_list(Field), Value} | Headers],
server_loop(Module, Socket, Request, NewHeaders, Responders);
{ok, {http_header, _, Field, _, Value}} when is_list(Field) ->
NewHeaders = [{Field, Value} | Headers],
server_loop(Module, Socket, Request, NewHeaders, Responders);
{ok, http_eoh} ->
RequestBody = case proplists:get_value("Content-Length", Headers) of
undefined ->
<<>>;
"0" ->
<<>>;
SLength ->
Length = list_to_integer(SLength),
setopts(Module, Socket, [{packet, raw}]),
{ok, Body} = Module:recv(Socket, Length),
setopts(Module, Socket, [{packet, http}]),
Body
end,
H(Module, Socket, Request, Headers, RequestBody),
case proplists:get_value("Connection", Headers) of
"close" ->
Module:close(Socket);
_ ->
server_loop(Module, Socket, none, [], T)
end;
{error, timeout} ->
server_loop(Module, Socket, Request, Headers, Responders);
{error, closed} ->
Module:close(Socket)
end
end.
listen(ssl, Addr, Family) ->
Root = code:lib_dir(fusco, test),
Opts = [
Family,
{packet, http},
binary,
{active, false},
{ip, Addr},
{verify,0},
{keyfile, filename:join(Root, "key.pem")},
{certfile, filename:join(Root,"crt.pem")}
],
{ok, LS} = ssl:listen(0, Opts),
LS;
listen(Module, Addr, Family) ->
{ok, LS} = Module:listen(0, [
Family,
{packet, http},
binary,
{active, false},
{ip, Addr}
]),
LS.
get_addr(Host, Family) ->
case inet:getaddr(Host, Family) of
{ok, Addr} ->
{ok, Addr};
_ ->
{error, family_not_supported}
end.
accept(ssl, ListenSocket) ->
case ssl:transport_accept(ListenSocket, 1000000) of
{ok, Socket} ->
ok = ssl:ssl_accept(Socket),
Socket;
{error, _} ->
error
end;
accept(Module, ListenSocket) ->
case Module:accept(ListenSocket, 100000) of
{ok, Socket} ->
Socket;
{error, _} ->
error
end.
setopts(ssl, Socket, Options) ->
ssl:setopts(Socket, Options);
setopts(_, Socket, Options) ->
inet:setopts(Socket, Options).
port(ssl, Socket) ->
{ok, {_, Port}} = ssl:sockname(Socket),
Port;
port(_, Socket) ->
{ok, Port} = inet:port(Socket),
Port.