summaryrefslogtreecommitdiff
path: root/deps/ibrowse/test/ibrowse_test_server.erl
diff options
context:
space:
mode:
Diffstat (limited to 'deps/ibrowse/test/ibrowse_test_server.erl')
-rw-r--r--deps/ibrowse/test/ibrowse_test_server.erl195
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]).
+