diff options
author | Micael Karlberg <[email protected]> | 2011-05-16 09:46:10 +0200 |
---|---|---|
committer | Micael Karlberg <[email protected]> | 2011-05-16 09:46:10 +0200 |
commit | b19a2c6648e65b5ad1b8a0351928856fad941f99 (patch) | |
tree | 60c75bda36560b1e4a3422c349f3f95bfe337a6e /lib/inets/test/httpc_SUITE.erl | |
parent | 37210d94cf9f1cfa20654103d76039a2d453bc7c (diff) | |
parent | 8c9edd9c00142d0622beb74ef852c79871a631a6 (diff) | |
download | otp-b19a2c6648e65b5ad1b8a0351928856fad941f99.tar.gz otp-b19a2c6648e65b5ad1b8a0351928856fad941f99.tar.bz2 otp-b19a2c6648e65b5ad1b8a0351928856fad941f99.zip |
OTP-9094: [httpc] Add support for upload body streaming (PUT and POST).
Filipe David Manana
OTP-9114: [ftp] Added (type) spec for all exported functions.
OTP-9123: mod_esi:deliver/2 made to accept binary data.
Bernard Duggan
OTP-9124: [httpd] Prevent XSS in error pages.
Michael Santos
OTP-9131: [httpd] Wrong security property names used in documentation.
Garrett Smith
OTP-9157: [httpd] Improved error messages.
Ricardo Catalinas Jim�nez
OTP-9158: [httpd] Fix timeout message generated by mod_esi.
Bernard Duggan
OTP-9202: [httpd] Extended support for file descriptors.
Attila Rajmund Nohl
OTP-9230: The default ssl kind has now been changed to essl.
OTP-9246: [httpc] httpc manager crash because of a handler retry
race condition.
Merge branch 'bmk/inets/inet56_integration' into dev
Diffstat (limited to 'lib/inets/test/httpc_SUITE.erl')
-rw-r--r-- | lib/inets/test/httpc_SUITE.erl | 418 |
1 files changed, 304 insertions, 114 deletions
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 2c8febf5ed..1998bd3950 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -28,6 +28,7 @@ -include("test_server_line.hrl"). -include_lib("kernel/include/file.hrl"). +-include("inets_test_lib.hrl"). %% Note: This directive should only be used in test suites. -compile(export_all). @@ -62,36 +63,84 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [proxy_options, proxy_head, proxy_get, proxy_trace, - proxy_post, proxy_put, proxy_delete, proxy_auth, - proxy_headers, proxy_emulate_lower_versions, - http_options, http_head, http_get, http_post, - http_dummy_pipe, http_inets_pipe, http_trace, - http_async, http_save_to_file, http_save_to_file_async, - http_headers, http_headers_dummy, http_bad_response, - ssl_head, ossl_head, essl_head, ssl_get, ossl_get, - essl_get, ssl_trace, ossl_trace, essl_trace, - http_redirect, http_redirect_loop, - http_internal_server_error, http_userinfo, http_cookie, - http_server_does_not_exist, http_invalid_http, - http_emulate_lower_versions, http_relaxed, - page_does_not_exist, proxy_page_does_not_exist, - proxy_https_not_supported, http_stream, - http_stream_once, proxy_stream, parse_url, options, - ipv6, headers_as_is, {group, tickets}]. + [ + proxy_options, + proxy_head, + proxy_get, + proxy_trace, + proxy_post, + proxy_put, + proxy_delete, + proxy_auth, + proxy_headers, + proxy_emulate_lower_versions, + http_options, + http_head, + http_get, + http_post, + http_post_streaming, + http_dummy_pipe, + http_inets_pipe, + http_trace, + http_async, + http_save_to_file, + http_save_to_file_async, + http_headers, + http_headers_dummy, + http_bad_response, + ssl_head, + ossl_head, + essl_head, + ssl_get, + ossl_get, + essl_get, + ssl_trace, + ossl_trace, + essl_trace, + http_redirect, + http_redirect_loop, + http_internal_server_error, + http_userinfo, http_cookie, + http_server_does_not_exist, + http_invalid_http, + http_emulate_lower_versions, + http_relaxed, + page_does_not_exist, + proxy_page_does_not_exist, + proxy_https_not_supported, + http_stream, + http_stream_once, + proxy_stream, + parse_url, + options, + ipv6, + headers_as_is, + {group, tickets}, + initial_server_connect + ]. groups() -> - [{tickets, [], - [hexed_query_otp_6191, empty_body_otp_6243, - empty_response_header_otp_6830, - transfer_encoding_otp_6807, proxy_not_modified_otp_6821, - no_content_204_otp_6982, missing_CR_otp_7304, - {group, otp_7883}, {group, otp_8154}, {group, otp_8106}, - otp_8056, otp_8352, otp_8371, otp_8739]}, - {otp_7883, [], [otp_7883_1, otp_7883_2]}, + [{tickets, [], [hexed_query_otp_6191, + empty_body_otp_6243, + empty_response_header_otp_6830, + transfer_encoding_otp_6807, + proxy_not_modified_otp_6821, + no_content_204_otp_6982, + missing_CR_otp_7304, + {group, otp_7883}, + {group, otp_8154}, + {group, otp_8106}, + otp_8056, + otp_8352, + otp_8371, + otp_8739]}, + {otp_7883, [], [otp_7883_1, + otp_7883_2]}, {otp_8154, [], [otp_8154_1]}, - {otp_8106, [], - [otp_8106_pid, otp_8106_fun, otp_8106_mfa]}]. + {otp_8106, [], [otp_8106_pid, + otp_8106_fun, + otp_8106_mfa]}]. + init_per_group(_GroupName, Config) -> Config. @@ -138,6 +187,7 @@ init_per_suite(Config) -> {local_port, ?IP_PORT}, {local_ssl_port, ?SSL_PORT} | Config]. + %%-------------------------------------------------------------------- %% Function: end_per_suite(Config) -> _ %% Config - [tuple()] @@ -165,6 +215,20 @@ end_per_suite(Config) -> %%-------------------------------------------------------------------- init_per_testcase(otp_8154_1 = Case, Config) -> init_per_testcase(Case, 5, Config); + +init_per_testcase(initial_server_connect, Config) -> + %% Try to check if crypto actually exist or not, + %% this test case does not work unless it does + case (catch crypto:start()) of + ok -> + application:start(public_key), + application:start(ssl), + inets:start(), + Config; + _ -> + {skip,"Could not start crypto"} + end; + init_per_testcase(Case, Config) -> init_per_testcase(Case, 2, Config). @@ -180,8 +244,8 @@ init_per_testcase_ssl(Tag, PrivDir, SslConfFile, Config) -> [{local_ssl_server, Server} | Config2]. init_per_testcase(Case, Timeout, Config) -> - io:format(user, "~n~n*** INIT ~w:[~w][~w] ***~n~n", - [?MODULE, Timeout, Case]), + io:format(user, "~n~n*** INIT ~w:~w[~w] ***~n~n", + [?MODULE, Case, Timeout]), PrivDir = ?config(priv_dir, Config), tsp("init_per_testcase -> stop inets"), application:stop(inets), @@ -205,9 +269,10 @@ init_per_testcase(Case, Timeout, Config) -> [$e, $s, $s, $l | _] -> init_per_testcase_ssl(essl, PrivDir, SslConfFile, [{watchdog, Dog} | TmpConfig]); - "proxy" ++ Rest -> + "proxy_" ++ Rest -> + io:format("init_per_testcase -> Rest: ~p~n", [Rest]), case Rest of - "_https_not_supported" -> + "https_not_supported" -> tsp("init_per_testcase -> [proxy case] start inets"), inets:start(), tsp("init_per_testcase -> [proxy case] start ssl"), @@ -221,13 +286,39 @@ init_per_testcase(Case, Timeout, Config) -> | TmpConfig] end; _ -> + %% We use erlang.org for the proxy tests + %% and after the switch to erlang-web, many + %% of the test cases no longer work (erlang.org + %% previously run on Apache). + %% Until we have had time to update inets + %% (and updated erlang.org to use that inets) + %% and the test cases, we simply skip the + %% problematic test cases. + %% This is not ideal, but I am busy.... case is_proxy_available(?PROXY, ?PROXY_PORT) of true -> - inets:start(), - [{watchdog, Dog} | TmpConfig]; + BadCases = + [ + "delete", + "get", + "head", + "not_modified_otp_6821", + "options", + "page_does_not_exist", + "post", + "put", + "stream" + ], + case lists:member(Rest, BadCases) of + true -> + [{skip, "TC and server not compatible"}| + TmpConfig]; + false -> + inets:start(), + [{watchdog, Dog} | TmpConfig] + end; false -> - [{skip, "Failed to contact proxy"} | - TmpConfig] + [{skip, "proxy not responding"} | TmpConfig] end end; _ -> @@ -395,6 +486,53 @@ http_post(Config) when is_list(Config) -> end. %%------------------------------------------------------------------------- +http_post_streaming(doc) -> + ["Test streaming http post request against local server. " + "We only care about the client side of the the post. " + "The server script will not actually use the post data."]; +http_post_streaming(suite) -> + []; +http_post_streaming(Config) when is_list(Config) -> + case ?config(local_server, Config) of + ok -> + Port = ?config(local_port, Config), + URL = case test_server:os_type() of + {win32, _} -> + ?URL_START ++ integer_to_list(Port) ++ + "/cgi-bin/cgi_echo.exe"; + _ -> + ?URL_START ++ integer_to_list(Port) ++ + "/cgi-bin/cgi_echo" + end, + %% Cgi-script expects the body length to be 100 + BodyFun = fun(0) -> + io:format("~w:http_post_streaming_fun -> " + "zero~n", [?MODULE]), + eof; + (LenLeft) -> + io:format("~w:http_post_streaming_fun -> " + "LenLeft: ~p~n", [?MODULE, LenLeft]), + {ok, lists:duplicate(10, "1"), LenLeft - 10} + end, + + {ok, {{_,200,_}, [_ | _], [_ | _]}} = + httpc:request(post, {URL, + [{"expect", "100-continue"}, + {"content-length", "100"}], + "text/plain", {BodyFun, 100}}, [], []), + + {ok, {{_,504,_}, [_ | _], []}} = + httpc:request(post, {URL, + [{"expect", "100-continue"}, + {"content-length", "10"}], + "text/plain", {BodyFun, 10}}, [], []); + + _ -> + {skip, "Failed to start local http-server"} + end. + + +%%------------------------------------------------------------------------- http_emulate_lower_versions(doc) -> ["Perform request as 0.9 and 1.0 clients."]; http_emulate_lower_versions(suite) -> @@ -478,34 +616,35 @@ http_inets_pipe(Config) when is_list(Config) -> {skip, "Failed to start local http-server"} end. + test_pipeline(URL) -> - p("test_pipeline -> entry with" - "~n URL: ~p", [URL]), + p("test_pipeline -> entry with" + "~n URL: ~p", [URL]), - httpc:set_options([{pipeline_timeout, 50000}]), - - p("test_pipeline -> issue (async) request 1"), - {ok, RequestId1} = + httpc:set_options([{pipeline_timeout, 50000}]), + + p("test_pipeline -> issue (async) request 1"), + {ok, RequestId1} = httpc:request(get, {URL, []}, [], [{sync, false}]), - test_server:format("RequestId1: ~p~n", [RequestId1]), - p("test_pipeline -> RequestId1: ~p", [RequestId1]), + test_server:format("RequestId1: ~p~n", [RequestId1]), + p("test_pipeline -> RequestId1: ~p", [RequestId1]), - %% Make sure pipeline is initiated - p("test_pipeline -> sleep some", []), - test_server:sleep(4000), + %% Make sure pipeline is initiated + p("test_pipeline -> sleep some", []), + test_server:sleep(4000), - p("test_pipeline -> issue (async) request 2"), - {ok, RequestId2} = + p("test_pipeline -> issue (async) request 2"), + {ok, RequestId2} = httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId2: ~p", [RequestId2]), - p("test_pipeline -> RequestId2: ~p", [RequestId2]), + tsp("RequestId2: ~p", [RequestId2]), + p("test_pipeline -> RequestId2: ~p", [RequestId2]), - p("test_pipeline -> issue (sync) request 3"), - {ok, {{_,200,_}, [_ | _], [_ | _]}} = + p("test_pipeline -> issue (sync) request 3"), + {ok, {{_,200,_}, [_ | _], [_ | _]}} = httpc:request(get, {URL, []}, [], []), p("test_pipeline -> expect reply for (async) request 1 or 2"), - receive + receive {http, {RequestId1, {{_, 200, _}, _, _}}} -> p("test_pipeline -> received reply for (async) request 1 - now wait for 2"), receive @@ -523,46 +662,46 @@ test_pipeline(URL) -> ok; {http, Msg2} -> test_server:fail(Msg2) - end; + end; {http, Msg3} -> test_server:fail(Msg3) - after 60000 -> - receive Any1 -> - tsp("received crap after timeout: ~n ~p", [Any1]), - test_server:fail({error, {timeout, Any1}}) - end + after 60000 -> + receive Any1 -> + tsp("received crap after timeout: ~n ~p", [Any1]), + test_server:fail({error, {timeout, Any1}}) + end end, - - p("test_pipeline -> sleep some"), - test_server:sleep(4000), - p("test_pipeline -> issue (async) request 4"), - {ok, RequestId3} = - httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId3: ~p", [RequestId3]), - p("test_pipeline -> RequestId3: ~p", [RequestId3]), + p("test_pipeline -> sleep some"), + test_server:sleep(4000), - p("test_pipeline -> issue (async) request 5"), - {ok, RequestId4} = + p("test_pipeline -> issue (async) request 4"), + {ok, RequestId3} = httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId4: ~p~n", [RequestId4]), - p("test_pipeline -> RequestId4: ~p", [RequestId4]), - - p("test_pipeline -> cancel (async) request 4"), - ok = httpc:cancel_request(RequestId3), - - p("test_pipeline -> expect *no* reply for cancelled (async) request 4 (for 3 secs)"), - receive - {http, {RequestId3, _}} -> - test_server:fail(http_cancel_request_failed) - after 3000 -> - ok - end, + tsp("RequestId3: ~p", [RequestId3]), + p("test_pipeline -> RequestId3: ~p", [RequestId3]), - p("test_pipeline -> expect reply for (async) request 4"), - Body = - receive - {http, {RequestId4, {{_, 200, _}, _, BinBody4}}} = Res -> + p("test_pipeline -> issue (async) request 5"), + {ok, RequestId4} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + tsp("RequestId4: ~p~n", [RequestId4]), + p("test_pipeline -> RequestId4: ~p", [RequestId4]), + + p("test_pipeline -> cancel (async) request 4"), + ok = httpc:cancel_request(RequestId3), + + p("test_pipeline -> expect *no* reply for cancelled (async) request 4 (for 3 secs)"), + receive + {http, {RequestId3, _}} -> + test_server:fail(http_cancel_request_failed) + after 3000 -> + ok + end, + + p("test_pipeline -> expect reply for (async) request 4"), + Body = + receive + {http, {RequestId4, {{_, 200, _}, _, BinBody4}}} = Res -> p("test_pipeline -> received reply for (async) request 5"), tsp("Receive : ~p", [Res]), BinBody4; @@ -577,9 +716,9 @@ test_pipeline(URL) -> p("test_pipeline -> check reply for (async) request 5"), inets_test_lib:check_body(binary_to_list(Body)), - + p("test_pipeline -> ensure no unexpected incomming"), - receive + receive {http, Any} -> test_server:fail({unexpected_message, Any}) after 500 -> @@ -589,8 +728,6 @@ test_pipeline(URL) -> p("test_pipeline -> done"), ok. - - %%------------------------------------------------------------------------- http_trace(doc) -> ["Perform a TRACE request that goes through a proxy."]; @@ -1253,6 +1390,9 @@ proxy_options(doc) -> proxy_options(suite) -> []; proxy_options(Config) when is_list(Config) -> + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets, which + %% do not implement "options". case ?config(skip, Config) of undefined -> case httpc:request(options, {?PROXY_URL, []}, [], []) of @@ -1277,6 +1417,8 @@ proxy_head(doc) -> proxy_head(suite) -> []; proxy_head(Config) when is_list(Config) -> + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets. case ?config(skip, Config) of undefined -> case httpc:request(head, {?PROXY_URL, []}, [], []) of @@ -1372,6 +1514,8 @@ proxy_post(doc) -> proxy_post(suite) -> []; proxy_post(Config) when is_list(Config) -> + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets. case ?config(skip, Config) of undefined -> case httpc:request(post, {?PROXY_URL, [], @@ -1394,6 +1538,8 @@ proxy_put(doc) -> proxy_put(suite) -> []; proxy_put(Config) when is_list(Config) -> + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets. case ?config(skip, Config) of undefined -> case httpc:request(put, {"http://www.erlang.org/foobar.html", [], @@ -1418,6 +1564,8 @@ proxy_delete(doc) -> proxy_delete(suite) -> []; proxy_delete(Config) when is_list(Config) -> + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets. case ?config(skip, Config) of undefined -> URL = ?PROXY_URL ++ "/foobar.html", @@ -1541,25 +1689,11 @@ proxy_https_not_supported(suite) -> proxy_https_not_supported(Config) when is_list(Config) -> Result = httpc:request(get, {"https://login.yahoo.com", []}, [], []), case Result of - {error, Reason} -> - %% ok so far - case Reason of - {failed_connecting, Why} -> - %% ok, now check why - case Why of - https_through_proxy_is_not_currently_supported -> - ok; - _ -> - tsf({unexpected_why, Why}) - end; - _ -> - tsf({unexpected_reason, Reason}) - end; + {error, https_through_proxy_is_not_currently_supported} -> + ok; _ -> - tsf({unexpected_result, Result}) - end, - ok. - + tsf({unexpected_reason, Result}) + end. %%------------------------------------------------------------------------- @@ -2312,7 +2446,7 @@ otp_8106_fun(Config) when is_list(Config) -> ok; _ -> {skip, "Failed to start local http-server"} - end. + end. otp_8106_mfa(doc) -> @@ -2538,7 +2672,7 @@ otp_8739(Config) when is_list(Config) -> Request = {URL, []}, HttpOptions = [{connect_timeout, 500}, {timeout, 1}], Options = [{sync, true}], - case http:request(Method, Request, HttpOptions, Options) of + case httpc:request(Method, Request, HttpOptions, Options) of {error, timeout} -> %% And now we check the size of the handler db Info = httpc:info(), @@ -2573,7 +2707,7 @@ otp_8739_dummy_server_init(Parent) -> Parent ! {port, Port}, otp_8739_dummy_server_main(Parent, ListenSocket). -otp_8739_dummy_server_main(Parent, ListenSocket) -> +otp_8739_dummy_server_main(_Parent, ListenSocket) -> case gen_tcp:accept(ListenSocket) of {ok, Sock} -> %% Ignore the request, and simply wait for the socket to close @@ -2595,7 +2729,31 @@ otp_8739_dummy_server_main(Parent, ListenSocket) -> exit(Error) end. - +%%------------------------------------------------------------------------- + +initial_server_connect(doc) -> + ["If this test cases times out the init of httpc_handler process is" + "blocking the manager/client process (implementation dependent which) but nither" + "should be blocked."]; +initial_server_connect(suite) -> + []; +initial_server_connect(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + ok = httpc:set_options([{ipfamily, inet}]), + + CertFile = filename:join(DataDir, "ssl_server_cert.pem"), + SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}], + + {DummyServerPid, Port} = dummy_ssl_server_hang(self(), ipv4, SSLOptions), + + URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/index.html", + + httpc:request(get, {URL, []}, [{ssl,{essl,[]}}], [{sync, false}]), + + [{session_cookies,[]}] = httpc:which_cookies(), + + DummyServerPid ! stop, + ok = httpc:set_options([{ipfamily, inet6fb4}]). %%-------------------------------------------------------------------- %% Internal functions @@ -3108,11 +3266,9 @@ pick_header(Headers, Name) -> Val end. - not_implemented_yet() -> exit(not_implemented_yet). - p(F) -> p(F, []). @@ -3126,3 +3282,37 @@ tsp(F, A) -> tsf(Reason) -> test_server:fail(Reason). + + +dummy_ssl_server_hang(Caller, IpV, SslOpt) -> + Pid = spawn(httpc_SUITE, dummy_ssl_server_hang_init, [Caller, IpV, SslOpt]), + receive + {port, Port} -> + {Pid, Port} + end. + +dummy_ssl_server_hang_init(Caller, IpV, SslOpt) -> + {ok, ListenSocket} = + case IpV of + ipv4 -> + ssl:listen(0, [binary, inet, {packet, 0}, + {reuseaddr,true}, + {active, false}] ++ SslOpt); + ipv6 -> + ssl:listen(0, [binary, inet6, {packet, 0}, + {reuseaddr,true}, + {active, false}] ++ SslOpt) + end, + {ok, {_,Port}} = ssl:sockname(ListenSocket), + tsp("dummy_ssl_server_hang_init -> Port: ~p", [Port]), + Caller ! {port, Port}, + {ok, AcceptSocket} = ssl:transport_accept(ListenSocket), + dummy_ssl_server_hang_loop(AcceptSocket). + +dummy_ssl_server_hang_loop(_) -> + %% Do not do ssl:ssl_accept as we + %% want to time out the underlying gen_tcp:connect + receive + stop -> + ok + end. |