202 lines
7.9 KiB
Erlang
202 lines
7.9 KiB
Erlang
|
%%% ----------------------------------------------------------------------------
|
||
|
%%% 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 <oscar@hellstrom.st>
|
||
|
-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.
|