|
@ -1,49 +1,60 @@ |
|
|
%%% File : ibrowse_test_server.erl |
|
|
%%% File : ibrowse_test_server.erl |
|
|
%%% Author : Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk> |
|
|
%%% Author : Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk> |
|
|
|
|
|
%%% Benjamin Lee <http://github.com/benjaminplee> |
|
|
|
|
|
%%% Dan Schwabe <http://github.com/dfschwabe> |
|
|
|
|
|
%%% Brian Richards <http://github.com/richbria> |
|
|
%%% Description : A server to simulate various test scenarios |
|
|
%%% Description : A server to simulate various test scenarios |
|
|
%%% Created : 17 Oct 2010 by Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk> |
|
|
%%% Created : 17 Oct 2010 by Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk> |
|
|
|
|
|
|
|
|
-module(ibrowse_test_server). |
|
|
-module(ibrowse_test_server). |
|
|
-export([ |
|
|
-export([ |
|
|
start_server/2, |
|
|
start_server/2, |
|
|
stop_server/1 |
|
|
|
|
|
|
|
|
stop_server/1, |
|
|
|
|
|
get_conn_pipeline_depth/0 |
|
|
]). |
|
|
]). |
|
|
|
|
|
|
|
|
-record(request, {method, uri, version, headers = [], body = []}). |
|
|
-record(request, {method, uri, version, headers = [], body = []}). |
|
|
|
|
|
|
|
|
-define(dec2hex(X), erlang:integer_to_list(X, 16)). |
|
|
-define(dec2hex(X), erlang:integer_to_list(X, 16)). |
|
|
|
|
|
-define(ACCEPT_TIMEOUT_MS, 1000). |
|
|
|
|
|
-define(CONN_PIPELINE_DEPTH, conn_pipeline_depth). |
|
|
|
|
|
|
|
|
start_server(Port, Sock_type) -> |
|
|
start_server(Port, Sock_type) -> |
|
|
Fun = fun() -> |
|
|
Fun = fun() -> |
|
|
Proc_name = server_proc_name(Port), |
|
|
|
|
|
case whereis(Proc_name) of |
|
|
|
|
|
undefined -> |
|
|
|
|
|
register(Proc_name, self()), |
|
|
|
|
|
case do_listen(Sock_type, Port, [{active, false}, |
|
|
|
|
|
{reuseaddr, true}, |
|
|
|
|
|
{nodelay, true}, |
|
|
|
|
|
{packet, http}]) of |
|
|
|
|
|
{ok, Sock} -> |
|
|
|
|
|
do_trace("Server listening on port: ~p~n", [Port]), |
|
|
|
|
|
accept_loop(Sock, Sock_type); |
|
|
|
|
|
Err -> |
|
|
|
|
|
erlang:error( |
|
|
|
|
|
lists:flatten( |
|
|
|
|
|
io_lib:format( |
|
|
|
|
|
"Failed to start server on port ~p. ~p~n", |
|
|
|
|
|
[Port, Err]))), |
|
|
|
|
|
exit({listen_error, Err}) |
|
|
|
|
|
end; |
|
|
|
|
|
_X -> |
|
|
|
|
|
ok |
|
|
|
|
|
end |
|
|
|
|
|
end, |
|
|
|
|
|
|
|
|
Proc_name = server_proc_name(Port), |
|
|
|
|
|
case whereis(Proc_name) of |
|
|
|
|
|
undefined -> |
|
|
|
|
|
register(Proc_name, self()), |
|
|
|
|
|
ets:new(?CONN_PIPELINE_DEPTH, [named_table, public, set]), |
|
|
|
|
|
case do_listen(Sock_type, Port, [{active, false}, |
|
|
|
|
|
{reuseaddr, true}, |
|
|
|
|
|
{nodelay, true}, |
|
|
|
|
|
{packet, http}]) of |
|
|
|
|
|
{ok, Sock} -> |
|
|
|
|
|
do_trace("Server listening on port: ~p~n", [Port]), |
|
|
|
|
|
accept_loop(Sock, Sock_type); |
|
|
|
|
|
Err -> |
|
|
|
|
|
erlang:error( |
|
|
|
|
|
lists:flatten( |
|
|
|
|
|
io_lib:format( |
|
|
|
|
|
"Failed to start server on port ~p. ~p~n", |
|
|
|
|
|
[Port, Err]))), |
|
|
|
|
|
exit({listen_error, Err}) |
|
|
|
|
|
end; |
|
|
|
|
|
_X -> |
|
|
|
|
|
ok |
|
|
|
|
|
end |
|
|
|
|
|
end, |
|
|
spawn_link(Fun). |
|
|
spawn_link(Fun). |
|
|
|
|
|
|
|
|
stop_server(Port) -> |
|
|
stop_server(Port) -> |
|
|
server_proc_name(Port) ! stop, |
|
|
server_proc_name(Port) ! stop, |
|
|
|
|
|
timer:sleep(2000), % wait for server to receive msg and unregister |
|
|
ok. |
|
|
ok. |
|
|
|
|
|
|
|
|
|
|
|
get_conn_pipeline_depth() -> |
|
|
|
|
|
ets:tab2list(?CONN_PIPELINE_DEPTH). |
|
|
|
|
|
|
|
|
server_proc_name(Port) -> |
|
|
server_proc_name(Port) -> |
|
|
list_to_atom("ibrowse_test_server_"++integer_to_list(Port)). |
|
|
list_to_atom("ibrowse_test_server_"++integer_to_list(Port)). |
|
|
|
|
|
|
|
@ -55,24 +66,36 @@ do_listen(ssl, Port, Opts) -> |
|
|
ssl:listen(Port, Opts). |
|
|
ssl:listen(Port, Opts). |
|
|
|
|
|
|
|
|
do_accept(tcp, Listen_sock) -> |
|
|
do_accept(tcp, Listen_sock) -> |
|
|
gen_tcp:accept(Listen_sock); |
|
|
|
|
|
|
|
|
gen_tcp:accept(Listen_sock, ?ACCEPT_TIMEOUT_MS); |
|
|
do_accept(ssl, Listen_sock) -> |
|
|
do_accept(ssl, Listen_sock) -> |
|
|
ssl:ssl_accept(Listen_sock). |
|
|
|
|
|
|
|
|
ssl:ssl_accept(Listen_sock, ?ACCEPT_TIMEOUT_MS). |
|
|
|
|
|
|
|
|
accept_loop(Sock, Sock_type) -> |
|
|
accept_loop(Sock, Sock_type) -> |
|
|
case do_accept(Sock_type, Sock) of |
|
|
case do_accept(Sock_type, Sock) of |
|
|
{ok, Conn} -> |
|
|
{ok, Conn} -> |
|
|
Pid = spawn_link( |
|
|
|
|
|
fun() -> |
|
|
|
|
|
server_loop(Conn, Sock_type, #request{}) |
|
|
|
|
|
end), |
|
|
|
|
|
|
|
|
Pid = spawn_link(fun() -> connection(Conn, Sock_type) end), |
|
|
set_controlling_process(Conn, Sock_type, Pid), |
|
|
set_controlling_process(Conn, Sock_type, Pid), |
|
|
Pid ! {setopts, [{active, true}]}, |
|
|
Pid ! {setopts, [{active, true}]}, |
|
|
accept_loop(Sock, Sock_type); |
|
|
accept_loop(Sock, Sock_type); |
|
|
|
|
|
{error, timeout} -> |
|
|
|
|
|
receive |
|
|
|
|
|
stop -> |
|
|
|
|
|
ok |
|
|
|
|
|
after 10 -> |
|
|
|
|
|
accept_loop(Sock, Sock_type) |
|
|
|
|
|
end; |
|
|
Err -> |
|
|
Err -> |
|
|
Err |
|
|
Err |
|
|
end. |
|
|
end. |
|
|
|
|
|
|
|
|
|
|
|
connection(Conn, Sock_type) -> |
|
|
|
|
|
catch ets:insert(?CONN_PIPELINE_DEPTH, {self(), 0}), |
|
|
|
|
|
try |
|
|
|
|
|
server_loop(Conn, Sock_type, #request{}) |
|
|
|
|
|
after |
|
|
|
|
|
catch ets:delete(?CONN_PIPELINE_DEPTH, self()) |
|
|
|
|
|
end. |
|
|
|
|
|
|
|
|
set_controlling_process(Sock, tcp, Pid) -> |
|
|
set_controlling_process(Sock, tcp, Pid) -> |
|
|
gen_tcp:controlling_process(Sock, Pid); |
|
|
gen_tcp:controlling_process(Sock, Pid); |
|
|
set_controlling_process(Sock, ssl, Pid) -> |
|
|
set_controlling_process(Sock, ssl, Pid) -> |
|
@ -86,6 +109,7 @@ setopts(Sock, ssl, Opts) -> |
|
|
server_loop(Sock, Sock_type, #request{headers = Headers} = Req) -> |
|
|
server_loop(Sock, Sock_type, #request{headers = Headers} = Req) -> |
|
|
receive |
|
|
receive |
|
|
{http, Sock, {http_request, HttpMethod, HttpUri, HttpVersion}} -> |
|
|
{http, Sock, {http_request, HttpMethod, HttpUri, HttpVersion}} -> |
|
|
|
|
|
catch ets:update_counter(?CONN_PIPELINE_DEPTH, self(), 1), |
|
|
server_loop(Sock, Sock_type, Req#request{method = HttpMethod, |
|
|
server_loop(Sock, Sock_type, Req#request{method = HttpMethod, |
|
|
uri = HttpUri, |
|
|
uri = HttpUri, |
|
|
version = HttpVersion}); |
|
|
version = HttpVersion}); |
|
@ -95,9 +119,12 @@ server_loop(Sock, Sock_type, #request{headers = Headers} = Req) -> |
|
|
case process_request(Sock, Sock_type, Req) of |
|
|
case process_request(Sock, Sock_type, Req) of |
|
|
close_connection -> |
|
|
close_connection -> |
|
|
gen_tcp:shutdown(Sock, read_write); |
|
|
gen_tcp:shutdown(Sock, read_write); |
|
|
|
|
|
not_done -> |
|
|
|
|
|
ok; |
|
|
_ -> |
|
|
_ -> |
|
|
server_loop(Sock, Sock_type, #request{}) |
|
|
|
|
|
end; |
|
|
|
|
|
|
|
|
catch ets:update_counter(?CONN_PIPELINE_DEPTH, self(), -1) |
|
|
|
|
|
end, |
|
|
|
|
|
server_loop(Sock, Sock_type, #request{}); |
|
|
{http, Sock, {http_error, Err}} -> |
|
|
{http, Sock, {http_error, Err}} -> |
|
|
io:format("Error parsing HTTP request:~n" |
|
|
io:format("Error parsing HTTP request:~n" |
|
|
"Req so far : ~p~n" |
|
|
"Req so far : ~p~n" |
|
@ -109,8 +136,6 @@ server_loop(Sock, Sock_type, #request{headers = Headers} = Req) -> |
|
|
{tcp_closed, Sock} -> |
|
|
{tcp_closed, Sock} -> |
|
|
do_trace("Client closed connection~n", []), |
|
|
do_trace("Client closed connection~n", []), |
|
|
ok; |
|
|
ok; |
|
|
stop -> |
|
|
|
|
|
ok; |
|
|
|
|
|
Other -> |
|
|
Other -> |
|
|
io:format("Recvd unknown msg: ~p~n", [Other]), |
|
|
io:format("Recvd unknown msg: ~p~n", [Other]), |
|
|
exit({unknown_msg, Other}) |
|
|
exit({unknown_msg, Other}) |
|
@ -163,7 +188,6 @@ process_request(Sock, Sock_type, |
|
|
uri = {abs_path, "/ibrowse_head_transfer_enc"}}) -> |
|
|
uri = {abs_path, "/ibrowse_head_transfer_enc"}}) -> |
|
|
Resp = <<"HTTP/1.1 400 Bad Request\r\nServer: Apache-Coyote/1.1\r\nContent-Length:5\r\nDate: Wed, 04 Apr 2012 16:53:49 GMT\r\n\r\nabcde">>, |
|
|
Resp = <<"HTTP/1.1 400 Bad Request\r\nServer: Apache-Coyote/1.1\r\nContent-Length:5\r\nDate: Wed, 04 Apr 2012 16:53:49 GMT\r\n\r\nabcde">>, |
|
|
do_send(Sock, Sock_type, Resp); |
|
|
do_send(Sock, Sock_type, Resp); |
|
|
|
|
|
|
|
|
process_request(Sock, Sock_type, |
|
|
process_request(Sock, Sock_type, |
|
|
#request{method='GET', |
|
|
#request{method='GET', |
|
|
headers = Headers, |
|
|
headers = Headers, |
|
@ -215,6 +239,8 @@ process_request(Sock, Sock_type, |
|
|
Resp = <<"HTTP/1.1 200 OK\r\nServer: Apache-Coyote/1.1\r\nDate: Wed, 04 Apr 2012 16:53:49 GMT\r\nConnection: close\r\n\r\n">>, |
|
|
Resp = <<"HTTP/1.1 200 OK\r\nServer: Apache-Coyote/1.1\r\nDate: Wed, 04 Apr 2012 16:53:49 GMT\r\nConnection: close\r\n\r\n">>, |
|
|
do_send(Sock, Sock_type, Resp), |
|
|
do_send(Sock, Sock_type, Resp), |
|
|
close_connection; |
|
|
close_connection; |
|
|
|
|
|
process_request(_Sock, _Sock_type, #request{uri = {abs_path, "/never_respond"} } ) -> |
|
|
|
|
|
not_done; |
|
|
process_request(Sock, Sock_type, Req) -> |
|
|
process_request(Sock, Sock_type, Req) -> |
|
|
do_trace("Recvd req: ~p~n", [Req]), |
|
|
do_trace("Recvd req: ~p~n", [Req]), |
|
|
Resp = <<"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n">>, |
|
|
Resp = <<"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n">>, |
|
@ -226,7 +252,6 @@ do_send(Sock, tcp, Resp) -> |
|
|
do_send(Sock, ssl, Resp) -> |
|
|
do_send(Sock, ssl, Resp) -> |
|
|
ssl:send(Sock, Resp). |
|
|
ssl:send(Sock, Resp). |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
%%------------------------------------------------------------------------------ |
|
|
%%------------------------------------------------------------------------------ |
|
|
%% Utility functions |
|
|
%% Utility functions |
|
|
%%------------------------------------------------------------------------------ |
|
|
%%------------------------------------------------------------------------------ |
|
|