Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

727 lignes
27 KiB

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