Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.

374 righe
14 KiB

14 anni fa
9 anni fa
14 anni fa
14 anni fa
14 anni fa
9 anni fa
14 anni fa
9 anni fa
14 anni fa
9 anni fa
14 anni fa
14 anni fa
14 anni fa
14 anni fa
14 anni fa
14 anni fa
14 anni fa
14 anni fa
14 anni fa
14 anni fa
14 anni fa
14 anni fa
14 anni fa
14 anni fa
14 anni fa
14 anni fa
14 anni fa
13 anni fa
13 anni fa
9 anni fa
14 anni fa
14 anni fa
14 anni fa
  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/0,
  8. start_server/2,
  9. stop_server/1,
  10. get_conn_pipeline_depth/0
  11. ]).
  12. -ifdef(new_rand).
  13. -define(RAND, rand).
  14. -else.
  15. -define(RAND, random).
  16. -endif.
  17. -record(request, {method, uri, version, headers = [], body = [], state}).
  18. -define(dec2hex(X), erlang:integer_to_list(X, 16)).
  19. -define(ACCEPT_TIMEOUT_MS, 10000).
  20. -define(CONN_PIPELINE_DEPTH, conn_pipeline_depth).
  21. start_server() ->
  22. start_server(8181, tcp).
  23. start_server(Port, Sock_type) ->
  24. Fun = fun() ->
  25. Proc_name = server_proc_name(Port),
  26. case whereis(Proc_name) of
  27. undefined ->
  28. register(Proc_name, self()),
  29. ets:new(?CONN_PIPELINE_DEPTH, [named_table, public, set]),
  30. case do_listen(Sock_type, Port, [{active, false},
  31. {reuseaddr, true},
  32. {nodelay, true},
  33. {packet, http}]) of
  34. {ok, Sock} ->
  35. do_trace("Server listening on port: ~p~n", [Port]),
  36. accept_loop(Sock, Sock_type);
  37. Err ->
  38. erlang:error(
  39. lists:flatten(
  40. io_lib:format(
  41. "Failed to start server on port ~p. ~p~n",
  42. [Port, Err]))),
  43. exit({listen_error, Err})
  44. end;
  45. _X ->
  46. ok
  47. end
  48. end,
  49. spawn_link(Fun),
  50. ibrowse_socks_server:start(8282, 0), %% No auth
  51. ibrowse_socks_server:start(8383, 2). %% Username/Password auth
  52. stop_server(Port) ->
  53. catch server_proc_name(Port) ! stop,
  54. ibrowse_socks_server:stop(8282),
  55. ibrowse_socks_server:stop(8383),
  56. timer:sleep(2000), % wait for server to receive msg and unregister
  57. ok.
  58. get_conn_pipeline_depth() ->
  59. ets:tab2list(?CONN_PIPELINE_DEPTH).
  60. server_proc_name(Port) ->
  61. list_to_atom("ibrowse_test_server_"++integer_to_list(Port)).
  62. do_listen(tcp, Port, Opts) ->
  63. gen_tcp:listen(Port, Opts);
  64. do_listen(ssl, Port, Opts) ->
  65. application:start(crypto),
  66. application:start(ssl),
  67. ssl:listen(Port, Opts).
  68. -ifdef(OTP_RELEASE).
  69. do_accept(tcp, Listen_sock) ->
  70. gen_tcp:accept(Listen_sock, ?ACCEPT_TIMEOUT_MS);
  71. do_accept(ssl, Listen_sock) ->
  72. ssl:handshake(Listen_sock, ?ACCEPT_TIMEOUT_MS).
  73. -else.
  74. do_accept(tcp, Listen_sock) ->
  75. gen_tcp:accept(Listen_sock, ?ACCEPT_TIMEOUT_MS);
  76. do_accept(ssl, Listen_sock) ->
  77. ssl:ssl_accept(Listen_sock, ?ACCEPT_TIMEOUT_MS).
  78. -endif.
  79. accept_loop(Sock, Sock_type) ->
  80. case do_accept(Sock_type, Sock) of
  81. {ok, Conn} ->
  82. Pid = spawn_link(fun() -> connection(Conn, Sock_type) end),
  83. set_controlling_process(Conn, Sock_type, Pid),
  84. accept_loop(Sock, Sock_type);
  85. {error, timeout} ->
  86. receive
  87. stop ->
  88. ok
  89. after 10 ->
  90. accept_loop(Sock, Sock_type)
  91. end;
  92. Err ->
  93. Err
  94. end.
  95. connection(Conn, Sock_type) ->
  96. catch ets:insert(?CONN_PIPELINE_DEPTH, {self(), 0}),
  97. try
  98. inet:setopts(Conn, [{packet, http}, {active, true}]),
  99. server_loop(Conn, Sock_type, #request{})
  100. after
  101. catch ets:delete(?CONN_PIPELINE_DEPTH, self())
  102. end.
  103. set_controlling_process(Sock, tcp, Pid) ->
  104. gen_tcp:controlling_process(Sock, Pid);
  105. set_controlling_process(Sock, ssl, Pid) ->
  106. ssl:controlling_process(Sock, Pid).
  107. setopts(Sock, tcp, Opts) ->
  108. inet:setopts(Sock, Opts);
  109. setopts(Sock, ssl, Opts) ->
  110. ssl:setopts(Sock, Opts).
  111. server_loop(Sock, Sock_type, #request{headers = Headers} = Req) ->
  112. receive
  113. {http, Sock, {http_request, HttpMethod, HttpUri, HttpVersion}} ->
  114. catch ets:update_counter(?CONN_PIPELINE_DEPTH, self(), 1),
  115. server_loop(Sock, Sock_type, Req#request{method = HttpMethod,
  116. uri = HttpUri,
  117. version = HttpVersion});
  118. {http, Sock, {http_header, _, _, _, _} = H} ->
  119. server_loop(Sock, Sock_type, Req#request{headers = [H | Headers]});
  120. {http, Sock, http_eoh} ->
  121. case process_request(Sock, Sock_type, Req) of
  122. close_connection ->
  123. gen_tcp:shutdown(Sock, read_write);
  124. not_done ->
  125. ok;
  126. collect_body ->
  127. server_loop(Sock, Sock_type, Req#request{state = collect_body});
  128. _ ->
  129. catch ets:update_counter(?CONN_PIPELINE_DEPTH, self(), -1)
  130. end,
  131. server_loop(Sock, Sock_type, #request{});
  132. {http, Sock, {http_error, Packet}} when Req#request.state == collect_body ->
  133. Req_1 = Req#request{body = list_to_binary([Packet, Req#request.body])},
  134. case process_request(Sock, Sock_type, Req_1) of
  135. close_connection ->
  136. gen_tcp:shutdown(Sock, read_write);
  137. ok ->
  138. server_loop(Sock, Sock_type, #request{});
  139. collect_body ->
  140. server_loop(Sock, Sock_type, Req_1)
  141. end;
  142. {http, Sock, {http_error, Err}} ->
  143. io:format("Error parsing HTTP request:~n"
  144. "Req so far : ~p~n"
  145. "Err : ~p", [Req, Err]),
  146. exit({http_error, Err});
  147. {setopts, Opts} ->
  148. setopts(Sock, Sock_type, Opts),
  149. server_loop(Sock, Sock_type, Req);
  150. {tcp_closed, Sock} ->
  151. do_trace("Client closed connection~n", []),
  152. ok;
  153. Other ->
  154. io:format("Recvd unknown msg: ~p~n", [Other]),
  155. exit({unknown_msg, Other})
  156. after 120000 ->
  157. do_trace("Timing out client connection~n", []),
  158. ok
  159. end.
  160. do_trace(Fmt, Args) ->
  161. do_trace(get(my_trace_flag), Fmt, Args).
  162. do_trace(true, Fmt, Args) ->
  163. io:format("~s -- " ++ Fmt, [ibrowse_lib:printable_date() | Args]);
  164. do_trace(_, _, _) ->
  165. ok.
  166. process_request(Sock, Sock_type,
  167. #request{method='GET',
  168. headers = Headers,
  169. uri = {abs_path, "/ibrowse_stream_once_chunk_pipeline_test"}} = Req) ->
  170. Req_id = case lists:keysearch("X-Ibrowse-Request-Id", 3, Headers) of
  171. false ->
  172. "";
  173. {value, {http_header, _, _, _, Req_id_1}} ->
  174. Req_id_1
  175. end,
  176. Req_id_header = ["x-ibrowse-request-id: ", Req_id, "\r\n"],
  177. do_trace("Recvd req: ~p~n", [Req]),
  178. Body = string:join([integer_to_list(X) || X <- lists:seq(1,100)], "-"),
  179. Chunked_body = chunk_request_body(Body, 50),
  180. Resp_1 = [<<"HTTP/1.1 200 OK\r\n">>,
  181. Req_id_header,
  182. <<"Transfer-Encoding: chunked\r\n\r\n">>],
  183. Resp_2 = Chunked_body,
  184. do_send(Sock, Sock_type, Resp_1),
  185. timer:sleep(100),
  186. do_send(Sock, Sock_type, Resp_2);
  187. process_request(Sock, Sock_type,
  188. #request{method='GET',
  189. headers = _Headers,
  190. uri = {abs_path, "/ibrowse_inac_timeout_test"}} = Req) ->
  191. do_trace("Recvd req: ~p. Sleeping for 30 secs...~n", [Req]),
  192. timer:sleep(3000),
  193. do_trace("...Sending response now.~n", []),
  194. Resp = <<"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n">>,
  195. do_send(Sock, Sock_type, Resp);
  196. process_request(Sock, Sock_type,
  197. #request{method='HEAD',
  198. headers = _Headers,
  199. uri = {abs_path, "/ibrowse_head_transfer_enc"}}) ->
  200. 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">>,
  201. do_send(Sock, Sock_type, Resp);
  202. process_request(Sock, Sock_type,
  203. #request{method='POST',
  204. headers = Headers,
  205. uri = {abs_path, "/echo_body"},
  206. body = Body}) ->
  207. Content_len = get_content_length(Headers),
  208. case iolist_size(Body) == Content_len of
  209. true ->
  210. Resp = [<<"HTTP/1.1 200 OK\r\nContent-Length: ">>, integer_to_list(Content_len), <<"\r\nServer: ibrowse_test_server\r\n\r\n">>, Body],
  211. do_send(Sock, Sock_type, list_to_binary(Resp));
  212. false ->
  213. collect_body
  214. end;
  215. process_request(Sock, Sock_type,
  216. #request{method='GET',
  217. headers = Headers,
  218. uri = {abs_path, "/ibrowse_echo_header"}}) ->
  219. Tag = "x-binary",
  220. Headers_1 = [{to_lower(X), to_lower(Y)} || {http_header, _, X, _, Y} <- Headers],
  221. X_binary_header_val = case lists:keysearch(Tag, 1, Headers_1) of
  222. false ->
  223. "not_found";
  224. {value, {_, V}} ->
  225. V
  226. end,
  227. Resp = [<<"HTTP/1.1 200 OK\r\n">>,
  228. <<"Server: ibrowse_test\r\n">>,
  229. Tag, ": ", X_binary_header_val, "\r\n",
  230. <<"Content-Length: 0\r\n\r\n">>],
  231. do_send(Sock, Sock_type, Resp);
  232. process_request(Sock, Sock_type,
  233. #request{method='HEAD',
  234. headers = _Headers,
  235. uri = {abs_path, "/ibrowse_head_test"}}) ->
  236. 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">>,
  237. do_send(Sock, Sock_type, Resp);
  238. process_request(Sock, Sock_type,
  239. #request{method='POST',
  240. headers = _Headers,
  241. uri = {abs_path, "/ibrowse_303_no_body_test"}}) ->
  242. Resp = <<"HTTP/1.1 303 See Other\r\nLocation: http://example.org\r\n\r\n">>,
  243. do_send(Sock, Sock_type, Resp);
  244. process_request(Sock, Sock_type,
  245. #request{method='POST',
  246. headers = _Headers,
  247. uri = {abs_path, "/ibrowse_303_with_body_test"}}) ->
  248. Resp = <<"HTTP/1.1 303 See Other\r\nLocation: http://example.org\r\nContent-Length: 5\r\n\r\nabcde">>,
  249. do_send(Sock, Sock_type, Resp);
  250. process_request(Sock, Sock_type,
  251. #request{method='GET',
  252. headers = _Headers,
  253. uri = {abs_path, "/ibrowse_preserve_status_line"}}) ->
  254. Resp = <<"HTTP/1.1 200 OKBlah\r\nContent-Length: 5\r\n\r\nabcde">>,
  255. do_send(Sock, Sock_type, Resp);
  256. process_request(Sock, Sock_type,
  257. #request{method='GET',
  258. headers = _Headers,
  259. uri = {abs_path, "/ibrowse_handle_one_request_only_with_delay"}}) ->
  260. timer:sleep(2000),
  261. 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">>,
  262. do_send(Sock, Sock_type, Resp),
  263. close_connection;
  264. process_request(Sock, Sock_type,
  265. #request{method='GET',
  266. headers = _Headers,
  267. uri = {abs_path, "/ibrowse_handle_one_request_only"}}) ->
  268. 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">>,
  269. do_send(Sock, Sock_type, Resp),
  270. close_connection;
  271. process_request(Sock, Sock_type,
  272. #request{method='GET',
  273. headers = _Headers,
  274. uri = {abs_path, "/ibrowse_send_file_conn_close"}}) ->
  275. 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\nblahblah-">>,
  276. do_send(Sock, Sock_type, Resp),
  277. timer:sleep(1000),
  278. do_send(Sock, Sock_type, <<"blahblah">>),
  279. close_connection;
  280. process_request(_Sock, _Sock_type, #request{uri = {abs_path, "/never_respond"} } ) ->
  281. not_done;
  282. process_request(Sock, Sock_type, Req) ->
  283. do_trace("Recvd req: ~p~n", [Req]),
  284. Resp = <<"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n">>,
  285. do_send(Sock, Sock_type, Resp),
  286. timer:sleep(?RAND:uniform(100)).
  287. do_send(Sock, tcp, Resp) ->
  288. gen_tcp:send(Sock, Resp);
  289. do_send(Sock, ssl, Resp) ->
  290. ssl:send(Sock, Resp).
  291. %%------------------------------------------------------------------------------
  292. %% Utility functions
  293. %%------------------------------------------------------------------------------
  294. chunk_request_body(Body, _ChunkSize) when is_tuple(Body) orelse
  295. is_function(Body) ->
  296. Body;
  297. chunk_request_body(Body, ChunkSize) ->
  298. chunk_request_body(Body, ChunkSize, []).
  299. chunk_request_body(Body, _ChunkSize, Acc) when Body == <<>>; Body == [] ->
  300. LastChunk = "0\r\n",
  301. lists:reverse(["\r\n", LastChunk | Acc]);
  302. chunk_request_body(Body, ChunkSize, Acc) when is_binary(Body),
  303. size(Body) >= ChunkSize ->
  304. <<ChunkBody:ChunkSize/binary, Rest/binary>> = Body,
  305. Chunk = [?dec2hex(ChunkSize),"\r\n",
  306. ChunkBody, "\r\n"],
  307. chunk_request_body(Rest, ChunkSize, [Chunk | Acc]);
  308. chunk_request_body(Body, _ChunkSize, Acc) when is_binary(Body) ->
  309. BodySize = size(Body),
  310. Chunk = [?dec2hex(BodySize),"\r\n",
  311. Body, "\r\n"],
  312. LastChunk = "0\r\n",
  313. lists:reverse(["\r\n", LastChunk, Chunk | Acc]);
  314. chunk_request_body(Body, ChunkSize, Acc) when length(Body) >= ChunkSize ->
  315. {ChunkBody, Rest} = split_list_at(Body, ChunkSize),
  316. Chunk = [?dec2hex(ChunkSize),"\r\n",
  317. ChunkBody, "\r\n"],
  318. chunk_request_body(Rest, ChunkSize, [Chunk | Acc]);
  319. chunk_request_body(Body, _ChunkSize, Acc) when is_list(Body) ->
  320. BodySize = length(Body),
  321. Chunk = [?dec2hex(BodySize),"\r\n",
  322. Body, "\r\n"],
  323. LastChunk = "0\r\n",
  324. lists:reverse(["\r\n", LastChunk, Chunk | Acc]).
  325. split_list_at(List, N) ->
  326. split_list_at(List, N, []).
  327. split_list_at([], _, Acc) ->
  328. {lists:reverse(Acc), []};
  329. split_list_at(List2, 0, List1) ->
  330. {lists:reverse(List1), List2};
  331. split_list_at([H | List2], N, List1) ->
  332. split_list_at(List2, N-1, [H | List1]).
  333. to_lower(X) when is_atom(X) ->
  334. list_to_atom(to_lower(atom_to_list(X)));
  335. to_lower(X) when is_list(X) ->
  336. string:to_lower(X).
  337. get_content_length([{http_header, _, 'Content-Length', _, V} | _]) ->
  338. list_to_integer(V);
  339. get_content_length([{http_header, _, _X, _, _Y} | T]) ->
  340. get_content_length(T);
  341. get_content_length([]) ->
  342. undefined.