diff options
Diffstat (limited to 'lib/inets/test/httpc_SUITE.erl')
-rw-r--r-- | lib/inets/test/httpc_SUITE.erl | 179 |
1 files changed, 160 insertions, 19 deletions
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 4b1c6931d2..93b96e101f 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -3,16 +3,17 @@ %% %% Copyright Ericsson AB 2004-2015. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% @@ -28,6 +29,7 @@ -include_lib("common_test/include/ct.hrl"). -include("inets_test_lib.hrl"). -include("http_internal.hrl"). +-include("httpc_internal.hrl"). %% Note: This directive should only be used in test suites. -compile(export_all). @@ -66,6 +68,7 @@ real_requests()-> get, post, post_stream, + patch, async, pipeline, persistent_connection, @@ -96,16 +99,21 @@ only_simulated() -> stream_once, stream_single_chunk, stream_no_length, + not_streamed_once, + stream_large_not_200_or_206, no_content_204, tolerate_missing_CR, userinfo, bad_response, internal_server_error, invalid_http, + invalid_chunk_size, headers_dummy, + headers_with_obs_fold, empty_response_header, remote_socket_close, remote_socket_close_async, + process_leak_on_keepalive, transfer_encoding, transfer_encoding_identity, redirect_loop, @@ -250,6 +258,28 @@ post(Config) when is_list(Config) -> "text/plain", "foobar"}, [], []). %%-------------------------------------------------------------------- +patch() -> + [{"Test http patch request against local server. We do in this case " + "only care about the client side of the the patch. The server " + "script will not actually use the patch data."}]. +patch(Config) when is_list(Config) -> + CGI = case test_server:os_type() of + {win32, _} -> + "/cgi-bin/cgi_echo.exe"; + _ -> + "/cgi-bin/cgi_echo" + end, + + URL = url(group_name(Config), CGI, Config), + + %% Cgi-script expects the body length to be 100 + Body = lists:duplicate(100, "1"), + + {ok, {{_,200,_}, [_ | _], [_ | _]}} = + httpc:request(patch, {URL, [{"expect","100-continue"}], + "text/plain", Body}, [], []). + +%%-------------------------------------------------------------------- post_stream() -> [{"Test streaming http post request against local server. " "We only care about the client side of the the post. " @@ -404,6 +434,21 @@ stream_no_length(Config) when is_list(Config) -> stream_test(Request1, {stream, self}), Request2 = {url(group_name(Config), "/http_1_0_no_length_multiple.html", Config), []}, stream_test(Request2, {stream, self}). +%%------------------------------------------------------------------------- +stream_large_not_200_or_206() -> + [{doc, "Test the option stream for large responses with status codes " + "other than 200 or 206" }]. +stream_large_not_200_or_206(Config) when is_list(Config) -> + Request = {url(group_name(Config), "/large_404_response.html", Config), []}, + {404, _} = not_streamed_test(Request, {stream, self}). +%%------------------------------------------------------------------------- +not_streamed_once() -> + [{doc, "Test not streamed responses with once streaming"}]. +not_streamed_once(Config) when is_list(Config) -> + Request0 = {url(group_name(Config), "/404.html", Config), []}, + {404, _} = not_streamed_test(Request0, {stream, {self, once}}), + Request1 = {url(group_name(Config), "/404_chunked.html", Config), []}, + {404, _} = not_streamed_test(Request1, {stream, {self, once}}). %%------------------------------------------------------------------------- @@ -744,6 +789,22 @@ invalid_http(Config) when is_list(Config) -> ct:print("Parse error: ~p ~n", [Reason]). %%------------------------------------------------------------------------- + +invalid_chunk_size(doc) -> + ["Test parse error of HTTP chunk size"]; +invalid_chunk_size(suite) -> + []; +invalid_chunk_size(Config) when is_list(Config) -> + + URL = url(group_name(Config), "/invalid_chunk_size.html", Config), + + {error, {chunk_size, _} = Reason} = + httpc:request(get, {URL, []}, [], []), + + ct:print("Parse error: ~p ~n", [Reason]). + +%%------------------------------------------------------------------------- + emulate_lower_versions(doc) -> [{doc, "Perform request as 0.9 and 1.0 clients."}]; emulate_lower_versions(Config) when is_list(Config) -> @@ -890,6 +951,13 @@ headers_dummy(Config) when is_list(Config) -> %%------------------------------------------------------------------------- +headers_with_obs_fold(Config) when is_list(Config) -> + Request = {url(group_name(Config), "/obs_folded_headers.html", Config), []}, + {ok, {{_,200,_}, Headers, [_|_]}} = httpc:request(get, Request, [], []), + "a b" = proplists:get_value("folded", Headers). + +%%------------------------------------------------------------------------- + invalid_headers(Config) -> Request = {url(group_name(Config), "/dummy.html", Config), [{"cookie", undefined}]}, {error, _} = httpc:request(get, Request, [], []). @@ -913,6 +981,33 @@ remote_socket_close_async(Config) when is_list(Config) -> %%------------------------------------------------------------------------- +process_leak_on_keepalive(Config) -> + {ok, ClosedSocket} = gen_tcp:listen(6666, [{active, false}]), + ok = gen_tcp:close(ClosedSocket), + Request = {url(group_name(Config), "/dummy.html", Config), []}, + HttpcHandlers0 = supervisor:which_children(httpc_handler_sup), + {ok, {{_, 200, _}, _, Body}} = httpc:request(get, Request, [], []), + HttpcHandlers1 = supervisor:which_children(httpc_handler_sup), + ChildrenCount = supervisor:count_children(httpc_handler_sup), + %% Assuming that the new handler will be selected for keep_alive + %% which could not be the case if other handlers existed + [{undefined, Pid, worker, [httpc_handler]}] = + ordsets:to_list( + ordsets:subtract(ordsets:from_list(HttpcHandlers1), + ordsets:from_list(HttpcHandlers0))), + sys:replace_state( + Pid, fun (State) -> + Session = element(3, State), + setelement(3, State, Session#session{socket=ClosedSocket}) + end), + {ok, {{_, 200, _}, _, Body}} = httpc:request(get, Request, [], []), + %% bad handler with the closed socket should get replaced by + %% the new one, so children count should stay the same + ChildrenCount = supervisor:count_children(httpc_handler_sup), + ok. + +%%------------------------------------------------------------------------- + stream_to_pid(Config) when is_list(Config) -> ReceiverPid = create_receiver(pid), Receiver = ReceiverPid, @@ -1079,6 +1174,19 @@ stream_test(Request, To) -> Body = binary_to_list(StreamedBody). +not_streamed_test(Request, To) -> + {ok, {{_,Code,_}, [_ | _], Body}} = + httpc:request(get, Request, [], [{body_format, binary}]), + {ok, RequestId} = + httpc:request(get, Request, [], [{body_format, binary}, {sync, false}, To]), + + receive + {http, {RequestId, {{_, Code, _}, _Headers, Body}}} -> + {Code, binary_to_list(Body)}; + {http, Msg} -> + ct:fail(Msg) + end. + url(http, End, Config) -> Port = ?config(port, Config), {ok,Host} = inet:gethostname(), @@ -1610,6 +1718,11 @@ handle_uri(_,"/307.html",Port,_,Socket,_) -> "Content-Length:" ++ integer_to_list(length(Body)) ++ "\r\n\r\n" ++ Body; +handle_uri(_,"/404.html",_,_,_,_) -> + "HTTP/1.1 404 not found\r\n" ++ + "Content-Length:14\r\n\r\n" ++ + "Page not found"; + handle_uri(_,"/500.html",_,_,_,_) -> "HTTP/1.1 500 Internal Server Error\r\n" ++ "Content-Length:47\r\n\r\n" ++ @@ -1683,6 +1796,13 @@ handle_uri(_,"/dummy_headers.html",_,_,Socket,_) -> send(Socket, http_chunk:encode("obar</BODY></HTML>")), http_chunk:encode_last(); +handle_uri(_,"/obs_folded_headers.html",_,_,_,_) -> + "HTTP/1.1 200 ok\r\n" + "Content-Length:5\r\n" + "Folded: a\r\n" + " b\r\n\r\n" + "Hello"; + handle_uri(_,"/capital_transfer_encoding.html",_,_,Socket,_) -> Head = "HTTP/1.1 200 ok\r\n" ++ "Transfer-Encoding:Chunked\r\n\r\n", @@ -1738,6 +1858,15 @@ handle_uri(_,"/once_chunked.html",_,_,Socket,_) -> http_chunk:encode("obar</BODY></HTML>")), http_chunk:encode_last(); +handle_uri(_,"/404_chunked.html",_,_,Socket,_) -> + Head = "HTTP/1.1 404 not found\r\n" ++ + "Transfer-Encoding:Chunked\r\n\r\n", + send(Socket, Head), + send(Socket, http_chunk:encode("<HTML><BODY>Not ")), + send(Socket, + http_chunk:encode("found</BODY></HTML>")), + http_chunk:encode_last(); + handle_uri(_,"/single_chunk.html",_,_,Socket,_) -> Chunk = "HTTP/1.1 200 ok\r\n" ++ "Transfer-Encoding:Chunked\r\n\r\n" ++ @@ -1762,6 +1891,17 @@ handle_uri(_,"/http_1_0_no_length_multiple.html",_,_,Socket,_) -> send(Socket, string:copies("other multiple packets ", 200)), close(Socket); +handle_uri(_,"/large_404_response.html",_,_,Socket,_) -> + %% long body to make sure it will be sent in multiple tcp packets + Body = string:copies("other multiple packets ", 200), + Head = io_lib:format("HTTP/1.1 404 not found\r\n" + "Content-length: ~B\r\n" + "Content-type: text/plain\r\n\r\n", + [length(Body)]), + send(Socket, Head), + send(Socket, Body), + close(Socket); + handle_uri(_,"/once.html",_,_,Socket,_) -> Head = "HTTP/1.1 200 ok\r\n" ++ "Content-Length:32\r\n\r\n", @@ -1776,6 +1916,10 @@ handle_uri(_,"/invalid_http.html",_,_,_,_) -> "HTTP/1.1 301\r\nDate:Sun, 09 Dec 2007 13:04:18 GMT\r\n" ++ "Transfer-Encoding:chunked\r\n\r\n"; +handle_uri(_,"/invalid_chunk_size.html",_,_,_,_) -> + "HTTP/1.1 200 ok\r\n" ++ + "Transfer-Encoding:chunked\r\n\r\nåäö\r\n"; + handle_uri(_,"/missing_reason_phrase.html",_,_,_,_) -> "HTTP/1.1 200\r\n" ++ "Content-Length: 32\r\n\r\n" @@ -1909,12 +2053,13 @@ run_clients(NumClients, ServerPort, SeqNumServer) -> wait4clients([], _Timeout) -> ok; wait4clients(Clients, Timeout) when Timeout > 0 -> - Time = now_ms(), + Time = inets_time_compat:monotonic_time(), + receive {'DOWN', _MRef, process, Pid, normal} -> {value, {Id, _, _}} = lists:keysearch(Pid, 2, Clients), NewClients = lists:keydelete(Id, 1, Clients), - wait4clients(NewClients, Timeout - (now_ms() - Time)); + wait4clients(NewClients, Timeout - inets_lib:millisec_passed(Time)); {'DOWN', _MRef, process, Pid, Reason} -> {value, {Id, _, _}} = lists:keysearch(Pid, 2, Clients), ct:fail({bad_client_termination, Id, Reason}) @@ -2007,14 +2152,10 @@ parse_connection_type(Request) -> "keep-alive" -> keep_alive end. -%% Time in milli seconds -now_ms() -> - {A,B,C} = erlang:now(), - A*1000000000+B*1000+(C div 1000). - set_random_seed() -> - {_, _, Micros} = now(), - A = erlang:phash2([make_ref(), self(), Micros]), + Unique = inets_time_compat:unique_integer(), + + A = erlang:phash2([make_ref(), self(), Unique]), random:seed(A, A, A). |