You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

280 line
11 KiB

14 年之前
14 年之前
14 年之前
13 年之前
14 年之前
14 年之前
14 年之前
13 年之前
14 年之前
14 年之前
14 年之前
14 年之前
13 年之前
13 年之前
14 年之前
14 年之前
14 年之前
  1. %%% File : ibrowse_test_server.erl
  2. %%% Author : Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk>
  3. %%% Description : A server to simulate various test scenarios
  4. %%% Created : 17 Oct 2010 by Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk>
  5. -module(ibrowse_test_server).
  6. -export([
  7. start_server/2,
  8. stop_server/1
  9. ]).
  10. -record(request, {method, uri, version, headers = [], body = []}).
  11. -define(dec2hex(X), erlang:integer_to_list(X, 16)).
  12. start_server(Port, Sock_type) ->
  13. Fun = fun() ->
  14. Proc_name = server_proc_name(Port),
  15. case whereis(Proc_name) of
  16. undefined ->
  17. register(Proc_name, self()),
  18. case do_listen(Sock_type, Port, [{active, false},
  19. {reuseaddr, true},
  20. {nodelay, true},
  21. {packet, http}]) of
  22. {ok, Sock} ->
  23. do_trace("Server listening on port: ~p~n", [Port]),
  24. accept_loop(Sock, Sock_type);
  25. Err ->
  26. erlang:error(
  27. lists:flatten(
  28. io_lib:format(
  29. "Failed to start server on port ~p. ~p~n",
  30. [Port, Err]))),
  31. exit({listen_error, Err})
  32. end;
  33. _X ->
  34. ok
  35. end
  36. end,
  37. spawn_link(Fun).
  38. stop_server(Port) ->
  39. server_proc_name(Port) ! stop,
  40. ok.
  41. server_proc_name(Port) ->
  42. list_to_atom("ibrowse_test_server_"++integer_to_list(Port)).
  43. do_listen(tcp, Port, Opts) ->
  44. gen_tcp:listen(Port, Opts);
  45. do_listen(ssl, Port, Opts) ->
  46. application:start(crypto),
  47. application:start(ssl),
  48. ssl:listen(Port, Opts).
  49. do_accept(tcp, Listen_sock) ->
  50. gen_tcp:accept(Listen_sock);
  51. do_accept(ssl, Listen_sock) ->
  52. ssl:ssl_accept(Listen_sock).
  53. accept_loop(Sock, Sock_type) ->
  54. case do_accept(Sock_type, Sock) of
  55. {ok, Conn} ->
  56. Pid = spawn_link(
  57. fun() ->
  58. server_loop(Conn, Sock_type, #request{})
  59. end),
  60. set_controlling_process(Conn, Sock_type, Pid),
  61. Pid ! {setopts, [{active, true}]},
  62. accept_loop(Sock, Sock_type);
  63. Err ->
  64. Err
  65. end.
  66. set_controlling_process(Sock, tcp, Pid) ->
  67. gen_tcp:controlling_process(Sock, Pid);
  68. set_controlling_process(Sock, ssl, Pid) ->
  69. ssl:controlling_process(Sock, Pid).
  70. setopts(Sock, tcp, Opts) ->
  71. inet:setopts(Sock, Opts);
  72. setopts(Sock, ssl, Opts) ->
  73. ssl:setopts(Sock, Opts).
  74. server_loop(Sock, Sock_type, #request{headers = Headers} = Req) ->
  75. receive
  76. {http, Sock, {http_request, HttpMethod, HttpUri, HttpVersion}} ->
  77. server_loop(Sock, Sock_type, Req#request{method = HttpMethod,
  78. uri = HttpUri,
  79. version = HttpVersion});
  80. {http, Sock, {http_header, _, _, _, _} = H} ->
  81. server_loop(Sock, Sock_type, Req#request{headers = [H | Headers]});
  82. {http, Sock, http_eoh} ->
  83. case process_request(Sock, Sock_type, Req) of
  84. close_connection ->
  85. gen_tcp:shutdown(Sock, read_write);
  86. _ ->
  87. server_loop(Sock, Sock_type, #request{})
  88. end;
  89. {http, Sock, {http_error, Err}} ->
  90. io:format("Error parsing HTTP request:~n"
  91. "Req so far : ~p~n"
  92. "Err : ~p", [Req, Err]),
  93. exit({http_error, Err});
  94. {setopts, Opts} ->
  95. setopts(Sock, Sock_type, Opts),
  96. server_loop(Sock, Sock_type, Req);
  97. {tcp_closed, Sock} ->
  98. do_trace("Client closed connection~n", []),
  99. ok;
  100. stop ->
  101. ok;
  102. Other ->
  103. io:format("Recvd unknown msg: ~p~n", [Other]),
  104. exit({unknown_msg, Other})
  105. after 120000 ->
  106. do_trace("Timing out client connection~n", []),
  107. ok
  108. end.
  109. do_trace(Fmt, Args) ->
  110. do_trace(get(my_trace_flag), Fmt, Args).
  111. do_trace(true, Fmt, Args) ->
  112. io:format("~s -- " ++ Fmt, [ibrowse_lib:printable_date() | Args]);
  113. do_trace(_, _, _) ->
  114. ok.
  115. process_request(Sock, Sock_type,
  116. #request{method='GET',
  117. headers = Headers,
  118. uri = {abs_path, "/ibrowse_stream_once_chunk_pipeline_test"}} = Req) ->
  119. Req_id = case lists:keysearch("X-Ibrowse-Request-Id", 3, Headers) of
  120. false ->
  121. "";
  122. {value, {http_header, _, _, _, Req_id_1}} ->
  123. Req_id_1
  124. end,
  125. Req_id_header = ["x-ibrowse-request-id: ", Req_id, "\r\n"],
  126. do_trace("Recvd req: ~p~n", [Req]),
  127. Body = string:join([integer_to_list(X) || X <- lists:seq(1,100)], "-"),
  128. Chunked_body = chunk_request_body(Body, 50),
  129. Resp_1 = [<<"HTTP/1.1 200 OK\r\n">>,
  130. Req_id_header,
  131. <<"Transfer-Encoding: chunked\r\n\r\n">>],
  132. Resp_2 = Chunked_body,
  133. do_send(Sock, Sock_type, Resp_1),
  134. timer:sleep(100),
  135. do_send(Sock, Sock_type, Resp_2);
  136. process_request(Sock, Sock_type,
  137. #request{method='GET',
  138. headers = _Headers,
  139. uri = {abs_path, "/ibrowse_inac_timeout_test"}} = Req) ->
  140. do_trace("Recvd req: ~p. Sleeping for 30 secs...~n", [Req]),
  141. timer:sleep(3000),
  142. do_trace("...Sending response now.~n", []),
  143. Resp = <<"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n">>,
  144. do_send(Sock, Sock_type, Resp);
  145. process_request(Sock, Sock_type,
  146. #request{method='HEAD',
  147. headers = _Headers,
  148. uri = {abs_path, "/ibrowse_head_transfer_enc"}}) ->
  149. 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">>,
  150. do_send(Sock, Sock_type, Resp);
  151. process_request(Sock, Sock_type,
  152. #request{method='GET',
  153. headers = Headers,
  154. uri = {abs_path, "/ibrowse_echo_header"}}) ->
  155. Tag = "x-binary",
  156. Headers_1 = [{to_lower(X), to_lower(Y)} || {http_header, _, X, _, Y} <- Headers],
  157. X_binary_header_val = case lists:keysearch(Tag, 1, Headers_1) of
  158. false ->
  159. "not_found";
  160. {value, {_, V}} ->
  161. V
  162. end,
  163. Resp = [<<"HTTP/1.1 200 OK\r\n">>,
  164. <<"Server: ibrowse_test\r\n">>,
  165. Tag, ": ", X_binary_header_val, "\r\n",
  166. <<"Content-Length: 0\r\n\r\n">>],
  167. do_send(Sock, Sock_type, Resp);
  168. process_request(Sock, Sock_type,
  169. #request{method='HEAD',
  170. headers = _Headers,
  171. uri = {abs_path, "/ibrowse_head_test"}}) ->
  172. Resp = <<"HTTP/1.1 200 OK\r\nServer: Apache-Coyote/1.1\r\Date: Wed, 04 Apr 2012 16:53:49 GMT\r\nConnection: close\r\n\r\n">>,
  173. do_send(Sock, Sock_type, Resp);
  174. process_request(Sock, Sock_type,
  175. #request{method='POST',
  176. headers = _Headers,
  177. uri = {abs_path, "/ibrowse_303_no_body_test"}}) ->
  178. Resp = <<"HTTP/1.1 303 See Other\r\nLocation: http://example.org\r\n">>,
  179. do_send(Sock, Sock_type, Resp);
  180. process_request(Sock, Sock_type,
  181. #request{method='POST',
  182. headers = _Headers,
  183. uri = {abs_path, "/ibrowse_303_with_body_test"}}) ->
  184. Resp = <<"HTTP/1.1 303 See Other\r\nLocation: http://example.org\r\nContent-Length: 5\r\n\r\nabcde">>,
  185. do_send(Sock, Sock_type, Resp);
  186. process_request(Sock, Sock_type,
  187. #request{method='GET',
  188. headers = _Headers,
  189. uri = {abs_path, "/ibrowse_handle_one_request_only_with_delay"}}) ->
  190. timer:sleep(2000),
  191. 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">>,
  192. do_send(Sock, Sock_type, Resp),
  193. close_connection;
  194. process_request(Sock, Sock_type,
  195. #request{method='GET',
  196. headers = _Headers,
  197. uri = {abs_path, "/ibrowse_handle_one_request_only"}}) ->
  198. 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">>,
  199. do_send(Sock, Sock_type, Resp),
  200. close_connection;
  201. process_request(Sock, Sock_type, Req) ->
  202. do_trace("Recvd req: ~p~n", [Req]),
  203. Resp = <<"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n">>,
  204. do_send(Sock, Sock_type, Resp),
  205. timer:sleep(random:uniform(100)).
  206. do_send(Sock, tcp, Resp) ->
  207. gen_tcp:send(Sock, Resp);
  208. do_send(Sock, ssl, Resp) ->
  209. ssl:send(Sock, Resp).
  210. %%------------------------------------------------------------------------------
  211. %% Utility functions
  212. %%------------------------------------------------------------------------------
  213. chunk_request_body(Body, _ChunkSize) when is_tuple(Body) orelse
  214. is_function(Body) ->
  215. Body;
  216. chunk_request_body(Body, ChunkSize) ->
  217. chunk_request_body(Body, ChunkSize, []).
  218. chunk_request_body(Body, _ChunkSize, Acc) when Body == <<>>; Body == [] ->
  219. LastChunk = "0\r\n",
  220. lists:reverse(["\r\n", LastChunk | Acc]);
  221. chunk_request_body(Body, ChunkSize, Acc) when is_binary(Body),
  222. size(Body) >= ChunkSize ->
  223. <<ChunkBody:ChunkSize/binary, Rest/binary>> = Body,
  224. Chunk = [?dec2hex(ChunkSize),"\r\n",
  225. ChunkBody, "\r\n"],
  226. chunk_request_body(Rest, ChunkSize, [Chunk | Acc]);
  227. chunk_request_body(Body, _ChunkSize, Acc) when is_binary(Body) ->
  228. BodySize = size(Body),
  229. Chunk = [?dec2hex(BodySize),"\r\n",
  230. Body, "\r\n"],
  231. LastChunk = "0\r\n",
  232. lists:reverse(["\r\n", LastChunk, Chunk | Acc]);
  233. chunk_request_body(Body, ChunkSize, Acc) when length(Body) >= ChunkSize ->
  234. {ChunkBody, Rest} = split_list_at(Body, ChunkSize),
  235. Chunk = [?dec2hex(ChunkSize),"\r\n",
  236. ChunkBody, "\r\n"],
  237. chunk_request_body(Rest, ChunkSize, [Chunk | Acc]);
  238. chunk_request_body(Body, _ChunkSize, Acc) when is_list(Body) ->
  239. BodySize = length(Body),
  240. Chunk = [?dec2hex(BodySize),"\r\n",
  241. Body, "\r\n"],
  242. LastChunk = "0\r\n",
  243. lists:reverse(["\r\n", LastChunk, Chunk | Acc]).
  244. split_list_at(List, N) ->
  245. split_list_at(List, N, []).
  246. split_list_at([], _, Acc) ->
  247. {lists:reverse(Acc), []};
  248. split_list_at(List2, 0, List1) ->
  249. {lists:reverse(List1), List2};
  250. split_list_at([H | List2], N, List1) ->
  251. split_list_at(List2, N-1, [H | List1]).
  252. to_lower(X) when is_atom(X) ->
  253. list_to_atom(to_lower(atom_to_list(X)));
  254. to_lower(X) when is_list(X) ->
  255. string:to_lower(X).