%%% ---------------------------------------------------------------------------- %%% Copyright (c) 2009, Erlang Training and Consulting Ltd. %%% All rights reserved. %%% %%% Redistribution and use in source and binary forms, with or without %%% modification, are permitted provided that the following conditions are met: %%% * Redistributions of source code must retain the above copyright %%% notice, this list of conditions and the following disclaimer. %%% * Redistributions in binary form must reproduce the above copyright %%% notice, this list of conditions and the following disclaimer in the %%% documentation and/or other materials provided with the distribution. %%% * Neither the name of Erlang Training and Consulting Ltd. nor the %%% names of its contributors may be used to endorse or promote products %%% derived from this software without specific prior written permission. %%% %%% THIS SOFTWARE IS PROVIDED BY Erlang Training and Consulting Ltd. ''AS IS'' %%% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE %%% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE %%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Training and Consulting Ltd. BE %%% LIABLE SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR %%% BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, %%% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR %%% OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF %%% ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. %%% ---------------------------------------------------------------------------- %%% @author Oscar Hellström -module(fusco_tests). -export([test_no/2]). -include_lib("eunit/include/eunit.hrl"). test_no(N, Tests) -> setelement(2, Tests, setelement(4, element(2, Tests), lists:nth(N, element(4, element(2, Tests))))). %%% Eunit setup stuff start_app() -> [application:start(App) || App <- apps()]. apps() -> [crypto, asn1, public_key, ssl]. stop_app(_) -> [application:stop(App) || App <- lists:reverse(apps())]. tcp_test_() -> {inorder, {setup, fun start_app/0, fun stop_app/1, [ ?_test(get_with_connect_options()), ?_test(no_content_length()), ?_test(no_content_length_1_0()), ?_test(pre_1_1_server_connection()), ?_test(pre_1_1_server_keep_alive()), ?_test(post_100_continue()), ?_test(request_timeout()), ?_test(trailing_space_header()), ?_test(closed_after_timeout()) ]} }. options_test() -> invalid_options(). get_with_connect_options() -> {ok, _, _, Port} = webserver:start(gen_tcp, [fun webserver_utils:empty_body/5]), URL = url(Port), Options = [{connect_options, [{ip, {127, 0, 0, 1}}, {port, 0}]}], {ok, Client} = fusco:start(URL, Options), {ok, Response} = fusco:request(Client, <<"/empty">>, "GET", [], [], 1, 1000), ?assertEqual({<<"200">>, <<"OK">>}, status(Response)), ?assertEqual(<<>>, body(Response)). no_content_length() -> {ok, _, _, Port} = webserver:start(gen_tcp, [fun webserver_utils:no_content_length/5]), URL = url(Port), {ok, Client} = fusco:start(URL, []), {ok, Response} = fusco:request(Client, <<"/no_cl">>, "GET", [], [], 1000), ?assertEqual({<<"200">>, <<"OK">>}, status(Response)), ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response)). no_content_length_1_0() -> {ok, _, _, Port} = webserver:start(gen_tcp, [fun webserver_utils:no_content_length_1_0/5]), URL = url(Port), {ok, Client} = fusco:start(URL, []), {ok, Response} = fusco:request(Client, <<"/no_cl">>, "GET", [], [], 1000), ?assertEqual({<<"200">>, <<"OK">>}, status(Response)), ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response)). %% Check the header value is trimming spaces on header values %% which can cause crash in fusco_client:body_type when Content-Length %% is converted from list to integer trailing_space_header() -> {ok, _, _, Port} = webserver:start(gen_tcp, [fun webserver_utils:trailing_space_header/5]), URL = url(Port), {ok, Client} = fusco:start(URL, []), {ok, Response} = fusco:request(Client, <<"/no_cl">>, "GET", [], [], 1000), Headers = headers(Response), ContentLength = fusco_lib:header_value(<<"content-length">>, Headers), ?assertEqual(<<"14">>, ContentLength). pre_1_1_server_connection() -> {ok, _, _, Port} = webserver:start(gen_tcp, [fun webserver_utils:pre_1_1_server/5]), URL = url(Port), Body = pid_to_list(self()), {ok, Client} = fusco:start(URL, []), {ok, _} = fusco:request(Client, <<"/close">>, "PUT", [], Body, 1000), % Wait for the server to see that socket has been closed. % The socket should be closed by us since the server responded with a % 1.0 version, and not the Connection: keep-alive header. receive closed -> ok end. pre_1_1_server_keep_alive() -> {ok, _, _, Port} = webserver:start(gen_tcp, [ fun webserver_utils:pre_1_1_server_keep_alive/5, fun webserver_utils:pre_1_1_server/5 ]), URL = url(Port), Body = pid_to_list(self()), {ok, Client} = fusco:start(URL, []), {ok, Response1} = fusco:request(Client, <<"/close">>, "GET", [], [], 1000), {ok, Response2} = fusco:request(Client, <<"/close">>, "PUT", [], Body, 1000), ?assertEqual({<<"200">>, <<"OK">>}, status(Response1)), ?assertEqual({<<"200">>, <<"OK">>}, status(Response2)), ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response1)), ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response2)), % Wait for the server to see that socket has been closed. % The socket should be closed by us since the server responded with a % 1.0 version, and not the Connection: keep-alive header. receive closed -> ok end. post_100_continue() -> {ok, _, _, Port} = webserver:start(gen_tcp, [fun webserver_utils:copy_body_100_continue/5]), URL = url(Port), {X, Y, Z} = now(), Body = [ "This is a rather simple post :)", integer_to_list(X), integer_to_list(Y), integer_to_list(Z) ], {ok, Client} = fusco:start(URL, []), {ok, Response} = fusco:request(Client, <<"/post">>, "POST", [], Body, 1000), {StatusCode, ReasonPhrase} = status(Response), ?assertEqual(<<"200">>, StatusCode), ?assertEqual(<<"OK">>, ReasonPhrase), ?assertEqual(iolist_to_binary(Body), body(Response)). request_timeout() -> {ok, _, _, Port} = webserver:start(gen_tcp, [fun webserver_utils:very_slow_response/5]), URL = url(Port), {ok, Client} = fusco:start(URL, []), ?assertEqual({error, timeout}, fusco:request(Client, <<"/slow">>, "GET", [], [], 50)). invalid_options() -> URL = url(5050), ?assertError({bad_option, bad_option}, fusco:start(URL, [bad_option, {foo, bar}])), ?assertError({bad_option, {foo, bar}}, fusco:start(URL, [{foo, bar}, bad_option])). closed_after_timeout() -> {ok, _, _, Port} = webserver:start(gen_tcp, [fun webserver_utils:no_response/5, stay_open]), URL = url(Port), {ok, Client} = fusco:start(URL, []), fusco:request(Client, <<"/slow">>, "GET", [], [], 50), fusco:disconnect(Client), wait_for_exit(10, Client), ?assertEqual(false,erlang:is_process_alive(Client)). wait_for_exit(0, _) -> ok; wait_for_exit(N, Proc) -> timer:sleep(50), case is_process_alive(Proc) of false -> ok; true -> wait_for_exit(N - 1, Proc) end. url(Port) -> url(inet, Port). url(inet, Port) -> "http://localhost:" ++ integer_to_list(Port); url(inet6, Port) -> "http://[::1]:" ++ integer_to_list(Port). status({Status, _, _, _, _}) -> Status. body({_, _, Body, _, _}) -> Body. headers({_, Headers, _, _, _}) -> Headers.