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.

679 lines
25 KiB

13 years ago
14 years ago
13 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
13 years ago
13 years ago
13 years ago
  1. %%% File : ibrowse_test.erl
  2. %%% Author : Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk>
  3. %%% Description : Test ibrowse
  4. %%% Created : 14 Oct 2003 by Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk>
  5. -module(ibrowse_test).
  6. -export([
  7. load_test/3,
  8. send_reqs_1/3,
  9. do_send_req/2,
  10. unit_tests/0,
  11. unit_tests/1,
  12. unit_tests_1/2,
  13. ue_test/0,
  14. ue_test/1,
  15. verify_chunked_streaming/0,
  16. verify_chunked_streaming/1,
  17. test_chunked_streaming_once/0,
  18. i_do_async_req_list/4,
  19. test_stream_once/3,
  20. test_stream_once/4,
  21. test_20122010/0,
  22. test_20122010/1,
  23. test_pipeline_head_timeout/0,
  24. test_pipeline_head_timeout/1,
  25. do_test_pipeline_head_timeout/4,
  26. test_head_transfer_encoding/0,
  27. test_head_transfer_encoding/1,
  28. test_head_response_with_body/0,
  29. test_head_response_with_body/1,
  30. i_do_streaming_request/4,
  31. i_do_streaming_request2/2,
  32. test_put_request/0,
  33. test_put_request/1,
  34. test_put_request_chunked/0,
  35. test_put_request_chunked/1
  36. ]).
  37. test_stream_once(Url, Method, Options) ->
  38. test_stream_once(Url, Method, Options, 5000).
  39. test_stream_once(Url, Method, Options, Timeout) ->
  40. case ibrowse:send_req(Url, [], Method, [], [{stream_to, {self(), once}} | Options], Timeout) of
  41. {ibrowse_req_id, Req_id} ->
  42. case ibrowse:stream_next(Req_id) of
  43. ok ->
  44. test_stream_once(Req_id);
  45. Err ->
  46. Err
  47. end;
  48. Err ->
  49. Err
  50. end.
  51. test_stream_once(Req_id) ->
  52. receive
  53. {ibrowse_async_headers, Req_id, StatCode, Headers} ->
  54. io:format("Recvd headers~n~p~n", [{ibrowse_async_headers, Req_id, StatCode, Headers}]),
  55. case ibrowse:stream_next(Req_id) of
  56. ok ->
  57. test_stream_once(Req_id);
  58. Err ->
  59. Err
  60. end;
  61. {ibrowse_async_response, Req_id, {error, Err}} ->
  62. io:format("Recvd error: ~p~n", [Err]);
  63. {ibrowse_async_response, Req_id, Body_1} ->
  64. io:format("Recvd body part: ~n~p~n", [{ibrowse_async_response, Req_id, Body_1}]),
  65. case ibrowse:stream_next(Req_id) of
  66. ok ->
  67. test_stream_once(Req_id);
  68. Err ->
  69. Err
  70. end;
  71. {ibrowse_async_response_end, Req_id} ->
  72. ok
  73. end.
  74. %% Use ibrowse:set_max_sessions/3 and ibrowse:set_max_pipeline_size/3 to
  75. %% tweak settings before running the load test. The defaults are 10 and 10.
  76. load_test(Url, NumWorkers, NumReqsPerWorker) when is_list(Url),
  77. is_integer(NumWorkers),
  78. is_integer(NumReqsPerWorker),
  79. NumWorkers > 0,
  80. NumReqsPerWorker > 0 ->
  81. proc_lib:spawn(?MODULE, send_reqs_1, [Url, NumWorkers, NumReqsPerWorker]).
  82. send_reqs_1(Url, NumWorkers, NumReqsPerWorker) ->
  83. Start_time = now(),
  84. ets:new(pid_table, [named_table, public]),
  85. ets:new(ibrowse_test_results, [named_table, public]),
  86. ets:new(ibrowse_errors, [named_table, public, ordered_set]),
  87. init_results(),
  88. process_flag(trap_exit, true),
  89. log_msg("Starting spawning of workers...~n", []),
  90. spawn_workers(Url, NumWorkers, NumReqsPerWorker),
  91. log_msg("Finished spawning workers...~n", []),
  92. do_wait(Url),
  93. End_time = now(),
  94. log_msg("All workers are done...~n", []),
  95. log_msg("ibrowse_test_results table: ~n~p~n", [ets:tab2list(ibrowse_test_results)]),
  96. log_msg("Start time: ~1000.p~n", [calendar:now_to_local_time(Start_time)]),
  97. log_msg("End time : ~1000.p~n", [calendar:now_to_local_time(End_time)]),
  98. Elapsed_time_secs = trunc(timer:now_diff(End_time, Start_time) / 1000000),
  99. log_msg("Elapsed : ~p~n", [Elapsed_time_secs]),
  100. log_msg("Reqs/sec : ~p~n", [round(trunc((NumWorkers*NumReqsPerWorker) / Elapsed_time_secs))]),
  101. dump_errors().
  102. init_results() ->
  103. ets:insert(ibrowse_test_results, {crash, 0}),
  104. ets:insert(ibrowse_test_results, {send_failed, 0}),
  105. ets:insert(ibrowse_test_results, {other_error, 0}),
  106. ets:insert(ibrowse_test_results, {success, 0}),
  107. ets:insert(ibrowse_test_results, {retry_later, 0}),
  108. ets:insert(ibrowse_test_results, {trid_mismatch, 0}),
  109. ets:insert(ibrowse_test_results, {success_no_trid, 0}),
  110. ets:insert(ibrowse_test_results, {failed, 0}),
  111. ets:insert(ibrowse_test_results, {timeout, 0}),
  112. ets:insert(ibrowse_test_results, {req_id, 0}).
  113. spawn_workers(_Url, 0, _) ->
  114. ok;
  115. spawn_workers(Url, NumWorkers, NumReqsPerWorker) ->
  116. Pid = proc_lib:spawn_link(?MODULE, do_send_req, [Url, NumReqsPerWorker]),
  117. ets:insert(pid_table, {Pid, []}),
  118. spawn_workers(Url, NumWorkers - 1, NumReqsPerWorker).
  119. do_wait(Url) ->
  120. receive
  121. {'EXIT', _, normal} ->
  122. catch ibrowse:show_dest_status(Url),
  123. catch ibrowse:show_dest_status(),
  124. do_wait(Url);
  125. {'EXIT', Pid, Reason} ->
  126. ets:delete(pid_table, Pid),
  127. ets:insert(ibrowse_errors, {Pid, Reason}),
  128. ets:update_counter(ibrowse_test_results, crash, 1),
  129. do_wait(Url);
  130. Msg ->
  131. io:format("Recvd unknown message...~p~n", [Msg]),
  132. do_wait(Url)
  133. after 1000 ->
  134. case ets:info(pid_table, size) of
  135. 0 ->
  136. done;
  137. _ ->
  138. catch ibrowse:show_dest_status(Url),
  139. catch ibrowse:show_dest_status(),
  140. do_wait(Url)
  141. end
  142. end.
  143. do_send_req(Url, NumReqs) ->
  144. do_send_req_1(Url, NumReqs).
  145. do_send_req_1(_Url, 0) ->
  146. ets:delete(pid_table, self());
  147. do_send_req_1(Url, NumReqs) ->
  148. Counter = integer_to_list(ets:update_counter(ibrowse_test_results, req_id, 1)),
  149. case ibrowse:send_req(Url, [{"ib_req_id", Counter}], get, [], [], 10000) of
  150. {ok, _Status, Headers, _Body} ->
  151. case lists:keysearch("ib_req_id", 1, Headers) of
  152. {value, {_, Counter}} ->
  153. ets:update_counter(ibrowse_test_results, success, 1);
  154. {value, _} ->
  155. ets:update_counter(ibrowse_test_results, trid_mismatch, 1);
  156. false ->
  157. ets:update_counter(ibrowse_test_results, success_no_trid, 1)
  158. end;
  159. {error, req_timedout} ->
  160. ets:update_counter(ibrowse_test_results, timeout, 1);
  161. {error, send_failed} ->
  162. ets:update_counter(ibrowse_test_results, send_failed, 1);
  163. {error, retry_later} ->
  164. ets:update_counter(ibrowse_test_results, retry_later, 1);
  165. Err ->
  166. ets:insert(ibrowse_errors, {now(), Err}),
  167. ets:update_counter(ibrowse_test_results, other_error, 1),
  168. ok
  169. end,
  170. do_send_req_1(Url, NumReqs-1).
  171. dump_errors() ->
  172. case ets:info(ibrowse_errors, size) of
  173. 0 ->
  174. ok;
  175. _ ->
  176. {A, B, C} = now(),
  177. Filename = lists:flatten(
  178. io_lib:format("ibrowse_errors_~p_~p_~p.txt" , [A, B, C])),
  179. case file:open(Filename, [write, delayed_write, raw]) of
  180. {ok, Iod} ->
  181. dump_errors(ets:first(ibrowse_errors), Iod);
  182. Err ->
  183. io:format("failed to create file ~s. Reason: ~p~n", [Filename, Err]),
  184. ok
  185. end
  186. end.
  187. dump_errors('$end_of_table', Iod) ->
  188. file:close(Iod);
  189. dump_errors(Key, Iod) ->
  190. [{_, Term}] = ets:lookup(ibrowse_errors, Key),
  191. file:write(Iod, io_lib:format("~p~n", [Term])),
  192. dump_errors(ets:next(ibrowse_errors, Key), Iod).
  193. %%------------------------------------------------------------------------------
  194. %% Unit Tests
  195. %%------------------------------------------------------------------------------
  196. -define(TEST_LIST, [{"http://intranet/messenger", get},
  197. {"http://www.google.co.uk", get},
  198. {"http://www.google.com", get},
  199. {"http://www.google.com", options},
  200. {"https://mail.google.com", get},
  201. {"http://www.sun.com", get},
  202. {"http://www.oracle.com", get},
  203. {"http://www.bbc.co.uk", get},
  204. {"http://www.bbc.co.uk", trace},
  205. {"http://www.bbc.co.uk", options},
  206. {"http://yaws.hyber.org", get},
  207. {"http://jigsaw.w3.org/HTTP/ChunkedScript", get},
  208. {"http://jigsaw.w3.org/HTTP/TE/foo.txt", get},
  209. {"http://jigsaw.w3.org/HTTP/TE/bar.txt", get},
  210. {"http://jigsaw.w3.org/HTTP/connection.html", get},
  211. {"http://jigsaw.w3.org/HTTP/cc.html", get},
  212. {"http://jigsaw.w3.org/HTTP/cc-private.html", get},
  213. {"http://jigsaw.w3.org/HTTP/cc-proxy-revalidate.html", get},
  214. {"http://jigsaw.w3.org/HTTP/cc-nocache.html", get},
  215. {"http://jigsaw.w3.org/HTTP/h-content-md5.html", get},
  216. {"http://jigsaw.w3.org/HTTP/h-retry-after.html", get},
  217. {"http://jigsaw.w3.org/HTTP/h-retry-after-date.html", get},
  218. {"http://jigsaw.w3.org/HTTP/neg", get},
  219. {"http://jigsaw.w3.org/HTTP/negbad", get},
  220. {"http://jigsaw.w3.org/HTTP/400/toolong/", get},
  221. {"http://jigsaw.w3.org/HTTP/300/", get},
  222. {"http://jigsaw.w3.org/HTTP/Basic/", get, [{basic_auth, {"guest", "guest"}}]},
  223. {"http://jigsaw.w3.org/HTTP/CL/", get},
  224. {"http://www.httpwatch.com/httpgallery/chunked/", get},
  225. {"https://github.com", get, [{ssl_options, [{depth, 2}]}]},
  226. {local_test_fun, test_20122010, []},
  227. {local_test_fun, test_pipeline_head_timeout, []},
  228. {local_test_fun, test_head_transfer_encoding, []},
  229. {local_test_fun, test_head_response_with_body, []},
  230. {local_test_fun, test_put_request, []},
  231. {local_test_fun, test_put_request_chunked, []}
  232. ]).
  233. unit_tests() ->
  234. unit_tests([]).
  235. unit_tests(Options) ->
  236. application:start(crypto),
  237. application:start(public_key),
  238. application:start(ssl),
  239. (catch ibrowse_test_server:start_server(8181, tcp)),
  240. ibrowse:start(),
  241. Options_1 = Options ++ [{connect_timeout, 5000}],
  242. Test_timeout = proplists:get_value(test_timeout, Options, 60000),
  243. {Pid, Ref} = erlang:spawn_monitor(?MODULE, unit_tests_1, [self(), Options_1]),
  244. receive
  245. {done, Pid} ->
  246. ok;
  247. {'DOWN', Ref, _, _, Info} ->
  248. io:format("Test process crashed: ~p~n", [Info])
  249. after Test_timeout ->
  250. exit(Pid, kill),
  251. io:format("Timed out waiting for tests to complete~n", [])
  252. end,
  253. catch ibrowse_test_server:stop_server(8181),
  254. ok.
  255. unit_tests_1(Parent, Options) ->
  256. lists:foreach(fun({local_test_fun, Fun_name, Args}) ->
  257. execute_req(local_test_fun, Fun_name, Args);
  258. ({Url, Method}) ->
  259. execute_req(Url, Method, Options);
  260. ({Url, Method, X_Opts}) ->
  261. execute_req(Url, Method, X_Opts ++ Options)
  262. end, ?TEST_LIST),
  263. Parent ! {done, self()}.
  264. verify_chunked_streaming() ->
  265. verify_chunked_streaming([]).
  266. verify_chunked_streaming(Options) ->
  267. io:format("~nVerifying that chunked streaming is working...~n", []),
  268. Url = "http://www.httpwatch.com/httpgallery/chunked/",
  269. io:format(" URL: ~s~n", [Url]),
  270. io:format(" Fetching data without streaming...~n", []),
  271. Result_without_streaming = ibrowse:send_req(
  272. Url, [], get, [],
  273. [{response_format, binary} | Options]),
  274. io:format(" Fetching data with streaming as list...~n", []),
  275. Async_response_list = do_async_req_list(
  276. Url, get, i_do_async_req_list, [{response_format, list} | Options]),
  277. io:format(" Fetching data with streaming as binary...~n", []),
  278. Async_response_bin = do_async_req_list(
  279. Url, get, i_do_async_req_list, [{response_format, binary} | Options]),
  280. io:format(" Fetching data with streaming as binary, {active, once}...~n", []),
  281. Async_response_bin_once = do_async_req_list(
  282. Url, get, i_do_async_req_list, [once, {response_format, binary} | Options]),
  283. Res1 = compare_responses(Result_without_streaming, Async_response_list, Async_response_bin),
  284. Res2 = compare_responses(Result_without_streaming, Async_response_list, Async_response_bin_once),
  285. case {Res1, Res2} of
  286. {success, success} ->
  287. io:format(" Chunked streaming working~n", []);
  288. _ ->
  289. ok
  290. end.
  291. test_chunked_streaming_once() ->
  292. test_chunked_streaming_once([]).
  293. test_chunked_streaming_once(Options) ->
  294. io:format("~nTesting chunked streaming with the {stream_to, {Pid, once}} option...~n", []),
  295. Url = "http://www.httpwatch.com/httpgallery/chunked/",
  296. io:format(" URL: ~s~n", [Url]),
  297. io:format(" Fetching data with streaming as binary, {active, once}...~n", []),
  298. case do_async_req_list(Url, get, i_do_async_req_list, [once, {response_format, binary} | Options]) of
  299. {ok, _, _, _} ->
  300. io:format(" Success!~n", []);
  301. Err ->
  302. io:format(" Fail: ~p~n", [Err])
  303. end.
  304. compare_responses({ok, St_code, _, Body}, {ok, St_code, _, Body}, {ok, St_code, _, Body}) ->
  305. success;
  306. compare_responses({ok, St_code, _, Body_1}, {ok, St_code, _, Body_2}, {ok, St_code, _, Body_3}) ->
  307. case Body_1 of
  308. Body_2 ->
  309. io:format("Body_1 and Body_2 match~n", []);
  310. Body_3 ->
  311. io:format("Body_1 and Body_3 match~n", []);
  312. _ when Body_2 == Body_3 ->
  313. io:format("Body_2 and Body_3 match~n", []);
  314. _ ->
  315. io:format("All three bodies are different!~n", [])
  316. end,
  317. io:format("Body_1 -> ~p~n", [Body_1]),
  318. io:format("Body_2 -> ~p~n", [Body_2]),
  319. io:format("Body_3 -> ~p~n", [Body_3]),
  320. fail_bodies_mismatch;
  321. compare_responses(R1, R2, R3) ->
  322. io:format("R1 -> ~p~n", [R1]),
  323. io:format("R2 -> ~p~n", [R2]),
  324. io:format("R3 -> ~p~n", [R3]),
  325. fail.
  326. %% do_async_req_list(Url) ->
  327. %% do_async_req_list(Url, get).
  328. %% do_async_req_list(Url, Method) ->
  329. %% do_async_req_list(Url, Method, [{stream_to, self()},
  330. %% {stream_chunk_size, 1000}]).
  331. do_async_req_list(Url, Method, Fun, Options) ->
  332. {Pid,_} = erlang:spawn_monitor(?MODULE, Fun,
  333. [self(), Url, Method,
  334. Options ++ [{stream_chunk_size, 1000}]]),
  335. %% io:format("Spawned process ~p~n", [Pid]),
  336. wait_for_resp(Pid).
  337. wait_for_resp(Pid) ->
  338. receive
  339. {async_result, Pid, Res} ->
  340. Res;
  341. {async_result, Other_pid, _} ->
  342. io:format("~p: Waiting for result from ~p: got from ~p~n", [self(), Pid, Other_pid]),
  343. wait_for_resp(Pid);
  344. {'DOWN', _, _, Pid, Reason} ->
  345. {'EXIT', Reason};
  346. {'DOWN', _, _, _, _} ->
  347. wait_for_resp(Pid);
  348. Msg ->
  349. io:format("Recvd unknown message: ~p~n", [Msg]),
  350. wait_for_resp(Pid)
  351. after 100000 ->
  352. {error, timeout}
  353. end.
  354. i_do_async_req_list(Parent, Url, Method, Options) ->
  355. Options_1 = case lists:member(once, Options) of
  356. true ->
  357. [{stream_to, {self(), once}} | (Options -- [once])];
  358. false ->
  359. [{stream_to, self()} | Options]
  360. end,
  361. Res = ibrowse:send_req(Url, [], Method, [], Options_1),
  362. case Res of
  363. {ibrowse_req_id, Req_id} ->
  364. Result = wait_for_async_resp(Req_id, Options, undefined, undefined, []),
  365. Parent ! {async_result, self(), Result};
  366. Err ->
  367. Parent ! {async_result, self(), Err}
  368. end.
  369. wait_for_async_resp(Req_id, Options, Acc_Stat_code, Acc_Headers, Body) ->
  370. receive
  371. {ibrowse_async_headers, Req_id, StatCode, Headers} ->
  372. %% io:format("Recvd headers...~n", []),
  373. maybe_stream_next(Req_id, Options),
  374. wait_for_async_resp(Req_id, Options, StatCode, Headers, Body);
  375. {ibrowse_async_response_end, Req_id} ->
  376. %% io:format("Recvd end of response.~n", []),
  377. Body_1 = list_to_binary(lists:reverse(Body)),
  378. {ok, Acc_Stat_code, Acc_Headers, Body_1};
  379. {ibrowse_async_response, Req_id, Data} ->
  380. maybe_stream_next(Req_id, Options),
  381. %% io:format("Recvd data...~n", []),
  382. wait_for_async_resp(Req_id, Options, Acc_Stat_code, Acc_Headers, [Data | Body]);
  383. {ibrowse_async_response, Req_id, {error, _} = Err} ->
  384. {ok, Acc_Stat_code, Acc_Headers, Err};
  385. Err ->
  386. {ok, Acc_Stat_code, Acc_Headers, Err}
  387. after 10000 ->
  388. {timeout, Acc_Stat_code, Acc_Headers, Body}
  389. end.
  390. maybe_stream_next(Req_id, Options) ->
  391. case lists:member(once, Options) of
  392. true ->
  393. ibrowse:stream_next(Req_id);
  394. false ->
  395. ok
  396. end.
  397. i_do_streaming_request(Parent, Url, Method, Options) ->
  398. {Headers, Options_1} = case lists:member(chunked, Options) of
  399. true -> {[], [{transfer_encoding, chunked} | (Options -- [chunked])]};
  400. false -> {[{"Content-Length", "6"}], Options}
  401. end,
  402. Res = ibrowse:send_req(Url, Headers, Method, <<"">>,
  403. [{stream_to, self()} | Options_1]),
  404. case Res of
  405. {ibrowse_req_id, Req_id} ->
  406. Result = i_do_streaming_request2(Req_id, Options),
  407. Parent ! {async_result, self(), Result};
  408. Err ->
  409. Parent ! {async_result, self(), Err}
  410. end.
  411. i_do_streaming_request2(Req_id, Options) ->
  412. ibrowse:send_chunk(Req_id, <<"aaa">>),
  413. ibrowse:send_chunk(Req_id, <<"bbb">>),
  414. ibrowse:send_done(Req_id),
  415. wait_for_async_resp(Req_id, Options, undefined, undefined, []).
  416. execute_req(local_test_fun, Method, Args) ->
  417. io:format(" ~-54.54w: ", [Method]),
  418. Result = (catch apply(?MODULE, Method, Args)),
  419. io:format("~p~n", [Result]);
  420. execute_req(Url, Method, Options) ->
  421. io:format("~7.7w, ~50.50s: ", [Method, Url]),
  422. Result = (catch ibrowse:send_req(Url, [], Method, [], Options)),
  423. case Result of
  424. {ok, SCode, _H, _B} ->
  425. io:format("Status code: ~p~n", [SCode]);
  426. Err ->
  427. io:format("~p~n", [Err])
  428. end.
  429. ue_test() ->
  430. ue_test(lists:duplicate(1024, $?)).
  431. ue_test(Data) ->
  432. {Time, Res} = timer:tc(ibrowse_lib, url_encode, [Data]),
  433. io:format("Time -> ~p~n", [Time]),
  434. io:format("Data Length -> ~p~n", [length(Data)]),
  435. io:format("Res Length -> ~p~n", [length(Res)]).
  436. % io:format("Result -> ~s~n", [Res]).
  437. log_msg(Fmt, Args) ->
  438. io:format("~s -- " ++ Fmt,
  439. [ibrowse_lib:printable_date() | Args]).
  440. %%------------------------------------------------------------------------------
  441. %% Test what happens when the response to a HEAD request is a
  442. %% Chunked-Encoding response with a non-empty body. Issue #67 on
  443. %% Github
  444. %% ------------------------------------------------------------------------------
  445. test_head_transfer_encoding() ->
  446. clear_msg_q(),
  447. test_head_transfer_encoding("http://localhost:8181/ibrowse_head_test").
  448. test_head_transfer_encoding(Url) ->
  449. case ibrowse:send_req(Url, [], head) of
  450. {ok, "200", _, _} ->
  451. success;
  452. Res ->
  453. {test_failed, Res}
  454. end.
  455. %%------------------------------------------------------------------------------
  456. %% Test what happens when the response to a HEAD request is a
  457. %% Chunked-Encoding response with a non-empty body. Issue #67 on
  458. %% Github
  459. %% ------------------------------------------------------------------------------
  460. test_head_response_with_body() ->
  461. clear_msg_q(),
  462. test_head_response_with_body("http://localhost:8181/ibrowse_head_transfer_enc").
  463. test_head_response_with_body(Url) ->
  464. case ibrowse:send_req(Url, [], head, [], [{workaround, head_response_with_body}]) of
  465. {ok, "400", _, _} ->
  466. success;
  467. Res ->
  468. {test_failed, Res}
  469. end.
  470. %%------------------------------------------------------------------------------
  471. %% Test what happens when the request at the head of a pipeline times out
  472. %%------------------------------------------------------------------------------
  473. test_pipeline_head_timeout() ->
  474. clear_msg_q(),
  475. test_pipeline_head_timeout("http://localhost:8181/ibrowse_inac_timeout_test").
  476. test_pipeline_head_timeout(Url) ->
  477. {ok, Pid} = ibrowse:spawn_worker_process(Url),
  478. Test_parent = self(),
  479. Fun = fun({fixed, Timeout}) ->
  480. spawn(fun() ->
  481. do_test_pipeline_head_timeout(Url, Pid, Test_parent, Timeout)
  482. end);
  483. (Timeout_mult) ->
  484. spawn(fun() ->
  485. Timeout = 1000 + Timeout_mult*1000,
  486. do_test_pipeline_head_timeout(Url, Pid, Test_parent, Timeout)
  487. end)
  488. end,
  489. Pids = [Fun(X) || X <- [{fixed, 32000} | lists:seq(1,10)]],
  490. Result = accumulate_worker_resp(Pids),
  491. case lists:all(fun({_, X_res}) ->
  492. X_res == {error,req_timedout}
  493. end, Result) of
  494. true ->
  495. success;
  496. false ->
  497. {test_failed, Result}
  498. end.
  499. do_test_pipeline_head_timeout(Url, Pid, Test_parent, Req_timeout) ->
  500. Resp = ibrowse:send_req_direct(
  501. Pid,
  502. Url,
  503. [], get, [],
  504. [{socket_options,[{keepalive,true}]},
  505. {inactivity_timeout,180000},
  506. {connect_timeout,180000}], Req_timeout),
  507. Test_parent ! {self(), Resp}.
  508. accumulate_worker_resp(Pids) ->
  509. accumulate_worker_resp(Pids, []).
  510. accumulate_worker_resp([_ | _] = Pids, Acc) ->
  511. receive
  512. {Pid, Res} when is_pid(Pid) ->
  513. accumulate_worker_resp(Pids -- [Pid], [{Pid, Res} | Acc]);
  514. Err ->
  515. io:format("Received unexpected: ~p~n", [Err])
  516. end;
  517. accumulate_worker_resp([], Acc) ->
  518. lists:reverse(Acc).
  519. clear_msg_q() ->
  520. receive
  521. _ ->
  522. clear_msg_q()
  523. after 0 ->
  524. ok
  525. end.
  526. %%------------------------------------------------------------------------------
  527. %%
  528. %%------------------------------------------------------------------------------
  529. test_20122010() ->
  530. test_20122010("http://localhost:8181").
  531. test_20122010(Url) ->
  532. {ok, Pid} = ibrowse:spawn_worker_process(Url),
  533. Expected_resp = <<"1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24-25-26-27-28-29-30-31-32-33-34-35-36-37-38-39-40-41-42-43-44-45-46-47-48-49-50-51-52-53-54-55-56-57-58-59-60-61-62-63-64-65-66-67-68-69-70-71-72-73-74-75-76-77-78-79-80-81-82-83-84-85-86-87-88-89-90-91-92-93-94-95-96-97-98-99-100">>,
  534. Test_parent = self(),
  535. Fun = fun() ->
  536. do_test_20122010(Url, Pid, Expected_resp, Test_parent)
  537. end,
  538. Pids = [erlang:spawn_monitor(Fun) || _ <- lists:seq(1,10)],
  539. wait_for_workers(Pids).
  540. wait_for_workers([{Pid, _Ref} | Pids]) ->
  541. receive
  542. {Pid, success} ->
  543. wait_for_workers(Pids)
  544. after 60000 ->
  545. test_failed
  546. end;
  547. wait_for_workers([]) ->
  548. success.
  549. do_test_20122010(Url, Pid, Expected_resp, Test_parent) ->
  550. do_test_20122010(10, Url, Pid, Expected_resp, Test_parent).
  551. do_test_20122010(0, _Url, _Pid, _Expected_resp, Test_parent) ->
  552. Test_parent ! {self(), success};
  553. do_test_20122010(Rem_count, Url, Pid, Expected_resp, Test_parent) ->
  554. {ibrowse_req_id, Req_id} = ibrowse:send_req_direct(
  555. Pid,
  556. Url ++ "/ibrowse_stream_once_chunk_pipeline_test",
  557. [], get, [],
  558. [{stream_to, {self(), once}},
  559. {inactivity_timeout, 10000},
  560. {include_ibrowse_req_id, true}]),
  561. do_trace("~p -- sent request ~1000.p~n", [self(), Req_id]),
  562. Req_id_str = lists:flatten(io_lib:format("~1000.p",[Req_id])),
  563. receive
  564. {ibrowse_async_headers, Req_id, "200", Headers} ->
  565. case lists:keysearch("x-ibrowse-request-id", 1, Headers) of
  566. {value, {_, Req_id_str}} ->
  567. ok;
  568. {value, {_, Req_id_1}} ->
  569. do_trace("~p -- Sent req-id: ~1000.p. Recvd: ~1000.p~n",
  570. [self(), Req_id, Req_id_1]),
  571. exit(req_id_mismatch)
  572. end
  573. after 5000 ->
  574. do_trace("~p -- response headers not received~n", [self()]),
  575. exit({timeout, test_failed})
  576. end,
  577. do_trace("~p -- response headers received~n", [self()]),
  578. ok = ibrowse:stream_next(Req_id),
  579. case do_test_20122010_1(Expected_resp, Req_id, []) of
  580. true ->
  581. do_test_20122010(Rem_count - 1, Url, Pid, Expected_resp, Test_parent);
  582. false ->
  583. Test_parent ! {self(), failed}
  584. end.
  585. do_test_20122010_1(Expected_resp, Req_id, Acc) ->
  586. receive
  587. {ibrowse_async_response, Req_id, Body_part} ->
  588. ok = ibrowse:stream_next(Req_id),
  589. do_test_20122010_1(Expected_resp, Req_id, [Body_part | Acc]);
  590. {ibrowse_async_response_end, Req_id} ->
  591. Acc_1 = list_to_binary(lists:reverse(Acc)),
  592. Result = Acc_1 == Expected_resp,
  593. do_trace("~p -- End of response. Result: ~p~n", [self(), Result]),
  594. Result
  595. after 1000 ->
  596. exit({timeout, test_failed})
  597. end.
  598. do_trace(Fmt, Args) ->
  599. do_trace(get(my_trace_flag), Fmt, Args).
  600. do_trace(true, Fmt, Args) ->
  601. io:format("~s -- " ++ Fmt, [ibrowse_lib:printable_date() | Args]);
  602. do_trace(_, _, _) ->
  603. ok.
  604. test_put_request() ->
  605. clear_msg_q(),
  606. test_put_request("http://localhost:8181/ibrowse_put_request").
  607. test_put_request(Url) ->
  608. case do_async_req_list(Url, put, i_do_streaming_request,
  609. [{stream_request, true}]) of
  610. {ok, "204", _, _} ->
  611. io:format(" Success!~n", []);
  612. Err ->
  613. io:format(" Fail: ~p~n", [Err])
  614. end.
  615. test_put_request_chunked() ->
  616. clear_msg_q(),
  617. test_put_request_chunked("http://localhost:8181/ibrowse_put_request").
  618. test_put_request_chunked(Url) ->
  619. case do_async_req_list(Url, put, i_do_streaming_request,
  620. [chunked, {stream_request, true}]) of
  621. {ok, "204", _, _} ->
  622. io:format(" Success!~n", []);
  623. Err ->
  624. io:format(" Fail: ~p~n", [Err])
  625. end.