blob: 37d760fd7fe1dd686bb2824f912c50c2ed13c6ae [file] [log] [blame]
%%% File : ibrowse_test_server.erl
%%% Author : Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk>
%%% Description : A server to simulate various test scenarios
%%% Created : 17 Oct 2010 by Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk>
-module(ibrowse_test_server).
-export([
start_server/2,
stop_server/1
]).
-record(request, {method, uri, version, headers = [], body = []}).
start_server(Port, Sock_type) ->
Fun = fun() ->
register(server_proc_name(Port), self()),
case do_listen(Sock_type, Port, [{active, false},
{packet, http}]) of
{ok, Sock} ->
do_trace("Server listening on port: ~p~n", [Port]),
accept_loop(Sock, Sock_type);
Err ->
do_trace("Failed to start server on port ~p. ~p~n",
[Port, Err]),
Err
end
end,
spawn(Fun).
stop_server(Port) ->
exit(whereis(server_proc_name(Port)), kill).
server_proc_name(Port) ->
list_to_atom("ibrowse_test_server_"++integer_to_list(Port)).
do_listen(tcp, Port, Opts) ->
gen_tcp:listen(Port, Opts);
do_listen(ssl, Port, Opts) ->
application:start(crypto),
application:start(ssl),
ssl:listen(Port, Opts).
do_accept(tcp, Listen_sock) ->
gen_tcp:accept(Listen_sock);
do_accept(ssl, Listen_sock) ->
ssl:ssl_accept(Listen_sock).
accept_loop(Sock, Sock_type) ->
case do_accept(Sock_type, Sock) of
{ok, Conn} ->
Pid = spawn_link(
fun() ->
server_loop(Conn, Sock_type, #request{})
end),
set_controlling_process(Conn, Sock_type, Pid),
Pid ! {setopts, [{active, true}]},
accept_loop(Sock, Sock_type);
Err ->
Err
end.
set_controlling_process(Sock, tcp, Pid) ->
gen_tcp:controlling_process(Sock, Pid);
set_controlling_process(Sock, ssl, Pid) ->
ssl:controlling_process(Sock, Pid).
setopts(Sock, tcp, Opts) ->
inet:setopts(Sock, Opts);
setopts(Sock, ssl, Opts) ->
ssl:setopts(Sock, Opts).
server_loop(Sock, Sock_type, #request{headers = Headers} = Req) ->
receive
{http, Sock, {http_request, HttpMethod, HttpUri, HttpVersion}} ->
server_loop(Sock, Sock_type, Req#request{method = HttpMethod,
uri = HttpUri,
version = HttpVersion});
{http, Sock, {http_header, _, _, _, _} = H} ->
server_loop(Sock, Sock_type, Req#request{headers = [H | Headers]});
{http, Sock, http_eoh} ->
process_request(Sock, Sock_type, Req),
server_loop(Sock, Sock_type, #request{});
{http, Sock, {http_error, Err}} ->
do_trace("Error parsing HTTP request:~n"
"Req so far : ~p~n"
"Err : ", [Req, Err]),
exit({http_error, Err});
{setopts, Opts} ->
setopts(Sock, Sock_type, Opts),
server_loop(Sock, Sock_type, Req);
Other ->
do_trace("Recvd unknown msg: ~p~n", [Other]),
exit({unknown_msg, Other})
after 5000 ->
do_trace("Timing out client connection~n", []),
ok
end.
do_trace(Fmt, Args) ->
io:format("~s -- " ++ Fmt, [ibrowse_lib:printable_date() | Args]).
process_request(Sock, Sock_type, Req) ->
do_trace("Recvd req: ~p~n", [Req]),
Resp = <<"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n">>,
do_send(Sock, Sock_type, Resp).
do_send(Sock, tcp, Resp) ->
ok = gen_tcp:send(Sock, Resp);
do_send(Sock, ssl, Resp) ->
ok = ssl:send(Sock, Resp).