diff options
Diffstat (limited to 'deps/ibrowse/test/ibrowse_test_server.erl')
-rw-r--r-- | deps/ibrowse/test/ibrowse_test_server.erl | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/deps/ibrowse/test/ibrowse_test_server.erl b/deps/ibrowse/test/ibrowse_test_server.erl new file mode 100644 index 00000000..45c69587 --- /dev/null +++ b/deps/ibrowse/test/ibrowse_test_server.erl @@ -0,0 +1,195 @@ +%%% 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 = []}). + +-define(dec2hex(X), erlang:integer_to_list(X, 16)). + +start_server(Port, Sock_type) -> + Fun = fun() -> + register(server_proc_name(Port), 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 + end, + spawn_link(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); + {tcp_closed, Sock} -> + do_trace("Client closed connection~n", []), + ok; + 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) -> + do_trace(get(my_trace_flag), Fmt, Args). + +do_trace(true, Fmt, Args) -> + io:format("~s -- " ++ Fmt, [ibrowse_lib:printable_date() | Args]); +do_trace(_, _, _) -> + ok. + +process_request(Sock, Sock_type, + #request{method='GET', + headers = Headers, + uri = {abs_path, "/ibrowse_stream_once_chunk_pipeline_test"}} = Req) -> + Req_id = case lists:keysearch("X-Ibrowse-Request-Id", 3, Headers) of + false -> + ""; + {value, {http_header, _, _, _, Req_id_1}} -> + Req_id_1 + end, + Req_id_header = ["x-ibrowse-request-id: ", Req_id, "\r\n"], + do_trace("Recvd req: ~p~n", [Req]), + Body = string:join([integer_to_list(X) || X <- lists:seq(1,100)], "-"), + Chunked_body = chunk_request_body(Body, 50), + Resp_1 = [<<"HTTP/1.1 200 OK\r\n">>, + Req_id_header, + <<"Transfer-Encoding: chunked\r\n\r\n">>], + Resp_2 = Chunked_body, + do_send(Sock, Sock_type, Resp_1), + timer:sleep(100), + do_send(Sock, Sock_type, Resp_2); +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). + + +%%------------------------------------------------------------------------------ +%% Utility functions +%%------------------------------------------------------------------------------ + +chunk_request_body(Body, _ChunkSize) when is_tuple(Body) orelse + is_function(Body) -> + Body; +chunk_request_body(Body, ChunkSize) -> + chunk_request_body(Body, ChunkSize, []). + +chunk_request_body(Body, _ChunkSize, Acc) when Body == <<>>; Body == [] -> + LastChunk = "0\r\n", + lists:reverse(["\r\n", LastChunk | Acc]); +chunk_request_body(Body, ChunkSize, Acc) when is_binary(Body), + size(Body) >= ChunkSize -> + <<ChunkBody:ChunkSize/binary, Rest/binary>> = Body, + Chunk = [?dec2hex(ChunkSize),"\r\n", + ChunkBody, "\r\n"], + chunk_request_body(Rest, ChunkSize, [Chunk | Acc]); +chunk_request_body(Body, _ChunkSize, Acc) when is_binary(Body) -> + BodySize = size(Body), + Chunk = [?dec2hex(BodySize),"\r\n", + Body, "\r\n"], + LastChunk = "0\r\n", + lists:reverse(["\r\n", LastChunk, Chunk | Acc]); +chunk_request_body(Body, ChunkSize, Acc) when length(Body) >= ChunkSize -> + {ChunkBody, Rest} = split_list_at(Body, ChunkSize), + Chunk = [?dec2hex(ChunkSize),"\r\n", + ChunkBody, "\r\n"], + chunk_request_body(Rest, ChunkSize, [Chunk | Acc]); +chunk_request_body(Body, _ChunkSize, Acc) when is_list(Body) -> + BodySize = length(Body), + Chunk = [?dec2hex(BodySize),"\r\n", + Body, "\r\n"], + LastChunk = "0\r\n", + lists:reverse(["\r\n", LastChunk, Chunk | Acc]). + +split_list_at(List, N) -> + split_list_at(List, N, []). + +split_list_at([], _, Acc) -> + {lists:reverse(Acc), []}; +split_list_at(List2, 0, List1) -> + {lists:reverse(List1), List2}; +split_list_at([H | List2], N, List1) -> + split_list_at(List2, N-1, [H | List1]). + |