diff options
author | Sverker Eriksson <[email protected]> | 2016-01-28 16:27:34 +0100 |
---|---|---|
committer | Sverker Eriksson <[email protected]> | 2016-01-28 16:27:34 +0100 |
commit | 8be1fad4075eba013a811c6879e3a906f365d3d4 (patch) | |
tree | 300e8a413408f9cea0acfe41e76a4c66ef2d18fb /lib/inets/test | |
parent | 042677624b1d7b3f4c99be4e1483180e7fe8b2c0 (diff) | |
parent | 632eb0ee2fbad692255ca2b1d0c3300fd0b6e492 (diff) | |
download | otp-8be1fad4075eba013a811c6879e3a906f365d3d4.tar.gz otp-8be1fad4075eba013a811c6879e3a906f365d3d4.tar.bz2 otp-8be1fad4075eba013a811c6879e3a906f365d3d4.zip |
Merge branch 'master' into sverk/hipe-line-table-bug/master/OTP-13282
Diffstat (limited to 'lib/inets/test')
-rw-r--r-- | lib/inets/test/Makefile | 7 | ||||
-rw-r--r-- | lib/inets/test/http_format_SUITE.erl | 238 | ||||
-rw-r--r-- | lib/inets/test/httpc_SUITE.erl | 91 | ||||
-rw-r--r-- | lib/inets/test/httpc_proxy_SUITE.erl | 20 | ||||
-rwxr-xr-x | lib/inets/test/httpc_proxy_SUITE_data/server_proxy.sh | 2 | ||||
-rw-r--r-- | lib/inets/test/httpd_1_1.erl | 11 | ||||
-rw-r--r-- | lib/inets/test/httpd_SUITE.erl | 71 | ||||
-rw-r--r-- | lib/inets/test/httpd_SUITE_data/Makefile.src | 5 | ||||
-rw-r--r-- | lib/inets/test/httpd_test_lib.erl | 20 | ||||
-rw-r--r-- | lib/inets/test/inets_SUITE.erl | 167 | ||||
-rw-r--r-- | lib/inets/test/inets_socketwrap_SUITE.erl | 154 | ||||
-rw-r--r-- | lib/inets/test/inets_socketwrap_SUITE_data/Makefile.src | 39 | ||||
-rw-r--r-- | lib/inets/test/inets_socketwrap_SUITE_data/setuid_socket_wrap.c | 259 | ||||
-rw-r--r-- | lib/inets/test/inets_test_lib.erl | 9 | ||||
-rw-r--r-- | lib/inets/test/uri_SUITE.erl | 23 |
15 files changed, 827 insertions, 289 deletions
diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile index cae77a05f3..607ec7c182 100644 --- a/lib/inets/test/Makefile +++ b/lib/inets/test/Makefile @@ -174,7 +174,8 @@ MODULES = \ inets_appup_test \ tftp_test_lib \ tftp_SUITE \ - uri_SUITE + uri_SUITE \ + inets_socketwrap_SUITE EBIN = . @@ -203,7 +204,7 @@ INETS_FILES = inets.config $(INETS_SPECS) # inets_ftp_suite \ # inets_tftp_suite -INETS_DATADIRS = inets_SUITE_data inets_sup_SUITE_data +INETS_DATADIRS = inets_SUITE_data inets_socketwrap_SUITE_data HTTPD_DATADIRS = httpd_test_data httpd_SUITE_data httpd_basic_SUITE_data old_httpd_SUITE_data HTTPC_DATADIRS = httpc_SUITE_data httpc_proxy_SUITE_data FTP_DATADIRS = ftp_SUITE_data @@ -250,7 +251,7 @@ ERL_COMPILE_FLAGS += \ # 1) INETS_PRIV_DIR must be created # ---------------------------------------------------- -tests debug opt: $(BUILDTARGET) +tests debug opt: $(BUILDTARGET) targets: $(TARGET_FILES) diff --git a/lib/inets/test/http_format_SUITE.erl b/lib/inets/test/http_format_SUITE.erl index a97b51601f..e977bd1b9b 100644 --- a/lib/inets/test/http_format_SUITE.erl +++ b/lib/inets/test/http_format_SUITE.erl @@ -20,26 +20,12 @@ %% -module(http_format_SUITE). --author('[email protected]'). -include_lib("common_test/include/ct.hrl"). --include("test_server_line.hrl"). -include("http_internal.hrl"). -%% Test server specific exports --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2]). - -%% Test cases must be exported. --export([ chunk_decode/1, chunk_encode/1, - chunk_extensions_otp_6005/1, chunk_decode_otp_6264/1, - chunk_decode_empty_chunk_otp_6511/1, - chunk_decode_trailer/1, - http_response/1, http_request/1, validate_request_line/1, - esi_parse_headers/1, cgi_parse_headers/1, - is_absolut_uri/1, convert_netscapecookie_date/1, - check_content_length_encoding/1]). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +%% Note: This directive should only be used in test suites. +-compile(export_all). all() -> [{group, chunk}, http_response, http_request, @@ -52,7 +38,8 @@ groups() -> [chunk_decode, chunk_encode, chunk_extensions_otp_6005, chunk_decode_otp_6264, chunk_decode_empty_chunk_otp_6511, - chunk_decode_trailer]}]. + chunk_whitespace_suffix, + chunk_decode_trailer, chunk_max_headersize, chunk_max_bodysize, chunk_not_hex]}]. init_per_suite(Config) -> Config. @@ -81,12 +68,8 @@ end_per_testcase(_, Config) -> %% Test cases starts here. %%------------------------------------------------------------------------- - -%%------------------------------------------------------------------------- -chunk_decode(doc) -> - ["Test http_chunk:decode/3"]; -chunk_decode(suite) -> - []; +chunk_decode() -> + [{doc, "Test http_chunk:decode/3"}]. chunk_decode(Config) when is_list(Config) -> ReqHeaders = #http_request_h{'transfer-encoding' = "chunked"}, ChunkedBody = "A" ++ ?CRLF ++ "1234567890" ++ ?CRLF ++ "4" ++ @@ -109,15 +92,11 @@ chunk_decode(Config) when is_list(Config) -> ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), {_, Body} = parse(Module, Function, Args, tl(NewChunkedBody)), - "1234567890HEJ!" = binary_to_list(Body), - - ok. + "1234567890HEJ!" = binary_to_list(Body). %%------------------------------------------------------------------------- -chunk_extensions_otp_6005(doc) -> - ["Make sure so called extensions are ignored"]; -chunk_extensions_otp_6005(suite) -> - []; +chunk_extensions_otp_6005() -> + [{doc, "Make sure so called extensions are ignored"}]. chunk_extensions_otp_6005(Config) when is_list(Config)-> ChunkedBody = "A;ignore this" ++ ?CRLF ++ "1234567890" ++ ?CRLF ++ "4" ++ ?CRLF ++ "HEJ!"++ ?CRLF ++ "0" ++ @@ -136,14 +115,11 @@ chunk_extensions_otp_6005(Config) when is_list(Config)-> ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), {_, NewBody} = parse(Module1, Function1, Args1, tl(ChunkedBody1)), - "1234567890HEJ!" = binary_to_list(NewBody), - ok. + "1234567890HEJ!" = binary_to_list(NewBody). %%------------------------------------------------------------------------- -chunk_decode_otp_6264(doc) -> - ["Check that 0 in the body does not count as the last chunk"]; -chunk_decode_otp_6264(suite) -> - []; +chunk_decode_otp_6264() -> + [{doc, "Check that 0 in the body does not count as the last chunk"}]. chunk_decode_otp_6264(Config) when is_list(Config)-> ChunkedBody = "A;ignore this" ++ ?CRLF ++ "1234567890" ++ ?CRLF ++ "4" ++ ?CRLF ++ "0123"++ ?CRLF ++ "0" ++ @@ -173,27 +149,33 @@ chunk_decode_otp_6264(Config) when is_list(Config)-> ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), {_, NewBody} = parse(Module1, Function1, Args1, tl(NewChunkedBody1)), - "12345678900" = binary_to_list(NewBody), - - ok. + "12345678900" = binary_to_list(NewBody). %%------------------------------------------------------------------------- -chunk_decode_empty_chunk_otp_6511(doc) -> - [""]; -chunk_decode_empty_chunk_otp_6511(suite) -> - []; chunk_decode_empty_chunk_otp_6511(Config) when is_list(Config) -> ChunkedBody = "0" ++ ?CRLF ++ ?CRLF, {ok,{["content-length:0"],<<>>}} = http_chunk:decode(list_to_binary(ChunkedBody), - ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), - ok. + ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE). %%------------------------------------------------------------------------- -chunk_decode_trailer(doc) -> - ["Make sure trailers are handled correctly. Trailers should" - "become new headers"]; -chunk_decode_trailer(suite) -> - []; +chunk_whitespace_suffix() -> + [{doc, "Test whitespace after chunked length header"}]. +chunk_whitespace_suffix(Config) when is_list(Config) -> + ChunkedBody = "1a ; ignore-stuff-here" ++ ?CRLF ++ + "abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10 " ++ ?CRLF + ++ "1234567890abcdef" ++ ?CRLF ++ "0 " ++ ?CRLF + ++ "some-footer:some-value" ++ ?CRLF + ++ "another-footer:another-value" ++ ?CRLF ++ ?CRLF, + {ok, {["content-length:42", "another-footer:another-value", + "some-footer:some-value", ""], + <<"abcdefghijklmnopqrstuvwxyz1234567890abcdef">>}} = + http_chunk:decode(list_to_binary(ChunkedBody), + ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE). + +%%------------------------------------------------------------------------- +chunk_decode_trailer() -> + [{doc,"Make sure trailers are handled correctly. Trailers should" + "become new headers"}]. chunk_decode_trailer(Config) when is_list(Config)-> ChunkedBody = "1a; ignore-stuff-here" ++ ?CRLF ++ "abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10" ++ ?CRLF @@ -249,30 +231,79 @@ chunk_decode_trailer(Config) when is_list(Config)-> ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), {_, NewBody} = parse(Module1, Function1, Args1, tl(ChunkedBody3)), - "abcdefghijklmnopqrstuvwxyz1234567890abcdef" = binary_to_list(NewBody), - - ok. + "abcdefghijklmnopqrstuvwxyz1234567890abcdef" = binary_to_list(NewBody). %%------------------------------------------------------------------------- -chunk_encode(doc) -> - ["Test http_chunk:encode/1 & http_chunk:encode_last/0"]; -chunk_encode(suite) -> - []; +chunk_encode() -> + [{doc, "Test http_chunk:encode/1 & http_chunk:encode_last/0"}]. chunk_encode(Config) when is_list(Config) -> <<54, ?CR, ?LF, 102,111,111,98,97,114, ?CR, ?LF>> = http_chunk:encode(list_to_binary("foobar")), ["6", ?CR, ?LF,"foobar", ?CR, ?LF] = http_chunk:encode("foobar"), - <<$0, ?CR, ?LF, ?CR, ?LF >> = http_chunk:encode_last(), - ok. - + <<$0, ?CR, ?LF, ?CR, ?LF >> = http_chunk:encode_last(). +%%------------------------------------------------------------------------- +chunk_max_headersize() -> + [{doc, "Test max header limit"}]. +chunk_max_headersize(Config) when is_list(Config) -> + ChunkedBody = "1a; ignore-stuff-here" ++ ?CRLF ++ + "abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10" ++ ?CRLF + ++ "1234567890abcdef" ++ ?CRLF ++ "0" ++ ?CRLF + ++ "some-footer:some-value" ++ ?CRLF + ++ "another-footer:another-value" ++ ?CRLF ++ ?CRLF, + + {ok, {_, _}} = + http_chunk:decode(list_to_binary(ChunkedBody), + ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), + + %% Too long in length header + {error,{header_too_long, {max, 1}}} = + (catch http_chunk:decode(list_to_binary(ChunkedBody), + ?HTTP_MAX_BODY_SIZE, 1)), + + %% Too long in extension field + {error,{header_too_long, {max, 10}}} = + (catch http_chunk:decode(list_to_binary(ChunkedBody), + ?HTTP_MAX_BODY_SIZE, 10)), + + %% Too long in trailer + {error,{header_too_long, {max, 30}}} = + (catch http_chunk:decode(list_to_binary(ChunkedBody), + ?HTTP_MAX_BODY_SIZE, 30)). +%%------------------------------------------------------------------------- +chunk_not_hex() -> + [{doc, "Test bad chunked length header"}]. +chunk_not_hex(Config) when is_list(Config) -> + ChunkedBody = "åäö; ignore-stuff-here" ++ ?CRLF ++ + "abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10" ++ ?CRLF + ++ "1234567890abcdef" ++ ?CRLF ++ "0" ++ ?CRLF + ++ "some-footer:some-value" ++ ?CRLF + ++ "another-footer:another-value" ++ ?CRLF ++ ?CRLF, + {error,{chunk_size, "åäö"}} = + (catch http_chunk:decode(list_to_binary(ChunkedBody), + ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE)). +%%------------------------------------------------------------------------- +chunk_max_bodysize() -> + [{doc, "Test max body limit"}]. +chunk_max_bodysize(Config) when is_list(Config) -> + ChunkedBody = "1a; ignore-stuff-here" ++ ?CRLF ++ + "abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10" ++ ?CRLF + ++ "1234567890abcdef" ++ ?CRLF ++ "0" ++ ?CRLF + ++ "some-footer:some-value" ++ ?CRLF + ++ "another-footer:another-value" ++ ?CRLF ++ ?CRLF, + {ok, {_, _}} = + http_chunk:decode(list_to_binary(ChunkedBody), + ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), + + %% Too long body + {error,{body_too_big, {max, 10}}} = + (catch http_chunk:decode(list_to_binary(ChunkedBody), + 10, ?HTTP_MAX_HEADER_SIZE)). %%------------------------------------------------------------------------- -http_response(doc) -> - ["Test httpc_response:parse*. This test case will simulate that the " +http_response() -> + [{doc, "Test httpc_response:parse*. This test case will simulate that the " "message will be recived a little at the time on a socket and the " - "package may be broken up into smaller parts at arbitrary point."]; -http_response(suite) -> - []; + "package may be broken up into smaller parts at arbitrary point."}]. http_response(Config) when is_list(Config) -> HttpHead1 = ["HTTP", "/1.1 ", "20", "0 ", "ok", [?CR, ?LF], @@ -340,12 +371,10 @@ http_response(Config) when is_list(Config) -> [<<>>,Length1], HttpBody1)), ok. %%------------------------------------------------------------------------- -http_request(doc) -> - ["Test httpd_request:parse* This test case will simulate that the " +http_request() -> + [{doc, "Test httpd_request:parse* This test case will simulate that the " "message will be recived a little at the time on a socket and the " - "package may be broken up into smaller parts at arbitrary point."]; -http_request(suite) -> - []; + "package may be broken up into smaller parts at arbitrary point."}]. http_request(Config) when is_list(Config) -> HttpHead = ["GE", "T ", "http://www.erlang", ".org ", "HTTP", @@ -407,15 +436,12 @@ http_request(Config) when is_list(Config) -> NewBody1 = binary_to_list(parse (httpd_request, whole_body, - [<<>>, Length1], HttpBody1)), - ok. + [<<>>, Length1], HttpBody1)). %%------------------------------------------------------------------------- -validate_request_line(doc) -> - ["Test httpd_request:validate/3. Makes sure you can not get past" +validate_request_line() -> + [{doc, "Test httpd_request:validate/3. Makes sure you can not get past" " the server_root and that the request is recognized by the server" - " and protcol version." ]; -validate_request_line(suite) -> - []; + " and protcol version."}]. validate_request_line(Config) when is_list(Config) -> %% HTTP/0.9 only has GET requests @@ -468,16 +494,12 @@ validate_request_line(Config) when is_list(Config) -> NewForbiddenUri1 = "http://127.0.0.1:8888/../home/ingela/test.html", {error, {bad_request, {forbidden, NewForbiddenUri1}}} = - httpd_request:validate("GET", NewForbiddenUri1, "HTTP/1.1"), - - ok. + httpd_request:validate("GET", NewForbiddenUri1, "HTTP/1.1"). %%------------------------------------------------------------------------- -check_content_length_encoding(doc) -> - ["Test http_request:headers/2. Check that the content-length is" - " encoded even when it is zero." ]; -check_content_length_encoding(suite) -> - []; +check_content_length_encoding() -> + [{doc, "Test http_request:headers/2. Check that the content-length is" + " encoded even when it is zero."}]. check_content_length_encoding(Config) when is_list(Config) -> %% Check that the content-length is preserved. @@ -486,16 +508,12 @@ check_content_length_encoding(Config) when is_list(Config) -> true = (string:str(Header1, "content-length: 123\r\n") > 0), %% Check that content-length=0 is handled correctly. Header2 = http_request:http_headers(#http_request_h{'content-length'="0"}), - true = (string:str(Header2, "content-length: 0\r\n") > 0), - - ok. + true = (string:str(Header2, "content-length: 0\r\n") > 0). %%------------------------------------------------------------------------- -esi_parse_headers(doc) -> - ["Test httpd_esi:*. All header values are received in the same" - " erlang message."]; -esi_parse_headers(suite) -> - []; +esi_parse_headers() -> + [{doc, "Test httpd_esi:*. All header values are received in the same" + " erlang message."}]. esi_parse_headers(Config) when is_list(Config) -> ESIResult = "content-type:text/html\r\ndate:Thu, 28 Oct 2004 07:57:43 " @@ -522,16 +540,14 @@ esi_parse_headers(Config) when is_list(Config) -> httpd_esi:handle_headers(Headers2), {proceed,"/foo/bar.html"} = - httpd_esi:handle_headers("location:/foo/bar.html\r\n"), - ok. + httpd_esi:handle_headers("location:/foo/bar.html\r\n"). %%-------------------------------------------------------------------- -cgi_parse_headers(doc) -> - ["Test httpd_cgi:*. This test case will simulate that the " +cgi_parse_headers() -> + [{doc, "Test httpd_cgi:*. This test case will simulate that the " "message will be recived a little at the time on a socket and the " - "package may be broken up into smaller parts at arbitrary point."]; -cgi_parse_headers(suite) -> - []; + "package may be broken up into smaller parts at arbitrary point."}]. + cgi_parse_headers(Config) when is_list(Config) -> CGIResult = ["content-type:text", "/html\ndate:Thu, 28 Oct 2004 07:57:43 " @@ -567,26 +583,18 @@ cgi_parse_headers(Config) when is_list(Config) -> {ok,[{"content-type","text/html"}, {"connection","close"}, {"content-language","en"}, - {"age","4711"}], {200,"ok"}} = httpd_cgi:handle_headers(Headers3), - - ok. - + {"age","4711"}], {200,"ok"}} = httpd_cgi:handle_headers(Headers3). %%------------------------------------------------------------------------- -is_absolut_uri(doc) -> - ["Test http_request:is_absolut_uri/1."]; -is_absolut_uri(suite) -> - []; +is_absolut_uri() -> + [{doc, "Test http_request:is_absolut_uri/1."}]. is_absolut_uri(Config) when is_list(Config) -> true = http_request:is_absolut_uri("http://www.erlang.org"), true = http_request:is_absolut_uri("https://www.erlang.org"), false = http_request:is_absolut_uri("index.html"). - %%------------------------------------------------------------------------- -convert_netscapecookie_date(doc) -> - ["Test http_util:convert_netscapecookie_date/1."]; -convert_netscapecookie_date(suite) -> - []; +convert_netscapecookie_date() -> + [{doc, "Test http_util:convert_netscapecookie_date/1."}]. convert_netscapecookie_date(Config) when is_list(Config) -> {{2006,1,6},{8,59,38}} = http_util:convert_netscapecookie_date("Mon, 06-Jan-2006 08:59:38 GMT"), @@ -619,9 +627,7 @@ convert_netscapecookie_date(Config) when is_list(Config) -> {{2006,12,12},{8,59,38}} = http_util:convert_netscapecookie_date("Sun 12-Dec-06 08:59:38 GMT"), {{2036,1,1},{8,0,1}} = - http_util:convert_netscapecookie_date("Tue Jan 01 08:00:01 2036 GMT"), - ok. - + http_util:convert_netscapecookie_date("Tue Jan 01 08:00:01 2036 GMT"). %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 5b40d08859..c6c59ab1af 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -98,13 +98,17 @@ 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, @@ -407,6 +411,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}}). %%------------------------------------------------------------------------- @@ -747,6 +766,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) -> @@ -893,6 +928,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, [], []). @@ -1109,6 +1151,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(), @@ -1640,6 +1695,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" ++ @@ -1713,6 +1773,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", @@ -1768,6 +1835,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" ++ @@ -1792,6 +1868,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", @@ -1806,6 +1893,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" diff --git a/lib/inets/test/httpc_proxy_SUITE.erl b/lib/inets/test/httpc_proxy_SUITE.erl index 786de1bc42..6d7af4ea5d 100644 --- a/lib/inets/test/httpc_proxy_SUITE.erl +++ b/lib/inets/test/httpc_proxy_SUITE.erl @@ -58,7 +58,7 @@ groups() -> [http_emulate_lower_versions |local_proxy_cases()]}, {local_proxy_https,[], - local_proxy_cases()}]. + local_proxy_cases() ++ local_proxy_https_cases()}]. %% internal functions @@ -77,6 +77,9 @@ local_proxy_cases() -> http_stream, http_not_modified_otp_6821]. +local_proxy_https_cases() -> + [https_connect_error]. + %%-------------------------------------------------------------------- init_per_suite(Config0) -> @@ -432,6 +435,21 @@ header_value(Name, [{HeaderName,HeaderValue}|Headers]) -> end. %%-------------------------------------------------------------------- +https_connect_error(doc) -> + ["Error from CONNECT tunnel should be returned"]; +https_connect_error(Config) when is_list(Config) -> + {HttpServer,HttpPort} = ?config(http, Config), + Method = get, + %% using HTTPS scheme with HTTP port to trigger connection error + URL = "https://" ++ HttpServer ++ ":" ++ + integer_to_list(HttpPort) ++ "/index.html", + Opts = [], + HttpOpts = [], + Request = {URL,[]}, + {error,{failed_connect,[_,{tls,_,_}]}} = + httpc:request(Method, Request, HttpOpts, Opts). + +%%-------------------------------------------------------------------- %% Internal Functions ------------------------------------------------ %%-------------------------------------------------------------------- diff --git a/lib/inets/test/httpc_proxy_SUITE_data/server_proxy.sh b/lib/inets/test/httpc_proxy_SUITE_data/server_proxy.sh index 9d1698c386..473024ae63 100755 --- a/lib/inets/test/httpc_proxy_SUITE_data/server_proxy.sh +++ b/lib/inets/test/httpc_proxy_SUITE_data/server_proxy.sh @@ -169,6 +169,8 @@ MaxRequestsPerChild 0 ViaProxyName "tinyproxy" ConnectPort $APACHE_HTTPS_PORT +# to test connect error +ConnectPort $APACHE_HTTP_PORT EOF (tinyproxy -d -c tinyproxy.conf 1>/dev/null 2>&1 </dev/null &)& wait_for_pidfile tinyproxy.pid diff --git a/lib/inets/test/httpd_1_1.erl b/lib/inets/test/httpd_1_1.erl index dd9d21bbfc..db6def9d17 100644 --- a/lib/inets/test/httpd_1_1.erl +++ b/lib/inets/test/httpd_1_1.erl @@ -24,7 +24,7 @@ -include_lib("kernel/include/file.hrl"). -export([host/4, chunked/4, expect/4, range/4, if_test/5, trace/4, - head/4, mod_cgi_chunked_encoding_test/5]). + head/4, mod_cgi_chunked_encoding_test/5, mod_esi_chunk_timeout/4]). %% -define(all_keys_lower_case,true). -ifndef(all_keys_lower_case). @@ -274,6 +274,15 @@ mod_cgi_chunked_encoding_test(Type, Port, Host, Node, [Request| Rest])-> [{statuscode, 200}]), mod_cgi_chunked_encoding_test(Type, Port, Host, Node, Rest). + +mod_esi_chunk_timeout(Type, Port, Host, Node) -> + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /cgi-bin/erl/httpd_example/chunk_timeout?input=20000 HTTP/1.1\r\n" + "Host:"++ Host ++"\r\n" + "\r\n", + [{statuscode, 200}, + {header, "warning"}]). + %%-------------------------------------------------------------------- %% Internal functions %%-------------------------------------------------------------------- diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl index a6236f828a..1d8a603981 100644 --- a/lib/inets/test/httpd_SUITE.erl +++ b/lib/inets/test/httpd_SUITE.erl @@ -97,7 +97,7 @@ groups() -> {https_reload, [], [{group, reload}]}, {http_mime_types, [], [alias_1_1, alias_1_0, alias_0_9]}, {limit, [], [max_clients_1_1, max_clients_1_0, max_clients_0_9]}, - {custom, [], [customize]}, + {custom, [], [customize, add_default]}, {reload, [], [non_disturbing_reconfiger_dies, disturbing_reconfiger_dies, non_disturbing_1_1, @@ -117,7 +117,7 @@ groups() -> {htaccess, [], [htaccess_1_1, htaccess_1_0, htaccess_0_9]}, {security, [], [security_1_1, security_1_0]}, %% Skip 0.9 as causes timing issus in test code {http_1_1, [], [host, chunked, expect, cgi, cgi_chunked_encoding_test, - trace, range, if_modified_since] ++ http_head() ++ http_get() ++ load()}, + trace, range, if_modified_since, mod_esi_chunk_timeout] ++ http_head() ++ http_get() ++ load()}, {http_1_0, [], [host, cgi, trace] ++ http_head() ++ http_get() ++ load()}, {http_0_9, [], http_head() ++ http_get() ++ load()} ]. @@ -757,6 +757,13 @@ esi(Config) when is_list(Config) -> Config, [{statuscode, 200}, {no_header, "cache-control"}]). %%------------------------------------------------------------------------- +mod_esi_chunk_timeout(Config) when is_list(Config) -> + ok = httpd_1_1:mod_esi_chunk_timeout(?config(type, Config), + ?config(port, Config), + ?config(host, Config), + ?config(node, Config)). + +%%------------------------------------------------------------------------- cgi() -> [{doc, "Test mod_cgi"}]. @@ -1003,10 +1010,23 @@ customize(Config) when is_list(Config) -> {no_header, "Server"}, {version, Version}]). -response_header({"server", _}) -> - false; -response_header(Header) -> - {true, Header}. +add_default() -> + [{doc, "Test adding default header with custom callback"}]. + +add_default(Config) when is_list(Config) -> + Version = "HTTP/1.1", + Host = ?config(host, Config), + Type = ?config(type, Config), + ok = httpd_test_lib:verify_request(?config(type, Config), Host, + ?config(port, Config), + transport_opts(Type, Config), + ?config(node, Config), + http_request("GET /index.html ", Version, Host), + [{statuscode, 200}, + {header, "Content-Type", "text/html"}, + {header, "Date", "Override-date"}, + {header, "X-Frame-Options"}, + {version, Version}]). %%------------------------------------------------------------------------- max_header() -> @@ -1421,13 +1441,15 @@ server_config(http_reload, Config) -> server_config(https_reload, Config) -> [{keep_alive_timeout, 2}] ++ server_config(https, Config); server_config(http_limit, Config) -> - [{max_clients, 1}, - %% Make sure option checking code is run - {max_content_length, 100000002}] ++ server_config(http, Config); + Conf = [{max_clients, 1}, + %% Make sure option checking code is run + {max_content_length, 100000002}] ++ server_config(http, Config), + ct:pal("Received message ~p~n", [Conf]), + Conf; server_config(http_custom, Config) -> - [{custom, ?MODULE}] ++ server_config(http, Config); + [{customize, ?MODULE}] ++ server_config(http, Config); server_config(https_custom, Config) -> - [{custom, ?MODULE}] ++ server_config(https, Config); + [{customize, ?MODULE}] ++ server_config(https, Config); server_config(https_limit, Config) -> [{max_clients, 1}] ++ server_config(https, Config); server_config(http_basic_auth, Config) -> @@ -1473,6 +1495,7 @@ server_config(http_mime_types, Config0) -> server_config(http, Config) -> ServerRoot = ?config(server_root, Config), [{port, 0}, + {socket_type, {ip_comm, [{nodelay, true}]}}, {server_name,"httpd_test"}, {server_root, ServerRoot}, {document_root, ?config(doc_root, Config)}, @@ -1494,13 +1517,14 @@ server_config(http, Config) -> server_config(https, Config) -> PrivDir = ?config(priv_dir, Config), [{socket_type, {essl, - [{cacertfile, - filename:join(PrivDir, "public_key_cacert.pem")}, - {certfile, - filename:join(PrivDir, "public_key_cert.pem")}, - {keyfile, - filename:join(PrivDir, "public_key_cert_key.pem")} - ]}}] ++ server_config(http, Config). + [{nodelay, true}, + {cacertfile, + filename:join(PrivDir, "public_key_cacert.pem")}, + {certfile, + filename:join(PrivDir, "public_key_cert.pem")}, + {keyfile, + filename:join(PrivDir, "public_key_cert_key.pem")} + ]}}] ++ proplists:delete(socket_type, server_config(http, Config)). init_httpd(Group, Config0) -> Config1 = proplists:delete(port, Config0), @@ -2030,3 +2054,14 @@ typestr(ip_comm) -> "tcp"; typestr(_) -> "ssl". + +response_header({"server", _}) -> + false; +response_header(Header) -> + {true, Header}. + +response_default_headers() -> + [%% Add new header + {"X-Frame-Options", "SAMEORIGIN"}, + %% Override built-in default + {"Date", "Override-date"}]. diff --git a/lib/inets/test/httpd_SUITE_data/Makefile.src b/lib/inets/test/httpd_SUITE_data/Makefile.src index b0fdb43d8d..cea40dd8cb 100644 --- a/lib/inets/test/httpd_SUITE_data/Makefile.src +++ b/lib/inets/test/httpd_SUITE_data/Makefile.src @@ -10,5 +10,10 @@ all: $(PROGS) cgi_echo@exe@: cgi_echo@obj@ $(LD) $(CROSSLDFLAGS) -o cgi_echo cgi_echo@obj@ @LIBS@ +@IFEQ@ (@CC@, cl -nologo) +cgi_echo@obj@: cgi_echo.c + $(CC) /c /Focgi_echo@obj@ $(CFLAGS) cgi_echo.c +@ELSE@ cgi_echo@obj@: cgi_echo.c $(CC) -c -o cgi_echo@obj@ $(CFLAGS) cgi_echo.c +@ENDIF@ diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl index cb2e86c81e..c58966ce10 100644 --- a/lib/inets/test/httpd_test_lib.erl +++ b/lib/inets/test/httpd_test_lib.erl @@ -235,11 +235,17 @@ validate(RequestStr, #state{status_line = {Version, StatusCode, _}, _ -> ok end, - do_validate(http_response:header_list(Headers), Options, N, P), - check_body(RequestStr, StatusCode, - Headers#http_response_h.'content-type', - list_to_integer(Headers#http_response_h.'content-length'), - Body). + HList = http_response:header_list(Headers), + do_validate(HList, Options, N, P), + case lists:keysearch("warning", 1, HList) of + {value, _} -> + ok; + _ -> + check_body(RequestStr, StatusCode, + Headers#http_response_h.'content-type', + list_to_integer(Headers#http_response_h.'content-length'), + Body) + end. %-------------------------------------------------------------------- %% Internal functions @@ -294,9 +300,9 @@ do_validate(Header, [{header, HeaderField, Value}|Rest],N,P) -> {value, {LowerHeaderField, Value}} -> ok; false -> - ct:fail({wrong_header_field_value, LowerHeaderField, Header}); + ct:fail({wrong_header_field_value, LowerHeaderField, Header, Value}); _ -> - ct:fail({wrong_header_field_value, LowerHeaderField, Header}) + ct:fail({wrong_header_field_value, LowerHeaderField, Header, Value}) end, do_validate(Header, Rest, N, P); do_validate(Header,[{no_header, HeaderField}|Rest],N,P) -> diff --git a/lib/inets/test/inets_SUITE.erl b/lib/inets/test/inets_SUITE.erl index c3586f09e3..928d9dc391 100644 --- a/lib/inets/test/inets_SUITE.erl +++ b/lib/inets/test/inets_SUITE.erl @@ -21,7 +21,6 @@ -module(inets_SUITE). -include_lib("common_test/include/ct.hrl"). --include("test_server_line.hrl"). -include("inets_test_lib.hrl"). %% Note: This directive should only be used in test suites. @@ -37,8 +36,12 @@ all() -> groups() -> [{services_test, [], - [start_inets, start_httpc, start_httpd, start_ftpc, - start_tftpd]}, + [start_inets, + start_httpc, + start_httpd, + start_ftpc, + start_tftpd + ]}, {app_test, [], [{inets_app_test, all}]}, {appup_test, [], [{inets_appup_test, all}]}]. @@ -48,9 +51,6 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. - - - %%-------------------------------------------------------------------- %% Function: init_per_suite(Config) -> Config %% Config - [tuple()] @@ -103,14 +103,8 @@ end_per_testcase(_, Config) -> %% Test cases starts here. %%------------------------------------------------------------------------- - - -%%------------------------------------------------------------------------- - -start_inets(doc) -> - ["Test inets API functions"]; -start_inets(suite) -> - []; +start_inets() -> + [{doc, "Test inets API functions"}]. start_inets(Config) when is_list(Config) -> [_|_] = inets:service_names(), @@ -134,134 +128,85 @@ start_inets(Config) when is_list(Config) -> ok = inets:start(permanent), ok = inets:stop(). - %%------------------------------------------------------------------------- -start_httpc(doc) -> - ["Start/stop of httpc service"]; -start_httpc(suite) -> - []; +start_httpc() -> + [{doc, "Start/stop of httpc service"}]. start_httpc(Config) when is_list(Config) -> process_flag(trap_exit, true), - tsp("start_httpc -> entry with" - "~n Config: ~p", [Config]), - PrivDir = ?config(priv_dir, Config), - tsp("start_httpc -> start (empty) inets"), ok = inets:start(), - - tsp("start_httpc -> start httpc (as inets service) with profile foo"), {ok, Pid0} = inets:start(httpc, [{profile, foo}]), - tsp("start_httpc -> check running services"), Pids0 = [ServicePid || {_, ServicePid} <- inets:services()], true = lists:member(Pid0, Pids0), [_|_] = inets:services_info(), - tsp("start_httpc -> stop httpc"), inets:stop(httpc, Pid0), - tsp("start_httpc -> sleep some"), test_server:sleep(100), - tsp("start_httpc -> check running services"), Pids1 = [ServicePid || {_, ServicePid} <- inets:services()], false = lists:member(Pid0, Pids1), - tsp("start_httpc -> start httpc (stand-alone) with profile bar"), {ok, Pid1} = inets:start(httpc, [{profile, bar}], stand_alone), - tsp("start_httpc -> check running services"), Pids2 = [ServicePid || {_, ServicePid} <- inets:services()], false = lists:member(Pid1, Pids2), - tsp("start_httpc -> stop httpc"), ok = inets:stop(stand_alone, Pid1), receive {'EXIT', Pid1, shutdown} -> ok after 100 -> - tsf(stand_alone_not_shutdown) + ct:fail(stand_alone_not_shutdown) end, - tsp("start_httpc -> stop inets"), ok = inets:stop(), - tsp("start_httpc -> unload inets"), application:load(inets), - - tsp("start_httpc -> set inets environment (httpc profile foo)"), application:set_env(inets, services, [{httpc,[{profile, foo}, {data_dir, PrivDir}]}]), - - tsp("start_httpc -> start inets"), ok = inets:start(), - tsp("start_httpc -> check running services"), (?NUM_DEFAULT_SERVICES + 1) = length(inets:services()), - tsp("start_httpc -> unset inets env"), application:unset_env(inets, services), - - tsp("start_httpc -> stop inets"), ok = inets:stop(), - - tsp("start_httpc -> start (empty) inets"), ok = inets:start(), - tsp("start_httpc -> start inets httpc service with profile foo"), {ok, Pid3} = inets:start(httpc, [{profile, foo}]), - - tsp("start_httpc -> stop inets service httpc with profile foo"), ok = inets:stop(httpc, foo), - - tsp("start_httpc -> check running services"), Pids3 = [ServicePid || {_, ServicePid} <- inets:services()], false = lists:member(Pid3, Pids3), - - tsp("start_httpc -> stop inets"), - ok = inets:stop(), - - tsp("start_httpc -> done"), - ok. - + ok = inets:stop(). %%------------------------------------------------------------------------- -start_httpd(doc) -> - ["Start/stop of httpd service"]; -start_httpd(suite) -> - []; +start_httpd() -> + [{doc, "Start/stop of httpd service"}]. start_httpd(Config) when is_list(Config) -> process_flag(trap_exit, true), - i("start_httpd -> entry with" - "~n Config: ~p", [Config]), PrivDir = ?config(priv_dir, Config), HttpdConf = [{server_name, "httpd_test"}, {server_root, PrivDir}, - {document_root, PrivDir}, {bind_address, "localhost"}], + {document_root, PrivDir}, {bind_address, any}], - i("start_httpd -> start inets"), ok = inets:start(), - - i("start_httpd -> start httpd service"), {ok, Pid0} = inets:start(httpd, [{port, 0}, {ipfamily, inet} | HttpdConf]), Pids0 = [ServicePid || {_, ServicePid} <- inets:services()], true = lists:member(Pid0, Pids0), [_|_] = inets:services_info(), - i("start_httpd -> stop httpd service"), inets:stop(httpd, Pid0), test_server:sleep(500), Pids1 = [ServicePid || {_, ServicePid} <- inets:services()], false = lists:member(Pid0, Pids1), - i("start_httpd -> start (stand-alone) httpd service"), {ok, Pid1} = inets:start(httpd, [{port, 0}, {ipfamily, inet} | HttpdConf], stand_alone), Pids2 = [ServicePid || {_, ServicePid} <- inets:services()], false = lists:member(Pid1, Pids2), - i("start_httpd -> stop (stand-alone) httpd service"), ok = inets:stop(stand_alone, Pid1), receive {'EXIT', Pid1, shutdown} -> @@ -269,7 +214,6 @@ start_httpd(Config) when is_list(Config) -> after 100 -> test_server:fail(stand_alone_not_shutdown) end, - i("start_httpd -> stop inets"), ok = inets:stop(), File0 = filename:join(PrivDir, "httpd.conf"), {ok, Fd0} = file:open(File0, [write]), @@ -277,17 +221,12 @@ start_httpd(Config) when is_list(Config) -> ok = file:write(Fd0, Str), file:close(Fd0), - i("start_httpd -> [application] load inets"), application:load(inets), - i("start_httpd -> [application] set httpd services env with proplist-file"), application:set_env(inets, services, [{httpd, [{proplist_file, File0}]}]), - i("start_httpd -> start inets"), ok = inets:start(), (?NUM_DEFAULT_SERVICES + 1) = length(inets:services()), - i("start_httpd -> [application] unset services env"), application:unset_env(inets, services), - i("start_httpd -> stop inets"), ok = inets:stop(), File1 = filename:join(PrivDir, "httpd_apache.conf"), @@ -300,68 +239,46 @@ start_httpd(Config) when is_list(Config) -> file:write(Fd1, "Port 0\r\n"), file:close(Fd1), - i("start_httpd -> [application] load inets"), application:load(inets), - i("start_httpd -> [application] set httpd services env with file"), application:set_env(inets, services, [{httpd, [{file, File1}]}]), - i("start_httpd -> start inets"), ok = inets:start(), (?NUM_DEFAULT_SERVICES + 1) = length(inets:services()), - i("start_httpd -> [application] unset services env"), application:unset_env(inets, services), - i("start_httpd -> stop inets"), ok = inets:stop(), %% OLD format - i("start_httpd -> [application] load inets"), application:load(inets), - i("start_httpd -> [application] set httpd services OLD env"), application:set_env(inets, services, [{httpd, File1}]), - i("start_httpd -> start inets"), ok = inets:start(), (?NUM_DEFAULT_SERVICES + 1) = length(inets:services()), - i("start_httpd -> [application] unset services enc"), application:unset_env(inets, services), - i("start_httpd -> stop inets"), ok = inets:stop(), - - i("start_httpd -> start inets"), ok = inets:start(), - i("start_httpd -> try (and fail) start httpd service - server_name"), {error, {missing_property, server_name}} = inets:start(httpd, [{port, 0}, {server_root, PrivDir}, {document_root, PrivDir}, {bind_address, "localhost"}]), - i("start_httpd -> try (and fail) start httpd service - missing document_root"), {error, {missing_property, document_root}} = inets:start(httpd, [{port, 0}, {server_name, "httpd_test"}, {server_root, PrivDir}, {bind_address, "localhost"}]), - i("start_httpd -> try (and fail) start httpd service - missing server_root"), {error, {missing_property, server_root}} = inets:start(httpd, [{port, 0}, {server_name, "httpd_test"}, {document_root, PrivDir}, {bind_address, "localhost"}]), - i("start_httpd -> try (and fail) start httpd service - missing port"), {error, {missing_property, port}} = inets:start(httpd, HttpdConf), - i("start_httpd -> stop inets"), - ok = inets:stop(), - i("start_httpd -> done"), - ok. - + ok = inets:stop(). %%------------------------------------------------------------------------- start_ftpc(doc) -> - ["Start/stop of ftpc service"]; -start_ftpc(suite) -> - []; + [{doc, "Start/stop of ftpc service"}]; start_ftpc(Config) when is_list(Config) -> process_flag(trap_exit, true), ok = inets:start(), @@ -389,7 +306,7 @@ start_ftpc(Config) when is_list(Config) -> {'EXIT', Pid1, shutdown} -> ok after 100 -> - tsf(stand_alone_not_shutdown) + ct:fail(stand_alone_not_shutdown) end, ok = inets:stop(), ok; @@ -401,15 +318,11 @@ start_ftpc(Config) when is_list(Config) -> throw:{error, not_found} -> {skip, "No available FTP servers"} end. - - %%------------------------------------------------------------------------- -start_tftpd(doc) -> - ["Start/stop of tfpd service"]; -start_tftpd(suite) -> - []; +start_tftpd() -> + [{doc, "Start/stop of tfpd service"}]. start_tftpd(Config) when is_list(Config) -> process_flag(trap_exit, true), ok = inets:start(), @@ -441,16 +354,12 @@ start_tftpd(Config) when is_list(Config) -> application:unset_env(inets, services), ok = inets:stop(). - %%------------------------------------------------------------------------- -httpd_reload(doc) -> - ["Reload httpd configuration without restarting service"]; -httpd_reload(suite) -> - []; +httpd_reload() -> + [{doc, "Reload httpd configuration without restarting service"}]. httpd_reload(Config) when is_list(Config) -> process_flag(trap_exit, true), - i("httpd_reload -> starting"), PrivDir = ?config(priv_dir, Config), DataDir = ?config(data_dir, Config), HttpdConf = [{server_name, "httpd_test"}, @@ -458,23 +367,18 @@ httpd_reload(Config) when is_list(Config) -> {document_root, PrivDir}, {bind_address, "localhost"}], - i("httpd_reload -> start inets"), - ok = inets:start(), test_server:sleep(5000), - i("httpd_reload -> inets started - start httpd service"), - {ok, Pid0} = inets:start(httpd, [{port, 0}, {ipfamily, inet} | HttpdConf]), + {ok, Pid0} = inets:start(httpd, [{port, 0}, + {ipfamily, inet} | HttpdConf]), test_server:sleep(5000), - i("httpd_reload -> httpd service started (~p) - get port", [Pid0]), [{port, Port0}] = httpd:info(Pid0, [port]), test_server:sleep(5000), - i("httpd_reload -> Port: ~p - get document root", [Port0]), [{document_root, PrivDir}] = httpd:info(Pid0, [document_root]), test_server:sleep(5000), - i("httpd_reload -> document root: ~p - reload config", [PrivDir]), ok = httpd:reload_config([{port, Port0}, {ipfamily, inet}, {server_name, "httpd_test"}, @@ -482,11 +386,8 @@ httpd_reload(Config) when is_list(Config) -> {document_root, DataDir}, {bind_address, "localhost"}], non_disturbing), test_server:sleep(5000), - io:format("~w:~w:httpd_reload - reloaded - get document root~n", [?MODULE, ?LINE]), - [{document_root, DataDir}] = httpd:info(Pid0, [document_root]), test_server:sleep(5000), - i("httpd_reload -> document root: ~p - reload config", [DataDir]), ok = httpd:reload_config([{port, Port0}, {ipfamily, inet}, {server_name, "httpd_test"}, @@ -539,23 +440,5 @@ httpd_reload(Config) when is_list(Config) -> ok = inets:stop(httpd, Pid1), application:unset_env(inets, services), - ok = inets:stop(), - i("httpd_reload -> starting"), - ok. - - -tsf(Reason) -> - test_server:fail(Reason). - -tsp(F) -> - tsp(F, []). -tsp(F, A) -> - Timestamp = inets_lib:formated_timestamp(), - test_server:format("** ~s ** ~p ~p:" ++ F ++ "~n", [Timestamp, self(), ?MODULE | A]). - -i(F) -> - i(F, []). + ok = inets:stop(). -i(F, A) -> - Timestamp = inets_lib:formated_timestamp(), - io:format("*** ~s ~w:" ++ F ++ "~n", [Timestamp, ?MODULE | A]). diff --git a/lib/inets/test/inets_socketwrap_SUITE.erl b/lib/inets/test/inets_socketwrap_SUITE.erl new file mode 100644 index 0000000000..cfbda3ccf5 --- /dev/null +++ b/lib/inets/test/inets_socketwrap_SUITE.erl @@ -0,0 +1,154 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2015. All Rights Reserved. +%% +%% 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% +%% +%% +-module(inets_socketwrap_SUITE). + +-include_lib("common_test/include/ct.hrl"). +-include("inets_test_lib.hrl"). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +suite() -> + [{ct_hooks,[ts_install_cth]}]. + +all() -> + [start_httpd_fd, start_tftpd_fd]. + +init_per_suite(Config) -> + case os:type() of + {unix, linux} -> + Config; + _ -> + {skip, linux_feature} + end. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +init_per_testcase(Case, Config) -> + end_per_testcase(Case, Config), + Config. + +end_per_testcase(_, Config) -> + inets:stop(), + Config. + +%%------------------------------------------------------------------------- +start_httpd_fd() -> + [{doc, "Start/stop of httpd service with socket wrapper"}]. +start_httpd_fd(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + DataDir = ?config(data_dir, Config), + HttpdConf = [{port, 80}, {ipfamily, inet}, + {server_name, "httpd_fd_test"}, {server_root, PrivDir}, + {document_root, PrivDir}, {bind_address, any}], + case setup_node_info(node()) of + {skip, _} = Skip -> + Skip; + {Node, NodeArg} -> + InetPort = inets_test_lib:inet_port(node()), + ct:pal("Node: ~p Port ~p~n", [Node, InetPort]), + Wrapper = filename:join(DataDir, "setuid_socket_wrap"), + Cmd = Wrapper ++ + " -s -httpd_80,0:" ++ integer_to_list(InetPort) + ++ " -p " ++ os:find_executable("erl") ++ + " -- " ++ NodeArg, + ct:pal("cmd: ~p~n", [Cmd]), + case open_port({spawn, Cmd}, [stderr_to_stdout]) of + Port when is_port(Port) -> + wait_node_up(Node, 10), + ct:pal("~p", [rpc:call(Node, init, get_argument, [httpd_80])]), + ok = rpc:call(Node, inets, start, []), + {ok, Pid} = rpc:call(Node, inets, start, [httpd, HttpdConf]), + [{port, InetPort}] = rpc:call(Node, httpd, info, [Pid, [port]]), + rpc:call(Node, erlang, halt, []); + _ -> + ct:fail(open_port_failed) + end + end. +%%------------------------------------------------------------------------- +start_tftpd_fd() -> + [{doc, "Start/stop of tfpd service with socket wrapper"}]. +start_tftpd_fd(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + case setup_node_info(node()) of + {skip, _} = Skip -> + Skip; + {Node, NodeArg} -> + InetPort = inets_test_lib:inet_port(node()), + ct:pal("Node: ~p~n", [Node]), + Wrapper = filename:join(DataDir, "setuid_socket_wrap"), + Cmd = Wrapper ++ + " -s -tftpd_69,0:" ++ integer_to_list(InetPort) + ++ " -p " ++ os:find_executable("erl") ++ + " -- " ++ NodeArg, + ct:pal("cmd: ~p~n", [Cmd]), + case open_port({spawn, Cmd}, [stderr_to_stdout]) of + Port when is_port(Port) -> + wait_node_up(Node, 10), + ct:pal("~p", [rpc:call(Node, init, get_argument, [tftpd_69])]), + ok = rpc:call(Node, inets, start, []), + {ok, Pid} = rpc:call(Node, inets, start, + [tftpd,[{host, "localhost"}]]), + {ok, Info} = rpc:call(Node, tftp, info, [Pid]), + {value,{port, InetPort}} = lists:keysearch(port, 1, Info), + rpc:call(Node, erlang, halt, []); + _ -> + ct:fail(open_port_failed) + end + end. +%%------------------------------------------------------------------------- +%% Internal functions +%%------------------------------------------------------------------------- +setup_node_info(nonode@nohost) -> + {skip, needs_distributed_node}; +setup_node_info(Node) -> + Static = "-detached -noinput", + Name = "inets_fd_test", + NameSw = case net_kernel:longnames() of + false -> "-sname "; + _ -> "-name " + end, + StrNode = + Static ++ " " + ++ NameSw ++ " " ++ Name ++ " " + ++ "-setcookie " ++ atom_to_list(erlang:get_cookie()), + [_, Location] = string:tokens(atom_to_list(Node), "$@"), + TestNode = Name ++ "@" ++ Location, + {list_to_atom(TestNode), StrNode}. + +wait_node_up(Node, 0) -> + ct:fail({failed_to_start_node, Node}); +wait_node_up(Node, N) -> + ct:pal("(Node ~p: net_adm:ping(~p)~n", [node(), Node]), + case net_adm:ping(Node) of + pong -> + ok; + pang -> + ct:sleep(5000), + wait_node_up(Node, N-1) + end. diff --git a/lib/inets/test/inets_socketwrap_SUITE_data/Makefile.src b/lib/inets/test/inets_socketwrap_SUITE_data/Makefile.src new file mode 100644 index 0000000000..0933815b58 --- /dev/null +++ b/lib/inets/test/inets_socketwrap_SUITE_data/Makefile.src @@ -0,0 +1,39 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2015-2015. All Rights Reserved. +# +# 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% +# + +CC = @CC@ +LD = @LD@ +CFLAGS = @CFLAGS@ +CROSSLDFLAGS = @CROSSLDFLAGS@ + +PROGS = setuid_socket_wrap@exe@ + +.PHONY: all +@IFEQ@ (@os@, linux-gnu) +all: $(PROGS) +@ELSE@ +all: +@ENDIF@ + +setuid_socket_wrap@exe@: setuid_socket_wrap@obj@ + $(LD) $(CROSSLDFLAGS) -o setuid_socket_wrap setuid_socket_wrap@obj@ @LIBS@ + +setuid_socket_wrap@obj@: setuid_socket_wrap.c + $(CC) -c $(CFLAGS) -o setuid_socket_wrap@obj@ setuid_socket_wrap.c diff --git a/lib/inets/test/inets_socketwrap_SUITE_data/setuid_socket_wrap.c b/lib/inets/test/inets_socketwrap_SUITE_data/setuid_socket_wrap.c new file mode 100644 index 0000000000..b28f6b1c08 --- /dev/null +++ b/lib/inets/test/inets_socketwrap_SUITE_data/setuid_socket_wrap.c @@ -0,0 +1,259 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1999-2009. All Rights Reserved. + * + * 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% + */ +/* + * setuid_socket_wrap.c + * + * ./a.out [-s [tag,][addr]:[port]]* [-d [tag,][addr]:[port]]* + * [-r [tag,]proto]* [-p erl_path]* -- program args + * + * Where: -s = stream socket, -d datagram socket and -r means raw socket. + * + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff +#endif + +struct sock_list { + struct sock_list *next; + int fd; + int type; + int protocol; + struct sockaddr_in addr; + char *arg; +}; + +int parse_addr(addr, str) + struct sockaddr_in *addr; + char *str; +{ + int port = 0; + char *cp; + struct hostent *hp; + struct servent *se; + + if ((cp = strrchr(str, (int)':')) != NULL) + *cp++ = '\0'; + if (cp) { + if (!isdigit((int)cp[0])) { + if ((se = getservbyname(cp, "tcp")) != NULL) { + port = ntohs(se->s_port); + } else { + fprintf(stderr, "unknown port %s\n", cp); + return -1; + } + } else { + port = atoi(cp); + } + } + if (port < 0 || port > 0xffff) { + fprintf(stderr, "bad port number %d\n", port); + return -1; + } + + bzero(addr, sizeof(*addr)); + addr->sin_family = AF_INET; + addr->sin_port = htons(port); + if (*str == '\000') { + addr->sin_addr.s_addr = INADDR_ANY; + } else { + if ((addr->sin_addr.s_addr = inet_addr(str)) == INADDR_NONE) { + if ((hp = gethostbyname(str)) == NULL) { + fprintf(stderr, "\"%s\" unknown host or address!\n", str); + return -1; + } else { + bcopy(hp->h_addr_list[0], &addr->sin_addr.s_addr,hp->h_length); + } + } + } + return 0; +} + +struct sock_list *new_entry(type, argstr) + int type; + char *argstr; +{ + struct sock_list *sle; + char *cp; + + sle = (struct sock_list *)malloc(sizeof(struct sock_list)); + if (!sle) + return NULL; + sle->next = NULL; + sle->fd = -1; + + if ((cp = strchr(argstr, (int)',')) != NULL) { + *cp++ = '\0'; + sle->arg = argstr; + argstr = cp; + } else { + sle->arg = "-fd"; + } + sle->type = type; + switch (type) { + case SOCK_RAW: { + struct protoent *pe; + pe = getprotobyname(argstr); + if (!pe) { + fprintf(stderr, "Unknown protocol: %s\n", argstr); + free(sle); + return NULL; + } + sle->protocol = pe->p_proto; + break; + } + case SOCK_STREAM: + case SOCK_DGRAM: + sle->protocol = 0; + if (parse_addr(&sle->addr, argstr) < 0) { + free(sle); + return NULL; + } + break; + } + return sle; +} + +int open_socket(sle) + struct sock_list *sle; +{ + sle->fd = socket(AF_INET, sle->type, sle->protocol); + if (sle->fd < 0) { + perror("socket"); + return -1; + } + if (sle->type != SOCK_RAW) { +#if 0 + printf("binding fd %d to %s:%d\n", sle->fd, + inet_ntoa(sle->addr.sin_addr), ntohs(sle->addr.sin_port)); +#endif + if (bind(sle->fd, (struct sockaddr *)&sle->addr, sizeof(sle->addr))<0){ + perror("bind"); + close(sle->fd); + return -1; + } + } + return sle->fd; +} + +int main(argc, argv) + int argc; + char *argv[]; +{ + struct sock_list *sl = NULL, *sltmp = NULL; + int count = 0; + int c; + char *run_prog = NULL; + + while ((c = getopt(argc, argv, "s:d:r:p:")) != EOF) + switch (c) { + case 's': + sltmp = new_entry(SOCK_STREAM, optarg); + if (!sltmp) { + exit(1); + } + sltmp->next = sl; + sl = sltmp; + count++; + break; + case 'd': + sltmp = new_entry(SOCK_DGRAM, optarg); + if (!sltmp) { + exit(1); + } + sltmp->next = sl; + sl = sltmp; + count++; + break; + case 'r': + sltmp = new_entry(SOCK_RAW, optarg); + if (!sltmp) { + exit(1); + } + sltmp->next = sl; + sl = sltmp; + count++; + break; + case 'p': + run_prog = optarg; + break; + default: + exit(1); + } + argc -= optind; + argv += optind; + + for(sltmp = sl; sltmp != NULL; sltmp = sltmp->next) + if (open_socket(sltmp) < 0) { + fprintf(stderr, "failed to create socket!\n"); + exit(1); + } + + setuid(getuid()); + + { + int i; + char **newargv; + char *run_prog_name; + + newargv = (char **)malloc((1 + 2*count + argc + 1) * sizeof(char*)); + + if ((run_prog_name = strrchr(run_prog, (int)'/')) == NULL) + run_prog_name = run_prog; + else + run_prog_name++; + + i = 0; + newargv[i++] = run_prog_name; + + for (; argc; argc--, argv++, i++) + newargv[i] = *argv; + for(sltmp = sl; sltmp != NULL; ) { + char *fd_str = (char *)malloc(8); + if (!fd_str) exit(1); + sprintf(fd_str, "%d", sltmp->fd); + if (sltmp->arg && *(sltmp->arg)) + newargv[i++] = sltmp->arg; + newargv[i++] = fd_str; + sl = sltmp; + sltmp = sltmp->next; + free(sl); + } + newargv[i] = (char *)NULL; + execv(run_prog, newargv); + perror("exec"); + exit(1); + } + exit(0); +} diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl index b471dcf784..f1185f7574 100644 --- a/lib/inets/test/inets_test_lib.erl +++ b/lib/inets/test/inets_test_lib.erl @@ -563,3 +563,12 @@ stop_apps(Apps) -> application:stop(App) end, Apps). +inet_port(Node) -> + {Port, Socket} = do_inet_port(Node), + rpc:call(Node, gen_tcp, close, [Socket]), + Port. + +do_inet_port(Node) -> + {ok, Socket} = rpc:call(Node, gen_tcp, listen, [0, [{reuseaddr, true}]]), + {ok, Port} = rpc:call(Node, inet, port, [Socket]), + {Port, Socket}. diff --git a/lib/inets/test/uri_SUITE.erl b/lib/inets/test/uri_SUITE.erl index bfcd7bd339..2642b8fd4e 100644 --- a/lib/inets/test/uri_SUITE.erl +++ b/lib/inets/test/uri_SUITE.erl @@ -49,7 +49,8 @@ all() -> queries, fragments, escaped, - hexed_query + hexed_query, + scheme_validation ]. %%-------------------------------------------------------------------- @@ -175,6 +176,26 @@ hexed_query(Config) when is_list(Config) -> verify_uri(URI2, Verify2), verify_uri(URI3, Verify3). +scheme_validation(Config) when is_list(Config) -> + {ok, {http,[],"localhost",80,"/",""}} = + http_uri:parse("http://localhost#fragment"), + + ValidationFun = + fun("http") -> valid; + (_) -> {error, bad_scheme} + end, + + {ok, {http,[],"localhost",80,"/",""}} = + http_uri:parse("http://localhost#fragment", + [{scheme_validation_fun, ValidationFun}]), + {error, bad_scheme} = + http_uri:parse("https://localhost#fragment", + [{scheme_validation_fun, ValidationFun}]), + %% non-fun scheme_validation_fun works as no option passed + {ok, {https,[],"localhost",443,"/",""}} = + http_uri:parse("https://localhost#fragment", + [{scheme_validation_fun, none}]). + %%-------------------------------------------------------------------- %% Internal Functions ------------------------------------------------ |