|
|
- %%% 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]).
-
|