Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

375 рядки
14 KiB

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